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