USBIP C library 0.7.0
Virtual USB devices & host drivers over USB/IP
Loading...
Searching...
No Matches
Host driver

The host API is the other half of the library: instead of being a USB device, you drive one.

It is deliberately libusb-shaped - porting libusb code is essentially s/libusb_/usbip_host_/ - and it talks USB/IP itself, so it needs no kernel driver, no vhci and no root. It runs the same on Linux, Windows and macOS.

In USB/IP terms this side is the client (role table): 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. Both are driven with identical code.

Link -lusbip-host (never both roles in one binary - see Getting Started).

This below example lists what a server exports, imports one device by vendor/product id, reads its descriptor over a control transfer, and round-trips a payload through its bulk endpoints. Run vendor_device first, then this against it.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "usbip-host.h"
#define VENDOR_ID 0x1209 /* what vendor_device serves */
#define PRODUCT_ID 0x0004
#define EP_BULK_OUT 0x01 /* host -> device */
#define EP_BULK_IN 0x81 /* device -> host */
int main(int argc, char **argv)
{
const char *server = (argc > 1) ? argv[1] : NULL; /* NULL = local, 127.0.0.1 */
int port = (argc > 2) ? atoi(argv[2]) : 3240;
/* 1. a context. It starts on the local transport; name a server to go remote. */
int rc = usbip_host_init(&ctx);
if (rc != USB_SUCCESS)
{
fprintf(stderr, "usbip_host_init failed: %s\n", usb_strerror(rc));
return 1;
}
if (server)
{
usb_transport *transport = usbip_transport(server, port);
usbip_host_set_transport(ctx, transport);
}
/* 2. what does the server export? (the USB/IP device list) */
long n_devices = usbip_host_get_device_list(ctx, &list);
if (n_devices < 0)
{
fprintf(stderr, "device list failed: %s (is a device being served?)\n",
usb_strerror((int)n_devices));
return 1;
}
printf("exported devices: %ld\n", n_devices);
for (long i = 0; i < n_devices; i++)
{
printf(" [%ld] %04x:%04x\n", i, desc.idVendor, desc.idProduct);
}
/* 3. import one of them by vendor/product id */
usbip_host_handle *handle = usbip_host_open_vid_pid(ctx, VENDOR_ID, PRODUCT_ID);
if (!handle)
{
fprintf(stderr, "open %04x:%04x failed (is vendor_device running?)\n",
VENDOR_ID, PRODUCT_ID);
return 1;
}
/* 4. a control transfer: the standard GET_DESCRIPTOR(device) */
0x0100, 0, (uint8_t *)&desc, sizeof(desc), 1000);
printf("opened %04x:%04x (device descriptor: %d bytes, USB %x.%02x)\n",
desc.idVendor, desc.idProduct, desc_bytes, USB_U16_MSB(desc.bcdUSB), USB_U16_LSB(desc.bcdUSB));
usbip_host_set_configuration(handle, 1); /* required before endpoint I/O */
/* 5. bulk I/O: send a payload, read the device's echo back */
const char *message = "hello device";
uint8_t echo[64];
int n_sent = 0;
int n_received = 0;
rc = usbip_host_bulk_transfer(handle, EP_BULK_OUT, (uint8_t *)message,
(int)strlen(message), &n_sent, 1000);
if (rc == USB_SUCCESS)
rc = usbip_host_bulk_transfer(handle, EP_BULK_IN, echo, sizeof(echo), &n_received, 1000);
if (rc != USB_SUCCESS)
{
fprintf(stderr, "bulk transfer failed: %s\n", usb_strerror(rc));
return 1;
}
printf("sent %d bytes, received %d back: \"%.*s\"\n", n_sent, n_received, n_received, echo);
/* 6. done */
int matched = (n_received == n_sent) && memcmp(echo, message, (size_t)n_sent) == 0;
const char *verdict = matched ? "OK: loopback round-trip matched\n" : "MISMATCH\n";
printf("%s", verdict);
return matched ? 0 : 1;
}
#define USB_REQ_DIR_IN
bmRequestType bit 7: device-to-host
Definition usbip.h:311
#define USB_REQ_GET_DESCRIPTOR
Fetch a descriptor; wValue is type:index (see the USB_DT_* codes)
Definition usbip.h:302
#define USB_SUCCESS
Error/status codes - same values as libusb, so ported constants keep working.
Definition usbip.h:391
const char * usb_strerror(int code)
Map a USB_* status/error code to a human-readable string.
int usbip_host_claim_interface(usbip_host_handle *handle, int iface)
Claim an interface.
void usbip_host_exit(usbip_host_context *ctx)
Destroy a context created by usbip_host_init() and free its resources.
long usbip_host_get_device_list(usbip_host_context *ctx, usbip_host_device ***list)
Enumerate the devices reachable on the context's transport.
struct usbip_host_context usbip_host_context
Library session / device list owner.
Definition usbip-host.h:30
int usbip_host_init(usbip_host_context **ctx)
Create a library context (the root object for enumeration and I/O).
void usbip_host_close(usbip_host_handle *handle)
Close a handle from usbip_host_open() / usbip_host_open_vid_pid().
struct usbip_host_device usbip_host_device
An enumerated device, not yet opened.
Definition usbip-host.h:31
int usbip_host_control_transfer(usbip_host_handle *handle, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint8_t *data, uint16_t wLength, unsigned timeout_ms)
Perform a synchronous control transfer on endpoint 0 (libusb-shaped).
int usbip_host_set_configuration(usbip_host_handle *handle, int config)
Select a configuration (standard SET_CONFIGURATION request).
int usbip_host_set_transport(usbip_host_context *ctx, usb_transport *transport)
Bind a USB/IP transport to the context - the only USB/IP-aware call.
int usbip_host_bulk_transfer(usbip_host_handle *handle, uint8_t endpoint, uint8_t *data, int length, int *transferred, unsigned timeout_ms)
Perform a synchronous bulk transfer (libusb-shaped).
struct usbip_host_handle usbip_host_handle
An opened device, usable for I/O
Definition usbip-host.h:32
int usbip_host_get_device_descriptor(usbip_host_device *dev, usb_device_descriptor *out)
Copy an enumerated device's 18-byte device descriptor.
usbip_host_handle * usbip_host_open_vid_pid(usbip_host_context *ctx, uint16_t vid, uint16_t pid)
Convenience: enumerate, match the first device by VID:PID, and open it.
int usbip_host_release_interface(usbip_host_handle *handle, int iface)
Release an interface claimed with usbip_host_claim_interface() (compatibility no-op).
void usbip_host_free_device_list(usbip_host_device **list)
Free a device list returned by usbip_host_get_device_list().
struct usb_transport usb_transport
Opaque handle to a USB/IP transport (the wire under host/device calls).
Definition usbip.h:494
usb_transport * usbip_transport(const char *host, int port)
Create a real USB/IP transport over TCP.
Standard USB device descriptor (Sec.9.6.1).
Definition usbip.h:449
uint16_t idVendor
Definition usbip.h:453
uint16_t idProduct
Definition usbip.h:453
Host API - write host drivers that drive USB devices over USB/IP.
#define USB_U16_LSB(x)
The least significant byte of a 16-bit value - the index half of a wValue, an interface number out of...
Definition usbip.h:238
#define USB_U16_MSB(x)
The most significant byte of a 16-bit value - the type half of a wValue, a control selector,...
Definition usbip.h:242
gcc host_drive.c -I. libusbip-host.a -pthread -o host_drive
./vendor_device # the device being driven
./host_drive # in a second terminal

