linux/Documentation/usb/gadget_hid.txt
<<
>>
Prefs
   1
   2                     Linux USB HID gadget driver
   3
   4Introduction
   5
   6        The HID Gadget driver provides emulation of USB Human Interface
   7        Devices (HID). The basic HID handling is done in the kernel,
   8        and HID reports can be sent/received through I/O on the
   9        /dev/hidgX character devices.
  10
  11        For more details about HID, see the developer page on
  12        http://www.usb.org/developers/hidpage/
  13
  14Configuration
  15
  16        g_hid is a platform driver, so to use it you need to add
  17        struct platform_device(s) to your platform code defining the
  18        HID function descriptors you want to use - E.G. something
  19        like:
  20
  21#include <linux/platform_device.h>
  22#include <linux/usb/g_hid.h>
  23
  24/* hid descriptor for a keyboard */
  25static struct hidg_func_descriptor my_hid_data = {
  26        .subclass               = 0, /* No subclass */
  27        .protocol               = 1, /* Keyboard */
  28        .report_length          = 8,
  29        .report_desc_length     = 63,
  30        .report_desc            = {
  31                0x05, 0x01,     /* USAGE_PAGE (Generic Desktop)           */
  32                0x09, 0x06,     /* USAGE (Keyboard)                       */
  33                0xa1, 0x01,     /* COLLECTION (Application)               */
  34                0x05, 0x07,     /*   USAGE_PAGE (Keyboard)                */
  35                0x19, 0xe0,     /*   USAGE_MINIMUM (Keyboard LeftControl) */
  36                0x29, 0xe7,     /*   USAGE_MAXIMUM (Keyboard Right GUI)   */
  37                0x15, 0x00,     /*   LOGICAL_MINIMUM (0)                  */
  38                0x25, 0x01,     /*   LOGICAL_MAXIMUM (1)                  */
  39                0x75, 0x01,     /*   REPORT_SIZE (1)                      */
  40                0x95, 0x08,     /*   REPORT_COUNT (8)                     */
  41                0x81, 0x02,     /*   INPUT (Data,Var,Abs)                 */
  42                0x95, 0x01,     /*   REPORT_COUNT (1)                     */
  43                0x75, 0x08,     /*   REPORT_SIZE (8)                      */
  44                0x81, 0x03,     /*   INPUT (Cnst,Var,Abs)                 */
  45                0x95, 0x05,     /*   REPORT_COUNT (5)                     */
  46                0x75, 0x01,     /*   REPORT_SIZE (1)                      */
  47                0x05, 0x08,     /*   USAGE_PAGE (LEDs)                    */
  48                0x19, 0x01,     /*   USAGE_MINIMUM (Num Lock)             */
  49                0x29, 0x05,     /*   USAGE_MAXIMUM (Kana)                 */
  50                0x91, 0x02,     /*   OUTPUT (Data,Var,Abs)                */
  51                0x95, 0x01,     /*   REPORT_COUNT (1)                     */
  52                0x75, 0x03,     /*   REPORT_SIZE (3)                      */
  53                0x91, 0x03,     /*   OUTPUT (Cnst,Var,Abs)                */
  54                0x95, 0x06,     /*   REPORT_COUNT (6)                     */
  55                0x75, 0x08,     /*   REPORT_SIZE (8)                      */
  56                0x15, 0x00,     /*   LOGICAL_MINIMUM (0)                  */
  57                0x25, 0x65,     /*   LOGICAL_MAXIMUM (101)                */
  58                0x05, 0x07,     /*   USAGE_PAGE (Keyboard)                */
  59                0x19, 0x00,     /*   USAGE_MINIMUM (Reserved)             */
  60                0x29, 0x65,     /*   USAGE_MAXIMUM (Keyboard Application) */
  61                0x81, 0x00,     /*   INPUT (Data,Ary,Abs)                 */
  62                0xc0            /* END_COLLECTION                         */
  63        }
  64};
  65
  66static struct platform_device my_hid = {
  67        .name                   = "hidg",
  68        .id                     = 0,
  69        .num_resources          = 0,
  70        .resource               = 0,
  71        .dev.platform_data      = &my_hid_data,
  72};
  73
  74        You can add as many HID functions as you want, only limited by
  75        the amount of interrupt endpoints your gadget driver supports.
  76
  77Send and receive HID reports
  78
  79        HID reports can be sent/received using read/write on the
  80        /dev/hidgX character devices. See below for an example program
  81        to do this.
  82
  83        hid_gadget_test is a small interactive program to test the HID
  84        gadget driver. To use, point it at a hidg device and set the
  85        device type (keyboard / mouse / joystick) - E.G.:
  86
  87                # hid_gadget_test /dev/hidg0 keyboard
  88
  89        You are now in the prompt of hid_gadget_test. You can type any
  90        combination of options and values. Available options and
  91        values are listed at program start. In keyboard mode you can
  92        send up to six values.
  93
  94        For example type: g i s t r --left-shift
  95
  96        Hit return and the corresponding report will be sent by the
  97        HID gadget.
  98
  99        Another interesting example is the caps lock test. Type
 100        --caps-lock and hit return. A report is then sent by the
 101        gadget and you should receive the host answer, corresponding
 102        to the caps lock LED status.
 103
 104                --caps-lock
 105                recv report:2
 106
 107        With this command:
 108
 109                # hid_gadget_test /dev/hidg1 mouse
 110
 111        You can test the mouse emulation. Values are two signed numbers.
 112
 113
 114Sample code
 115
 116/* hid_gadget_test */
 117
 118#include <pthread.h>
 119#include <string.h>
 120#include <stdio.h>
 121#include <ctype.h>
 122#include <fcntl.h>
 123#include <errno.h>
 124#include <stdio.h>
 125#include <stdlib.h>
 126#include <unistd.h>
 127
 128#define BUF_LEN 512
 129
 130struct options {
 131        const char    *opt;
 132        unsigned char val;
 133};
 134
 135static struct options kmod[] = {
 136        {.opt = "--left-ctrl",          .val = 0x01},
 137        {.opt = "--right-ctrl",         .val = 0x10},
 138        {.opt = "--left-shift",         .val = 0x02},
 139        {.opt = "--right-shift",        .val = 0x20},
 140        {.opt = "--left-alt",           .val = 0x04},
 141        {.opt = "--right-alt",          .val = 0x40},
 142        {.opt = "--left-meta",          .val = 0x08},
 143        {.opt = "--right-meta",         .val = 0x80},
 144        {.opt = NULL}
 145};
 146
 147static struct options kval[] = {
 148        {.opt = "--return",     .val = 0x28},
 149        {.opt = "--esc",        .val = 0x29},
 150        {.opt = "--bckspc",     .val = 0x2a},
 151        {.opt = "--tab",        .val = 0x2b},
 152        {.opt = "--spacebar",   .val = 0x2c},
 153        {.opt = "--caps-lock",  .val = 0x39},
 154        {.opt = "--f1",         .val = 0x3a},
 155        {.opt = "--f2",         .val = 0x3b},
 156        {.opt = "--f3",         .val = 0x3c},
 157        {.opt = "--f4",         .val = 0x3d},
 158        {.opt = "--f5",         .val = 0x3e},
 159        {.opt = "--f6",         .val = 0x3f},
 160        {.opt = "--f7",         .val = 0x40},
 161        {.opt = "--f8",         .val = 0x41},
 162        {.opt = "--f9",         .val = 0x42},
 163        {.opt = "--f10",        .val = 0x43},
 164        {.opt = "--f11",        .val = 0x44},
 165        {.opt = "--f12",        .val = 0x45},
 166        {.opt = "--insert",     .val = 0x49},
 167        {.opt = "--home",       .val = 0x4a},
 168        {.opt = "--pageup",     .val = 0x4b},
 169        {.opt = "--del",        .val = 0x4c},
 170        {.opt = "--end",        .val = 0x4d},
 171        {.opt = "--pagedown",   .val = 0x4e},
 172        {.opt = "--right",      .val = 0x4f},
 173        {.opt = "--left",       .val = 0x50},
 174        {.opt = "--down",       .val = 0x51},
 175        {.opt = "--kp-enter",   .val = 0x58},
 176        {.opt = "--up",         .val = 0x52},
 177        {.opt = "--num-lock",   .val = 0x53},
 178        {.opt = NULL}
 179};
 180
 181int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold)
 182{
 183        char *tok = strtok(buf, " ");
 184        int key = 0;
 185        int i = 0;
 186
 187        for (; tok != NULL; tok = strtok(NULL, " ")) {
 188
 189                if (strcmp(tok, "--quit") == 0)
 190                        return -1;
 191
 192                if (strcmp(tok, "--hold") == 0) {
 193                        *hold = 1;
 194                        continue;
 195                }
 196
 197                if (key < 6) {
 198                        for (i = 0; kval[i].opt != NULL; i++)
 199                                if (strcmp(tok, kval[i].opt) == 0) {
 200                                        report[2 + key++] = kval[i].val;
 201                                        break;
 202                                }
 203                        if (kval[i].opt != NULL)
 204                                continue;
 205                }
 206
 207                if (key < 6)
 208                        if (islower(tok[0])) {
 209                                report[2 + key++] = (tok[0] - ('a' - 0x04));
 210                                continue;
 211                        }
 212
 213                for (i = 0; kmod[i].opt != NULL; i++)
 214                        if (strcmp(tok, kmod[i].opt) == 0) {
 215                                report[0] = report[0] | kmod[i].val;
 216                                break;
 217                        }
 218                if (kmod[i].opt != NULL)
 219                        continue;
 220
 221                if (key < 6)
 222                        fprintf(stderr, "unknown option: %s\n", tok);
 223        }
 224        return 8;
 225}
 226
 227static struct options mmod[] = {
 228        {.opt = "--b1", .val = 0x01},
 229        {.opt = "--b2", .val = 0x02},
 230        {.opt = "--b3", .val = 0x04},
 231        {.opt = NULL}
 232};
 233
 234int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold)
 235{
 236        char *tok = strtok(buf, " ");
 237        int mvt = 0;
 238        int i = 0;
 239        for (; tok != NULL; tok = strtok(NULL, " ")) {
 240
 241                if (strcmp(tok, "--quit") == 0)
 242                        return -1;
 243
 244                if (strcmp(tok, "--hold") == 0) {
 245                        *hold = 1;
 246                        continue;
 247                }
 248
 249                for (i = 0; mmod[i].opt != NULL; i++)
 250                        if (strcmp(tok, mmod[i].opt) == 0) {
 251                                report[0] = report[0] | mmod[i].val;
 252                                break;
 253                        }
 254                if (mmod[i].opt != NULL)
 255                        continue;
 256
 257                if (!(tok[0] == '-' && tok[1] == '-') && mvt < 2) {
 258                        errno = 0;
 259                        report[1 + mvt++] = (char)strtol(tok, NULL, 0);
 260                        if (errno != 0) {
 261                                fprintf(stderr, "Bad value:'%s'\n", tok);
 262                                report[1 + mvt--] = 0;
 263                        }
 264                        continue;
 265                }
 266
 267                fprintf(stderr, "unknown option: %s\n", tok);
 268        }
 269        return 3;
 270}
 271
 272static struct options jmod[] = {
 273        {.opt = "--b1",         .val = 0x10},
 274        {.opt = "--b2",         .val = 0x20},
 275        {.opt = "--b3",         .val = 0x40},
 276        {.opt = "--b4",         .val = 0x80},
 277        {.opt = "--hat1",       .val = 0x00},
 278        {.opt = "--hat2",       .val = 0x01},
 279        {.opt = "--hat3",       .val = 0x02},
 280        {.opt = "--hat4",       .val = 0x03},
 281        {.opt = "--hatneutral", .val = 0x04},
 282        {.opt = NULL}
 283};
 284
 285int joystick_fill_report(char report[8], char buf[BUF_LEN], int *hold)
 286{
 287        char *tok = strtok(buf, " ");
 288        int mvt = 0;
 289        int i = 0;
 290
 291        *hold = 1;
 292
 293        /* set default hat position: neutral */
 294        report[3] = 0x04;
 295
 296        for (; tok != NULL; tok = strtok(NULL, " ")) {
 297
 298                if (strcmp(tok, "--quit") == 0)
 299                        return -1;
 300
 301                for (i = 0; jmod[i].opt != NULL; i++)
 302                        if (strcmp(tok, jmod[i].opt) == 0) {
 303                                report[3] = (report[3] & 0xF0) | jmod[i].val;
 304                                break;
 305                        }
 306                if (jmod[i].opt != NULL)
 307                        continue;
 308
 309                if (!(tok[0] == '-' && tok[1] == '-') && mvt < 3) {
 310                        errno = 0;
 311                        report[mvt++] = (char)strtol(tok, NULL, 0);
 312                        if (errno != 0) {
 313                                fprintf(stderr, "Bad value:'%s'\n", tok);
 314                                report[mvt--] = 0;
 315                        }
 316                        continue;
 317                }
 318
 319                fprintf(stderr, "unknown option: %s\n", tok);
 320        }
 321        return 4;
 322}
 323
 324void print_options(char c)
 325{
 326        int i = 0;
 327
 328        if (c == 'k') {
 329                printf("        keyboard options:\n"
 330                       "                --hold\n");
 331                for (i = 0; kmod[i].opt != NULL; i++)
 332                        printf("\t\t%s\n", kmod[i].opt);
 333                printf("\n      keyboard values:\n"
 334                       "                [a-z] or\n");
 335                for (i = 0; kval[i].opt != NULL; i++)
 336                        printf("\t\t%-8s%s", kval[i].opt, i % 2 ? "\n" : "");
 337                printf("\n");
 338        } else if (c == 'm') {
 339                printf("        mouse options:\n"
 340                       "                --hold\n");
 341                for (i = 0; mmod[i].opt != NULL; i++)
 342                        printf("\t\t%s\n", mmod[i].opt);
 343                printf("\n      mouse values:\n"
 344                       "                Two signed numbers\n"
 345                       "--quit to close\n");
 346        } else {
 347                printf("        joystick options:\n");
 348                for (i = 0; jmod[i].opt != NULL; i++)
 349                        printf("\t\t%s\n", jmod[i].opt);
 350                printf("\n      joystick values:\n"
 351                       "                three signed numbers\n"
 352                       "--quit to close\n");
 353        }
 354}
 355
 356int main(int argc, const char *argv[])
 357{
 358        const char *filename = NULL;
 359        int fd = 0;
 360        char buf[BUF_LEN];
 361        int cmd_len;
 362        char report[8];
 363        int to_send = 8;
 364        int hold = 0;
 365        fd_set rfds;
 366        int retval, i;
 367
 368        if (argc < 3) {
 369                fprintf(stderr, "Usage: %s devname mouse|keyboard|joystick\n",
 370                        argv[0]);
 371                return 1;
 372        }
 373
 374        if (argv[2][0] != 'k' && argv[2][0] != 'm' && argv[2][0] != 'j')
 375          return 2;
 376
 377        filename = argv[1];
 378
 379        if ((fd = open(filename, O_RDWR, 0666)) == -1) {
 380                perror(filename);
 381                return 3;
 382        }
 383
 384        print_options(argv[2][0]);
 385
 386        while (42) {
 387
 388                FD_ZERO(&rfds);
 389                FD_SET(STDIN_FILENO, &rfds);
 390                FD_SET(fd, &rfds);
 391
 392                retval = select(fd + 1, &rfds, NULL, NULL, NULL);
 393                if (retval == -1 && errno == EINTR)
 394                        continue;
 395                if (retval < 0) {
 396                        perror("select()");
 397                        return 4;
 398                }
 399
 400                if (FD_ISSET(fd, &rfds)) {
 401                        cmd_len = read(fd, buf, BUF_LEN - 1);
 402                        printf("recv report:");
 403                        for (i = 0; i < cmd_len; i++)
 404                                printf(" %02x", buf[i]);
 405                        printf("\n");
 406                }
 407
 408                if (FD_ISSET(STDIN_FILENO, &rfds)) {
 409                        memset(report, 0x0, sizeof(report));
 410                        cmd_len = read(STDIN_FILENO, buf, BUF_LEN - 1);
 411
 412                        if (cmd_len == 0)
 413                                break;
 414
 415                        buf[cmd_len - 1] = '\0';
 416                        hold = 0;
 417
 418                        memset(report, 0x0, sizeof(report));
 419                        if (argv[2][0] == 'k')
 420                                to_send = keyboard_fill_report(report, buf, &hold);
 421                        else if (argv[2][0] == 'm')
 422                                to_send = mouse_fill_report(report, buf, &hold);
 423                        else
 424                                to_send = joystick_fill_report(report, buf, &hold);
 425
 426                        if (to_send == -1)
 427                                break;
 428
 429                        if (write(fd, report, to_send) != to_send) {
 430                                perror(filename);
 431                                return 5;
 432                        }
 433                        if (!hold) {
 434                                memset(report, 0x0, sizeof(report));
 435                                if (write(fd, report, to_send) != to_send) {
 436                                        perror(filename);
 437                                        return 6;
 438                                }
 439                        }
 440                }
 441        }
 442
 443        close(fd);
 444        return 0;
 445}
 446
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.