3. USB包结构与事务:令牌包、数据包、握手包、SOF包、事务格式与PID序列
USB总线上的所有数据传输均以“包(Packet)”为基本单元。主机与设备之间通过一系列有序的包构成一个“事务(Transaction)”,而多个事务则组成一次“传输(Transfer)”。理解包结构与事务格式是编写EHCI主机控制器驱动、解析USB总线流量的前提。
3.1 USB包结构总览
USB包由同步域(SYNC)、包标识符(PID)、数据字段(Payload,可选)、CRC校验和包结束符(EOP)组成。所有包在总线上以LSB优先方式传输。
| 字段 | 长度(位) | 说明 |
|---|---|---|
| SYNC | 8 | 同步序列,用于时钟恢复,固定为 00000001B |
| PID | 8 | 包标识符,高4位为类型,低4位为校验(取反) |
| ADDR/ENDP/数据 | 可变 | 令牌包含地址+端点;数据包含数据负载;SOF包含帧号 |
| CRC | 5 或 16 | 令牌包使用5位CRC,数据包使用16位CRC |
| EOP | 2 | 包结束信号,SE0状态持续2位时间 |
驱动开发要点: EHCI控制器硬件自动处理SYNC、CRC和EOP。驱动开发者只需关注PID序列的构造与解析,以及数据缓冲区(QH/qTD)的正确配置。
3.2 包标识符(PID)类型与序列
PID是USB包的核心标识,共16种,分为4组。驱动中常通过PID判断当前事务阶段。
| PID组 | PID值(二进制) | 名称 | 用途 |
|---|---|---|---|
| 令牌(Token) | 0001 | OUT | 主机→设备,通知设备接收数据 |
| 1001 | IN | 主机→设备,请求设备发送数据 | |
| 0101 | SOF | 主机→设备,帧起始广播 | |
| 1101 | SETUP | 主机→设备,控制传输设置阶段 | |
| 数据(Data) | 0011 | DATA0 | 数据包,偶数据PID |
| 1011 | DATA1 | 数据包,奇数据PID | |
| 握手(Handshake) | 0010 | ACK | 设备→主机,确认正确接收 |
| 1010 | NAK | 设备→主机,忙,无法处理 | |
| 1110 | STALL | 设备→主机,端点挂起或错误 | |
| 0110 | NYET | 设备→主机,尚未准备好(高速模式) | |
| 特殊(Special) | 1100 | PRE | 前导包,用于低速设备通过集线器通信 |
| 0100 | ERR | 分割事务错误 | |
| 1000 | SPLIT | 分割事务令牌 | |
| 0000 | PING | 高速流控制探测 |
PID校验机制: PID的低4位是高4位的按位取反。例如OUT包PID=0001,低4位应为1110。硬件自动校验,若校验失败则丢弃该包。驱动在调试时可通过逻辑分析仪捕获PID错误来定位总线噪声或设备异常。
3.3 四种包格式详解
3.3.1 令牌包(Token Packet)
令牌包由主机发出,用于启动一个事务。包含7位设备地址和4位端点号。SETUP/IN/OUT三种令牌包结构相同,仅PID不同。
位序: 0 7 8 15 16 18 19 23
+--------+--------+--------+--------+
| SYNC | PID | ADDR | ENDP | CRC5 |
| (8位) | (8位) | (7位) | (4位) | (5位)|
+--------+--------+--------+--------+
驱动陷阱: EHCI控制器在高速模式下使用qTD中的“端点号”和“设备地址”字段自动生成令牌包。驱动在填充qTD时若地址或端点号错误,会导致设备无响应或NAK风暴。务必核对设备枚举阶段分配的地址。
3.3.2 数据包(Data Packet)
数据包紧跟在令牌包之后,携带实际数据负载。数据长度由端点描述符的wMaxPacketSize决定。高速批量端点最大为512字节,高速控制端点为64字节。
位序: 0 7 8 15 16 N N+15
+--------+--------+------------------------+--------+
| SYNC | PID | DATA FIELD | CRC16 |
| (8位) | (8位) | (0~1024字节) | (16位) |
+--------+--------+------------------------+--------+
3.3.3 握手包(Handshake Packet)
握手包仅由SYNC、PID和EOP组成,无数据字段和CRC。设备通过握手包向主机反馈事务状态。
位序: 0 7 8 15
+--------+--------+
| SYNC | PID |
| (8位) | (8位) |
+--------+--------+
3.3.4 SOF包(Start-of-Frame Packet)
SOF包由主机每1ms(全速/低速)或125µs(高速微帧)广播一次,用于同步帧起始和总线时钟。包含11位帧号。
位序: 0 7 8 15 16 26 27 31
+--------+--------+--------+--------+
| SYNC | PID | FRAME | CRC5 |
| (8位) | (8位) | (11位) | (5位) |
+--------+--------+--------+--------+
EHCI与SOF: EHCI控制器内部维护一个帧计数器(Frame Index Register),驱动通过设置Periodic Frame List基地址和微帧掩码来控制SOF的发送。SOF包由硬件自动生成,驱动无需手动构造。
3.4 事务格式与PID序列
一个完整的事务由令牌包阶段、数据包阶段(可选)和握手包阶段组成。不同传输类型的事务格式不同。
3.5 PID序列与数据切换
USB协议使用数据切换(Data Toggle)机制保证数据完整性。每个端点维护一个Toggle位,DATA0和DATA1交替使用。
| 传输类型 | PID序列规则 | Toggle初始值 | 驱动注意事项 |
|---|---|---|---|
| 控制传输(SETUP) | SETUP(DATA0) → IN/OUT(DATA1) → STATUS(DATA1) | DATA0 | SETUP阶段固定DATA0,驱动需在qTD中设置Toggle=0 |
| 批量传输 | 连续事务中DATA0/DATA1交替 | DATA0 | EHCI的qTD中DT位控制Toggle,出错重试时不翻转 |
| 中断传输 | 同批量,DATA0/DATA1交替 | DATA0 | 中断端点需保证Toggle与主机同步 |
| 等时传输 | 固定DATA0,无Toggle | DATA0 | 无握手,不重传,Toggle固定 |
Toggle同步实战: 当设备返回STALL时,主机驱动必须对端点执行ClearFeature(ENDPOINT_HALT)请求,并将该端点的Data Toggle重置为DATA0。EHCI控制器通过qTD的“Data Toggle”位和“Alternate”qTD链表自动处理Toggle,驱动在填充qTD时需正确初始化。
3.6 事务超时与错误处理
驱动在处理事务时必须考虑超时场景。USB协议规定主机在发出令牌包后,设备必须在规定时间内响应。
| 错误类型 | 触发条件 | 驱动处理策略 |
|---|---|---|
| 超时(Timeout) | 设备在16位时间内无响应 | 重试事务,超过重试次数则终止传输并报告错误 |
| NAK | 设备忙,无法处理数据 | 批量端点:重试;控制端点:重试;中断端点:等待下一轮询 |
| STALL | 端点挂起或不支持请求 | 停止该端点上的所有后续事务,执行清除挂起操作 |
| CRC错误 | 数据包CRC校验失败 | 丢弃数据,重试事务(等时传输除外) |
| 位填充错误 | 总线信号违反位填充规则 | 硬件自动重试,驱动记录错误计数 |
EHCI超时处理: EHCI控制器通过qTD的“中断完成”和“错误计数”字段管理重试。驱动在初始化qTD时应设置合适的重试次数(通常为3次)。若错误计数耗尽,控制器将终止该qTD并产生中断,驱动需在中断服务例程中检查qTD状态并清理。
3.7 事务在EHCI中的硬件映射
在EHCI控制器中,事务被抽象为qTD(Queue Element Transfer Descriptor)。驱动通过填充qTD来定义事务的PID序列、数据缓冲区和Toggle状态。
/* EHCI qTD 结构关键字段(简化) */
struct ehci_qtd {
__le32 hw_next; /* 下一个qTD指针 */
__le32 hw_alt_next; /* 备用qTD指针(错误处理) */
__le32 hw_token; /* 令牌域:包含PID、Toggle、数据长度、错误计数 */
__le32 hw_buf[5]; /* 数据缓冲区指针(最多5个page) */
};
/* hw_token 字段位定义 */
#define QTD_TOKEN_PID_OUT 0x00 /* OUT令牌 */
#define QTD_TOKEN_PID_IN 0x01 /* IN令牌 */
#define QTD_TOKEN_PID_SETUP 0x02 /* SETUP令牌 */
#define QTD_TOKEN_DT 0x40 /* Data Toggle位 */
#define QTD_TOKEN_CERR_SHIFT 10 /* 错误计数 */
#define QTD_TOKEN_LENGTH_MASK 0x7fff /* 数据长度 */
驱动实战: 构造一个批量IN事务时,驱动需设置qTD的hw_token为PID_IN,DT位根据当前Toggle状态置位或清零,并指定数据缓冲区地址。EHCI控制器自动发送IN令牌包,等待设备返回数据包,然后发送ACK握手。整个过程无需驱动干预,仅需在完成中断后检查qTD状态。
3.8 本章小结
- USB包由SYNC、PID、数据字段、CRC和EOP组成,PID是包类型标识。
- 四种包类型:令牌包(启动事务)、数据包(携带数据)、握手包(状态反馈)、SOF包(帧同步)。
- 事务由令牌阶段、数据阶段(可选)和握手阶段(可选)组成,不同传输类型事务格式不同。
- 数据切换(DATA0/DATA1)保证数据完整性,驱动需正确管理Toggle状态。
- EHCI控制器通过qTD硬件自动执行事务,驱动负责配置qTD的PID、Toggle、缓冲区和错误处理策略。