Pure-python wrapper for libusb-1.0 providing comprehensive USB device access with support for all transfer types
—
USB device access involves two main classes: USBDevice for device enumeration and descriptor access, and USBDeviceHandle for actual communication and configuration. This separation allows efficient device discovery without claiming system resources until communication is needed.
Access device identification, location, and capability information without opening the device for communication.
class USBDevice:
def getBusNumber(self):
"""
Get device's bus number.
Returns:
int: Bus number
"""
def getPortNumber(self):
"""
Get device's port number.
Returns:
int: Port number
"""
def getPortNumberList(self):
"""
Get list of port numbers from root hub to device.
Returns:
list[int]: Port numbers along the path
"""
def getDeviceAddress(self):
"""
Get device's address on the bus.
Returns:
int: Device address (1-127)
"""
def getVendorID(self):
"""
Get device's vendor ID.
Returns:
int: 16-bit vendor ID
"""
def getProductID(self):
"""
Get device's product ID.
Returns:
int: 16-bit product ID
"""
def getbcdUSB(self):
"""
Get USB specification version in BCD format.
Returns:
int: USB version (e.g. 0x0200 for USB 2.0)
"""
def getbcdDevice(self):
"""
Get device release number in BCD format.
Returns:
int: Device release number
"""
def getDeviceClass(self):
"""
Get device class code.
Returns:
int: Device class (0 if specified at interface level)
"""
def getDeviceSubClass(self):
"""
Get device subclass code.
Returns:
int: Device subclass
"""
def getDeviceProtocol(self):
"""
Get device protocol code.
Returns:
int: Device protocol
"""
def getDeviceSpeed(self):
"""
Get device operating speed.
Returns:
int: Speed constant (SPEED_LOW, SPEED_FULL, SPEED_HIGH, SPEED_SUPER, SPEED_SUPER_PLUS)
"""
def getMaxPacketSize0(self):
"""
Get maximum packet size for endpoint 0 (control endpoint).
Returns:
int: Maximum packet size in bytes
"""
def getMaxPacketSize(self, endpoint):
"""
Get maximum packet size for specified endpoint.
Args:
endpoint (int): Endpoint address
Returns:
int: Maximum packet size in bytes
Warning:
May not always give expected results. Consult endpoint descriptor instead.
"""
def getMaxISOPacketSize(self, endpoint):
"""
Get maximum isochronous packet size for endpoint.
Args:
endpoint (int): Endpoint address
Returns:
int: Maximum ISO packet size in bytes
Warning:
May not always give expected results. Consult endpoint descriptor instead.
"""
def getNumConfigurations(self):
"""
Get number of possible configurations.
Returns:
int: Number of configurations
"""Open devices for communication and manage device handle lifecycle.
class USBDevice:
def open(self):
"""
Open device for communication.
Returns:
USBDeviceHandle: Handle for device communication
Raises:
USBError: If device cannot be opened
"""
def close(self):
"""Close device and release resources."""
class USBDeviceHandle:
def close(self):
"""
Close device handle and release resources.
Note:
Cancels any in-flight transfers. May block until transfers complete.
"""
def getDevice(self):
"""
Get USBDevice instance for this handle.
Returns:
USBDevice: Associated device instance
"""Retrieve human-readable device information including manufacturer, product name, and serial number.
class USBDevice:
def getManufacturer(self):
"""
Get manufacturer string (opens device temporarily).
Returns:
str or None: Manufacturer name or None if not available
"""
def getProduct(self):
"""
Get product string (opens device temporarily).
Returns:
str or None: Product name or None if not available
"""
def getSerialNumber(self):
"""
Get serial number string (opens device temporarily).
Returns:
str or None: Serial number or None if not available
"""
def getSupportedLanguageList(self):
"""
Get list of supported language IDs (opens device temporarily).
Returns:
list[int]: List of language IDs
"""
class USBDeviceHandle:
def getManufacturer(self):
"""
Get manufacturer string.
Returns:
str or None: Manufacturer name or None if not available
"""
def getProduct(self):
"""
Get product string.
Returns:
str or None: Product name or None if not available
"""
def getSerialNumber(self):
"""
Get serial number string.
Returns:
str or None: Serial number or None if not available
"""
def getSupportedLanguageList(self):
"""
Get list of supported language IDs.
Returns:
list[int]: List of USB language identifiers
"""
def getStringDescriptor(self, descriptor, lang_id, errors='strict'):
"""
Get string descriptor in specified language.
Args:
descriptor (int): String descriptor index
lang_id (int): Language ID
errors (str): Error handling mode ('strict', 'ignore', 'replace')
Returns:
str or None: String content or None if not found
"""
def getASCIIStringDescriptor(self, descriptor, errors='strict'):
"""
Get string descriptor in first available language as ASCII.
Args:
descriptor (int): String descriptor index
errors (str): Error handling mode ('strict', 'ignore', 'replace')
Returns:
str or None: String content or None if not found
"""Get and set device configuration, the top-level organizational unit for device functionality.
class USBDeviceHandle:
def getConfiguration(self):
"""
Get current configuration number.
Returns:
int: Configuration value (1-based, 0 = unconfigured)
"""
def setConfiguration(self, configuration):
"""
Set device configuration.
Args:
configuration (int): Configuration value (1-based, 0 = unconfigure)
Raises:
USBError: If configuration cannot be set
"""Claim and release interfaces for exclusive access, and manage alternate settings within interfaces.
class USBDeviceHandle:
def claimInterface(self, interface):
"""
Claim interface for exclusive access.
Args:
interface (int): Interface number
Returns:
_ReleaseInterface: Context manager that auto-releases on exit
Raises:
USBError: If interface cannot be claimed
Usage:
with handle.claimInterface(0):
# perform operations
pass
# interface automatically released
"""
def releaseInterface(self, interface):
"""
Release interface, allowing other processes to claim it.
Args:
interface (int): Interface number
Raises:
USBError: If interface cannot be released
"""
def setInterfaceAltSetting(self, interface, alt_setting):
"""
Set alternate setting for interface.
Args:
interface (int): Interface number
alt_setting (int): Alternate setting number
Raises:
USBError: If alternate setting cannot be set
"""Manage endpoint states and clear error conditions.
class USBDeviceHandle:
def clearHalt(self, endpoint):
"""
Clear halt/stall condition on endpoint.
Args:
endpoint (int): Endpoint address
Raises:
USBError: If halt cannot be cleared
"""Reset device to initial state, potentially causing disconnection and reconnection.
class USBDeviceHandle:
def resetDevice(self):
"""
Reset device to initial state.
Raises:
USBErrorNotFound: If device disconnects and must be rediscovered
USBError: If reset fails
Note:
May cause device to disconnect and reconnect with new address.
"""Manage kernel driver attachment and detachment for interface access on Linux and similar systems.
class USBDeviceHandle:
def kernelDriverActive(self, interface):
"""
Check if kernel driver is active on interface.
Args:
interface (int): Interface number
Returns:
bool: True if kernel driver is active
"""
def detachKernelDriver(self, interface):
"""
Detach kernel driver from interface.
Args:
interface (int): Interface number
Raises:
USBError: If driver cannot be detached
Note:
Required on Linux before claiming some interfaces.
"""
def attachKernelDriver(self, interface):
"""
Re-attach kernel driver to interface.
Args:
interface (int): Interface number
Raises:
USBError: If driver cannot be attached
"""
def setAutoDetachKernelDriver(self, enable):
"""
Enable/disable automatic kernel driver detachment.
Args:
enable (bool): True to enable auto-detach
Raises:
USBError: If auto-detach cannot be configured
"""Create transfer objects for asynchronous operations.
class USBDeviceHandle:
def getTransfer(self, iso_packets=0, short_is_error=False, add_zero_packet=False):
"""
Create transfer object for asynchronous operations.
Args:
iso_packets (int): Number of isochronous packet descriptors
short_is_error (bool): Treat short transfers as errors
add_zero_packet (bool): Add zero-length packet for transfers that are multiples of endpoint size
Returns:
USBTransfer: Transfer object for async operations
"""import usb1
with usb1.USBContext() as context:
# Find device by vendor/product ID
device = context.getByVendorIDAndProductID(0x1234, 0x5678)
if not device:
print("Device not found")
return
# Display device information
print(f"Device: {device.getVendorID():04x}:{device.getProductID():04x}")
print(f"Bus {device.getBusNumber():03d}, Address {device.getDeviceAddress():03d}")
print(f"USB {device.getbcdUSB() >> 8}.{(device.getbcdUSB() >> 4) & 0xf}")
print(f"Speed: {device.getDeviceSpeed()}")
print(f"Class: {device.getDeviceClass()}")
# Access string descriptors
try:
print(f"Manufacturer: {device.getManufacturer()}")
print(f"Product: {device.getProduct()}")
print(f"Serial: {device.getSerialNumber()}")
except usb1.USBError as e:
print(f"Error reading strings: {e}")import usb1
with usb1.USBContext() as context:
device = context.getByVendorIDAndProductID(0x1234, 0x5678)
if device:
with device.open() as handle:
# Check current configuration
current_config = handle.getConfiguration()
print(f"Current configuration: {current_config}")
# Set configuration if needed
if current_config != 1:
handle.setConfiguration(1)
print("Set configuration to 1")
# Check for kernel driver and detach if necessary
interface_num = 0
if handle.kernelDriverActive(interface_num):
print("Detaching kernel driver")
handle.detachKernelDriver(interface_num)
# Claim interface with automatic release
with handle.claimInterface(interface_num):
print(f"Claimed interface {interface_num}")
# Set alternate setting if needed
handle.setInterfaceAltSetting(interface_num, 0)
print("Set alternate setting 0")
# Perform operations here
# ...
print("Interface automatically released")import usb1
def print_device_info(device):
"""Print comprehensive device information."""
print(f"\nDevice {device.getVendorID():04x}:{device.getProductID():04x}")
print(f" Bus: {device.getBusNumber():03d}")
print(f" Address: {device.getDeviceAddress():03d}")
print(f" Port path: {' -> '.join(map(str, device.getPortNumberList()))}")
print(f" USB version: {device.getbcdUSB() >> 8}.{(device.getbcdUSB() >> 4) & 0xf}")
print(f" Device version: {device.getbcdDevice() >> 8}.{(device.getbcdDevice() >> 4) & 0xf}")
print(f" Speed: {device.getDeviceSpeed()}")
print(f" Class: {device.getDeviceClass()}")
print(f" Subclass: {device.getDeviceSubClass()}")
print(f" Protocol: {device.getDeviceProtocol()}")
print(f" Max packet size (EP0): {device.getMaxPacketSize0()}")
print(f" Configurations: {device.getNumConfigurations()}")
# Try to get string descriptors
try:
manufacturer = device.getManufacturer()
product = device.getProduct()
serial = device.getSerialNumber()
languages = device.getSupportedLanguageList()
if manufacturer:
print(f" Manufacturer: {manufacturer}")
if product:
print(f" Product: {product}")
if serial:
print(f" Serial: {serial}")
if languages:
print(f" Languages: {[hex(lang) for lang in languages]}")
except usb1.USBError as e:
print(f" Error reading strings: {e}")
with usb1.USBContext() as context:
for device in context.getDeviceIterator(skip_on_error=True):
print_device_info(device)import usb1
def safe_device_operation(vendor_id, product_id):
"""Demonstrate safe device operation with proper error handling."""
context = None
device = None
handle = None
try:
# Create context
context = usb1.USBContext()
context.open()
# Find device
device = context.getByVendorIDAndProductID(vendor_id, product_id)
if not device:
print("Device not found")
return False
# Open device
handle = device.open()
# Detach kernel driver if needed
interface_num = 0
kernel_attached = False
try:
if handle.kernelDriverActive(interface_num):
handle.detachKernelDriver(interface_num)
kernel_attached = True
print("Detached kernel driver")
except usb1.USBErrorNotSupported:
print("Kernel driver operations not supported")
# Claim interface
interface_claimed = False
try:
handle.claimInterface(interface_num)
interface_claimed = True
print("Claimed interface")
# Perform operations here
print("Performing device operations...")
return True
except usb1.USBError as e:
print(f"Error claiming interface: {e}")
return False
finally:
# Clean up in reverse order
if interface_claimed:
try:
handle.releaseInterface(interface_num)
print("Released interface")
except usb1.USBError as e:
print(f"Error releasing interface: {e}")
if kernel_attached:
try:
handle.attachKernelDriver(interface_num)
print("Re-attached kernel driver")
except usb1.USBError as e:
print(f"Error re-attaching kernel driver: {e}")
except usb1.USBError as e:
print(f"USB error: {e}")
return False
finally:
# Clean up resources
if handle:
handle.close()
if device:
device.close()
if context:
context.close()
# Usage
safe_device_operation(0x1234, 0x5678)Install with Tessl CLI
npx tessl i tessl/pypi-libusb1