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 Inline Diff
samples/uhid/uhid-example.c
1 | /* | 1 | /* |
2 | * UHID Example | 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 | * The code may be used by anyone for any purpose, | 6 | * The code may be used by anyone for any purpose, |
7 | * and can serve as a starting point for developing | 7 | * and can serve as a starting point for developing |
8 | * applications using uhid. | 8 | * applications using uhid. |
9 | */ | 9 | */ |
10 | 10 | ||
11 | /* UHID Example | 11 | /* |
12 | * UHID Example | ||
12 | * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this | 13 | * 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 | * program as root and then use the following keys to control the mouse: |
14 | * q: Quit the application | 15 | * q: Quit the application |
15 | * 1: Toggle left button (down, up, ...) | 16 | * 1: Toggle left button (down, up, ...) |
16 | * 2: Toggle right button | 17 | * 2: Toggle right button |
17 | * 3: Toggle middle button | 18 | * 3: Toggle middle button |
18 | * a: Move mouse left | 19 | * a: Move mouse left |
19 | * d: Move mouse right | 20 | * d: Move mouse right |
20 | * w: Move mouse up | 21 | * w: Move mouse up |
21 | * s: Move mouse down | 22 | * s: Move mouse down |
22 | * r: Move wheel up | 23 | * r: Move wheel up |
23 | * f: Move wheel down | 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 | * If uhid is not available as /dev/uhid, then you can pass a different path as | 31 | * If uhid is not available as /dev/uhid, then you can pass a different path as |
26 | * first argument. | 32 | * first argument. |
27 | * If <linux/uhid.h> is not installed in /usr, then compile this with: | 33 | * 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 | 34 | * 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 | 35 | * And ignore the warning about kernel headers. However, it is recommended to |
30 | * use the installed uhid.h if available. | 36 | * use the installed uhid.h if available. |
31 | */ | 37 | */ |
32 | 38 | ||
33 | #include <errno.h> | 39 | #include <errno.h> |
34 | #include <fcntl.h> | 40 | #include <fcntl.h> |
35 | #include <poll.h> | 41 | #include <poll.h> |
36 | #include <stdbool.h> | 42 | #include <stdbool.h> |
37 | #include <stdio.h> | 43 | #include <stdio.h> |
38 | #include <stdlib.h> | 44 | #include <stdlib.h> |
39 | #include <string.h> | 45 | #include <string.h> |
40 | #include <termios.h> | 46 | #include <termios.h> |
41 | #include <unistd.h> | 47 | #include <unistd.h> |
42 | #include <linux/uhid.h> | 48 | #include <linux/uhid.h> |
43 | 49 | ||
44 | /* HID Report Desciptor | 50 | /* |
45 | * We emulate a basic 3 button mouse with wheel. This is the report-descriptor | 51 | * HID Report Desciptor |
46 | * as the kernel will parse it: | 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 | * Field(0) | 56 | * Field(0) |
50 | * Physical(GenericDesktop.Pointer) | 57 | * Physical(GenericDesktop.Pointer) |
51 | * Application(GenericDesktop.Mouse) | 58 | * Application(GenericDesktop.Mouse) |
52 | * Usage(3) | 59 | * Usage(3) |
53 | * Button.0001 | 60 | * Button.0001 |
54 | * Button.0002 | 61 | * Button.0002 |
55 | * Button.0003 | 62 | * Button.0003 |
56 | * Logical Minimum(0) | 63 | * Logical Minimum(0) |
57 | * Logical Maximum(1) | 64 | * Logical Maximum(1) |
58 | * Report Size(1) | 65 | * Report Size(1) |
59 | * Report Count(3) | 66 | * Report Count(3) |
60 | * Report Offset(0) | 67 | * Report Offset(0) |
61 | * Flags( Variable Absolute ) | 68 | * Flags( Variable Absolute ) |
62 | * Field(1) | 69 | * Field(1) |
63 | * Physical(GenericDesktop.Pointer) | 70 | * Physical(GenericDesktop.Pointer) |
64 | * Application(GenericDesktop.Mouse) | 71 | * Application(GenericDesktop.Mouse) |
65 | * Usage(3) | 72 | * Usage(3) |
66 | * GenericDesktop.X | 73 | * GenericDesktop.X |
67 | * GenericDesktop.Y | 74 | * GenericDesktop.Y |
68 | * GenericDesktop.Wheel | 75 | * GenericDesktop.Wheel |
69 | * Logical Minimum(-128) | 76 | * Logical Minimum(-128) |
70 | * Logical Maximum(127) | 77 | * Logical Maximum(127) |
71 | * Report Size(8) | 78 | * Report Size(8) |
72 | * Report Count(3) | 79 | * Report Count(3) |
73 | * Report Offset(8) | 80 | * Report Offset(8) |
74 | * Flags( Variable Relative ) | 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 | * This is the mapping that we expect: | 96 | * This is the mapping that we expect: |
77 | * Button.0001 ---> Key.LeftBtn | 97 | * Button.0001 ---> Key.LeftBtn |
78 | * Button.0002 ---> Key.RightBtn | 98 | * Button.0002 ---> Key.RightBtn |
79 | * Button.0003 ---> Key.MiddleBtn | 99 | * Button.0003 ---> Key.MiddleBtn |
80 | * GenericDesktop.X ---> Relative.X | 100 | * GenericDesktop.X ---> Relative.X |
81 | * GenericDesktop.Y ---> Relative.Y | 101 | * GenericDesktop.Y ---> Relative.Y |
82 | * GenericDesktop.Wheel ---> Relative.Wheel | 102 | * GenericDesktop.Wheel ---> Relative.Wheel |
103 | * LED.NumLock ---> LED.NumLock | ||
104 | * LED.CapsLock ---> LED.CapsLock | ||
105 | * LED.ScrollLock ---> LED.ScrollLock | ||
83 | * | 106 | * |
84 | * This information can be verified by reading /sys/kernel/debug/hid/<dev>/rdesc | 107 | * This information can be verified by reading /sys/kernel/debug/hid/<dev>/rdesc |
85 | * This file should print the same information as showed above. | 108 | * This file should print the same information as showed above. |
86 | */ | 109 | */ |
87 | 110 | ||
88 | static unsigned char rdesc[] = { | 111 | static unsigned char rdesc[] = { |
89 | 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, | 112 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ |
90 | 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, | 113 | 0x09, 0x02, /* USAGE (Mouse) */ |
91 | 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, | 114 | 0xa1, 0x01, /* COLLECTION (Application) */ |
92 | 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, | 115 | 0x09, 0x01, /* USAGE (Pointer) */ |
93 | 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, | 116 | 0xa1, 0x00, /* COLLECTION (Physical) */ |
94 | 0x15, 0x80, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, | 117 | 0x85, 0x01, /* REPORT_ID (1) */ |
95 | 0x81, 0x06, 0xc0, 0xc0, | 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 | static int uhid_write(int fd, const struct uhid_event *ev) | 158 | static int uhid_write(int fd, const struct uhid_event *ev) |
99 | { | 159 | { |
100 | ssize_t ret; | 160 | ssize_t ret; |
101 | 161 | ||
102 | ret = write(fd, ev, sizeof(*ev)); | 162 | ret = write(fd, ev, sizeof(*ev)); |
103 | if (ret < 0) { | 163 | if (ret < 0) { |
104 | fprintf(stderr, "Cannot write to uhid: %m\n"); | 164 | fprintf(stderr, "Cannot write to uhid: %m\n"); |
105 | return -errno; | 165 | return -errno; |
106 | } else if (ret != sizeof(*ev)) { | 166 | } else if (ret != sizeof(*ev)) { |
107 | fprintf(stderr, "Wrong size written to uhid: %ld != %lu\n", | 167 | fprintf(stderr, "Wrong size written to uhid: %ld != %lu\n", |
108 | ret, sizeof(ev)); | 168 | ret, sizeof(ev)); |
109 | return -EFAULT; | 169 | return -EFAULT; |
110 | } else { | 170 | } else { |
111 | return 0; | 171 | return 0; |
112 | } | 172 | } |
113 | } | 173 | } |
114 | 174 | ||
115 | static int create(int fd) | 175 | static int create(int fd) |
116 | { | 176 | { |
117 | struct uhid_event ev; | 177 | struct uhid_event ev; |
118 | 178 | ||
119 | memset(&ev, 0, sizeof(ev)); | 179 | memset(&ev, 0, sizeof(ev)); |
120 | ev.type = UHID_CREATE; | 180 | ev.type = UHID_CREATE; |
121 | strcpy((char*)ev.u.create.name, "test-uhid-device"); | 181 | strcpy((char*)ev.u.create.name, "test-uhid-device"); |
122 | ev.u.create.rd_data = rdesc; | 182 | ev.u.create.rd_data = rdesc; |
123 | ev.u.create.rd_size = sizeof(rdesc); | 183 | ev.u.create.rd_size = sizeof(rdesc); |
124 | ev.u.create.bus = BUS_USB; | 184 | ev.u.create.bus = BUS_USB; |
125 | ev.u.create.vendor = 0x15d9; | 185 | ev.u.create.vendor = 0x15d9; |
126 | ev.u.create.product = 0x0a37; | 186 | ev.u.create.product = 0x0a37; |
127 | ev.u.create.version = 0; | 187 | ev.u.create.version = 0; |
128 | ev.u.create.country = 0; | 188 | ev.u.create.country = 0; |
129 | 189 | ||
130 | return uhid_write(fd, &ev); | 190 | return uhid_write(fd, &ev); |
131 | } | 191 | } |
132 | 192 | ||
133 | static void destroy(int fd) | 193 | static void destroy(int fd) |
134 | { | 194 | { |
135 | struct uhid_event ev; | 195 | struct uhid_event ev; |
136 | 196 | ||
137 | memset(&ev, 0, sizeof(ev)); | 197 | memset(&ev, 0, sizeof(ev)); |
138 | ev.type = UHID_DESTROY; | 198 | ev.type = UHID_DESTROY; |
139 | 199 | ||
140 | uhid_write(fd, &ev); | 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 | static int event(int fd) | 224 | static int event(int fd) |
144 | { | 225 | { |
145 | struct uhid_event ev; | 226 | struct uhid_event ev; |
146 | ssize_t ret; | 227 | ssize_t ret; |
147 | 228 | ||
148 | memset(&ev, 0, sizeof(ev)); | 229 | memset(&ev, 0, sizeof(ev)); |
149 | ret = read(fd, &ev, sizeof(ev)); | 230 | ret = read(fd, &ev, sizeof(ev)); |
150 | if (ret == 0) { | 231 | if (ret == 0) { |
151 | fprintf(stderr, "Read HUP on uhid-cdev\n"); | 232 | fprintf(stderr, "Read HUP on uhid-cdev\n"); |
152 | return -EFAULT; | 233 | return -EFAULT; |
153 | } else if (ret < 0) { | 234 | } else if (ret < 0) { |
154 | fprintf(stderr, "Cannot read uhid-cdev: %m\n"); | 235 | fprintf(stderr, "Cannot read uhid-cdev: %m\n"); |
155 | return -errno; | 236 | return -errno; |
156 | } else if (ret != sizeof(ev)) { | 237 | } else if (ret != sizeof(ev)) { |
157 | fprintf(stderr, "Invalid size read from uhid-dev: %ld != %lu\n", | 238 | fprintf(stderr, "Invalid size read from uhid-dev: %ld != %lu\n", |
158 | ret, sizeof(ev)); | 239 | ret, sizeof(ev)); |
159 | return -EFAULT; | 240 | return -EFAULT; |
160 | } | 241 | } |
161 | 242 | ||
162 | switch (ev.type) { | 243 | switch (ev.type) { |
163 | case UHID_START: | 244 | case UHID_START: |
164 | fprintf(stderr, "UHID_START from uhid-dev\n"); | 245 | fprintf(stderr, "UHID_START from uhid-dev\n"); |
165 | break; | 246 | break; |
166 | case UHID_STOP: | 247 | case UHID_STOP: |
167 | fprintf(stderr, "UHID_STOP from uhid-dev\n"); | 248 | fprintf(stderr, "UHID_STOP from uhid-dev\n"); |
168 | break; | 249 | break; |
169 | case UHID_OPEN: | 250 | case UHID_OPEN: |
170 | fprintf(stderr, "UHID_OPEN from uhid-dev\n"); | 251 | fprintf(stderr, "UHID_OPEN from uhid-dev\n"); |
171 | break; | 252 | break; |
172 | case UHID_CLOSE: | 253 | case UHID_CLOSE: |
173 | fprintf(stderr, "UHID_CLOSE from uhid-dev\n"); | 254 | fprintf(stderr, "UHID_CLOSE from uhid-dev\n"); |
174 | break; | 255 | break; |
175 | case UHID_OUTPUT: | 256 | case UHID_OUTPUT: |
176 | fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); | 257 | fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); |
258 | handle_output(&ev); | ||
177 | break; | 259 | break; |
178 | case UHID_OUTPUT_EV: | 260 | case UHID_OUTPUT_EV: |
179 | fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); | 261 | fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); |
180 | break; | 262 | break; |
181 | default: | 263 | default: |
182 | fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); | 264 | fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); |
183 | } | 265 | } |
184 | 266 | ||
185 | return 0; | 267 | return 0; |
186 | } | 268 | } |
187 | 269 | ||
188 | static bool btn1_down; | 270 | static bool btn1_down; |
189 | static bool btn2_down; | 271 | static bool btn2_down; |
190 | static bool btn3_down; | 272 | static bool btn3_down; |
191 | static signed char abs_hor; | 273 | static signed char abs_hor; |
192 | static signed char abs_ver; | 274 | static signed char abs_ver; |
193 | static signed char wheel; | 275 | static signed char wheel; |
194 | 276 | ||
195 | static int send_event(int fd) | 277 | static int send_event(int fd) |
196 | { | 278 | { |
197 | struct uhid_event ev; | 279 | struct uhid_event ev; |
198 | 280 | ||
199 | memset(&ev, 0, sizeof(ev)); | 281 | memset(&ev, 0, sizeof(ev)); |
200 | ev.type = UHID_INPUT; | 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 | if (btn1_down) | 286 | if (btn1_down) |
204 | ev.u.input.data[0] |= 0x1; | 287 | ev.u.input.data[1] |= 0x1; |
205 | if (btn2_down) | 288 | if (btn2_down) |
206 | ev.u.input.data[0] |= 0x2; | 289 | ev.u.input.data[1] |= 0x2; |
207 | if (btn3_down) | 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; | 293 | ev.u.input.data[2] = abs_hor; |
211 | ev.u.input.data[2] = abs_ver; | 294 | ev.u.input.data[3] = abs_ver; |
212 | ev.u.input.data[3] = wheel; | 295 | ev.u.input.data[4] = wheel; |
213 | 296 | ||
214 | return uhid_write(fd, &ev); | 297 | return uhid_write(fd, &ev); |
215 | } | 298 | } |
216 | 299 | ||
217 | static int keyboard(int fd) | 300 | static int keyboard(int fd) |
218 | { | 301 | { |
219 | char buf[128]; | 302 | char buf[128]; |
220 | ssize_t ret, i; | 303 | ssize_t ret, i; |
221 | 304 | ||
222 | ret = read(STDIN_FILENO, buf, sizeof(buf)); | 305 | ret = read(STDIN_FILENO, buf, sizeof(buf)); |
223 | if (ret == 0) { | 306 | if (ret == 0) { |
224 | fprintf(stderr, "Read HUP on stdin\n"); | 307 | fprintf(stderr, "Read HUP on stdin\n"); |
225 | return -EFAULT; | 308 | return -EFAULT; |
226 | } else if (ret < 0) { | 309 | } else if (ret < 0) { |
227 | fprintf(stderr, "Cannot read stdin: %m\n"); | 310 | fprintf(stderr, "Cannot read stdin: %m\n"); |
228 | return -errno; | 311 | return -errno; |
229 | } | 312 | } |
230 | 313 | ||
231 | for (i = 0; i < ret; ++i) { | 314 | for (i = 0; i < ret; ++i) { |
232 | switch (buf[i]) { | 315 | switch (buf[i]) { |
233 | case '1': | 316 | case '1': |
234 | btn1_down = !btn1_down; | 317 | btn1_down = !btn1_down; |
235 | ret = send_event(fd); | 318 | ret = send_event(fd); |
236 | if (ret) | 319 | if (ret) |
237 | return ret; | 320 | return ret; |
238 | break; | 321 | break; |
239 | case '2': | 322 | case '2': |
240 | btn2_down = !btn2_down; | 323 | btn2_down = !btn2_down; |
241 | ret = send_event(fd); | 324 | ret = send_event(fd); |
242 | if (ret) | 325 | if (ret) |
243 | return ret; | 326 | return ret; |
244 | break; | 327 | break; |
245 | case '3': | 328 | case '3': |
246 | btn3_down = !btn3_down; | 329 | btn3_down = !btn3_down; |
247 | ret = send_event(fd); | 330 | ret = send_event(fd); |
248 | if (ret) | 331 | if (ret) |
249 | return ret; | 332 | return ret; |
250 | break; | 333 | break; |
251 | case 'a': | 334 | case 'a': |
252 | abs_hor = -20; | 335 | abs_hor = -20; |
253 | ret = send_event(fd); | 336 | ret = send_event(fd); |
254 | abs_hor = 0; | 337 | abs_hor = 0; |
255 | if (ret) | 338 | if (ret) |
256 | return ret; | 339 | return ret; |
257 | break; | 340 | break; |
258 | case 'd': | 341 | case 'd': |
259 | abs_hor = 20; | 342 | abs_hor = 20; |
260 | ret = send_event(fd); | 343 | ret = send_event(fd); |
261 | abs_hor = 0; | 344 | abs_hor = 0; |
262 | if (ret) | 345 | if (ret) |
263 | return ret; | 346 | return ret; |
264 | break; | 347 | break; |
265 | case 'w': | 348 | case 'w': |
266 | abs_ver = -20; | 349 | abs_ver = -20; |
267 | ret = send_event(fd); | 350 | ret = send_event(fd); |
268 | abs_ver = 0; | 351 | abs_ver = 0; |
269 | if (ret) | 352 | if (ret) |
270 | return ret; | 353 | return ret; |
271 | break; | 354 | break; |
272 | case 's': | 355 | case 's': |
273 | abs_ver = 20; | 356 | abs_ver = 20; |
274 | ret = send_event(fd); | 357 | ret = send_event(fd); |
275 | abs_ver = 0; | 358 | abs_ver = 0; |
276 | if (ret) | 359 | if (ret) |
277 | return ret; | 360 | return ret; |
278 | break; | 361 | break; |
279 | case 'r': | 362 | case 'r': |
280 | wheel = 1; | 363 | wheel = 1; |
281 | ret = send_event(fd); | 364 | ret = send_event(fd); |
282 | wheel = 0; | 365 | wheel = 0; |
283 | if (ret) | 366 | if (ret) |
284 | return ret; | 367 | return ret; |
285 | break; | 368 | break; |
286 | case 'f': | 369 | case 'f': |
287 | wheel = -1; | 370 | wheel = -1; |
288 | ret = send_event(fd); | 371 | ret = send_event(fd); |
289 | wheel = 0; | 372 | wheel = 0; |
290 | if (ret) | 373 | if (ret) |
291 | return ret; | 374 | return ret; |
292 | break; | 375 | break; |
293 | case 'q': | 376 | case 'q': |
294 | return -ECANCELED; | 377 | return -ECANCELED; |
295 | default: | 378 | default: |
296 | fprintf(stderr, "Invalid input: %c\n", buf[i]); | 379 | fprintf(stderr, "Invalid input: %c\n", buf[i]); |
297 | } | 380 | } |
298 | } | 381 | } |
299 | 382 | ||
300 | return 0; | 383 | return 0; |
301 | } | 384 | } |
302 | 385 | ||
303 | int main(int argc, char **argv) | 386 | int main(int argc, char **argv) |
304 | { | 387 | { |
305 | int fd; | 388 | int fd; |
306 | const char *path = "/dev/uhid"; | 389 | const char *path = "/dev/uhid"; |
307 | struct pollfd pfds[2]; | 390 | struct pollfd pfds[2]; |
308 | int ret; | 391 | int ret; |
309 | struct termios state; | 392 | struct termios state; |
310 | 393 | ||
311 | ret = tcgetattr(STDIN_FILENO, &state); | 394 | ret = tcgetattr(STDIN_FILENO, &state); |
312 | if (ret) { | 395 | if (ret) { |
313 | fprintf(stderr, "Cannot get tty state\n"); | 396 | fprintf(stderr, "Cannot get tty state\n"); |
314 | } else { | 397 | } else { |
315 | state.c_lflag &= ~ICANON; | 398 | state.c_lflag &= ~ICANON; |
316 | state.c_cc[VMIN] = 1; | 399 | state.c_cc[VMIN] = 1; |
317 | ret = tcsetattr(STDIN_FILENO, TCSANOW, &state); | 400 | ret = tcsetattr(STDIN_FILENO, TCSANOW, &state); |
318 | if (ret) | 401 | if (ret) |
319 | fprintf(stderr, "Cannot set tty state\n"); | 402 | fprintf(stderr, "Cannot set tty state\n"); |
320 | } | 403 | } |
321 | 404 | ||
322 | if (argc >= 2) { | 405 | if (argc >= 2) { |
323 | if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { | 406 | if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { |
324 | fprintf(stderr, "Usage: %s [%s]\n", argv[0], path); | 407 | fprintf(stderr, "Usage: %s [%s]\n", argv[0], path); |
325 | return EXIT_SUCCESS; | 408 | return EXIT_SUCCESS; |
326 | } else { | 409 | } else { |
327 | path = argv[1]; | 410 | path = argv[1]; |
328 | } | 411 | } |
329 | } | 412 | } |
330 | 413 | ||
331 | fprintf(stderr, "Open uhid-cdev %s\n", path); | 414 | fprintf(stderr, "Open uhid-cdev %s\n", path); |
332 | fd = open(path, O_RDWR | O_CLOEXEC); | 415 | fd = open(path, O_RDWR | O_CLOEXEC); |
333 | if (fd < 0) { | 416 | if (fd < 0) { |
334 | fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path); | 417 | fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path); |
335 | return EXIT_FAILURE; | 418 | return EXIT_FAILURE; |
336 | } | 419 | } |
337 | 420 | ||
338 | fprintf(stderr, "Create uhid device\n"); | 421 | fprintf(stderr, "Create uhid device\n"); |
339 | ret = create(fd); | 422 | ret = create(fd); |
340 | if (ret) { | 423 | if (ret) { |
341 | close(fd); | 424 | close(fd); |
342 | return EXIT_FAILURE; | 425 | return EXIT_FAILURE; |
343 | } | 426 | } |
344 | 427 | ||
345 | pfds[0].fd = STDIN_FILENO; | 428 | pfds[0].fd = STDIN_FILENO; |
346 | pfds[0].events = POLLIN; | 429 | pfds[0].events = POLLIN; |
347 | pfds[1].fd = fd; | 430 | pfds[1].fd = fd; |
348 | pfds[1].events = POLLIN; | 431 | pfds[1].events = POLLIN; |
349 | 432 | ||
350 | fprintf(stderr, "Press 'q' to quit...\n"); | 433 | fprintf(stderr, "Press 'q' to quit...\n"); |
351 | while (1) { | 434 | while (1) { |
352 | ret = poll(pfds, 2, -1); | 435 | ret = poll(pfds, 2, -1); |
353 | if (ret < 0) { | 436 | if (ret < 0) { |
354 | fprintf(stderr, "Cannot poll for fds: %m\n"); | 437 | fprintf(stderr, "Cannot poll for fds: %m\n"); |
355 | break; | 438 | break; |
356 | } | 439 | } |
357 | if (pfds[0].revents & POLLHUP) { | 440 | if (pfds[0].revents & POLLHUP) { |
358 | fprintf(stderr, "Received HUP on stdin\n"); | 441 | fprintf(stderr, "Received HUP on stdin\n"); |
359 | break; | 442 | break; |
360 | } | 443 | } |
361 | if (pfds[1].revents & POLLHUP) { | 444 | if (pfds[1].revents & POLLHUP) { |
362 | fprintf(stderr, "Received HUP on uhid-cdev\n"); | 445 | fprintf(stderr, "Received HUP on uhid-cdev\n"); |
363 | break; | 446 | break; |
364 | } | 447 | } |
365 | 448 | ||
366 | if (pfds[0].revents & POLLIN) { | 449 | if (pfds[0].revents & POLLIN) { |
367 | ret = keyboard(fd); | 450 | ret = keyboard(fd); |
368 | if (ret) | 451 | if (ret) |
369 | break; | 452 | break; |
370 | } | 453 | } |
371 | if (pfds[1].revents & POLLIN) { | 454 | if (pfds[1].revents & POLLIN) { |
372 | ret = event(fd); | 455 | ret = event(fd); |
373 | if (ret) | 456 | if (ret) |
374 | break; | 457 | break; |
375 | } | 458 | } |
376 | } | 459 | } |
377 | 460 | ||
378 | fprintf(stderr, "Destroy uhid device\n"); | 461 | fprintf(stderr, "Destroy uhid device\n"); |
379 | destroy(fd); | 462 | destroy(fd); |
380 | return EXIT_SUCCESS; | 463 | return EXIT_SUCCESS; |
381 | } | 464 | } |
382 | 465 |