pyusb¶
pyusb is the usual way to talk to USB from Python,
and it can drive a virtual device from this library without a kernel module, root,
or vhci - by loading the C libusb-1.0 wrapper as its backend
instead of the system libusb.
This is worth doing when the code that should drive the device is already written against pyusb. If you are writing the host side from scratch, the native host API is simpler - no C build, no environment variables.
What you need¶
| Piece | How you get it |
|---|---|
| pyusb | pip install pyusb - stock, unpatched |
| the wrapper | make libusb in the C library (mingw32-make libusb on Windows) → libusb-1.0.so.0 (Linux), libusb-1.0.0.dylib (macOS) or libusb-1.0.dll (Windows), in its platform build directory |
| a device to drive | any device example, in either language |
pyusb loads its libusb backend from a path you choose, so pointing it at the wrapper
needs no LD_PRELOAD, no copying a DLL, and no change to the rest of the program.
A complete session¶
Serve the vendor example - a bulk loopback that echoes whatever it receives:
Then drive it. With the variables set in os.environ rather than the shell, this script
is identical on every OS:
import os
import usb.core, usb.util, usb.backend.libusb1
# the wrapper reads these in libusb_init(), i.e. inside get_backend()
os.environ["USBIP_HOST"] = "127.0.0.1"
os.environ["USBIP_PORT"] = "3240"
# Linux : libusb-1.0.so.0
# macOS : libusb-1.0.0.dylib
# Windows: libusb-1.0.dll
backend = usb.backend.libusb1.get_backend(
find_library=lambda _: "/path/to/libusb-1.0.so.0")
dev = usb.core.find(idVendor=0x1209, idProduct=0x0004, backend=backend)
if dev is None:
raise SystemExit("device not found - is vendor_device.py running?")
dev.set_configuration() # standard requests go over USB/IP
dev.write(0x01, b"hi") # bulk OUT
print(bytes(dev.read(0x81, 64))) # bulk IN -> b"hi"
usb.util.dispose_resources(dev)
Set the environment before get_backend()
A stock binary has no way to pass a server address, so the wrapper reads
USBIP_HOST / USBIP_PORT from the
environment - once, inside
libusb_init(), which pyusb calls from get_backend(). Setting them afterwards
has no effect. They default to 127.0.0.1:3240.