1/*	$OpenBSD: usbdevs.c,v 1.36 2022/12/28 21:30:19 jmc Exp $	*/
2/*	$NetBSD: usbdevs.c,v 1.19 2002/02/21 00:34:31 christos Exp $	*/
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 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/types.h>
34#include <dev/usb/usb.h>
35
36#include <err.h>
37#include <errno.h>
38#include <fcntl.h>
39#include <limits.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <vis.h>
43#include <string.h>
44#include <unistd.h>
45
46#ifndef nitems
47#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
48#endif
49
50#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
51
52#define USBDEV "/dev/usb"
53
54int verbose = 0;
55char done[USB_MAX_DEVICES];
56
57void usage(void);
58void dump_device(int, uint8_t);
59void dump_controller(char *, int, uint8_t);
60int main(int, char **);
61
62extern char *__progname;
63
64void
65usage(void)
66{
67	fprintf(stderr, "usage: %s [-v] [-a addr] [-d usbdev]\n", __progname);
68	exit(1);
69}
70
71void
72dump_device(int fd, uint8_t addr)
73{
74	struct usb_device_info di;
75	int i;
76	char vv[sizeof(di.udi_vendor)*4], vp[sizeof(di.udi_product)*4];
77	char vr[sizeof(di.udi_release)*4], vs[sizeof(di.udi_serial)*4];
78
79	di.udi_addr = addr;
80	if (ioctl(fd, USB_DEVICEINFO, &di) == -1) {
81		if (errno != ENXIO)
82			warn("addr %u", addr);
83		return;
84	}
85
86	done[addr] = 1;
87
88	strvis(vv, di.udi_vendor, VIS_CSTYLE);
89	strvis(vp, di.udi_product, VIS_CSTYLE);
90	printf("addr %02u: %04x:%04x %s, %s", addr,
91	    di.udi_vendorNo, di.udi_productNo,
92	    vv, vp);
93
94	if (verbose) {
95		printf("\n\t ");
96		switch (di.udi_speed) {
97		case USB_SPEED_LOW:
98			printf("low speed");
99			break;
100		case USB_SPEED_FULL:
101			printf("full speed");
102			break;
103		case USB_SPEED_HIGH:
104			printf("high speed");
105			break;
106		case USB_SPEED_SUPER:
107			printf("super speed");
108			break;
109		default:
110			break;
111		}
112
113		if (di.udi_power)
114			printf(", power %d mA", di.udi_power);
115		else
116			printf(", self powered");
117
118		if (di.udi_config)
119			printf(", config %d", di.udi_config);
120		else
121			printf(", unconfigured");
122
123		strvis(vr, di.udi_release, VIS_CSTYLE);
124		printf(", rev %s", vr);
125
126		if (di.udi_serial[0] != '\0') {
127			strvis(vs, di.udi_serial, VIS_CSTYLE);
128			printf(", iSerial %s", vs);
129		}
130	}
131	printf("\n");
132
133	if (verbose)
134		for (i = 0; i < USB_MAX_DEVNAMES; i++)
135			if (di.udi_devnames[i][0] != '\0')
136				printf("\t driver: %s\n", di.udi_devnames[i]);
137
138	if (verbose > 1) {
139		int port, nports;
140
141		nports = MINIMUM(di.udi_nports, nitems(di.udi_ports));
142		for (port = 0; port < nports; port++) {
143			uint16_t status, change;
144
145			status = di.udi_ports[port] & 0xffff;
146			change = di.udi_ports[port] >> 16;
147
148			printf("\t port %02u: %04x.%04x", port+1, change,
149			    status);
150
151			if (status & UPS_CURRENT_CONNECT_STATUS)
152				printf(" connect");
153
154			if (status & UPS_PORT_ENABLED)
155				printf(" enabled");
156
157			if (status & UPS_SUSPEND)
158				printf(" suspend");
159
160			if (status & UPS_OVERCURRENT_INDICATOR)
161				printf(" overcurrent");
162
163			if (di.udi_speed < USB_SPEED_SUPER) {
164				if (status & UPS_PORT_L1)
165					printf(" l1");
166
167				if (status & UPS_PORT_POWER)
168					printf(" power");
169			} else {
170				if (status & UPS_PORT_POWER_SS)
171					printf(" power");
172
173				switch (UPS_PORT_LS_GET(status)) {
174				case UPS_PORT_LS_U0:
175					printf(" U0");
176					break;
177				case UPS_PORT_LS_U1:
178					printf(" U1");
179					break;
180				case UPS_PORT_LS_U2:
181					printf(" U2");
182					break;
183				case UPS_PORT_LS_U3:
184					printf(" U3");
185					break;
186				case UPS_PORT_LS_SS_DISABLED:
187					printf(" SS.disabled");
188					break;
189				case UPS_PORT_LS_RX_DETECT:
190					printf(" Rx.detect");
191					break;
192				case UPS_PORT_LS_SS_INACTIVE:
193					printf(" ss.inactive");
194					break;
195				case UPS_PORT_LS_POLLING:
196					printf(" polling");
197					break;
198				case UPS_PORT_LS_RECOVERY:
199					printf(" recovery");
200					break;
201				case UPS_PORT_LS_HOT_RESET:
202					printf(" hot.reset");
203					break;
204				case UPS_PORT_LS_COMP_MOD:
205					printf(" comp.mod");
206					break;
207				case UPS_PORT_LS_LOOPBACK:
208					printf(" loopback");
209					break;
210				}
211			}
212
213			printf("\n");
214		}
215	}
216}
217
218void
219dump_controller(char *name, int fd, uint8_t addr)
220{
221	memset(done, 0, sizeof(done));
222
223	if (addr) {
224		dump_device(fd, addr);
225		return;
226	}
227
228	printf("Controller %s:\n", name);
229	for (addr = 1; addr < USB_MAX_DEVICES; addr++)
230		if (!done[addr])
231			dump_device(fd, addr);
232}
233
234int
235main(int argc, char **argv)
236{
237	int ch, fd;
238	char *controller = NULL;
239	uint8_t addr = 0;
240	const char *errstr;
241
242	while ((ch = getopt(argc, argv, "a:d:v")) != -1) {
243		switch (ch) {
244		case 'a':
245			addr = strtonum(optarg, 1, USB_MAX_DEVICES-1, &errstr);
246			if (errstr)
247				errx(1, "addr %s", errstr);
248			break;
249		case 'd':
250			controller = optarg;
251			break;
252		case 'v':
253			verbose++;
254			break;
255		default:
256			usage();
257		}
258	}
259	argc -= optind;
260	argv += optind;
261
262	if (argc != 0)
263		usage();
264
265	if (unveil("/dev", "r") == -1)
266		err(1, "unveil /dev");
267	if (unveil(NULL, NULL) == -1)
268		err(1, "unveil");
269
270	if (controller == NULL) {
271		int i;
272		int ncont = 0;
273
274		for (i = 0; i < 10; i++) {
275			char path[PATH_MAX];
276
277			snprintf(path, sizeof(path), "%s%d", USBDEV, i);
278			if ((fd = open(path, O_RDONLY)) < 0) {
279				if (errno != ENOENT && errno != ENXIO)
280					warn("%s", path);
281				continue;
282			}
283
284			dump_controller(path, fd, addr);
285			close(fd);
286			ncont++;
287		}
288		if (verbose && ncont == 0)
289			printf("%s: no USB controllers found\n",
290			    __progname);
291	} else {
292		if ((fd = open(controller, O_RDONLY)) < 0)
293			err(1, "%s", controller);
294
295		dump_controller(controller, fd, addr);
296		close(fd);
297	}
298
299	return 0;
300}
301