Re: Modified usb-devices.sh script

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux