2. USB协议基础(一):USB数据包结构、包类型与总线事务

USB通信的本质是包(Packet)的交换。主机与设备之间所有的数据传输,最终都被拆解为一系列符合USB协议规范的包。理解包的结构、类型以及由包组成的事务,是开发USB设备驱动和深入主机控制器原理的基石。

2.1 USB数据包结构

一个标准的USB数据包由以下字段按顺序组成:

字段 全称 长度(位) 说明
SOP Start of Packet — 同步序列,用于接收器锁定时钟。由SYNC字段实现(通常为8位,全0后跟一个1)。
PID Packet Identifier 8 包标识符,包含4位类型码和4位校验码。决定包的类型和方向。
ADDR Address 7 设备地址,范围0~127。地址0用于默认控制管道(枚举阶段)。
ENDP Endpoint 4 端点号,范围0~15。控制端点必须为0。
CRC Cyclic Redundancy Check 5或16 循环冗余校验。Token包使用5位CRC,Data包使用16位CRC。
EOP End of Packet — 包结束标志,由SE0状态(D+和D-同时为低)持续2位时间构成。
关键点: PID字段中的4位校验码是类型码的按位取反。例如,OUT令牌的PID为0001,其校验码为1110,组合后为0001 1110(0x1E)。接收方通过校验码快速检测PID传输错误。

2.1.1 数据包结构示意图

下图展示了USB数据包在总线上的完整帧结构,从SOP到EOP的每个字段依次排列:

USB 数据包通用结构 SOP (SYNC) PID (8位) ADDR (7位) ENDP (4位) CRC (5/16位) EOP (SE0) 说明:Token包仅包含SOP+PID+ADDR+ENDP+CRC5+EOP;Data包包含SOP+PID+数据负载+CRC16+EOP。 Handshake包仅包含SOP+PID+EOP,无ADDR、ENDP和CRC字段。

2.2 包类型(Packet Types)

USB协议定义了四大类包,由PID字段区分。每一类包服务于总线事务的不同阶段。

2.2.1 Token包(令牌包)

Token包由主机发出,用于启动一次总线事务。它包含设备地址和端点号,指定了后续数据传输的方向和参与者。

  • OUT (PID=0001):通知设备,主机即将发送数据到该设备的指定端点。
  • IN (PID=1001):通知设备,主机准备从该设备的指定端点接收数据。
  • SETUP (PID=1101):用于控制传输的建立阶段,主机向设备发送控制请求。
  • SOF (PID=0101):Start of Frame,主机每1ms(全速/低速)或125µs(高速)广播一次,用于同步帧起始。

2.2.2 Data包(数据包)

Data包承载实际的有效载荷数据。根据数据切换(Data Toggle)机制,分为DATA0和DATA1两种类型,用于保证数据完整性。

  • DATA0 (PID=0011):初始数据包或偶数序号数据包。
  • DATA1 (PID=1011):奇数序号数据包。

2.2.3 Handshake包(握手包)

Handshake包用于报告事务的完成状态。它是最简单的包,仅由PID和EOP组成。

  • ACK (PID=0010):确认。数据正确接收,无错误。
  • NAK (PID=1010):否认。设备暂时无法发送或接收数据(忙),但未发生错误。
  • STALL (PID=1110):停滞。设备端点出现严重错误,需要主机干预(如控制端点发送错误请求)。
  • NYET (PID=0110):未准备好。高速传输中,设备尚未准备好接收下一个数据包。

2.2.4 Special包(特殊包)

特殊包用于更高级的总线管理功能,主要在高速模式下使用。

  • PRE (PID=1100):前导包。用于主机在高速总线上与低速设备通信前,先发送PRE包切换总线模式。
  • ERR (PID=1100):错误包。在高速分裂事务中,集线器向主机报告下游总线错误。
  • SPLIT (PID=1000):分裂事务包。用于高速主机通过集线器与全速/低速设备通信。
  • PING (PID=0100):探测包。高速模式下,主机在发送数据前先探测设备端点是否准备好接收。
实战要点: 在驱动开发中,最常见的握手包是ACK、NAK和STALL。NAK通常表示设备忙,驱动应稍后重试;STALL表示协议错误,驱动需要执行清除停滞(Clear Feature)操作。

