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