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