diff options
Diffstat (limited to '')
-rwxr-xr-x | lsusb.py.in | 560 |
1 files changed, 560 insertions, 0 deletions
diff --git a/lsusb.py.in b/lsusb.py.in new file mode 100755 index 0000000..592e5cf --- /dev/null +++ b/lsusb.py.in @@ -0,0 +1,560 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only +# +# lsusb-VERSION.py +# +# Displays your USB devices in reasonable form. +# +# Copyright (c) 2009 Kurt Garloff <garloff@suse.de> +# Copyright (c) 2013,2018 Kurt Garloff <kurt@garloff.de> +# +# Usage: See usage() + +# Py2 compat +from __future__ import print_function +import getopt +import os +import re +import sys + +HUB_ICLASS = 0x09 + +# Global options +showint = False +showhubint = False +noemptyhub = False +nohub = False +showeps = False +showwakeup = False + +prefix = "/sys/bus/usb/devices/" +usbids = [ + "@usbids@", + "/usr/share/hwdata/usb.ids", + "/usr/share/usb.ids", +] +cols = ("", "", "", "", "", "") + +norm = "\033[0;0m" +bold = "\033[0;1m" +red = "\033[0;31m" +green = "\033[0;32m" +amber = "\033[0;33m" +blue = "\033[0;34m" + +usbvendors = {} +usbproducts = {} +usbclasses = {} + +def colorize(num, text): + return cols[num] + str(text) + cols[0] + +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 open_read_ign(fn): + try: + return open(fn, 'r', errors='ignore') + except: + return open(fn, 'r') + +def myenum(*args): + enums = dict(zip(args, range(len(args)))) + return type('MyEnum', (), enums) + +def parse_usb_ids(): + "Parse /usr/share/usb.ids and fill usbvendors, usbproducts, usbclasses" + vid = 0 + did = 0 + modes = myenum('Vendor', 'Class', 'Misc') + mode = modes.Vendor + strg = "" + cstrg = "" + for unm in usbids: + if os.path.exists(unm): + break + for ln in open_read_ign(unm).readlines(): + if ln[0] == '#': + continue + ln = ln.rstrip('\n') + if len(ln) == 0: + continue + if ishexdigit(ln[0:4]): + mode = modes.Vendor + vid = int(ln[:4], 16) + usbvendors[vid] = ln[6:] + continue + if ln[0] == '\t' and ishexdigit(ln[1:3]): + # usb.ids has a device id of 01xy, sigh + if ln[3:5] == "xy": + did = int(ln[1:3], 16)*256 + else: + did = int(ln[1:5], 16) + # USB devices + if mode == modes.Vendor: + usbproducts[vid, did] = ln[7:] + continue + elif mode == modes.Class: + nm = ln[5:] + if nm != "Unused": + strg = cstrg + ":" + nm + else: + strg = cstrg + ":" + usbclasses[vid, did, -1] = strg + continue + if ln[0] == 'C': + mode = modes.Class + cid = int(ln[2:4], 16) + cstrg = ln[6:] + usbclasses[cid, -1, -1] = cstrg + continue + if mode == modes.Class and ln[0] == '\t' and ln[1] == '\t' and ishexdigit(ln[2:4]): + prid = int(ln[2:4], 16) + usbclasses[cid, did, prid] = ln[6:] + continue + mode = modes.Misc + usbclasses[0xFF, 0xFF, 0xFF] = "Vendor Specific" + +def find_usb_prod(vid, pid): + "Return device name from USB Vendor:Product list" + strg = "" + vendor = usbvendors.get(vid) + if vendor: + strg = str(vendor) + else: + return "" + product = usbproducts.get((vid, pid)) + if product: + return strg + " " + str(product) + return strg + +def find_usb_class(cid, sid, pid): + "Return USB protocol from usbclasses list" + lnlst = len(usbclasses) + cls = usbclasses.get((cid, sid, pid)) \ + or usbclasses.get((cid, sid, -1)) \ + or usbclasses.get((cid, -1, -1)) + if cls: + return str(cls) + 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 + #'usbhid', # hidraw +] + +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 add_drv(path, drvnm): + res = "" + try: + for e2 in os.listdir(path+"/"+drvnm): + if e2[0:len(drvnm)] == drvnm: + res += e2 + " " + try: + if res: + res += "(" + os.path.basename(os.readlink(path+"/driver")) + ") " + except: + pass + except: + pass + return res + +def find_dev(driver, usbname): + "Return pseudo devname that's driven by driver" + res = "" + for nm in devlst: + dirnm = prefix + usbname + prep = "" + idx = nm.find('/') + if idx != -1: + prep = nm[:idx+1] + dirnm += "/" + nm[:idx] + nm = nm[idx+1:] + ln = len(nm) + try: + for ent in os.listdir(dirnm): + if ent[:ln] == nm: + res += prep+ent+" " + if nm == "host": + res += "(" + find_storage(ent[ln:])[:-1] + ")" + except: + pass + if driver == "usbhid": + rg = re.compile(r'[0-9A-F]{4}:[0-9A-F]{4}:[0-9A-F]{4}\.[0-9A-F]{4}') + for ent in os.listdir(prefix + usbname): + m = rg.match(ent) + if m: + res += add_drv(prefix+usbname+"/"+ent, "hidraw") + add = add_drv(prefix+usbname+"/"+ent, "input") + if add: + res += add + else: + for ent2 in os.listdir(prefix+usbname+"/"+ent): + m = rg.match(ent2) + if m: + res += add_drv(prefix+usbname+"/"+ent+"/"+ent2, "input") + return res + + +class UsbObject: + def read_attr(self, name): + path = prefix + self.path + "/" + name + return open(path).readline().strip() + + def read_link(self, name): + path = prefix + self.path + "/" + name + return os.path.basename(os.readlink(path)) + +class UsbEndpoint(UsbObject): + "Container for USB endpoint info" + def __init__(self, parent, fname, level): + self.parent = parent + self.level = level + self.fname = fname + self.path = "" + self.epaddr = 0 + self.len = 0 + self.ival = "" + self.type = "" + self.attr = 0 + self.max = 0 + if self.fname: + self.read(self.fname) + + def read(self, fname): + self.fname = fname + self.path = self.parent.path + "/" + fname + self.epaddr = int(self.read_attr("bEndpointAddress"), 16) + ival = int(self.read_attr("bInterval"), 16) + if ival: + self.ival = " (%s)" % self.read_attr("interval") + self.len = int(self.read_attr("bLength"), 16) + self.type = self.read_attr("type") + self.attr = int(self.read_attr("bmAttributes"), 16) + self.max = int(self.read_attr("wMaxPacketSize"), 16) + + def __repr__(self): + return "<UsbEndpoint[%r]>" % self.fname + + def __str__(self): + indent = " " * self.level + #name = "%s/ep_%02X" % (self.parent.fname, self.epaddr) + name = "" + body = "(EP) %02x: %s%s attr %02x len %02x max %03x" % \ + (self.epaddr, self.type, self.ival, self.attr, self.len, self.max) + body = colorize(5, body) + return "%-17s %s\n" % (indent + name, indent + body) + + +class UsbInterface(UsbObject): + "Container for USB interface info" + def __init__(self, parent, fname, level=1): + self.parent = parent + self.level = level + self.fname = fname + self.path = "" + self.iclass = 0 + self.isclass = 0 + self.iproto = 0 + self.noep = 0 + self.driver = "" + self.devname = "" + self.protoname = "" + self.eps = [] + if self.fname: + self.read(self.fname) + + def read(self, fname): + self.fname = fname + self.path = self.parent.path + "/" + fname + self.iclass = int(self.read_attr("bInterfaceClass"),16) + self.isclass = int(self.read_attr("bInterfaceSubClass"),16) + self.iproto = int(self.read_attr("bInterfaceProtocol"),16) + self.noep = int(self.read_attr("bNumEndpoints")) + try: + self.driver = self.read_link("driver") + self.devname = find_dev(self.driver, self.path) + except: + pass + self.protoname = find_usb_class(self.iclass, self.isclass, self.iproto) + if showeps: + for dirent in os.listdir(prefix + self.path): + if dirent.startswith("ep_"): + ep = UsbEndpoint(self, dirent, self.level+1) + self.eps.append(ep) + + def __repr__(self): + return "<UsbInterface[%r]>" % self.fname + + def __str__(self): + indent = " " * self.level + name = self.fname + plural = (" " if self.noep == 1 else "s") + body = "(IF) %02x:%02x:%02x %iEP%s (%s) %s %s" % \ + (self.iclass, self.isclass, self.iproto, self.noep, plural, + self.protoname, colorize(3, self.driver), colorize(4, self.devname)) + strg = "%-17s %s\n" % (indent + name, indent + body) + if showeps and self.eps: + for ep in self.eps: + strg += str(ep) + return strg + +class UsbDevice(UsbObject): + "Container for USB device info" + def __init__(self, parent, fname, level=0): + self.parent = parent + self.level = level + self.fname = fname + self.path = "" + self.iclass = 0 + self.isclass = 0 + self.iproto = 0 + self.vid = 0 + self.pid = 0 + self.name = "" + self.usbver = "" + self.speed = "" + self.maxpower = "" + self.wakeup = "" + self.noports = 0 + self.nointerfaces = 0 + self.driver = "" + self.devname = "" + self.interfaces = [] + self.children = [] + if self.fname: + self.read(self.fname) + self.readchildren() + + def read(self, fname): + self.fname = fname + self.path = fname + self.iclass = int(self.read_attr("bDeviceClass"), 16) + self.isclass = int(self.read_attr("bDeviceSubClass"), 16) + self.iproto = int(self.read_attr("bDeviceProtocol"), 16) + self.vid = int(self.read_attr("idVendor"), 16) + self.pid = int(self.read_attr("idProduct"), 16) + try: + self.name = self.read_attr("manufacturer") + " " \ + + self.read_attr("product") + except: + pass + if self.name: + mch = re.match(r"Linux [^ ]* (.hci[_-]hcd) .HCI Host Controller", self.name) + if mch: + self.name = mch.group(1) + 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.startswith("Generic"): + oldnm = self.name + self.name = find_usb_prod(self.vid, self.pid) + if not self.name: + self.name = oldnm + try: + ser = self.read_attr("serial") + # Some USB devs report "serial" as serial no. suppress + if (ser and ser != "serial"): + self.name += " " + ser + except: + pass + self.usbver = self.read_attr("version") + self.speed = self.read_attr("speed") + self.maxpower = self.read_attr("bMaxPower") + self.noports = int(self.read_attr("maxchild")) + try: + self.nointerfaces = int(self.read_attr("bNumInterfaces")) + except: + self.nointerfaces = 0 + try: + self.driver = self.read_link("driver") + self.devname = find_dev(self.driver, self.path) + except: + pass + if showwakeup: + try: + self.wakeup = self.read_attr('power/wakeup') + except: + self.wakeup = "unsupported" + + 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 + if os.access(prefix + dirent + "/bInterfaceClass", os.R_OK): + iface = UsbInterface(self, dirent, self.level+1) + self.interfaces.append(iface) + else: + usbdev = UsbDevice(self, dirent, self.level+1) + self.children.append(usbdev) + usbsortkey = lambda obj: [int(x) for x in re.split(r"[-:.]", obj.fname)] + self.interfaces.sort(key=usbsortkey) + self.children.sort(key=usbsortkey) + + def __repr__(self): + return "<UsbDevice[%r]>" % self.fname + + def __str__(self): + is_hub = (self.iclass == HUB_ICLASS) + if is_hub: + if noemptyhub and len(self.children) == 0: + return "" + strg = "" + if not (nohub and is_hub): + indent = " " * self.level + name = self.fname + plural = (" " if self.nointerfaces == 1 else "s") + body = "%s %02x %iIF%s [USB %s, %5s Mbps, %5s%s] (%s)%s" % \ + (colorize(1, "%04x:%04x" % (self.vid, self.pid)), + self.iclass, self.nointerfaces, plural, + self.usbver.strip(), self.speed, self.maxpower, + ("" if self.wakeup == "" else (", power wakeup: %s" % self.wakeup)), + colorize(2 if is_hub else 1, self.name), + colorize(2, " hub") if is_hub else "") + strg = "%-17s %s\n" % (indent + name, indent + body) + if not (is_hub and not showhubint): + if showeps: + ep = UsbEndpoint(self, "ep_00", self.level+1) + strg += str(ep) + if showint: + for iface in self.interfaces: + strg += str(iface) + for child in self.children: + strg += str(child) + return strg + + +def usage(): + "Displays usage information" + print("Usage: lsusb.py [options]") + print() + print("Options:") + print(" -h, --help display this help") + print(" -i, --interfaces display interface information") + print(" -I, --hub-interfaces display interface information, even for hubs") + print(" -u, --hide-empty-hubs suppress empty hubs") + print(" -U, --hide-hubs suppress all hubs") + print(" -c, --color use colors") + print(" -C, --no-color disable colors") + print(" -e, --endpoints display endpoint info") + print(" -f FILE, --usbids-path FILE") + print(" override filename for /usr/share/usb.ids") + print(" -w, --wakeup display power wakeup setting") + print() + print("Use lsusb.py -ciu to get a nice overview of your USB devices.") + +def read_usb(): + "Read toplevel USB entries and print" + root_hubs = [] + for dirent in os.listdir(prefix): + if not dirent[0:3] == "usb": + continue + usbdev = UsbDevice(None, dirent, 0) + root_hubs.append(usbdev) + root_hubs.sort(key=lambda x: int(x.fname[3:])) + for usbdev in root_hubs: + print(usbdev, end="") + +def main(argv): + "main entry point" + global showint, showhubint, noemptyhub, nohub + global cols, usbids, showeps, showwakeup + usecols = None + + long_options = [ + "help", + "interfaces", + "hub-interfaces", + "hide-empty-hubs", + "hide-hubs", + "color", + "no-color", + "usbids-path=", + "endpoints", + "wakeup", + ] + + try: + (optlist, args) = getopt.gnu_getopt(argv[1:], "hiIuUwcCef:", long_options) + except getopt.GetoptError as exc: + print("Error:", exc, file=sys.stderr) + sys.exit(2) + for opt in optlist: + if opt[0] in {"-h", "--help"}: + usage() + sys.exit(0) + elif opt[0] in {"-i", "--interfaces"}: + showint = True + elif opt[0] in {"-I", "--hub-interfaces"}: + showint = True + showhubint = True + elif opt[0] in {"-u", "--hide-empty-hubs"}: + noemptyhub = True + elif opt[0] in {"-U", "--hide-hubs"}: + noemptyhub = True + nohub = True + elif opt[0] in {"-c", "--color"}: + usecols = True + elif opt[0] in {"-C", "--no-color"}: + usecols = False + elif opt[0] in {"-f", "--usbids-path"}: + usbids = [opt[1]] + elif opt[0] in {"-e", "--endpoints"}: + showeps = True + elif opt[0] in {"-w", "--wakeup"}: + showwakeup = True + if len(args) > 0: + print("Error: excess args %s ..." % args[0], file=sys.stderr) + sys.exit(2) + + if usecols is None: + usecols = sys.stdout.isatty() and os.environ.get("TERM", "dumb") != "dumb" + + if usecols: + cols = (norm, bold, red, green, amber, blue) + + if usbids[0]: + try: + parse_usb_ids() + except: + print(" WARNING: Failure to read usb.ids", file=sys.stderr) + #print(sys.exc_info(), file=sys.stderr) + read_usb() + +# Entry point +if __name__ == "__main__": + main(sys.argv) + + |