1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111static unsigned char rdesc[] = {
112 0x05, 0x01,
113 0x09, 0x02,
114 0xa1, 0x01,
115 0x09, 0x01,
116 0xa1, 0x00,
117 0x85, 0x01,
118 0x05, 0x09,
119 0x19, 0x01,
120 0x29, 0x03,
121 0x15, 0x00,
122 0x25, 0x01,
123 0x95, 0x03,
124 0x75, 0x01,
125 0x81, 0x02,
126 0x95, 0x01,
127 0x75, 0x05,
128 0x81, 0x01,
129 0x05, 0x01,
130 0x09, 0x30,
131 0x09, 0x31,
132 0x09, 0x38,
133 0x15, 0x81,
134 0x25, 0x7f,
135 0x75, 0x08,
136 0x95, 0x03,
137 0x81, 0x06,
138 0xc0,
139 0xc0,
140 0x05, 0x01,
141 0x09, 0x06,
142 0xa1, 0x01,
143 0x85, 0x02,
144 0x05, 0x08,
145 0x19, 0x01,
146 0x29, 0x03,
147 0x15, 0x00,
148 0x25, 0x01,
149 0x95, 0x03,
150 0x75, 0x01,
151 0x91, 0x02,
152 0x95, 0x01,
153 0x75, 0x05,
154 0x91, 0x01,
155 0xc0,
156};
157
158static 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
175static 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
193static 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
204
205
206
207static void handle_output(struct uhid_event *ev)
208{
209
210 if (ev->u.output.rtype != UHID_OUTPUT_REPORT)
211 return;
212
213 if (ev->u.output.size != 2)
214 return;
215
216 if (ev->u.output.data[0] != 0x2)
217 return;
218
219
220 fprintf(stderr, "LED output report received with flags %x\n",
221 ev->u.output.data[1]);
222}
223
224static 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
270static bool btn1_down;
271static bool btn2_down;
272static bool btn3_down;
273static signed char abs_hor;
274static signed char abs_ver;
275static signed char wheel;
276
277static 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
300static 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
386int 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