3. Linux USB子系统架构
Linux USB 子系统采用经典的三层软件架构设计,将 USB 协议栈从下到上划分为 USB 设备控制器驱动(Gadget)、主机控制器驱动(HCD) 和 USB 核心层(Core)。这种分层设计实现了硬件无关性与协议处理的解耦,使得同一套 USB 核心代码可以适配不同的主机控制器(如 EHCI、XHCI)和设备角色(Host/Device)。
3.1 USB 核心层(USB Core)
USB Core 是 Linux USB 子系统的中枢,负责管理 USB 总线、设备枚举、配置解析、驱动匹配以及 urb(USB Request Block)的调度。它不直接操作硬件,而是通过 HCD 或 Gadget 层提供的回调函数与底层交互。
核心职责:
- 设备枚举:检测 USB 设备插入/拔出,读取设备描述符、配置描述符、接口描述符等。
- 驱动匹配:根据
idVendor/idProduct或接口类代码匹配对应的 USB 设备驱动。 - 管道管理:创建、维护和销毁 USB 管道(pipe),管理端点(endpoint)的通信。
- urb 生命周期:分配、提交、取消和完成 urb,协调数据传输。
- 电源管理:处理挂起/恢复、远程唤醒等电源事件。
USB Core 在源码中位于 drivers/usb/core/ 目录,主要文件包括:
| 文件 | 功能 |
|---|---|
hub.c |
USB Hub 驱动,处理端口状态变化和设备枚举 |
usb.c |
USB 设备初始化、注册、注销 |
driver.c |
USB 驱动模型,驱动与设备的绑定/解绑 |
urb.c |
urb 分配、提交、取消和完成处理 |
message.c |
控制传输(setup 包)的发送与接收 |
3.2 主机控制器驱动(HCD)
HCD 是 USB Host 模式下直接与硬件控制器交互的软件层。它实现了 USB 主机控制器规范(如 EHCI、OHCI、UHCI、XHCI),负责将 USB Core 下发的 urb 转换为硬件可执行的传输描述符(TD),并处理中断、DMA 传输等底层操作。
Linux 内核支持四种主流 HCD 标准:
| HCD 类型 | 对应规范 | 速度支持 | 源码目录 |
|---|---|---|---|
| EHCI | Enhanced Host Controller Interface | USB 2.0 (High Speed) | drivers/usb/host/ehci-*.c |
| OHCI | Open Host Controller Interface | USB 1.1 (Full/Low Speed) | drivers/usb/host/ohci-*.c |
| UHCI | Universal Host Controller Interface | USB 1.1 (Full/Low Speed) | drivers/usb/host/uhci-*.c |
| XHCI | eXtensible Host Controller Interface | USB 3.0/2.0/1.1 (SuperSpeed+) | drivers/usb/host/xhci-*.c |
移植要点:在嵌入式平台移植 HCD 时,通常需要关注以下方面:
- 时钟和电源管理:确保 USB 控制器时钟已使能,电源域正常。
- DMA 配置:检查系统内存是否支持 USB 控制器所需的 DMA 类型(如 coherent 或 streaming DMA)。
- 中断号与触发方式:确认设备树或平台数据中中断号、中断触发类型(边沿/电平)正确。
- PHY 初始化:部分 SoC 需要额外配置 USB PHY(物理层收发器)。
3.3 设备控制器驱动(Gadget)
Gadget 层使 Linux 设备能够作为 USB 外设(Device)工作,例如模拟成 U 盘、串口、网卡或复合设备。它由三部分组成:UDC(USB Device Controller)驱动、Gadget 功能驱动和配置层(Configfs / Legacy)。
Gadget 层与 HCD 层在架构上对称,但方向相反:
| 对比维度 | HCD(主机侧) | Gadget(设备侧) |
|---|---|---|
| 角色 | Host(主设备) | Device(从设备) |
| 数据流向 | Core → HCD → 硬件(发送/接收) | 硬件 → UDC → 功能驱动(响应请求) |
| 核心数据结构 | struct usb_hcd |
struct usb_gadget |
| 传输发起方 | 主机主动发起传输 | 设备等待主机请求,被动响应 |
| 典型应用 | USB 鼠标、键盘、U 盘接入 | 模拟 U 盘、RNDIS 网卡、串口 |
3.4 三层之间的接口与数据流
USB Core 通过一组标准接口与 HCD 和 Gadget 层通信。对于 Host 模式,核心数据结构是 struct usb_hcd,其中包含 struct hc_driver 函数指针表;对于 Device 模式,核心数据结构是 struct usb_gadget,通过 struct usb_gadget_ops 与 UDC 交互。
Host 模式关键接口(hc_driver):
struct hc_driver {
const char *description;
int (*reset) (struct usb_hcd *hcd);
int (*start) (struct usb_hcd *hcd);
int (*urb_enqueue) (struct usb_hcd *hcd,
struct urb *urb, gfp_t mem_flags);
int (*urb_dequeue) (struct usb_hcd *hcd,
struct urb *urb, int status);
irqreturn_t (*irq) (struct usb_hcd *hcd);
void (*stop) (struct usb_hcd *hcd);
// ... 其他回调
};
Device 模式关键接口(usb_gadget_ops):
struct usb_gadget_ops {
int (*get_frame) (struct usb_gadget *);
int (*wakeup) (struct usb_gadget *);
int (*set_selfpowered) (struct usb_gadget *, int);
int (*vbus_session) (struct usb_gadget *, int);
int (*vbus_draw) (struct usb_gadget *, unsigned mA);
int (*pullup) (struct usb_gadget *, int);
int (*udc_start) (struct usb_gadget *,
struct usb_gadget_driver *);
int (*udc_stop) (struct usb_gadget *);
};
典型的数据流(以 Host 模式读取 U 盘为例):
- USB Core 检测到 Hub 端口状态变化,启动枚举流程。
- USB Core 通过控制传输(urb)读取设备描述符,分配地址。
- USB Core 匹配 U 盘驱动(如
usb-storage),调用驱动 probe。 - U 盘驱动 提交批量传输 urb 到 USB Core。
- USB Core 将 urb 传递给 HCD,HCD 将其转换为硬件传输描述符。
- HCD 启动 DMA 或 PIO 传输,完成后触发中断。
- HCD 中断处理函数调用 urb 完成回调,通知 USB Core 和驱动层。
常见移植陷阱:
- HCD 的
urb_enqueue回调中未正确处理 urb 的transfer_flags(如 URB_NO_TRANSFER_DMA_MAP),导致 DMA 映射错误。 - Gadget 的 UDC 驱动未实现
pullup回调,导致设备无法被主机识别。 - 在支持 OTG(On-The-Go)的平台上,未正确切换 HCD 和 Gadget 模式,导致角色协商失败。
3.5 三层模型在嵌入式移植中的意义
理解三层模型对于嵌入式 Linux USB 驱动移植至关重要:
- 代码复用:USB Core 和功能驱动(如 usb-storage、CDC 以太网)是平台无关的,移植时只需关注 HCD 或 UDC 层。
- 调试隔离:当 USB 不工作时,可以快速定位问题属于 Core 层、HCD 层还是硬件层。
- 性能优化:在 HCD 层可以调整 DMA 缓冲区大小、中断合并策略、urb 队列深度等参数。
- 电源管理:三层模型支持逐层挂起/恢复,HCD 层负责硬件寄存器操作,Core 层负责策略决策。
实践建议:在开始移植前,先通过 lsusb -t 或 cat /sys/kernel/debug/usb/devices 确认当前系统 USB 拓扑和驱动绑定情况,这有助于验证三层模型是否正常工作。