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