2. USB Gadget框架简介:Linux Gadget子系统架构、UDC驱动层、Gadget功能驱动层

在嵌入式Linux系统中,USB Gadget框架使得设备能够扮演USB外设(Device/Peripheral)角色,例如模拟成U盘、虚拟串口、以太网卡或音频设备。理解其分层架构是进行Gadget驱动开发的基础。本章将剖析Linux Gadget子系统的三层模型:Gadget功能驱动层、UDC(USB Device Controller)驱动层以及连接二者的核心层(Gadget API)。

核心概念: Linux Gadget框架的设计哲学是“功能与控制器分离”。功能驱动(如File-backed Storage、Ethernet)只需关注USB协议的高层逻辑,而底层硬件操作(如寄存器读写、DMA传输)由UDC驱动封装。这种分层极大提升了代码复用性和可移植性。

2.1 三层架构总览

下图展示了Linux Gadget子系统的完整软件栈。用户空间通过ConfigFS或Legacy Gadget API与内核交互,内核内部则分为三个清晰的层次。

Linux USB Gadget 子系统三层架构 用户空间 (User Space) ConfigFS (/sys/kernel/config/usb_gadget/) | Legacy Gadget API (g_*.ko) Gadget 功能驱动层 (Function Drivers) g_mass_storage (U盘) | g_serial (虚拟串口) | g_ether (RNDIS/ECM) | g_audio | g_hid g_printer | g_cdc | g_multi (复合设备) | 自定义Function 实现: usb_function 结构体 + 端点管理 + 数据传输回调 Gadget 核心层 (Gadget API / UDC Core) usb_gadget_driver | usb_gadget | usb_ep | usb_request 提供标准API:usb_ep_queue() / usb_ep_dequeue() / usb_gadget_connect() UDC 驱动层 (Device Controller Drivers) dwc2 (Raspberry Pi / Allwinner) | dwc3 (高通/瑞芯微) | chipidea (NXP i.MX) musb (TI OMAP) | fsl_qe (Freescale) | net2280 (PCIe) 实现: usb_gadget_ops 结构体 + 中断处理 + 端点寄存器操作 硬件层:USB Device Controller (DWC2 / DWC3 / ChipIdea ...)
开发提示: 在调试Gadget驱动时,通常需要同时关注三个层次:功能驱动是否注册成功?核心层是否将端点映射正确?UDC驱动是否检测到VBUS并完成枚举?使用 lsusb -t 和 cat /sys/kernel/debug/usb/gadget 可以快速定位问题所在层。

2.2 UDC驱动层:硬件抽象与端点管理

UDC驱动是Gadget框架中最接近硬件的部分。它直接操作USB Device Controller的寄存器,负责处理USB总线事件(复位、挂起、恢复)、管理端点(Endpoint)以及处理数据传输中断。

2.2.1 UDC驱动的核心数据结构

每个UDC驱动都需要实现 usb_gadget_ops 结构体,该结构体定义了控制器级别的操作回调。

// include/linux/usb/gadget.h
struct usb_gadget_ops {
    int     (*get_frame)(struct usb_gadget *);
    int     (*wakeup)(struct usb_gadget *);
    int     (*set_selfpowered)(struct usb_gadget *, int);
    int     (*vbus_session)(struct usb_gadget *, int);
    int     (*vbus_draw)(struct usb_gadget *, unsigned mA);
    int     (*pullup)(struct usb_gadget *, int);
    int     (*ioctl)(struct usb_gadget *, unsigned, unsigned long);
    int     (*get_config_params)(struct usb_gadget *, struct usb_dcd_config_params *);
    int     (*udc_start)(struct usb_gadget *, struct usb_gadget_driver *);
    int     (*udc_stop)(struct usb_gadget *);
    // ... 其他可选回调
};
UDC驱动关键回调函数说明
回调函数触发时机典型实现
udc_startGadget功能驱动注册时使能控制器时钟、初始化端点、连接D+上拉电阻
udc_stopGadget功能驱动注销时断开D+上拉、复位端点状态、关闭时钟
pullup用户使能/禁用设备时控制D+上拉电阻,通知主机设备连接/断开
vbus_draw配置描述符指定电流时设置控制器VBUS电流限制(用于自供电/总线供电切换)

