HID 设备协议栈
本节介绍 XRUSB 的 USB HID(Human Interface Device) 设备类实现与扩展方式,重点覆盖:
- HID 设备类的模板化基类(
LibXR::USB::HID)与自动描述符生成 - 可选 OUT 中断端点(Output Report over Interrupt OUT)
- 标准请求
GET_DESCRIPTOR(HID / Report 描述符)的处理路径 - HID 类请求(
GET_REPORT/SET_REPORT/GET_IDLE/SET_IDLE/GET_PROTOCOL/SET_PROTOCOL)与数据阶段回调 - 输入报告发送(Input Report over Interrupt IN)与传输完成回调
- 典型派生类:鼠标(
HIDMouse)、键盘(HIDKeyboard)、手柄(HIDGamepadT)
组件概览
LibXR::USB::HID<REPORT_DESC_LEN, TX_REPORT_LEN, RX_REPORT_LEN>
HID 是一个模板化 HID 基类(继承自 DeviceClass),通过模板参数在编译期固化报告与描述符尺寸,便于实现键盘、鼠标、手柄等设备:
REPORT_DESC_LEN:Report Descriptor 长度(字节)TX_REPORT_LEN:Input Report 最大长度(Interrupt IN 端点wMaxPacketSize)RX_REPORT_LEN:Output Report 长度(Interrupt OUT 端点wMaxPacketSize,默认 0 表示不使用)
它实现并封装了:
- 端点资源申请与配置(Interrupt IN;可选 Interrupt OUT)
- Interface + HID Descriptor(0x21) + Endpoint Descriptor 的配置描述符块自动生成
- 标准请求
GET_DESCRIPTOR:返回 HID 描述符(0x21)或 Report 描述符(0x22) - HID 类请求与数据阶段处理框架(可在派生类中覆盖)
- 输入报告发送辅助函数
SendInputReport()(拷贝到端点缓冲区并启动传输) - IN/OUT 传输完成回调钩子(
OnDataInComplete/OnDataOutComplete)
描述符与端点布局
Interface(接口)
HID 作为单接口设备类:
GetInterfaceNum()固定返回1HasIAD()固定返回falsebInterfaceClass = 0x03(HID)bNumEndpoints = 1(仅 IN)或2(IN + OUT)
HID Descriptor(0x21)
基类在配置描述符中生成 HID 类描述符(9 字节),关键字段如下:
bcdHID = 0x0111(HID v1.11)bNumDescriptors = 1bReportDescriptorType = 0x22wReportDescriptorLength = REPORT_DESC_LEN
Endpoints(端点)
基类在 Init() 中从 EndpointPool 申请并配置端点:
| 端点 | 方向 | 类型 | wMaxPacketSize | 用途 |
|---|---|---|---|---|
| IN 端点 | IN | INTERRUPT | TX_REPORT_LEN | 设备 → 主机 Input Report |
| OUT 端点(可选) | OUT | INTERRUPT | RX_REPORT_LEN | 主机 → 设备 Output Report |
端点轮询间隔:
in_ep_interval_:写入 IN 端点描述符的bIntervalout_ep_interval_:写入 OUT 端点描述符的bInterval
注意:不同速度下
bInterval的语义不同(FS 通常按帧 1ms,HS 以 microframe 编码)。本实现以“毫秒”语义组织参数,最终解释取决于底层 USB 控制器/栈对bInterval的处理方式。
初始化与资源释放
Init 行为(HID::Init(endpoint_pool, start_itf_num))
初始化的关键步骤:
- 记录接口号
itf_num_ = start_itf_num,清理端点指针与inited_标志 - 从
EndpointPool获取 Interrupt IN 端点,并Configure({IN, INTERRUPT, TX_REPORT_LEN}) - 若启用 OUT:获取 Interrupt OUT 端点,并
Configure({OUT, INTERRUPT, RX_REPORT_LEN}) - 填充配置描述符块:
- Interface Descriptor
- HID Descriptor (0x21)
- Endpoint IN Descriptor -(可选)Endpoint OUT Descriptor
- 通过
SetData(RawData{...})将描述符块交给设备框架拼入 Configuration Descriptor - 注册端点完成回调:
ep_in_->SetOnTransferCompleteCallback(on_data_in_complete_cb_)-(可选)ep_out_->SetOnTransferCompleteCallback(on_data_out_complete_cb_)
- 若启用 OUT:启动首次 OUT 接收
ep_out_->Transfer(RX_REPORT_LEN)(随后每次完成会自动 re-arm) - 设置
inited_ = true
Deinit 行为(HID::Deinit(endpoint_pool))
inited_ = false- 关闭并归还 IN/OUT 端点给
EndpointPool - 端点指针置空
标准请求:GET_DESCRIPTOR(HID / Report)
HID 覆盖 OnGetDescriptor(),用于处理标准请求 GET_DESCRIPTOR:
- 当
wValue >> 8为0x21:返回 HID Descriptor(GetHIDDesc()) - 当
wValue >> 8为0x22:返回 Report Descriptor(GetReportDesc()) - 其他类型(如 Physical 0x23):返回
ErrorCode::NOT_SUPPORT
返回数据会按 wLength 截断,避免超出主机请求长度。
派生类必须实现:
virtual ConstRawData GetReportDesc() = 0;
用于提供 Report Descriptor 的数据指针与长度。
HID 类请求与数据阶段
基类在 OnClassRequest() 中处理 HID 常见类请求(Class-Specific Requests):
GET_REPORT:按wValue高字节区分INPUT/OUTPUT/FEATURE,分别调用:OnGetInputReport(report_id, result)OnGetLastOutputReport(report_id, result)OnGetFeatureReport(report_id, result)
SET_REPORT:仅校验wLength != 0,并交由:OnSetReport(report_id, result)(此处通常设置result.read_data让控制传输数据阶段写入)- 数据阶段由
OnClassData()接收,最终回调OnSetReportData(in_isr, data)
GET_IDLE / SET_IDLE:维护一个idle_rate_(单位 4ms),当前实现仅支持report_id=0GET_PROTOCOL / SET_PROTOCOL:维护protocol_(BOOT / REPORT)
你可以覆盖以下虚函数来实现具体逻辑:
- 报告获取:
OnGetInputReport()/OnGetLastOutputReport()/OnGetFeatureReport()
- 报告设置:
OnSetReport():在控制传输 Setup 阶段准备接收缓冲等OnSetReportData():在控制传输 Data 阶段处理主机发来的数据
- 自定义扩展:
OnCustomClassRequest()/OnCustomClassData()