xref: /freebsd/usr.bin/usbhidctl/usbhid.c (revision f4b37ed0f8b307b1f3f0f630ca725d68f1dff30d)
1 /*	$NetBSD: usbhid.c,v 1.14 2000/07/03 02:51:37 matt Exp $	*/
2 /*	$FreeBSD$ */
3 
4 /*
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Lennart Augustsson (augustss@netbsd.org).
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <err.h>
40 #include <ctype.h>
41 #include <errno.h>
42 #include <usbhid.h>
43 #include <dev/usb/usbhid.h>
44 
45 static struct variable {
46 	char *name;
47 	int instance;
48 	int val;
49 	struct hid_item h;
50 	struct variable *next;
51 } *vars;
52 
53 static int verbose = 0;
54 static int noname = 0;
55 static int hexdump = 0;
56 static int wflag = 0;
57 static int zflag = 0;
58 
59 static void usage(void);
60 static void dumpitem(const char *label, struct hid_item *h);
61 static void dumpitems(report_desc_t r);
62 static void prdata(u_char *buf, struct hid_item *h);
63 static void dumpdata(int f, report_desc_t r, int loop);
64 static void writedata(int f, report_desc_t r);
65 
66 static void
67 parceargs(report_desc_t r, int all, int nnames, char **names)
68 {
69 	struct hid_data *d;
70 	struct hid_item h;
71 	char colls[1000];
72 	char hname[1000], *tmp1, *tmp2;
73 	struct variable *var, **pnext;
74 	int i, instance, cp, t;
75 
76 	pnext = &vars;
77 	if (all) {
78 		if (wflag)
79 			errx(1, "Must not specify -w to read variables");
80 		cp = 0;
81 		for (d = hid_start_parse(r,
82 		    1<<hid_input | 1<<hid_output | 1<<hid_feature, -1);
83 		    hid_get_item(d, &h); ) {
84 			if (h.kind == hid_collection) {
85 				cp += sprintf(&colls[cp], "%s%s:%s",
86 				    cp != 0 ? "." : "",
87 				    hid_usage_page(HID_PAGE(h.usage)),
88 				    hid_usage_in_page(h.usage));
89 			} else if (h.kind == hid_endcollection) {
90 				tmp1 = strrchr(colls, '.');
91 				if (tmp1 != NULL) {
92 					cp -= strlen(tmp1);
93 					tmp1[0] = 0;
94 				} else {
95 					cp = 0;
96 					colls[0] = 0;
97 				}
98 			}
99 			if ((h.kind != hid_input && h.kind != hid_output &&
100 			    h.kind != hid_feature) || (h.flags & HIO_CONST))
101 				continue;
102 			var = malloc(sizeof(*var));
103 			memset(var, 0, sizeof(*var));
104 			asprintf(&var->name, "%s%s%s:%s",
105 			    colls, colls[0] != 0 ? "." : "",
106 			    hid_usage_page(HID_PAGE(h.usage)),
107 			    hid_usage_in_page(h.usage));
108 			var->h = h;
109 			*pnext = var;
110 			pnext = &var->next;
111 		}
112 		hid_end_parse(d);
113 		return;
114 	}
115 	for (i = 0; i < nnames; i++) {
116 		var = malloc(sizeof(*var));
117 		memset(var, 0, sizeof(*var));
118 		tmp1 = tmp2 = strdup(names[i]);
119 		strsep(&tmp2, "=");
120 		var->name = strsep(&tmp1, "#");
121 		if (tmp1 != NULL)
122 			var->instance = atoi(tmp1);
123 		if (tmp2 != NULL) {
124 			if (!wflag)
125 				errx(1, "Must specify -w to write variables");
126 			var->val = atoi(tmp2);
127 		} else
128 			if (wflag)
129 				errx(1, "Must not specify -w to read variables");
130 		*pnext = var;
131 		pnext = &var->next;
132 
133 		instance = 0;
134 		cp = 0;
135 		for (d = hid_start_parse(r,
136 		    1<<hid_input | 1<<hid_output | 1<<hid_feature, -1);
137 		    hid_get_item(d, &h); ) {
138 			if (h.kind == hid_collection) {
139 				cp += sprintf(&colls[cp], "%s%s:%s",
140 				    cp != 0 ? "." : "",
141 				    hid_usage_page(HID_PAGE(h.usage)),
142 				    hid_usage_in_page(h.usage));
143 			} else if (h.kind == hid_endcollection) {
144 				tmp1 = strrchr(colls, '.');
145 				if (tmp1 != NULL) {
146 					cp -= strlen(tmp1);
147 					tmp1[0] = 0;
148 				} else {
149 					cp = 0;
150 					colls[0] = 0;
151 				}
152 			}
153 			if ((h.kind != hid_input && h.kind != hid_output &&
154 			    h.kind != hid_feature) || (h.flags & HIO_CONST))
155 				continue;
156 			snprintf(hname, sizeof(hname), "%s%s%s:%s",
157 			    colls, colls[0] != 0 ? "." : "",
158 			    hid_usage_page(HID_PAGE(h.usage)),
159 			    hid_usage_in_page(h.usage));
160 			t = strlen(hname) - strlen(var->name);
161 			if (t > 0) {
162 				if (strcmp(hname + t, var->name) != 0)
163 					continue;
164 				if (hname[t - 1] != '.')
165 					continue;
166 			} else if (strcmp(hname, var->name) != 0)
167 				continue;
168 			if (var->instance != instance++)
169 				continue;
170 			var->h = h;
171 			break;
172 		}
173 		hid_end_parse(d);
174 		if (var->h.usage == 0)
175 			errx(1, "Unknown item '%s'", var->name);
176 	}
177 }
178 
179 static void
180 usage(void)
181 {
182 
183 	fprintf(stderr,
184                 "usage: %s -f device "
185                 "[-l] [-n] [-r] [-t tablefile] [-v] [-x] [-z] name ...\n",
186                 getprogname());
187 	fprintf(stderr,
188                 "       %s -f device "
189                 "[-l] [-n] [-r] [-t tablefile] [-v] [-x] [-z] -a\n",
190                 getprogname());
191 	fprintf(stderr,
192                 "       %s -f device "
193                 "[-t tablefile] [-v] [-z] -w name=value\n",
194                 getprogname());
195 	exit(1);
196 }
197 
198 static void
199 dumpitem(const char *label, struct hid_item *h)
200 {
201 	if ((h->flags & HIO_CONST) && !verbose)
202 		return;
203 	printf("%s rid=%d size=%d count=%d page=%s usage=%s%s%s", label,
204 	       h->report_ID, h->report_size, h->report_count,
205 	       hid_usage_page(HID_PAGE(h->usage)),
206 	       hid_usage_in_page(h->usage),
207 	       h->flags & HIO_CONST ? " Const" : "",
208 	       h->flags & HIO_VARIABLE ? "" : " Array");
209 	printf(", logical range %d..%d",
210 	       h->logical_minimum, h->logical_maximum);
211 	if (h->physical_minimum != h->physical_maximum)
212 		printf(", physical range %d..%d",
213 		       h->physical_minimum, h->physical_maximum);
214 	if (h->unit)
215 		printf(", unit=0x%02x exp=%d", h->unit, h->unit_exponent);
216 	printf("\n");
217 }
218 
219 static const char *
220 hid_collection_type(int32_t type)
221 {
222 	static char num[8];
223 
224 	switch (type) {
225 	case 0: return ("Physical");
226 	case 1: return ("Application");
227 	case 2: return ("Logical");
228 	case 3: return ("Report");
229 	case 4: return ("Named_Array");
230 	case 5: return ("Usage_Switch");
231 	case 6: return ("Usage_Modifier");
232 	}
233 	snprintf(num, sizeof(num), "0x%02x", type);
234 	return (num);
235 }
236 
237 static void
238 dumpitems(report_desc_t r)
239 {
240 	struct hid_data *d;
241 	struct hid_item h;
242 	int size;
243 
244 	for (d = hid_start_parse(r, ~0, -1); hid_get_item(d, &h); ) {
245 		switch (h.kind) {
246 		case hid_collection:
247 			printf("Collection type=%s page=%s usage=%s\n",
248 			       hid_collection_type(h.collection),
249 			       hid_usage_page(HID_PAGE(h.usage)),
250 			       hid_usage_in_page(h.usage));
251 			break;
252 		case hid_endcollection:
253 			printf("End collection\n");
254 			break;
255 		case hid_input:
256 			dumpitem("Input  ", &h);
257 			break;
258 		case hid_output:
259 			dumpitem("Output ", &h);
260 			break;
261 		case hid_feature:
262 			dumpitem("Feature", &h);
263 			break;
264 		}
265 	}
266 	hid_end_parse(d);
267 	size = hid_report_size(r, hid_input, -1);
268 	printf("Total   input size %d bytes\n", size);
269 
270 	size = hid_report_size(r, hid_output, -1);
271 	printf("Total  output size %d bytes\n", size);
272 
273 	size = hid_report_size(r, hid_feature, -1);
274 	printf("Total feature size %d bytes\n", size);
275 }
276 
277 static void
278 prdata(u_char *buf, struct hid_item *h)
279 {
280 	u_int data;
281 	int i, pos;
282 
283 	pos = h->pos;
284 	for (i = 0; i < h->report_count; i++) {
285 		data = hid_get_data(buf, h);
286 		if (i > 0)
287 			printf(" ");
288 		if (h->logical_minimum < 0)
289 			printf("%d", (int)data);
290 		else
291 			printf("%u", data);
292                 if (hexdump)
293 			printf(" [0x%x]", data);
294 		h->pos += h->report_size;
295 	}
296 	h->pos = pos;
297 }
298 
299 static void
300 dumpdata(int f, report_desc_t rd, int loop)
301 {
302 	struct variable *var;
303 	int dlen, havedata, i, match, r, rid, use_rid;
304 	u_char *dbuf;
305 	enum hid_kind kind;
306 
307 	kind = zflag ? 3 : 0;
308 	rid = -1;
309 	use_rid = !!hid_get_report_id(f);
310 	do {
311 		if (kind < 3) {
312 			if (++rid >= 256) {
313 				rid = 0;
314 				kind++;
315 			}
316 			if (kind >= 3)
317 				rid = -1;
318 			for (var = vars; var; var = var->next) {
319 				if (rid == var->h.report_ID &&
320 				    kind == var->h.kind)
321 					break;
322 			}
323 			if (var == NULL)
324 				continue;
325 		}
326 		dlen = hid_report_size(rd, kind < 3 ? kind : hid_input, rid);
327 		if (dlen <= 0)
328 			continue;
329 		dbuf = malloc(dlen);
330 		memset(dbuf, 0, dlen);
331 		if (kind < 3) {
332 			dbuf[0] = rid;
333 			r = hid_get_report(f, kind, dbuf, dlen);
334 			if (r < 0)
335 				warn("hid_get_report(rid %d)", rid);
336 			havedata = !r && (rid == 0 || dbuf[0] == rid);
337 			if (rid != 0)
338 				dbuf[0] = rid;
339 		} else {
340 			r = read(f, dbuf, dlen);
341 			if (r < 1)
342 				err(1, "read error");
343 			havedata = 1;
344 		}
345 		if (verbose) {
346 			printf("Got %s report %d (%d bytes):",
347 			    kind == hid_output ? "output" :
348 			    kind == hid_feature ? "feature" : "input",
349 			    use_rid ? dbuf[0] : 0, dlen);
350 			if (havedata) {
351 				for (i = 0; i < dlen; i++)
352 					printf(" %02x", dbuf[i]);
353 			}
354 			printf("\n");
355 		}
356 		match = 0;
357 		for (var = vars; var; var = var->next) {
358 			if ((kind < 3 ? kind : hid_input) != var->h.kind)
359 				continue;
360 			if (var->h.report_ID != 0 &&
361 			    dbuf[0] != var->h.report_ID)
362 				continue;
363 			match = 1;
364 			if (!noname)
365 				printf("%s=", var->name);
366 			if (havedata)
367 				prdata(dbuf, &var->h);
368 			printf("\n");
369 		}
370 		if (match)
371 			printf("\n");
372 		free(dbuf);
373 	} while (loop || kind < 3);
374 }
375 
376 static void
377 writedata(int f, report_desc_t rd)
378 {
379 	struct variable *var;
380 	int dlen, i, r, rid;
381 	u_char *dbuf;
382 	enum hid_kind kind;
383 
384 	kind = 0;
385 	rid = 0;
386 	for (kind = 0; kind < 3; kind ++) {
387 	    for (rid = 0; rid < 256; rid ++) {
388 		for (var = vars; var; var = var->next) {
389 			if (rid == var->h.report_ID && kind == var->h.kind)
390 				break;
391 		}
392 		if (var == NULL)
393 			continue;
394 		dlen = hid_report_size(rd, kind, rid);
395 		if (dlen <= 0)
396 			continue;
397 		dbuf = malloc(dlen);
398 		memset(dbuf, 0, dlen);
399 		dbuf[0] = rid;
400 		if (!zflag && hid_get_report(f, kind, dbuf, dlen) == 0) {
401 			if (verbose) {
402 				printf("Got %s report %d (%d bytes):",
403 				    kind == hid_input ? "input" :
404 				    kind == hid_output ? "output" : "feature",
405 				    rid, dlen);
406 				for (i = 0; i < dlen; i++)
407 					printf(" %02x", dbuf[i]);
408 				printf("\n");
409 			}
410 		} else if (!zflag) {
411 			warn("hid_get_report(rid %d)", rid);
412 			if (verbose) {
413 				printf("Can't get %s report %d (%d bytes). "
414 				    "Will be initialized with zeros.\n",
415 				    kind == hid_input ? "input" :
416 				    kind == hid_output ? "output" : "feature",
417 				    rid, dlen);
418 			}
419 		}
420 		for (var = vars; var; var = var->next) {
421 			if (rid != var->h.report_ID || kind != var->h.kind)
422 				continue;
423 			hid_set_data(dbuf, &var->h, var->val);
424 		}
425 		if (verbose) {
426 			printf("Setting %s report %d (%d bytes):",
427 			    kind == hid_output ? "output" :
428 			    kind == hid_feature ? "feature" : "input",
429 			    rid, dlen);
430 			for (i = 0; i < dlen; i++)
431 				printf(" %02x", dbuf[i]);
432 			printf("\n");
433 		}
434 		r = hid_set_report(f, kind, dbuf, dlen);
435 		if (r != 0)
436 			warn("hid_set_report(rid %d)", rid);
437 		free(dbuf);
438 	    }
439 	}
440 }
441 
442 int
443 main(int argc, char **argv)
444 {
445 	report_desc_t r;
446 	char *table = 0;
447 	char devnam[100], *dev = NULL;
448 	int f;
449 	int all = 0;
450 	int ch;
451 	int repdump = 0;
452 	int loop = 0;
453 
454 	while ((ch = getopt(argc, argv, "af:lnrt:vwxz")) != -1) {
455 		switch(ch) {
456 		case 'a':
457 			all++;
458 			break;
459 		case 'f':
460 			dev = optarg;
461 			break;
462 		case 'l':
463 			loop ^= 1;
464 			break;
465 		case 'n':
466 			noname++;
467 			break;
468 		case 'r':
469 			repdump++;
470 			break;
471 		case 't':
472 			table = optarg;
473 			break;
474 		case 'v':
475 			verbose++;
476 			break;
477 		case 'w':
478 			wflag = 1;
479 			break;
480 		case 'x':
481 			hexdump = 1;
482 			break;
483 		case 'z':
484 			zflag = 1;
485 			break;
486 		case '?':
487 		default:
488 			usage();
489 		}
490 	}
491 	argc -= optind;
492 	argv += optind;
493 	if (dev == NULL)
494 		usage();
495 
496 	if (argc == 0 && !all && !repdump)
497 		usage();
498 
499 	if (dev[0] != '/') {
500 		if (isdigit(dev[0]))
501 			snprintf(devnam, sizeof(devnam), "/dev/uhid%s", dev);
502 		else
503 			snprintf(devnam, sizeof(devnam), "/dev/%s", dev);
504 		dev = devnam;
505 	}
506 
507 	hid_init(table);
508 
509 	f = open(dev, O_RDWR);
510 	if (f < 0)
511 		err(1, "%s", dev);
512 
513 	r = hid_get_report_desc(f);
514 	if (r == 0)
515 		errx(1, "USB_GET_REPORT_DESC");
516 
517 	if (repdump) {
518 		printf("Report descriptor:\n");
519 		dumpitems(r);
520 	}
521 	if (argc != 0 || all) {
522 		parceargs(r, all, argc, argv);
523 		if (wflag)
524 			writedata(f, r);
525 		else
526 			dumpdata(f, r, loop);
527 	}
528 
529 	hid_dispose_report_desc(r);
530 	exit(0);
531 }
532