--- usbutils-0.11/lsusb.c 2002-08-05 23:35:21.000000000 -0700 +++ usbutils/lsusb.c 2004-02-14 13:00:41.531597608 -0800 @@ -58,8 +58,8 @@ #define _GNU_SOURCE #include -#define CTRL_RETRIES 50 -#define CTRL_TIMEOUT 100 /* milliseconds */ +#define CTRL_RETRIES 2 +#define CTRL_TIMEOUT (5*1000) /* milliseconds */ #define USB_DT_CS_DEVICE 0x21 #define USB_DT_CS_CONFIG 0x22 @@ -106,7 +106,6 @@ static int usb_control_msg(int fd, u_int ctrl.value = value; ctrl.index = index; ctrl.length = size; - ctrl.timeout = 1000; ctrl.data = data; ctrl.timeout = CTRL_TIMEOUT; try = 0; @@ -119,10 +118,11 @@ static int usb_control_msg(int fd, u_int /* ---------------------------------------------------------------------- */ +static int dev_has_strings; + static int get_string(int fd, char *buf, size_t size, u_int8_t id, u_int16_t lang) { unsigned char b[256]; - wchar_t w[128]; unsigned int i; int ret; @@ -133,17 +133,38 @@ static int get_string(int fd, char *buf, if (!id || fd == -1) return 0; - b[0] = b[1] = 0xbf; - ret = usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | id, 0, sizeof(b), b); + b[0] = b[1] = 0; + + /* try reading all at once ... some devices misbehave here */ + ret = usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | id, lang, sizeof(b), b); + if (ret > 0) + goto got_all; + + /* read string length first ... most devices handle this ok */ + ret = usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | id, lang, 2, b); + if (ret < 0) { + if (open_mode == O_RDWR) + fprintf(stderr, "cannot peek string descriptor %d, error = %s(%d)\n", id, strerror(errno), errno); + return 0; + } + if (ret < 2 || b[0] < 2 || b[1] != USB_DT_STRING) { + fprintf(stderr, "string descriptor %d invalid (%02x %02x; len=%d)\n", id, b[0], b[1], ret); + return 0; + } + + ret = usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | id, lang, b[0], b); if (ret < 0) { if (open_mode == O_RDWR) fprintf(stderr, "cannot get string descriptor %d, error = %s(%d)\n", id, strerror(errno), errno); return 0; } + +got_all: if (ret < 2 || b[0] < 2 || b[1] != USB_DT_STRING) { fprintf(stderr, "string descriptor %d invalid (%02x %02x; len=%d)\n", id, b[0], b[1], ret); return 0; } + dev_has_strings = 1; #if 0 for (i = 0; i < ((b[0] - 2) / 2); i++) w[i] = b[2+2*i] | (b[3+2*i] << 8); @@ -151,7 +172,9 @@ static int get_string(int fd, char *buf, return wcstombs(buf, w, size); #else for (i = 0; i < ((b[0] - 2) / 2); i++) - buf[i] = b[2+2*i]; + buf[i] = b[3+2*i] + ? '?' /* character's not available in iso-8859/1 */ + : b[2+2*i]; buf[i] = 0; return i; #endif @@ -297,21 +320,25 @@ static void dump_device(int fd, unsigned dump_junk(buf, " ", 18); } -static void dump_config(int fd, unsigned char *buf) +static void dump_config(int fd, unsigned char *buf, u_int16_t lang) { + char str[128]; + if (buf[1] != USB_DT_CONFIG) printf(" Warning: Invalid descriptor\n"); else if (buf[0] < 9) printf(" Warning: Descriptor too short\n"); + get_string(fd, str, sizeof(str), buf[6], lang); printf(" Configuration Descriptor:\n" " bLength %5u\n" " bDescriptorType %5u\n" " wTotalLength %5u\n" " bNumInterfaces %5u\n" " bConfigurationValue %5u\n" - " iConfiguration %5u\n" + " iConfiguration %5u %s\n" " bmAttributes 0x%02x\n", - buf[0], buf[1], buf[2] | (buf[3] << 8), buf[4], buf[5], buf[6], buf[7]); + buf[0], buf[1], buf[2] | (buf[3] << 8), buf[4], buf[5], + buf[6], str, buf[7]); if (buf[7] & 0x40) printf(" Self Powered\n"); if (buf[7] & 0x20) @@ -352,6 +379,9 @@ static void dump_endpoint(int fd, unsign { static const char *typeattr[] = { "Control", "Isochronous", "Bulk", "Interrupt" }; static const char *syncattr[] = { "none", "Asynchronous", "Adaptive", "Synchronous" }; + static const char *usage[] = { "Data", "Feedback", "Implicit feedback Data", "(reserved)" }; + static const char *hb[] = { "once", "twice", "three times", "(?)" }; + unsigned wMaxPacket = buf[4] | (buf[5] << 8); if (buf[1] != USB_DT_ENDPOINT) printf(" Warning: Invalid descriptor\n"); @@ -364,15 +394,19 @@ static void dump_endpoint(int fd, unsign " bmAttributes %5u\n" " Transfer Type %s\n" " Synch Type %s\n" - " wMaxPacketSize %5u\n" + " Usage Type %s\n" + " wMaxPacketSize 0x%04x bytes %d %s\n" " bInterval %5u\n", buf[0], buf[1], buf[2], buf[2] & 15, (buf[2] & 0x80) ? "IN" : "OUT", buf[3], typeattr[buf[3] & 3], syncattr[(buf[3] >> 2) & 3], - buf[4] | (buf[5] << 8), buf[6]); + usage[(buf[3] >> 4) & 3], + wMaxPacket, wMaxPacket & 0x3ff, hb [(wMaxPacket >> 11) & 3], + buf[6]); if (buf[0] < 9) { dump_junk(buf, " ", 7); return; } + /* only for audio endpoints */ printf(" bRefresh %5u\n" " bSynchAddress %5u\n", buf[7], buf[8]); @@ -1017,23 +1051,53 @@ static void dump_midistreaming_endpoint( dump_junk(buf, " ", 4+buf[3]); } - -static void dump_hub(char *p) +static void dump_hub(char *prefix, unsigned char *p, int has_tt) { unsigned int l, i, j; + unsigned wHubChar = (p[4] << 8) | p[3]; - printf(" Hub Descriptor:\n"); - printf(" bLength %3u\n",p[0]); - printf(" bDesriptorType %3u\n",p[1]); - printf(" nNbrPorts %3u\n",p[2]); - printf(" wHubCharacteristic 0x%02x 0x%02x\n", p[3],p[4]); - printf(" bPwrOn2PwrGood %3u * 2 milli seconds\n",p[5]); - printf(" bHubContrCurrent %3u milli Ampere\n",p[6]); + printf("%sHub Descriptor:\n", prefix); + printf("%s bLength %3u\n", prefix, p[0]); + printf("%s bDescriptorType %3u\n", prefix, p[1]); + printf("%s nNbrPorts %3u\n", prefix, p[2]); + printf("%s wHubCharacteristic 0x%04x\n", prefix, wHubChar); + switch (wHubChar & 0x03) { + case 0: + printf("%s Ganged power switching\n", prefix); + break; + case 1: + printf("%s Per-port power switching\n", prefix); + break; + default: + printf("%s No power switching (usb 1.0)\n", prefix); + break; + } + if (wHubChar & 0x04) + printf("%s Compound device\n", prefix); + switch ((wHubChar >> 3) & 0x03) { + case 0: + printf("%s Ganged overcurrent protection\n", prefix); + break; + case 1: + printf("%s Per-port overcurrent protection\n", prefix); + break; + default: + printf("%s No overcurrent protection\n", prefix); + break; + } + if (has_tt) { + l = (wHubChar >> 5) & 0x03; + printf("%s TT think time %d FS bits\n", prefix, (l + 1) * 8); + } + if (wHubChar & (1<<7)) + printf("%s Port indicators\n", prefix); + printf("%s bPwrOn2PwrGood %3u * 2 milli seconds\n", prefix, p[5]); + printf("%s bHubContrCurrent %3u milli Ampere\n", prefix, p[6]); l= (p[2] >> 3) + 1; /* this determines the variable number of bytes following */ - printf(" DeviceRemovable "); + printf("%s DeviceRemovable ", prefix); for(i = 0; i < l; i++) printf(" 0x%02x", p[7+i]); - printf("\n PortPwrCtrlMask "); + printf("\n%s PortPwrCtrlMask ", prefix); for(j = 0; j < l; j++) printf(" 0x%02x ", p[7+i+j]); printf("\n"); @@ -1047,7 +1111,7 @@ static void dump_hub(char *p) static void dump_report_desc(unsigned char *b, int l) { - unsigned int t, j, bsize, btag, btype, data, hut; + unsigned int t, j, bsize, btag, btype, data = 1000, hut = 1000; int i; char *types[4] = { "Main", "Global", "Local", "reserved" }; char indent[] = " "; @@ -1168,21 +1232,131 @@ static void dump_hid_device(int fd, unsi } } +static char * +dump_comm_descriptor(int fd, unsigned char *buf, char *indent, u_int16_t lang) +{ + int tmp; + char str [128]; + + switch (buf[2]) { + case 0: + if (buf[0] != 5) + goto bad; + printf( "%sCDC Header:\n" + "%s bcdCDC %x.%02x\n", + indent, + indent, buf[4], buf[3]); + break; + case 0x01: /* call management functional desc */ + if (buf [0] != 5) + goto bad; + printf( "%sCDC Call Management:\n" + "%s bmCapabilities 0x%02x\n", + indent, + indent, buf[3]); + if (buf[3] & 0x01) + printf( "%s call management\n", indent); + if (buf[3] & 0x02) + printf( "%s use DataInterface\n", indent); + printf("%s bDataInterface %d\n", indent, buf[4]); + break; + case 0x02: /* acm functional desc */ + if (buf [0] != 4) + goto bad; + printf( "%sCDC ACM:\n" + "%s bmCapabilities %02x\n", + indent, + indent, buf[3]); + if (buf[3] & 0x08) + printf( "%s connection notifications\n", indent); + if (buf[3] & 0x04) + printf( "%s sends break\n", indent); + if (buf[3] & 0x02) + printf( "%s line coding and serial state\n", indent); + if (buf[3] & 0x01) + printf( "%s get/set/clear comm features\n", indent); + break; + case 0x06: /* union desc */ + if (buf [0] < 5) + goto bad; + printf( "%sCDC Union:\n" + "%s bMasterInterface %d\n" + "%s bSlaveInterface ", + indent, + indent, buf [3], + indent); + for (tmp = 4; tmp < buf [0]; tmp++) + printf("%d ", buf [tmp]); + printf("\n"); + break; + case 0x0f: /* ethernet functional desc */ + if (buf [0] != 13) + goto bad; + get_string(fd, str, sizeof str, buf[3], lang); + tmp = buf [7] << 8; + tmp |= buf [6]; tmp <<= 8; + tmp |= buf [5]; tmp <<= 8; + tmp |= buf [4]; + printf( "%sCDC Ethernet:\n" + "%s iMacAddress %d %s\n" + "%s bmEthernetStatistics 0x%08x\n", + indent, + indent, buf[3], (buf[3] && *str) ? str : "(?)", + indent, tmp); + /* FIXME dissect ALL 28 bits */ + printf( "%s wMaxSegmentSize %d\n" + "%s wNumberMCFilters 0x%04x\n" + "%s bNumberPowerFilters %d\n", + indent, (buf[9]<<8)|buf[8], + indent, (buf[11]<<8)|buf[10], + indent, buf[12]); + break; + /* FIXME there are about a dozen more descriptor types */ + default: + return "unsupported comm descriptor"; + } + return 0; + +bad: + return "corrupt comm descriptor"; +} + /* ---------------------------------------------------------------------- */ +static void do_hub(int fd, int has_tt) +{ + unsigned char buf [7]; + int ret; + + ret = usb_control_msg(fd, USB_DIR_IN | USB_TYPE_CLASS, + USB_REQ_GET_DESCRIPTOR, 0x29 << 8, 0, + sizeof buf, buf); + if (ret != sizeof buf) { + perror ("can't get hub descriptor"); + return; + } + dump_hub("", buf, has_tt); +} + static void do_config(int fd, unsigned int nr, u_int16_t lang) { - unsigned char buf[1024],*p; - unsigned int sz,curinterface; - int l; + unsigned char buf[1024],*p, *err; + unsigned int sz,curinterface = 1000; u_int8_t curclass = 0xff, cursubclass = 0xff; + unsigned retries = 0; + for (;;) { if (usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_CONFIG << 8) | nr, 0, USB_DT_CONFIG_SIZE, buf) < 0) { + if (retries++ >= 5) { if (open_mode == O_RDWR) fprintf(stdout, "cannot get config descriptor %d, %s (%d)\n", nr, strerror(errno), errno); return; + } + continue; + } else break; } + if (buf[0] < USB_DT_CONFIG_SIZE || buf[1] != USB_DT_CONFIG) fprintf(stderr, "Warning: invalid config descriptor\n"); sz = buf[2] | buf[3] << 8; @@ -1208,13 +1382,14 @@ static void do_config(int fd, unsigned i } switch (p[1]) { case USB_DT_DEVICE: + /* NOTE: should NOT be found here! */ dump_device(fd, p, lang); curclass = p[4]; cursubclass = p[5]; break; case USB_DT_CONFIG: - dump_config(fd, p); + dump_config(fd, p, lang); break; case USB_DT_INTERFACE: @@ -1229,39 +1404,39 @@ static void do_config(int fd, unsigned i break; case USB_DT_CS_DEVICE: - if (curclass == 3) { + if (curclass == USB_CLASS_HID) { dump_hid_device(fd, p, curinterface); break; } - printf(" unknown descriptor type:"); - dump_junk2(p, p[0]); - break; - - case USB_DT_CS_CONFIG: - printf(" unknown descriptor type:"); - dump_junk2(p, p[0]); - break; - - case USB_DT_CS_STRING: - printf(" unknown descriptor type:"); - dump_junk2(p, p[0]); - break; + err = "unknown device class descriptor"; + goto junk; case USB_DT_CS_INTERFACE: - if (curclass == 1 && cursubclass == 1) { - dump_audiocontrol_interface(fd, p, lang); + err = "unknown interface class descriptor"; + switch (curclass) { + case USB_CLASS_AUDIO: + switch (cursubclass) { + case 1: + dump_audiocontrol_interface(fd, p, lang); + break; + case 2: + dump_audiostreaming_interface(fd, p); + break; + case 3: + dump_midistreaming_interface(fd, p, lang); + break; + default: + goto junk; + } break; - } - if (curclass == 1 && cursubclass == 2) { - dump_audiostreaming_interface(fd, p); - break; - } - if (curclass == 1 && cursubclass == 3) { - dump_midistreaming_interface(fd, p, lang); + case USB_CLASS_COMM: + err = dump_comm_descriptor (fd, p, " ", lang); + if (err) + goto junk; break; + default: + goto junk; } - printf(" unknown descriptor type:"); - dump_junk2(p, p[0]); break; case USB_DT_CS_ENDPOINT: @@ -1273,16 +1448,20 @@ static void do_config(int fd, unsigned i dump_midistreaming_endpoint(fd, p); break; } - printf(" unknown descriptor type:"); - dump_junk2(p, p[0]); - break; + err = "unknown endpoint class descriptor"; + goto junk; case USB_DT_HUB: - dump_hub(p); + /* NOTE only rather ancient hubs will include + * this with the config descriptor. + */ + dump_hub(" ", p, 0); break; default: - printf(" unknown descriptor type:"); + err = "unknown descriptor type"; +junk: + printf(" %s:", err); dump_junk2(p, p[0]); } sz -= p[0]; @@ -1303,21 +1482,41 @@ static u_int16_t dump_langids(int fd, in int i, l; u_int16_t lang; - b[0] = b[1] = 0xbf; - l = usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR, USB_DT_STRING << 8, 0, sizeof(b), b); + /* read string length first, like recent linuxes -- else some devices misbehave */ + b[0] = b[1] = 0; + l = usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR, USB_DT_STRING << 8, 0, 4, b); + if (l < 0 && errno == EPIPE) + l = 0; if (l < 0) { - if (open_mode == O_RDWR) + if (open_mode == O_RDWR && !quiet) printf(" Language IDs: none (cannot get min. string descriptor; got len=%d, error=%d:%s)\n", l, errno, strerror(errno)); return 0; } - if (l < 4 || b[0] != l) { + if (l == 0) { + if (!quiet) + printf(" Language IDs: none\n"); + return 0; + } + if (l < 4) { + if (!quiet) printf(" Language IDs: none (invalid length string descriptor %02x; len=%d)\n", b[0], l); return 0; } /* save first language ID for further get_string_descriptors */ lang = b[2] | (b[3] << 8); + + /* maybe there's more than one */ + if (b[0] > 4) { + l = usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR, USB_DT_STRING << 8, 0, b[0], b); + if (l < 4 || b[0] != l) { + fprintf(stderr, "string 0 broken re-read, l = %d, b[0] = %d \n", l, b[0]); + b[0] = 4; + b[2] = lang; + b[3] = lang >> 8; + } + } #if 0 printf ("dump_langids: ret=%d:%d, lang=0x%x, length=%d\n", l, errno, lang, b[0]); dump_junk2 (b, 32); @@ -1342,11 +1541,17 @@ static void dumpdev(unsigned char *devde maxcfg = devdesc[17]; if (devdesc[0] < 18 || devdesc[1] != USB_DT_DEVICE) maxcfg = 1; + dev_has_strings = 0; lang = dump_langids(fd, 1); dump_device(fd, devdesc, lang); for (i = 0; i < maxcfg; i++) do_config(fd, i, lang); - lang = dump_langids(fd, 0); + if (devdesc[4] == USB_CLASS_HUB) + do_hub(fd, devdesc [6]); + /* FIXME if it's usb 2.0 and there's a device qualifier, + * optionally dump it and the other-speed config data + */ + lang = dump_langids(fd, !dev_has_strings); } /* ---------------------------------------------------------------------- */ @@ -1560,7 +1765,7 @@ int main(int argc, char *argv[]) exit(1); } if ((err = names_init("./usb.ids")) != 0) - if(err = names_init(USBIDS_FILE)) { + if((err = names_init(USBIDS_FILE)) != 0) { printf("Error, cannot open USBIDS File \"%s\", %s\n", USBIDS_FILE, strerror(err)); exit(1); }