七、USB 协议中的事务(Transaction)详解
USB 协议学习目录
点击标题可跳转到对应的网络文章
- 一、USB 协议结构详解
- 二、USB 协议中的设备类
- 三、USB 协议通信过程
- 四、USB 协议中的描述符
- 五、USB 协议中的请求
- 六、USB 协议中的接口与端点
- 七、USB 协议中的事务
- 八、USB 协议分析与调试实战
目录
- 一、概述
- [1.1 事务在 USB 分层中的位置](#1.1 事务在 USB 分层中的位置)
- [1.2 事务 vs 传输 vs 包](#1.2 事务 vs 传输 vs 包)
- [二、USB 包的类型与结构](#二、USB 包的类型与结构)
- [2.1 令牌包(Token Packet)](#2.1 令牌包(Token Packet))
- [2.2 数据包(Data Packet)](#2.2 数据包(Data Packet))
- [2.3 握手包(Handshake Packet)](#2.3 握手包(Handshake Packet))
- [2.4 特殊包(Special Packet)](#2.4 特殊包(Special Packet))
- [三、PID(Packet Identifier)详解](#三、PID(Packet Identifier)详解)
- [3.1 PID 编码规则](#3.1 PID 编码规则)
- [3.2 PID 类型速查表](#3.2 PID 类型速查表)
- 四、三种基本事务
- [4.1 SETUP 事务](#4.1 SETUP 事务)
- [4.2 IN 事务](#4.2 IN 事务)
- [4.3 OUT 事务](#4.3 OUT 事务)
- 五、事务的详细流程与错误处理
- [5.1 正常事务流程](#5.1 正常事务流程)
- [5.2 NAK 响应处理](#5.2 NAK 响应处理)
- [5.3 STALL 响应处理](#5.3 STALL 响应处理)
- [5.4 超时与重传](#5.4 超时与重传)
- [六、数据切换机制(Data Toggle)](#六、数据切换机制(Data Toggle))
- [6.1 Data Toggle 原理](#6.1 Data Toggle 原理)
- [6.2 各种情况下的 Data Toggle 行为](#6.2 各种情况下的 Data Toggle 行为)
- 七、事务与传输类型的关系
- [7.1 控制传输中的事务](#7.1 控制传输中的事务)
- [7.2 批量传输中的事务](#7.2 批量传输中的事务)
- [7.3 中断传输中的事务](#7.3 中断传输中的事务)
- [7.4 同步传输中的事务](#7.4 同步传输中的事务)
- 八、帧与微帧中的事务调度
- [九、USB 3.x 事务差异](#九、USB 3.x 事务差异)
- 十、事务抓包实例分析
- 十一、总结
一、概述
1.1 事务在 USB 分层中的位置
USB 协议采用分层架构,事务层(Transaction Layer) 位于数据包层之上、传输层之下,是连接两者的关键环节。
应用层
HID/Mass Storage/Video
传输层
Transfer Layer
控制/批量/中断/同步
事务层
Transaction Layer
SETUP/IN/OUT
数据包层
Packet Layer
Token/Data/Handshake
物理层
Physical Layer
差分信号/电气特性
事务层的职责:
- 将传输 分解为多个事务
- 管理数据包的顺序(Data Toggle)
- 处理握手响应(ACK/NAK/STALL)
- 控制总线访问时机
1.2 事务 vs 传输 vs 包
包(Packet)
事务(Transaction)
传输(Transfer)
分解为 N 个
由 2~3 个
一次完整的 Host-Device 数据交换
如: 读取 1024 字节文件
令牌包
数据包
握手包
总线上一次独立的信号传输
以 SYNC 开始,EOP 结束
三者的关系:
| 层级 | 单位 | 组成 | 类比 |
|---|---|---|---|
| 传输(Transfer) | 一次完整的数据交换 | 1~N 个事务 | 一次文件读取 |
| 事务(Transaction) | 一次独立的通信操作 | 2~3 个包 | 一次请求-响应 |
| 包(Packet) | 总线上的一次信号突发 | SYNC + PID + 数据 + EOP | 一个数据帧 |
二、USB 包的类型与结构
所有 USB 包都以 SYNC 字段 开始,以 EOP(End of Packet) 结束。
SYNC (8 bit) 0 7 PID (8 bit) 8 15 数据载荷 (0~1023 byte) 16 23 CRC (5/16 bit) 24 31 EOP 32 39 USB 包通用结构
2.1 令牌包(Token Packet)
令牌包由 Host 发送,用于启动一次事务,声明事务类型和目标端点。
SYNC 0 7 PID (Token) 8 15 ADDR (7 bit) 16 26 ENDP (4 bit) 27 30 CRC5 (5 bit) 31 CRC5 (5 bit) 32 35 EOP 36 43 令牌包结构
令牌包类型:
| PID | 名称 | 功能 |
|---|---|---|
| OUT | 输出令牌 | Host 向 Device 的 OUT 端点发送数据 |
| IN | 输入令牌 | Host 从 Device 的 IN 端点读取数据 |
| SETUP | 设置令牌 | Host 向 Device 发送 Setup 数据(控制传输) |
| SOF | 帧起始令牌 | Host 发送帧/微帧开始标记 |
2.2 数据包(Data Packet)
数据包携带实际的数据载荷,由 Host 或 Device 发送。
SYNC 0 7 PID (Data) 8 15 数据载荷 (0~1024 byte) 16 23 CRC16 (16 bit) 24 31 CRC16 (16 bit) 32 39 EOP 40 47 数据包结构
数据包类型:
| PID | 名称 | 用途 |
|---|---|---|
| DATA0 | 数据 0 | 数据包(首次或偶数次) |
| DATA1 | 数据 1 | 数据包(奇数次) |
| DATA2 | 数据 2 | USB 2.0 高速分裂传输 |
| MDATA | 数据 M | 高速分裂传输 |
2.3 握手包(Handshake Packet)
握手包用于确认或拒绝数据传输,无数据载荷。
SYNC 0 7 PID (Handshake) 8 15 EOP 16 23 握手包结构
握手包类型:
| PID | 名称 | 发送者 | 含义 |
|---|---|---|---|
| ACK | 确认 | Host/Device | 数据正确接收,准备下一包 |
| NAK | 未就绪 | Device | 暂时无法处理,请重试 |
| STALL | 停止 | Device | 端点挂起,需要 Host 干预 |
| NYET | 未就绪 | Device | 高速传输中,尚未准备好 |
2.4 特殊包(Special Packet)
| PID | 名称 | 功能 |
|---|---|---|
| PRE | 前导 | 告诉 Hub 即将与低速设备通信 |
| SPLIT | 分裂 | 高速 Hub 分裂事务(连接全速/低速设备) |
| ERR | 错误 | Hub 报告分裂事务错误 |
三、PID(Packet Identifier)详解
3.1 PID 编码规则
PID 占 8 位,低 4 位标识类型,高 4 位是低 4 位的取反(用于校验)。
┌────────┬────────┐
│ PID[7~4] │ PID[3~0] │
│ ~低4位 │ 类型码 │
│ 校验用 │ 实际含义 │
└────────┴────────┘
编码示例:
| PID 名称 | 类型码 | 取反 | 完整 PID |
|---|---|---|---|
| OUT | 0001 | 1110 | 11100001 = 0xE1 |
| IN | 1001 | 0110 | 01101001 = 0x69 |
| SETUP | 1101 | 0010 | 00101101 = 0x2D |
| DATA0 | 0011 | 1100 | 11000011 = 0xC3 |
| DATA1 | 1011 | 0100 | 01001011 = 0x4B |
| ACK | 0010 | 1101 | 11010010 = 0xD2 |
| NAK | 1010 | 0101 | 01011010 = 0x5A |
| STALL | 1110 | 0001 | 00011110 = 0x1E |
校验规则 :如果
(PID & 0x0F) == (~(PID >> 4) & 0x0F),则 PID 有效。
3.2 PID 类型速查表
| PID 值 | 类型 | 组 | 说明 |
|---|---|---|---|
| 0xE1 | OUT | 令牌 | Host → Device OUT 端点 |
| 0xD2 | ACK | 握手 | 确认接收 |
| 0x69 | IN | 令牌 | Host ← Device IN 端点 |
| 0x5A | NAK | 握手 | 未就绪 |
| 0x2D | SETUP | 令牌 | 控制传输开始 |
| 0x1E | STALL | 握手 | 端点挂起 |
| 0xC3 | DATA0 | 数据 | 数据包(偶数序号) |
| 0x4B | DATA1 | 数据 | 数据包(奇数序号) |
| 0xA5 | SOF | 令牌 | 帧起始 |
| 0x3C | PRE | 特殊 | 前导包 |
| 0x78 | SPLIT | 特殊 | 分裂事务 |
| 0x87 | EXT | 特殊 | 扩展(保留) |
四、三种基本事务
USB 2.0 定义了三种基本事务类型:SETUP、IN、OUT。所有传输类型都由这三种事务组合而成。
4.1 SETUP 事务
SETUP 事务用于控制传输的 Setup 阶段,由 Host 发起,向设备发送 8 字节的 Setup 数据包。
USB Device USB Host USB Device USB Host SETUP 事务(3 个包) SETUP 事务特点: 1. 总是 Host → Device 2. 总是使用 DATA0 3. 设备必须 ACK 4. 复位端点 Data Toggle TOKEN: SETUP + ADDR + ENDP DATA0: 8字节 Setup 数据 HANDSHAKE: ACK
SETUP 事务规则:
- 总是由 Host 发起
- 数据包总是使用 DATA0 PID
- 设备必须返回 ACK(不允许 NAK 或 STALL)
- 收到 SETUP 后,端点的 Data Toggle 被复位为 DATA0
- 数据载荷固定为 8 字节(Setup 数据包)
4.2 IN 事务
IN 事务用于从 Device 读取数据,Host 发送 IN 令牌,Device 返回数据。
USB Device USB Host USB Device USB Host IN 事务 - 正常情况 IN 事务 - 设备无数据 IN 事务 - 端点挂起 TOKEN: IN + ADDR + ENDP DATA0/DATA1: 数据 HANDSHAKE: ACK TOKEN: IN + ADDR + ENDP HANDSHAKE: NAK TOKEN: IN + ADDR + ENDP HANDSHAKE: STALL
IN 事务规则:
- Host 发送 IN 令牌,声明要读取的端点
- Device 响应:
- 有数据 → 发送 DATAx + Host ACK
- 无数据 → 发送 NAK
- 端点挂起 → 发送 STALL
- 数据包使用 DATA0/DATA1 交替
4.3 OUT 事务
OUT 事务用于向 Device 写入数据,Host 发送 OUT 令牌和数据,Device 返回握手。
USB Device USB Host USB Device USB Host OUT 事务 - 正常情况 OUT 事务 - 设备忙 OUT 事务 - 端点挂起 TOKEN: OUT + ADDR + ENDP DATA0/DATA1: 数据 HANDSHAKE: ACK TOKEN: OUT + ADDR + ENDP DATA0/DATA1: 数据 HANDSHAKE: NAK TOKEN: OUT + ADDR + ENDP DATA0/DATA1: 数据 HANDSHAKE: STALL
OUT 事务规则:
- Host 发送 OUT 令牌 + 数据包
- Device 响应:
- 接收成功 → 返回 ACK
- 缓冲区满 → 返回 NAK
- 端点挂起 → 返回 STALL
- 数据包使用 DATA0/DATA1 交替
五、事务的详细流程与错误处理
5.1 正常事务流程
收到数据/握手
是
否
超时
Host 决定发送事务
发送令牌包
OUT/IN/SETUP
等待响应
数据正确?
发送 ACK
不发送 ACK
超时重传
超时处理
事务完成
切换 Data Toggle
重传
5.2 NAK 响应处理
NAK 表示设备暂时无法处理,不是错误。
USB Device USB Host USB Device USB Host Host 向 Device 发送数据 Host 等待一段时间后重试 NAK 场景: - 设备缓冲区满 - 设备正在处理前一包 - 中断端点暂无数据 OUT + DATA0 NAK OUT + DATA0 NAK OUT + DATA0 ACK
NAK 的处理规则:
- Host 收到 NAK 后,不重试同一帧,等待下一帧/微帧再试
- 批量传输:Host 可以在同一帧内稍后重试
- 中断传输:Host 在下一个 bInterval 周期重试
- NAK 不切换 Data Toggle
5.3 STALL 响应处理
STALL 表示端点发生错误或被挂起。
USB Device USB Host USB Device USB Host Host 停止向该端点发送数据 端点 Data Toggle 复位为 DATA0 OUT + DATA1 STALL CLEAR_FEATURE (ENDPOINT_HALT, EPx) ACK OUT + DATA0 ACK
STALL 的处理规则:
- Host 收到 STALL 后,停止向该端点发送事务
- 必须通过 CLEAR_FEATURE(ENDPOINT_HALT) 清除
- 清除后,端点 Data Toggle 复位为 DATA0
- 控制端点的 STALL:通常表示请求不被支持或参数错误
5.4 超时与重传
是
否
是
否
是
否
Host 发送令牌包
启动超时计时器
收到响应?
CRC 正确?
超时
处理响应
重试次数 < 3?
重传相同包
不切换 Data Toggle
报告传输失败
事务完成
超时规则:
- Host 发送令牌包后启动超时计时器
- 超时时间取决于速度:FS 约 16~18 bit times,HS 更短
- 超时后重传,不切换 Data Toggle
- 通常重试 3 次后放弃
六、数据切换机制(Data Toggle)
6.1 Data Toggle 原理
Data Toggle 是 USB 保证数据包按正确顺序传输的机制。
USB Device USB Host USB Device USB Host 初始状态: Host=DATA0, Device=DATA0 双方切换: Host=DATA1, Device=DATA1 双方切换: Host=DATA0, Device=DATA0 假设此包丢失 Host 超时未收到 ACK 重传成功,双方切换: Host=DATA1, Device=DATA1 OUT + DATA0 ACK OUT + DATA1 ACK OUT + DATA0 OUT + DATA0 ACK
核心规则:
- 发送方和接收方各自维护一个 Data Toggle 位
- 成功传输后,双方同时切换(DATA0 ↔ DATA1)
- 接收方期望的 PID 与实际不符 → 丢弃数据但返回 ACK
- 发送方超时未收到 ACK → 重传相同 PID,不切换
6.2 各种情况下的 Data Toggle 行为
| 场景 | Host Toggle | Device Toggle | 结果 |
|---|---|---|---|
| 初始状态 | DATA0 | DATA0 | - |
| 正常传输 + ACK | 切换 DATA1 | 切换 DATA1 | 成功 |
| 数据包丢失 | 保持 DATA0 | 保持 DATA0 | Host 重传 DATA0 |
| ACK 丢失 | 保持 DATA0 | 切换 DATA1 | Host 重传 DATA0,Device 收到重复数据,丢弃但 ACK |
| SETUP 包 | 复位 DATA0 | 复位 DATA0 | 控制传输开始 |
| CLEAR_FEATURE(HALT) | - | 复位 DATA0 | 恢复端点 |
ACK 丢失的特殊情况:
USB Device USB Host USB Device USB Host 情况: 数据到达但 ACK 丢失 Host 未收到 ACK,认为传输失败 Device 已切换为 DATA1 收到 DATA0 ≠ 期望 DATA1 Host 收到 ACK,认为传输成功 但 Host 尚未切换! 双方恢复同步 OUT + DATA0 接收成功 ACK (丢失!) OUT + DATA0 (重传) ACK (丢弃重复数据) OUT + DATA1 ACK
七、事务与传输类型的关系
7.1 控制传输中的事务
控制传输由三个阶段组成,每个阶段包含多个事务。
USB Device USB Host USB Device USB Host === Setup 阶段 === === Data 阶段 (可选) === alt Host → Device (OUT) Device → Host (IN) === Status 阶段 === alt 无 Data 阶段 或 Data 为 OUT Data 为 IN SETUP + DATA0: 8字节 Setup ACK OUT + DATA1: 数据包1 ACK OUT + DATA0: 数据包2 ACK IN DATA1: 数据包1 ACK IN DATA0: 数据包2 ACK IN DATA1: ZLP ACK OUT + DATA1: ZLP ACK
控制传输事务组合:
| 阶段 | 事务类型 | 数据方向 | PID 序列 |
|---|---|---|---|
| Setup | SETUP + DATA0 + ACK | Host→Device | SETUP→DATA0→ACK |
| Data OUT | OUT + DATAx + ACK | Host→Device | OUT→DATA1→ACK→OUT→DATA0→ACK... |
| Data IN | IN + DATAx + ACK | Device→Host | IN→DATA1→ACK→IN→DATA0→ACK... |
| Status OUT | OUT + DATA1 + ACK | Host→Device | OUT→DATA1(ZLP)→ACK |
| Status IN | IN + DATA1 + ACK | Device→Host | IN→DATA1(ZLP)→ACK |
7.2 批量传输中的事务
批量传输由连续的 IN 或 OUT 事务 组成。
USB Device USB Host USB Device USB Host 批量读 (IN) 短包/ZLP = 传输结束 IN DATA0: 数据包1 (64B) ACK IN DATA1: 数据包2 (64B) ACK IN DATA0: 数据包3 (32B) ACK
批量传输结束标志:
- 短包:数据包长度 < 端点最大包大小
- ZLP:零长度包(wMaxPacketSize 的整数倍时)
7.3 中断传输中的事务
中断传输本质上是周期性的 IN 或 OUT 事务。
USB Device USB Host USB Device USB Host 中断传输 (周期性轮询) loop 每 10ms (bInterval) 无按键按下 loop 下一周期 loop 再下一周期 IN DATA0: 按键报告 (8B) ACK IN NAK IN DATA1: 按键报告 (8B) ACK
7.4 同步传输中的事务
同步传输无握手包,只有令牌包 + 数据包。
USB Device USB Host USB Device USB Host 同步 IN (无 ACK/NAK!) 无握手! loop 每帧/微帧 IN DATA0: 音频帧 (1023B) IN DATA1: 下一帧
同步传输特点:
- 2 个包:令牌 + 数据(无握手)
- 不保证数据正确性(无 CRC 重传)
- DATA0/DATA1 仍然交替,用于同步而非纠错
八、帧与微帧中的事务调度
8.1 帧结构
SYNC 0 7 PID = SOF (0xA5) 8 15 Frame Number (11 bit) 16 26 CRC5 27 31 EOP 32 39 SOF 包结构
| 速度 | 帧周期 | 微帧数 | 帧号范围 |
|---|---|---|---|
| Full Speed | 1 ms | 1 | 0~2047 |
| High Speed | 1 ms | 8 | 0~2047 |
| 微帧周期 | 125 μs | - | - |
8.2 帧内事务调度
SOF 包
帧开始
同步传输
带宽预留
最高优先级
中断传输
周期保证
控制传输
10% 带宽预留
批量传输
剩余带宽
帧结束
空闲
调度规则:
- 每帧/微帧以 SOF 开始
- 同步 + 中断 优先(带宽预留,延迟保证)
- 控制 最多 10% 帧带宽
- 批量 使用剩余带宽
九、USB 3.x 事务差异
| 特性 | USB 2.0 | USB 3.0+ |
|---|---|---|
| 通信方向 | 半双工 | 全双工 |
| 事务类型 | 广播 | 路由到目标设备 |
| 握手机制 | ACK/NAK/STALL 每包 | 基于信用的流控制 |
| 数据同步 | DATA0/DATA1 | 序列号机制 |
| 广播 | 所有设备接收 | 仅目标设备接收 |
| 端点数量 | 16 (IN+OUT) | 32 (IN+OUT) |
| 最大包大小 | 1024 (HS) | 1024 |
| 突发传输 | 无 | 最多 16 个包不握手 |
USB 3.0 事务简化:
- 不再使用 DATA0/DATA1,改用序列号
- 允许突发传输(Burst),连续发送多个包
- 使用信用机制(Credit-based Flow Control)代替 NAK
十、事务抓包实例分析
以下是一次 GET_DESCRIPTOR(Device, 8) 请求的事务级抓包:
事务 1:SETUP 事务
[Host] TOKEN: SETUP ADDR=0 ENDP=0 CRC5=OK
PID: 0x2D (00101101)
[Host] DATA0: Setup Packet (8 bytes)
80 06 00 01 00 00 08 00
PID: 0xC3 (11000011)
CRC16: OK
[Device] HANDSHAKE: ACK
PID: 0xD2 (11010010)
事务 2:IN 事务(Data 阶段)
[Host] TOKEN: IN ADDR=0 ENDP=0 CRC5=OK
PID: 0x69 (01101001)
[Device] DATA1: Device Descriptor (8 bytes)
12 01 00 02 00 00 00 08
PID: 0x4B (01001011)
CRC16: OK
[Host] HANDSHAKE: ACK
PID: 0xD2 (11010010)
事务 3:OUT 事务(Status 阶段)
[Host] TOKEN: OUT ADDR=0 ENDP=0 CRC5=OK
PID: 0xE1 (11100001)
[Host] DATA1: ZLP (Zero Length Packet)
PID: 0x4B (01001011)
CRC16: OK
[Device] HANDSHAKE: ACK
PID: 0xD2 (11010010)
完整控制传输 = 3 个事务 = 9 个包:
| 事务 | 包 1 | 包 2 | 包 3 | 方向 |
|---|---|---|---|---|
| SETUP | SETUP | DATA0 | ACK | Host→Device |
| IN | IN | DATA1 | ACK | Device→Host |
| OUT | OUT | DATA1 | ACK | Host→Device |
十一、总结
传输组合
事务组合
包类型
Token
OUT/IN/SETUP/SOF
Data
DATA0/DATA1/DATA2/MDATA
Handshake
ACK/NAK/STALL/NYET
SETUP 事务
Token+Data0+ACK
控制传输 Setup 阶段
IN 事务
Token+Data+ACK
Host 读取数据
OUT 事务
Token+Data+ACK
Host 写入数据
控制传输
SETUP+IN/OUT+IN/OUT
批量传输
IN/OUT × N
中断传输
周期性 IN/OUT
同步传输
Token+Data
无握手
核心要点回顾:
| 要点 | 说明 |
|---|---|
| 事务 = 2~3 个包 | 令牌 + 数据 + 握手(同步传输无握手) |
| 三种基本事务 | SETUP、IN、OUT |
| SETUP 用 DATA0 | 且复位 Data Toggle |
| Data Toggle | DATA0/DATA1 交替,保证顺序 |
| NAK | 暂时无法处理,不重试同一帧 |
| STALL | 端点挂起,需 CLEAR_FEATURE 恢复 |
| 同步传输 | 无握手,实时但不可靠 |
| 帧调度 | SOF → 同步 → 中断 → 控制 → 批量 |