4. USB主机控制器驱动(HCD)层:HCD接口、hcd结构体、与USB核心的交互机制

USB主机控制器驱动(Host Controller Driver, HCD)是Linux USB子系统的底层核心,直接与硬件寄存器打交道,向上为USB核心层提供统一的硬件抽象接口。本章深入剖析HCD层的设计哲学、核心数据结构struct usb_hcd以及它与USB核心的交互机制。

4.1 HCD层的分层定位与职责

HCD层位于USB子系统的最底层,直接操作主机控制器(如EHCI、OHCI、UHCI、xHCI)的硬件寄存器。其核心职责包括:

  • 硬件初始化与去初始化:配置主机控制器寄存器、分配DMA缓冲区、设置中断。
  • 根Hub模拟:在软件层面模拟一个USB Hub,处理端口状态变化、复位、使能等操作。
  • URB传输管理:将USB核心下发的URB(USB Request Block)转换为硬件可执行的传输描述符(TD/TRB)。
  • 中断处理:响应硬件中断,完成URB回调,通知USB核心层。
关键概念: HCD是唯一直接与硬件交互的软件层,USB核心和USB设备驱动完全通过HCD的抽象接口访问硬件,不感知具体的主机控制器类型。

4.2 struct usb_hcd 结构体深度解析

struct usb_hcd是HCD层的核心数据结构,定义在include/linux/usb/hcd.h中。它封装了主机控制器的所有软件状态和硬件操作函数指针。

struct usb_hcd {
    /* 指向主机控制器硬件操作函数集 */
    const struct hc_driver *driver;

    /* 本HCD对应的USB总线编号 */
    int    bus_num;

    /* 根Hub设备指针 */
    struct usb_device *self;

    /* 根Hub的端口状态位图 */
    unsigned long   port_status[0];

    /* DMA掩码 */
    u64     dma_mask;

    /* 中断号 */
    unsigned    irq;

    /* 标志位:是否已经启动 */
    unsigned    flags;

    /* 私有数据,由具体HCD驱动使用 */
    void *hcd_priv;
};
struct usb_hcd 关键字段说明
字段 类型 说明
driver const struct hc_driver * 指向硬件操作函数集,包含start、stop、urb_enqueue、urb_dequeue等回调
self struct usb_device * 根Hub设备实例,用于向USB核心层注册根Hub
port_status unsigned long[] 位图数组,每个bit对应一个根Hub端口的状态变化标志
irq unsigned int 主机控制器使用的中断号
hcd_priv void * 私有数据指针,具体HCD驱动(如ehci_hcd)通过此字段存放硬件寄存器基址等

4.3 hc_driver 操作函数集

struct hc_driver定义了HCD必须实现的一组回调函数,USB核心通过这组函数与HCD交互。核心函数包括:

  • start():启动主机控制器,配置硬件寄存器,使能中断。
  • stop():停止主机控制器,释放资源。
  • urb_enqueue():将一个URB加入硬件传输队列。
  • urb_dequeue():从传输队列中移除一个URB。
  • hub_status_data():返回根Hub端口状态变化位图。
  • hub_control():处理根Hub的控制请求(如GetPortStatus、SetPortFeature)。
实践提示: 编写新的HCD驱动时,最少需要实现start、stop、urb_enqueue、urb_dequeue、hub_status_data和hub_control六个回调。USB核心通过usb_add_hcd()注册HCD后,会自动调用start。

4.4 HCD与USB核心的交互流程

HCD与USB核心的交互主要通过以下三个维度展开:

  1. 注册与注销:USB核心调用usb_add_hcd()将HCD注册到系统中,HCD内部完成硬件初始化并注册根Hub。
  2. URB传输:USB核心通过usb_submit_urb()下发URB,最终调用HCD的urb_enqueue回调;传输完成后HCD通过usb_hcd_giveback_urb()将URB回传给核心。
  3. 根Hub事件:HCD在中断处理中检测到端口状态变化,通过usb_hcd_poll_rh_status()通知USB核心,核心随后调用hub_control获取详细状态。
HCD与USB核心交互架构图 USB Core HCD (struct usb_hcd) Host Controller 硬件 usb_add_hcd / usb_remove_hcd urb_enqueue / urb_dequeue 中断处理函数 硬件中断 根Hub状态轮询 hub_status_data 交互要点 HCD不直接调用USB核心函数 通过回调函数和URB回传机制解耦

4.5 根Hub模拟机制

根Hub(Root Hub)是HCD在软件层面模拟的USB Hub,它使得USB核心可以像管理普通Hub一样管理主机控制器的端口。根Hub模拟的关键在于:

  • 端口状态寄存器映射:HCD将硬件端口状态寄存器映射为根Hub的端口状态。
  • 状态变化检测:HCD在中断处理中读取端口状态变化寄存器,设置port_status位图。
  • 轮询机制:USB核心定期调用hub_status_data检查端口变化,然后通过hub_control获取详细状态。
注意: 根Hub的端口号与硬件端口号一一对应。例如,一个4端口的主机控制器,根Hub也有4个端口。HCD必须确保hub_status_data返回的位图与硬件端口状态变化寄存器一致,否则USB核心无法正确检测设备插拔。

4.6 URB生命周期在HCD层的流转

一个URB从提交到完成,在HCD层经历以下阶段:

URB在HCD层的生命周期
阶段 调用函数 说明
提交 urb_enqueue HCD将URB转换为硬件传输描述符,加入硬件调度队列
调度 硬件DMA 主机控制器根据调度策略自动执行传输
完成 中断处理 硬件产生中断,HCD读取传输状态,更新URB状态
回传 usb_hcd_giveback_urb HCD调用此函数将URB回传给USB核心,触发完成回调
URB在HCD层的生命周期 1. 提交 2. 调度 3. 完成 4. 回传 各阶段关键操作 1. 提交:urb_enqueue → 分配DMA缓冲区 → 填充传输描述符 2. 调度:硬件自动执行,无需软件干预 3. 完成:中断处理 → 读取传输状态 → 更新URB状态 4. 回传:usb_hcd_giveback_urb → 释放DMA缓冲区 → 调用完成回调

4.7 HCD与USB核心的接口函数

USB核心提供了一组辅助函数,供HCD在实现回调时调用:

  • usb_hcd_giveback_urb():将完成的URB回传给USB核心,必须在HCD的完成处理中调用。
  • usb_hcd_poll_rh_status():通知USB核心根Hub端口状态发生变化,触发轮询。
  • usb_hcd_link_urb_to_ep() / usb_hcd_unlink_urb_from_ep():管理URB与端点的关联,用于并发控制。
  • usb_hcd_check_unlink_urb():检查URB是否可以被取消,避免竞态条件。
最佳实践: 在HCD的urb_enqueue回调中,必须先调用usb_hcd_link_urb_to_ep()将URB关联到端点,然后进行硬件调度。在完成处理中,先调用usb_hcd_unlink_urb_from_ep()解除关联,再调用usb_hcd_giveback_urb()回传。这个顺序保证了URB生命周期管理的正确性。

4.8 本章小结

HCD层是USB子系统的硬件抽象层,通过struct usb_hcd和struct hc_driver实现了与USB核心的解耦。根Hub模拟机制使得USB核心可以统一管理所有端口,URB生命周期管理确保了传输的可靠性和并发安全。理解HCD层的设计,是深入掌握Linux USB子系统的基础,也是编写或移植HCD驱动的必备知识。