1. 课程导入
USB(Universal Serial Bus)自 1996 年问世以来,已成为嵌入式系统与 PC 外设最通用的连接标准。在 Linux 内核中,USB 子系统是一个高度分层、模块化的软件栈,其核心设计目标是:在保持主机-设备拓扑结构不变的前提下,为不同类别的设备提供统一的驱动接口。本章作为课程的开篇,将系统梳理 USB 协议栈的架构层次、主机控制器与设备的角色分工,并重点阐明 URB(USB Request Block) 在 USB 通信中的核心地位——它是驱动与硬件之间传递数据的最小工作单元,也是后续批量传输性能测试的基石。
1.1 USB 协议栈架构
Linux USB 子系统采用经典的 分层架构,从上到下依次为:USB 设备驱动层、USB 核心层(USBCore)、主机控制器驱动层(HCD),以及底层的 硬件主机控制器。每一层都封装了特定的职责,并通过标准接口与相邻层交互。
下面的 SVG 图展示了 USB 协议栈的完整分层结构,以及数据流在层与层之间的传递路径:
各层的主要职责与交互接口如下表所示:
| 层次 | 主要职责 | 关键数据结构 / 接口 |
|---|---|---|
| 用户空间 | 通过 libusb 或 sysfs 发起 I/O 请求,不直接接触内核 URB | libusb_transfer → 内核 URB 转换 |
| USB 设备驱动 | 实现具体设备协议(如存储、串口、HID),分配并提交 URB | usb_fill_bulk_urb() / usb_submit_urb() |
| USB 核心层 | 管理设备枚举、配置选择、URB 路由、电源管理 | struct usb_device / struct usb_host_endpoint |
| 主机控制器驱动 | 将 URB 转换为硬件特定的 TD(Transfer Descriptor) | struct urb → struct ehci_qtd / struct xhci_trb |
| 硬件控制器 | 在 USB 总线上执行实际的数据包传输 | 寄存器级 DMA 操作 / 帧微帧调度 |
1.2 主机控制器与设备角色
USB 总线采用 主从式拓扑:主机(Host) 始终作为总线主控,负责发起所有事务;设备(Device) 只能被动响应主机的请求。这种设计简化了设备侧的硬件复杂度,但也将调度压力全部集中到了主机控制器上。
1.2.1 主机控制器的类型与演进
Linux 内核支持四种主流主机控制器规范,它们在寄存器接口、调度粒度和传输性能上存在显著差异:
| 规范 | 总线类型 | 速度支持 | 调度粒度 | 典型驱动 |
|---|---|---|---|---|
| UHCI | PCI | USB 1.1 (Full/Low) | 1 ms 帧 | uhci-hcd |
| OHCI | PCI / 嵌入式 | USB 1.1 (Full/Low) | 1 ms 帧 | ohci-hcd |
| EHCI | PCI | USB 2.0 (High) | 125 µs 微帧 | ehci-hcd |
| XHCI | PCI / ACPI | USB 3.x (SuperSpeed+) | 125 µs 微帧 + 流式传输 | xhci-hcd |
下面的 SVG 图展示了主机控制器与设备之间的角色关系,以及 URB 在其中的流动路径:
1.3 URB 在 USB 通信中的核心地位
URB(USB Request Block) 是 Linux USB 子系统中最核心的数据结构,定义在 <linux/usb.h> 中。它抽象了一次完整的 USB 数据传输请求,包含了从设备驱动到硬件控制器的所有必要信息。可以毫不夸张地说:USB 设备驱动的本质,就是 URB 的管理器。
1.3.1 URB 的关键字段
一个 URB 实例包含以下关键成员,它们直接决定了传输的行为和性能:
| 字段 | 类型 | 说明 |
|---|---|---|
dev |
struct usb_device * |
目标 USB 设备 |
pipe |
unsigned int |
端点管道信息(含方向、类型、端点号) |
transfer_buffer |
void * |
数据缓冲区指针(DMA 安全) |
transfer_buffer_length |
int |
缓冲区长度(字节) |
transfer_flags |
unsigned int |
传输标志(如 URB_NO_TRANSFER_DMA_MAP) |
complete |
usb_complete_t |
完成回调函数(在中断上下文或 tasklet 中执行) |
context |
void * |
回调上下文(通常指向驱动私有数据) |
actual_length |
int |
实际传输的字节数(在完成回调中读取) |
status |
int |
传输状态(0 表示成功,负值表示错误) |
interval |
int |
中断/等时传输的轮询间隔(毫秒或微帧) |
1.3.2 URB 的生命周期
一个典型的 URB 从创建到销毁,经历以下阶段:
- 分配: 使用
usb_alloc_urb()分配 URB 实例,传入 iso_packets 数量(非等时传输传 0)。 - 填充: 根据传输类型调用对应的填充函数:
usb_fill_control_urb()— 控制传输usb_fill_bulk_urb()— 批量传输usb_fill_int_urb()— 中断传输- 等时传输需要手动设置
urb->iso_frame_desc[]
- 提交: 调用
usb_submit_urb()将 URB 交给 USBCore。此函数可能睡眠(GFP_KERNEL)或在原子上下文中调用(GFP_ATOMIC)。 - 调度与执行: USBCore 将 URB 路由到对应的 HCD,HCD 将其转换为硬件事务描述符,由主机控制器在总线上执行。
- 完成回调: 传输完成后(成功或失败),HCD 调用 URB 的
complete回调函数。驱动在此回调中检查urb->status和urb->actual_length。 - 回收: 驱动调用
usb_free_urb()释放 URB 内存,或在完成回调中重用 URB(通过usb_submit_urb()再次提交)。
下面的 SVG 图展示了 URB 从分配到完成的完整生命周期,以及各阶段涉及的关键函数:
1.3.3 URB 与批量传输的关系
在 USB 规范定义的四种传输类型中,批量传输(Bulk Transfer) 与 URB 的绑定最为紧密:
- 批量传输特点: 非周期性、大块数据、硬件级错误重传(CRC 校验 + 自动重试)、无带宽保证。
- URB 映射: 一个批量 URB 对应一次或多次批量事务(取决于端点支持的最大包大小)。驱动通过
usb_fill_bulk_urb()设置端点、缓冲区和回调。 - 性能关键指标:
- URB 提交频率: 每秒提交的 URB 数量直接影响吞吐量。
- URB 大小: 批量传输建议使用端点支持的最大包大小(USB 2.0 为 512 字节,USB 3.0 为 1024 字节)的整数倍。
- URB 队列深度: 同时提交多个 URB 到同一个端点,可以充分利用硬件流水线。
1.4 本章小结
本章建立了 USB 子系统的基础认知框架:
- 分层架构: 设备驱动 → USBCore → HCD → 硬件,每一层都有明确的职责边界。
- 主从角色: 主机控制器始终是总线主控,设备被动响应;不同主机控制器(UHCI/OHCI/EHCI/XHCI)的调度机制影响传输性能。
- URB 核心地位: URB 是 USB 通信的最小工作单元,其分配、填充、提交、完成回调、回收构成了驱动开发的主循环。批量传输的性能优化,本质上是对 URB 生命周期的精细控制。