Host driver¶
The host side drives a USB device instead of being one. It talks USB/IP itself, so
it needs no kernel driver, no vhci and no root - which makes it ideal for tests:
serve a device in one process and drive it from another, on any OS.
In USB/IP terms this side is the client
(which end is the server): something
must already be serving before any of the calls below can succeed - a program on the
device API, or a real usbipd exporting genuine hardware.
This imports a device, reads its descriptor over a control transfer, and round-trips a
payload through its bulk endpoints. Run vendor_device.py first, then this against it.
#!/usr/bin/env python3
"""
The documentation's host example: import a device from a USB/IP server and drive
it with usbip.host (libusb-shaped), with no kernel driver, no vhci and no root.
Mirrors the C library's doc/examples/host_drive.c.
In USB terms this program is the HOST; in USB/IP terms it is the CLIENT - it
CONNECTS to a server that is already serving a device (the device end listens).
Start the device first, then drive it:
python3 examples/vendor_device.py & # serves 1209:0004 on :3240
python3 host_drive.py # ...or: host_drive.py --host 10.0.0.5
The same code drives a REAL device exported by a real usbipd - point it at that
server, name the bus id it exported, and drop the --vid/--pid check.
"""
import argparse
import sys
import usbip.host
from usbip import USBIP, Stall
from usbip.core import DeviceDescriptor
VENDOR_ID = 0x1209 # what vendor_device.py serves
PRODUCT_ID = 0x0004
EP_BULK_OUT = 0x01 # host -> device
EP_BULK_IN = 0x81 # device -> host
def main():
ap = argparse.ArgumentParser(description="drive a USB/IP device from Python")
ap.add_argument("--host", default=None, help="USB/IP server (default: local)")
ap.add_argument("--port", type=int, default=3240)
ap.add_argument("--busid", default="1-1", help="bus id to import (real usbipd exports others)")
ap.add_argument("--vid", type=lambda s: int(s, 0), default=VENDOR_ID)
ap.add_argument("--pid", type=lambda s: int(s, 0), default=PRODUCT_ID)
args = ap.parse_args()
# 1. import the device. Naming a transport is the only USB/IP-aware step;
# without one it connects to 127.0.0.1:3240. open() imports the bus id,
# checks vid/pid, reads the device descriptor and sets configuration 1.
transport = USBIP(args.host, args.port) if args.host else None
try:
handle = usbip.host.open(args.vid, args.pid, busid=args.busid, transport=transport)
except OSError as exc:
print(
f"cannot reach the USB/IP server: {exc} (is vendor_device.py running?)", file=sys.stderr
)
return 1
except Exception as exc: # NotFound, protocol errors
print(f"import failed: {exc}", file=sys.stderr)
return 1
with handle:
# 2. a control transfer: the standard GET_DESCRIPTOR(device)
raw = handle.control(0x80, 0x06, 0x0100, 0, 18)
desc = DeviceDescriptor.parse(raw)
print(
f"opened {desc.idVendor:04x}:{desc.idProduct:04x} "
f"(device descriptor: {len(raw)} bytes, USB {desc.bcdUSB >> 8:x}.{desc.bcdUSB & 0xFF:02x})"
)
# 3. bulk I/O: send a payload, read the device's echo back
message = b"hello device"
try:
handle.bulk_out(EP_BULK_OUT, message)
echo = handle.bulk_in(EP_BULK_IN, 64)
except Stall:
print("the device STALLed the pipe", file=sys.stderr)
return 1
print(f"sent {len(message)} bytes, received {len(echo)} back: {echo!r}")
matched = echo == message
verdict = "OK: loopback round-trip matched" if matched else "MISMATCH"
print(verdict)
return 0 if matched else 1
if __name__ == "__main__":
sys.exit(main())
Start the device in one terminal and the driver in another:
python3 examples/vendor_device.py # the device being driven
python3 examples/doc/host_drive.py # in a second terminal
usbip.open() / usbip_host_open_vid_pid() return a handle with the
familiar control / bulk / interrupt / iso transfers.
#include <stdio.h>
#include "usbip-host.h"
int main(void) {
usbip_host_context *ctx;
usbip_host_init(&ctx); /* local: 127.0.0.1:3240 */
usbip_host_handle *h = usbip_host_open_vid_pid(ctx, 0x1209, 0x0001);
if (!h) {
fprintf(stderr, "device not found\n");
return 1;
}
usb_device_descriptor d;
usbip_host_control_transfer(h, 0x80, 0x06, 0x0100, 0, (uint8_t *)&d, 18, 1000);
int n; uint8_t echo[64];
usbip_host_bulk_transfer(h, 0x01, (uint8_t *)"ping\n", 5, &n, 1000); /* OUT */
usbip_host_bulk_transfer(h, 0x81, echo, sizeof(echo), &n, 1000); /* IN */
printf("%04x:%04x, %d echo bytes\n", d.idVendor, d.idProduct, n);
usbip_host_close(h);
usbip_host_exit(ctx);
return 0;
}
A STALLed transfer raises Stall (USB_ERROR_PIPE in C); recover the pipe with
handle.clear_halt(addr).
The same code drives genuine hardware that a Linux box exports with the kernel's own
usbipd - the API neither knows nor cares that the device is physical, and your driver
still runs on any OS. Exporting real hardware is the one step that is Linux-only, since
usbipd/usbip bind have no equivalent elsewhere; run it on the machine holding the
device:
sudo modprobe usbip-host
sudo usbipd -D # the USB/IP server daemon
usbip list -l # find the bus id, e.g. 3-2
sudo usbip bind -b 3-2 # export it
Then import it by that bus id, without a vid/pid check:
import usbip
h = usbip.attach("10.0.0.5", "3-2") # remote host, the exported bus id
print(h.control(0x80, 0x06, 0x0100, 0, 18)) # its real device descriptor
Unlike the kernel's usbip list -r, the Python host has no device-list call: it imports
the bus id you name (1-1 is what this library always serves).
For a real device class, subclass Driver (Python) or register a
usbip_host_driver (C). The bundled drivers - HID, CDC-ACM, MSC,
MTP, Bluetooth - open with Driver.open(); see
Host & drivers for the pattern.
If the program that should drive the device already exists - dfu-util, lsusb, a
libusbK or WinUSB application - it needs no porting and none of the API above: the
C wrapper libraries let a stock binary drive a virtual device
unmodified. For pyusb specifically - a Python caller pointed at the libusb
wrapper - see pyusb.
Mix languages
A C device can be driven by the Python host and vice-versa - they share the wire protocol. The test suite does exactly this for cross-language verification.
Full programs: examples/doc/host_drive.py; in C, examples/host/cdc_host.c,
uvc_host.c, host_probe.c. API: Host API ·
Host drivers.