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

Get the library, build against it, run a device, import it with a USB/IP client.

The only prebuilt package is for Windows - one archive per release, both architectures in it: github.com/jabezwinston/usbip-c/releases. It unpacks to:

Path What it is
include/ every header - one -Iinclude covers all of them
lib/x86, lib/x64 both roles, static (.a) and shared (.dll + .dll.a)
bin/x86, bin/x64 each device example, as a ready-to-run .exe
dropin/x86, dropin/x64 the drop-in libusb-1.0.dll and libusbK.dll
src/ the class layer, which has no binary form

Everywhere else, build from source. It needs GNU make and a C11 compiler, and nothing else - the headers are plain C11 on the platform's own sockets and threads.

git clone https://github.com/jabezwinston/usbip-c
cd usbip-c
make # examples, plus both role libraries in src/build/

On Windows that is mingw32-make, not MSYS2's make; to cross-build for Windows from Linux or macOS, make OS=Windows_NT.

On Linux and macOS, sudo make install PREFIX=/usr/local also drops pkg-config files:

gcc my_device.c $(pkg-config --cflags --libs usbip-device) -o my_device

Either way, what you build against is three headers and one library per role:

File What it is
usbip.h shared USB types, errors, transport - both roles include it
usbip-device.h the device API - be a USB device
usbip-host.h the host API - drive a USB device
libusbip-device.a, libusbip-host.a static libraries, one per role
.so.0 / .dll (+ .dll.a) / .0.dylib the same two, shared - Linux / Windows / macOS
Note
Link one role per binary. The two libraries share internal code, so a program linking both gets duplicate symbols. A process that needs both sides at once (in-process loopback tests) should build the library from source instead.

Linux / macOS, from a source tree (macOS: clang, and DYLD_LIBRARY_PATH in place of LD_LIBRARY_PATH):

gcc my_device.c -Iinclude src/build/libusbip-device.a -pthread -o my_device
gcc my_device.c -Iinclude -Lsrc/build -lusbip-device -o my_device # shared instead
LD_LIBRARY_PATH=src/build ./my_device

Windows (mingw-w64), from the unpacked archive: -lws2_32 is winsock, and -static folds in winpthreads so you ship one .exe. A shared build instead wants the .dll beside the program, which is where Windows looks first:

gcc my_device.c -Iinclude lib\x64\libusbip-device.a -lws2_32 -pthread -static -o my_device.exe

Swap -lusbip-device for -lusbip-host to build a host driver instead.