On Windows the link line adds -lws2_32 -static and the programs end in .exe.

A Linux box exports real USB hardware with the kernel's own usbipd. Exporting is the one step that is Linux-only - usbipd / usbip bind have no equivalent elsewhere - while your driver still runs on any OS. Bind the device to the USB/IP host driver on the machine holding it:

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 point a host context at that machine and use it exactly as above - the API does not know or care that the device is physical:

usb_transport *transport = usbip_transport("10.0.0.5", 3240);
usbip_host_set_transport(ctx, transport);

examples/host/host_probe.c is the tool for this: it enumerates a server, opens the first device, walks the configuration descriptor and issues a class request. It is how the library is tested against real kernel gadgets.

Everything hangs off a usbip_host_context: usbip_host_init() creates it, usbip_host_exit() destroys it. The context starts on the local transport; usbip_host_set_transport() points it at a remote server (the context does not take ownership - free the transport yourself with usbip_transport_free() after exit).

Enumeration is libusb's model: usbip_host_get_device_list() returns a NULL-terminated array of usbip_host_device (and the count), each of which answers usbip_host_get_device_descriptor() and opens with usbip_host_open(); release the array with usbip_host_free_device_list() (open handles stay valid). usbip_host_open_vid_pid() collapses all of that into one call when a VID:PID match is enough.

Configuration is the standard requests under libusb names: usbip_host_set_configuration() (SET_CONFIGURATION) and usbip_host_set_interface_alt_setting() (SET_INTERFACE). usbip_host_claim_interface() / usbip_host_release_interface() are compatibility no-ops - USB/IP has no kernel driver to detach and no exclusive-ownership concept - kept so ported libusb code compiles and runs unchanged.

Function Transfer
usbip_host_control_transfer() control (endpoint 0)
usbip_host_bulk_transfer() bulk
usbip_host_interrupt_transfer() interrupt
usbip_host_alloc_transfer() + usbip_host_fill_iso_transfer() + usbip_host_submit_transfer() isochronous

The isochronous path mirrors libusb's asynchronous API - usbip_host_alloc_transfer(), usbip_host_fill_iso_transfer(), usbip_host_set_iso_packet_lengths(), usbip_host_get_iso_packet_buffer_simple(), usbip_host_free_transfer() - except that submission is synchronous: the USBIP host is blocking by design. examples/host/uvc_host.c pulls webcam frames this way.

A STALLed transfer returns USB_ERROR_PIPE; recover the pipe with usbip_host_clear_halt() (USB concepts explains STALL and halt). Every code renders through usb_strerror().

If the program that should drive the device already exists - dfu-util, lsusb, a pyusb script, a libusbK or WinUSB application - it needs no porting and none of the API above: Wrappers lets a stock binary drive a virtual device unmodified, and pyusb covers pyusb specifically.

  • Host API - the full host API reference
  • Wrappers - run an existing libusb / libusbK program instead
  • USB/IP clients - importing into an OS instead of driving from code
  • examples/host/ - the runnable programs: cdc_host, uvc_host, host_probe