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握手。
USB包结构时序图 USB包结构时序图(以IN传输为例) 令牌包 PID=IN, ADDR, ENDP 数据包 PID=DATA0/1, DATA, CRC16 握手包 PID=ACK/NAK/STALL 说明: 1. 令牌包由Host发出,指定传输方向和目标端点。 2. 数据包由发送方发出,包含实际数据负载和CRC校验。 3. 握手包由接收方发出,表示传输成功(ACK)或失败(NAK/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字节。

USB四种传输类型对比 USB四种传输类型对比 控制传输 • 双向、可靠、有重试 • 用于枚举、配置、类请求 • 三个阶段:建立→数据→状态 • 最大包:64B(FS)/64B(HS) 批量传输 • 单向/双向、可靠、有重试 • 利用剩余带宽,无实时保证 • DATA0/DATA1交替机制 • 最大包:64B(FS)/512B(HS) 中断传输 • 单向、可靠、有重试 • 固定轮询间隔(bInterval) • 低延迟,适合人机交互设备 • 最大包:64B(FS)/1024B(HS) 等时传输 • 单向、不可靠、无重试 • 带宽预留,实时性最好 • 无数据切换,使用DATA0 • 最大包:1023B(FS)/1024B(HS) FS=全速(12Mbps) HS=高速(480Mbps) SS=超高速(5Gbps)

2.3 USB描述符

USB描述符是设备向Host报告自身能力和配置的数据结构。Host通过控制传输读取描述符,完成设备枚举。描述符采用层级结构:设备描述符 → 配置描述符 → 接口描述符 → 端点描述符。

2.3.1 描述符层级结构

  1. 设备描述符(Device Descriptor): 描述设备的通用信息,如USB版本、厂商ID、产品ID、设备类等。每个设备只有一个设备描述符。
  2. 配置描述符(Configuration Descriptor): 描述设备的供电模式、最大电流、接口数量等。一个设备可以有多个配置,但同一时刻只能激活一个。
  3. 接口描述符(Interface Descriptor): 描述一个功能单元,包含端点数量、接口类/子类/协议等。一个配置可以有多个接口。
  4. 端点描述符(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访问。
USB描述符层级结构 USB描述符层级结构 设备描述符 bLength=18, bDescriptorType=0x01 1个 配置描述符 wTotalLength, bNumInterfaces 1~N个 接口描述符 bInterfaceClass, bNumEndpoints 0~N个 端点描述符 bEndpointAddress, bmAttributes 注意:端点0(默认控制端点)没有端点描述符,其最大包大小在设备描述符的bMaxPacketSize0字段中定义。 字符串描述符(iManufacturer, iProduct, iSerialNumber)为可选描述符,通过索引引用。

2.4 本章小结

  • USB包结构是总线通信的最小单元,分为令牌包、数据包和握手包三种。
  • 四种传输类型(控制、批量、中断、等时)对应不同的应用场景,驱动需根据端点属性选择。
  • USB描述符采用层级结构(设备→配置→接口→端点),Host通过枚举过程读取并解析。
  • Linux内核在ch9.h中定义了与USB规范一致的数据结构,驱动开发时直接使用。
下节预告: 第3章将深入USB枚举过程,分析Host如何通过控制传输读取描述符、分配地址、选择配置,并最终使设备进入就绪状态。