Blob Blame History Raw
--- 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);
 	}