brintos

brintos / linux-shallow public Read only

0
0
Text · 11.6 KiB · e623416 Raw
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