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