xref: /openbmc/linux/samples/uhid/uhid-example.c (revision 3b64b188)
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 }
382