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序列

一个完整的事务由令牌包阶段、数据包阶段(可选)和握手包阶段组成。不同传输类型的事务格式不同。

USB事务格式与PID序列 SETUP 事务 SETUP DATA0 ACK DATA 事务(IN/OUT) IN/OUT DATA0/1 ACK/NAK 批量 IN 事务 IN DATA0/1 ACK 批量 OUT 事务 OUT DATA0/1 ACK/NAK 中断 IN 事务 IN DATA ACK 等时 OUT 事务 OUT DATA 无握手 事务格式说明 令牌包(Token):由主机发出,启动事务 数据包(Data):携带数据负载,PID交替(DATA0/DATA1) 握手包(Handshake):设备反馈状态(ACK/NAK/STALL) 等时传输无握手阶段,不保证交付 注:高速模式下批量/控制事务可包含 PING 令牌用于流控制

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、缓冲区和错误处理策略。