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