1 /* 2 * UHID Example 3 * 4 * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com> 5 * 6 * The code may be used by anyone for any purpose, 7 * and can serve as a starting point for developing 8 * applications using uhid. 9 */ 10 11 /* UHID Example 12 * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this 13 * program as root and then use the following keys to control the mouse: 14 * q: Quit the application 15 * 1: Toggle left button (down, up, ...) 16 * 2: Toggle right button 17 * 3: Toggle middle button 18 * a: Move mouse left 19 * d: Move mouse right 20 * w: Move mouse up 21 * s: Move mouse down 22 * r: Move wheel up 23 * f: Move wheel down 24 * 25 * If uhid is not available as /dev/uhid, then you can pass a different path as 26 * first argument. 27 * If <linux/uhid.h> is not installed in /usr, then compile this with: 28 * gcc -o ./uhid_test -Wall -I./include ./samples/uhid/uhid-example.c 29 * And ignore the warning about kernel headers. However, it is recommended to 30 * use the installed uhid.h if available. 31 */ 32 33 #include <errno.h> 34 #include <fcntl.h> 35 #include <poll.h> 36 #include <stdbool.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <termios.h> 41 #include <unistd.h> 42 #include <linux/uhid.h> 43 44 /* HID Report Desciptor 45 * We emulate a basic 3 button mouse with wheel. This is the report-descriptor 46 * as the kernel will parse it: 47 * 48 * INPUT[INPUT] 49 * Field(0) 50 * Physical(GenericDesktop.Pointer) 51 * Application(GenericDesktop.Mouse) 52 * Usage(3) 53 * Button.0001 54 * Button.0002 55 * Button.0003 56 * Logical Minimum(0) 57 * Logical Maximum(1) 58 * Report Size(1) 59 * Report Count(3) 60 * Report Offset(0) 61 * Flags( Variable Absolute ) 62 * Field(1) 63 * Physical(GenericDesktop.Pointer) 64 * Application(GenericDesktop.Mouse) 65 * Usage(3) 66 * GenericDesktop.X 67 * GenericDesktop.Y 68 * GenericDesktop.Wheel 69 * Logical Minimum(-128) 70 * Logical Maximum(127) 71 * Report Size(8) 72 * Report Count(3) 73 * Report Offset(8) 74 * Flags( Variable Relative ) 75 * 76 * This is the mapping that we expect: 77 * Button.0001 ---> Key.LeftBtn 78 * Button.0002 ---> Key.RightBtn 79 * Button.0003 ---> Key.MiddleBtn 80 * GenericDesktop.X ---> Relative.X 81 * GenericDesktop.Y ---> Relative.Y 82 * GenericDesktop.Wheel ---> Relative.Wheel 83 * 84 * This information can be verified by reading /sys/kernel/debug/hid/<dev>/rdesc 85 * This file should print the same information as showed above. 86 */ 87 88 static unsigned char rdesc[] = { 89 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, 90 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, 91 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, 92 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, 93 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, 94 0x15, 0x80, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, 95 0x81, 0x06, 0xc0, 0xc0, 96 }; 97 98 static int uhid_write(int fd, const struct uhid_event *ev) 99 { 100 ssize_t ret; 101 102 ret = write(fd, ev, sizeof(*ev)); 103 if (ret < 0) { 104 fprintf(stderr, "Cannot write to uhid: %m\n"); 105 return -errno; 106 } else if (ret != sizeof(*ev)) { 107 fprintf(stderr, "Wrong size written to uhid: %ld != %lu\n", 108 ret, sizeof(ev)); 109 return -EFAULT; 110 } else { 111 return 0; 112 } 113 } 114 115 static int create(int fd) 116 { 117 struct uhid_event ev; 118 119 memset(&ev, 0, sizeof(ev)); 120 ev.type = UHID_CREATE; 121 strcpy((char*)ev.u.create.name, "test-uhid-device"); 122 ev.u.create.rd_data = rdesc; 123 ev.u.create.rd_size = sizeof(rdesc); 124 ev.u.create.bus = BUS_USB; 125 ev.u.create.vendor = 0x15d9; 126 ev.u.create.product = 0x0a37; 127 ev.u.create.version = 0; 128 ev.u.create.country = 0; 129 130 return uhid_write(fd, &ev); 131 } 132 133 static void destroy(int fd) 134 { 135 struct uhid_event ev; 136 137 memset(&ev, 0, sizeof(ev)); 138 ev.type = UHID_DESTROY; 139 140 uhid_write(fd, &ev); 141 } 142 143 static int event(int fd) 144 { 145 struct uhid_event ev; 146 ssize_t ret; 147 148 memset(&ev, 0, sizeof(ev)); 149 ret = read(fd, &ev, sizeof(ev)); 150 if (ret == 0) { 151 fprintf(stderr, "Read HUP on uhid-cdev\n"); 152 return -EFAULT; 153 } else if (ret < 0) { 154 fprintf(stderr, "Cannot read uhid-cdev: %m\n"); 155 return -errno; 156 } else if (ret != sizeof(ev)) { 157 fprintf(stderr, "Invalid size read from uhid-dev: %ld != %lu\n", 158 ret, sizeof(ev)); 159 return -EFAULT; 160 } 161 162 switch (ev.type) { 163 case UHID_START: 164 fprintf(stderr, "UHID_START from uhid-dev\n"); 165 break; 166 case UHID_STOP: 167 fprintf(stderr, "UHID_STOP from uhid-dev\n"); 168 break; 169 case UHID_OPEN: 170 fprintf(stderr, "UHID_OPEN from uhid-dev\n"); 171 break; 172 case UHID_CLOSE: 173 fprintf(stderr, "UHID_CLOSE from uhid-dev\n"); 174 break; 175 case UHID_OUTPUT: 176 fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); 177 break; 178 case UHID_OUTPUT_EV: 179 fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); 180 break; 181 default: 182 fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); 183 } 184 185 return 0; 186 } 187 188 static bool btn1_down; 189 static bool btn2_down; 190 static bool btn3_down; 191 static signed char abs_hor; 192 static signed char abs_ver; 193 static signed char wheel; 194 195 static int send_event(int fd) 196 { 197 struct uhid_event ev; 198 199 memset(&ev, 0, sizeof(ev)); 200 ev.type = UHID_INPUT; 201 ev.u.input.size = 4; 202 203 if (btn1_down) 204 ev.u.input.data[0] |= 0x1; 205 if (btn2_down) 206 ev.u.input.data[0] |= 0x2; 207 if (btn3_down) 208 ev.u.input.data[0] |= 0x4; 209 210 ev.u.input.data[1] = abs_hor; 211 ev.u.input.data[2] = abs_ver; 212 ev.u.input.data[3] = wheel; 213 214 return uhid_write(fd, &ev); 215 } 216 217 static int keyboard(int fd) 218 { 219 char buf[128]; 220 ssize_t ret, i; 221 222 ret = read(STDIN_FILENO, buf, sizeof(buf)); 223 if (ret == 0) { 224 fprintf(stderr, "Read HUP on stdin\n"); 225 return -EFAULT; 226 } else if (ret < 0) { 227 fprintf(stderr, "Cannot read stdin: %m\n"); 228 return -errno; 229 } 230 231 for (i = 0; i < ret; ++i) { 232 switch (buf[i]) { 233 case '1': 234 btn1_down = !btn1_down; 235 ret = send_event(fd); 236 if (ret) 237 return ret; 238 break; 239 case '2': 240 btn2_down = !btn2_down; 241 ret = send_event(fd); 242 if (ret) 243 return ret; 244 break; 245 case '3': 246 btn3_down = !btn3_down; 247 ret = send_event(fd); 248 if (ret) 249 return ret; 250 break; 251 case 'a': 252 abs_hor = -20; 253 ret = send_event(fd); 254 abs_hor = 0; 255 if (ret) 256 return ret; 257 break; 258 case 'd': 259 abs_hor = 20; 260 ret = send_event(fd); 261 abs_hor = 0; 262 if (ret) 263 return ret; 264 break; 265 case 'w': 266 abs_ver = -20; 267 ret = send_event(fd); 268 abs_ver = 0; 269 if (ret) 270 return ret; 271 break; 272 case 's': 273 abs_ver = 20; 274 ret = send_event(fd); 275 abs_ver = 0; 276 if (ret) 277 return ret; 278 break; 279 case 'r': 280 wheel = 1; 281 ret = send_event(fd); 282 wheel = 0; 283 if (ret) 284 return ret; 285 break; 286 case 'f': 287 wheel = -1; 288 ret = send_event(fd); 289 wheel = 0; 290 if (ret) 291 return ret; 292 break; 293 case 'q': 294 return -ECANCELED; 295 default: 296 fprintf(stderr, "Invalid input: %c\n", buf[i]); 297 } 298 } 299 300 return 0; 301 } 302 303 int main(int argc, char **argv) 304 { 305 int fd; 306 const char *path = "/dev/uhid"; 307 struct pollfd pfds[2]; 308 int ret; 309 struct termios state; 310 311 ret = tcgetattr(STDIN_FILENO, &state); 312 if (ret) { 313 fprintf(stderr, "Cannot get tty state\n"); 314 } else { 315 state.c_lflag &= ~ICANON; 316 state.c_cc[VMIN] = 1; 317 ret = tcsetattr(STDIN_FILENO, TCSANOW, &state); 318 if (ret) 319 fprintf(stderr, "Cannot set tty state\n"); 320 } 321 322 if (argc >= 2) { 323 if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { 324 fprintf(stderr, "Usage: %s [%s]\n", argv[0], path); 325 return EXIT_SUCCESS; 326 } else { 327 path = argv[1]; 328 } 329 } 330 331 fprintf(stderr, "Open uhid-cdev %s\n", path); 332 fd = open(path, O_RDWR | O_CLOEXEC); 333 if (fd < 0) { 334 fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path); 335 return EXIT_FAILURE; 336 } 337 338 fprintf(stderr, "Create uhid device\n"); 339 ret = create(fd); 340 if (ret) { 341 close(fd); 342 return EXIT_FAILURE; 343 } 344 345 pfds[0].fd = STDIN_FILENO; 346 pfds[0].events = POLLIN; 347 pfds[1].fd = fd; 348 pfds[1].events = POLLIN; 349 350 fprintf(stderr, "Press 'q' to quit...\n"); 351 while (1) { 352 ret = poll(pfds, 2, -1); 353 if (ret < 0) { 354 fprintf(stderr, "Cannot poll for fds: %m\n"); 355 break; 356 } 357 if (pfds[0].revents & POLLHUP) { 358 fprintf(stderr, "Received HUP on stdin\n"); 359 break; 360 } 361 if (pfds[1].revents & POLLHUP) { 362 fprintf(stderr, "Received HUP on uhid-cdev\n"); 363 break; 364 } 365 366 if (pfds[0].revents & POLLIN) { 367 ret = keyboard(fd); 368 if (ret) 369 break; 370 } 371 if (pfds[1].revents & POLLIN) { 372 ret = event(fd); 373 if (ret) 374 break; 375 } 376 } 377 378 fprintf(stderr, "Destroy uhid device\n"); 379 destroy(fd); 380 return EXIT_SUCCESS; 381 } 382