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-NetBSD
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
47static struct variable {
48	char *name;
49	int instance;
50	int val;
51	struct hid_item h;
52	struct variable *next;
53} *vars;
54
55static int verbose = 0;
56static int noname = 0;
57static int hexdump = 0;
58static int wflag = 0;
59static int zflag = 0;
60
61static void usage(void);
62static void dumpitem(const char *label, struct hid_item *h);
63static void dumpitems(report_desc_t r);
64static void prdata(u_char *buf, struct hid_item *h);
65static void dumpdata(int f, report_desc_t r, int loop);
66static void writedata(int f, report_desc_t r);
67
68static void
69parceargs(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
181static void
182usage(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
200static void
201dumpitem(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
221static const char *
222hid_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
239static void
240dumpitems(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
279static void
280prdata(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
301static void
302dumpdata(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
378static void
379writedata(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
444int
445main(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