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
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
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
111
112static unsigned char rdesc[] = {
113 0x05, 0x01,
114 0x09, 0x02,
115 0xa1, 0x01,
116 0x09, 0x01,
117 0xa1, 0x00,
118 0x85, 0x01,
119 0x05, 0x09,
120 0x19, 0x01,
121 0x29, 0x03,
122 0x15, 0x00,
123 0x25, 0x01,
124 0x95, 0x03,
125 0x75, 0x01,
126 0x81, 0x02,
127 0x95, 0x01,
128 0x75, 0x05,
129 0x81, 0x01,
130 0x05, 0x01,
131 0x09, 0x30,
132 0x09, 0x31,
133 0x09, 0x38,
134 0x15, 0x81,
135 0x25, 0x7f,
136 0x75, 0x08,
137 0x95, 0x03,
138 0x81, 0x06,
139 0xc0,
140 0xc0,
141 0x05, 0x01,
142 0x09, 0x06,
143 0xa1, 0x01,
144 0x85, 0x02,
145 0x05, 0x08,
146 0x19, 0x01,
147 0x29, 0x03,
148 0x15, 0x00,
149 0x25, 0x01,
150 0x95, 0x03,
151 0x75, 0x01,
152 0x91, 0x02,
153 0x95, 0x01,
154 0x75, 0x05,
155 0x91, 0x01,
156 0xc0,
157};
158
159static 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: %ld != %lu\n",
169 ret, sizeof(ev));
170 return -EFAULT;
171 } else {
172 return 0;
173 }
174}
175
176static 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
194static 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
205
206
207
208static void handle_output(struct uhid_event *ev)
209{
210
211 if (ev->u.output.rtype != UHID_OUTPUT_REPORT)
212 return;
213
214 if (ev->u.output.size != 2)
215 return;
216
217 if (ev->u.output.data[0] != 0x2)
218 return;
219
220
221 fprintf(stderr, "LED output report received with flags %x\n",
222 ev->u.output.data[1]);
223}
224
225static 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: %ld != %lu\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
271static bool btn1_down;
272static bool btn2_down;
273static bool btn3_down;
274static signed char abs_hor;
275static signed char abs_ver;
276static signed char wheel;
277
278static 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
301static 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
387int 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