Get the library, build against it, run a device, import it with a USB/IP client.
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.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define VENDOR_ID 0x1209
#define PRODUCT_ID 0x0011
#define EP_HID_IN 0x81
#define HID_DT_HID 0x21
#define HID_DT_REPORT 0x22
#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
#define HID_PROTOCOL_KEYBOARD 0x01
#define KEY_REPORT_LEN 8
static const uint8_t KEYBOARD_REPORT_DESC[] = {
0x05, 0x01,
0x09, 0x06,
0xA1, 0x01,
0x05, 0x07,
0x19, 0xE0,
0x29, 0xE7,
0x15, 0x00,
0x25, 0x01,
0x75, 0x01,
0x95, 0x08,
0x81, 0x02,
0x95, 0x01,
0x75, 0x08,
0x81, 0x03,
0x95, 0x05,
0x75, 0x01,
0x05, 0x08,
0x19, 0x01,
0x29, 0x05,
0x91, 0x02,
0x95, 0x01,
0x75, 0x03,
0x91, 0x03,
0x95, 0x06,
0x75, 0x08,
0x15, 0x00,
0x25, 0x65,
0x05, 0x07,
0x19, 0x00,
0x29, 0x65,
0x81, 0x00,
0xC0,
};
{
uint8_t bLength, bDescriptorType;
uint16_t bcdHID;
uint8_t bCountryCode, bNumDescriptors, bReportType;
uint16_t wReportLength;
} hid_descriptor;
static int hid_control(
void *ctx,
const usb_setup *s, uint8_t *buf, uint16_t len)
{
(void)ctx;
{
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;
{
case HID_GET_REPORT:
if (len > KEY_REPORT_LEN)
len = KEY_REPORT_LEN;
memset(buf, 0, len);
return len;
case HID_SET_REPORT:
{
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;
return len >= 1 ? 1 : 0;
}
return -1;
}
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;
*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;
}
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};
usleep(20000);
usleep(20000);
}
fprintf(stderr, "[kbd] typed \"%s\"\n", text);
}
static void wait_here(void)
{
#ifdef _WIN32
for (;;)
sleep(1);
#else
for (;;)
pause();
#endif
}
int main(int argc, char **argv)
{
int port = (argc > 1) ? atoi(argv[1]) : 3240;
const char *text = (argc > 2) ? argv[2] : "hello";
if (!dev)
{
fprintf(stderr, "usbip_device_create failed\n");
return 1;
}
.bInterfaceNumber = 0,
.bNumEndpoints = 0,
});
.bLength = 9,
.bDescriptorType = HID_DT_HID,
.bcdHID = 0x0111,
.bNumDescriptors = 1,
.bReportType = HID_DT_REPORT,
.wReportLength = sizeof(KEYBOARD_REPORT_DESC)
});
.bEndpointAddress = EP_HID_IN,
.wMaxPacketSize = KEY_REPORT_LEN,
.bInterval = 10
});
if (!hid_in)
{
fprintf(stderr, "usbip_device_add_endpoint failed\n");
return 1;
}
{
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");
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
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:
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().