"Fossies" - the Fresh Open Source Software Archive

Member "usbutils-015/lsusb.py.in" (22 Feb 2021, 14202 Bytes) of package /linux/misc/usbutils-015.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "lsusb.py.in": 013_vs_014.

    1 #!/usr/bin/python3
    2 # SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
    3 #
    4 # lsusb-VERSION.py
    5 #
    6 # Displays your USB devices in reasonable form.
    7 #
    8 # Copyright (c) 2009 Kurt Garloff <garloff@suse.de>
    9 # Copyright (c) 2013,2018 Kurt Garloff <kurt@garloff.de>
   10 #
   11 # Usage: See usage()
   12 
   13 # Py2 compat
   14 from __future__ import print_function
   15 import getopt
   16 import os
   17 import re
   18 import sys
   19 
   20 HUB_ICLASS = 0x09
   21 
   22 # Global options
   23 showint = False
   24 showhubint = False
   25 noemptyhub = False
   26 nohub = False
   27 showeps = False
   28 
   29 prefix = "/sys/bus/usb/devices/"
   30 usbids = [
   31     "@usbids@",
   32     "/usr/share/usb.ids",
   33 ]
   34 cols = ("", "", "", "", "", "")
   35 
   36 norm    = "\033[0;0m"
   37 bold    = "\033[0;1m"
   38 red = "\033[0;31m"
   39 green   = "\033[0;32m"
   40 amber   = "\033[0;33m"
   41 blue    = "\033[0;34m"
   42 
   43 usbvendors = {}
   44 usbproducts = {}
   45 usbclasses = {}
   46 
   47 def colorize(num, text):
   48     return cols[num] + str(text) + cols[0]
   49 
   50 def ishexdigit(str):
   51     "return True if all digits are valid hex digits"
   52     for dg in str:
   53         if not dg.isdigit() and not dg in 'abcdef':
   54             return False
   55     return True
   56 
   57 def open_read_ign(fn):
   58     try:
   59         return open(fn, 'r', errors='ignore')
   60     except:
   61         return open(fn, 'r')
   62 
   63 def myenum(*args):
   64     enums = dict(zip(args, range(len(args))))
   65     return type('MyEnum', (), enums)
   66 
   67 def parse_usb_ids():
   68     "Parse /usr/share/usb.ids and fill usbvendors, usbproducts, usbclasses"
   69     vid = 0
   70     did = 0
   71     modes = myenum('Vendor', 'Class', 'Misc')
   72     mode = modes.Vendor
   73     strg = ""
   74     cstrg = ""
   75     for unm in usbids:
   76         if os.path.exists(unm):
   77             break
   78     for ln in open_read_ign(unm).readlines():
   79         if ln[0] == '#':
   80             continue
   81         ln = ln.rstrip('\n')
   82         if len(ln) == 0:
   83             continue
   84         if ishexdigit(ln[0:4]):
   85             mode = modes.Vendor
   86             vid = int(ln[:4], 16)
   87             usbvendors[vid] = ln[6:]
   88             continue
   89         if ln[0] == '\t' and ishexdigit(ln[1:3]):
   90             # usb.ids has a device id of 01xy, sigh
   91             if ln[3:5] == "xy":
   92                 did = int(ln[1:3], 16)*256
   93             else:
   94                 did = int(ln[1:5], 16)
   95             # USB devices
   96             if mode == modes.Vendor:
   97                 usbproducts[vid, did] = ln[7:]
   98                 continue
   99             elif mode == modes.Class:
  100                 nm = ln[5:]
  101                 if nm != "Unused":
  102                     strg = cstrg + ":" + nm
  103                 else:
  104                     strg = cstrg + ":"
  105                 usbclasses[vid, did, -1] = strg
  106                 continue
  107         if ln[0] == 'C':
  108             mode = modes.Class
  109             cid = int(ln[2:4], 16)
  110             cstrg = ln[6:]
  111             usbclasses[cid, -1, -1] = cstrg
  112             continue
  113         if mode == modes.Class and ln[0] == '\t' and ln[1] == '\t' and ishexdigit(ln[2:4]):
  114             prid = int(ln[2:4], 16)
  115             usbclasses[cid, did, prid] = ln[6:]
  116             continue
  117         mode = modes.Misc
  118     usbclasses[0xFF, 0xFF, 0xFF] = "Vendor Specific"
  119 
  120 def find_usb_prod(vid, pid):
  121     "Return device name from USB Vendor:Product list"
  122     strg = ""
  123     vendor = usbvendors.get(vid)
  124     if vendor:
  125         strg = str(vendor)
  126     else:
  127         return ""
  128     product = usbproducts.get((vid, pid))
  129     if product:
  130         return strg + " " + str(product)
  131     return strg
  132 
  133 def find_usb_class(cid, sid, pid):
  134     "Return USB protocol from usbclasses list"
  135     lnlst = len(usbclasses)
  136     cls = usbclasses.get((cid, sid, pid)) \
  137         or usbclasses.get((cid, sid, -1)) \
  138         or usbclasses.get((cid, -1, -1))
  139     if cls:
  140         return str(cls)
  141     return ""
  142 
  143 
  144 devlst = [
  145     'host',         # usb-storage
  146     'video4linux/video',    # uvcvideo et al.
  147     'sound/card',       # snd-usb-audio
  148     'net/',         # cdc_ether, ...
  149     'input/input',      # usbhid
  150     'usb:hiddev',       # usb hid
  151     'bluetooth/hci',    # btusb
  152     'ttyUSB',       # btusb
  153     'tty/',         # cdc_acm
  154     'usb:lp',       # usblp
  155     #'usb/lp',      # usblp
  156     'usb/',         # hiddev, usblp
  157     #'usbhid',      # hidraw
  158 ]
  159 
  160 def find_storage(hostno):
  161     "Return SCSI block dev names for host"
  162     res = ""
  163     for ent in os.listdir("/sys/class/scsi_device/"):
  164         (host, bus, tgt, lun) = ent.split(":")
  165         if host == hostno:
  166             try:
  167                 for ent2 in os.listdir("/sys/class/scsi_device/%s/device/block" % ent):
  168                     res += ent2 + " "
  169             except:
  170                 pass
  171     return res
  172 
  173 def add_drv(path, drvnm):
  174     res = ""
  175     try:
  176         for e2 in os.listdir(path+"/"+drvnm):
  177             if e2[0:len(drvnm)] == drvnm:
  178                 res += e2 + " "
  179         try:
  180             if res:
  181                 res += "(" + os.path.basename(os.readlink(path+"/driver")) + ") "
  182         except:
  183             pass
  184     except:
  185         pass
  186     return res
  187 
  188 def find_dev(driver, usbname):
  189     "Return pseudo devname that's driven by driver"
  190     res = ""
  191     for nm in devlst:
  192         dirnm = prefix + usbname
  193         prep = ""
  194         idx = nm.find('/')
  195         if idx != -1:
  196             prep = nm[:idx+1]
  197             dirnm += "/" + nm[:idx]
  198             nm = nm[idx+1:]
  199         ln = len(nm)
  200         try:
  201             for ent in os.listdir(dirnm):
  202                 if ent[:ln] == nm:
  203                     res += prep+ent+" "
  204                     if nm == "host":
  205                         res += "(" + find_storage(ent[ln:])[:-1] + ")"
  206         except:
  207             pass
  208     if driver == "usbhid":
  209         rg = re.compile(r'[0-9A-F]{4}:[0-9A-F]{4}:[0-9A-F]{4}\.[0-9A-F]{4}')
  210         for ent in os.listdir(prefix + usbname):
  211             m = rg.match(ent)
  212             if m:
  213                 res += add_drv(prefix+usbname+"/"+ent, "hidraw")
  214                 add = add_drv(prefix+usbname+"/"+ent, "input")
  215                 if add:
  216                     res += add
  217                 else:
  218                     for ent2 in os.listdir(prefix+usbname+"/"+ent):
  219                         m = rg.match(ent2)
  220                         if m:
  221                             res += add_drv(prefix+usbname+"/"+ent+"/"+ent2, "input")
  222     return res
  223 
  224 
  225 class UsbObject:
  226     def read_attr(self, name):
  227         path = prefix + self.path + "/" + name
  228         return open(path).readline().strip()
  229 
  230     def read_link(self, name):
  231         path = prefix + self.path + "/" + name
  232         return os.path.basename(os.readlink(path))
  233 
  234 class UsbEndpoint(UsbObject):
  235     "Container for USB endpoint info"
  236     def __init__(self, parent, fname, level):
  237         self.parent = parent
  238         self.level = level
  239         self.fname = fname
  240         self.path = ""
  241         self.epaddr = 0
  242         self.len = 0
  243         self.ival = ""
  244         self.type = ""
  245         self.attr = 0
  246         self.max = 0
  247         if self.fname:
  248             self.read(self.fname)
  249 
  250     def read(self, fname):
  251         self.fname = fname
  252         self.path = self.parent.path + "/" + fname
  253         self.epaddr = int(self.read_attr("bEndpointAddress"), 16)
  254         ival = int(self.read_attr("bInterval"), 16)
  255         if ival:
  256             self.ival = " (%s)" % self.read_attr("interval")
  257         self.len = int(self.read_attr("bLength"), 16)
  258         self.type = self.read_attr("type")
  259         self.attr = int(self.read_attr("bmAttributes"), 16)
  260         self.max = int(self.read_attr("wMaxPacketSize"), 16)
  261 
  262     def __repr__(self):
  263         return "<UsbEndpoint[%r]>" % self.fname
  264 
  265     def __str__(self):
  266         indent = "  " * self.level
  267         #name = "%s/ep_%02X" % (self.parent.fname, self.epaddr)
  268         name = ""
  269         body = "(EP) %02x: %s%s attr %02x len %02x max %03x" % \
  270             (self.epaddr, self.type, self.ival, self.attr, self.len, self.max)
  271         body = colorize(5, body)
  272         return "%-17s %s\n" % (indent + name, indent + body)
  273 
  274 
  275 class UsbInterface(UsbObject):
  276     "Container for USB interface info"
  277     def __init__(self, parent, fname, level=1):
  278         self.parent = parent
  279         self.level = level
  280         self.fname = fname
  281         self.path = ""
  282         self.iclass = 0
  283         self.isclass = 0
  284         self.iproto = 0
  285         self.noep = 0
  286         self.driver = ""
  287         self.devname = ""
  288         self.protoname = ""
  289         self.eps = []
  290         if self.fname:
  291             self.read(self.fname)
  292 
  293     def read(self, fname):
  294         self.fname = fname
  295         self.path = self.parent.path + "/" + fname
  296         self.iclass = int(self.read_attr("bInterfaceClass"),16)
  297         self.isclass = int(self.read_attr("bInterfaceSubClass"),16)
  298         self.iproto = int(self.read_attr("bInterfaceProtocol"),16)
  299         self.noep = int(self.read_attr("bNumEndpoints"))
  300         try:
  301             self.driver = self.read_link("driver")
  302             self.devname = find_dev(self.driver, self.path)
  303         except:
  304             pass
  305         self.protoname = find_usb_class(self.iclass, self.isclass, self.iproto)
  306         if showeps:
  307             for dirent in os.listdir(prefix + self.path):
  308                 if dirent.startswith("ep_"):
  309                     ep = UsbEndpoint(self, dirent, self.level+1)
  310                     self.eps.append(ep)
  311 
  312     def __repr__(self):
  313         return "<UsbInterface[%r]>" % self.fname
  314 
  315     def __str__(self):
  316         indent = "  " * self.level
  317         name = self.fname
  318         plural = (" " if self.noep == 1 else "s")
  319         body = "(IF) %02x:%02x:%02x %iEP%s (%s) %s %s" % \
  320             (self.iclass, self.isclass, self.iproto, self.noep, plural,
  321              self.protoname, colorize(3, self.driver), colorize(4, self.devname))
  322         strg = "%-17s %s\n" % (indent + name, indent + body)
  323         if showeps and self.eps:
  324             for ep in self.eps:
  325                 strg += str(ep)
  326         return strg
  327 
  328 class UsbDevice(UsbObject):
  329     "Container for USB device info"
  330     def __init__(self, parent, fname, level=0):
  331         self.parent = parent
  332         self.level = level
  333         self.fname = fname
  334         self.path = ""
  335         self.iclass = 0
  336         self.isclass = 0
  337         self.iproto = 0
  338         self.vid = 0
  339         self.pid = 0
  340         self.name = ""
  341         self.usbver = ""
  342         self.speed = ""
  343         self.maxpower = ""
  344         self.noports = 0
  345         self.nointerfaces = 0
  346         self.driver = ""
  347         self.devname = ""
  348         self.interfaces = []
  349         self.children = []
  350         if self.fname:
  351             self.read(self.fname)
  352             self.readchildren()
  353 
  354     def read(self, fname):
  355         self.fname = fname
  356         self.path = fname
  357         self.iclass = int(self.read_attr("bDeviceClass"), 16)
  358         self.isclass = int(self.read_attr("bDeviceSubClass"), 16)
  359         self.iproto = int(self.read_attr("bDeviceProtocol"), 16)
  360         self.vid = int(self.read_attr("idVendor"), 16)
  361         self.pid = int(self.read_attr("idProduct"), 16)
  362         try:
  363             self.name = self.read_attr("manufacturer") + " " \
  364                   + self.read_attr("product")
  365         except:
  366             pass
  367         if self.name:
  368             mch = re.match(r"Linux [^ ]* (.hci[_-]hcd) .HCI Host Controller", self.name)
  369             if mch:
  370                 self.name = mch.group(1)
  371         if not self.name:
  372             self.name = find_usb_prod(self.vid, self.pid)
  373         # Some USB Card readers have a better name then Generic ...
  374         if self.name.startswith("Generic"):
  375             oldnm = self.name
  376             self.name = find_usb_prod(self.vid, self.pid)
  377             if not self.name:
  378                 self.name = oldnm
  379         try:
  380             ser = self.read_attr("serial")
  381             # Some USB devs report "serial" as serial no. suppress
  382             if (ser and ser != "serial"):
  383                 self.name += " " + ser
  384         except:
  385             pass
  386         self.usbver = self.read_attr("version")
  387         self.speed = self.read_attr("speed")
  388         self.maxpower = self.read_attr("bMaxPower")
  389         self.noports = int(self.read_attr("maxchild"))
  390         try:
  391             self.nointerfaces = int(self.read_attr("bNumInterfaces"))
  392         except:
  393             self.nointerfaces = 0
  394         try:
  395             self.driver = self.read_link("driver")
  396             self.devname = find_dev(self.driver, self.path)
  397         except:
  398             pass
  399 
  400     def readchildren(self):
  401         if self.fname[0:3] == "usb":
  402             fname = self.fname[3:]
  403         else:
  404             fname = self.fname
  405         for dirent in os.listdir(prefix + self.fname):
  406             if not dirent[0:1].isdigit():
  407                 continue
  408             if os.access(prefix + dirent + "/bInterfaceClass", os.R_OK):
  409                 iface = UsbInterface(self, dirent, self.level+1)
  410                 self.interfaces.append(iface)
  411             else:
  412                 usbdev = UsbDevice(self, dirent, self.level+1)
  413                 self.children.append(usbdev)
  414         usbsortkey = lambda obj: [int(x) for x in re.split(r"[-:.]", obj.fname)]
  415         self.interfaces.sort(key=usbsortkey)
  416         self.children.sort(key=usbsortkey)
  417 
  418     def __repr__(self):
  419         return "<UsbDevice[%r]>" % self.fname
  420 
  421     def __str__(self):
  422         is_hub = (self.iclass == HUB_ICLASS)
  423         if is_hub:
  424             if noemptyhub and len(self.children) == 0:
  425                 return ""
  426         strg = ""
  427         if not (nohub and is_hub):
  428             indent = "  " * self.level
  429             name = self.fname
  430             plural = (" " if self.nointerfaces == 1 else "s")
  431             body = "%s %02x %iIF%s [USB %s, %5s Mbps, %5s] (%s)%s" % \
  432                 (colorize(1, "%04x:%04x" % (self.vid, self.pid)),
  433                  self.iclass, self.nointerfaces, plural,
  434                  self.usbver.strip(), self.speed, self.maxpower,
  435                  colorize(2 if is_hub else 1, self.name),
  436                  colorize(2, " hub") if is_hub else "")
  437             strg = "%-17s %s\n" % (indent + name, indent + body)
  438             if not (is_hub and not showhubint):
  439                 if showeps:
  440                     ep = UsbEndpoint(self, "ep_00", self.level+1)
  441                     strg += str(ep)
  442                 if showint: 
  443                     for iface in self.interfaces:
  444                         strg += str(iface)
  445         for child in self.children:
  446             strg += str(child)
  447         return strg
  448 
  449 
  450 def usage():
  451     "Displays usage information"
  452     print("Usage: lsusb.py [options]")
  453     print()
  454     print("Options:")
  455     print("  -h, --help            display this help")
  456     print("  -i, --interfaces      display interface information")
  457     print("  -I, --hub-interfaces  display interface information, even for hubs")
  458     print("  -u, --hide-empty-hubs suppress empty hubs")
  459     print("  -U, --hide-hubs       suppress all hubs")
  460     print("  -c, --color           use colors")
  461     print("  -C, --no-color        disable colors")
  462     print("  -e, --endpoints       display endpoint info")
  463     print("  -f FILE, --usbids-path FILE")
  464     print("                        override filename for /usr/share/usb.ids")
  465     print()
  466     print("Use lsusb.py -ciu to get a nice overview of your USB devices.")
  467 
  468 def read_usb():
  469     "Read toplevel USB entries and print"
  470     root_hubs = []
  471     for dirent in os.listdir(prefix):
  472         if not dirent[0:3] == "usb":
  473             continue
  474         usbdev = UsbDevice(None, dirent, 0)
  475         root_hubs.append(usbdev)
  476     root_hubs.sort(key=lambda x: int(x.fname[3:]))
  477     for usbdev in root_hubs:
  478         print(usbdev, end="")
  479 
  480 def main(argv):
  481     "main entry point"
  482     global showint, showhubint, noemptyhub, nohub
  483     global cols, usbids, showeps
  484     usecols = None
  485 
  486     long_options = [
  487         "help",
  488         "interfaces",
  489         "hub-interfaces",
  490         "hide-empty-hubs",
  491         "hide-hubs",
  492         "color",
  493         "no-color",
  494         "usbids-path=",
  495         "endpoints",
  496     ]
  497 
  498     try:
  499         (optlist, args) = getopt.gnu_getopt(argv[1:], "hiIuUwcCef:", long_options)
  500     except getopt.GetoptError as exc:
  501         print("Error:", exc, file=sys.stderr)
  502         sys.exit(2)
  503     for opt in optlist:
  504         if opt[0] in {"-h", "--help"}:
  505             usage()
  506             sys.exit(0)
  507         elif opt[0] in {"-i", "--interfaces"}:
  508             showint = True
  509         elif opt[0] in {"-I", "--hub-interfaces"}:
  510             showint = True
  511             showhubint = True
  512         elif opt[0] in {"-u", "--hide-empty-hubs"}:
  513             noemptyhub = True
  514         elif opt[0] in {"-U", "--hide-hubs"}:
  515             noemptyhub = True
  516             nohub = True
  517         elif opt[0] in {"-c", "--color"}:
  518             usecols = True
  519         elif opt[0] in {"-C", "--no-color"}:
  520             usecols = False
  521         elif opt[0] == "-w":
  522             print("Warning: option -w is no longer supported", file=sys.stderr)
  523         elif opt[0] in {"-f", "--usbids-path"}:
  524             usbids = [opt[1]]
  525         elif opt[0] in {"-e", "--endpoints"}:
  526             showeps = True
  527     if len(args) > 0:
  528         print("Error: excess args %s ..." % args[0], file=sys.stderr)
  529         sys.exit(2)
  530 
  531     if usecols is None:
  532         usecols = sys.stdout.isatty() and os.environ.get("TERM", "dumb") != "dumb"
  533 
  534     if usecols:
  535         cols = (norm, bold, red, green, amber, blue)
  536 
  537     if usbids[0]:
  538         try:
  539             parse_usb_ids()
  540         except:
  541             print(" WARNING: Failure to read usb.ids", file=sys.stderr)
  542             #print(sys.exc_info(), file=sys.stderr)
  543     read_usb()
  544 
  545 # Entry point
  546 if __name__ == "__main__":
  547     main(sys.argv)
  548 
  549