mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usbutils.git
synced 2024-11-30 07:13:40 +08:00
86dcab8964
Autotools is great for what it is designed for, but that epoch is long ago, and shipping random files/scripts that are not actually checked into the build system is ripe for problems. Convert the build over to use meson instead. Bonus is that the build logic is simpler (if not still messy in places), and that the build itself is way faster. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
561 lines
14 KiB
Python
Executable File
561 lines
14 KiB
Python
Executable File
#!/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 = [
|
|
"@DATADIR@/usb.ids",
|
|
"/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)
|
|
|
|
|