uhid-example.c 11 KB

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