2. Linux USB驱动框架:USB核心、HCD、UDC、Gadget框架简介
Linux USB子系统采用分层、模块化的架构设计,将主机侧与设备侧的驱动逻辑解耦。理解这一框架是进行USB控制器驱动开发、设备树绑定以及PHY初始化的前提。本章从全局视角剖析USB核心、主机控制器驱动(HCD)、设备控制器驱动(UDC)以及Gadget框架的职责与协作关系。
2.1 USB子系统整体分层架构
Linux USB驱动栈从顶层到底层可划分为四个主要层次,每一层都通过标准的内核API与相邻层交互。下图展示了主机侧与设备侧完整的软件栈结构。
核心概念: USB核心层(USB Core)是整个子系统的枢纽,它向上提供统一的文件系统接口(通过usbfs或sysfs),向下抽象出HCD与UDC的通用操作集。所有USB设备枚举、配置、传输管理都由核心层协调。
设计要点: 主机侧与设备侧的USB核心层共享同一套代码(drivers/usb/core/),但通过不同的注册接口(usb_register() vs usb_gadget_probe_driver())区分角色。PHY层在两侧都独立存在,由设备树中的phy-handle属性绑定。
2.2 USB核心层(USB Core)
USB核心层位于drivers/usb/core/目录下,是USB子系统的中枢神经系统。它负责以下关键职责:
- 总线管理: 维护usb_bus_type总线,管理usb_device与usb_driver的匹配与绑定。
- 设备枚举: 实现完整的USB 2.0/3.0枚举流程,包括获取设备描述符、配置描述符、分配地址。
- URB处理: 提供usb_submit_urb()、usb_kill_urb()等API,管理USB请求块的生命周期。
- 标准请求: 封装控制传输的标准设备请求(GetDescriptor、SetConfiguration等)。
- sysfs接口: 在/sys/bus/usb/下暴露设备拓扑与属性信息。
| 数据结构 | 所在文件 | 核心作用 |
|---|---|---|
| struct usb_device | include/linux/usb.h | 代表一个USB设备,包含设备描述符、配置列表、端点信息 |
| struct usb_driver | include/linux/usb.h | USB设备驱动接口,定义probe/disconnect/id_table |
| struct urb | include/linux/usb.h | USB请求块,描述一次数据传输的完整上下文 |
| struct usb_bus | include/linux/usb.h | 代表一条USB总线,关联HCD与根Hub |
| struct usb_host_config | include/linux/usb.h | 解析后的配置描述符及关联接口/端点数组 |
2.3 HCD(Host Controller Driver)框架
HCD是主机控制器驱动的标准抽象层,位于drivers/usb/host/目录。它向下直接操作USB主机控制器寄存器,向上通过usb_hcd结构体向核心层注册。Linux支持四种主流主机控制器标准:
- xHCI (eXtensible Host Controller Interface): USB 3.0/3.1/3.2标准,兼容USB 2.0/1.1,当前主流方案。
- EHCI (Enhanced Host Controller Interface): USB 2.0高速标准,通常与OHCI/UHCI配合使用。
- OHCI (Open Host Controller Interface): USB 1.1标准,主要用于嵌入式平台。
- UHCI (Universal Host Controller Interface): USB 1.1标准,Intel主导,软件复杂度高于OHCI。
HCD初始化流程: 在probe()中调用usb_create_hcd()分配hcd结构体,设置hcd->driver指向具体的hc_driver操作集,然后调用usb_add_hcd()启动控制器。PHY初始化通常在usb_add_hcd()之前完成,通过devm_usb_get_phy()获取PHY句柄并调用phy_init()/phy_power_on()。
2.4 UDC(USB Device Controller)框架
UDC是设备侧的核心驱动框架,位于drivers/usb/gadget/udc/目录。它负责管理USB设备控制器硬件,使嵌入式设备能够作为USB外设(如虚拟串口、U盘、以太网卡)与主机通信。UDC框架的核心抽象包括:
- struct usb_gadget: 代表一个USB设备控制器实例,包含端点列表、速度能力、电源管理状态。
- struct usb_gadget_ops: 控制器操作函数集,包括udc_start()、udc_stop()、pullup()、vbus_session()等。
- struct usb_ep: 抽象一个USB端点,提供queue()、dequeue()、enable()、disable()等操作。
- struct usb_request: 对应主机侧的URB,描述一次端点数据传输请求。
常见误区: UDC驱动不直接处理USB标准请求(如GetDescriptor)。这些请求由Gadget Function驱动或Composite框架解析。UDC只负责将硬件中断转化为usb_gadget_generic_ops回调,并将数据通过端点队列传递到上层。
2.5 Gadget框架与Composite设备
Gadget框架是设备侧USB功能驱动的实现层,位于drivers/usb/gadget/目录。它允许开发者将多个USB功能(Function)组合成一个复合设备(Composite Device)。Gadget框架分为三个层次:
| 层次 | 目录/文件 | 职责 |
|---|---|---|
| Gadget Function驱动 | drivers/usb/gadget/function/ | 实现具体USB功能,如f_serial.c(串口)、f_mass_storage.c(U盘)、f_ecm.c(以太网) |
| Composite框架 | drivers/usb/gadget/composite.c | 管理多个Function的绑定、配置描述符生成、字符串描述符管理 |
| Gadget UDC驱动 | drivers/usb/gadget/udc/ | 直接操作硬件控制器,实现usb_gadget_ops |
典型的Gadget驱动初始化流程如下:
// 1. 定义USB设备描述符
static struct usb_device_descriptor device_desc = {
.bLength = sizeof(device_desc),
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = 0,
.idVendor = cpu_to_le16(0x1d6b), // Linux Foundation
.idProduct = cpu_to_le16(0x0104),
};
// 2. 定义Composite绑定结构
static struct usb_composite_driver my_gadget_driver = {
.name = "g_mygadget",
.dev = &device_desc,
.strings = dev_strings,
.bind = my_bind,
.unbind = my_unbind,
};
// 3. 注册Composite驱动
static int __init my_init(void)
{
return usb_composite_probe(&my_gadget_driver);
}
module_init(my_init);
实战提示: 在设备树中,UDC控制器节点通常通过dr_mode属性指定工作模式(host/peripheral/otg)。当dr_mode = "peripheral"时,内核只加载UDC驱动;当dr_mode = "otg"时,需要根据ID引脚状态动态切换角色。PHY初始化在两种模式下都需要进行,但PHY的配置参数(如电压摆幅、终端阻抗)可能不同。
2.6 主机侧与设备侧的数据流对比
理解数据在USB栈中的流动方向是驱动开发的基础。下表对比了主机侧(HCD)与设备侧(UDC/Gadget)的数据路径差异:
| 对比维度 | 主机侧 (HCD) | 设备侧 (UDC/Gadget) |
|---|---|---|
| 数据发起方 | 主机软件主动发起URB | Gadget Function准备数据,等待主机IN令牌 |
| 传输方向 | URB指定方向(OUT/IN) | 端点方向由描述符固定(IN/OUT) |
| 调度方式 | HCD通过周期/异步列表调度 | UDC响应主机令牌,被动传输 |
| 错误处理 | URB回调报告错误状态 | usb_request的status字段返回结果 |
| DMA管理 | HCD负责DMA映射与同步 | UDC驱动处理DMA缓冲区 |
| 电源管理 | 通过usb_hcd结构体管理 | 通过usb_gadget->ops->suspend/resume |
2.7 设备树中的USB控制器绑定
设备树是嵌入式Linux平台描述USB控制器硬件拓扑的标准方式。一个典型的USB控制器设备树节点包含以下关键属性:
&usb1 {
compatible = "snps,dwc3";
reg = <0x0 0x31000000 0x0 0x10000>;
interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
dr_mode = "otg"; // 工作模式:host/peripheral/otg
phy-names = "usb2-phy", "usb3-phy";
phys = <&usb2_phy>, <&usb3_phy>;
maximum-speed = "super-speed"; // 限制最高速率
snps,dis_u2_susphy_quirk; // 关闭USB2.0挂起PHY
snps,dis_u3_susphy_quirk; // 关闭USB3.0挂起PHY
status = "okay";
};
关键绑定解析: phy-names与phys属性成对出现,用于将PHY设备绑定到控制器。内核通过of_phandle_args解析phys属性,调用phy_init()/phy_power_on()完成初始化。dr_mode属性决定内核加载HCD还是UDC驱动,或同时加载OTG驱动。