2.2.2 端点管理与DMA传输

UDC驱动需要为每个物理端点创建 usb_ep 实例,并实现端点操作函数。端点0(控制端点)由核心层自动管理,功能驱动使用非0端点进行数据传输。

// UDC驱动中端点初始化示例 (以DWC2为例)
static struct usb_ep_ops dwc2_gadget_ep_ops = {
    .enable         = dwc2_hsotg_ep_enable,
    .disable        = dwc2_hsotg_ep_disable,
    .alloc_request  = dwc2_hsotg_ep_alloc_request,
    .free_request   = dwc2_hsotg_ep_free_request,
    .queue          = dwc2_hsotg_ep_queue,      // 提交传输请求
    .dequeue        = dwc2_hsotg_ep_dequeue,    // 取消传输请求
    .set_halt       = dwc2_hsotg_set_halt,      // 设置端点STALL
    .set_wedge      = dwc2_hsotg_set_wedge,     // 设置端点WEDGE
};

// 在UDC probe时注册端点
for (i = 0; i < hsotg->num_of_eps; i++) {
    ep = &hsotg->eps[i];
    ep->ep.ops = &dwc2_gadget_ep_ops;
    ep->ep.name = dwc2_gadget_ep_names[i];
    ep->ep.maxpacket = ep_type_maxpacket[i];
    list_add_tail(&ep->ep.ep_list, &hsotg->gadget.ep_list);
}
调试陷阱: 许多UDC控制器(如DWC2)的端点0最大包长度固定为64字节(高速)或8字节(全速)。如果功能驱动在控制传输阶段发送超过端点0最大包长度的数据,会导致枚举失败。务必检查 ep0_maxpacket 的配置。

2.3 Gadget功能驱动层:协议逻辑与用户接口

功能驱动是Gadget框架中最接近应用逻辑的层次。它负责实现具体的USB设备类协议(如Mass Storage、CDC ACM、RNDIS),并通过端点与主机进行数据交换。

2.3.1 功能驱动的生命周期

一个典型的功能驱动需要完成以下步骤:

  1. 定义描述符: 实现设备描述符、配置描述符、接口描述符、端点描述符以及字符串描述符。
  2. 实现绑定函数: 在 bind() 回调中分配端点、注册接口、初始化数据缓冲区。
  3. 处理控制请求: 通过 setup() 回调响应主机发起的标准请求(如GET_DESCRIPTOR)和类特定请求。
  4. 数据传输: 使用 usb_ep_queue() 提交IN/OUT请求,在完成回调中处理数据。
  5. 实现卸载函数: 在 unbind() 中释放端点、清理资源。
// 功能驱动核心结构体
struct usb_function {
    const char *name;
    struct usb_gadget_strings **strings;
    struct usb_descriptor_header **descriptors;
    struct usb_descriptor_header **hs_descriptors;  // 高速描述符
    struct usb_descriptor_header **ss_descriptors;  // 超高速描述符

    int (*bind)(struct usb_configuration *, struct usb_function *);
    void (*unbind)(struct usb_configuration *, struct usb_function *);
    int (*set_alt)(struct usb_function *, unsigned interface, unsigned alt);
    int (*get_alt)(struct usb_function *, unsigned interface);
    void (*disable)(struct usb_function *);
    int (*setup)(struct usb_function *, const struct usb_ctrlrequest *);
    bool (*req_match)(struct usb_function *, const struct usb_ctrlrequest *, bool config);
    void (*suspend)(struct usb_function *);
    void (*resume)(struct usb_function *);
    // ... 其他成员
};

2.3.2 常见功能驱动对比

