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