mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usbutils.git
synced 2024-11-15 06:53:43 +08:00
a9ab323074
I want to suggest adding one more space between version and speed in lsusb.py. For USB 3.0 devices are these numbers connected. Example: 3-4.1.2 090c:1000 00 2.00 480MBit/s 100mA 1IFs (SMI Corporation USB DISK AA04012700009416) usb4 1d6b:0003 09 3.005000MBit/s 0mA 1IFs (xhci_hcd 0000:00:14.0) Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
527 lines
13 KiB
Python
Executable File
527 lines
13 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# lsusb.py
|
|
# Displays your USB devices in reasonable form.
|
|
# (c) Kurt Garloff <garloff@suse.de>, 2/2009, GPL v2 or v3.
|
|
# Usage: See usage()
|
|
|
|
import os, sys, re, getopt
|
|
|
|
# from __future__ import print_function
|
|
|
|
# Global options
|
|
showint = False
|
|
showhubint = False
|
|
noemptyhub = False
|
|
nohub = False
|
|
warnsort = False
|
|
|
|
prefix = "/sys/bus/usb/devices/"
|
|
usbids = "/usr/share/usb.ids"
|
|
|
|
esc = chr(27)
|
|
norm = esc + "[0;0m"
|
|
bold = esc + "[0;1m"
|
|
red = esc + "[0;31m"
|
|
green= esc + "[0;32m"
|
|
amber= esc + "[0;33m"
|
|
|
|
cols = ("", "", "", "", "")
|
|
|
|
def readattr(path, name):
|
|
"Read attribute from sysfs and return as string"
|
|
f = open(prefix + path + "/" + name);
|
|
return f.readline().rstrip("\n");
|
|
|
|
def readlink(path, name):
|
|
"Read symlink and return basename"
|
|
return os.path.basename(os.readlink(prefix + path + "/" + name));
|
|
|
|
class UsbClass:
|
|
"Container for USB Class/Subclass/Protocol"
|
|
def __init__(self, cl, sc, pr, str = ""):
|
|
self.pclass = cl
|
|
self.subclass = sc
|
|
self.proto = pr
|
|
self.desc = str
|
|
def __repr__(self):
|
|
return self.desc
|
|
def __cmp__(self, oth):
|
|
# Works only on 64bit systems:
|
|
#return self.pclass*0x10000+self.subclass*0x100+self.proto \
|
|
# - oth.pclass*0x10000-oth.subclass*0x100-oth.proto
|
|
if self.pclass != oth.pclass:
|
|
return self.pclass - oth.pclass
|
|
if self.subclass != oth.subclass:
|
|
return self.subclass - oth.subclass
|
|
return self.proto - oth.proto
|
|
|
|
class UsbVendor:
|
|
"Container for USB Vendors"
|
|
def __init__(self, vid, vname = ""):
|
|
self.vid = vid
|
|
self.vname = vname
|
|
def __repr__(self):
|
|
return self.vname
|
|
def __cmp__(self, oth):
|
|
return self.vid - oth.vid
|
|
|
|
class UsbProduct:
|
|
"Container for USB VID:PID devices"
|
|
def __init__(self, vid, pid, pname = ""):
|
|
self.vid = vid
|
|
self.pid = pid
|
|
self.pname = pname
|
|
def __repr__(self):
|
|
return self.pname
|
|
def __cmp__(self, oth):
|
|
# Works only on 64bit systems:
|
|
# return self.vid*0x10000 + self.pid \
|
|
# - oth.vid*0x10000 - oth.pid
|
|
if self.vid != oth.vid:
|
|
return self.vid - oth.vid
|
|
return self.pid - oth.pid
|
|
|
|
usbvendors = []
|
|
usbproducts = []
|
|
usbclasses = []
|
|
|
|
def ishexdigit(str):
|
|
"return True if all digits are valid hex digits"
|
|
for dg in str:
|
|
if not dg.isdigit() and not dg in 'abcdef':
|
|
return False
|
|
return True
|
|
|
|
def parse_usb_ids():
|
|
"Parse /usr/share/usb.ids and fill usbvendors, usbproducts, usbclasses"
|
|
id = 0
|
|
sid = 0
|
|
mode = 0
|
|
strg = ""
|
|
cstrg = ""
|
|
for ln in file(usbids, "r").readlines():
|
|
if ln[0] == '#':
|
|
continue
|
|
ln = ln.rstrip('\n')
|
|
if len(ln) == 0:
|
|
continue
|
|
if ishexdigit(ln[0:4]):
|
|
mode = 0
|
|
id = int(ln[:4], 16)
|
|
usbvendors.append(UsbVendor(id, ln[6:]))
|
|
continue
|
|
if ln[0] == '\t' and ishexdigit(ln[1:3]):
|
|
sid = int(ln[1:5], 16)
|
|
# USB devices
|
|
if mode == 0:
|
|
usbproducts.append(UsbProduct(id, sid, ln[7:]))
|
|
continue
|
|
elif mode == 1:
|
|
nm = ln[5:]
|
|
if nm != "Unused":
|
|
strg = cstrg + ":" + nm
|
|
else:
|
|
strg = cstrg + ":"
|
|
usbclasses.append(UsbClass(id, sid, -1, strg))
|
|
continue
|
|
if ln[0] == 'C':
|
|
mode = 1
|
|
id = int(ln[2:4], 16)
|
|
cstrg = ln[6:]
|
|
usbclasses.append(UsbClass(id, -1, -1, cstrg))
|
|
continue
|
|
if mode == 1 and ln[0] == '\t' and ln[1] == '\t' and ishexdigit(ln[2:4]):
|
|
prid = int(ln[2:4], 16)
|
|
usbclasses.append(UsbClass(id, sid, prid, strg + ":" + ln[6:]))
|
|
continue
|
|
mode = 2
|
|
|
|
def bin_search(first, last, item, list):
|
|
"binary search on list, returns -1 on fail, match idx otherwise, recursive"
|
|
#print "bin_search(%i,%i)" % (first, last)
|
|
if first == last:
|
|
return -1
|
|
if first == last-1:
|
|
if item == list[first]:
|
|
return first
|
|
else:
|
|
return -1
|
|
mid = (first+last) // 2
|
|
if item == list[mid]:
|
|
return mid
|
|
elif item < list[mid]:
|
|
return bin_search(first, mid, item, list)
|
|
else:
|
|
return bin_search(mid, last, item, list)
|
|
|
|
|
|
def find_usb_prod(vid, pid):
|
|
"Return device name from USB Vendor:Product list"
|
|
strg = ""
|
|
dev = UsbVendor(vid, "")
|
|
lnvend = len(usbvendors)
|
|
ix = bin_search(0, lnvend, dev, usbvendors)
|
|
if ix != -1:
|
|
strg = usbvendors[ix].__repr__()
|
|
else:
|
|
return ""
|
|
dev = UsbProduct(vid, pid, "")
|
|
lnprod = len(usbproducts)
|
|
ix = bin_search(0, lnprod, dev, usbproducts)
|
|
if ix != -1:
|
|
return strg + " " + usbproducts[ix].__repr__()
|
|
return strg
|
|
|
|
def find_usb_class(cid, sid, pid):
|
|
"Return USB protocol from usbclasses list"
|
|
if cid == 0xff and sid == 0xff and pid == 0xff:
|
|
return "Vendor Specific"
|
|
lnlst = len(usbclasses)
|
|
dev = UsbClass(cid, sid, pid, "")
|
|
ix = bin_search(0, lnlst, dev, usbclasses)
|
|
if ix != -1:
|
|
return usbclasses[ix].__repr__()
|
|
dev = UsbClass(cid, sid, -1, "")
|
|
ix = bin_search(0, lnlst, dev, usbclasses)
|
|
if ix != -1:
|
|
return usbclasses[ix].__repr__()
|
|
dev = UsbClass(cid, -1, -1, "")
|
|
ix = bin_search(0, lnlst, dev, usbclasses)
|
|
if ix != -1:
|
|
return usbclasses[ix].__repr__()
|
|
return ""
|
|
|
|
|
|
devlst = ( 'host', # usb-storage
|
|
'video4linux/video', # uvcvideo et al.
|
|
'sound/card', # snd-usb-audio
|
|
'net/', # cdc_ether, ...
|
|
'input/input', # usbhid
|
|
'usb:hiddev', # usb hid
|
|
'bluetooth/hci', # btusb
|
|
'ttyUSB', # btusb
|
|
'tty/', # cdc_acm
|
|
'usb:lp', # usblp
|
|
#'usb/lp', # usblp
|
|
'usb/', # hiddev, usblp
|
|
)
|
|
|
|
def find_storage(hostno):
|
|
"Return SCSI block dev names for host"
|
|
res = ""
|
|
for ent in os.listdir("/sys/class/scsi_device/"):
|
|
(host, bus, tgt, lun) = ent.split(":")
|
|
if host == hostno:
|
|
try:
|
|
for ent2 in os.listdir("/sys/class/scsi_device/%s/device/block" % ent):
|
|
res += ent2 + " "
|
|
except:
|
|
pass
|
|
return res
|
|
|
|
def find_dev(driver, usbname):
|
|
"Return pseudo devname that's driven by driver"
|
|
res = ""
|
|
for nm in devlst:
|
|
dir = prefix + usbname
|
|
prep = ""
|
|
#print nm
|
|
idx = nm.find('/')
|
|
if idx != -1:
|
|
prep = nm[:idx+1]
|
|
dir += "/" + nm[:idx]
|
|
nm = nm[idx+1:]
|
|
ln = len(nm)
|
|
try:
|
|
for ent in os.listdir(dir):
|
|
if ent[:ln] == nm:
|
|
res += prep+ent+" "
|
|
if nm == "host":
|
|
res += "(" + find_storage(ent[ln:])[:-1] + ")"
|
|
except:
|
|
pass
|
|
return res
|
|
|
|
|
|
class UsbInterface:
|
|
"Container for USB interface info"
|
|
def __init__(self, parent = None, level = 1):
|
|
self.parent = parent
|
|
self.level = level
|
|
self.fname = ""
|
|
self.iclass = 0
|
|
self.isclass = 0
|
|
self.iproto = 0
|
|
self.noep = 0
|
|
self.driver = ""
|
|
self.devname = ""
|
|
self.protoname = ""
|
|
def read(self, fname):
|
|
fullpath = ""
|
|
if self.parent:
|
|
fullpath += self.parent.fname + "/"
|
|
fullpath += fname
|
|
#self.fname = fullpath
|
|
self.fname = fname
|
|
self.iclass = int(readattr(fullpath, "bInterfaceClass"),16)
|
|
self.isclass = int(readattr(fullpath, "bInterfaceSubClass"),16)
|
|
self.iproto = int(readattr(fullpath, "bInterfaceProtocol"),16)
|
|
self.noep = int(readattr(fullpath, "bNumEndpoints"))
|
|
try:
|
|
self.driver = readlink(fname, "driver")
|
|
self.devname = find_dev(self.driver, fname)
|
|
except:
|
|
pass
|
|
self.protoname = find_usb_class(self.iclass, self.isclass, self.iproto)
|
|
def __str__(self):
|
|
return "%-16s(IF) %02x:%02x:%02x %iEPs (%s) %s%s %s%s%s\n" % \
|
|
(" " * self.level+self.fname, self.iclass,
|
|
self.isclass, self.iproto, self.noep,
|
|
self.protoname,
|
|
cols[3], self.driver,
|
|
cols[4], self.devname, cols[0])
|
|
|
|
class UsbDevice:
|
|
"Container for USB device info"
|
|
def __init__(self, parent = None, level = 0):
|
|
self.parent = parent
|
|
self.level = level
|
|
self.fname = ""
|
|
self.iclass = 0
|
|
self.isclass = 0
|
|
self.iproto = 0
|
|
self.vid = 0
|
|
self.pid = 0
|
|
self.name = ""
|
|
self.usbver = ""
|
|
self.speed = ""
|
|
self.maxpower = ""
|
|
self.noports = 0
|
|
self.nointerfaces = 0
|
|
self.driver = ""
|
|
self.devname = ""
|
|
self.interfaces = []
|
|
self.children = []
|
|
|
|
def read(self, fname):
|
|
self.fname = fname
|
|
self.iclass = int(readattr(fname, "bDeviceClass"), 16)
|
|
self.isclass = int(readattr(fname, "bDeviceSubClass"), 16)
|
|
self.iproto = int(readattr(fname, "bDeviceProtocol"), 16)
|
|
self.vid = int(readattr(fname, "idVendor"), 16)
|
|
self.pid = int(readattr(fname, "idProduct"), 16)
|
|
try:
|
|
self.name = readattr(fname, "manufacturer") + " " \
|
|
+ readattr(fname, "product")
|
|
#self.name += " " + readattr(fname, "serial")
|
|
if self.name[:5] == "Linux":
|
|
rx = re.compile(r"Linux [^ ]* (.hci_hcd) .HCI Host Controller")
|
|
mch = rx.match(self.name)
|
|
if mch:
|
|
self.name = mch.group(1)
|
|
|
|
except:
|
|
pass
|
|
if not self.name:
|
|
self.name = find_usb_prod(self.vid, self.pid)
|
|
# Some USB Card readers have a better name then Generic ...
|
|
if self.name[:7] == "Generic":
|
|
oldnm = self.name
|
|
self.name = find_usb_prod(self.vid, self.pid)
|
|
if not self.name:
|
|
self.name = oldnm
|
|
try:
|
|
ser = readattr(fname, "serial")
|
|
# Some USB devs report "serial" as serial no. suppress
|
|
if (ser and ser != "serial"):
|
|
self.name += " " + ser
|
|
except:
|
|
pass
|
|
self.usbver = readattr(fname, "version")
|
|
self.speed = readattr(fname, "speed")
|
|
self.maxpower = readattr(fname, "bMaxPower")
|
|
self.noports = int(readattr(fname, "maxchild"))
|
|
try:
|
|
self.nointerfaces = int(readattr(fname, "bNumInterfaces"))
|
|
except:
|
|
#print "ERROR: %s/bNumInterfaces = %s" % (fname,
|
|
# readattr(fname, "bNumInterfaces"))a
|
|
self.nointerfaces = 0
|
|
try:
|
|
self.driver = readlink(fname, "driver")
|
|
self.devname = find_dev(self.driver, fname)
|
|
except:
|
|
pass
|
|
|
|
def readchildren(self):
|
|
if self.fname[0:3] == "usb":
|
|
fname = self.fname[3:]
|
|
else:
|
|
fname = self.fname
|
|
for dirent in os.listdir(prefix + self.fname):
|
|
if not dirent[0:1].isdigit():
|
|
continue
|
|
#print dirent
|
|
if os.access(prefix + dirent + "/bInterfaceClass", os.R_OK):
|
|
iface = UsbInterface(self, self.level+1)
|
|
iface.read(dirent)
|
|
self.interfaces.append(iface)
|
|
else:
|
|
usbdev = UsbDevice(self, self.level+1)
|
|
usbdev.read(dirent)
|
|
usbdev.readchildren()
|
|
self.children.append(usbdev)
|
|
|
|
def __str__(self):
|
|
#str = " " * self.level + self.fname
|
|
if self.iclass == 9:
|
|
col = cols[2]
|
|
if noemptyhub and len(self.children) == 0:
|
|
return ""
|
|
if nohub:
|
|
str = ""
|
|
else:
|
|
col = cols[1]
|
|
if not nohub or self.iclass != 9:
|
|
str = "%-16s%s%04x:%04x%s %02x %s%5sMBit/s %s %iIFs (%s%s%s)" % \
|
|
(" " * self.level + self.fname,
|
|
cols[1], self.vid, self.pid, cols[0],
|
|
self.iclass, self.usbver, self.speed, self.maxpower,
|
|
self.nointerfaces, col, self.name, cols[0])
|
|
#if self.driver != "usb":
|
|
# str += " %s" % self.driver
|
|
if self.iclass == 9 and not showhubint:
|
|
str += " %shub%s\n" % (cols[2], cols[0])
|
|
else:
|
|
str += "\n"
|
|
if showint:
|
|
for iface in self.interfaces:
|
|
str += iface.__str__()
|
|
for child in self.children:
|
|
str += child.__str__()
|
|
return str
|
|
|
|
def deepcopy(lst):
|
|
"Returns a deep copy from the list lst"
|
|
copy = []
|
|
for item in lst:
|
|
copy.append(item)
|
|
return copy
|
|
|
|
def display_diff(lst1, lst2, fmtstr, args):
|
|
"Compare lists (same length!) and display differences"
|
|
for idx in range(0, len(lst1)):
|
|
if lst1[idx] != lst2[idx]:
|
|
print "Warning: " + fmtstr % args(lst2[idx])
|
|
|
|
def fix_usbvend():
|
|
"Sort USB vendor list and (optionally) display diffs"
|
|
if warnsort:
|
|
oldusbvend = deepcopy(usbvendors)
|
|
usbvendors.sort()
|
|
if warnsort:
|
|
display_diff(usbvendors, oldusbvend,
|
|
"Unsorted Vendor ID %04x",
|
|
lambda x: (x.vid,))
|
|
|
|
def fix_usbprod():
|
|
"Sort USB products list"
|
|
if warnsort:
|
|
oldusbprod = deepcopy(usbproducts)
|
|
usbproducts.sort()
|
|
if warnsort:
|
|
display_diff(usbproducts, oldusbprod,
|
|
"Unsorted Vendor:Product ID %04x:%04x",
|
|
lambda x: (x.vid, x.pid))
|
|
|
|
def fix_usbclass():
|
|
"Sort USB class list"
|
|
if warnsort:
|
|
oldusbcls = deepcopy(usbclasses)
|
|
usbclasses.sort()
|
|
if warnsort:
|
|
display_diff(usbclasses, oldusbcls,
|
|
"Unsorted USB class %02x:%02x:%02x",
|
|
lambda x: (x.pclass, x.subclass, x.proto))
|
|
|
|
|
|
def usage():
|
|
"Displays usage information"
|
|
print "Usage: lsusb.py [options]"
|
|
print "Options:"
|
|
print " -h display this help"
|
|
print " -i display interface information"
|
|
print " -I display interface information, even for hubs"
|
|
print " -u suppress empty hubs"
|
|
print " -U suppress all hubs"
|
|
print " -c use colors"
|
|
print " -w display warning if usb.ids is not sorted correctly"
|
|
print " -f FILE override filename for /usr/share/usb.ids"
|
|
return 2
|
|
|
|
def read_usb():
|
|
"Read toplevel USB entries and print"
|
|
for dirent in os.listdir(prefix):
|
|
#print dirent,
|
|
if not dirent[0:3] == "usb":
|
|
continue
|
|
usbdev = UsbDevice(None, 0)
|
|
usbdev.read(dirent)
|
|
usbdev.readchildren()
|
|
os.write(sys.stdout.fileno(), usbdev.__str__())
|
|
|
|
def main(argv):
|
|
"main entry point"
|
|
global showint, showhubint, noemptyhub, nohub, warnsort, cols, usbids
|
|
try:
|
|
(optlist, args) = getopt.gnu_getopt(argv[1:], "hiIuUwcf:", ("help",))
|
|
except getopt.GetoptError, exc:
|
|
print "Error:", exc
|
|
sys.exit(usage())
|
|
for opt in optlist:
|
|
if opt[0] == "-h" or opt[0] == "--help":
|
|
usage()
|
|
sys.exit(0)
|
|
if opt[0] == "-i":
|
|
showint = True
|
|
continue
|
|
if opt[0] == "-I":
|
|
showint = True
|
|
showhubint = True
|
|
continue
|
|
if opt[0] == "-u":
|
|
noemptyhub = True
|
|
continue
|
|
if opt[0] == "-U":
|
|
noemptyhub = True
|
|
nohub = True
|
|
continue
|
|
if opt[0] == "-c":
|
|
cols = (norm, bold, red, green, amber)
|
|
continue
|
|
if opt[0] == "-w":
|
|
warnsort = True
|
|
continue
|
|
if opt[0] == "-f":
|
|
usbids = opt[1]
|
|
continue
|
|
if len(args) > 0:
|
|
print "Error: excess args %s ..." % args[0]
|
|
sys.exit(usage())
|
|
|
|
try:
|
|
parse_usb_ids()
|
|
fix_usbvend()
|
|
fix_usbprod()
|
|
fix_usbclass()
|
|
except:
|
|
print >>sys.stderr, " WARNING: Failure to read usb.ids"
|
|
print >>sys.stderr, sys.exc_info()
|
|
read_usb()
|
|
|
|
# Entry point
|
|
if __name__ == "__main__":
|
|
main(sys.argv)
|
|
|
|
|