HID Device Stack
This section describes XRUSB’s USB HID (Human Interface Device) device-class implementation and how to extend it. It covers:
- The template HID base class
LibXR::USB::HID<REPORT_DESC_LEN, TX_REPORT_LEN, RX_REPORT_LEN> - Automatic generation of the configuration-descriptor block (Interface + HID Descriptor + Endpoint Descriptors)
- Optional Interrupt OUT (Output Report over Interrupt OUT)
- Standard request
GET_DESCRIPTOR(HID / Report Descriptor) - A handling framework for HID class requests (
GET_REPORT/SET_REPORT/GET_IDLE/SET_IDLE/GET_PROTOCOL/SET_PROTOCOL) - Input Report transmission and IN/OUT completion callbacks
- Typical derived classes: mouse, keyboard, gamepad
1. Base Class Overview
1.1 LibXR::USB::HID<REPORT_DESC_LEN, TX_REPORT_LEN, RX_REPORT_LEN>
HID is a template HID base class (derived from DeviceClass). Template parameters fix report and descriptor sizes at compile time, making it convenient to implement devices such as keyboards, mice, and gamepads.
Template parameters:
REPORT_DESC_LEN: Report Descriptor length (bytes)TX_REPORT_LEN: Maximum Input Report length (Interrupt IN endpoint max packet size)RX_REPORT_LEN: Maximum Output Report length (Interrupt OUT endpoint max packet size)- Set to
0to disable the Interrupt OUT endpoint
- Set to
The base class provides:
- Endpoint allocation and configuration: Interrupt IN (required) + Interrupt OUT (optional)
- Automatic configuration-descriptor block generation
GET_DESCRIPTOR(HID / Report) responses- A HID class-request handling framework (override/extend in derived classes)
- Input Report send helper:
SendInputReport(...) - IN/OUT transfer-completion hooks:
OnDataInComplete(...)/OnDataOutComplete(...)
2. Descriptor and Endpoint Layout
2.1 Interface
The HID base class contributes one HID interface and does not use an IAD:
bInterfaceClass = 0x03(HID)bNumEndpoints = 1(IN only) or2(IN + OUT)
2.2 HID Descriptor (0x21)
The configuration descriptor includes a HID class descriptor (9 bytes). Key fields:
bcdHID = 0x0111(HID v1.11)bNumDescriptors = 1bReportDescriptorType = 0x22wReportDescriptorLength = REPORT_DESC_LEN
2.3 Endpoints
| Endpoint | Dir | Type | Max Packet Size | Purpose |
|---|---|---|---|---|
| IN endpoint | IN | INTERRUPT | TX_REPORT_LEN | Device → Host Input Report |
| OUT endpoint (opt.) | OUT | INTERRUPT | RX_REPORT_LEN | Host → Device Output Report |
Polling interval:
in_ep_interval_/out_ep_interval_are written into endpoint descriptors asbInterval
Note: bInterval semantics differ by speed (FS is typically 1 ms frames; HS uses microframe encoding). This implementation organizes parameters in “millisecond” terms; the final interpretation depends on the underlying USB controller/stack.
3. Lifecycle: Bind / Unbind
3.1 Bind (Initialization)
During initialization, the class typically:
- Records the interface number and clears runtime flags
- Allocates Interrupt IN from
EndpointPooland configures it withTX_REPORT_LEN - If
RX_REPORT_LEN > 0: allocates Interrupt OUT and configures it withRX_REPORT_LEN - Generates and submits the configuration-descriptor block (Interface + HID Descriptor + Endpoint Descriptors)
- Registers endpoint completion callbacks (IN required; OUT optional)
- If OUT is enabled: starts the first OUT receive (subsequent receives are re-armed automatically)
- Sets
inited_ = true
3.2 Unbind (Release)
During unbind, the class typically:
- Clears
inited_, closes and returns IN/OUT endpoints toEndpointPool - Sets endpoint pointers to null
4. Standard Request: GET_DESCRIPTOR (HID / Report)
The base class handles the standard request GET_DESCRIPTOR:
DescriptorType = 0x21: returns the HID DescriptorDescriptorType = 0x22: returns the Report Descriptor- Other types (e.g., Physical 0x23): not supported
Returned data is truncated to wLength to avoid exceeding the host-requested length.
Derived classes must provide the Report Descriptor (called by the base class):
virtual ConstRawData GetReportDesc() = 0;
5. HID Class Requests and Data Stage
The base class supports common HID Class-Specific Requests:
GET_REPORT- Uses the high byte of
wValueto distinguishINPUT/OUTPUT/FEATURE, then calls derived hooks to produce response data
- Uses the high byte of
SET_REPORT- Performs basic validation in the Setup stage and prepares to receive data
- When the Data stage arrives, invokes a derived hook to process the payload
GET_IDLE / SET_IDLE- Maintains
idle_rate_(units of 4 ms; many implementations only supportreport_id = 0)
- Maintains
GET_PROTOCOL / SET_PROTOCOL- Maintains
protocol_(BOOT / REPORT)
- Maintains
Recommended hooks to override as needed:
- Report retrieval:
OnGetInputReport(...)/OnGetLastOutputReport(...)/OnGetFeatureReport(...) - Report setting:
OnSetReport(...)(Setup stage) andOnSetReportData(...)(Data stage) - Custom extensions:
OnCustomClassRequest(...)/OnCustomClassData(...)
6. Data Path: Interrupt IN/OUT
6.1 Sending an Input Report: SendInputReport(...)
Typical behavior:
- Validates initialization and endpoint availability
- Validates
0 < report_len <= TX_REPORT_LEN - Validates the IN endpoint is idle; otherwise returns
BUSY - Copies the report into the IN endpoint buffer and starts the transfer
Common return-code semantics (subject to stack definitions):
OK: transfer started successfullyBUSY: endpoint is still transferringARG_ERR: invalid report lengthFAILED/NO_BUFF: endpoint/buffer unavailable
6.2 Receiving an Output Report (Interrupt OUT, optional)
If the OUT endpoint is enabled, the base class continuously receives:
- Starts one OUT receive after Bind
- After each OUT completion:
- Calls
OnDataOutComplete(in_isr, data)so the derived class can consume the data - Automatically re-arms to receive the next packet
- Calls
This path is suitable for “asynchronous Output Reports” such as keyboard LED states or gamepad vibration commands.
6.3 IN Completion Callback
After an IN transfer completes, OnDataInComplete(in_isr, data) is called. Typical uses:
- Advancing a send queue (send the next report)
- Statistics/monitoring (throughput, link state, etc.)
7. Typical Derived Classes (Overview)
7.1 HIDMouse
- Standard Boot mouse
- Input Report: commonly 4 bytes (Buttons + X + Y + Wheel)
- IN endpoint only
7.2 HIDKeyboard
- Standard Boot keyboard
- Input Report: commonly 8 bytes (Modifier + Reserved + 6 KeyCodes)
- Optional OUT endpoint (e.g., 1-byte LED)
- Can also support host LED updates via control endpoint (
SET_REPORT)
7.3 HIDGamepadT
- Template gamepad (e.g., 4 axes + 8 buttons)
- Input Report and Report Descriptor are fixed at compile time
- Typically provides convenience send APIs to update axes and button bitmap
8. Usage Examples (Conceptual)
Note: How the USB device framework registers the class list depends on the upper-layer usb_dev implementation. The examples below illustrate typical usage only.
8.1 Mouse
#include "hid_mouse.hpp"
LibXR::USB::HIDMouse hid_mouse;
// usb_dev class list: {{&hid_mouse}}
// usb_dev.Init();
// usb_dev.Start();
hid_mouse.Move(LibXR::USB::HIDMouse::LEFT, 10, 0);
hid_mouse.Release();
8.2 Keyboard (with LED callback)
#include "hid_keyboard.hpp"
// enable_out_endpoint=true enables the optional Interrupt OUT endpoint for LED reports
LibXR::USB::HIDKeyboard hid_kbd(true);
// Send: Shift + A
hid_kbd.PressKey({LibXR::USB::HIDKeyboard::KeyCode::A},
LibXR::USB::HIDKeyboard::Modifier::LEFT_SHIFT);
hid_kbd.ReleaseAll();
8.3 Gamepad
#include "hid_gamepad.hpp"
LibXR::USB::HIDGamepad gamepad;
gamepad.Send(1024, 1024, 1024, 1024, LibXR::USB::HIDGamepad::BTN1);
9. Extension Guidance (Derived-Class Implementation)
Implementing a new HID derived class typically requires:
- Provide the Report Descriptor (implement
GetReportDesc()) - Define and manage the Input Report data structure (length must not exceed
TX_REPORT_LEN) - If Output/Feature is needed:
- Control transfer: override
OnSetReport(...) / OnSetReportData(...) - Interrupt OUT endpoint: enable
RX_REPORT_LEN > 0and overrideOnDataOutComplete(...)
- Control transfer: override
If continuous sending or queueing is needed, implement “send next report” scheduling in OnDataInComplete(...).