A USB Boot-protocol HID keyboard, written on the device core alone: hand-authored descriptors and one control handler, with no class layer involved. Beyond the four core calls, the rest is the USB spec's own boot-keyboard boilerplate.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "usbip-device.h"
/* identity + wiring */
#define VENDOR_ID 0x1209 /* pid.codes, the free VID for open hardware */
#define PRODUCT_ID 0x0011
#define EP_HID_IN 0x81 /* device -> host: the 8-byte key reports */
/* HID 1.11 - the handful of constants this example needs */
#define HID_DT_HID 0x21 /* the HID descriptor, inside the configuration */
#define HID_DT_REPORT 0x22 /* the Report descriptor, fetched separately */
#define HID_GET_REPORT 0x01
#define HID_GET_IDLE 0x02
#define HID_GET_PROTOCOL 0x03
#define HID_SET_REPORT 0x09
#define HID_SET_IDLE 0x0A
#define HID_SET_PROTOCOL 0x0B
#define HID_SUBCLASS_BOOT 0x01 /* bInterfaceSubClass: boot interface */
#define HID_PROTOCOL_KEYBOARD 0x01 /* bInterfaceProtocol: keyboard */
#define KEY_REPORT_LEN 8 /* modifiers, reserved, 6 key codes */
/* The standard boot-keyboard Report descriptor: an 8-byte Input report
* (modifiers, reserved, 6 key codes) plus a 1-byte LED Output report. Raw
* bytes in wire order - nothing beyond the core is used here. */
static const uint8_t KEYBOARD_REPORT_DESC[] = {
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x06, /* Usage (Keyboard) */
0xA1, 0x01, /* Collection (Application) */
0x05, 0x07, /* Usage Page (Keyboard/Keypad) */
0x19, 0xE0, /* Usage Minimum (Left Control) */
0x29, 0xE7, /* Usage Maximum (Right GUI) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x08, /* Report Count (8) */
0x81, 0x02, /* Input (Data,Var,Abs) - modifiers */
0x95, 0x01, /* Report Count (1) */
0x75, 0x08, /* Report Size (8) */
0x81, 0x03, /* Input (Const) - reserved byte */
0x95, 0x05, /* Report Count (5) */
0x75, 0x01, /* Report Size (1) */
0x05, 0x08, /* Usage Page (LEDs) */
0x19, 0x01, /* Usage Minimum (Num Lock) */
0x29, 0x05, /* Usage Maximum (Kana) */
0x91, 0x02, /* Output (Data,Var,Abs) - LEDs */
0x95, 0x01, /* Report Count (1) */
0x75, 0x03, /* Report Size (3) */
0x91, 0x03, /* Output (Const) - padding */
0x95, 0x06, /* Report Count (6) */
0x75, 0x08, /* Report Size (8) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x65, /* Logical Maximum (101) */
0x05, 0x07, /* Usage Page (Keyboard/Keypad) */
0x19, 0x00, /* Usage Minimum (0) */
0x29, 0x65, /* Usage Maximum (101) */
0x81, 0x00, /* Input (Data,Array) - 6 key codes */
0xC0, /* End Collection */
};
/* The HID class descriptor (HID 1.11 6.2.1). Class-specific descriptor structs
* are not in usbip.h - the core is class-agnostic - so the example declares its
* own and appends it as a typed descriptor. */
typedef struct USB_PACKED
{
uint8_t bLength, bDescriptorType;
uint16_t bcdHID;
uint8_t bCountryCode, bNumDescriptors, bReportType;
uint16_t wReportLength;
} hid_descriptor;
/* ---- the one control handler this device needs -------------------------- */
/* The core answers every standard request itself, except GET_DESCRIPTOR for a
* class-specific descriptor type - that is the Report descriptor below. Class
* requests (the HID_* codes) are ours too. Returning < 0 STALLs the pipe. */
static int hid_control(void *ctx, const usb_setup *s, uint8_t *buf, uint16_t len)
{
(void)ctx;
{
if (USB_U16_MSB(s->wValue) != HID_DT_REPORT)
return -1;
int n = (int)sizeof(KEYBOARD_REPORT_DESC) < len ? (int)sizeof(KEYBOARD_REPORT_DESC) : len;
memcpy(buf, KEYBOARD_REPORT_DESC, (size_t)n);
return n;
}
return -1;
switch (s->bRequest)
{
case HID_GET_REPORT:
if (len > KEY_REPORT_LEN)
len = KEY_REPORT_LEN;
memset(buf, 0, len); /* no key is held right now */
return len;
case HID_SET_REPORT:
{ /* the Num/Caps/Scroll Lock LED report, sent on endpoint 0 */
uint8_t leds = len > 0 ? buf[0] : 0;
const char *num_lock = (leds & 0x01) ? "on" : "off";
const char *caps_lock = (leds & 0x02) ? "on" : "off";
const char *scroll_lock = (leds & 0x04) ? "on" : "off";
fprintf(stderr, "[kbd] LEDs: NumLock=%s CapsLock=%s ScrollLock=%s\n",
num_lock, caps_lock, scroll_lock);
return 0;
}
case HID_SET_IDLE:
case HID_SET_PROTOCOL:
return 0;
case HID_GET_IDLE:
buf[0] = 0;
return len >= 1 ? 1 : 0;
case HID_GET_PROTOCOL:
buf[0] = 1; /* report protocol */
return len >= 1 ? 1 : 0;
}
return -1;
}
/* ---- typing ------------------------------------------------------------- */
/* minimal ASCII -> HID Usage (Keyboard/Keypad page) */
static int key_for(char ch, uint8_t *mod, uint8_t *code)
{
*mod = 0;
if (ch >= 'a' && ch <= 'z')
{
*code = (uint8_t)(0x04 + (ch - 'a'));
return 1;
}
if (ch >= 'A' && ch <= 'Z')
{
*mod = 0x02; /* Left Shift */
*code = (uint8_t)(0x04 + (ch - 'A'));
return 1;
}
if (ch >= '1' && ch <= '9')
{
*code = (uint8_t)(0x1E + (ch - '1'));
return 1;
}
if (ch == '0')
{
*code = 0x27;
return 1;
}
if (ch == ' ')
{
*code = 0x2C;
return 1;
}
if (ch == '\n')
{
*code = 0x28;
return 1;
}
return 0;
}
/* One key press is two reports on the interrupt IN endpoint: the key down, then
* the key up. usbip_device_write() hands the report to a waiting IN transfer, or
* queues it until the host asks for one. */
static void type_string(usbip_ep *hid_in, const char *text)
{
for (const char *cursor = text; *cursor; cursor++)
{
uint8_t mod;
uint8_t code;
if (!key_for(*cursor, &mod, &code))
continue;
uint8_t press[KEY_REPORT_LEN] = {mod, 0, code, 0, 0, 0, 0, 0};
uint8_t release[KEY_REPORT_LEN] = {0};
usbip_device_write(hid_in, press, sizeof(press), 0); /* key down */
usleep(20000);
usbip_device_write(hid_in, release, sizeof(release), 0); /* key up */
usleep(20000);
}
fprintf(stderr, "[kbd] typed \"%s\"\n", text);
}
static void wait_here(void)
{
#ifdef _WIN32
for (;;)
sleep(1); /* no pause() on Windows; idle */
#else
for (;;)
pause();
#endif
}
int main(int argc, char **argv)
{
int port = (argc > 1) ? atoi(argv[1]) : 3240; /* the USB/IP port to serve on */
const char *text = (argc > 2) ? argv[2] : "hello";
/* 1. the device: vendor + product id, and the strings the host displays */
usbip_device *dev = usbip_device_create(VENDOR_ID, PRODUCT_ID);
if (!dev)
{
fprintf(stderr, "usbip_device_create failed\n");
return 1;
}
usbip_device_set_strings(dev, "USB over IP", "USBIP Boot Keyboard", "0011");
/* 2. its descriptors, appended to the configuration in wire order:
* interface -> HID class descriptor -> endpoint */
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bNumEndpoints = 0, /* auto-counted as endpoints are added */
.bInterfaceClass = USB_CLASS_HID,
.bInterfaceSubClass = HID_SUBCLASS_BOOT,
.bInterfaceProtocol = HID_PROTOCOL_KEYBOARD
});
usbip_device_add_descriptor(dev, &(hid_descriptor){
.bLength = 9,
.bDescriptorType = HID_DT_HID,
.bcdHID = 0x0111, /* HID 1.11 */
.bNumDescriptors = 1,
.bReportType = HID_DT_REPORT,
.wReportLength = sizeof(KEYBOARD_REPORT_DESC)
});
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_HID_IN,
.bmAttributes = USB_INTR,
.wMaxPacketSize = KEY_REPORT_LEN,
.bInterval = 10 /* poll every 10 ms */
});
/* 3. the requests the core cannot answer for us */
usbip_device_on_control(dev, 0, hid_control, NULL);
if (!hid_in)
{
fprintf(stderr, "usbip_device_add_endpoint failed\n");
return 1;
}
/* 4. plug it in: serve USB/IP on every interface, on `port` */
usb_transport *transport = usbip_transport(NULL, port);
int rc = usbip_device_plug(dev, transport);
if (rc != USB_SUCCESS)
{
fprintf(stderr, "usbip_device_plug failed: %s\n", usb_strerror(rc));
return 1;
}
fprintf(stderr, "[kbd] serving %04x:%04x on :%d\n", VENDOR_ID, PRODUCT_ID, port);
fprintf(stderr, "[kbd] attach it: sudo usbip attach -r 127.0.0.1 -b 1-1\n");
/* 5. use it. The reports wait in the endpoint queue until a host attaches. */
type_string(hid_in, text);
wait_here();
return 0;
}
#define HID_PROTOCOL_KEYBOARD
Boot keyboard: 8-byte Input report.
Definition hid.h:72
#define HID_SUBCLASS_BOOT
Boot interface: the fixed report layout a BIOS parses.
Definition hid.h:67
#define USB_DT_ENDPOINT
Endpoint descriptor.
Definition usbip.h:356
#define USB_REQ_TYPE(bmRequestType)
Extract the usb_req_type from a bmRequestType (bits 6:5).
Definition usbip.h:313
#define USB_DT_INTERFACE
Interface descriptor.
Definition usbip.h:355
#define USB_REQ_GET_DESCRIPTOR
Fetch a descriptor; wValue is type:index (see the USB_DT_* codes)
Definition usbip.h:302
#define USB_CLASS_HID
Human Interface Device.
Definition usbip.h:365
@ USB_INTR
Interrupt - small periodic data, bounded latency.
Definition usbip.h:271
@ USB_CLASS
A class-specific request (handled by a device class)
Definition usbip.h:287
@ USB_STANDARD
A standard request defined by the USB spec.
Definition usbip.h:286
void usbip_device_set_strings(usbip_device *dev, const char *mfr, const char *product, const char *serial)
Set the manufacturer / product / serial string descriptors (indices 1-3).
usbip_ep * usbip_device_add_endpoint(usbip_device *dev, const void *ep_descriptor)
Append an endpoint descriptor and return the pipe it created.
int usbip_device_write(usbip_ep *ep, const void *buf, int len, unsigned timeout_ms)
Write device-to-host (IN) data to an endpoint, blocking up to timeout_ms.
int usbip_device_plug(usbip_device *dev, usb_transport *transport)
Plug the device onto a transport and start serving it.
usbip_device * usbip_device_create(uint16_t vid, uint16_t pid)
Create a virtual device with the given vendor/product IDs.
int usbip_device_add_descriptor(usbip_device *dev, const void *descriptor)
Append a typed descriptor (interface / endpoint / HID / class-specific) to the device's configuration...
void usbip_device_on_control(usbip_device *dev, int ifnum, usbip_device_control_fn cb, void *ctx)
Register the class/vendor control handler for one interface.
struct usbip_device usbip_device
The virtual device
Definition usbip-device.h:36
struct usbip_ep usbip_ep
An endpoint (byte pipe)
Definition usbip-device.h:37
#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.
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 endpoint descriptor (Sec.9.6.6).
Definition usbip.h:471
Standard interface descriptor (Sec.9.6.5).
Definition usbip.h:465
The 8-byte SETUP packet that begins every control transfer (host byte order in the API).
Definition usbip.h:423
uint16_t wValue
Request-specific parameter (often a type/index)
Definition usbip.h:426
uint8_t bRequest
Request code (e.g.
Definition usbip.h:425
uint8_t bmRequestType
Direction | type | recipient bitmask (see above)
Definition usbip.h:424
Device core API - descriptors, standard requests, endpoints, dispatch.
#define USB_PACKED
Give a descriptor struct the wire's layout: no padding, and little-endian storage.
Definition usbip.h:73
#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

Build and run it:

gcc boot_keyboard.c -Iinclude src/build/libusbip-device.a -pthread -o boot_keyboard
./boot_keyboard # serves 1209:0011 on TCP :3240

Nothing happens yet - that is expected. usbip_device_plug() opens a listening TCP socket on port 3240 and waits: your program is the USB/IP server (see the role table), and until a client imports the device the queued key reports simply sit in the endpoint.

The keyboard above is deliberately raw - it shows the whole core API. In practice a ready-made device class does the descriptors and the class protocol for you; the same keyboard is then three lines:

usb_speed speed = high_speed ? USB_SPEED_HIGH : USB_SPEED_FULL;
usbip_device_set_strings(dev, "USB over IP", "USBIP HID", "0011");
hid_iface *func = hid_add(dev, &hopts); /* any Report descriptor: keyboard, mouse, raw */
hid_iface * hid_add(usbip_device *dev, const hid_opts *opts)
Add a generic HID interface to a device.
usb_speed
Reported link speed (select with usbip_device_set_speed()).
Definition usbip.h:344
@ USB_SPEED_FULL
Full speed, 12 Mbit/s (the default)
Definition usbip.h:346
@ USB_SPEED_HIGH
High speed, 480 Mbit/s (512-byte bulk endpoints)
Definition usbip.h:347
void usbip_device_set_speed(usbip_device *dev, usb_speed speed)
Report a link speed (default USB_SPEED_FULL).
One generic HID interface: a single instance of the class on a device.
Definition hid.h:43

See USB Classes for the full list and each class's reference page.

On Linux the client is built into the kernel - the vhci-hcd virtual host controller:

sudo modprobe vhci-hcd # once per boot
sudo usbip attach -r 127.0.0.1 -b 1-1 # import the served device

Detach with sudo usbip detach -p 0. On Windows there is no in-box client; install a usbip-win-style one and run usbip.exe attach -r 127.0.0.1 -b 1-1. macOS has no in-box client and only an experimental third-party one that needs SIP disabled, so a hardware client is the practical route there; a Mac can also serve a device, or drive one with the Host driver. USB/IP clients has the full commands for each OS, Hardware clients the embedded importers.

Once imported, the keyboard types its text into whatever window has focus: the host binds its ordinary HID driver, because as far as it is concerned this is a real keyboard. The server side can also pull the plug itself with usbip_device_unplug().

Two environment variables turn on diagnostics for any program on the library: USBIP_PCAPNG=file.pcapng records every transfer for Wireshark (Capturing traffic), and USBIP_DEBUG=1 logs every control request and data transfer as it happens (Debugging). The full table - including the USBIP_HOST/USBIP_PORT pair the wrappers read - is on Environment variables.

  • USB concepts - the USB vocabulary the API uses
  • Device - a recipe per device class (serial, storage, camera, audio, ...)
  • Host - drive a device from your own code, with no kernel driver