2. Linux USB驱动架构
Linux USB 子系统采用分层的软件架构设计,将复杂的 USB 协议栈抽象为四个清晰的层次。理解每一层的职责、边界以及数据流动路径,是开发稳定、高效的 USB 驱动的基础。本章将深入剖析 USB 核心层、主机控制器驱动(HCD)、设备驱动层以及 Gadget 驱动层的功能与协作机制。
架构核心思想: 分层解耦。上层驱动无需关心底层硬件的具体寄存器操作,底层驱动也无需理解上层协议的具体含义。USB 核心层作为枢纽,负责标准化数据结构和传输流程。
2.1 四层架构总览
Linux USB 驱动架构从上到下(或从主机侧到设备侧)可以分为以下四个主要层次:
- USB 设备驱动层(USB Device Driver Layer): 位于最上层,直接与用户空间交互或为内核其他子系统提供服务。它负责处理具体的 USB 设备功能,如 U 盘、键盘、鼠标、摄像头等。
- USB 核心层(USB Core Layer): 架构的枢纽,提供一组通用的 API 和数据结构,用于管理 USB 总线、设备、配置、接口、端点以及传输请求(URB)。
- USB 主机控制器驱动层(Host Controller Driver, HCD): 硬件抽象层,直接与 USB 主机控制器硬件(如 EHCI、XHCI)交互,负责完成底层的数据传输事务。
- USB Gadget 驱动层(Gadget Driver Layer): 用于设备端(Device Mode),使 Linux 设备能够模拟成 USB 外设(如虚拟串口、虚拟网卡、大容量存储设备)。
提示: 在嵌入式系统中,一个 SoC 可能同时具备主机模式(Host Mode)和设备模式(Device Mode)。HCD 层负责主机模式,而 Gadget 层负责设备模式。两者通过底层的 UDC(USB Device Controller)驱动与硬件交互。
2.2 USB 核心层(USB Core)
USB Core 是 Linux USB 子系统的“操作系统”。它不直接操作硬件,而是为上层驱动和底层 HCD 提供标准化的服务。其主要职责包括:
- 总线管理: 注册和管理 USB 总线,处理设备的热插拔事件。
- 设备枚举: 当 USB 设备插入时,负责读取设备描述符、配置描述符、接口描述符等,并为设备分配唯一的地址。
- URB 管理: 提供
usb_alloc_urb()、usb_submit_urb()、usb_kill_urb()等 API,用于管理 USB 请求块(URB)。 - 标准请求处理: 封装了 USB 规范中定义的标准设备请求(如 Get_Descriptor、Set_Configuration、Clear_Feature)。
- 驱动模型集成: 将 USB 设备、接口、驱动注册到 Linux 设备驱动模型(LDM)中,实现
probe()和disconnect()回调。
关键数据结构:
struct usb_device 代表一个 USB 设备;struct usb_interface 代表一个功能接口;struct urb 代表一次 USB 数据传输请求。
2.3 主机控制器驱动层(HCD)
HCD 是 USB 主机控制器硬件的软件抽象。不同的 USB 规范(如 USB 1.x、2.0、3.x)对应不同的主机控制器接口标准,因此 Linux 内核提供了多个 HCD 实现。
| HCD 名称 | 对应规范 | 速度支持 | 典型硬件 |
|---|---|---|---|
| UHCI | USB 1.x | 低速 (1.5 Mbps)、全速 (12 Mbps) | Intel/VIA 芯片组 |
| OHCI | USB 1.x | 低速、全速 | 非 Intel 厂商(如 SIS、ALi) |
| EHCI | USB 2.0 | 高速 (480 Mbps) | 大多数 USB 2.0 主机控制器 |
| XHCI | USB 3.x | 超高速 (5/10/20 Gbps),且向下兼容 | 现代 Intel/AMD/ARM SoC |
HCD 的核心任务是实现 struct hc_driver 结构体中定义的回调函数,包括:
start()/stop():启动和停止主机控制器。urb_enqueue()/urb_dequeue():将 URB 提交到硬件传输队列或从队列中移除。hub_control():处理根集线器的控制请求(如复位端口、获取端口状态)。
注意: 在设备树中,通常不需要为标准的 PCIe 连接的 USB 控制器配置 HCD 驱动。但对于嵌入式 SoC 内部集成的 USB 控制器,必须通过设备树节点指定 compatible 属性来匹配对应的 HCD 驱动(如
"snps,dwc3" 对应 DWC3 控制器驱动)。
2.4 USB 设备驱动层
设备驱动层是开发者最常接触的层次。一个 USB 设备驱动通常绑定到一个 struct usb_interface 上。驱动开发的核心工作是填充 struct usb_driver 结构体:
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,
};
module_usb_driver(my_usb_driver);
设备驱动层的主要工作包括:
- 匹配与探测: 通过
id_table匹配设备的 VID/PID 或类代码,匹配成功后调用probe()函数。 - 端点发现: 在
probe()中遍历接口的altsetting,获取struct usb_host_endpoint,确定传输类型(控制、批量、中断、等时)。 - 数据传输: 分配并提交 URB,实现与设备的业务逻辑交互。
- 与用户空间交互: 通过创建字符设备、输入设备、网络设备等内核接口,将数据传递给用户空间。
开发建议: 对于简单的自定义 USB 设备,可以使用
usbfs(通过 libusb 库)在用户空间完成驱动开发,避免编写内核驱动。只有当性能要求极高或需要访问内核特有资源时,才考虑编写内核态 USB 设备驱动。
2.5 USB Gadget 驱动层
Gadget 驱动层使 Linux 设备能够扮演 USB 外设的角色。与主机侧架构对称,Gadget 层也分为三个子层次:
- UDC 驱动(USB Device Controller Driver): 最底层,直接操作 SoC 内部的 USB 设备控制器硬件。类似于主机侧的 HCD。
- Gadget 核心层(Gadget Core / UDC Core): 提供通用的 API,管理端点、请求和绑定操作。类似于主机侧的 USB Core。
- Gadget 功能驱动(Gadget Function Driver): 最上层,实现具体的 USB 功能,如串口(Serial)、以太网(Ethernet)、大容量存储(Mass Storage)。
现代 Linux 内核推荐使用 ConfigFS 方式来配置 Gadget 功能,这种方式允许在用户空间动态组合和绑定功能驱动,无需重新编译内核模块。
# 通过 ConfigFS 创建 USB 虚拟串口和虚拟网卡示例
mount -t configfs none /sys/kernel/config
mkdir /sys/kernel/config/usb_gadget/g1
echo 0x1d6b > /sys/kernel/config/usb_gadget/g1/idVendor
echo 0x0104 > /sys/kernel/config/usb_gadget/g1/idProduct
mkdir /sys/kernel/config/usb_gadget/g1/strings/0x409
echo "MyCompany" > /sys/kernel/config/usb_gadget/g1/strings/0x409/manufacturer
echo "MyGadget" > /sys/kernel/config/usb_gadget/g1/strings/0x409/product
# 创建配置
mkdir /sys/kernel/config/usb_gadget/g1/configs/c.1
mkdir /sys/kernel/config/usb_gadget/g1/configs/c.1/strings/0x409
echo "Conf1" > /sys/kernel/config/usb_gadget/g1/configs/c.1/strings/0x409/configuration
# 绑定功能:串口
mkdir /sys/kernel/config/usb_gadget/g1/functions/acm.usb0
ln -s /sys/kernel/config/usb_gadget/g1/functions/acm.usb0 /sys/kernel/config/usb_gadget/g1/configs/c.1/
# 绑定功能:以太网
mkdir /sys/kernel/config/usb_gadget/g1/functions/ecm.usb0
ln -s /sys/kernel/config/usb_gadget/g1/functions/ecm.usb0 /sys/kernel/config/usb_gadget/g1/configs/c.1/
# 启用 Gadget(绑定到 UDC 驱动)
echo "musb-hdrc.0.auto" > /sys/kernel/config/usb_gadget/g1/UDC
设备树绑定注意: 在设备树中,Gadget 模式通常通过设置 USB 控制器的
dr_mode 属性为 "peripheral" 或 "otg" 来启用。例如:dr_mode = "peripheral";。同时,需要确保 UDC 驱动(如 musb-hdrc)的 compatible 属性与设备树节点匹配。
2.6 四层架构对比总结
下表从职责、数据流方向、典型驱动和硬件接口四个维度,对比主机侧和设备侧的架构层次:
| 对比维度 | 主机侧(Host) | 设备侧(Device / Gadget) |
|---|---|---|
| 顶层驱动 | USB 设备驱动(usb-storage, usbhid) | Gadget 功能驱动(g_serial, g_ether) |
| 核心层 | USB Core(urb, usb_device, usb_interface) | Gadget Core(usb_gadget, usb_ep, usb_request) |
| 底层硬件抽象 | HCD(ehci-hcd, xhci-hcd) | UDC 驱动(dwc3-udc, musb-hdrc) |
| 数据流方向 | 用户空间 → 设备驱动 → Core → HCD → 硬件 | 硬件 → UDC 驱动 → Core → 功能驱动 → 用户空间 |
| 设备树角色 | 配置主机控制器(dr_mode = "host") | 配置设备控制器(dr_mode = "peripheral") |
架构设计启示: 无论是主机侧还是设备侧,Linux USB 子系统都遵循“核心层 + 硬件抽象层 + 功能驱动层”的三层模式。这种设计使得驱动开发人员可以专注于功能逻辑,而将硬件差异和协议细节交给核心层和硬件抽象层处理。在后续章节中,我们将深入探讨如何编写一个实际的 USB 设备驱动,并为其配置设备树绑定。