主流Gadget功能驱动特性对比
功能驱动内核模块名USB类典型端点配置主要应用场景
Mass Storageg_mass_storage.koMass Storage (0x08)1x BULK IN + 1x BULK OUT模拟U盘、SD卡读卡器
Serial (ACM)g_serial.koCDC ACM (0x02)1x INT IN + 1x BULK IN + 1x BULK OUT虚拟串口、调试终端
Ethernet (RNDIS)g_ether.koCDC ECM / RNDIS1x INT IN + 2x BULK (IN/OUT)USB网络共享、RNDIS
Audio (UAC2)g_audio.koAudio (0x01)1x ISO IN + 1x ISO OUTUSB声卡、音频采集
HIDg_hid.koHID (0x03)1x INT IN自定义HID设备、键盘、鼠标
复合设备g_multi.ko多接口组合多个接口的端点集合同时支持U盘+串口+网卡

2.4 Gadget核心层:桥梁与调度

核心层(通常称为UDC Core或Gadget API)位于功能驱动和UDC驱动之间,提供标准化的接口和数据结构。它负责:

  • 驱动注册与匹配: 管理 usb_gadget_driver 和 usb_gadget 的绑定与解绑。
  • 端点分配: 将功能驱动请求的逻辑端点映射到UDC的物理端点。
  • 请求生命周期管理: 跟踪 usb_request 的分配、提交、完成和取消。
  • 标准请求处理: 自动处理部分标准设备请求(如SET_ADDRESS、SET_CONFIGURATION),将类特定请求转发给功能驱动。
Gadget核心层数据流与调度 USB Host UDC驱动 Gadget核心 功能驱动 核心层调度流程 中断处理与请求分发 1. UDC触发中断 → 核心层读取中断状态寄存器 2. 控制传输完成 → 核心层解析Setup包,调用功能驱动的 setup() 回调 3. 数据IN/OUT完成 → 核心层调用 usb_request.complete() 回调 4. 总线事件(复位/挂起)→ 核心层调用功能驱动的 suspend/resume/disable 回调

2.5 复合设备与ConfigFS

现代Linux Gadget开发强烈推荐使用 ConfigFS 接口(/sys/kernel/config/usb_gadget/),它允许在用户空间动态组合多个功能驱动,无需重新编译内核模块。

# 使用ConfigFS创建复合设备(U盘 + 虚拟串口)
modprobe libcomposite
mkdir /sys/kernel/config/usb_gadget/g1
cd /sys/kernel/config/usb_gadget/g1

# 设置VID/PID
echo 0x1d6b > idVendor   # Linux Foundation
echo 0x0104 > idProduct  # Multifunction Composite Gadget

# 创建配置
mkdir configs/c.1
echo 0xC0 > configs/c.1/bmAttributes  # 自供电
echo 250 > configs/c.1/MaxPower       # 250mA

# 添加Mass Storage功能
mkdir functions/mass_storage.usb0
echo /dev/mmcblk0 > functions/mass_storage.usb0/lun.0/file
ln -s functions/mass_storage.usb0 configs/c.1/

# 添加Serial功能
mkdir functions/acm.usb0
ln -s functions/acm.usb0 configs/c.1/

# 使能Gadget
echo "musb-hdrc.0" > UDC   # 绑定到UDC控制器
最佳实践: 使用ConfigFS时,务必先加载 libcomposite.ko 模块。如果UDC控制器名称不确定,可以通过 ls /sys/class/udc/ 查看可用的UDC实例。绑定UDC后,主机端会立即枚举设备。

2.6 本章小结

Linux USB Gadget框架通过清晰的三层架构实现了硬件无关性与功能可扩展性:

  • UDC驱动层 负责与具体硬件交互,管理端点、处理中断、控制DMA传输。
  • Gadget核心层 提供标准API,协调功能驱动与UDC驱动之间的数据流和控制流。
  • 功能驱动层 实现USB设备类协议,通过ConfigFS或Legacy API暴露给用户空间。