Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

David Herrmann and committed by
Jiri Kosina
5148fa52 d99b8bad

+391
+10
samples/uhid/Makefile
··· 1 + # kbuild trick to avoid linker error. Can be omitted if a module is built. 2 + obj- := dummy.o 3 + 4 + # List of programs to build 5 + hostprogs-y := uhid-example 6 + 7 + # Tell kbuild to always build the programs 8 + always := $(hostprogs-y) 9 + 10 + HOSTCFLAGS_uhid-example.o += -I$(objtree)/usr/include
+381
samples/uhid/uhid-example.c
··· 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 + }