linux/samples/uhid/uhid-example.c
<<
>>
Prefs
   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
  88static 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
  98static 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
 115static 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
 133static 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
 143static 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
 188static bool btn1_down;
 189static bool btn2_down;
 190static bool btn3_down;
 191static signed char abs_hor;
 192static signed char abs_ver;
 193static signed char wheel;
 194
 195static 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
 217static 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
 303int 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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.