uhid-example.c 11 KB

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