2. USB Gadget框架概述

USB Gadget子系统是Linux内核中用于实现设备端(Device Side)USB功能的软件框架。与主机侧(Host Side)的USB主机控制器驱动(HCD)不同,Gadget子系统允许嵌入式设备模拟成各种USB外设,如U盘、串口、网卡、音频设备等。理解其整体架构是进行Gadget驱动开发的第一步。

核心概念: Linux USB Gadget子系统采用分层设计思想,将硬件控制、协议处理与功能逻辑解耦。这种架构使得开发者可以在不修改底层硬件驱动的情况下,通过配置或编写功能驱动来快速实现不同的USB设备角色。

2.1 子系统三层架构总览

USB Gadget子系统从下到上主要分为三层:UDC驱动层(底层)、Gadget核心层(中间层)、功能驱动层(上层)。ConfigFS接口则作为用户空间与内核空间交互的桥梁,主要用于上层功能的动态组合与配置。

Linux USB Gadget 子系统三层架构 用户空间 / ConfigFS 接口 /sys/kernel/config/usb_gadget/ 配置层 Gadget 功能驱动层 (Function Drivers) g_serial.ko | g_ether.ko | g_mass_storage.ko | g_audio.ko | g_hid.ko 实现具体USB类协议(CDC ACM、RNDIS、MSC、UAC、HID) Gadget 核心层 (Gadget API) usb_gadget_driver | usb_gadget | usb_ep | usb_request 抽象UDC硬件差异,提供统一编程接口 UDC 驱动层 (UDC Drivers) dwc2.ko | dwc3.ko | chipidea.ko | musb.ko | at91_udc.ko 直接操作USB设备控制器寄存器,处理中断与DMA传输 USB 设备控制器硬件 (UDC Hardware)
架构要点: 数据流方向为“用户空间配置 → 功能驱动处理协议 → 核心层调度 → UDC驱动控制硬件”。每一层都通过标准API与相邻层通信,层与层之间不直接访问对方内部数据结构。

2.2 UDC驱动层(底层)

UDC(USB Device Controller)驱动层是Gadget子系统中最接近硬件的部分。它直接与SoC或外挂芯片中的USB设备控制器硬件交互,负责处理USB总线上的物理信号、包传输、中断管理以及DMA操作。

UDC驱动名称 对应控制器 常见平台 特点
dwc2.ko DesignWare Cores DWC2 Raspberry Pi、Allwinner、Rockchip 支持OTG,驱动代码成熟
dwc3.ko DesignWare Cores DWC3 高通、TI、Xilinx、Intel 支持USB 3.0 SuperSpeed,性能高
chipidea.ko NXP ChipIdea i.MX系列、LPC系列 集成度高,常用于工业控制
musb.ko Mentor Graphics MUSB OMAP、DaVinci、Blackfin 双角色控制器,历史悠久
at91_udc.ko Microchip AT91 UDC AT91SAM系列 专用于AT91 ARM SoC

UDC驱动需要实现的核心回调函数包括:udc_start()、udc_stop()、ep_enable()、ep_disable()、ep_queue()、ep_dequeue() 等。这些函数通过 usb_gadget_ops 结构体注册到核心层。

// UDC驱动必须实现的操作集结构体(位于 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 value);
    int     (*vbus_session)(struct usb_gadget *, int is_active);
    int     (*vbus_draw)(struct usb_gadget *, unsigned int mA);
    int     (*pullup)(struct usb_gadget *, int is_on);
    int     (*ioctl)(struct usb_gadget *, unsigned int code, unsigned long param);
    int     (*udc_start)(struct usb_gadget *, struct usb_gadget_driver *);
    int     (*udc_stop)(struct usb_gadget *);
};
开发注意: UDC驱动通常由SoC厂商或芯片IP提供商编写,内核主线中已包含大部分主流控制器的驱动。在Gadget功能开发中,除非你使用的硬件非常特殊,否则不需要修改UDC驱动层。你的工作重点在上层功能驱动和ConfigFS配置。

2.3 Gadget功能驱动层(上层)

功能驱动层是开发者最常接触的部分。每个功能驱动实现一种具体的USB设备类协议,例如串口(CDC ACM)、以太网(RNDIS/ECM)、大容量存储(MSC)、音频(UAC)或HID设备。

功能驱动通过Gadget核心层提供的API与UDC驱动交互。它负责构建USB描述符(设备描述符、配置描述符、接口描述符、端点描述符)、处理控制传输(Setup包)、管理批量/中断/等时传输的数据流。

