Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
uhid-example.c
Go to the documentation of this file.
1 /*
2  * UHID Example
3  *
4  * Copyright (c) 2012 David Herrmann <[email protected]>
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 }