1. USB Gadget框架概述
1.1 Linux USB Gadget子系统架构
USB Gadget子系统是Linux内核中用于让嵌入式设备模拟USB外设(如U盘、串口、网卡、音频设备等)的核心框架。与USB Host驱动不同,Gadget驱动运行在Device端,通过UDC(USB Device Controller)硬件与Host通信。
核心概念: USB Gadget子系统将复杂的USB协议栈抽象为三层结构,使开发者能够以模块化方式组合不同的USB功能。
1.2 UDC / UDC Core / Gadget Function层关系
三层之间通过标准API进行交互,每一层职责清晰、接口明确:
| 层次 | 职责 | 关键数据结构 | 典型文件路径 |
|---|---|---|---|
| UDC层 | 直接操作USB Device Controller硬件寄存器,处理中断、端点传输 | struct usb_gadgetstruct usb_epstruct usb_request |
drivers/usb/gadget/udc/ |
| UDC Core层 | 提供Gadget API抽象,管理Composite设备、配置、字符串描述符 | struct usb_gadget_driverstruct usb_composite_dev |
drivers/usb/gadget/composite.c / configfs.c |
| Gadget Function层 | 实现具体USB功能协议,如Mass Storage、Serial、ECM等 | struct usb_functionstruct f_xxx_opts |
drivers/usb/gadget/function/ |
开发要点: 大多数工程问题出现在Function层与UDC Core层的接口对接处,尤其是端点分配、描述符长度对齐、以及DMA缓冲区管理方面。
1.3 核心数据结构关系
理解以下关键数据结构之间的关联,是掌握Gadget驱动开发的基础:
1.4 ConfigFS与Legacy模式对比
Linux内核提供了两种Gadget配置模式:传统的Legacy模式和现代的ConfigFS模式。理解两者的差异对于选择合适的开发路径至关重要。
| 对比维度 | Legacy模式 | ConfigFS模式 |
|---|---|---|
| 配置方式 | 内核编译时静态配置,或通过模块参数 | 运行时通过sysfs/configfs动态配置 |
| 灵活性 | 低,修改配置需重新编译内核或加载模块 | 高,用户空间可动态绑定/解绑Function |
| 适用场景 | 产品功能固定、不需要运行时切换 | 原型开发、多配置切换、用户定制 |
| 代码路径 | drivers/usb/gadget/legacy/ |
drivers/usb/gadget/configfs.c |
| 调试难度 | 较低,逻辑集中 | 较高,涉及用户空间与内核空间交互 |
| 内核配置 | CONFIG_USB_GADGET + 具体Function |
CONFIG_USB_CONFIGFS + 各Function |
工程建议: 新项目推荐使用ConfigFS模式,它提供了更好的灵活性和可维护性。Legacy模式适合对启动时间有严格要求的嵌入式产品,因为其初始化路径更短、依赖更少。
1.5 ConfigFS配置流程示例
以下是一个典型的ConfigFS配置流程,用于创建一个同时包含Mass Storage和Serial功能的复合Gadget设备:
# 1. 挂载configfs
mount -t configfs none /sys/kernel/config
# 2. 创建Gadget实例
mkdir /sys/kernel/config/usb_gadget/g1
cd /sys/kernel/config/usb_gadget/g1
# 3. 设置VID/PID
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
# 4. 创建配置(英文)
mkdir configs/c.1
echo "MultiFunction" > configs/c.1/strings/0x409/configuration
# 5. 创建Mass Storage Function
mkdir functions/mass_storage.usb0
echo /dev/mmcblk0p1 > functions/mass_storage.usb0/lun.0/file
# 6. 创建Serial Function
mkdir functions/serial.usb0
echo 0x1234 > functions/serial.usb0/idProduct
# 7. 绑定Function到配置
ln -s functions/mass_storage.usb0 configs/c.1
ln -s functions/serial.usb0 configs/c.1
# 8. 绑定到UDC(启用设备)
echo musb-hdrc.0 > UDC
关键点: 第8步将Gadget绑定到UDC后,USB设备立即生效。如果UDC文件写入失败,常见原因包括:UDC驱动未加载、端点资源不足、或描述符配置错误。
1.6 Legacy模式代码结构示例
Legacy模式下,Gadget驱动通常以内核模块形式编写,直接在bind回调中完成所有配置:
#include <linux/usb/composite.h>
static struct usb_composite_driver my_gadget_driver = {
.name = "my_gadget",
.dev = &my_device_desc,
.strings = my_strings,
.max_speed = USB_SPEED_HIGH,
.bind = my_bind,
};
static int __init my_bind(struct usb_composite_dev *cdev)
{
struct usb_configuration *config;
struct usb_function *f_mass, *f_serial;
// 创建配置
config = usb_get_configuration(cdev, 1);
config->label = "My Config";
config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
config->MaxPower = 100;
// 创建并绑定Mass Storage Function
f_mass = usb_get_function_instance("mass_storage");
usb_add_function(config, f_mass);
// 创建并绑定Serial Function
f_serial = usb_get_function_instance("serial");
usb_add_function(config, f_serial);
return 0;
}
module_init(my_bind);
对比总结: Legacy模式代码更紧凑,但每次修改Function组合都需要重新编译模块;ConfigFS模式将配置逻辑移到用户空间,更适合快速迭代和产品定制。
1.7 本章小结
USB Gadget子系统的三层架构(UDC → UDC Core → Gadget Function)为嵌入式设备提供了灵活的外设模拟能力。理解各层职责、核心数据结构关系以及ConfigFS与Legacy两种配置模式的差异,是后续章节解决具体工程问题的基础。
- UDC层:硬件相关,处理端点传输和中断
- UDC Core层:提供通用API,管理Composite设备和描述符
- Gadget Function层:实现具体USB协议功能
- ConfigFS模式:动态配置,适合开发和定制
- Legacy模式:静态配置,适合固定功能产品