2. USB协议体系
USB协议是一个分层的主从式通信协议,由物理层(包结构)、逻辑层(传输类型)和信息层(描述符)构成。理解这三层是开发USB Host驱动的基石。
学习提示: 本章内容偏重协议规范,建议结合USB分析仪或逻辑分析仪观察实际波形,能更快建立直观认识。
2.1 USB包结构
USB总线上的所有通信都以包(Packet)为基本单位。包由多个字段组成,通过同步字段(SYNC)开始,以包结束(EOP)终止。根据PID(包标识符)的不同,USB包分为令牌包、数据包和握手包三大类。
2.1.1 包格式与字段
| 字段 | 长度(位) | 说明 |
|---|---|---|
| SYNC | 8(低速/全速)/ 32(高速) | 同步序列,用于时钟同步 |
| PID | 8 | 包标识符,高4位为校验位 |
| ADDR | 7 | 设备地址(0~127) |
| ENDP | 4 | 端点号(0~15) |
| DATA | 0~1024 字节 | 数据负载 |
| CRC | 5(令牌包)/ 16(数据包) | 循环冗余校验 |
| EOP | 2位宽度的SE0 | 包结束信号 |
2.1.2 三种包类型
- 令牌包(Token Packet): 由Host发出,用于启动一次传输。包含SETUP、IN、OUT、SOF等PID。
- 数据包(Data Packet): 承载实际数据,分为DATA0和DATA1两种PID,用于数据切换同步。
- 握手包(Handshake Packet): 用于确认传输状态,包括ACK、NAK、STALL、NYET(高速)等。
驱动关注点: 在Host驱动中,我们通常不直接操作包字段,而是通过Host控制器(如EHCI、XHCI)的DMA描述符来配置传输。但理解包结构有助于调试USB总线错误,例如CRC错误或STALL握手。
2.2 USB传输类型
USB协议定义了四种传输类型,分别对应不同的应用场景。Host驱动需要根据设备端点描述符中的bmAttributes字段选择正确的传输类型。
| 传输类型 | 特点 | 带宽保证 | 错误重试 | 典型应用 |
|---|---|---|---|---|
| 控制传输 | 双向、可靠、用于枚举和配置 | 10%(保留) | 是(硬件重试) | 设备枚举、设置配置、类请求 |
| 批量传输 | 单向/双向、可靠、非实时 | 无(利用剩余带宽) | 是(硬件重试) | U盘、打印机、网络适配器 |
| 中断传输 | 单向、可靠、低延迟 | 有(轮询间隔保证) | 是(硬件重试) | 鼠标、键盘、游戏手柄 |
| 等时传输 | 单向、不可靠、实时 | 有(带宽预留) | 否(无重试) | 音频、视频、摄像头 |
2.2.1 控制传输
控制传输是USB协议中最复杂的传输类型,由建立阶段、数据阶段(可选)和状态阶段组成。每个控制传输对应一个控制请求(Setup Packet),其格式如下:
struct usb_ctrlrequest {
__u8 bRequestType; // 方向(bit7)、类型(bit5-6)、接收者(bit0-4)
__u8 bRequest; // 请求码(如GET_DESCRIPTOR、SET_ADDRESS)
__le16 wValue; // 请求参数(如描述符类型和索引)
__le16 wIndex; // 接口或端点号
__le16 wLength; // 数据阶段长度
} __attribute__ ((packed));
驱动开发注意: 控制传输的Setup包必须使用DATA0 PID,数据阶段使用DATA0/DATA1交替,状态阶段使用DATA1。Host控制器硬件通常自动处理PID切换,但驱动需要正确设置传输描述符的数据切换位。
2.2.2 批量传输
批量传输用于传输大量数据,利用USB总线空闲带宽。批量传输使用DATA0/DATA1交替机制保证数据完整性。在高速模式下,批量传输的最大包大小为512字节。
2.2.3 中断传输
中断传输并非真正的中断,而是Host以固定间隔轮询设备。轮询间隔在端点描述符的bInterval字段中指定。对于全速设备,间隔为1~255ms;高速设备间隔为2^(bInterval-1)×125μs。
2.2.4 等时传输
等时传输用于实时数据流,不保证数据正确性(无重试机制)。等时传输使用DATA0 PID,不进行数据切换。在高速模式下,等时传输的最大包大小为1024字节。
2.3 USB描述符
USB描述符是设备向Host报告自身能力和配置的数据结构。Host通过控制传输读取描述符,完成设备枚举。描述符采用层级结构:设备描述符 → 配置描述符 → 接口描述符 → 端点描述符。
2.3.1 描述符层级结构
- 设备描述符(Device Descriptor): 描述设备的通用信息,如USB版本、厂商ID、产品ID、设备类等。每个设备只有一个设备描述符。
- 配置描述符(Configuration Descriptor): 描述设备的供电模式、最大电流、接口数量等。一个设备可以有多个配置,但同一时刻只能激活一个。
- 接口描述符(Interface Descriptor): 描述一个功能单元,包含端点数量、接口类/子类/协议等。一个配置可以有多个接口。
- 端点描述符(Endpoint Descriptor): 描述一个端点的地址、方向、传输类型、最大包大小等。接口0的端点0是默认控制端点,没有端点描述符。
2.3.2 关键描述符字段
| 描述符类型 | 关键字段 | 说明 |
|---|---|---|
| 设备描述符 | bcdUSB, idVendor, idProduct, bNumConfigurations | USB版本、VID/PID、配置数量 |
| 配置描述符 | wTotalLength, bNumInterfaces, bmAttributes, bMaxPower | 总长度、接口数、供电属性、最大电流 |
| 接口描述符 | bInterfaceNumber, bAlternateSetting, bNumEndpoints, bInterfaceClass | 接口号、备用设置、端点数、类代码 |
| 端点描述符 | bEndpointAddress, bmAttributes, wMaxPacketSize, bInterval | 端点地址、传输类型、最大包大小、轮询间隔 |
2.3.3 描述符在Linux内核中的表示
Linux内核USB子系统定义了与USB规范对应的数据结构,位于include/uapi/linux/usb/ch9.h:
/* 设备描述符 */
struct usb_device_descriptor {
__u8 bLength; // 描述符长度(18字节)
__u8 bDescriptorType; // 描述符类型(0x01)
__le16 bcdUSB; // USB规范版本号(BCD码)
__u8 bDeviceClass; // 设备类
__u8 bDeviceSubClass; // 设备子类
__u8 bDeviceProtocol; // 设备协议
__u8 bMaxPacketSize0; // 端点0最大包大小
__le16 idVendor; // 厂商ID
__le16 idProduct; // 产品ID
__le16 bcdDevice; // 设备版本号
__u8 iManufacturer; // 厂商字符串描述符索引
__u8 iProduct; // 产品字符串描述符索引
__u8 iSerialNumber; // 序列号字符串描述符索引
__u8 bNumConfigurations; // 配置描述符数量
} __attribute__ ((packed));
/* 端点描述符 */
struct usb_endpoint_descriptor {
__u8 bLength; // 描述符长度(7字节)
__u8 bDescriptorType; // 描述符类型(0x05)
__u8 bEndpointAddress; // 端点地址(bit7:方向, bit3-0:端点号)
__u8 bmAttributes; // 传输类型(bit1-0: 0=控制,1=等时,2=批量,3=中断)
__le16 wMaxPacketSize; // 最大包大小
__u8 bInterval; // 轮询间隔
} __attribute__ ((packed));
驱动开发技巧: 在Linux USB驱动中,使用
usb_get_descriptor()函数读取原始描述符,或通过struct usb_device的descriptor字段直接访问解析后的设备描述符。接口和端点描述符则通过struct usb_interface和struct usb_host_endpoint访问。
2.4 本章小结
- USB包结构是总线通信的最小单元,分为令牌包、数据包和握手包三种。
- 四种传输类型(控制、批量、中断、等时)对应不同的应用场景,驱动需根据端点属性选择。
- USB描述符采用层级结构(设备→配置→接口→端点),Host通过枚举过程读取并解析。
- Linux内核在
ch9.h中定义了与USB规范一致的数据结构,驱动开发时直接使用。
下节预告: 第3章将深入USB枚举过程,分析Host如何通过控制传输读取描述符、分配地址、选择配置,并最终使设备进入就绪状态。