usbhid.c revision 67256
13515Sache/*	$NetBSD: usbhid.c,v 1.14 2000/07/03 02:51:37 matt Exp $	*/
23515Sache/*	$FreeBSD: head/usr.bin/usbhidctl/usbhid.c 67256 2000-10-17 17:13:38Z n_hibma $ */
33515Sache
43515Sache/*
53515Sache * Copyright (c) 1998 The NetBSD Foundation, Inc.
612983Sjkh * All rights reserved.
712983Sjkh *
83515Sache * This code is derived from software contributed to The NetBSD Foundation
93515Sache * by Lennart Augustsson (augustss@netbsd.org).
103515Sache *
113515Sache * Redistribution and use in source and binary forms, with or without
123515Sache * modification, are permitted provided that the following conditions
133515Sache * are met:
143515Sache * 1. Redistributions of source code must retain the above copyright
153515Sache *    notice, this list of conditions and the following disclaimer.
163515Sache * 2. Redistributions in binary form must reproduce the above copyright
173515Sache *    notice, this list of conditions and the following disclaimer in the
183515Sache *    documentation and/or other materials provided with the distribution.
193515Sache * 3. All advertising materials mentioning features or use of this software
203515Sache *    must display the following acknowledgement:
213515Sache *        This product includes software developed by the NetBSD
223515Sache *        Foundation, Inc. and its contributors.
233515Sache * 4. Neither the name of The NetBSD Foundation nor the names of its
243740Sache *    contributors may be used to endorse or promote products derived
253515Sache *    from this software without specific prior written permission.
263515Sache *
273515Sache * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2812983Sjkh * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2912983Sjkh * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
303515Sache * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
3112983Sjkh * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
323515Sache * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
333515Sache * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
343515Sache * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
353515Sache * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
363515Sache * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
373515Sache * POSSIBILITY OF SUCH DAMAGE.
383515Sache */
3913135Sjkh
4013135Sjkh#include <stdio.h>
4115322Sjkh#include <stdlib.h>
423515Sache#include <string.h>
4315273Sjkh#include <sys/types.h>
4415322Sjkh#include <fcntl.h>
4515273Sjkh#include <sys/ioctl.h>
4615273Sjkh#include <unistd.h>
4715289Sjkh#include <err.h>
4815322Sjkh#include <ctype.h>
4915273Sjkh#include <errno.h>
5015273Sjkh#include <libusb.h>
5115273Sjkh#include <dev/usb/usb.h>
5215376Sjkh#include <dev/usb/usbhid.h>
5315273Sjkh
5415273Sjkhint verbose = 0;
5515273Sjkhint all = 0;
5615273Sjkhint noname = 0;
5715273Sjkh
5815322Sjkhchar **names;
5915273Sjkhint nnames;
6015322Sjkh
6115273Sjkhvoid prbits(int bits, char **strs, int n);
6215273Sjkhvoid usage(void);
6315322Sjkhvoid dumpitem(char *label, struct hid_item *h);
6415273Sjkhvoid dumpitems(report_desc_t r);
6515273Sjkhvoid rev(struct hid_item **p);
6615273Sjkhvoid prdata(u_char *buf, struct hid_item *h);
6715273Sjkhvoid dumpdata(int f, report_desc_t r, int loop);
6815273Sjkhint gotname(char *n);
6915273Sjkh
7015322Sjkhint
7115273Sjkhgotname(char *n)
7215322Sjkh{
7315322Sjkh	int i;
7415273Sjkh
7515273Sjkh	for (i = 0; i < nnames; i++)
7615273Sjkh		if (strcmp(names[i], n) == 0)
7715273Sjkh			return 1;
7815273Sjkh	return 0;
7915273Sjkh}
8015273Sjkh
8115273Sjkhvoid
8215273Sjkhprbits(int bits, char **strs, int n)
8315273Sjkh{
8415273Sjkh	int i;
8515273Sjkh
8615273Sjkh	for(i = 0; i < n; i++, bits >>= 1)
8715273Sjkh		if (strs[i*2])
8812983Sjkh			printf("%s%s", i == 0 ? "" : ", ", strs[i*2 + (bits&1)]);
8915273Sjkh}
9015273Sjkh
9115273Sjkhvoid
9215273Sjkhusage(void)
9315273Sjkh{
9415273Sjkh	extern char *__progname;
9512983Sjkh
9615273Sjkh	fprintf(stderr, "Usage: %s -f device [-l] [-n] [-r] [-t tablefile] [-v] name ...\n", __progname);
9715273Sjkh	fprintf(stderr, "       %s -f device [-l] [-n] [-r] [-t tablefile] [-v] -a\n", __progname);
9815273Sjkh	exit(1);
9915273Sjkh}
10015273Sjkh
10115273Sjkhvoid
10215273Sjkhdumpitem(char *label, struct hid_item *h)
1034565Sache{
10415273Sjkh	if ((h->flags & HIO_CONST) && !verbose)
10515273Sjkh		return;
10615273Sjkh	printf("%s size=%d count=%d page=%s usage=%s%s", label,
10715273Sjkh	       h->report_size, h->report_count,
10815273Sjkh	       hid_usage_page(HID_PAGE(h->usage)),
10915273Sjkh	       hid_usage_in_page(h->usage),
11015273Sjkh	       h->flags & HIO_CONST ? " Const" : "");
11115273Sjkh	printf(", logical range %d..%d",
11215273Sjkh	       h->logical_minimum, h->logical_maximum);
1134527Sache	if (h->physical_minimum != h->physical_maximum)
1143515Sache		printf(", physical range %d..%d",
11515273Sjkh		       h->physical_minimum, h->physical_maximum);
11615273Sjkh	if (h->unit)
1173515Sache		printf(", unit=0x%02x exp=%d", h->unit, h->unit_exponent);
11815273Sjkh	printf("\n");
11915273Sjkh}
12015273Sjkh
12115273Sjkhvoid
12215273Sjkhdumpitems(report_desc_t r)
12315273Sjkh{
12415273Sjkh	struct hid_data *d;
12515273Sjkh	struct hid_item h;
12615273Sjkh	int size;
12715273Sjkh
12815273Sjkh	for (d = hid_start_parse(r, ~0); hid_get_item(d, &h); ) {
12915273Sjkh		switch (h.kind) {
13015273Sjkh		case hid_collection:
13115273Sjkh			printf("Collection page=%s usage=%s\n",
13215273Sjkh			       hid_usage_page(HID_PAGE(h.usage)),
13315273Sjkh			       hid_usage_in_page(h.usage));
13415273Sjkh			break;
13515273Sjkh		case hid_endcollection:
13615273Sjkh			printf("End collection\n");
13715273Sjkh			break;
13815273Sjkh		case hid_input:
13915273Sjkh			dumpitem("Input  ", &h);
14015273Sjkh			break;
14115273Sjkh		case hid_output:
14215273Sjkh			dumpitem("Output ", &h);
14315273Sjkh			break;
14415273Sjkh		case hid_feature:
14515273Sjkh			dumpitem("Feature", &h);
14615273Sjkh			break;
14715273Sjkh		}
14815273Sjkh	}
14915273Sjkh	hid_end_parse(d);
15015273Sjkh	size = hid_report_size(r, 0, hid_input);
15115273Sjkh	printf("Total   input size %s%d bytes\n", size);
15215273Sjkh
15315273Sjkh	size = hid_report_size(r, 0, hid_output);
15415273Sjkh	printf("Total  output size %d bytes\n", size);
15515273Sjkh
15615273Sjkh	size = hid_report_size(r, 0, hid_feature);
15712983Sjkh	printf("Total feature size %d bytes\n", size);
15815273Sjkh}
15915273Sjkh
16015273Sjkhvoid
16115273Sjkhrev(struct hid_item **p)
16212983Sjkh{
16315273Sjkh	struct hid_item *cur, *prev, *next;
16415273Sjkh
16515273Sjkh	prev = 0;
16615273Sjkh	cur = *p;
16715273Sjkh	while(cur != 0) {
16815273Sjkh		next = cur->next;
16915273Sjkh		cur->next = prev;
17015273Sjkh		prev = cur;
17115273Sjkh		cur = next;
17215273Sjkh	}
17315273Sjkh	*p = prev;
17415273Sjkh}
17515273Sjkh
17615273Sjkhvoid
17715273Sjkhprdata(u_char *buf, struct hid_item *h)
17815273Sjkh{
17915273Sjkh	u_int data;
18015273Sjkh	int i, pos;
18115273Sjkh
18215273Sjkh	pos = h->pos;
18315273Sjkh	for (i = 0; i < h->report_count; i++) {
18415273Sjkh		data = hid_get_data(buf, h);
18515273Sjkh		if (h->logical_minimum < 0)
18615273Sjkh			printf("%d", (int)data);
18715273Sjkh		else
18815273Sjkh			printf("%u", data);
18915273Sjkh		pos += h->report_size;
19015273Sjkh	}
19115273Sjkh}
19215273Sjkh
19315273Sjkhvoid
19415273Sjkhdumpdata(int f, report_desc_t rd, int loop)
19515273Sjkh{
19615273Sjkh	struct hid_data *d;
19715273Sjkh	struct hid_item h, *hids, *n;
19815273Sjkh	int r, dlen;
19915273Sjkh	u_char *dbuf;
20015273Sjkh	static int one = 1;
20115273Sjkh	u_int32_t colls[100];
20215273Sjkh	int sp = 0;
20315273Sjkh	int report_id;
20415273Sjkh	char namebuf[10000], *namep;
20515273Sjkh
20615289Sjkh	hids = 0;
20715289Sjkh	for (d = hid_start_parse(rd, 1<<hid_input);
20815289Sjkh	     hid_get_item(d, &h); ) {
20915289Sjkh		if (h.kind == hid_collection)
21015289Sjkh			colls[++sp] = h.usage;
21115289Sjkh		else if (h.kind == hid_endcollection)
21215289Sjkh			--sp;
21315289Sjkh		if (h.kind != hid_input || (h.flags & HIO_CONST))
21415273Sjkh			continue;
21515289Sjkh		h.next = hids;
21615273Sjkh		h.collection = colls[sp];
21715273Sjkh		hids = malloc(sizeof *hids);
21815273Sjkh		*hids = h;
21915273Sjkh	}
22015273Sjkh	hid_end_parse(d);
22115273Sjkh	rev(&hids);
22215273Sjkh	dlen = hid_report_size(rd, 0, hid_input);
22315273Sjkh	dbuf = malloc(dlen);
22415273Sjkh	if (!loop)
22515273Sjkh		if (ioctl(f, USB_SET_IMMED, &one) < 0) {
22615290Sjkh			if (errno == EOPNOTSUPP)
22715290Sjkh				warnx("device does not support immediate mode, only changes reported.");
22815273Sjkh			else
22915142Sjkh				err(1, "USB_SET_IMMED");
23015273Sjkh		}
23115273Sjkh	do {
23215273Sjkh		r = read(f, dbuf, dlen);
23315273Sjkh		if (r != dlen) {
23415289Sjkh			err(1, "bad read %d != %d", r, dlen);
23512983Sjkh		}
23615289Sjkh		for (n = hids; n; n = n->next) {
23715273Sjkh			namep = namebuf;
23815289Sjkh			namep += sprintf(namep, "%s:%s.",
23915273Sjkh					 hid_usage_page(HID_PAGE(n->collection)),
24015273Sjkh					 hid_usage_in_page(n->collection));
24115354Sjkh			namep += sprintf(namep, "%s:%s",
24215273Sjkh					 hid_usage_page(HID_PAGE(n->usage)),
24315289Sjkh					 hid_usage_in_page(n->usage));
24415273Sjkh			if (all || gotname(namebuf)) {
24515290Sjkh				if (!noname)
24615273Sjkh					printf("%s=", namebuf);
24715273Sjkh				prdata(dbuf + (report_id != 0), n);
24815273Sjkh				printf("\n");
24915273Sjkh			}
25015273Sjkh		}
25115273Sjkh		if (loop)
25215273Sjkh			printf("\n");
25315273Sjkh	} while (loop);
25415273Sjkh	free(dbuf);
25515273Sjkh}
25615273Sjkh
25712983Sjkhint
25815273Sjkhmain(int argc, char **argv)
25915273Sjkh{
26015273Sjkh	int f;
26115273Sjkh	report_desc_t r;
26215273Sjkh	char devname[100], *dev = 0;
26315273Sjkh	int ch;
26415273Sjkh	int repdump = 0;
26515273Sjkh	int loop = 0;
26615273Sjkh	char *table = 0;
26715273Sjkh
26815273Sjkh	while ((ch = getopt(argc, argv, "af:lnrt:v")) != -1) {
26915273Sjkh		switch(ch) {
27015273Sjkh		case 'a':
27115273Sjkh			all++;
27215273Sjkh			break;
27315273Sjkh		case 'f':
27415273Sjkh			dev = optarg;
27515273Sjkh			break;
27615273Sjkh		case 'l':
27715273Sjkh			loop ^= 1;
27815273Sjkh			break;
27915273Sjkh		case 'n':
28015273Sjkh			noname++;
28115273Sjkh			break;
28215273Sjkh		case 'r':
28315273Sjkh			repdump++;
28415273Sjkh			break;
28515273Sjkh		case 't':
28615273Sjkh			table = optarg;
28715273Sjkh			break;
28815273Sjkh		case 'v':
28915273Sjkh			verbose++;
29015273Sjkh			break;
29115273Sjkh		case '?':
29215273Sjkh		default:
29315273Sjkh			usage();
29415273Sjkh		}
29515273Sjkh	}
29615273Sjkh	argc -= optind;
29715273Sjkh	argv += optind;
29815273Sjkh	if (dev == 0)
29915273Sjkh		usage();
30015273Sjkh	names = argv;
30115273Sjkh	nnames = argc;
30215273Sjkh
30315273Sjkh	if (nnames == 0 && !all && !repdump)
30415273Sjkh		usage();
30515273Sjkh
30615273Sjkh	if (dev[0] != '/') {
30715273Sjkh		if (isdigit(dev[0]))
30815273Sjkh			snprintf(devname, sizeof(devname), "/dev/uhid%s", dev);
30915273Sjkh		else
31015273Sjkh			snprintf(devname, sizeof(devname), "/dev/%s", dev);
31115273Sjkh		dev = devname;
31215273Sjkh	}
31315289Sjkh
31415289Sjkh	hid_init(table);
31515289Sjkh
31612983Sjkh	f = open(dev, O_RDWR);
31715289Sjkh	if (f < 0)
31815289Sjkh		err(1, "%s", dev);
31915380Sjkh
32015380Sjkh	r = hid_get_report_desc(f);
32115380Sjkh	if (r == 0)
32215380Sjkh		errx(1, "USB_GET_REPORT_DESC");
32315273Sjkh
32415354Sjkh	if (repdump) {
32515323Sjkh		printf("Report descriptor:\n");
32615323Sjkh		dumpitems(r);
32715289Sjkh	}
32815289Sjkh	if (nnames != 0 || all)
32915289Sjkh		dumpdata(f, r, loop);
33015289Sjkh
33115273Sjkh	hid_dispose_report_desc(r);
33215289Sjkh	exit(0);
33315289Sjkh}
33415273Sjkh