Gene Heskett wrote:

Do I need a fresher version of usbutils that the 9.7 I just dl'd
and ungraded to 10 minutes ago?.
...

It hasn't really been touched other than those patches I sent.
...

Could you be so kind as to attach them to a reply here? lsusb is now working, but it looks as if some of the responses are "beyond its ability to grok"

Sorry for the delay -- I didn't have it handy. Here it is, with additional knowledge about hubs, and a small additional hack to retry config descriptor reads ... the retries help in one (flakey) hardware config.

- Dave


This replaces my late-August "usbutils" patch. Changes:


 - CDC descriptor parsing.  This understands the descriptors
   required by CDC Ethernet, and some used by one ACM modem.

 - More realistic control timeouts.  They were only 1/10 second,
   which is *way* too short.  Now they use the standard 5-second
   ceiling, with fewer retries.

 - Gentler reads for string descriptors:
     + if reading all-at-once fails, try smaller chunks.
     + pass the language code in.  it matters.

 - Prints a few more endpoint descriptor fields.  One was added
   in usb 2.0, another has been there all along.

 - Prints more hub descriptor fields, including some usb 2.0 ones.
   ALSO fetches the hub descriptor, so that reasonably-standard hubs
   will show their capabilities (not just bizarre ones).

- Shows config strings too, if present.

 - Retries config descriptor reads, for some (old usb 1.0)
   devices that seem to give Linux some indigestion.

- Some minor cleanup.

This makes a significant difference in robustness; the kernel logs
don't fill up with so many timeout errors (with 50 retries!), many
devices show strings they previously didn't seem to have, I can see
the 1300 byte HID report descriptors from an MGE UPS (low speed), and
of course now there's a decent start on CDC descriptor dumping!
--- 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 <getopt.h>
 
-#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);
        }

Reply via email to