Vendor Devices & WebUSB¶
Not every device fits a standard class. A vendor-specific interface (class
0xFF) has no OS driver at all - you define the protocol, and the other end is
your own host driver, a libusb program, or (with WebUSB) a web
page. This is the shape of most lab tools, programmers and one-off gadgets.
A vendor bulk device¶
One interface, one Bulk OUT, one Bulk IN, and a loopback for behaviour - the device the host driver walkthrough drives:
from usbip import USBDevice, Interface, In, Out
class VendorBulk(Interface):
bInterfaceClass = 0xFF # vendor-specific
bulk_out = Out(0x01, "bulk", mps=64) # host -> device
bulk_in = In(0x81, "bulk", mps=64) # device -> host
dev = USBDevice(0x1209, 0x0004, product="USBIP Vendor Bulk")
fn = dev.add(VendorBulk())
dev.plug()
while True: # loopback: OUT -> IN
data = fn.bulk_out.read(timeout=1.0)
if data:
fn.bulk_in.write(data)
#include "usbip-device.h"
int main(void) {
usbip_device *g = usbip_device_create(0x1209, 0x0004);
usbip_device_add_descriptor(g, &(usb_interface_descriptor){
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceClass = 0xFF
});
usbip_ep *tx_ep = usbip_device_add_endpoint(g, &(usb_endpoint_descriptor){
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x81,
.bmAttributes = USB_BULK,
.wMaxPacketSize = 64
});
usbip_ep *rx_ep = usbip_device_add_endpoint(g, &(usb_endpoint_descriptor){
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x01,
.bmAttributes = USB_BULK,
.wMaxPacketSize = 64
});
usbip_device_plug(g, NULL);
for (;;) {
/* loopback: OUT -> IN */
uint8_t buf[64];
int n = usbip_device_read(rx_ep, buf, sizeof(buf), 1000);
if (n > 0) usbip_device_write(tx_ep, buf, n, 0);
}
}
Vendor control requests (bmRequestType type = vendor) arrive at the
interface's on_control - see USB concepts
for decoding them.
Drive it from the host side with bulk_out/bulk_in
(libusb-shaped transfers), or from
stock libusb through the C wrapper or pyusb.
No class driver claims a vendor device once a client has imported it - your own program does:
Advertise the Microsoft OS descriptors with dev.enable_winusb() and Windows binds
WinUSB automatically - no INF, no Zadig
(Platforms → Windows).
WebUSB - open it from a browser¶
WebUSB lets an https page (or http://localhost) open a vendor device
directly. Per the spec the device advertises a BOS platform-capability
descriptor plus a vendor GET_URL request returning its landing page -
enable_webusb() does both:
Pick a vendor code that stays clear of the Microsoft OS codes (0x20/0x21). On the
page side:
const dev = await navigator.usb.requestDevice({filters: [{vendorId: 0x1209}]});
await dev.open();
await dev.selectConfiguration(1);
await dev.claimInterface(0);
await dev.transferOut(1, new TextEncoder().encode("hi"));
const r = await dev.transferIn(1, 64);
Full programs: examples/vendor_device.py, webusb_device.py; in C,
examples/device/vendor_device.c and webusb_device.c.