Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
[~shefty/rdma-dev.git] / 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
88 static 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
98 static 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
115 static 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
133 static 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
143 static 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
188 static bool btn1_down;
189 static bool btn2_down;
190 static bool btn3_down;
191 static signed char abs_hor;
192 static signed char abs_ver;
193 static signed char wheel;
194
195 static 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
217 static 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
303 int 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 }