usbdevs.c revision 1.41 1 1.41 riastrad /* $NetBSD: usbdevs.c,v 1.41 2022/09/13 08:34:37 riastradh Exp $ */
2 1.2 augustss
3 1.2 augustss /*
4 1.2 augustss * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 1.2 augustss * All rights reserved.
6 1.2 augustss *
7 1.5 augustss * This code is derived from software contributed to The NetBSD Foundation
8 1.22 grant * by Lennart Augustsson (augustss (at) NetBSD.org).
9 1.2 augustss *
10 1.2 augustss * Redistribution and use in source and binary forms, with or without
11 1.2 augustss * modification, are permitted provided that the following conditions
12 1.2 augustss * are met:
13 1.2 augustss * 1. Redistributions of source code must retain the above copyright
14 1.2 augustss * notice, this list of conditions and the following disclaimer.
15 1.2 augustss * 2. Redistributions in binary form must reproduce the above copyright
16 1.2 augustss * notice, this list of conditions and the following disclaimer in the
17 1.2 augustss * documentation and/or other materials provided with the distribution.
18 1.2 augustss *
19 1.2 augustss * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.2 augustss * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.2 augustss * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.2 augustss * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.2 augustss * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.2 augustss * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.2 augustss * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.2 augustss * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.2 augustss * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.2 augustss * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.2 augustss * POSSIBILITY OF SUCH DAMAGE.
30 1.2 augustss */
31 1.2 augustss
32 1.33 skrll #include <sys/cdefs.h>
33 1.33 skrll #ifndef lint
34 1.41 riastrad __RCSID("$NetBSD: usbdevs.c,v 1.41 2022/09/13 08:34:37 riastradh Exp $");
35 1.33 skrll #endif
36 1.33 skrll
37 1.39 mrg #include <sys/types.h>
38 1.41 riastrad
39 1.39 mrg #include <sys/drvctlio.h>
40 1.39 mrg
41 1.41 riastrad #include <ctype.h>
42 1.41 riastrad #include <err.h>
43 1.41 riastrad #include <errno.h>
44 1.41 riastrad #include <fcntl.h>
45 1.41 riastrad #include <iconv.h>
46 1.41 riastrad #include <inttypes.h>
47 1.41 riastrad #include <langinfo.h>
48 1.41 riastrad #include <locale.h>
49 1.1 augustss #include <stdio.h>
50 1.1 augustss #include <stdlib.h>
51 1.1 augustss #include <string.h>
52 1.1 augustss #include <unistd.h>
53 1.39 mrg
54 1.1 augustss #include <dev/usb/usb.h>
55 1.1 augustss
56 1.1 augustss #define USBDEV "/dev/usb"
57 1.1 augustss
58 1.28 joerg static int verbose = 0;
59 1.28 joerg static int showdevs = 0;
60 1.1 augustss
61 1.32 mlelstv struct stringtable {
62 1.32 mlelstv int row, col;
63 1.32 mlelstv const char *string;
64 1.32 mlelstv };
65 1.32 mlelstv
66 1.41 riastrad static void usage(void) __dead;
67 1.41 riastrad static void getstrings(const struct stringtable *, int, int,
68 1.41 riastrad const char **, const char **);
69 1.28 joerg static void usbdev(int f, int a, int rec);
70 1.28 joerg static void usbdump(int f);
71 1.28 joerg static void dumpone(char *name, int f, int addr);
72 1.1 augustss
73 1.28 joerg static void
74 1.28 joerg usage(void)
75 1.1 augustss {
76 1.20 simonb
77 1.27 wiz fprintf(stderr, "usage: %s [-dv] [-a addr] [-f dev]\n",
78 1.17 cgd getprogname());
79 1.29 jakllsch exit(EXIT_FAILURE);
80 1.1 augustss }
81 1.1 augustss
82 1.28 joerg static char done[USB_MAX_DEVICES];
83 1.28 joerg static int indent;
84 1.26 drochner #define MAXLEN USB_MAX_ENCODED_STRING_LEN /* assume can't grow over UTF-8 */
85 1.28 joerg static char vendor[MAXLEN], product[MAXLEN], serial[MAXLEN];
86 1.26 drochner
87 1.26 drochner static void
88 1.26 drochner u2t(const char *utf8str, char *termstr)
89 1.26 drochner {
90 1.26 drochner static iconv_t ic;
91 1.26 drochner static int iconv_inited = 0;
92 1.26 drochner size_t insz, outsz, icres;
93 1.26 drochner
94 1.26 drochner if (!iconv_inited) {
95 1.26 drochner setlocale(LC_ALL, "");
96 1.26 drochner ic = iconv_open(nl_langinfo(CODESET), "UTF-8");
97 1.26 drochner if (ic == (iconv_t)-1)
98 1.26 drochner ic = iconv_open("ASCII", "UTF-8"); /* g.c.d. */
99 1.26 drochner iconv_inited = 1;
100 1.26 drochner }
101 1.26 drochner if (ic != (iconv_t)-1) {
102 1.26 drochner insz = strlen(utf8str);
103 1.26 drochner outsz = MAXLEN - 1;
104 1.38 kamil icres = iconv(ic, __UNCONST(&utf8str), &insz, &termstr,
105 1.41 riastrad &outsz);
106 1.26 drochner if (icres != (size_t)-1) {
107 1.26 drochner *termstr = '\0';
108 1.26 drochner return;
109 1.26 drochner }
110 1.26 drochner }
111 1.26 drochner strcpy(termstr, "(invalid)");
112 1.26 drochner }
113 1.1 augustss
114 1.32 mlelstv struct stringtable class_strings[] = {
115 1.32 mlelstv { UICLASS_UNSPEC, -1, "Unspecified" },
116 1.32 mlelstv
117 1.32 mlelstv { UICLASS_AUDIO, -1, "Audio" },
118 1.32 mlelstv { UICLASS_AUDIO, UISUBCLASS_AUDIOCONTROL, "Audio Control" },
119 1.32 mlelstv { UICLASS_AUDIO, UISUBCLASS_AUDIOSTREAM, "Audio Streaming" },
120 1.32 mlelstv { UICLASS_AUDIO, UISUBCLASS_MIDISTREAM, "MIDI Streaming" },
121 1.32 mlelstv
122 1.32 mlelstv { UICLASS_CDC, -1, "Communications and CDC Control" },
123 1.32 mlelstv { UICLASS_CDC, UISUBCLASS_DIRECT_LINE_CONTROL_MODEL, "Direct Line" },
124 1.32 mlelstv { UICLASS_CDC, UISUBCLASS_ABSTRACT_CONTROL_MODEL, "Abstract" },
125 1.32 mlelstv { UICLASS_CDC, UISUBCLASS_TELEPHONE_CONTROL_MODEL, "Telephone" },
126 1.32 mlelstv { UICLASS_CDC, UISUBCLASS_MULTICHANNEL_CONTROL_MODEL, "Multichannel" },
127 1.32 mlelstv { UICLASS_CDC, UISUBCLASS_CAPI_CONTROLMODEL, "CAPI" },
128 1.32 mlelstv { UICLASS_CDC, UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, "Ethernet Networking" },
129 1.32 mlelstv { UICLASS_CDC, UISUBCLASS_ATM_NETWORKING_CONTROL_MODEL, "ATM Networking" },
130 1.32 mlelstv
131 1.32 mlelstv { UICLASS_HID, -1, "Human Interface Device" },
132 1.32 mlelstv { UICLASS_HID, UISUBCLASS_BOOT, "Boot" },
133 1.32 mlelstv
134 1.32 mlelstv { UICLASS_PHYSICAL, -1, "Physical" },
135 1.32 mlelstv
136 1.32 mlelstv { UICLASS_IMAGE, -1, "Image" },
137 1.32 mlelstv
138 1.32 mlelstv { UICLASS_PRINTER, -1, "Printer" },
139 1.32 mlelstv { UICLASS_PRINTER, UISUBCLASS_PRINTER, "Printer" },
140 1.32 mlelstv
141 1.32 mlelstv { UICLASS_MASS, -1, "Mass Storage" },
142 1.32 mlelstv { UICLASS_MASS, UISUBCLASS_RBC, "RBC" },
143 1.32 mlelstv { UICLASS_MASS, UISUBCLASS_SFF8020I, "SFF8020I" },
144 1.32 mlelstv { UICLASS_MASS, UISUBCLASS_QIC157, "QIC157" },
145 1.32 mlelstv { UICLASS_MASS, UISUBCLASS_UFI, "UFI" },
146 1.32 mlelstv { UICLASS_MASS, UISUBCLASS_SFF8070I, "SFF8070I" },
147 1.32 mlelstv { UICLASS_MASS, UISUBCLASS_SCSI, "SCSI" },
148 1.32 mlelstv { UICLASS_MASS, UISUBCLASS_SCSI, "SCSI" },
149 1.32 mlelstv
150 1.32 mlelstv { UICLASS_HUB, -1, "Hub" },
151 1.32 mlelstv { UICLASS_HUB, UISUBCLASS_HUB, "Hub" },
152 1.32 mlelstv
153 1.32 mlelstv { UICLASS_CDC_DATA, -1, "CDC-Data" },
154 1.32 mlelstv { UICLASS_CDC_DATA, UISUBCLASS_DATA, "Data" },
155 1.32 mlelstv
156 1.32 mlelstv { UICLASS_SMARTCARD, -1, "Smart Card" },
157 1.32 mlelstv
158 1.32 mlelstv { UICLASS_SECURITY, -1, "Content Security" },
159 1.32 mlelstv
160 1.32 mlelstv { UICLASS_VIDEO, -1, "Video" },
161 1.32 mlelstv { UICLASS_VIDEO, UISUBCLASS_VIDEOCONTROL, "Video Control" },
162 1.32 mlelstv { UICLASS_VIDEO, UISUBCLASS_VIDEOSTREAMING, "Video Streaming" },
163 1.32 mlelstv { UICLASS_VIDEO, UISUBCLASS_VIDEOCOLLECTION, "Video Collection" },
164 1.32 mlelstv
165 1.32 mlelstv #ifdef notyet
166 1.32 mlelstv { UICLASS_HEALTHCARE, -1, "Personal Healthcare" },
167 1.32 mlelstv { UICLASS_AVDEVICE, -1, "Audio/Video Device" },
168 1.32 mlelstv { UICLASS_BILLBOARD, -1, "Billboard" },
169 1.32 mlelstv #endif
170 1.32 mlelstv
171 1.32 mlelstv { UICLASS_DIAGNOSTIC, -1, "Diagnostic" },
172 1.32 mlelstv { UICLASS_WIRELESS, -1, "Wireless" },
173 1.32 mlelstv { UICLASS_WIRELESS, UISUBCLASS_RF, "Radio Frequency" },
174 1.32 mlelstv
175 1.32 mlelstv #ifdef notyet
176 1.32 mlelstv { UICLASS_MISC, -1, "Miscellaneous" },
177 1.32 mlelstv #endif
178 1.32 mlelstv
179 1.32 mlelstv { UICLASS_APPL_SPEC, -1, "Application Specific" },
180 1.32 mlelstv { UICLASS_APPL_SPEC, UISUBCLASS_FIRMWARE_DOWNLOAD, "Firmware Download" },
181 1.32 mlelstv { UICLASS_APPL_SPEC, UISUBCLASS_IRDA, "Irda" },
182 1.32 mlelstv
183 1.32 mlelstv { UICLASS_VENDOR, -1, "Vendor Specific" },
184 1.32 mlelstv
185 1.32 mlelstv { -1, -1, NULL }
186 1.32 mlelstv };
187 1.32 mlelstv
188 1.32 mlelstv static void
189 1.41 riastrad getstrings(const struct stringtable *table, int row, int col,
190 1.41 riastrad const char **rp, const char **cp)
191 1.41 riastrad {
192 1.32 mlelstv static char rbuf[5], cbuf[5];
193 1.32 mlelstv
194 1.32 mlelstv snprintf(rbuf, sizeof(rbuf), "0x%02x", row);
195 1.32 mlelstv snprintf(cbuf, sizeof(cbuf), "0x%02x", col);
196 1.32 mlelstv
197 1.32 mlelstv *rp = rbuf;
198 1.32 mlelstv *cp = cbuf;
199 1.32 mlelstv
200 1.32 mlelstv while (table->string != NULL) {
201 1.32 mlelstv if (table->row == row) {
202 1.32 mlelstv if (table->col == -1)
203 1.32 mlelstv *rp = table->string;
204 1.32 mlelstv else if (table->col == col)
205 1.32 mlelstv *cp = table->string;
206 1.32 mlelstv } else if (table->row > row)
207 1.32 mlelstv break;
208 1.32 mlelstv
209 1.32 mlelstv ++table;
210 1.32 mlelstv }
211 1.32 mlelstv }
212 1.32 mlelstv
213 1.28 joerg static void
214 1.14 augustss usbdev(int f, int a, int rec)
215 1.1 augustss {
216 1.1 augustss struct usb_device_info di;
217 1.35 skrll int e, i;
218 1.1 augustss
219 1.19 christos di.udi_addr = a;
220 1.1 augustss e = ioctl(f, USB_DEVICEINFO, &di);
221 1.10 augustss if (e) {
222 1.10 augustss if (errno != ENXIO)
223 1.10 augustss printf("addr %d: I/O error\n", a);
224 1.1 augustss return;
225 1.10 augustss }
226 1.10 augustss printf("addr %d: ", a);
227 1.1 augustss done[a] = 1;
228 1.1 augustss if (verbose) {
229 1.19 christos switch (di.udi_speed) {
230 1.18 augustss case USB_SPEED_LOW: printf("low speed, "); break;
231 1.18 augustss case USB_SPEED_FULL: printf("full speed, "); break;
232 1.18 augustss case USB_SPEED_HIGH: printf("high speed, "); break;
233 1.31 skrll case USB_SPEED_SUPER: printf("super speed, "); break;
234 1.36 jmcneill case USB_SPEED_SUPER_PLUS: printf("super speed+, "); break;
235 1.18 augustss default: break;
236 1.18 augustss }
237 1.19 christos if (di.udi_power)
238 1.19 christos printf("power %d mA, ", di.udi_power);
239 1.1 augustss else
240 1.1 augustss printf("self powered, ");
241 1.19 christos if (di.udi_config)
242 1.19 christos printf("config %d, ", di.udi_config);
243 1.1 augustss else
244 1.1 augustss printf("unconfigured, ");
245 1.1 augustss }
246 1.26 drochner u2t(di.udi_product, product);
247 1.26 drochner u2t(di.udi_vendor, vendor);
248 1.26 drochner u2t(di.udi_serial, serial);
249 1.6 augustss if (verbose) {
250 1.32 mlelstv printf("%s(0x%04x), %s(0x%04x), rev %s(0x%04x)",
251 1.41 riastrad product, di.udi_productNo,
252 1.41 riastrad vendor, di.udi_vendorNo,
253 1.41 riastrad di.udi_release, di.udi_releaseNo);
254 1.24 augustss if (di.udi_serial[0])
255 1.26 drochner printf(", serial %s", serial);
256 1.6 augustss } else
257 1.26 drochner printf("%s, %s", product, vendor);
258 1.1 augustss printf("\n");
259 1.32 mlelstv if (verbose > 1 && di.udi_class != UICLASS_UNSPEC) {
260 1.32 mlelstv const char *cstr, *sstr;
261 1.41 riastrad getstrings(class_strings, di.udi_class, di.udi_subclass,
262 1.41 riastrad &cstr, &sstr);
263 1.32 mlelstv printf("%*s %s(0x%02x), %s(0x%02x), proto %u\n", indent, "",
264 1.41 riastrad cstr, di.udi_class, sstr, di.udi_subclass,
265 1.41 riastrad di.udi_protocol);
266 1.32 mlelstv }
267 1.15 augustss if (showdevs) {
268 1.41 riastrad for (i = 0; i < USB_MAX_DEVNAMES; i++) {
269 1.41 riastrad if (di.udi_devnames[i][0]) {
270 1.15 augustss printf("%*s %s\n", indent, "",
271 1.41 riastrad di.udi_devnames[i]);
272 1.41 riastrad }
273 1.41 riastrad }
274 1.15 augustss }
275 1.1 augustss if (!rec)
276 1.1 augustss return;
277 1.35 skrll
278 1.41 riastrad unsigned int p, nports = di.udi_nports;
279 1.35 skrll
280 1.41 riastrad for (p = 0; p < nports && p < __arraycount(di.udi_ports); p++) {
281 1.19 christos int s = di.udi_ports[p];
282 1.1 augustss if (s >= USB_MAX_DEVICES) {
283 1.1 augustss if (verbose) {
284 1.41 riastrad printf("%*sport %d %s\n", indent + 1, "",
285 1.41 riastrad p + 1,
286 1.41 riastrad s == USB_PORT_ENABLED ? "enabled" :
287 1.41 riastrad s == USB_PORT_SUSPENDED ? "suspended" :
288 1.41 riastrad s == USB_PORT_POWERED ? "powered" :
289 1.41 riastrad s == USB_PORT_DISABLED ? "disabled" :
290 1.41 riastrad "???");
291 1.1 augustss }
292 1.1 augustss continue;
293 1.1 augustss }
294 1.1 augustss indent++;
295 1.1 augustss printf("%*s", indent, "");
296 1.1 augustss if (verbose)
297 1.41 riastrad printf("port %d ", p + 1);
298 1.10 augustss if (s == 0)
299 1.10 augustss printf("addr 0 should never happen!\n");
300 1.10 augustss else
301 1.10 augustss usbdev(f, s, 1);
302 1.1 augustss indent--;
303 1.1 augustss }
304 1.1 augustss }
305 1.1 augustss
306 1.28 joerg static void
307 1.14 augustss usbdump(int f)
308 1.1 augustss {
309 1.1 augustss int a;
310 1.1 augustss
311 1.30 jakllsch for (a = 0; a < USB_MAX_DEVICES; a++) {
312 1.1 augustss if (!done[a])
313 1.1 augustss usbdev(f, a, 1);
314 1.1 augustss }
315 1.1 augustss }
316 1.1 augustss
317 1.28 joerg static void
318 1.14 augustss dumpone(char *name, int f, int addr)
319 1.1 augustss {
320 1.41 riastrad
321 1.1 augustss if (verbose)
322 1.1 augustss printf("Controller %s:\n", name);
323 1.1 augustss indent = 0;
324 1.1 augustss memset(done, 0, sizeof done);
325 1.30 jakllsch if (addr >= 0)
326 1.1 augustss usbdev(f, addr, 0);
327 1.1 augustss else
328 1.1 augustss usbdump(f);
329 1.1 augustss }
330 1.1 augustss
331 1.39 mrg static int
332 1.39 mrg getusbcount_device(int fd, const char *dev, int depth)
333 1.39 mrg {
334 1.39 mrg struct devlistargs laa = {
335 1.41 riastrad .l_childname = NULL,
336 1.41 riastrad .l_children = 0,
337 1.39 mrg };
338 1.39 mrg size_t i;
339 1.39 mrg size_t children;
340 1.39 mrg int nbusses = 0;
341 1.39 mrg
342 1.39 mrg if (depth && (dev == NULL || *dev == '\0'))
343 1.39 mrg return 0;
344 1.39 mrg
345 1.39 mrg /*
346 1.39 mrg * Look for children that match "usb[0-9]*". Could maybe
347 1.39 mrg * simply return 1 here, but there's always a chance that
348 1.39 mrg * someone has eg, a USB to PCI bridge, with a USB
349 1.39 mrg * controller behind PCI.
350 1.39 mrg */
351 1.39 mrg if (strncmp(dev, "usb", 3) == 0 && isdigit((int)dev[3]))
352 1.39 mrg nbusses++;
353 1.39 mrg
354 1.39 mrg strlcpy(laa.l_devname, dev, sizeof(laa.l_devname));
355 1.39 mrg
356 1.39 mrg if (ioctl(fd, DRVLISTDEV, &laa) == -1)
357 1.39 mrg err(EXIT_FAILURE, "DRVLISTDEV");
358 1.39 mrg children = laa.l_children;
359 1.39 mrg
360 1.41 riastrad laa.l_childname = calloc(children, sizeof(laa.l_childname[0]));
361 1.39 mrg if (laa.l_childname == NULL)
362 1.39 mrg err(EXIT_FAILURE, "out of memory");
363 1.39 mrg if (ioctl(fd, DRVLISTDEV, &laa) == -1)
364 1.39 mrg err(EXIT_FAILURE, "DRVLISTDEV");
365 1.39 mrg if (laa.l_children > children)
366 1.39 mrg err(EXIT_FAILURE, "DRVLISTDEV: number of children grew");
367 1.39 mrg
368 1.39 mrg for (i = 0; i < laa.l_children; i++) {
369 1.41 riastrad nbusses += getusbcount_device(fd, laa.l_childname[i],
370 1.41 riastrad depth + 1);
371 1.39 mrg }
372 1.39 mrg
373 1.39 mrg return nbusses;
374 1.39 mrg }
375 1.39 mrg
376 1.1 augustss int
377 1.14 augustss main(int argc, char **argv)
378 1.1 augustss {
379 1.40 christos int ch, i, f, error;
380 1.1 augustss char buf[50];
381 1.29 jakllsch char *dev = NULL;
382 1.30 jakllsch int addr = -1;
383 1.4 augustss int ncont;
384 1.1 augustss
385 1.16 augustss while ((ch = getopt(argc, argv, "a:df:v?")) != -1) {
386 1.41 riastrad switch (ch) {
387 1.1 augustss case 'a':
388 1.40 christos addr = strtoi(optarg, NULL, 10, 0, USB_MAX_DEVICES - 1,
389 1.40 christos &error);
390 1.40 christos if (error) {
391 1.40 christos errc(EXIT_FAILURE, error,
392 1.40 christos "Bad value for device address: `%s'",
393 1.40 christos optarg);
394 1.40 christos }
395 1.15 augustss break;
396 1.15 augustss case 'd':
397 1.15 augustss showdevs++;
398 1.1 augustss break;
399 1.1 augustss case 'f':
400 1.1 augustss dev = optarg;
401 1.1 augustss break;
402 1.1 augustss case 'v':
403 1.32 mlelstv verbose++;
404 1.1 augustss break;
405 1.1 augustss case '?':
406 1.1 augustss default:
407 1.1 augustss usage();
408 1.1 augustss }
409 1.1 augustss }
410 1.1 augustss argc -= optind;
411 1.1 augustss argv += optind;
412 1.1 augustss
413 1.29 jakllsch if (dev == NULL) {
414 1.39 mrg int nbusses;
415 1.39 mrg int fd = open(DRVCTLDEV, O_RDONLY, 0);
416 1.39 mrg
417 1.39 mrg /* If no drvctl configured, default to 16. */
418 1.39 mrg if (fd != -1)
419 1.39 mrg nbusses = getusbcount_device(fd, "", 0);
420 1.39 mrg else
421 1.39 mrg nbusses = 16;
422 1.39 mrg close(fd);
423 1.39 mrg
424 1.39 mrg for (ncont = 0, i = 0; i < nbusses; i++) {
425 1.21 itojun snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
426 1.1 augustss f = open(buf, O_RDONLY);
427 1.1 augustss if (f >= 0) {
428 1.1 augustss dumpone(buf, f, addr);
429 1.1 augustss close(f);
430 1.3 augustss } else {
431 1.11 augustss if (errno == ENOENT || errno == ENXIO)
432 1.11 augustss continue;
433 1.11 augustss warn("%s", buf);
434 1.1 augustss }
435 1.11 augustss ncont++;
436 1.1 augustss }
437 1.41 riastrad if (verbose && ncont == 0) {
438 1.17 cgd printf("%s: no USB controllers found\n",
439 1.17 cgd getprogname());
440 1.41 riastrad }
441 1.1 augustss } else {
442 1.1 augustss f = open(dev, O_RDONLY);
443 1.1 augustss if (f >= 0)
444 1.1 augustss dumpone(dev, f, addr);
445 1.1 augustss else
446 1.1 augustss err(1, "%s", dev);
447 1.1 augustss }
448 1.40 christos return EXIT_SUCCESS;
449 1.1 augustss }
450