aboutsummaryrefslogtreecommitdiffstats
path: root/samples
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@googlemail.com>2012-06-10 09:16:27 -0400
committerJiri Kosina <jkosina@suse.cz>2012-06-18 07:42:03 -0400
commit5148fa52a12fa1b97c730b2fe321f2aad7ea041c (patch)
tree4d0db495f3c5b34dad9cb0dd47043c77bc48e617 /samples
parentd99b8bad7663c8b920b366877a27b4176dece062 (diff)
HID: uhid: add example program
This adds an example user-space program that emulates a 3 button mouse with wheel. It detects keyboard presses and moves the mouse accordingly. It register a fake HID device to feed the raw HID reports into the kernel. In this example, you could use uinput to get the same result, but this shows how to get the same behavior with uhid so you don't need HID parsers in user-space. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'samples')
-rw-r--r--samples/uhid/Makefile10
-rw-r--r--samples/uhid/uhid-example.c381
2 files changed, 391 insertions, 0 deletions
diff --git a/samples/uhid/Makefile b/samples/uhid/Makefile
new file mode 100644
index 000000000000..c95a696560a7
--- /dev/null
+++ b/samples/uhid/Makefile
@@ -0,0 +1,10 @@
1# kbuild trick to avoid linker error. Can be omitted if a module is built.
2obj- := dummy.o
3
4# List of programs to build
5hostprogs-y := uhid-example
6
7# Tell kbuild to always build the programs
8always := $(hostprogs-y)
9
10HOSTCFLAGS_uhid-example.o += -I$(objtree)/usr/include
diff --git a/samples/uhid/uhid-example.c b/samples/uhid/uhid-example.c
new file mode 100644
index 000000000000..03ce3c059a5e
--- /dev/null
+++ b/samples/uhid/uhid-example.c
@@ -0,0 +1,381 @@
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}