2. USB协议基础:USB传输类型与描述符体系
USB协议采用主机-设备主从架构,所有通信均由主机(Host)发起。理解USB传输类型与描述符体系,是掌握Linux USB子系统驱动初始化流程的前提。本章将系统梳理四种传输类型的特性与适用场景,并深入解析设备、配置、接口、端点四级描述符的结构与枚举顺序。
2.1 USB传输类型
USB规范定义了四种传输类型,分别对应不同的带宽、延迟和可靠性需求。下表从关键维度进行对比:
| 传输类型 | 方向 | 带宽保证 | 错误重试 | 典型应用 | 端点类型 |
|---|---|---|---|---|---|
| 控制传输 | 双向 | 10% 保留 | 是 | 枚举、配置命令 | 控制端点 (0) |
| 批量传输 | 单向 | 无 | 是(硬件重试) | U盘、打印机数据 | 批量端点 |
| 中断传输 | 单向 | 有(轮询间隔) | 是 | 鼠标、键盘 | 中断端点 |
| 同步传输 | 单向 | 有(固定带宽) | 否 | 音频、视频流 | 同步端点 |
关键点:控制传输是USB枚举的基石。设备连接后,主机首先通过默认控制管道(端点0)获取描述符、分配地址、配置设备。所有驱动初始化都始于控制传输的成功完成。
2.1.1 控制传输
控制传输由建立阶段、数据阶段(可选)、状态阶段组成。每个USB设备必须实现端点0,用于接收标准设备请求(如GET_DESCRIPTOR、SET_ADDRESS、SET_CONFIGURATION)。
- 建立阶段:主机发送8字节Setup包,包含请求类型、请求码、值、索引、长度。
- 数据阶段:按Setup包指定的方向传输数据,长度由wLength字段决定。
- 状态阶段:设备返回握手包(ACK/NAK/STALL),确认操作结果。
驱动视角:在Linux USB核心中,
usb_control_msg() 封装了完整的控制传输流程。驱动初始化阶段频繁调用此函数读取描述符、配置设备。
2.1.2 批量传输
批量传输利用未预留的带宽,在总线空闲时发送数据。硬件自动执行错误检测与重试,保证数据完整性。适用于对实时性要求不高、但数据量大的场景。
- 批量OUT:主机→设备,用于下发数据(如写入U盘)。
- 批量IN:设备→主机,用于读取数据(如读取U盘内容)。
2.1.3 中断传输
中断传输并非真正的中断,而是主机按固定轮询间隔(bInterval字段指定)查询设备是否有数据需要传输。适用于人机交互设备(HID)。
- 轮询间隔:全速设备最小1ms,高速设备最小125μs(微帧)。
- 数据保证:硬件重试失败的数据包,确保不丢失。
2.1.4 同步传输
同步传输为实时数据流预留固定带宽,不进行错误重试。适用于音频、视频等允许少量丢包但对延迟敏感的场景。
- 无重试:数据出错直接丢弃,不阻塞后续传输。
- 带宽预留:在帧/微帧中固定分配时间片。
常见误区:同步传输不保证数据正确性,驱动开发者必须自行处理数据校验或容忍丢包。在Linux中,同步端点通常使用URB的
iso_frame_desc 数组管理每个包的传输状态。
2.2 USB描述符体系
USB描述符是设备向主机报告自身能力的结构化数据。描述符采用层级树状组织:设备描述符 → 配置描述符 → 接口描述符 → 端点描述符。主机通过标准设备请求逐层读取,完成设备识别与驱动匹配。
2.2.1 描述符层级结构
- 设备描述符(Device Descriptor):位于顶层,描述设备的通用信息,如USB规范版本、VID/PID、设备类、最大包大小(端点0)。
- 配置描述符(Configuration Descriptor):每个设备可有一个或多个配置,描述供电方式、最大电流、接口数量。主机通过SET_CONFIGURATION选择激活的配置。
- 接口描述符(Interface Descriptor):每个配置包含一个或多个接口,描述功能类别(如HID、Mass Storage、Audio)和备选设置(Alternate Setting)。
- 端点描述符(Endpoint Descriptor):每个接口包含零个或多个端点,描述传输方向、传输类型、最大包大小、轮询间隔。
驱动匹配关键:Linux USB核心通过设备描述符中的
idVendor、idProduct、bDeviceClass 以及接口描述符中的 bInterfaceClass、bInterfaceSubClass、bInterfaceProtocol 来匹配驱动。驱动开发者通常在 usb_device_id 表中声明这些匹配字段。
2.2.2 关键描述符字段解析
以下列出驱动开发中最常涉及的描述符字段:
| 描述符类型 | 字段 | 偏移 | 说明 |
|---|---|---|---|
| 设备描述符 | idVendor |
8 | 厂商ID(USB-IF分配) |
idProduct |
10 | 产品ID(厂商自定义) | |
bDeviceClass |
4 | 设备类代码(0x00表示由接口描述符定义) | |
bMaxPacketSize0 |
7 | 端点0最大包大小(8/16/32/64) | |
| 配置描述符 | bNumInterfaces |
4 | 该配置下的接口数量 |
bConfigurationValue |
5 | SET_CONFIGURATION使用的配置值 | |
bMaxPower |
7 | 最大电流(单位2mA) | |
| 接口描述符 | bInterfaceNumber |
2 | 接口索引号 |
bInterfaceClass |
5 | 接口类代码(如0x03 HID、0x08 Mass Storage) | |
bInterfaceProtocol |
7 | 接口协议代码(如0x01键盘、0x02鼠标) | |
| 端点描述符 | bEndpointAddress |
2 | 端点地址(bit7方向:0 OUT/1 IN,bit3-0端点号) |
bmAttributes |
3 | 传输类型(0控制/1同步/2批量/3中断) | |
wMaxPacketSize |
4 | 最大包大小(同步传输含附加位) |
驱动开发提示:在Linux内核中,描述符结构体定义在
include/uapi/linux/usb/ch9.h。驱动通过 usb_get_descriptor() 或直接解析URB返回数据来获取描述符信息。枚举过程中,核心层自动完成描述符解析,驱动只需关注接口描述符和端点描述符即可。
2.2.3 枚举过程中的描述符读取顺序
USB设备插入后,主机按以下顺序读取描述符:
- 获取设备描述符(前8字节):主机发送GET_DESCRIPTOR请求,读取设备描述符的前8字节,获取端点0的最大包大小(bMaxPacketSize0)。
- 复位设备:主机发送SET_ADDRESS分配唯一地址,设备进入地址状态。
- 获取完整设备描述符:使用新地址重新读取完整设备描述符(18字节)。
- 获取配置描述符:读取配置描述符(9字节),获取总长度(wTotalLength),再读取完整配置描述符(包含所有接口和端点)。
- 解析接口和端点:主机遍历配置描述符中的接口描述符和端点描述符,构建设备拓扑。
- 选择配置:主机发送SET_CONFIGURATION激活配置,设备进入配置状态,驱动开始初始化。
常见问题:如果设备描述符中的bMaxPacketSize0与实际端点0能力不匹配,主机可能无法正确读取后续描述符,导致枚举失败。驱动开发者应确保固件中该字段准确无误。
2.3 传输类型与描述符在驱动初始化中的关联
驱动初始化阶段,传输类型与描述符信息紧密耦合:
- 控制传输:驱动通过端点0发送类特定请求(如HID的SET_IDLE、Mass Storage的Bulk-Only Mass Storage Reset),这些请求定义在接口描述符的bInterfaceClass和bInterfaceProtocol中。
- 批量/中断/同步端点注册:驱动解析端点描述符后,为每个非控制端点创建URB并指定传输类型。URB的
type字段必须与端点描述符的bmAttributes一致。 - 最大包大小对齐:驱动分配DMA缓冲区时,必须考虑端点描述符中的
wMaxPacketSize,确保URB数据长度不超过该值(批量传输除外,可分割为多个事务)。
代码示例:驱动从端点描述符获取信息并初始化URB
/* 遍历接口的端点列表 */
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
struct usb_endpoint_descriptor *ep = &iface_desc->ep[i].desc;
if (usb_endpoint_is_bulk_in(ep)) {
/* 批量IN端点:用于读取数据 */
dev->bulk_in_pipe = usb_rcvbulkpipe(dev->udev,
ep->bEndpointAddress);
dev->bulk_in_size = usb_endpoint_maxp(ep);
dev->bulk_in_buf = kmalloc(dev->bulk_in_size, GFP_KERNEL);
} else if (usb_endpoint_is_int_out(ep)) {
/* 中断OUT端点:用于发送命令 */
dev->int_out_pipe = usb_sndintpipe(dev->udev,
ep->bEndpointAddress);
dev->int_out_interval = ep->bInterval;
}
}
通过本章的学习,读者应掌握USB四种传输类型的特性与选择依据,理解描述符层级结构及其在枚举过程中的读取顺序,并能够将描述符信息与驱动初始化中的URB配置对应