uhid-example.c 8.1 KB

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