2、Linux USB子系统架构:核心层(usbcore)、HCD层、设备驱动层、USBFIO与URB机制
Linux USB子系统采用分层的模块化架构设计,从上到下依次为:设备驱动层、核心层(usbcore)、HCD层(Host Controller Driver),以及底层的USB主机控制器硬件。这种分层设计使得驱动开发人员只需关注自己所在的层级,通过标准化的API与相邻层交互,极大地提高了代码复用性和可移植性。
2.1 核心层 (usbcore)
usbcore是USB子系统的中枢神经,负责所有与USB协议栈管理相关的核心任务。它不直接操作硬件,而是通过定义好的接口调用HCD层的回调函数来完成底层通信。usbcore的主要职责包括:
- 总线管理: 维护USB总线拓扑结构,处理设备的热插拔事件(插入/拔出)。
- 设备枚举: 为新插入的设备分配地址,读取设备描述符、配置描述符、接口描述符和端点描述符。
- 资源分配: 管理USB带宽、传输类型(控制/批量/中断/等时)和端点地址。
- 驱动匹配: 根据设备的VID/PID或类代码,将设备与对应的设备驱动进行绑定。
- URB管理: 接收来自设备驱动的URB请求,将其路由到正确的HCD进行处理。
2.2 HCD层 (Host Controller Driver)
HCD层是软件与硬件之间的桥梁,直接操作USB主机控制器的寄存器。Linux内核针对不同的控制器规范实现了不同的HCD驱动:
| 控制器规范 | Linux HCD驱动 | 速度支持 | 典型应用 |
|---|---|---|---|
| UHCI (Universal Host Controller Interface) | uhci-hcd | USB 1.1 (低速/全速) | Intel/VIA 芯片组,软件复杂度高 |
| OHCI (Open Host Controller Interface) | ohci-hcd | USB 1.1 (低速/全速) | 非Intel平台,硬件复杂度高 |
| EHCI (Enhanced Host Controller Interface) | ehci-hcd | USB 2.0 (高速) | USB 2.0 标准控制器 |
| xHCI (eXtensible Host Controller Interface) | xhci-hcd | USB 3.0/3.1/3.2 (超高速/超高速+) | 现代PC和嵌入式平台 |
HCD层需要实现usbcore定义的一组标准回调函数,主要包括:
urb_enqueue():将一个URB提交到硬件处理队列。urb_dequeue():取消一个正在处理或等待的URB。endpoint_disable():禁用某个端点。get_frame_number():获取当前微帧/帧号。hub_status_data():读取根集线器的状态变化。
2.3 设备驱动层
设备驱动层位于USB子系统的最上层,直接与用户空间或内核其他子系统交互。Linux内核为常见的USB设备类提供了通用的驱动框架,开发者只需实现少量回调即可完成驱动开发:
- USB Mass Storage类: 使用
usb-storage驱动,通过SCSI命令集访问U盘、移动硬盘。 - HID类: 使用
usbhid驱动,支持鼠标、键盘、游戏手柄等输入设备。 - CDC类: 使用
cdc_acm、cdc_ether等驱动,支持USB串口、USB网卡。 - Video类: 使用
uvcvideo驱动,支持USB摄像头。 - Audio类: 使用
snd-usb-audio驱动,支持USB声卡。
设备驱动的核心工作是填充struct usb_driver结构体,并实现probe()和disconnect()回调:
static struct usb_driver my_usb_driver = {
.name = "my_usb_device",
.probe = my_usb_probe,
.disconnect = my_usb_disconnect,
.id_table = my_usb_id_table,
};
static int my_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *iface_desc = intf->cur_altsetting;
// 解析端点信息
for (int i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
struct usb_endpoint_descriptor *ep = &iface_desc->endpoint[i].desc;
// 分配URB并提交
}
return 0;
}
struct usb_driver框架和probe/disconnect热插拔模型。在RTOS中,通常没有自动的设备枚举和驱动匹配机制,开发者需要手动调用枚举函数,并在检测到设备插入后手动创建设备任务和端点管道。
2.4 USBFIO 机制
USBFIO (USB Framework I/O) 是Linux USB子系统提供的一个上层I/O抽象层,它封装了URB的创建、提交和回收过程,为设备驱动提供了更简洁的读写接口。USBFIO的核心思想是“发起I/O请求,等待完成”,屏蔽了URB的异步细节。
USBFIO提供的主要API包括:
| API 函数 | 功能描述 | 同步/异步 |
|---|---|---|
usb_bulk_msg() |
发送/接收批量传输数据 | 同步(阻塞等待) |
usb_control_msg() |
发送控制传输请求 | 同步(阻塞等待) |
usb_interrupt_msg() |
发送/接收中断传输数据 | 同步(阻塞等待) |
usb_alloc_urb() |
分配一个URB结构体 | — |
usb_submit_urb() |
提交URB到usbcore | 异步 |
usb_kill_urb() |
强制终止一个URB | — |
usb_bulk_msg)内部会创建一个URB、提交它,然后调用wait_for_completion()等待完成。这种方式简化了驱动开发,但牺牲了灵活性。对于需要高吞吐量或低延迟的场景(如USB摄像头、高速数据采集),驱动应直接使用URB异步接口。
2.5 URB 机制
URB (USB Request Block) 是Linux USB子系统中数据传输的核心载体,类似于网络子系统中的sk_buff。每个URB描述了一次完整的USB数据传输请求,包含目标设备、端点、传输方向、数据缓冲区、回调函数等关键信息。
URB的生命周期包含以下关键阶段:
- 分配: 驱动调用
usb_alloc_urb()分配URB结构体。 - 填充: 设置URB的目标端点、传输类型、数据缓冲区指针、回调函数等。
- 提交: 调用
usb_submit_urb()将URB提交给usbcore,usbcore将其路由到对应的HCD。 - 调度: HCD将URB转换为硬件描述符,放入主机控制器的处理链表。
- 执行: 主机控制器通过DMA或PIO方式完成数据传输。
- 完成: 硬件产生中断,HCD在中断处理中调用URB的回调函数,通知驱动传输完成。
- 回收: 驱动在回调中处理数据后,调用
usb_free_urb()释放URB。
URB结构体的核心字段如下:
struct urb {
struct list_head urb_list; // 链表节点
struct usb_device *dev; // 目标USB设备
unsigned int pipe; // 管道信息(端点+方向+类型)
int status; // 传输状态
unsigned int transfer_flags; // 传输标志
void *transfer_buffer; // 数据缓冲区
dma_addr_t transfer_dma; // DMA缓冲区地址
int transfer_buffer_length; // 缓冲区长度
int actual_length; // 实际传输长度
unsigned char *setup_packet; // 控制传输的setup包
usb_complete_t complete; // 完成回调函数
void *context; // 回调上下文
int interval; // 中断/等时传输的轮询间隔
int number_of_packets; // 等时传输的包数量
struct usb_iso_packet_descriptor iso_frame_desc[];
};
complete回调替换为RTOS的消息队列或事件标志组,将urb_list替换为RTOS的双向链表实现,并将DMA缓冲区的管理适配到RTOS的内存管理框架中。
2.6 各层协作流程总结
当用户空间程序通过read()系统调用读取USB鼠标数据时,整个USB子系统的协作流程如下:
- 用户空间调用
read()→ 进入设备驱动层的usbhid驱动。 - 设备驱动层调用
usb_submit_urb()提交一个中断传输URB → 进入usbcore。 - usbcore根据URB中的设备/端点信息,找到对应的HCD驱动 → 调用
urb_enqueue()。 - HCD层将URB转换为硬件中断传输描述符(如EHCI的qTD),写入主机控制器的周期性链表 → 启动调度。
- 硬件在下一个微帧中执行中断传输,将鼠标数据通过DMA写入内存 → 产生中断。
- HCD层中断处理函数读取中断状态,找到完成的URB → 调用URB的
complete回调。 - 设备驱动层回调函数处理数据(解析HID报告) → 唤醒等待的
read()进程。 - 用户空间获得鼠标数据。
- 分层简化: RTOS通常将usbcore和HCD合并,减少层间调用开销。
- 同步机制: Linux使用
wait_for_completion()和wake_up(),RTOS使用信号量/事件组。 - 内存管理: Linux使用slab分配器,RTOS使用静态内存池或堆分配。
- 中断处理: Linux将中断分为上半部和下半部(tasklet/workqueue),RTOS通常在ISR中直接发送消息给任务。
- 驱动模型: Linux有自动的
probe/disconnect热插拔机制,RTOS需要手动轮询或依赖GPIO中断检测设备插入。