2. Linux USB 子系统架构:核心层、主机控制器驱动与设备驱动框架

Linux USB 子系统采用分层的模块化设计,从上到下依次为 设备驱动框架、USB 核心层 (USB Core) 和 主机控制器驱动 (HCD)。这种架构将协议细节、硬件操作与设备逻辑解耦,使得开发者可以专注于各自层次的实现。

Linux USB 子系统分层架构 用户空间 (User Space) 设备驱动框架 (Device Driver Framework) USB 设备驱动 (usb_driver) / 类驱动 (usb_class_driver) USB 核心层 (USB Core) 协议管理 / 设备枚举 / 管道管理 / 电源管理 / 带宽分配 usb_device / usb_interface / urb 生命周期 主机控制器驱动 (HCD) ehci-hcd / ohci-hcd / uhci-hcd / xhci-hcd USB 主机控制器硬件 (USB Host Controller)

2.1 USB 核心层 (USB Core)

USB Core 是子系统的中枢,位于设备驱动与 HCD 之间。它提供与 USB 协议无关的通用服务,包括设备枚举、管道管理、URB 路由、电源管理和带宽计算。

核心职责:

  • 设备枚举:处理 Get_Descriptor、Set_Address、Set_Configuration 等标准请求。
  • 管道管理:维护每个端点的管道 (pipe) 对象,负责 URB 的提交与完成回调。
  • 带宽分配:为等时传输和中断传输预留总线时间片。
  • 电源管理:处理挂起/恢复、远程唤醒和选择性挂起。
  • 设备模型集成:将 USB 设备注册到 Linux 设备模型,触发驱动匹配。

USB Core 导出的关键数据结构包括 usb_device、usb_interface 和 urb。其中 usb_device 代表一个物理 USB 设备,usb_interface 代表设备中的一个功能接口,urb 是 USB 请求块,用于在驱动与 HCD 之间传递数据。

数据结构 作用 生命周期
usb_device 代表一个 USB 设备,包含设备描述符、配置、端点信息 从设备插入到拔出
usb_interface 代表设备中的一个功能接口,驱动绑定对象 从配置激活到配置切换
urb USB 请求块,封装传输方向、数据缓冲区、完成回调 从提交到完成/取消

2.2 主机控制器驱动 (HCD)

HCD 是 USB 子系统的底层软件模块,直接与硬件寄存器交互。它负责将 USB Core 下发的 URB 转换为硬件可执行的传输描述符 (TD),并处理硬件中断、完成传输、管理根集线器。

Linux 内核支持四种主流 HCD 实现,分别对应不同的 USB 规范:

HCD 驱动 对应规范 速度支持 典型硬件
uhci-hcd USB 1.1 UHCI 低速/全速 Intel/VIA 南桥
ohci-hcd USB 1.1 OHCI 低速/全速 非 Intel 平台 (S3C, OMAP)
ehci-hcd USB 2.0 EHCI 高速 (480 Mbps) 大多数 x86/ARM 平台
xhci-hcd USB 3.0/3.1 xHCI 超高速 (5/10 Gbps) 现代 x86/ARM 平台

调试提示: 在枚举失败时,首先检查 HCD 是否成功注册。使用 lsusb -t 查看设备树,确认 HCD 驱动已加载。若 HCD 未加载,检查内核配置是否启用了对应的 HCD 驱动 (如 CONFIG_USB_EHCI_HCD)。

HCD 内部结构:URB 到硬件传输描述符的转换 USB Core HCD 通用层 (hcd.c) 具体 HCD (ehci/xhci) URB → 拆分/合并 → 传输描述符 (TD) → 硬件链表 硬件中断 → 完成队列 → URB 回调 → USB Core 主机控制器寄存器 / DMA 描述符环

2.3 设备驱动框架

设备驱动框架位于 USB Core 之上,为具体的 USB 设备或设备类提供编程接口。驱动开发者通过注册 usb_driver 结构体,实现 probe 和 disconnect 回调,即可完成与 USB Core 的绑定。

设备驱动框架分为两类:

  • 通用设备驱动:针对特定 VID/PID 的设备,如 USB 转串口芯片 (FTDI、CP210x)。
  • 类驱动 (Class Driver):针对 USB 标准设备类,如 HID (人机交互设备)、Mass Storage (大容量存储)、CDC (通信设备类)。类驱动由内核提供,设备厂商只需遵循类规范即可免驱。

常见误区: 初学者常将 usb_driver 与 usb_class_driver 混淆。usb_driver 用于匹配 USB 接口并创建 usb_interface 实例,而 usb_class_driver 用于在 /dev 下创建设备节点,两者通常配合使用。

一个典型的 USB 设备驱动注册流程如下:

/* 定义 usb_device_id 表 */
static const struct usb_device_id my_table[] = {
    { USB_DEVICE(0x1234, 0x5678) },
    { } /* 终止项 */
};
MODULE_DEVICE_TABLE(usb, my_table);

/* 定义 usb_driver 结构体 */
static struct usb_driver my_driver = {
    .name       = "my_usb_driver",
    .probe      = my_probe,
    .disconnect = my_disconnect,
    .id_table   = my_table,
};

/* 模块入口注册 */
module_usb_driver(my_driver);

当 USB Core 检测到新设备时,会遍历已注册的 usb_driver 列表,调用 id_table 进行匹配。匹配成功后调用 probe 函数,驱动在此函数中完成初始化、URB 分配和端点配置。

设备驱动匹配与绑定流程 USB Core 枚举完成 遍历 usb_driver 列表 id_table 匹配 匹配失败 → 继续遍历 匹配成功 → 调用 probe() 设备就绪,URB 通信开始

2.4 三层协作:枚举流程中的角色分配

以 USB 设备插入为例,三层架构的协作流程如下:

  1. HCD 检测到端口状态变化 (DP/DM 电平变化),通知 USB Core。
  2. USB Core 启动枚举过程:复位设备、分配地址、读取设备描述符、配置描述符。
  3. USB Core 根据读取的配置信息创建 usb_device 和 usb_interface 实例。
  4. 设备驱动框架 根据 id_table 匹配驱动,调用 probe 完成初始化。
  5. 驱动通过 USB Core 提交 URB,USB Core 将 URB 路由到 HCD,最终由硬件执行。

调试切入点: 枚举失败时,通过 dmesg 观察 USB Core 的枚举日志 (如 New USB device found),可以快速定位问题发生在 HCD 层还是驱动匹配层。若没有枚举日志,问题大概率在 HCD 或硬件层。