Commit f5e4e7fdd57691d5308cf854dd0dbcfd58799e9a
Committed by
Jiri Kosina
1 parent
58c59bc997
Exists in
master
and in
20 other branches
HID: uhid: improve uhid example client
This extends the uhid example client. It properly documents the built-in report-descriptor an adds explicit report-numbers. Furthermore, LED output reports are added to utilize the new UHID output reports of the kernel. Support for 3 basic LEDs is added and a small report-parser to print debug messages if output reports were received. To test this, simply write the EV_LED+LED_CAPSL+1 event to the evdev device-node of the uhid-device and the kernel will forward it to your uhid client. Signed-off-by: David Herrmann <dh.herrmann@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Showing 1 changed file with 103 additions and 20 deletions Side-by-side Diff
samples/uhid/uhid-example.c
1 | 1 | /* |
2 | 2 | * UHID Example |
3 | 3 | * |
4 | - * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com> | |
4 | + * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com> | |
5 | 5 | * |
6 | 6 | * The code may be used by anyone for any purpose, |
7 | 7 | * and can serve as a starting point for developing |
8 | 8 | * applications using uhid. |
9 | 9 | */ |
10 | 10 | |
11 | -/* UHID Example | |
11 | +/* | |
12 | + * UHID Example | |
12 | 13 | * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this |
13 | 14 | * program as root and then use the following keys to control the mouse: |
14 | 15 | * q: Quit the application |
... | ... | @@ -22,6 +23,11 @@ |
22 | 23 | * r: Move wheel up |
23 | 24 | * f: Move wheel down |
24 | 25 | * |
26 | + * Additionally to 3 button mouse, 3 keyboard LEDs are also supported (LED_NUML, | |
27 | + * LED_CAPSL and LED_SCROLLL). The device doesn't generate any related keyboard | |
28 | + * events, though. You need to manually write the EV_LED/LED_XY/1 activation | |
29 | + * input event to the evdev device to see it being sent to this device. | |
30 | + * | |
25 | 31 | * If uhid is not available as /dev/uhid, then you can pass a different path as |
26 | 32 | * first argument. |
27 | 33 | * If <linux/uhid.h> is not installed in /usr, then compile this with: |
28 | 34 | |
... | ... | @@ -41,11 +47,12 @@ |
41 | 47 | #include <unistd.h> |
42 | 48 | #include <linux/uhid.h> |
43 | 49 | |
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: | |
50 | +/* | |
51 | + * HID Report Desciptor | |
52 | + * We emulate a basic 3 button mouse with wheel and 3 keyboard LEDs. This is | |
53 | + * the report-descriptor as the kernel will parse it: | |
47 | 54 | * |
48 | - * INPUT[INPUT] | |
55 | + * INPUT(1)[INPUT] | |
49 | 56 | * Field(0) |
50 | 57 | * Physical(GenericDesktop.Pointer) |
51 | 58 | * Application(GenericDesktop.Mouse) |
... | ... | @@ -72,6 +79,19 @@ |
72 | 79 | * Report Count(3) |
73 | 80 | * Report Offset(8) |
74 | 81 | * Flags( Variable Relative ) |
82 | + * OUTPUT(2)[OUTPUT] | |
83 | + * Field(0) | |
84 | + * Application(GenericDesktop.Keyboard) | |
85 | + * Usage(3) | |
86 | + * LED.NumLock | |
87 | + * LED.CapsLock | |
88 | + * LED.ScrollLock | |
89 | + * Logical Minimum(0) | |
90 | + * Logical Maximum(1) | |
91 | + * Report Size(1) | |
92 | + * Report Count(3) | |
93 | + * Report Offset(0) | |
94 | + * Flags( Variable Absolute ) | |
75 | 95 | * |
76 | 96 | * This is the mapping that we expect: |
77 | 97 | * Button.0001 ---> Key.LeftBtn |
78 | 98 | |
... | ... | @@ -80,19 +100,59 @@ |
80 | 100 | * GenericDesktop.X ---> Relative.X |
81 | 101 | * GenericDesktop.Y ---> Relative.Y |
82 | 102 | * GenericDesktop.Wheel ---> Relative.Wheel |
103 | + * LED.NumLock ---> LED.NumLock | |
104 | + * LED.CapsLock ---> LED.CapsLock | |
105 | + * LED.ScrollLock ---> LED.ScrollLock | |
83 | 106 | * |
84 | 107 | * This information can be verified by reading /sys/kernel/debug/hid/<dev>/rdesc |
85 | 108 | * This file should print the same information as showed above. |
86 | 109 | */ |
87 | 110 | |
88 | 111 | 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, | |
112 | + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ | |
113 | + 0x09, 0x02, /* USAGE (Mouse) */ | |
114 | + 0xa1, 0x01, /* COLLECTION (Application) */ | |
115 | + 0x09, 0x01, /* USAGE (Pointer) */ | |
116 | + 0xa1, 0x00, /* COLLECTION (Physical) */ | |
117 | + 0x85, 0x01, /* REPORT_ID (1) */ | |
118 | + 0x05, 0x09, /* USAGE_PAGE (Button) */ | |
119 | + 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ | |
120 | + 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */ | |
121 | + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ | |
122 | + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ | |
123 | + 0x95, 0x03, /* REPORT_COUNT (3) */ | |
124 | + 0x75, 0x01, /* REPORT_SIZE (1) */ | |
125 | + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ | |
126 | + 0x95, 0x01, /* REPORT_COUNT (1) */ | |
127 | + 0x75, 0x05, /* REPORT_SIZE (5) */ | |
128 | + 0x81, 0x01, /* INPUT (Cnst,Var,Abs) */ | |
129 | + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ | |
130 | + 0x09, 0x30, /* USAGE (X) */ | |
131 | + 0x09, 0x31, /* USAGE (Y) */ | |
132 | + 0x09, 0x38, /* USAGE (WHEEL) */ | |
133 | + 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ | |
134 | + 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ | |
135 | + 0x75, 0x08, /* REPORT_SIZE (8) */ | |
136 | + 0x95, 0x03, /* REPORT_COUNT (3) */ | |
137 | + 0x81, 0x06, /* INPUT (Data,Var,Rel) */ | |
138 | + 0xc0, /* END_COLLECTION */ | |
139 | + 0xc0, /* END_COLLECTION */ | |
140 | + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ | |
141 | + 0x09, 0x06, /* USAGE (Keyboard) */ | |
142 | + 0xa1, 0x01, /* COLLECTION (Application) */ | |
143 | + 0x85, 0x02, /* REPORT_ID (2) */ | |
144 | + 0x05, 0x08, /* USAGE_PAGE (Led) */ | |
145 | + 0x19, 0x01, /* USAGE_MINIMUM (1) */ | |
146 | + 0x29, 0x03, /* USAGE_MAXIMUM (3) */ | |
147 | + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ | |
148 | + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ | |
149 | + 0x95, 0x03, /* REPORT_COUNT (3) */ | |
150 | + 0x75, 0x01, /* REPORT_SIZE (1) */ | |
151 | + 0x91, 0x02, /* Output (Data,Var,Abs) */ | |
152 | + 0x95, 0x01, /* REPORT_COUNT (1) */ | |
153 | + 0x75, 0x05, /* REPORT_SIZE (5) */ | |
154 | + 0x91, 0x01, /* Output (Cnst,Var,Abs) */ | |
155 | + 0xc0, /* END_COLLECTION */ | |
96 | 156 | }; |
97 | 157 | |
98 | 158 | static int uhid_write(int fd, const struct uhid_event *ev) |
... | ... | @@ -140,6 +200,27 @@ |
140 | 200 | uhid_write(fd, &ev); |
141 | 201 | } |
142 | 202 | |
203 | +/* This parses raw output reports sent by the kernel to the device. A normal | |
204 | + * uhid program shouldn't do this but instead just forward the raw report. | |
205 | + * However, for ducomentational purposes, we try to detect LED events here and | |
206 | + * print debug messages for it. */ | |
207 | +static void handle_output(struct uhid_event *ev) | |
208 | +{ | |
209 | + /* LED messages are adverised via OUTPUT reports; ignore the rest */ | |
210 | + if (ev->u.output.rtype != UHID_OUTPUT_REPORT) | |
211 | + return; | |
212 | + /* LED reports have length 2 bytes */ | |
213 | + if (ev->u.output.size != 2) | |
214 | + return; | |
215 | + /* first byte is report-id which is 0x02 for LEDs in our rdesc */ | |
216 | + if (ev->u.output.data[0] != 0x2) | |
217 | + return; | |
218 | + | |
219 | + /* print flags payload */ | |
220 | + fprintf(stderr, "LED output report received with flags %x\n", | |
221 | + ev->u.output.data[1]); | |
222 | +} | |
223 | + | |
143 | 224 | static int event(int fd) |
144 | 225 | { |
145 | 226 | struct uhid_event ev; |
... | ... | @@ -174,6 +255,7 @@ |
174 | 255 | break; |
175 | 256 | case UHID_OUTPUT: |
176 | 257 | fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); |
258 | + handle_output(&ev); | |
177 | 259 | break; |
178 | 260 | case UHID_OUTPUT_EV: |
179 | 261 | fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); |
180 | 262 | |
181 | 263 | |
182 | 264 | |
183 | 265 | |
184 | 266 | |
... | ... | @@ -198,18 +280,19 @@ |
198 | 280 | |
199 | 281 | memset(&ev, 0, sizeof(ev)); |
200 | 282 | ev.type = UHID_INPUT; |
201 | - ev.u.input.size = 4; | |
283 | + ev.u.input.size = 5; | |
202 | 284 | |
285 | + ev.u.input.data[0] = 0x1; | |
203 | 286 | if (btn1_down) |
204 | - ev.u.input.data[0] |= 0x1; | |
287 | + ev.u.input.data[1] |= 0x1; | |
205 | 288 | if (btn2_down) |
206 | - ev.u.input.data[0] |= 0x2; | |
289 | + ev.u.input.data[1] |= 0x2; | |
207 | 290 | if (btn3_down) |
208 | - ev.u.input.data[0] |= 0x4; | |
291 | + ev.u.input.data[1] |= 0x4; | |
209 | 292 | |
210 | - ev.u.input.data[1] = abs_hor; | |
211 | - ev.u.input.data[2] = abs_ver; | |
212 | - ev.u.input.data[3] = wheel; | |
293 | + ev.u.input.data[2] = abs_hor; | |
294 | + ev.u.input.data[3] = abs_ver; | |
295 | + ev.u.input.data[4] = wheel; | |
213 | 296 | |
214 | 297 | return uhid_write(fd, &ev); |
215 | 298 | } |