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设备枚举、配置、传输管理都由核心层协调。
Linux USB 子系统分层架构 主机侧 (Host Side) 设备侧 (Device Side) 用户空间应用 / libusb 用户空间应用 / GadgetFS USB核心层 (USB Core) usb_bus_type / usb_device / usb_driver USB核心层 (USB Core) usb_gadget_probe_driver / usb_composite HCD (Host Controller Driver) struct hc_driver / urb 处理 UDC (USB Device Controller) struct usb_gadget_ops / ep ops USB主机控制器硬件 (xHCI/EHCI/OHCI) USB设备控制器硬件 (DWC3/Chipidea) USB PHY (物理层收发器) USB PHY (物理层收发器)
设计要点: 主机侧与设备侧的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/下暴露设备拓扑与属性信息。
USB核心层关键数据结构
数据结构所在文件核心作用
struct usb_deviceinclude/linux/usb.h代表一个USB设备,包含设备描述符、配置列表、端点信息
struct usb_driverinclude/linux/usb.hUSB设备驱动接口,定义probe/disconnect/id_table
struct urbinclude/linux/usb.hUSB请求块,描述一次数据传输的完整上下文
struct usb_businclude/linux/usb.h代表一条USB总线,关联HCD与根Hub
struct usb_host_configinclude/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()。
HCD 驱动注册与初始化流程 platform_driver_register() probe() 入口 PHY初始化 usb_create_hcd() 设置 hc_driver usb_add_hcd() 根Hub注册完成

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框架层次结构
层次目录/文件职责
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)
数据发起方主机软件主动发起URBGadget 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
主机侧 vs 设备侧数据流对比 主机侧数据流 USB设备驱动 USB Core HCD USB控制器硬件 设备侧数据流 Gadget Function Composite框架 UDC驱动 USB控制器硬件

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驱动。