root@cnblogs: ~/personal-blog

root@blog:~$ ./start-blog.sh

ACCESS GRANTED

[OK] Knowledge database connected

root@blog:~$ █

通用 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 控制传输。

控制命令通常采用以下任一方案:

  1. Host 使用 Output/Feature Report,设备使用 Input Report 响应;
  2. 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. 命令定义要求

每条命令必须定义:

  1. 命令名称和 Opcode;
  2. 请求与响应方向;
  3. 请求和响应 Payload 长度;
  4. 每个字节、每个有业务含义的 Bit;
  5. 数据类型、字节序、单位和比例;
  6. 枚举值和未知值处理;
  7. 参数范围、推荐值、后果和可照抄示例;
  8. 成功条件和可能的状态码;
  9. 超时、重试、幂等性和恢复方式;
  10. 权限、安全影响和设备状态前置条件。

推荐使用以下字段表:

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 容量时,不得直接截断。可以选择:

  1. 使用更大的 Report;
  2. 定义应用层分片;
  3. 改用 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 字节和期望解析结果的固定测试向量。
posted @ 2026-08-28 15:07  bk街头狂舞  阅读(24)  评论(0)    收藏  举报