usbhid.c revision 164531
162642Sn_hibma/*	$NetBSD: usbhid.c,v 1.14 2000/07/03 02:51:37 matt Exp $	*/
262642Sn_hibma/*	$FreeBSD: head/usr.bin/usbhidctl/usbhid.c 164531 2006-11-23 00:20:54Z grog $ */
362642Sn_hibma
462642Sn_hibma/*
562642Sn_hibma * Copyright (c) 1998 The NetBSD Foundation, Inc.
662642Sn_hibma * All rights reserved.
762642Sn_hibma *
862642Sn_hibma * This code is derived from software contributed to The NetBSD Foundation
962642Sn_hibma * by Lennart Augustsson (augustss@netbsd.org).
1062642Sn_hibma *
1162642Sn_hibma * Redistribution and use in source and binary forms, with or without
1262642Sn_hibma * modification, are permitted provided that the following conditions
1362642Sn_hibma * are met:
1462642Sn_hibma * 1. Redistributions of source code must retain the above copyright
1562642Sn_hibma *    notice, this list of conditions and the following disclaimer.
1662642Sn_hibma * 2. Redistributions in binary form must reproduce the above copyright
1762642Sn_hibma *    notice, this list of conditions and the following disclaimer in the
1862642Sn_hibma *    documentation and/or other materials provided with the distribution.
1962642Sn_hibma * 3. All advertising materials mentioning features or use of this software
2062642Sn_hibma *    must display the following acknowledgement:
2162642Sn_hibma *        This product includes software developed by the NetBSD
2262642Sn_hibma *        Foundation, Inc. and its contributors.
2362642Sn_hibma * 4. Neither the name of The NetBSD Foundation nor the names of its
2462642Sn_hibma *    contributors may be used to endorse or promote products derived
2562642Sn_hibma *    from this software without specific prior written permission.
2662642Sn_hibma *
2762642Sn_hibma * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2862642Sn_hibma * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2962642Sn_hibma * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
3062642Sn_hibma * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
3162642Sn_hibma * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3262642Sn_hibma * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3362642Sn_hibma * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3462642Sn_hibma * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3562642Sn_hibma * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3662642Sn_hibma * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3762642Sn_hibma * POSSIBILITY OF SUCH DAMAGE.
3862642Sn_hibma */
3962642Sn_hibma
4062642Sn_hibma#include <stdio.h>
4162642Sn_hibma#include <stdlib.h>
4262642Sn_hibma#include <string.h>
4362642Sn_hibma#include <sys/types.h>
4462642Sn_hibma#include <fcntl.h>
4562642Sn_hibma#include <sys/ioctl.h>
4662642Sn_hibma#include <unistd.h>
4762642Sn_hibma#include <err.h>
4862642Sn_hibma#include <ctype.h>
4962642Sn_hibma#include <errno.h>
50113273Smdodd#include <usbhid.h>
5162642Sn_hibma#include <dev/usb/usb.h>
5262642Sn_hibma#include <dev/usb/usbhid.h>
5362642Sn_hibma
5462642Sn_hibmaint verbose = 0;
5562642Sn_hibmaint all = 0;
5662642Sn_hibmaint noname = 0;
57164531Sgrogint hexdump = 0;
58113273Smdoddstatic int reportid;
5962642Sn_hibma
6062642Sn_hibmachar **names;
6162642Sn_hibmaint nnames;
6262642Sn_hibma
6362642Sn_hibmavoid prbits(int bits, char **strs, int n);
6462642Sn_hibmavoid usage(void);
6587699Smarkmvoid dumpitem(const char *label, struct hid_item *h);
6662642Sn_hibmavoid dumpitems(report_desc_t r);
6762642Sn_hibmavoid rev(struct hid_item **p);
6862642Sn_hibmavoid prdata(u_char *buf, struct hid_item *h);
6962642Sn_hibmavoid dumpdata(int f, report_desc_t r, int loop);
7062642Sn_hibmaint gotname(char *n);
7162642Sn_hibma
7262642Sn_hibmaint
7362642Sn_hibmagotname(char *n)
7462642Sn_hibma{
7562642Sn_hibma	int i;
7662642Sn_hibma
7762642Sn_hibma	for (i = 0; i < nnames; i++)
7862642Sn_hibma		if (strcmp(names[i], n) == 0)
7962642Sn_hibma			return 1;
8062642Sn_hibma	return 0;
8162642Sn_hibma}
8262642Sn_hibma
8362642Sn_hibmavoid
8462642Sn_hibmaprbits(int bits, char **strs, int n)
8562642Sn_hibma{
8662642Sn_hibma	int i;
8762642Sn_hibma
8862642Sn_hibma	for(i = 0; i < n; i++, bits >>= 1)
8962642Sn_hibma		if (strs[i*2])
9062642Sn_hibma			printf("%s%s", i == 0 ? "" : ", ", strs[i*2 + (bits&1)]);
9162642Sn_hibma}
9262642Sn_hibma
9362642Sn_hibmavoid
9462642Sn_hibmausage(void)
9562642Sn_hibma{
9662642Sn_hibma	extern char *__progname;
9762642Sn_hibma
98164531Sgrog	fprintf(stderr,
99164531Sgrog                "usage: %s -f device "
100164531Sgrog                "[-l] [-n] [-r] [-t tablefile] [-v] [x] name ...\n",
101164531Sgrog                __progname);
102164531Sgrog	fprintf(stderr,
103164531Sgrog                "       %s -f device "
104164531Sgrog                "[-l] [-n] [-r] [-t tablefile] [-v] [x] -a\n",
105164531Sgrog                __progname);
10662642Sn_hibma	exit(1);
10762642Sn_hibma}
10862642Sn_hibma
10962642Sn_hibmavoid
11087699Smarkmdumpitem(const char *label, struct hid_item *h)
11162642Sn_hibma{
11262642Sn_hibma	if ((h->flags & HIO_CONST) && !verbose)
11362642Sn_hibma		return;
11462642Sn_hibma	printf("%s size=%d count=%d page=%s usage=%s%s", label,
115164531Sgrog	       h->report_size, h->report_count,
116164531Sgrog	       hid_usage_page(HID_PAGE(h->usage)),
11762642Sn_hibma	       hid_usage_in_page(h->usage),
11862642Sn_hibma	       h->flags & HIO_CONST ? " Const" : "");
11962642Sn_hibma	printf(", logical range %d..%d",
12062642Sn_hibma	       h->logical_minimum, h->logical_maximum);
12162642Sn_hibma	if (h->physical_minimum != h->physical_maximum)
12262642Sn_hibma		printf(", physical range %d..%d",
12362642Sn_hibma		       h->physical_minimum, h->physical_maximum);
12462642Sn_hibma	if (h->unit)
12562642Sn_hibma		printf(", unit=0x%02x exp=%d", h->unit, h->unit_exponent);
12662642Sn_hibma	printf("\n");
12762642Sn_hibma}
12862642Sn_hibma
12962642Sn_hibmavoid
13062642Sn_hibmadumpitems(report_desc_t r)
13162642Sn_hibma{
13262642Sn_hibma	struct hid_data *d;
13362642Sn_hibma	struct hid_item h;
13467256Sn_hibma	int size;
13562642Sn_hibma
136113273Smdodd	for (d = hid_start_parse(r, ~0, reportid); hid_get_item(d, &h); ) {
13762642Sn_hibma		switch (h.kind) {
13862642Sn_hibma		case hid_collection:
13962642Sn_hibma			printf("Collection page=%s usage=%s\n",
140164531Sgrog			       hid_usage_page(HID_PAGE(h.usage)),
14162642Sn_hibma			       hid_usage_in_page(h.usage));
14262642Sn_hibma			break;
14362642Sn_hibma		case hid_endcollection:
14462642Sn_hibma			printf("End collection\n");
14562642Sn_hibma			break;
14662642Sn_hibma		case hid_input:
14762642Sn_hibma			dumpitem("Input  ", &h);
14862642Sn_hibma			break;
14962642Sn_hibma		case hid_output:
15062642Sn_hibma			dumpitem("Output ", &h);
15162642Sn_hibma			break;
15262642Sn_hibma		case hid_feature:
15362642Sn_hibma			dumpitem("Feature", &h);
15462642Sn_hibma			break;
15562642Sn_hibma		}
15662642Sn_hibma	}
15762642Sn_hibma	hid_end_parse(d);
15867256Sn_hibma	size = hid_report_size(r, 0, hid_input);
15975606Sn_hibma	printf("Total   input size %d bytes\n", size);
16062642Sn_hibma
16167256Sn_hibma	size = hid_report_size(r, 0, hid_output);
16267256Sn_hibma	printf("Total  output size %d bytes\n", size);
16367256Sn_hibma
16467256Sn_hibma	size = hid_report_size(r, 0, hid_feature);
16567256Sn_hibma	printf("Total feature size %d bytes\n", size);
16662642Sn_hibma}
16762642Sn_hibma
16862642Sn_hibmavoid
16962642Sn_hibmarev(struct hid_item **p)
17062642Sn_hibma{
17162642Sn_hibma	struct hid_item *cur, *prev, *next;
17262642Sn_hibma
17362642Sn_hibma	prev = 0;
17462642Sn_hibma	cur = *p;
17562642Sn_hibma	while(cur != 0) {
17662642Sn_hibma		next = cur->next;
17762642Sn_hibma		cur->next = prev;
17862642Sn_hibma		prev = cur;
17962642Sn_hibma		cur = next;
18062642Sn_hibma	}
18162642Sn_hibma	*p = prev;
18262642Sn_hibma}
18362642Sn_hibma
18462642Sn_hibmavoid
18562642Sn_hibmaprdata(u_char *buf, struct hid_item *h)
18662642Sn_hibma{
18762642Sn_hibma	u_int data;
18862642Sn_hibma	int i, pos;
18962642Sn_hibma
19062642Sn_hibma	pos = h->pos;
19162642Sn_hibma	for (i = 0; i < h->report_count; i++) {
19262642Sn_hibma		data = hid_get_data(buf, h);
19362642Sn_hibma		if (h->logical_minimum < 0)
19462642Sn_hibma			printf("%d", (int)data);
19562642Sn_hibma		else
19662642Sn_hibma			printf("%u", data);
197164531Sgrog                if (hexdump)
198164531Sgrog			printf(" [0x%x]", data);
19962642Sn_hibma		pos += h->report_size;
20062642Sn_hibma	}
20162642Sn_hibma}
20262642Sn_hibma
20362642Sn_hibmavoid
20462642Sn_hibmadumpdata(int f, report_desc_t rd, int loop)
20562642Sn_hibma{
20662642Sn_hibma	struct hid_data *d;
20762642Sn_hibma	struct hid_item h, *hids, *n;
20862642Sn_hibma	int r, dlen;
20962642Sn_hibma	u_char *dbuf;
21062642Sn_hibma	static int one = 1;
21162642Sn_hibma	u_int32_t colls[100];
21262642Sn_hibma	int sp = 0;
21362642Sn_hibma	char namebuf[10000], *namep;
21462642Sn_hibma
21562642Sn_hibma	hids = 0;
216164531Sgrog	for (d = hid_start_parse(rd, 1<<hid_input, reportid);
21762642Sn_hibma	     hid_get_item(d, &h); ) {
21862642Sn_hibma		if (h.kind == hid_collection)
21962642Sn_hibma			colls[++sp] = h.usage;
22062642Sn_hibma		else if (h.kind == hid_endcollection)
22162642Sn_hibma			--sp;
22262642Sn_hibma		if (h.kind != hid_input || (h.flags & HIO_CONST))
22362642Sn_hibma			continue;
22462642Sn_hibma		h.next = hids;
22562642Sn_hibma		h.collection = colls[sp];
22662642Sn_hibma		hids = malloc(sizeof *hids);
22762642Sn_hibma		*hids = h;
22862642Sn_hibma	}
22962642Sn_hibma	hid_end_parse(d);
23062642Sn_hibma	rev(&hids);
23167256Sn_hibma	dlen = hid_report_size(rd, 0, hid_input);
23262642Sn_hibma	dbuf = malloc(dlen);
23362642Sn_hibma	if (!loop)
23462642Sn_hibma		if (ioctl(f, USB_SET_IMMED, &one) < 0) {
23562642Sn_hibma			if (errno == EOPNOTSUPP)
23662642Sn_hibma				warnx("device does not support immediate mode, only changes reported.");
23762642Sn_hibma			else
23862642Sn_hibma				err(1, "USB_SET_IMMED");
23962642Sn_hibma		}
24062642Sn_hibma	do {
24162642Sn_hibma		r = read(f, dbuf, dlen);
24262642Sn_hibma		if (r != dlen) {
24362642Sn_hibma			err(1, "bad read %d != %d", r, dlen);
24462642Sn_hibma		}
24562642Sn_hibma		for (n = hids; n; n = n->next) {
24662642Sn_hibma			namep = namebuf;
24762642Sn_hibma			namep += sprintf(namep, "%s:%s.",
24862642Sn_hibma					 hid_usage_page(HID_PAGE(n->collection)),
24962642Sn_hibma					 hid_usage_in_page(n->collection));
25062642Sn_hibma			namep += sprintf(namep, "%s:%s",
25162642Sn_hibma					 hid_usage_page(HID_PAGE(n->usage)),
25262642Sn_hibma					 hid_usage_in_page(n->usage));
25362642Sn_hibma			if (all || gotname(namebuf)) {
25462642Sn_hibma				if (!noname)
25562642Sn_hibma					printf("%s=", namebuf);
256115317Smdodd				prdata(dbuf + (reportid != 0), n);
25762642Sn_hibma				printf("\n");
25862642Sn_hibma			}
25962642Sn_hibma		}
26062642Sn_hibma		if (loop)
26162642Sn_hibma			printf("\n");
26262642Sn_hibma	} while (loop);
26362642Sn_hibma	free(dbuf);
26462642Sn_hibma}
26562642Sn_hibma
26662642Sn_hibmaint
26762642Sn_hibmamain(int argc, char **argv)
26862642Sn_hibma{
26962642Sn_hibma	int f;
27062642Sn_hibma	report_desc_t r;
27187699Smarkm	char devnam[100], *dev = 0;
27262642Sn_hibma	int ch;
27362642Sn_hibma	int repdump = 0;
27462642Sn_hibma	int loop = 0;
27562642Sn_hibma	char *table = 0;
27662642Sn_hibma
277164531Sgrog	while ((ch = getopt(argc, argv, "af:lnrt:vx")) != -1) {
27862642Sn_hibma		switch(ch) {
27962642Sn_hibma		case 'a':
28062642Sn_hibma			all++;
28162642Sn_hibma			break;
28262642Sn_hibma		case 'f':
28362642Sn_hibma			dev = optarg;
28462642Sn_hibma			break;
28562642Sn_hibma		case 'l':
28662642Sn_hibma			loop ^= 1;
28762642Sn_hibma			break;
28862642Sn_hibma		case 'n':
28962642Sn_hibma			noname++;
29062642Sn_hibma			break;
29162642Sn_hibma		case 'r':
29262642Sn_hibma			repdump++;
29362642Sn_hibma			break;
29462642Sn_hibma		case 't':
29562642Sn_hibma			table = optarg;
29662642Sn_hibma			break;
29762642Sn_hibma		case 'v':
29862642Sn_hibma			verbose++;
29962642Sn_hibma			break;
300164531Sgrog		case 'x':
301164531Sgrog			hexdump ^= 1;
302164531Sgrog			break;
30362642Sn_hibma		case '?':
30462642Sn_hibma		default:
30562642Sn_hibma			usage();
30662642Sn_hibma		}
30762642Sn_hibma	}
30862642Sn_hibma	argc -= optind;
30962642Sn_hibma	argv += optind;
31062642Sn_hibma	if (dev == 0)
31162642Sn_hibma		usage();
31262642Sn_hibma	names = argv;
31362642Sn_hibma	nnames = argc;
31462642Sn_hibma
31562642Sn_hibma	if (nnames == 0 && !all && !repdump)
31662642Sn_hibma		usage();
31762642Sn_hibma
31862642Sn_hibma	if (dev[0] != '/') {
31962642Sn_hibma		if (isdigit(dev[0]))
32087699Smarkm			snprintf(devnam, sizeof(devnam), "/dev/uhid%s", dev);
32162642Sn_hibma		else
32287699Smarkm			snprintf(devnam, sizeof(devnam), "/dev/%s", dev);
32387699Smarkm		dev = devnam;
32462642Sn_hibma	}
32562642Sn_hibma
32662642Sn_hibma	hid_init(table);
32762642Sn_hibma
32862642Sn_hibma	f = open(dev, O_RDWR);
32962642Sn_hibma	if (f < 0)
33062642Sn_hibma		err(1, "%s", dev);
33162642Sn_hibma
33262642Sn_hibma	r = hid_get_report_desc(f);
333164531Sgrog	if (r == 0)
33462642Sn_hibma		errx(1, "USB_GET_REPORT_DESC");
335164531Sgrog
33662642Sn_hibma	if (repdump) {
33762642Sn_hibma		printf("Report descriptor:\n");
33862642Sn_hibma		dumpitems(r);
33962642Sn_hibma	}
34062642Sn_hibma	if (nnames != 0 || all)
34162642Sn_hibma		dumpdata(f, r, loop);
34262642Sn_hibma
34362642Sn_hibma	hid_dispose_report_desc(r);
34462642Sn_hibma	exit(0);
34562642Sn_hibma}
346