Skip to content

Commit

Permalink
Add better lsusb type output
Browse files Browse the repository at this point in the history
  • Loading branch information
deajan authored Oct 27, 2022
1 parent f6ed262 commit 748351d
Showing 1 changed file with 65 additions and 14 deletions.
79 changes: 65 additions & 14 deletions usb_reset.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#! /usr/bin/env python
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
#
# This file is part of ofunctions package
Expand All @@ -19,7 +19,7 @@
__copyright__ = "Copyright (C) 2022 Orsiris de Jong"
__description__ = "USB resetter allows to reset all USB controllers or a single USB device, also emulates lsusb"
__licence__ = "BSD 3 Clause"
__version__ = "1.0.0"
__version__ = "1.1.0"
__build__ = "2022102701"
__compat__ = "python2.7+"

Expand All @@ -31,13 +31,15 @@
import sys
import glob
from argparse import ArgumentParser
from collections import namedtuple


# Equivalent of the _IO('U', 20) constant in the linux kernel.
USBDEVFS_RESET = ord("U") << (4 * 2) | 20


def reset_usb_controllers():
# type: () -> None
"""
Allows to reset all USB controllers from a machine
Expand Down Expand Up @@ -65,36 +67,75 @@ def reset_usb_controllers():
bind.write(current_controller)


def get_usb_devices_paths(vendor_id: str = None, product_id: str = None) -> List[str]:
def get_usb_devices_paths(vendor_id=None, product_id=None, list_only=False):
# type: (str, str, bool) -> List[str]
"""
Emulates lsusb by reading from /sys/kernel/debug/usb/devices
Does not require lsusb to be installed and should work on a fair share of recent kernels
"""

found_devices = []
device_paths = []
Device = namedtuple(
"Devices", "vendor_id, product_id, device_path, manufacturer, product"
)
kernel_usb_debug_path = "/sys/kernel/debug/usb/devices" # see https://wiki.debian.org/HowToIdentifyADevice/USB

if not os.path.isfile(kernel_usb_debug_path):
# We could fallback to lsusb here if available, but then we need command_runner to deal with different subprocess.communicate outputs
return device_paths

with open(kernel_usb_debug_path, "r") as file_handle:
first_device = True
manufacturer = None
product = None
found_vendor_id = None
found_product_id = None
while True:
line = file_handle.readline()
if not line:
break
match = re.match(r"T:\s+Bus=(\d+).*Dev#=\s+(\d+)", line, re.IGNORECASE)
if match:
if not first_device:
# New device (begins with T:), let's reset previous values that belong to earlier found devices
found_devices.append(
Device(
vendor_id=found_vendor_id,
product_id=found_product_id,
device_path=device_path,
manufacturer=manufacturer,
product=product,
)
)
# print(
# "Found device {}:{} at {} Manufacturer={} Product={}".format(
# found_vendor_id, found_product_id, device_path, manufacturer, product
# )
# )
else:
first_device = False
manufacturer = None
product = None
found_vendor_id = None
found_product_id = None

# bus and dev are always 3 digit numbers, ex 001, 003, 004
possible_bus = "{:03d}".format(int(match.group(1)))
possible_dev = "{:03d}".format(int(match.group(2)))
bus = "{:03d}".format(int(match.group(1)))
dev = "{:03d}".format(int(match.group(2)))
match = re.match(r"S:\s+Manufacturer=(.*)", line, re.IGNORECASE)
if match:
manufacturer = match.group(1)
match = re.match(r"S:\s+Product=(.*)", line, re.IGNORECASE)
if match:
product = match.group(1)
match = re.match(
r"P:\s+Vendor=([0-9A-F]{4})\s+ProdID=([0-9A-F]{4})", line, re.IGNORECASE
)
if match:
found_vendor_id = match.group(1)
found_product_id = match.group(2)
device_path = os.path.join("/dev/bus/usb", possible_bus, possible_dev)
device_path = os.path.join("/dev/bus/usb", bus, dev)
if vendor_id == found_vendor_id and product_id == found_product_id:
if os.path.exists(device_path):
device_paths.append(device_path)
Expand All @@ -104,16 +145,26 @@ def get_usb_devices_paths(vendor_id: str = None, product_id: str = None) -> List
possible_bus, possible_dev
)
)
elif not vendor_id and not product_id:
print(
"Found device {}:{} at {}".format(
found_vendor_id, found_product_id, device_path
)
)
# We need to add the final device if exists:
if bus and dev:
found_devices.append(
Device(
vendor_id=found_vendor_id,
product_id=found_product_id,
device_path=device_path,
manufacturer=manufacturer,
product=product,
)
)

if list_only:
for device in found_devices:
print("Found device %s:%s at %s Manufacturer=%s, Product=%s" % device)
return device_paths


def reset_usb_device(device_path: str) -> bool:
def reset_usb_device(device_path):
# type: (str) -> bool
"""
Resets a usb device by dending USBDEVFS_RESET IOCTL to device
Device path is /dev/bus/usb/[bus_number]/[device_number]
Expand Down Expand Up @@ -188,4 +239,4 @@ def reset_usb_device(device_path: str) -> bool:
print("Bogus device {} given".format(device))

if args.list:
get_usb_devices_paths()
get_usb_devices_paths(list_only=True)

0 comments on commit 748351d

Please sign in to comment.