1. USB Gadget 基础概念
1.1 USB Gadget 框架简介
USB Gadget 是 Linux 内核中用于让嵌入式设备模拟 USB 外设(Peripheral)角色的软件框架。它运行在设备端(Device Side),通过硬件 USB 控制器(如 DWC2、DWC3、ChipIdea)向上层提供标准化的接口,使得开发者无需关心底层寄存器操作即可实现多种 USB 功能。
Gadget 框架的核心组件包括:
- UDC(USB Device Controller)驱动:直接操作硬件 USB 控制器,负责收发 USB 数据包、处理中断、管理端点。
- Gadget API:UDC 驱动向上层暴露的统一接口,包括
usb_gadget、usb_ep、usb_request等核心结构体。 - Function 驱动:实现具体 USB 功能(如 Ethernet、Serial、Mass Storage),通过 Gadget API 与 UDC 交互。
- ConfigFS / Legacy Gadget:用户态配置接口,允许动态组合多个 Function 形成复合设备。
关键路径: 用户态通过 ConfigFS 配置 Gadget → 内核 Function 驱动注册端点 → UDC 驱动处理硬件传输 → Host 端枚举并加载对应驱动。
1.2 Gadget 与 Host 的区别
在 USB 体系中,Host(主机)和 Gadget(设备)的角色有本质区别。理解这些差异是排查枚举失败的基础。
| 对比维度 | Host(主机) | Gadget(设备) |
|---|---|---|
| 硬件角色 | 提供 VBUS 电源,发起枚举 | 消耗 VBUS 电源,响应枚举 |
| 软件栈 | USB 主机控制器驱动 + HCI(xHCI/EHCI/OHCI) | UDC 驱动 + Gadget API + Function 驱动 |
| 端点方向 | OUT 表示数据从主机到设备,IN 表示数据从设备到主机 | OUT 表示数据从主机到设备,IN 表示数据从设备到主机(与 Host 对称) |
| 枚举发起者 | 主动发送 SETUP 包,获取设备描述符 | 被动响应 SETUP 包,返回描述符 |
| 典型硬件 | PC、笔记本、树莓派(Type-A 口) | 嵌入式开发板、手机、IoT 设备(Type-B / Type-C / Micro-AB 口) |
| 驱动加载方式 | 根据设备描述符匹配主机端驱动 | Function 驱动由 Gadget 框架自身加载 |
排查提示: 枚举失败时,首先确认硬件连接方向是否正确。Gadget 设备必须连接到 Host 的 Type-A 口或支持 DRP(Dual Role Port)的 Type-C 口。如果 Gadget 设备连接到另一个 Gadget 设备(如两块开发板直连),枚举必然失败。
1.3 常见 Gadget 类型
Linux Gadget 框架支持多种 USB 功能类型,以下三种是最常用且故障排查频率最高的:
1.3.1 Ethernet Gadget(USB 网卡)
将嵌入式设备模拟为 USB 以太网适配器,Host 端识别为网络接口(如 usb0、enp0s20f0u1)。支持两种协议:
- CDC ECM(Ethernet Control Model):标准协议,Linux/macOS 原生支持,Windows 需额外驱动。
- CDC NCM(Network Control Model):更高吞吐量,适合高速传输。
- RNDIS(Remote NDIS):Microsoft 专有协议,Windows 原生支持,Linux 需加载
rndis_host驱动。
常见故障: Host 端枚举成功但无法获取 IP 地址。通常是因为 Gadget 端未配置
g_ether 的 MAC 地址,或 Host 端未启用 DHCP 客户端。排查时使用 dmesg 查看 Host 端是否识别到 usb0 接口。
1.3.2 Serial Gadget(USB 串口)
将嵌入式设备模拟为 USB 转串口适配器,Host 端识别为 /dev/ttyUSB0(Linux)或 COM 端口(Windows)。常见实现:
- CDC ACM(Abstract Control Model):标准串口协议,广泛用于 Modem、GPS、工业设备。
- Generic Serial:简单的批量传输串口,适用于自定义协议。
排查提示: 如果 Host 端枚举成功但
/dev/ttyUSB0 未出现,检查 Gadget 端是否加载了 g_serial 或 u_serial 模块,并确认 ttyGS0 设备节点已创建。
1.3.3 Mass Storage Gadget(USB 存储设备)
将嵌入式设备的存储介质(如 SD 卡、eMMC 分区、RAM 磁盘)模拟为 USB 大容量存储设备,Host 端识别为 U 盘。支持两种模式:
- 直写模式(Direct Access):Host 直接读写底层块设备,适合文件传输。
- 只读模式(Read-Only):防止 Host 修改 Gadget 端数据,适合固件升级或安全场景。
| Gadget 类型 | 内核模块 | Host 端识别 | 典型应用 |
|---|---|---|---|
| Ethernet | g_ether.ko / u_ether.ko |
usb0 / enp* |
网络共享、IoT 设备管理 |
| Serial | g_serial.ko / u_serial.ko |
/dev/ttyUSB0 / COMx |
调试控制台、Modem 通信 |
| Mass Storage | g_mass_storage.ko / u_msc.ko |
/dev/sd* / 可移动磁盘 |
文件传输、固件升级 |
1.4 Gadget 配置示例(ConfigFS)
以下是一个使用 ConfigFS 配置 CDC ECM Ethernet 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(必须与 Host 驱动匹配)
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # CDC ECM
# 4. 设置字符串描述符(可选但推荐)
mkdir strings/0x409
echo "MyBoard" > strings/0x409/manufacturer
echo "USB Ethernet" > strings/0x409/product
echo "1234567890" > strings/0x409/serialnumber
# 5. 创建配置
mkdir configs/c.1
mkdir configs/c.1/strings/0x409
echo "ECM Config" > configs/c.1/strings/0x409/configuration
# 6. 创建 Ethernet Function
mkdir functions/ecm.usb0
# 7. 绑定 Function 到配置
ln -s functions/ecm.usb0 configs/c.1/
# 8. 绑定 UDC(替换为实际 UDC 名称)
echo "musb-hdrc.0.auto" > UDC
故障排查基线: 上述配置完成后,Host 端应枚举出
usb0 接口。如果枚举失败,首先检查 /sys/kernel/config/usb_gadget/g1/UDC 是否已写入正确的 UDC 名称,以及 dmesg 中是否有 UDC 驱动报错。