Gadget 功能驱动内部结构(以g_serial为例) g_serial.ko CDC ACM 协议处理 描述符生成 数据传输回调 Gadget核心API usb_ep_queue() usb_ep_enable() usb_control_msg() 数据流方向 用户态 ↓ 读写 /dev/ttyGS* 功能驱动 ↓ 协议封装 核心API ↓ 端点调度 UDC硬件

内核中预置了多个标准功能驱动,可以直接通过配置启用:

  • g_serial.ko — USB虚拟串口(CDC ACM),主机端显示为COM端口
  • g_ether.ko — USB虚拟以太网(CDC ECM/RNDIS),主机端显示为网卡
  • g_mass_storage.ko — USB大容量存储设备,将文件或块设备模拟为U盘
  • g_audio.ko — USB音频设备(UAC),实现双向音频流
  • g_hid.ko — USB HID设备,可模拟键盘、鼠标、游戏手柄
  • g_multi.ko — 复合设备,同时包含多个功能(如串口+网卡+存储)

2.4 ConfigFS接口

ConfigFS(Configuration File System)是Linux内核提供的一种基于文件系统的配置机制,挂载在 /sys/kernel/config/ 目录下。在USB Gadget子系统中,ConfigFS允许用户在用户空间通过创建目录、写入文件的方式动态构建USB设备配置,而无需编写内核代码或重新编译内核模块。

使用ConfigFS配置Gadget的基本流程如下:

  1. 创建Gadget实例: mkdir /sys/kernel/config/usb_gadget/g1
  2. 设置VID/PID: 写入 idVendor 和 idProduct 文件
  3. 创建配置: mkdir configs/c.1,设置配置属性(如 MaxPower)
  4. 创建功能实例: mkdir functions/acm.usb0
  5. 绑定功能到配置: 创建符号链接 ln -s functions/acm.usb0 configs/c.1
  6. 绑定UDC: 将Gadget实例的UDC文件写入对应的UDC控制器名称(如 echo "musb-hdrc.0" > UDC)
# 一个完整的ConfigFS配置示例:创建USB虚拟串口
# 1. 挂载ConfigFS(如果未挂载)
mount -t configfs none /sys/kernel/config

# 2. 创建Gadget实例
mkdir /sys/kernel/config/usb_gadget/g_serial_demo
cd /sys/kernel/config/usb_gadget/g_serial_demo

# 3. 设置USB标准描述符
echo 0x1d6b > idVendor    # Linux Foundation
echo 0x0104 > idProduct   # Multifunction Composite Gadget
echo 0x0100 > bcdDevice   # v1.0.0
echo 0x0200 > bcdUSB      # USB 2.0

# 4. 创建字符串描述符(英文)
mkdir strings/0x409
echo "MyCompany" > strings/0x409/manufacturer
echo "VirtualSerialPort" > strings/0x409/product
echo "1234567890" > strings/0x409/serialnumber

# 5. 创建配置
mkdir configs/c.1
mkdir configs/c.1/strings/0x409
echo "ACM Serial Config" > configs/c.1/strings/0x409/configuration
echo 120 > configs/c.1/MaxPower  # 120mA

# 6. 创建ACM功能并绑定
mkdir functions/acm.usb0
ln -s functions/acm.usb0 configs/c.1/

# 7. 绑定UDC控制器(假设控制器名称为 musb-hdrc.0)
echo "musb-hdrc.0" > UDC
ConfigFS优势: 无需编写内核模块即可实现Gadget功能切换。在运行时可以动态解绑UDC、修改配置、重新绑定,非常适合产品原型验证和需要频繁切换USB角色的场景。

2.5 各层协作关系总结

下表总结了USB Gadget子系统中各层的职责、核心数据结构以及开发者需要关注的程度:

层次 核心职责 关键数据结构/接口 开发者关注度
UDC驱动层 硬件寄存器操作、中断处理、DMA传输 usb_gadget_ops、usb_ep 低(除非移植新硬件)
Gadget核心层 UDC与功能驱动的桥梁、端点管理、请求调度 usb_gadget_driver、usb_request 中(理解API即可)
功能驱动层 USB类协议实现、描述符构建、数据收发 usb_function、usb_function_instance 高(开发重点)
ConfigFS接口 用户空间动态配置、功能组合、UDC绑定 文件系统操作(mkdir/echo/ln) 高(日常使用)
学习路径建议: 对于初学者,建议先从ConfigFS入手,使用内核预置的功能驱动(如g_serial、g_mass_storage)完成实际设备的模拟,建立对Gadget子系统的感性认识。在熟悉ConfigFS流程后,再深入阅读功能驱动的源码,最后根据项目需求编写自定义功能驱动。