2. USB协议体系:数据包结构与传输机制
USB协议采用分层拓扑 + 主从轮询的通信模型。主机(Host)始终作为事务发起方,设备(Device)被动响应。本章聚焦USB协议最核心的两个维度:数据包结构(传输的最小原子单元)与传输类型(面向不同场景的通信契约)。
2.1 USB数据包结构
USB总线上的所有通信都以数据包(Packet)为基本单位。每个包由SYNC同步域、PID包标识符、数据域(可选)、CRC校验组成。根据PID的不同,USB数据包分为四大类:
| 包类型 | PID值(低4位) | 方向 | 功能描述 |
|---|---|---|---|
| 令牌包(Token) | 0x1 / 0x9 / 0xD | Host → Device | 标识事务开始,指定端点号、方向和设备地址 |
| 数据包(Data) | 0x3 / 0xB | 双向 | 携带实际有效载荷(DATA0 / DATA1交替翻转) |
| 握手包(Handshake) | 0x2 / 0xA / 0xE | Device → Host | 确认传输状态:ACK/NAK/STALL/NYET |
| 特殊包(Special) | 0x0 / 0x8 / 0xC / 0xF | Host → Device | PRE(前导包)、ERR(拆分事务错误)、SPLIT(高速拆分) |
2.1.1 令牌包(Token Packet)
令牌包是USB事务的发起信号,由主机在每帧起始时发送。其结构固定为:SYNC + PID + 7位地址 + 4位端点号 + 5位CRC。三种核心令牌:
- OUT令牌(PID=0x1):通知设备主机即将发送数据。
- IN令牌(PID=0x9):请求设备向主机发送数据。
- SETUP令牌(PID=0xD):仅用于控制传输,启动设置阶段。
2.1.2 数据包(Data Packet)
数据包紧跟在令牌包之后,最大有效载荷为1024字节(高速模式)。USB协议使用数据切换(Data Toggle)机制保证可靠性:
- DATA0(PID=0x3):事务序列中的第一个数据包。
- DATA1(PID=0xB):事务序列中的后续数据包,交替翻转。
主机和设备各自维护一个Toggle位,只有PID匹配时数据才被接受,否则丢弃并重传。
2.1.3 握手包(Handshake Packet)
握手包是USB协议中最短的包(仅SYNC + PID),用于反馈事务结果:
| 握手类型 | PID | 含义 | 触发场景 |
|---|---|---|---|
| ACK | 0x2 | 数据正确接收 | 正常完成 |
| NAK | 0xA | 设备忙,无法处理 | 端点未准备好 |
| STALL | 0xE | 端点挂起/错误 | 不支持请求或端点halt |
| NYET | 0x6 | 尚未准备好(高速) | 拆分事务中 |
2.1.4 特殊包(Special Packet)
特殊包用于扩展总线管理能力,常见于高速/低速混合拓扑:
- PRE(前导包):主机在低速传输前发送,通知高速集线器切换端口。
- SPLIT(拆分事务包):用于高速主机与低速/全速设备之间的拆分事务。
- ERR(错误包):集线器向主机报告拆分事务错误。
struct urb描述传输方向、端点和缓冲区,底层由usb_submit_urb()完成包级封装。
2.2 USB传输类型
USB协议定义了四种传输类型,分别对应不同的带宽、延迟和可靠性需求。每种传输类型绑定到特定的端点(Endpoint),端点在设备枚举时通过端点描述符声明其传输类型和最大包大小。
| 传输类型 | 端点类型 | 带宽保证 | 错误重试 | 延迟 | 典型应用 |
|---|---|---|---|---|---|
| 控制传输 | 控制端点(EP0) | 10% 保留 | 自动重试 | 中等 | 设备枚举、配置、命令 |
| 批量传输 | 批量端点 | 尽力而为 | 自动重试(硬件) | 大 | U盘、打印机、网络 |
| 中断传输 | 中断端点 | ≤90% 帧内 | 自动重试 | 低(轮询间隔) | 鼠标、键盘、HID |
| 等时传输 | 等时端点 | 固定带宽 | 不重试 | 最低 | 音频、视频、摄像头 |
2.2.1 控制传输(Control Transfer)
控制传输是USB协议中唯一必须支持的传输类型,每个设备至少有一个默认控制端点(端点0)。它采用2~3阶段的握手式事务:
- 设置阶段(Setup Stage):主机发送SETUP令牌 + DATA0包(8字节标准请求)。
- 数据阶段(Data Stage)(可选):主机或设备发送数据,使用DATA1/DATA0交替。
- 状态阶段(Status Stage):接收方返回握手包,确认整个控制传输完成。
Linux驱动中,控制传输通过usb_control_msg()封装,驱动只需填充struct usb_ctrlrequest中的bRequestType、bRequest、wValue、wIndex、wLength字段。
2.2.2 批量传输(Bulk Transfer)
批量传输用于大数据量、非实时的场景,利用总线空闲带宽传输。其特点:
- 无带宽保证:仅在总线空闲时传输,可能被其他传输抢占。
- 硬件重试:数据错误时自动重传,保证数据完整性。
- 最大包大小:全速64字节,高速512字节,超高速1024字节。
在Linux中,批量传输使用usb_bulk_msg()或通过usb_fill_bulk_urb()构造URB提交。
2.2.3 中断传输(Interrupt Transfer)
中断传输并非硬件中断,而是主机轮询设备端点。设备在端点描述符中声明轮询间隔(bInterval):
- 全速:1~255 ms
- 高速:2^(bInterval-1)× 125 μs(微帧)
- 超高速:2^(bInterval-1)× 125 μs
中断传输保证在轮询间隔内至少有一次传输机会,适用于HID设备、USB键盘、游戏手柄等低延迟外设。
2.2.4 等时传输(Isochronous Transfer)
等时传输是USB协议中唯一不保证数据正确性的传输类型,但提供固定的带宽和最低延迟:
- 预分配带宽:在帧/微帧中预留固定时间片。
- 无重试:数据错误直接丢弃,不重传。
- 无握手:等时事务不包含握手阶段。
等时传输常用于USB摄像头、USB音频设备、数据采集卡等对实时性要求高、允许少量丢包的场景。Linux驱动中通过usb_fill_isoc_urb()构造等时URB,需指定每个帧描述符(struct usb_iso_packet_descriptor)。
-EXDEV(部分数据错误),驱动应丢弃该帧并等待下一帧,而不是重传。
2.3 USB帧与微帧机制
USB总线采用时间分片的方式管理总线访问。主机控制器将时间划分为固定长度的帧(Frame)或微帧(Microframe),在每个帧/微帧内发起多个事务。
| 总线速度 | 帧周期 | 微帧数/帧 | 每帧最大事务数 | 帧起始包 |
|---|---|---|---|---|
| 低速(1.5 Mbps) | 1 ms | 1 | 约 8~16 | SOF |
| 全速(12 Mbps) | 1 ms | 1 | 约 100~200 | SOF |
| 高速(480 Mbps) | 1 ms | 8 | 约 800~1600 | SOF + 微帧SOF |
| 超高速(5 Gbps) | 125 μs | 1(微帧即帧) | 约 4000+ | 特殊包 |
2.3.1 帧起始包(SOF)
在每个帧/微帧的起始时刻,主机发送SOF(Start of Frame)令牌包。SOF包包含一个帧号(Frame Number),设备可据此同步时钟或检测帧丢失。SOF包结构:
- PID:0x5(SOF)
- 帧号:11位,范围0~2047,每帧递增
- CRC:5位校验
2.3.2 微帧(Microframe)
高速模式下,每个1ms帧被划分为8个125μs的微帧。每个微帧以SOF包开始,主机可以在每个微帧内调度多个事务。微帧机制使得高速总线能够:
- 更精细的带宽分配:等时传输可以按125μs粒度预留带宽。
- 更低延迟:中断传输的轮询间隔可以低至125μs。
- 更高吞吐:批量传输可以在多个微帧间交错传输。
2.3.3 帧调度与带宽管理
主机控制器(HC)负责在每帧/微帧内调度所有待处理的事务。调度策略遵循以下优先级:
- 等时传输:固定带宽,优先调度,不可超时。
- 中断传输:按轮询间隔调度,保证延迟上限。
- 控制传输:保留10%带宽,用于枚举和命令。
- 批量传输:使用剩余带宽,无时间保证。
interval字段(中断传输)和number_of_packets字段(等时传输)间接影响帧调度。例如,设置urb->interval = 1表示每帧轮询一次(全速1ms,高速125μs)。
2.4 本章小结
USB协议体系的核心在于包结构与传输类型的精确匹配