usb
stableUSB protocol utilities for parsing descriptors, encoding control packets, looking up class and vendor names, and computing endpoint addresses and request type bytes.
use plugin usb::{parse_device_descriptor, format_vid_pid, class_name, …} Functions (12)
- parse_device_descriptor Parse 18-byte USB device descriptor
- format_vid_pid Format vendor and product IDs as hex
- class_name Resolve USB class code to a name
- encode_control_setup Encode an 8-byte control setup packet
- known_vendors Look up a vendor name by vendor ID
- parse_config_descriptor Parse 9-byte USB config descriptor
- parse_endpoint_descriptor Parse 7-byte USB endpoint descriptor
- parse_interface_descriptor Parse 9-byte USB interface descriptor
- speed_name Get USB speed name from speed code
- endpoint_address Compute endpoint address byte
- transfer_type_name Get transfer type name from bmAttributes
- request_type Compute bmRequestType byte
Overview
usb is a dependency-free toolkit for working with the USB protocol at the byte
level: it decodes the standard descriptors a device reports during enumeration,
encodes the 8-byte control setup packets the host sends, and resolves the many
numeric codes in the spec (class codes, vendor IDs, speed codes, transfer types)
into readable names. Nothing here talks to hardware — every function is a pure
transformation between bytes, integers, and tables, so it pairs naturally with
whatever transport library actually moves the data on the wire.
The mental model is two-sided. On the parsing side you feed a raw byte blob to a
parse_*_descriptor function and get back a plain table keyed by the spec field
names (idVendor, bNumEndpoints, and so on). On the building side you compose
small helpers like request_type and endpoint_address into the integer fields
that encode_control_setup packs into a setup packet. The lookup helpers
(class_name, known_vendors, speed_name, transfer_type_name) turn the
numbers from either side into human-readable strings.
Common patterns
Decode a device descriptor and render it as a friendly identity line:
use plugin usb::{parse_device_descriptor, format_vid_pid, known_vendors, class_name}
let desc = parse_device_descriptor(raw_bytes)
let id = format_vid_pid(desc["idVendor"], desc["idProduct"])
print("{id} — {known_vendors(desc["idVendor"])}")
print("class: {class_name(desc["bDeviceClass"])}")
Build a standard SET_CONFIGURATION control request from the ground up:
use plugin usb::{request_type, encode_control_setup}
let rt = request_type("host_to_device", "standard", "device")
let setup = encode_control_setup(rt, 9, 1, 0, 0)
print("setup packet ready")
Walk a config descriptor down to its endpoints, naming each one:
use plugin usb::{parse_config_descriptor, parse_endpoint_descriptor, transfer_type_name}
let cfg = parse_config_descriptor(config_bytes)
print("interfaces: {cfg["bNumInterfaces"]}, max power: {cfg["maxPower_mA"]} mA")
let ep = parse_endpoint_descriptor(endpoint_bytes)
print("EP {ep["endpoint_number"]} {ep["direction"]}: {transfer_type_name(ep["bmAttributes"])}")
Parse 18-byte USB device descriptor
Parses a standard 18-byte USB device descriptor binary blob into a structured table. Fields include bcdUSB, bDeviceClass, idVendor, idProduct, bcdDevice, bNumConfigurations, and more.
use plugin usb::{parse_device_descriptor, format_vid_pid}
let desc = parse_device_descriptor(raw_bytes)
let vid_pid = format_vid_pid(desc["idVendor"], desc["idProduct"])
print("Device: {vid_pid}")
The descriptor also reports the USB version and how many configurations the device exposes, useful for a quick capability summary:
use plugin usb::{parse_device_descriptor}
let desc = parse_device_descriptor(raw_bytes)
print("USB version field: {desc["bcdUSB"]}")
print("configurations: {desc["bNumConfigurations"]}")
Format vendor and product IDs as hex
Formats a vendor ID and product ID pair as a "VVVV:PPPP" uppercase hex string.
use plugin usb::{format_vid_pid}
print(format_vid_pid(0x2341, 0x0043))
It works directly off the fields of a parsed descriptor too:
use plugin usb::{parse_device_descriptor, format_vid_pid}
let desc = parse_device_descriptor(raw_bytes)
print(format_vid_pid(desc["idVendor"], desc["idProduct"]))
Resolve USB class code to a name
Returns the human-readable USB device class name for a numeric class code. Known classes include HID (0x03), Mass Storage (0x08), Hub (0x09), Vendor Specific (0xFF), and many more.
use plugin usb::{class_name}
print(class_name(0x03))
print(class_name(0x08))
Pair it with a parsed descriptor to label a device's reported class:
use plugin usb::{parse_device_descriptor, class_name}
let desc = parse_device_descriptor(raw_bytes)
print("device class: {class_name(desc["bDeviceClass"])}")
Encode an 8-byte control setup packet
Encodes the 8-byte USB control setup packet from its five fields in little-endian byte order, returning a raw byte buffer. Use with a USB transfer library to perform control requests.
use plugin usb::{encode_control_setup, request_type}
let rt = request_type("host_to_device", "standard", "device")
let pkt = encode_control_setup(rt, 9, 0x0300, 0, 8)
A GET_DESCRIPTOR request reads data back from the device, so its request type
flows from device to host and it declares a non-zero wLength:
use plugin usb::{encode_control_setup, request_type}
let rt = request_type("device_to_host", "standard", "device")
let pkt = encode_control_setup(rt, 6, 0x0100, 0, 18)
print("GET_DESCRIPTOR setup encoded")
Look up a vendor name by vendor ID
Returns a vendor name string for a known USB vendor ID. Returns "Unknown Vendor" if the ID is not in the built-in table. Covers vendors like Apple, Arduino, Raspberry Pi, FTDI, Logitech, and many others.
use plugin usb::{known_vendors}
print(known_vendors(0x2341))
print(known_vendors(0x239A))
Parse 9-byte USB config descriptor
Parses the first 9 bytes of a USB configuration descriptor. Returns fields including wTotalLength, bNumInterfaces, bConfigurationValue, bmAttributes, bMaxPower, and maxPower_mA (power in milliamps).
use plugin usb::{parse_config_descriptor}
let cfg = parse_config_descriptor(raw_bytes)
print("Interfaces: {cfg["bNumInterfaces"]}")
print("Max power: {cfg["maxPower_mA"]} mA")
Parse 7-byte USB endpoint descriptor
Parses a 7-byte USB endpoint descriptor. Returns endpoint_number, direction ("IN" or "OUT"), transfer_type, wMaxPacketSize, and bInterval alongside the raw fields.
use plugin usb::{parse_endpoint_descriptor}
let ep = parse_endpoint_descriptor(raw_bytes)
print("EP {ep["endpoint_number"]} {ep["direction"]}: {ep["transfer_type"]}")
Parse 9-byte USB interface descriptor
Parses a 9-byte USB interface descriptor. Returns fields including bInterfaceNumber, bAlternateSetting, bNumEndpoints, bInterfaceClass, bInterfaceSubClass, and bInterfaceProtocol.
use plugin usb::{parse_interface_descriptor, class_name}
let iface = parse_interface_descriptor(raw_bytes)
print(class_name(iface["bInterfaceClass"]))
Get USB speed name from speed code
Returns a human-readable speed name for a USB speed code integer. Codes: 1=Low Speed, 2=Full Speed, 3=High Speed, 4=Super Speed, 5=Super Speed Plus.
use plugin usb::{speed_name}
print(speed_name(3))
print(speed_name(4))
Compute endpoint address byte
Computes the bEndpointAddress byte from an endpoint number (0-15) and direction string ("IN" or "OUT"). IN endpoints have bit 7 set.
use plugin usb::{endpoint_address}
let addr = endpoint_address(1, "IN")
print(addr)
Feed the result straight into format_vid_pid-style hex inspection, or compare
the two directions of the same endpoint number:
use plugin usb::{endpoint_address}
print("OUT addr: {endpoint_address(2, "OUT")}")
print("IN addr: {endpoint_address(2, "IN")}")
Get transfer type name from bmAttributes
Extracts the transfer type name from a bmAttributes byte. Returns "Control", "Isochronous", "Bulk", or "Interrupt" based on bits 1:0.
use plugin usb::{transfer_type_name}
print(transfer_type_name(0x02))
print(transfer_type_name(0x03))
Compute bmRequestType byte
Computes the bmRequestType byte from three string parameters. direction is "host_to_device" or "device_to_host". type is "standard", "class", or "vendor". recipient is "device", "interface", "endpoint", or "other".
use plugin usb::{request_type}
let rt = request_type("device_to_host", "standard", "device")
print(rt)
A class-specific request aimed at an interface (such as a HID report request) combines different bits:
use plugin usb::{request_type, encode_control_setup}
let rt = request_type("host_to_device", "class", "interface")
let pkt = encode_control_setup(rt, 0x09, 0x0200, 0, 8)
print("HID SET_REPORT request built")