通用 USB HID 应用层协议规范
通用 USB HID 应用层协议规范
1. 目的与适用范围
本文档用于设计 Vendor-defined USB HID 设备的应用层通信协议,适用于配置、
状态查询、控制命令和低速遥测。
USB、HID 和自定义应用协议是三个不同层次:
自定义命令与 Payload
↓
HID Report 与 Report Descriptor
↓
USB Token / Data / Handshake 与物理链路
本文推荐的 Report 帧格式不是 USB-IF 强制格式。产品可以调整字段和长度,但一旦
发布,必须通过协议版本管理兼容性。
本文中的“必须”“不得”“应”“可以”分别对应 MUST、MUST NOT、SHOULD、MAY。
2. 标准依据
- USB 2.0 Specification
- Device Class Definition for Human Interface Devices (HID) 1.11
- HID Usage Tables
实现应以 USB-IF 发布的对应版本原文为最终依据。
3. USB 与 HID 的职责边界
3.1 USB 硬件和系统栈负责
- 设备枚举、地址分配和 Endpoint 调度;
- Token CRC5 和 Data CRC16;
- DATA0/DATA1 数据切换;
- ACK、NAK、STALL 和底层重试;
- 包同步、NRZI 编码、位填充和电气传输。
应用不得把 USB CRC5/CRC16 字节加入 HID Report。它们由 USB Controller 在发送时
自动添加,接收时自动校验,不属于应用可见的 Report 数据。
3.2 HID 层负责
- HID、Report 和物理描述符;
- Report ID、Report Size、Report Count;
- Input、Output、Feature Report 的方向和含义;
- Interrupt Endpoint 或 EP0
GET_REPORT/SET_REPORT传输。
3.3 自定义应用协议负责
- 协议版本、命令码、请求与响应方向;
- 请求编号和响应关联;
- Payload 实际长度和字段布局;
- 状态码、业务错误和参数校验;
- 超时、幂等性、重试策略和兼容规则。
USB CRC 正确只说明 USB 链路传输正确,不说明命令、参数或设备状态正确。
4. 传输方式
4.1 Report 类型
- Input Report:设备发送给 Host。
- Output Report:Host 发送给设备。
- Feature Report:双向配置或查询,可通过 EP0 控制传输。
控制命令通常采用以下任一方案:
- Host 使用 Output/Feature Report,设备使用 Input Report 响应;
- Host 和设备分别使用 Interrupt OUT/IN Endpoint。
协议必须明确采用哪一种,不得依赖操作系统的隐含默认行为。
4.2 Report ID
存在多个 Report 格式时必须使用 Report ID。Report ID 是类型标识,不是连续字节流
中的同步 Magic。
协议必须明确:
- Report ID 是否计入应用所称的“Report 总长度”;
- 每个 Report ID 对应的方向、长度和用途;
- Report ID 未知时的处理方式。
建议未知 Report ID 直接拒绝,并记录诊断信息。
4.3 Report 长度
HID 不强制所有 Report 都是 64 字节。Report 长度由 Report Descriptor 和 Endpoint
能力共同决定。
常见情况:
- Low-Speed Interrupt Endpoint 常见最大数据包为 8 字节;
- Full-Speed Interrupt Endpoint 常见最大数据包为 64 字节;
- High-Speed 可支持更大的 Interrupt 数据包;
- 大于单个 USB Packet 的 Report 可能需要多个 USB Transaction。
若 Full-Speed 控制协议选择固定 64 字节,并把 Report ID 计入总长度,则 Descriptor
中 Report ID 后的数据部分应为 63 字节。
固定长度 Report 的有效内容必须由 payloadLength 指示,未使用区域必须按规范填零。
5. 推荐的固定长度控制 Report
5.1 总体布局
下面给出一个总长度为 64 字节的推荐模板:
| 字节 | 字段 | 宽度 | 请求填写方式 | 含义 |
|---|---|---|---|---|
| 0 | reportId |
1 B | 固定值 | HID Report 类型 |
| 1 | version |
1 B | 固定值 | 应用协议主版本 |
| 2 | opcode |
1 B | 命令定义值 | 命令或响应类型 |
| 3 | flags |
1 B | 按位填写 | 请求、响应或事件 |
| 4..5 | sequence |
2 B | 0..65535 |
请求与响应关联号 |
| 6 | payloadLength |
1 B | 0..56 |
有效 Payload 字节数 |
| 7 | status |
1 B | 请求固定为 0 |
响应状态码 |
| 8..63 | payload/padding |
56 B | 按命令定义 | Payload 后补零 |
计算关系:
64 字节 = 8 字节公共头 + 最多 56 字节 Payload
paddingLength = 56 - payloadLength
5.2 示例
下面是一个 opcode=0x21、sequence=0、Payload 长度 15 字节的请求:
01 01 21 00 00 00 0F 00 | <15-byte payload> | <41-byte zero padding>
对应关系:
| Wire 字节 | Hex | 字段 | 当前值 |
|---|---|---|---|
| 0 | 01 |
Report ID | 1 |
| 1 | 01 |
Version | 1 |
| 2 | 21 |
Opcode | 0x21 |
| 3 | 00 |
Flags | Request |
| 4..5 | 00 00 |
Sequence | 0,小端 |
| 6 | 0F |
Payload Length | 15 |
| 7 | 00 |
Status | 请求保留为 0 |
| 8..22 | — | Payload | 15 字节 |
| 23..63 | 00 ... 00 |
Padding | 41 字节 |
6. 字段规则
6.1 字节序
协议必须声明所有多字节整数的字节序。推荐统一使用小端序。
例如十进制 10000:
10000 = 0x00002710
小端 Wire = 10 27 00 00
同一协议不得对同一种整数类型混用字节序,除非字段定义明确要求。
6.2 Version
- 主版本不兼容时必须返回
UNSUPPORTED_VERSION; - 新增可选尾字段时,旧接收端应根据长度忽略完整的未知尾部;
- 不得把缺失的必需字段当作默认值静默接受;
- 版本协商规则必须写入命令规范。
6.3 Opcode
每个 Opcode 必须唯一标识一种消息语义。协议必须明确响应采用:
- 与请求相同 Opcode,通过 Flags 区分;或
responseOpcode = requestOpcode | 0x80。
两种方案只能选择一种并保持稳定。未知 Opcode 必须返回明确错误,不得静默成功。
6.4 Flags
一个推荐定义如下:
| Bit | 名称 | 含义 |
|---|---|---|
| 0 | response |
0 请求,1 响应 |
| 1 | event |
1 表示设备异步事件 |
| 2 | ackRequired |
请求方要求应用层响应 |
| 3..7 | Reserved | 必须发送 0,接收端按版本规则处理 |
如果采用枚举式 Flags,也必须明确禁止的组合。
6.5 Sequence
- Host 为需要响应的请求分配 Sequence;
- 响应必须回显对应请求的 Sequence;
- 同一设备、同一会话内未完成请求不得重复使用相同 Sequence;
- 回绕后不得与仍在等待的请求冲突;
- 异步事件可使用保留值或独立编号规则。
sequence=0 可以作为离线示例,但生产系统应由会话管理器分配。
6.6 Payload Length
- 必须等于本 Report 中有效 Payload 的实际长度;
- 不得大于 Report 的 Payload 容量;
- 固定长度命令必须验证精确长度;
- 变长命令必须验证最小值、最大值及完整尾字段;
- 长度非法时不得继续读取越界字段。
6.7 Status
请求中的 Status 必须为 0。响应建议使用稳定状态码:
| 值 | 名称 | 含义 |
|---|---|---|
0x00 |
OK |
成功 |
0x01 |
INVALID_COMMAND |
未知或不可用命令 |
0x02 |
INVALID_ARGUMENT |
参数或长度非法 |
0x03 |
BUSY |
设备暂时无法执行 |
0x04 |
TRANSPORT_ERROR |
下游通信失败 |
0x05 |
UNSUPPORTED_VERSION |
协议版本不支持 |
0x06 |
INVALID_STATE |
当前设备状态不允许执行 |
0x07 |
PERMISSION_DENIED |
权限或安全策略拒绝 |
未知状态码必须保留原始数值并显示为 UNKNOWN。
6.8 Padding
- 发送端必须把未使用区域填
0; - 接收端应检查非零 Padding 并记录协议异常;
- Payload 解码只能读取
payloadLength指定的范围; - Padding 不属于 Payload,不得参与业务字段解析。
7. 命令定义要求
每条命令必须定义:
- 命令名称和 Opcode;
- 请求与响应方向;
- 请求和响应 Payload 长度;
- 每个字节、每个有业务含义的 Bit;
- 数据类型、字节序、单位和比例;
- 枚举值和未知值处理;
- 参数范围、推荐值、后果和可照抄示例;
- 成功条件和可能的状态码;
- 超时、重试、幂等性和恢复方式;
- 权限、安全影响和设备状态前置条件。
推荐使用以下字段表:
| Payload 字节 | 字段 | 类型 | 字节序 | 单位 | 推荐值 | 取值后果 | 示例 |
|---|---|---|---|---|---|---|---|
| 0 | mode |
enum/u8 | — | 无 | 1 |
选择运行模式 | 01 |
| 1..4 | timeoutMs |
u32 | little | ms | 10000 |
到期触发安全动作 | 10 27 00 00 |
8. 请求、响应与事件
8.1 请求
Host 必须:
- 填写正确 Version、Opcode、Flags、Sequence 和长度;
- 在发送前验证参数;
- 对敏感命令执行权限检查;
- 记录超时后结果是否确定。
8.2 响应
设备必须:
- 回显 Sequence;
- 返回匹配的响应 Opcode;
- 在 Status 非 OK 时返回规定的错误详情;
- 不得把业务失败伪装成成功;
- 对非幂等命令避免重复执行。
8.3 异步事件
事件必须具有独立 Flags 或 Opcode 范围。Host 不得把事件错误关联到普通请求。
高频遥测不适合使用控制 Report;应评估独立 Endpoint、批量 Report 或其他传输。
9. 超时与重试
USB 底层重试不等于应用层命令重试。
- USB CRC 错误由 Controller 处理,通常不会直接暴露给业务代码;
NAK表示暂未就绪,Host Controller 稍后重新调度;STALL需要软件执行 Endpoint 恢复;- 应用层超时表示在期限内没有得到可关联的有效响应。
应用命令必须分类:
- 只读、幂等:可以在策略允许时重试;
- 幂等写:相同参数重复执行结果一致,可以谨慎重试;
- 非幂等写:超时后必须先查询设备状态,不得盲目重放;
- 紧急命令:必须定义独立优先级和失败兜底。
10. 大 Payload 与分片
当 Payload 超过单 Report 容量时,不得直接截断。可以选择:
- 使用更大的 Report;
- 定义应用层分片;
- 改用 Bulk、CDC、WinUSB 或其他传输。
若使用分片,每片至少应包含:
- 消息 ID;
- 分片序号和总分片数;
- 总长度;
- 当前分片有效长度;
- 超时和丢片恢复规则。
分片重组必须设置总大小、分片数量和超时上限,防止资源耗尽。
11. CRC 与端到端完整性
普通 HID 控制 Report 通常不需要重复添加 CRC,因为 USB 已保护当前链路。
以下场景可以增加应用层 CRC、Hash、MAC 或签名:
- 数据离开 USB 后还会写入文件、Flash、网络或消息队列;
- 一条业务消息跨多个 Report;
- 需要检测软件内存、缓存或存储损坏;
- 需要防篡改或验证发送者身份。
CRC 只能检测随机错误,不能提供身份认证。涉及固件升级、解锁或安全配置时,应使用
签名或消息认证码,而不是只使用 CRC。
12. Report Descriptor 要求
Report Descriptor 必须与实现完全一致,并至少明确:
- Usage Page 使用 Vendor-defined 范围;
- 每个 Report ID;
- Input、Output、Feature 的方向;
- Report Size 和 Report Count;
- Logical Minimum/Maximum;
- 总 Report 长度。
固件、Host 和测试必须从同一权威定义生成或进行一致性校验。Descriptor 声明长度与
代码缓冲区长度不一致属于发布阻断问题。
13. Host 实现要求
Host 应:
- 按 VID、PID、Interface、Usage Page/Usage 识别设备;
- 不只依赖易变化的设备路径;
- 验证 Report ID、Version、Opcode、Flags、长度、Status 和 Padding;
- 使用 Sequence 管理并发请求;
- 对未知枚举和新尾字段保留原始值;
- 设置输入大小、并发数和超时上限;
- 设备拔出后结束所有 Pending 请求;
- 不把收到 USB ACK 当作业务命令成功。
14. Device 实现要求
Device 应:
- 在读取任何字段前先验证 Report ID 和长度;
- 使用有界缓冲区,不做越界访问;
- 请求 Padding 非零时按规范拒绝或记录;
- 未知 Version/Opcode 返回明确状态;
- 请求 Status 非零时拒绝;
- 响应填零所有未使用字节;
- 长操作采用状态机,不在 USB 中断中阻塞;
- 对紧急关断、安全状态和看门狗定义失效保护。
15. 安全要求
USB HID 本身不提供应用身份认证或授权。
敏感功能必须额外考虑:
- Host 侧用户授权;
- 命令能力白名单;
- 防重放 Nonce 或会话编号;
- 签名、MAC 或安全会话;
- 固件镜像签名验证;
- 审计日志;
- 设备断连、Host 崩溃时的安全默认状态。
不得把 VID/PID、Report ID、隐藏 Opcode 或 CRC 当作安全机制。
16. 兼容性规则
- 已发布字段的偏移、宽度、字节序和含义不得静默改变;
- 可以在明确长度规则下追加可选尾字段;
- Reserved 位发送端必须为零;
- 接收端是否忽略未知 Reserved 位必须按版本规则确定;
- 未知枚举应保留原始值,不应导致解析器崩溃;
- 不兼容修改必须提升主版本或分配新 Report ID。
17. 测试与验收
17.1 Descriptor
17.2 编解码
17.3 错误与恢复
17.4 跨平台
18. 发布物
一个可交付的 HID 应用协议至少应包含:
- HID/Report Descriptor;
- Report 总长度和 Report ID 表;
- 公共帧头定义;
- 所有命令的逐字节/逐 Bit 定义;
- 中英文参数填写指南;
- 状态码和错误恢复规则;
- Golden Vector;
- Host 与 Device 编解码测试;
- 版本兼容和变更记录。
19. 术语
- Report:HID 层的一次逻辑数据结构。
- USB Packet:Token、Data 或 Handshake 包。
- USB Transaction:Token、可选 Data、Handshake 的组合。
- Endpoint:USB 设备中的单向逻辑通信通道。
- Payload:应用命令实际数据,不含公共 Report 帧头和 Padding。
- Padding:固定长度 Report 中未使用的补零区域。
- Golden Vector:已知输入、完整 Wire 字节和期望解析结果的固定测试向量。

浙公网安备 33010602011771号