Hi All, Another script alternative. At first I thought, that is a very complicated shell script I can do it much smaller in awk. Then I did it and it was not much smaller or more beautiful as a script. It uses gawk extensions, and will not run under mawk - sorry ubuntu guys, apt-get gawk too. But then I started comparing the output with usb-devices.sh and cat /proc/bus/usb/devices, and important stuff was missing. So I added the printout of all the int/alt entries and the interface association. I got that by parsing the sysfs "descriptors" binary file" (after formatting it to hex using "od"). I still have a few questions, for the sysfs experts. 1) I cannot find any information in sysfs about bandwidth allocation, devices.c accesses hcd tables that are not available in sysfs? I could estimate what the hcd is using, but not know the exact values. 2) I cannot find any information in sysfs about power usage, it might be nice to show hub port and overall power consumption. It would help identify some possible device failures. 3) I don't have a multiple configuration device for test, but It doesn't look like sysfs maintains the information about multiple configs, which would be nice to display. 4) There are other defined strings in the usb spec, but I cannot find a generic way to find them in sysfs. It would be nice to print all the strings, but I really don't want the script to cause extra usb bus accesses, so I print only the 3 current strings, if they exist. 5) The output of this script could be expanded to display other configuration contained data, such as hid or audio or video etc descriptors, if desired. Additional descriptors that are driver requested (hid descriptors etc) are not part of sysfs. If they were they could also be displayed. 6) I hate the format of the output, Weird contractions confusing to humans, apparently the format arranged for scripts to grab things at fixed offsets in the string. Also, things in multiple radixes, should indicate the radix, ie, lead with 0 for octal (useful for those still using pdp-11s?), hex should indicate with 0x, default to decimal. If I say I am 38 years old, does that mean 38 decimal or 38 hex which is 56 decimal, or 38 in base 17? I think the point of this printout is so it is useful to both humans and scripts. Script/userspace writers will presumably look at documentation, humans are less predictable. But anyway I used the current usb-devices spec as the output format (except when it was wrong/ different from /proc/bus/usb/devices). I was not trying to make it faster, but the awk script is about 4 times faster than the bash script: "time usb-devices >/dev/null; time usbd.awk >/dev/null" real 0m0.916s user 0m0.368s sys 0m0.416s real 0m0.232s user 0m0.116s sys 0m0.116s OK, so I enclose the script, do with it what you wish - I'll also attach it, who knows what gmail will do to it. #!/usr/bin/gawk -f # Copyright: 2009 Steve Calfee # # This software may be used and distributed according to the terms of # the GNU General Public License (GPL), version 2, or at your option # any later version. # # called as usbd.awk BEGIN { USB_DT_DEVICE = 1 USB_DT_CONFIG = 2 USB_DT_STRING = 3 USB_DT_INTERFACE = 4 USB_DT_ENDPOINT = 5 USB_DT_DEVICE_QUALIFIER = 6 USB_DT_OTHER_SPEED_CONFIG = 7 USB_DT_INTERFACE_ASSOCIATION = 11 o_bLength = 0 o_bDescriptorType = 1 o_bConfigurationValue = 5 buses = "/sys/bus/usb/devices/usb*" FN = nbus = 1; class["00"] = ">ifc " class["01"] = "audio" class["02"] = "commc" class["03"] = "HID " class["05"] = "phys " class["06"] = "image" class["07"] = "print" class["08"] = "mstor" class["09"] = "hub " class["0a"] = "comdt" class["0b"] = "smcrd" class["0d"] = "cosec" class["0e"] = "video" class["0f"] = "perhc" class["dc"] = "diagd" class["e0"] = "wlcon" class["ef"] = "misc " class["fe"] = "app. " class["ff"] = "vend." ep_type[0] = "ctrl" ep_type[1] = "Isoc" ep_type[2] = "Bulk" ep_type[3] = "Int." while (("ls -d1 " buses | getline temp) > 0) { bus[nbus] = temp "/"; ARGV[nbus++] = temp "/busnum"; ARGC++; #print nbus - 1, ARGV[nbus - 1] } close("ls -d1 " buses) } function getdir(dir, ndirs, temp) { ndirs = 0 while (("ls -d1 " dir | getline temp) > 0) { dirs[ndirs] = temp "/"; #print "getdir ", ndirs, dirs[ndirs] ndirs++ } close("ls -d1 " dir) return ndirs } function f(path, t) { getline t < path close(path) return t } function class_decode(cls) { if (cls in class) { return class[cls] } else { return "unk" } } function print_string(file, name, t) { t = f(file) if (t) printf("S: %s=%s\n", name, t) } function le_word(a, b) { return a + (b * 256) } function scan_desc(d, i, nd, srch) { while (i < nd) { if (d[i + o_bDescriptorType] == srch) { #print "scan_desc found " i, d[i], d[i+1] return i } else { #print "scan_desc hunt " i, d[i], d[i+1] i += d[i + o_bLength] } } return 0; #not found exit } #return index of first int descriptor matching nif in config ncf, else 0 fail function find_int(desc, ncf, d, nd, i) { nd = split(desc, d) i = 2 while (i < nd) { i = scan_desc(d, i, nd, USB_DT_CONFIG) if (!i) return 0 if (d[i + o_bConfigurationValue] == ncf) { #print desc #print "found config #" d[i+o_bConfigurationValue] " length " le_word(d[i+2], d[i+3]) i = scan_desc(d, i + d[i + o_bLength], nd, USB_DT_INTERFACE) return i } else { #print "not found config #" d[i+o_bConfigurationValue] " length " le_word(d[i+2], d[i+3]) i += le_word(d[i+2], d[i+3]) } } return 0 } function print_assoc_def(d, i) { if (i) { FirstIf = d[i + 2] IfCount = d[i + 3] printf("A: FirstIf#=%2i IfCount=%2i Cls=%s(%s) Sub=%02i Prot=%02i\n", FirstIf, IfCount, sprintf("%02x",d[i + 4]), class_decode(sprintf("%02x",d[i + 4])), d[i + 5], d[i + 6]) } return i } function print_assoc(desc, ncf, d, nd, i) { nd = split(desc, d) i = 2 while (i < nd) { i = scan_desc(d, i, nd, USB_DT_CONFIG) if (!i) return 0 if (d[i + o_bConfigurationValue] == ncf) { #print desc #print "found config #" d[i+o_bConfigurationValue] " length " le_word(d[i+2], d[i+3]) i = scan_desc(d, i + d[i], nd, USB_DT_INTERFACE_ASSOCIATION) return print_assoc_def(d, i) } else { #print "not found config #" d[i+o_bConfigurationValue] " length " le_word(d[i+2], d[i+3]) i += le_word(d[i+2], d[i+3]) } } return 0 } function print_eps(d, i, nd, nep, spd, j, maxps){ for (j = 0; j < nep && i < nd;) { if (d[i+1] == USB_DT_ENDPOINT ) { maxps = le_word(d[i+4], d[i+5]) #print "print_eps", d[i+4], d[i+5], maxps printf("E: Ad=%02x(%s) Atr=%02i(%s) MxPS=%4i*%s Ivl=%s\n", d[i + 2], (d[i + 2] > 128) ? "in" : "out", d[i + 3], ep_type[d[i + 3] % 4], maxps % 2048, int(maxps / 2048) + 1, (spd <= 12) ? d[i + 6] "ms" : int(125 * (2 ^ (d[i + 6] - 1))) "us") j++ } i += d[i] } return i } function print_alts(p, nif, nalt, spd, drv, nd, d, i, filey, desc){ filey = "od -vAx -tu1 --width=5000 " p "descriptors" if ( (filey | getline desc) < 0) return; close(filey) if (!(nif >= FirstIf && nif < (FirstIf + IfCount))) print_assoc(desc, 1) i = find_int(desc, 1) nd = split(desc, d) while (i && i < nd) { if (nif == d[i + 2]) { printf("I:%s If#=%2i Alt=%2i #EPs=%2i Cls=%s(%s) Sub=%02i Prot=%02i Driver=%s\n", ((nalt != d[i + 3]) ? " " : "*"), d[i + 2], d[i + 3], d[i + 4], sprintf("%02x",d[i + 5]), class_decode(sprintf("%02x",d[i + 5])), d[i + 6], d[i + 7], drv) if (d[i + 4]) { i = print_eps(d, i + d[i], nd, d[i + 4], spd) continue } } i = scan_desc(d, i + d[i], nd, USB_DT_INTERFACE) } #print "print_alts if=" nif filey } function print_interfaces(dev, level, spd, ip, dr, p, t, t1, di, ndi, ndi2, i, nep, nif, nalt, drv) { ndi = getdir(dev "*-*:*") for (i in dirs) di[i] = dirs[i] delete dirs for (ip = 0; ip < ndi; ip++) { p = di[ip] if ( ( "readlink " p "/driver" | getline dr) <= 0) dr = "(none)" close("readlink " p "/driver") nep = f(p "bNumEndpoints") + 0 t = split(dr, t1, "/") drv = t1[t] nif = f(p "bInterfaceNumber") + 0 nalt = f(p "bAlternateSetting") + 0 print_alts(dev, nif, nalt, spd, drv) } } function print_device(dev, level, parent, count, port, dc, di, ndi, ip, spd, i) { port = 0 if (level > 0) { port = substr(dev, length(dev)-1, 1) - 1 } #print "print_device " dev, level, parent, count spd = f(dev "speed") printf("\nT: Bus=%02i Lev=%02i Prnt=%02i Port=%02i Cnt=%02i Dev#=%3i Spd=%-3s MxCh=%2i\n", f(dev "busnum"), level, parent, port, count, f(dev "devnum"), spd, f(dev "maxchild")) printf("D: Ver=%5s Cls=%s(%s) Sub=%s Prot=%s MxPS=%2i #Cfgs=%3i\n", f(dev "version"), f(dev "bDeviceClass"), class_decode(f(dev "bDeviceClass")), f(dev "bDeviceSubClass"), f(dev "bDeviceProtocol"), f(dev "bMaxPacketSize0"), f(dev "bNumConfigurations")) printf("P: Vendor=%s ProdID=%s Rev=%2i.%02i\n", f(dev "idVendor"), f(dev "idProduct"), substr(f(dev "bcdDevice"),1, 2), substr(f(dev "bcdDevice"),3, 2) ) print_string(dev "manufacturer", "Manufacturer") print_string(dev "product", "Product") print_string(dev "serial", "SerialNumber") printf("C:* #Ifs=%2i Cfg#=%2i Atr=%s MxPwr=%s\n", f(dev "bNumInterfaces"), f(dev "bConfigurationValue"), f(dev "bmAttributes"), f(dev "bMaxPower") ) print_interfaces(dev, level, spd) ndi = getdir(dev "*-*") for (i in dirs) di[i] = dirs[i] delete dirs for (ip = 0; ip < ndi; ip++) { if (!match(di[ip], /:/)) { print_device(di[ip], level+1, f(dev "devnum"), ++dc) } } } FILENAME != lastFILENAME { print_device(bus[FN], 0, 0, 0) lastFILENAME = FILENAME FN++ }
Attachment:
usbd.awk
Description: Binary data