--- /dev/null
+#!/usr/bin/perl
+#
+# Reads /proc/bus/usb/devices and selectively lists and/or
+# interprets it.
+#
+# Originally written by Randy Dunlap.
+#
+
+$DEVFILENAME = "/proc/bus/usb/devices";
+$PROGNAME = $0;
+
+if (! open (DEVNUM, "<$DEVFILENAME"))
+{
+ print "$PROGNAME: cannot open '$DEVFILENAME'\n";
+ exit 1;
+}
+
+$showconfig = "yes";
+
+while ($line = <DEVNUM>) # read a text line from DEVNUM
+{
+ # skip all lines except those we recognize:
+ if (($line !~ "^C:") # Configuration: one is active
+ && ($line !~ "^D:") # Device:
+ && ($line !~ "^I:") # Interface: protocol group
+ && ($line !~ "^S:") # String: used with root hub
+ && ($line !~ "^T:") # Topology: starts each device
+ )
+ {
+ next; # to the next line
+ }
+
+ chomp $line; # remove line endings
+
+ # First convert '=' signs to spaces.
+ $line =~ tr/=/ /;
+
+ # and convert all '(' and ')' to spaces.
+ $line =~ tr/(/ /;
+ $line =~ tr/)/ /;
+
+ # split the line at spaces.
+ @fields = split / +/, $line;
+
+ # T: Bus=01 Lev=01 Prnt=01 Port=03 Cnt=01 Dev#= 3 Spd=1.5 MxCh= 0
+ if ($line =~ "^T:")
+ {
+ # split yields: $bus, $level, $parent, $port, $count, $devnum, $speed, $maxchild.
+
+ $bus = @fields [2];
+ $level = @fields [4];
+ $parent = @fields [6]; # parent devnum
+ $port = @fields [8] + 1; # make $port 1-based
+ $count = @fields [10];
+ $devnum = @fields [12];
+ $speed = @fields [14];
+ $maxchild = @fields [16];
+ $devclass = "?";
+ $intclass = "?";
+ $driver = "?";
+ $ifnum = "?";
+ $showclass = "?"; # derived from $devclass or $intclass
+ $lastif = "?"; # show only first altsetting
+ $HCtype = "?";
+ $showconfig = "no";
+ $nconfig = "0";
+ next;
+ } # end T: line
+
+ # only show the _active_ configuration
+ # C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=100mA
+ elsif ( $line =~ "^C:" ) {
+ if ( $line =~ "^C:\\*" ) {
+ $showconfig = @fields[4];
+ } else {
+ $showconfig = "no";
+ }
+ next;
+ }
+
+ # D: Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
+ elsif ($line =~ "^D:")
+ { # for D: line
+ $devclass = @fields [5];
+ $nconfig = @fields [13];
+ next;
+ }
+
+ # in case this is a root hub, look at the device strings.
+ # - S: Manufacturer:Linux 2.6.5 ehci_hcd [all 2.6]
+ # - S: Product=USB UHCI Root Hub [all 2.4, 2.2]
+ # - S: Product=OPTi Inc 82C861 [2.6/PCI_NAMES]
+ elsif ( $line =~ "^S:" )
+ { # for S: line
+ if ( $level == 00 && $line =~ "hcd" )
+ {
+ $HCtype = @fields [4];
+ }
+ elsif ( $level == 00 && $line =~ "HCI" && $HCtype eq "?")
+ {
+ $HCtype = @fields [3];
+ }
+ next;
+ }
+
+ # the rest of this code:
+ # - only shows interface descriptors
+ # - for the active configuration
+ # - for the first (prefer: active!) altsetting
+ elsif ( ! ( $line =~ "^I:" )
+ || "$showconfig" eq "no") {
+ next;
+ }
+
+
+ # I: If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=02 Driver=hid
+ $intclass = @fields [9];
+ $ifnum = @fields [2];
+ $driver = @fields [15];
+
+ if (($devclass eq ">ifc") || ($devclass eq "unk."))
+ { # then use InterfaceClass, not DeviceClass
+ $showclass = $intclass;
+ }
+ else
+ { # use DeviceClass
+ $showclass = $devclass;
+ }
+
+ if ($level == 0)
+ {
+ # substitute real driver name
+ if ( $HCtype =~ "UHCI-alt" )
+ {
+ $HC = "uhci";
+ }
+ elsif ( $HCtype =~ "UHCI" )
+ {
+ $HC = "usb-uhci";
+ }
+ elsif ( $HCtype =~ "OHCI" )
+ {
+ $HC = "usb-ohci";
+ }
+ else
+ {
+ $HC = $HCtype;
+ }
+
+ print sprintf ("/: Bus $bus.Port $port: Dev $devnum, Class=root_hub, Driver=%s/%sp, %sM\n",
+ $HC, $maxchild, $speed );
+ }
+ elsif ($lastif ne $ifnum)
+ {
+ $temp = $level;
+ while ($temp >= 1)
+ {
+ print " ";
+ $temp--;
+ }
+
+ if ($nconfig ne "1") {
+ $temp = " Cfg $showconfig/$nconfig";
+ } else {
+ $temp = "";
+ }
+
+ print sprintf ("|__ Port $port: Dev $devnum$temp, If $ifnum, Class=$showclass, Driver=$driver%s, %sM\n",
+ ($maxchild == 0) ? "" : ("/" . $maxchild . "p"),
+ $speed);
+ $lastif = $ifnum;
+ }
+} # end while DEVNUM
+
+close (DEVNUM);
+
+# END.