summaryrefslogtreecommitdiffstats
path: root/lsusb.py.in
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:13:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:13:39 +0000
commit18d2dadb3c9132b52fdc88f509ea9005b9eebd5f (patch)
tree8e399598f572b2e88c16ddd26fb1fe601dd0e64b /lsusb.py.in
parentInitial commit. (diff)
downloadusbutils-18d2dadb3c9132b52fdc88f509ea9005b9eebd5f.tar.xz
usbutils-18d2dadb3c9132b52fdc88f509ea9005b9eebd5f.zip
Adding upstream version 1:017.upstream/1%017upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lsusb.py.in')
-rwxr-xr-xlsusb.py.in560
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)
+
+