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;
};
| 字段 | 类型 | 说明 |
|---|---|---|
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核心的交互主要通过以下三个维度展开:
- 注册与注销:USB核心调用
usb_add_hcd()将HCD注册到系统中,HCD内部完成硬件初始化并注册根Hub。 - URB传输:USB核心通过
usb_submit_urb()下发URB,最终调用HCD的urb_enqueue回调;传输完成后HCD通过usb_hcd_giveback_urb()将URB回传给核心。 - 根Hub事件:HCD在中断处理中检测到端口状态变化,通过
usb_hcd_poll_rh_status()通知USB核心,核心随后调用hub_control获取详细状态。
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_enqueue |
HCD将URB转换为硬件传输描述符,加入硬件调度队列 |
| 调度 | 硬件DMA | 主机控制器根据调度策略自动执行传输 |
| 完成 | 中断处理 | 硬件产生中断,HCD读取传输状态,更新URB状态 |
| 回传 | usb_hcd_giveback_urb |
HCD调用此函数将URB回传给USB核心,触发完成回调 |
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驱动的必备知识。