blob: e623416de4f1bae24ed12ae0f7fe0463b291ab5b [file] [log] [blame]
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -03001===========================
2Linux USB HID gadget driver
3===========================
Fabien Chouteau71adf112010-04-08 09:31:15 +02004
5Introduction
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -03006============
Fabien Chouteau71adf112010-04-08 09:31:15 +02007
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -03008The HID Gadget driver provides emulation of USB Human Interface
9Devices (HID). The basic HID handling is done in the kernel,
10and HID reports can be sent/received through I/O on the
11/dev/hidgX character devices.
Fabien Chouteau71adf112010-04-08 09:31:15 +020012
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030013For more details about HID, see the developer page on
Alexander A. Klimovffeb1e92020-07-19 18:09:10 +020014https://www.usb.org/developers/hidpage/
Fabien Chouteau71adf112010-04-08 09:31:15 +020015
16Configuration
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030017=============
Fabien Chouteau71adf112010-04-08 09:31:15 +020018
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030019g_hid is a platform driver, so to use it you need to add
20struct platform_device(s) to your platform code defining the
21HID function descriptors you want to use - E.G. something
22like::
Fabien Chouteau71adf112010-04-08 09:31:15 +020023
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030024 #include <linux/platform_device.h>
25 #include <linux/usb/g_hid.h>
Fabien Chouteau71adf112010-04-08 09:31:15 +020026
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030027 /* hid descriptor for a keyboard */
28 static struct hidg_func_descriptor my_hid_data = {
Fabien Chouteau71adf112010-04-08 09:31:15 +020029 .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 }
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030067 };
Fabien Chouteau71adf112010-04-08 09:31:15 +020068
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030069 static struct platform_device my_hid = {
Fabien Chouteau71adf112010-04-08 09:31:15 +020070 .name = "hidg",
71 .id = 0,
72 .num_resources = 0,
73 .resource = 0,
74 .dev.platform_data = &my_hid_data,
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030075 };
Fabien Chouteau71adf112010-04-08 09:31:15 +020076
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030077You can add as many HID functions as you want, only limited by
78the amount of interrupt endpoints your gadget driver supports.
Fabien Chouteau71adf112010-04-08 09:31:15 +020079
Andrzej Pietrasiewicz21a94762014-11-06 11:12:03 +010080Configuration with configfs
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030081===========================
Andrzej Pietrasiewicz21a94762014-11-06 11:12:03 +010082
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030083Instead of adding fake platform devices and drivers in order to pass
84some data to the kernel, if HID is a part of a gadget composed with
85configfs the hidg_func_descriptor.report_desc is passed to the kernel
86by writing the appropriate stream of bytes to a configfs attribute.
Andrzej Pietrasiewicz21a94762014-11-06 11:12:03 +010087
Fabien Chouteau71adf112010-04-08 09:31:15 +020088Send and receive HID reports
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030089============================
Fabien Chouteau71adf112010-04-08 09:31:15 +020090
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030091HID reports can be sent/received using read/write on the
92/dev/hidgX character devices. See below for an example program
93to do this.
Fabien Chouteau71adf112010-04-08 09:31:15 +020094
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030095hid_gadget_test is a small interactive program to test the HID
96gadget driver. To use, point it at a hidg device and set the
97device type (keyboard / mouse / joystick) - E.G.::
Fabien Chouteau71adf112010-04-08 09:31:15 +020098
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -030099 # hid_gadget_test /dev/hidg0 keyboard
Fabien Chouteau71adf112010-04-08 09:31:15 +0200100
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300101You are now in the prompt of hid_gadget_test. You can type any
102combination of options and values. Available options and
103values are listed at program start. In keyboard mode you can
104send up to six values.
Fabien Chouteau71adf112010-04-08 09:31:15 +0200105
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300106For example type: g i s t r --left-shift
Fabien Chouteau71adf112010-04-08 09:31:15 +0200107
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300108Hit return and the corresponding report will be sent by the
109HID gadget.
Fabien Chouteau71adf112010-04-08 09:31:15 +0200110
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300111Another interesting example is the caps lock test. Type
112--caps-lock and hit return. A report is then sent by the
113gadget and you should receive the host answer, corresponding
114to the caps lock LED status::
Fabien Chouteau71adf112010-04-08 09:31:15 +0200115
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300116 --caps-lock
117 recv report:2
Fabien Chouteau71adf112010-04-08 09:31:15 +0200118
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300119With this command::
Fabien Chouteau71adf112010-04-08 09:31:15 +0200120
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300121 # hid_gadget_test /dev/hidg1 mouse
Fabien Chouteau71adf112010-04-08 09:31:15 +0200122
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300123You can test the mouse emulation. Values are two signed numbers.
Fabien Chouteau71adf112010-04-08 09:31:15 +0200124
125
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300126Sample code::
Fabien Chouteau71adf112010-04-08 09:31:15 +0200127
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300128 /* hid_gadget_test */
Fabien Chouteau71adf112010-04-08 09:31:15 +0200129
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300130 #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>
Fabien Chouteau71adf112010-04-08 09:31:15 +0200139
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300140 #define BUF_LEN 512
Fabien Chouteau71adf112010-04-08 09:31:15 +0200141
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300142 struct options {
Fabien Chouteau71adf112010-04-08 09:31:15 +0200143 const char *opt;
144 unsigned char val;
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300145 };
Fabien Chouteau71adf112010-04-08 09:31:15 +0200146
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300147 static struct options kmod[] = {
Fabien Chouteau71adf112010-04-08 09:31:15 +0200148 {.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}
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300157 };
Fabien Chouteau71adf112010-04-08 09:31:15 +0200158
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300159 static struct options kval[] = {
Fabien Chouteau71adf112010-04-08 09:31:15 +0200160 {.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}
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300191 };
Fabien Chouteau71adf112010-04-08 09:31:15 +0200192
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300193 int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold)
194 {
Fabien Chouteau71adf112010-04-08 09:31:15 +0200195 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;
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300237 }
Fabien Chouteau71adf112010-04-08 09:31:15 +0200238
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300239 static struct options mmod[] = {
Fabien Chouteau71adf112010-04-08 09:31:15 +0200240 {.opt = "--b1", .val = 0x01},
241 {.opt = "--b2", .val = 0x02},
242 {.opt = "--b3", .val = 0x04},
243 {.opt = NULL}
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300244 };
Fabien Chouteau71adf112010-04-08 09:31:15 +0200245
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300246 int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold)
247 {
Fabien Chouteau71adf112010-04-08 09:31:15 +0200248 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;
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300282 }
Fabien Chouteau71adf112010-04-08 09:31:15 +0200283
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300284 static struct options jmod[] = {
Fabien Chouteau71adf112010-04-08 09:31:15 +0200285 {.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}
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300295 };
Fabien Chouteau71adf112010-04-08 09:31:15 +0200296
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300297 int joystick_fill_report(char report[8], char buf[BUF_LEN], int *hold)
298 {
Fabien Chouteau71adf112010-04-08 09:31:15 +0200299 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;
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300334 }
Fabien Chouteau71adf112010-04-08 09:31:15 +0200335
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300336 void print_options(char c)
337 {
Fabien Chouteau71adf112010-04-08 09:31:15 +0200338 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 }
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300366 }
Fabien Chouteau71adf112010-04-08 09:31:15 +0200367
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300368 int main(int argc, const char *argv[])
369 {
Fabien Chouteau71adf112010-04-08 09:31:15 +0200370 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 else
436 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;
Mauro Carvalho Chehabd80b5002019-04-15 23:56:01 -0300457 }