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