xref: /openbsd/usr.sbin/usbdevs/usbdevs.c (revision bc5a8259)
1 /*	$OpenBSD: usbdevs.c,v 1.34 2021/07/12 15:09:22 beck 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 
54 int verbose = 0;
55 char done[USB_MAX_DEVICES];
56 
57 void usage(void);
58 void dump_device(int, uint8_t);
59 void dump_controller(char *, int, uint8_t);
60 int main(int, char **);
61 
62 extern char *__progname;
63 
64 void
65 usage(void)
66 {
67 	fprintf(stderr, "usage: %s [-v] [-a addr] [-d usbdev]\n", __progname);
68 	exit(1);
69 }
70 
71 void
72 dump_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(" supsend");
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 
218 void
219 dump_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 
234 int
235 main(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