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