2.2.5 包类型分类表

大类 PID (二进制) 名称 方向 用途
Token 0001 OUT 主机→设备 启动主机到设备的数据传输
1001 IN 主机→设备 启动设备到主机的数据传输
1101 SETUP 主机→设备 启动控制传输的建立阶段
0101 SOF 主机→设备 帧起始广播
Data 0011 DATA0 双向 偶数序号数据
1011 DATA1 双向 奇数序号数据
Handshake 0010 ACK 接收方→发送方 确认成功接收
1010 NAK 设备→主机 设备忙,无法处理
1110 STALL 设备→主机 端点停滞,协议错误
0110 NYET 设备→主机 高速模式下未准备好
Special 1100 PRE/ERR 主机→设备 前导/错误报告
1000 SPLIT 主机→集线器 分裂事务
0100 PING 主机→设备 高速探测
— Reserved — 保留

2.3 总线事务(Bus Transactions)

一次总线事务由主机发起,包含令牌阶段、数据阶段(可选)和握手阶段(可选)。事务是USB数据传输的基本单元。

2.3.1 事务的三种基本类型

  1. IN事务:主机从设备读取数据。
    • 令牌阶段:主机发送IN Token包。
    • 数据阶段:设备返回DATA0/DATA1包(如果设备有数据要发送)。
    • 握手阶段:主机发送ACK(正确接收)或NAK/STALL(错误)。
  2. OUT事务:主机向设备发送数据。
    • 令牌阶段:主机发送OUT Token包。
    • 数据阶段:主机发送DATA0/DATA1包。
    • 握手阶段:设备返回ACK(成功接收)、NAK(忙)或STALL(停滞)。
  3. SETUP事务:控制传输的建立阶段,用于发送标准请求。
    • 令牌阶段:主机发送SETUP Token包。
    • 数据阶段:主机发送DATA0包(固定为DATA0,8字节标准请求)。
    • 握手阶段:设备必须返回ACK(成功接收),否则主机重试。

2.3.2 事务流程示意图

下图展示了IN事务和OUT事务的完整包序列:

USB 总线事务流程 IN 事务(主机读取) IN Token DATA0/1 ACK 主机→设备 设备→主机 主机→设备 OUT 事务(主机写入) OUT Token DATA0/1 ACK 主机→设备 主机→设备 设备→主机 说明:SETUP事务与OUT事务结构类似,但Token包为SETUP,数据包固定为DATA0,且设备必须返回ACK。

2.3.3 事务与传输的关系

USB协议定义了四种传输类型(Transfer Types),每种传输由多个总线事务组成:

传输类型 包含的事务 典型应用
控制传输 SETUP事务 + 0/多个IN/OUT事务 + 状态阶段事务 设备枚举、标准请求(如Get Descriptor)
批量传输 多个IN或OUT事务 U盘、打印机、网络适配器
中断传输 单个IN或OUT事务(保证轮询间隔) 鼠标、键盘、游戏手柄
等时传输 单个IN或OUT事务(无握手阶段) 音频、视频、实时数据流
常见误区: 初学者常将“事务”与“传输”混淆。一次控制传输可能包含2~3个事务(建立、数据、状态),而一次批量传输可能包含成千上万个IN/OUT事务。事务是总线上的原子操作,传输是逻辑上的数据交换过程。

2.4 数据切换(Data Toggle)机制

为了保证数据包不会丢失或重复,USB协议引入了数据切换机制。每个端点维护一个Toggle位(0或1),初始值为0。

  • 主机和设备各自维护独立的Toggle状态。
  • 每次成功的事务(收到ACK)后,Toggle位翻转(0→1或1→0)。
  • 发送方发送与当前Toggle匹配的DATA包(DATA0对应Toggle=0,DATA1对应Toggle=1)。
  • 接收方验证接收到的DATA包类型是否与期望的Toggle一致。如果不一致,说明发生了错误,接收方丢弃该包并返回NAK或ACK(取决于协议)。
驱动开发提示: 在Linux USB驱动中,usb_submit_urb()函数会自动管理Toggle位。但在某些底层调试场景(如使用usb_control_msg或自定义批量传输),开发者需要确保Toggle状态正确,否则设备会持续返回NAK或STALL。

2.5 本章小结