3. USB 2.0高速协议回顾
USB 2.0 高速(High-Speed, 480 Mbps)协议在事务层引入了比全速/低速更复杂的机制,包括微帧(Microframe)、PING 流控以及 SPLIT 拆分事务。理解这些基础结构是利用 xHCI 调试端口捕获和分析事务的前提。
3.1 微帧(Microframe)与帧结构
USB 2.0 全速模式下,每 1 ms 生成一个帧(Frame)。高速模式下,为了支持更低的延迟和更高的带宽利用率,将 1 ms 的帧进一步划分为 8 个微帧(Microframe),每个微帧持续 125 µs。
关键指标: 高速总线每 125 µs 发送一个 SOF(Start-of-Frame)包,SOF 包中的帧号(Frame Number)每 1 ms 递增一次,但微帧号(Microframe Number)隐含在 SOF 的时序中(0-7 循环)。
3.2 高速事务类型
USB 2.0 高速总线定义了 5 种核心事务类型,每种事务由不同的令牌包(Token Packet)标识。xHCI 调试端口捕获的每个事务包都对应以下类型之一:
| 事务类型 | 令牌 PID | 方向 | 主要用途 | 高速特性 |
|---|---|---|---|---|
| IN | 0x69 (1001) | 设备 → 主机 | 主机从端点读取数据 | 支持 NYET 握手 |
| OUT | 0xE1 (0001) | 主机 → 设备 | 主机向端点发送数据 | 支持 PING 流控 |
| SETUP | 0x2D (1101) | 主机 → 设备 | 控制传输的建立阶段 | 固定 8 字节数据 |
| PING | 0xB4 (0100) | 主机 → 设备 | 查询端点是否可接收数据 | 高速专用,替代全速的 NAK 轮询 |
| SPLIT | 0x2C (1100) / 0xAC (0100) | 主机 ↔ 集线器 | 高速主机与全速/低速设备通信 | 分为开始拆分(SSPLIT)和完成拆分(CSPLIT) |
调试提示: 在 xHCI 调试端口捕获的数据中,PING 事务是高速总线独有的流量控制机制。如果捕获到大量连续的 PING 事务且设备回复 NAK,说明设备端点处于繁忙状态,需要检查设备固件或驱动调度。
3.3 数据包格式(高速层)
USB 2.0 高速数据包在物理层使用 NRZI 编码 并带有 位填充(Bit Stuffing)。每个事务由三个连续的包阶段组成(部分事务可能省略数据包或握手包)。
3.3.1 令牌包(Token Packet)格式
所有高速事务都以令牌包开始。令牌包由 PID(8 位)、地址/端点字段(11 位) 和 CRC5(5 位) 组成:
字节 0: [PID0-PID7] — 包标识符(含反码校验)
字节 1-2: [ADDR6..ADDR0][ENDP3..ENDP0] — 设备地址 + 端点号
字节 2 (低5位): [CRC4..CRC0] — 5 位循环冗余校验
3.3.2 数据包(Data Packet)格式
高速数据包在 DATA0/DATA1 基础上增加了 DATA2 和 MDATA 类型,用于支持高带宽等时端点:
| 数据 PID | 二进制值 | 用途 | 数据长度 |
|---|---|---|---|
| DATA0 | 1100 0011 | 数据切换偶数帧 | 0-1024 字节 |
| DATA1 | 0100 1011 | 数据切换奇数帧 | 0-1024 字节 |
| DATA2 | 1011 0100 | 高带宽等时(微帧内第 2 个数据包) | 0-1024 字节 |
| MDATA | 1111 0000 | 高带宽等时(微帧内第 3+ 个数据包) | 0-1024 字节 |
数据包结构:
[SYNC] [PID (8位)] [DATA PAYLOAD (0-1024字节)] [CRC16 (16位)] [EOP]
3.3.3 握手包(Handshake Packet)格式
高速握手包只有 1 字节 PID,无 CRC:
| 握手 PID | 二进制值 | 含义 | 高速特有 |
|---|---|---|---|
| ACK | 0010 1101 | 数据正确接收 | — |
| NAK | 1010 1011 | 设备忙,无法处理 | — |
| STALL | 0001 1110 | 端点挂起/错误 | — |
| NYET | 1001 0110 | 设备未准备好接收下一数据 | ✓ 高速专用 |
| ERR | 0110 0001 | 拆分事务错误 | ✓ 高速专用 |
3.4 SPLIT 拆分事务机制
当高速主机需要与全速(12 Mbps)或低速(1.5 Mbps)设备通信时,必须通过 USB 2.0 集线器 进行事务拆分。SPLIT 事务分为两个阶段:
- 开始拆分(SSPLIT):主机在高速微帧中向集线器发送令牌,集线器将其转换为全速/低速事务转发给下游设备。
- 完成拆分(CSPLIT):主机在后续微帧中向集线器查询事务结果,集线器返回设备的数据或握手。
调试陷阱: 在 xHCI 调试端口捕获的数据中,SPLIT 事务的令牌包 PID 与普通 IN/OUT 不同。SSPLIT 使用 0x2C,CSPLIT 使用 0xAC。如果调试端口只显示标准 IN/OUT 而看不到 SPLIT,说明捕获点位于集线器下游,而非主机控制器侧。
3.5 高速事务时序与带宽计算
每个微帧(125 µs)内可以调度多个事务。对于高速批量端点,每个微帧最多可传输 3 个 512 字节的数据包(受限于微帧带宽预算)。
| 传输类型 | 每微帧最大事务数 | 最大数据负载/微帧 | 典型延迟 |
|---|---|---|---|
| 控制(Control) | 1 | 64 字节 | 高(保证交付) |
| 批量(Bulk) | 3 | 1536 字节 | 低(尽力而为) |
| 等时(Isochronous) | 3 | 3072 字节 | 最低(无重试) |
| 中断(Interrupt) | 1 | 1024 字节 | 中(轮询周期) |
性能调优要点: 使用 xHCI 调试端口捕获事务时,关注每个微帧内的 总线空闲时间(Idle Time)。如果微帧内空闲时间占比超过 30%,说明调度效率不足,可以尝试增大端点请求大小或调整驱动批量提交深度。