Skip to content

adafruit/pyusb-usbip-backend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pyusb-usbip-backend

PyUSB backend that connects to a USB/IP server as a client, using Linux-kernel-compatible USB/IP wire protocol messages.

Features

  • OP_REQ_DEVLIST + OP_REQ_IMPORT
  • USB/IP URB submit/reply flow
  • PyUSB backend interface implementation (usb.backend.IBackend)
  • Control transfers over EP0
  • Descriptor discovery over control requests
  • mDNS discovery helper for _usbip._tcp services
  • CLI for backend testing
  • Wireshark capture (pcapng) of USB URBs to file or live via named pipe
  • ESP syslog receiver (RFC 5424 over TCP), printed to console and captured alongside USB traffic

discover uses the cross-platform Python zeroconf library (no shelling out to Avahi tools).

Install

python -m pip install -e .

CLI examples

Discover USB/IP servers advertised via mDNS:

usbip-pyusb-test discover

List exported devices:

usbip-pyusb-test --host <server-ip> list

Probe a device through PyUSB:

usbip-pyusb-test --host <server-ip> probe --vid 0x303a --pid 0x4004 --get-device-desc

Issue a control IN transfer:

usbip-pyusb-test --host <server-ip> control \
  --vid 0x303a --pid 0x4004 \
  --direction in \
  --bm-request-type 0x80 --b-request 0x06 --w-value 0x0100 --length 18

Wireshark capture

Add --pcap <path> to any CLI command to log USB URBs in pcapng format. Add --syslog to also receive ESP log output (RFC 5424 over TCP port 1514) and include it in the capture.

The pcapng file contains two interfaces that Wireshark displays together:

  • usbip0 -- USB URB traffic
  • syslog0 -- ESP syslog messages (decoded by Wireshark's syslog dissector)

Capture to a file

usbip-pyusb-test --host <server-ip> --pcap capture.pcapng --syslog \
  probe --vid 0x303a --pid 0x4004
wireshark capture.pcapng

Live capture

Use a named pipe so Wireshark displays packets as they happen:

mkfifo /tmp/usb.pcap
wireshark -k -i /tmp/usb.pcap &
usbip-pyusb-test --host <server-ip> --pcap /tmp/usb.pcap --syslog \
  probe --vid 0x303a --pid 0x4004

Syslog only (no pcap)

Print ESP logs to stderr without capturing:

usbip-pyusb-test --host <server-ip> --syslog list

Integration tests

# Capture USB + syslog to file
python test_integration.py 192.168.1.94 --pcap capture.pcapng --syslog

# Live view
mkfifo /tmp/usb.pcap
wireshark -k -i /tmp/usb.pcap &
python test_integration.py 192.168.1.94 --pcap /tmp/usb.pcap --syslog

Programmatic capture

from usbip_backend.pcap import PcapWriter
from usbip_backend.syslog import SyslogReceiver
from usbip_backend import get_backend

pcap = PcapWriter(open("capture.pcapng", "wb"))
syslog = SyslogReceiver("192.168.1.20", pcap=pcap)
syslog.start()

backend = get_backend("192.168.1.20", pcap=pcap)
dev = usb.core.find(backend=backend, idVendor=0x303A, idProduct=0x4004)
# USB URBs and syslog messages are both logged to capture.pcapng

Python usage

import usb.core
from usbip_backend import get_backend

backend = get_backend("192.168.1.20")
dev = usb.core.find(backend=backend, idVendor=0x303A, idProduct=0x4004)
if dev is not None:
    data = dev.ctrl_transfer(0x80, 0x06, 0x0100, 0, 18)
    print(bytes(data))

PySerial URL-extension mechanic (serial_for_url)

If you want esptool / idf.py to open a custom URL like usbip://..., PySerial supports this via protocol handler modules discovered by serial.serial_for_url().

How resolution works

  • serial_for_url("<scheme>://...") extracts <scheme>.
  • It tries to import protocol_<scheme> from each package listed in serial.protocol_handler_packages.
  • The handler module must provide a Serial class implementing the PySerial API (typically by subclassing serial.serialutil.SerialBase).

So for usbip://..., PySerial looks for protocol_usbip.py.

Minimal package layout

src/
  yourpkg/
    urlhandler/
      __init__.py
      protocol_usbip.py   # defines class Serial(...)

Register handler package

import serial

# package containing protocol_usbip.py
serial.protocol_handler_packages.append("yourpkg.urlhandler")

ser = serial.serial_for_url("usbip://192.168.1.20/1-2.3", timeout=1)

Handler skeleton

# src/yourpkg/urlhandler/protocol_usbip.py
from serial.serialutil import SerialBase

class Serial(SerialBase):
    def open(self):
        if self.is_open:
            return
        # parse self.portstr (usbip://...)
        # connect transport
        self.is_open = True

    def close(self):
        self.is_open = False

    def read(self, size=1):
        return b""  # return up to size bytes

    def write(self, data):
        return len(data)

    @property
    def in_waiting(self):
        return 0

    def reset_input_buffer(self):
        pass

    def reset_output_buffer(self):
        pass

Notes for ESP flashing

For reliable ESP bootloader entry, implement DTR/RTS semantics correctly (ser.dtr = ..., ser.rts = ...) and honor read/write timeout behavior. esptool relies on these during connect/reset.

About

PyUSB backend that talks to USBIP servers directly.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages