Root/samples/uhid/uhid-example.c

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
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}
382

Archive Download this file



interactive