1 /* 2 * UHID Example 3 * 4 * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.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 /* 12 * UHID Example 13 * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this 14 * program as root and then use the following keys to control the mouse: 15 * q: Quit the application 16 * 1: Toggle left button (down, up, ...) 17 * 2: Toggle right button 18 * 3: Toggle middle button 19 * a: Move mouse left 20 * d: Move mouse right 21 * w: Move mouse up 22 * s: Move mouse down 23 * r: Move wheel up 24 * f: Move wheel down 25 * 26 * Additionally to 3 button mouse, 3 keyboard LEDs are also supported (LED_NUML, 27 * LED_CAPSL and LED_SCROLLL). The device doesn't generate any related keyboard 28 * events, though. You need to manually write the EV_LED/LED_XY/1 activation 29 * input event to the evdev device to see it being sent to this device. 30 * 31 * If uhid is not available as /dev/uhid, then you can pass a different path as 32 * first argument. 33 * If <linux/uhid.h> is not installed in /usr, then compile this with: 34 * gcc -o ./uhid_test -Wall -I./include ./samples/uhid/uhid-example.c 35 * And ignore the warning about kernel headers. However, it is recommended to 36 * use the installed uhid.h if available. 37 */ 38 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <poll.h> 42 #include <stdbool.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <termios.h> 47 #include <unistd.h> 48 #include <linux/uhid.h> 49 50 /* 51 * HID Report Desciptor 52 * We emulate a basic 3 button mouse with wheel and 3 keyboard LEDs. This is 53 * the report-descriptor as the kernel will parse it: 54 * 55 * INPUT(1)[INPUT] 56 * Field(0) 57 * Physical(GenericDesktop.Pointer) 58 * Application(GenericDesktop.Mouse) 59 * Usage(3) 60 * Button.0001 61 * Button.0002 62 * Button.0003 63 * Logical Minimum(0) 64 * Logical Maximum(1) 65 * Report Size(1) 66 * Report Count(3) 67 * Report Offset(0) 68 * Flags( Variable Absolute ) 69 * Field(1) 70 * Physical(GenericDesktop.Pointer) 71 * Application(GenericDesktop.Mouse) 72 * Usage(3) 73 * GenericDesktop.X 74 * GenericDesktop.Y 75 * GenericDesktop.Wheel 76 * Logical Minimum(-128) 77 * Logical Maximum(127) 78 * Report Size(8) 79 * Report Count(3) 80 * Report Offset(8) 81 * Flags( Variable Relative ) 82 * OUTPUT(2)[OUTPUT] 83 * Field(0) 84 * Application(GenericDesktop.Keyboard) 85 * Usage(3) 86 * LED.NumLock 87 * LED.CapsLock 88 * LED.ScrollLock 89 * Logical Minimum(0) 90 * Logical Maximum(1) 91 * Report Size(1) 92 * Report Count(3) 93 * Report Offset(0) 94 * Flags( Variable Absolute ) 95 * 96 * This is the mapping that we expect: 97 * Button.0001 ---> Key.LeftBtn 98 * Button.0002 ---> Key.RightBtn 99 * Button.0003 ---> Key.MiddleBtn 100 * GenericDesktop.X ---> Relative.X 101 * GenericDesktop.Y ---> Relative.Y 102 * GenericDesktop.Wheel ---> Relative.Wheel 103 * LED.NumLock ---> LED.NumLock 104 * LED.CapsLock ---> LED.CapsLock 105 * LED.ScrollLock ---> LED.ScrollLock 106 * 107 * This information can be verified by reading /sys/kernel/debug/hid/<dev>/rdesc 108 * This file should print the same information as showed above. 109 */ 110 111 static unsigned char rdesc[] = { 112 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 113 0x09, 0x02, /* USAGE (Mouse) */ 114 0xa1, 0x01, /* COLLECTION (Application) */ 115 0x09, 0x01, /* USAGE (Pointer) */ 116 0xa1, 0x00, /* COLLECTION (Physical) */ 117 0x85, 0x01, /* REPORT_ID (1) */ 118 0x05, 0x09, /* USAGE_PAGE (Button) */ 119 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ 120 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */ 121 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 122 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 123 0x95, 0x03, /* REPORT_COUNT (3) */ 124 0x75, 0x01, /* REPORT_SIZE (1) */ 125 0x81, 0x02, /* INPUT (Data,Var,Abs) */ 126 0x95, 0x01, /* REPORT_COUNT (1) */ 127 0x75, 0x05, /* REPORT_SIZE (5) */ 128 0x81, 0x01, /* INPUT (Cnst,Var,Abs) */ 129 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 130 0x09, 0x30, /* USAGE (X) */ 131 0x09, 0x31, /* USAGE (Y) */ 132 0x09, 0x38, /* USAGE (WHEEL) */ 133 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ 134 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ 135 0x75, 0x08, /* REPORT_SIZE (8) */ 136 0x95, 0x03, /* REPORT_COUNT (3) */ 137 0x81, 0x06, /* INPUT (Data,Var,Rel) */ 138 0xc0, /* END_COLLECTION */ 139 0xc0, /* END_COLLECTION */ 140 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 141 0x09, 0x06, /* USAGE (Keyboard) */ 142 0xa1, 0x01, /* COLLECTION (Application) */ 143 0x85, 0x02, /* REPORT_ID (2) */ 144 0x05, 0x08, /* USAGE_PAGE (Led) */ 145 0x19, 0x01, /* USAGE_MINIMUM (1) */ 146 0x29, 0x03, /* USAGE_MAXIMUM (3) */ 147 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 148 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 149 0x95, 0x03, /* REPORT_COUNT (3) */ 150 0x75, 0x01, /* REPORT_SIZE (1) */ 151 0x91, 0x02, /* Output (Data,Var,Abs) */ 152 0x95, 0x01, /* REPORT_COUNT (1) */ 153 0x75, 0x05, /* REPORT_SIZE (5) */ 154 0x91, 0x01, /* Output (Cnst,Var,Abs) */ 155 0xc0, /* END_COLLECTION */ 156 }; 157 158 static int uhid_write(int fd, const struct uhid_event *ev) 159 { 160 ssize_t ret; 161 162 ret = write(fd, ev, sizeof(*ev)); 163 if (ret < 0) { 164 fprintf(stderr, "Cannot write to uhid: %m\n"); 165 return -errno; 166 } else if (ret != sizeof(*ev)) { 167 fprintf(stderr, "Wrong size written to uhid: %ld != %lu\n", 168 ret, sizeof(ev)); 169 return -EFAULT; 170 } else { 171 return 0; 172 } 173 } 174 175 static int create(int fd) 176 { 177 struct uhid_event ev; 178 179 memset(&ev, 0, sizeof(ev)); 180 ev.type = UHID_CREATE; 181 strcpy((char*)ev.u.create.name, "test-uhid-device"); 182 ev.u.create.rd_data = rdesc; 183 ev.u.create.rd_size = sizeof(rdesc); 184 ev.u.create.bus = BUS_USB; 185 ev.u.create.vendor = 0x15d9; 186 ev.u.create.product = 0x0a37; 187 ev.u.create.version = 0; 188 ev.u.create.country = 0; 189 190 return uhid_write(fd, &ev); 191 } 192 193 static void destroy(int fd) 194 { 195 struct uhid_event ev; 196 197 memset(&ev, 0, sizeof(ev)); 198 ev.type = UHID_DESTROY; 199 200 uhid_write(fd, &ev); 201 } 202 203 /* This parses raw output reports sent by the kernel to the device. A normal 204 * uhid program shouldn't do this but instead just forward the raw report. 205 * However, for ducomentational purposes, we try to detect LED events here and 206 * print debug messages for it. */ 207 static void handle_output(struct uhid_event *ev) 208 { 209 /* LED messages are adverised via OUTPUT reports; ignore the rest */ 210 if (ev->u.output.rtype != UHID_OUTPUT_REPORT) 211 return; 212 /* LED reports have length 2 bytes */ 213 if (ev->u.output.size != 2) 214 return; 215 /* first byte is report-id which is 0x02 for LEDs in our rdesc */ 216 if (ev->u.output.data[0] != 0x2) 217 return; 218 219 /* print flags payload */ 220 fprintf(stderr, "LED output report received with flags %x\n", 221 ev->u.output.data[1]); 222 } 223 224 static int event(int fd) 225 { 226 struct uhid_event ev; 227 ssize_t ret; 228 229 memset(&ev, 0, sizeof(ev)); 230 ret = read(fd, &ev, sizeof(ev)); 231 if (ret == 0) { 232 fprintf(stderr, "Read HUP on uhid-cdev\n"); 233 return -EFAULT; 234 } else if (ret < 0) { 235 fprintf(stderr, "Cannot read uhid-cdev: %m\n"); 236 return -errno; 237 } else if (ret != sizeof(ev)) { 238 fprintf(stderr, "Invalid size read from uhid-dev: %ld != %lu\n", 239 ret, sizeof(ev)); 240 return -EFAULT; 241 } 242 243 switch (ev.type) { 244 case UHID_START: 245 fprintf(stderr, "UHID_START from uhid-dev\n"); 246 break; 247 case UHID_STOP: 248 fprintf(stderr, "UHID_STOP from uhid-dev\n"); 249 break; 250 case UHID_OPEN: 251 fprintf(stderr, "UHID_OPEN from uhid-dev\n"); 252 break; 253 case UHID_CLOSE: 254 fprintf(stderr, "UHID_CLOSE from uhid-dev\n"); 255 break; 256 case UHID_OUTPUT: 257 fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); 258 handle_output(&ev); 259 break; 260 case UHID_OUTPUT_EV: 261 fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); 262 break; 263 default: 264 fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); 265 } 266 267 return 0; 268 } 269 270 static bool btn1_down; 271 static bool btn2_down; 272 static bool btn3_down; 273 static signed char abs_hor; 274 static signed char abs_ver; 275 static signed char wheel; 276 277 static int send_event(int fd) 278 { 279 struct uhid_event ev; 280 281 memset(&ev, 0, sizeof(ev)); 282 ev.type = UHID_INPUT; 283 ev.u.input.size = 5; 284 285 ev.u.input.data[0] = 0x1; 286 if (btn1_down) 287 ev.u.input.data[1] |= 0x1; 288 if (btn2_down) 289 ev.u.input.data[1] |= 0x2; 290 if (btn3_down) 291 ev.u.input.data[1] |= 0x4; 292 293 ev.u.input.data[2] = abs_hor; 294 ev.u.input.data[3] = abs_ver; 295 ev.u.input.data[4] = wheel; 296 297 return uhid_write(fd, &ev); 298 } 299 300 static int keyboard(int fd) 301 { 302 char buf[128]; 303 ssize_t ret, i; 304 305 ret = read(STDIN_FILENO, buf, sizeof(buf)); 306 if (ret == 0) { 307 fprintf(stderr, "Read HUP on stdin\n"); 308 return -EFAULT; 309 } else if (ret < 0) { 310 fprintf(stderr, "Cannot read stdin: %m\n"); 311 return -errno; 312 } 313 314 for (i = 0; i < ret; ++i) { 315 switch (buf[i]) { 316 case '1': 317 btn1_down = !btn1_down; 318 ret = send_event(fd); 319 if (ret) 320 return ret; 321 break; 322 case '2': 323 btn2_down = !btn2_down; 324 ret = send_event(fd); 325 if (ret) 326 return ret; 327 break; 328 case '3': 329 btn3_down = !btn3_down; 330 ret = send_event(fd); 331 if (ret) 332 return ret; 333 break; 334 case 'a': 335 abs_hor = -20; 336 ret = send_event(fd); 337 abs_hor = 0; 338 if (ret) 339 return ret; 340 break; 341 case 'd': 342 abs_hor = 20; 343 ret = send_event(fd); 344 abs_hor = 0; 345 if (ret) 346 return ret; 347 break; 348 case 'w': 349 abs_ver = -20; 350 ret = send_event(fd); 351 abs_ver = 0; 352 if (ret) 353 return ret; 354 break; 355 case 's': 356 abs_ver = 20; 357 ret = send_event(fd); 358 abs_ver = 0; 359 if (ret) 360 return ret; 361 break; 362 case 'r': 363 wheel = 1; 364 ret = send_event(fd); 365 wheel = 0; 366 if (ret) 367 return ret; 368 break; 369 case 'f': 370 wheel = -1; 371 ret = send_event(fd); 372 wheel = 0; 373 if (ret) 374 return ret; 375 break; 376 case 'q': 377 return -ECANCELED; 378 default: 379 fprintf(stderr, "Invalid input: %c\n", buf[i]); 380 } 381 } 382 383 return 0; 384 } 385 386 int main(int argc, char **argv) 387 { 388 int fd; 389 const char *path = "/dev/uhid"; 390 struct pollfd pfds[2]; 391 int ret; 392 struct termios state; 393 394 ret = tcgetattr(STDIN_FILENO, &state); 395 if (ret) { 396 fprintf(stderr, "Cannot get tty state\n"); 397 } else { 398 state.c_lflag &= ~ICANON; 399 state.c_cc[VMIN] = 1; 400 ret = tcsetattr(STDIN_FILENO, TCSANOW, &state); 401 if (ret) 402 fprintf(stderr, "Cannot set tty state\n"); 403 } 404 405 if (argc >= 2) { 406 if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { 407 fprintf(stderr, "Usage: %s [%s]\n", argv[0], path); 408 return EXIT_SUCCESS; 409 } else { 410 path = argv[1]; 411 } 412 } 413 414 fprintf(stderr, "Open uhid-cdev %s\n", path); 415 fd = open(path, O_RDWR | O_CLOEXEC); 416 if (fd < 0) { 417 fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path); 418 return EXIT_FAILURE; 419 } 420 421 fprintf(stderr, "Create uhid device\n"); 422 ret = create(fd); 423 if (ret) { 424 close(fd); 425 return EXIT_FAILURE; 426 } 427 428 pfds[0].fd = STDIN_FILENO; 429 pfds[0].events = POLLIN; 430 pfds[1].fd = fd; 431 pfds[1].events = POLLIN; 432 433 fprintf(stderr, "Press 'q' to quit...\n"); 434 while (1) { 435 ret = poll(pfds, 2, -1); 436 if (ret < 0) { 437 fprintf(stderr, "Cannot poll for fds: %m\n"); 438 break; 439 } 440 if (pfds[0].revents & POLLHUP) { 441 fprintf(stderr, "Received HUP on stdin\n"); 442 break; 443 } 444 if (pfds[1].revents & POLLHUP) { 445 fprintf(stderr, "Received HUP on uhid-cdev\n"); 446 break; 447 } 448 449 if (pfds[0].revents & POLLIN) { 450 ret = keyboard(fd); 451 if (ret) 452 break; 453 } 454 if (pfds[1].revents & POLLIN) { 455 ret = event(fd); 456 if (ret) 457 break; 458 } 459 } 460 461 fprintf(stderr, "Destroy uhid device\n"); 462 destroy(fd); 463 return EXIT_SUCCESS; 464 } 465