usbhid.c revision 164531
1/*	$NetBSD: usbhid.c,v 1.14 2000/07/03 02:51:37 matt Exp $	*/
2/*	$FreeBSD: head/usr.bin/usbhidctl/usbhid.c 164531 2006-11-23 00:20:54Z grog $ */
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 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *        This product includes software developed by the NetBSD
22 *        Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 *    contributors may be used to endorse or promote products derived
25 *    from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <sys/types.h>
44#include <fcntl.h>
45#include <sys/ioctl.h>
46#include <unistd.h>
47#include <err.h>
48#include <ctype.h>
49#include <errno.h>
50#include <usbhid.h>
51#include <dev/usb/usb.h>
52#include <dev/usb/usbhid.h>
53
54int verbose = 0;
55int all = 0;
56int noname = 0;
57int hexdump = 0;
58static int reportid;
59
60char **names;
61int nnames;
62
63void prbits(int bits, char **strs, int n);
64void usage(void);
65void dumpitem(const char *label, struct hid_item *h);
66void dumpitems(report_desc_t r);
67void rev(struct hid_item **p);
68void prdata(u_char *buf, struct hid_item *h);
69void dumpdata(int f, report_desc_t r, int loop);
70int gotname(char *n);
71
72int
73gotname(char *n)
74{
75	int i;
76
77	for (i = 0; i < nnames; i++)
78		if (strcmp(names[i], n) == 0)
79			return 1;
80	return 0;
81}
82
83void
84prbits(int bits, char **strs, int n)
85{
86	int i;
87
88	for(i = 0; i < n; i++, bits >>= 1)
89		if (strs[i*2])
90			printf("%s%s", i == 0 ? "" : ", ", strs[i*2 + (bits&1)]);
91}
92
93void
94usage(void)
95{
96	extern char *__progname;
97
98	fprintf(stderr,
99                "usage: %s -f device "
100                "[-l] [-n] [-r] [-t tablefile] [-v] [x] name ...\n",
101                __progname);
102	fprintf(stderr,
103                "       %s -f device "
104                "[-l] [-n] [-r] [-t tablefile] [-v] [x] -a\n",
105                __progname);
106	exit(1);
107}
108
109void
110dumpitem(const char *label, struct hid_item *h)
111{
112	if ((h->flags & HIO_CONST) && !verbose)
113		return;
114	printf("%s size=%d count=%d page=%s usage=%s%s", label,
115	       h->report_size, h->report_count,
116	       hid_usage_page(HID_PAGE(h->usage)),
117	       hid_usage_in_page(h->usage),
118	       h->flags & HIO_CONST ? " Const" : "");
119	printf(", logical range %d..%d",
120	       h->logical_minimum, h->logical_maximum);
121	if (h->physical_minimum != h->physical_maximum)
122		printf(", physical range %d..%d",
123		       h->physical_minimum, h->physical_maximum);
124	if (h->unit)
125		printf(", unit=0x%02x exp=%d", h->unit, h->unit_exponent);
126	printf("\n");
127}
128
129void
130dumpitems(report_desc_t r)
131{
132	struct hid_data *d;
133	struct hid_item h;
134	int size;
135
136	for (d = hid_start_parse(r, ~0, reportid); hid_get_item(d, &h); ) {
137		switch (h.kind) {
138		case hid_collection:
139			printf("Collection page=%s usage=%s\n",
140			       hid_usage_page(HID_PAGE(h.usage)),
141			       hid_usage_in_page(h.usage));
142			break;
143		case hid_endcollection:
144			printf("End collection\n");
145			break;
146		case hid_input:
147			dumpitem("Input  ", &h);
148			break;
149		case hid_output:
150			dumpitem("Output ", &h);
151			break;
152		case hid_feature:
153			dumpitem("Feature", &h);
154			break;
155		}
156	}
157	hid_end_parse(d);
158	size = hid_report_size(r, 0, hid_input);
159	printf("Total   input size %d bytes\n", size);
160
161	size = hid_report_size(r, 0, hid_output);
162	printf("Total  output size %d bytes\n", size);
163
164	size = hid_report_size(r, 0, hid_feature);
165	printf("Total feature size %d bytes\n", size);
166}
167
168void
169rev(struct hid_item **p)
170{
171	struct hid_item *cur, *prev, *next;
172
173	prev = 0;
174	cur = *p;
175	while(cur != 0) {
176		next = cur->next;
177		cur->next = prev;
178		prev = cur;
179		cur = next;
180	}
181	*p = prev;
182}
183
184void
185prdata(u_char *buf, struct hid_item *h)
186{
187	u_int data;
188	int i, pos;
189
190	pos = h->pos;
191	for (i = 0; i < h->report_count; i++) {
192		data = hid_get_data(buf, h);
193		if (h->logical_minimum < 0)
194			printf("%d", (int)data);
195		else
196			printf("%u", data);
197                if (hexdump)
198			printf(" [0x%x]", data);
199		pos += h->report_size;
200	}
201}
202
203void
204dumpdata(int f, report_desc_t rd, int loop)
205{
206	struct hid_data *d;
207	struct hid_item h, *hids, *n;
208	int r, dlen;
209	u_char *dbuf;
210	static int one = 1;
211	u_int32_t colls[100];
212	int sp = 0;
213	char namebuf[10000], *namep;
214
215	hids = 0;
216	for (d = hid_start_parse(rd, 1<<hid_input, reportid);
217	     hid_get_item(d, &h); ) {
218		if (h.kind == hid_collection)
219			colls[++sp] = h.usage;
220		else if (h.kind == hid_endcollection)
221			--sp;
222		if (h.kind != hid_input || (h.flags & HIO_CONST))
223			continue;
224		h.next = hids;
225		h.collection = colls[sp];
226		hids = malloc(sizeof *hids);
227		*hids = h;
228	}
229	hid_end_parse(d);
230	rev(&hids);
231	dlen = hid_report_size(rd, 0, hid_input);
232	dbuf = malloc(dlen);
233	if (!loop)
234		if (ioctl(f, USB_SET_IMMED, &one) < 0) {
235			if (errno == EOPNOTSUPP)
236				warnx("device does not support immediate mode, only changes reported.");
237			else
238				err(1, "USB_SET_IMMED");
239		}
240	do {
241		r = read(f, dbuf, dlen);
242		if (r != dlen) {
243			err(1, "bad read %d != %d", r, dlen);
244		}
245		for (n = hids; n; n = n->next) {
246			namep = namebuf;
247			namep += sprintf(namep, "%s:%s.",
248					 hid_usage_page(HID_PAGE(n->collection)),
249					 hid_usage_in_page(n->collection));
250			namep += sprintf(namep, "%s:%s",
251					 hid_usage_page(HID_PAGE(n->usage)),
252					 hid_usage_in_page(n->usage));
253			if (all || gotname(namebuf)) {
254				if (!noname)
255					printf("%s=", namebuf);
256				prdata(dbuf + (reportid != 0), n);
257				printf("\n");
258			}
259		}
260		if (loop)
261			printf("\n");
262	} while (loop);
263	free(dbuf);
264}
265
266int
267main(int argc, char **argv)
268{
269	int f;
270	report_desc_t r;
271	char devnam[100], *dev = 0;
272	int ch;
273	int repdump = 0;
274	int loop = 0;
275	char *table = 0;
276
277	while ((ch = getopt(argc, argv, "af:lnrt:vx")) != -1) {
278		switch(ch) {
279		case 'a':
280			all++;
281			break;
282		case 'f':
283			dev = optarg;
284			break;
285		case 'l':
286			loop ^= 1;
287			break;
288		case 'n':
289			noname++;
290			break;
291		case 'r':
292			repdump++;
293			break;
294		case 't':
295			table = optarg;
296			break;
297		case 'v':
298			verbose++;
299			break;
300		case 'x':
301			hexdump ^= 1;
302			break;
303		case '?':
304		default:
305			usage();
306		}
307	}
308	argc -= optind;
309	argv += optind;
310	if (dev == 0)
311		usage();
312	names = argv;
313	nnames = argc;
314
315	if (nnames == 0 && !all && !repdump)
316		usage();
317
318	if (dev[0] != '/') {
319		if (isdigit(dev[0]))
320			snprintf(devnam, sizeof(devnam), "/dev/uhid%s", dev);
321		else
322			snprintf(devnam, sizeof(devnam), "/dev/%s", dev);
323		dev = devnam;
324	}
325
326	hid_init(table);
327
328	f = open(dev, O_RDWR);
329	if (f < 0)
330		err(1, "%s", dev);
331
332	r = hid_get_report_desc(f);
333	if (r == 0)
334		errx(1, "USB_GET_REPORT_DESC");
335
336	if (repdump) {
337		printf("Report descriptor:\n");
338		dumpitems(r);
339	}
340	if (nnames != 0 || all)
341		dumpdata(f, r, loop);
342
343	hid_dispose_report_desc(r);
344	exit(0);
345}
346