2. Linux USB Gadget框架:内核中USB Gadget的架构层次、UDC驱动、Gadget Function驱动、ConfigFS接口
Linux USB Gadget框架是构建多功能复合设备的软件基石。它采用分层的模块化设计,将USB设备角色(Device Role)的实现从硬件细节中抽象出来,使得开发者能够像搭积木一样组合不同的功能(Function),最终通过ConfigFS接口在用户空间动态装配和配置复合设备。本章将深入剖析这一框架的四个核心层次。
2.1 架构层次总览
Linux USB Gadget框架自下而上分为四个清晰的层次,每一层都封装了特定的职责,并通过标准API与相邻层交互。这种分层设计极大地提升了代码的可移植性和复用性。
UDC层(底层硬件抽象) → Gadget层(通用设备模型) → Function层(具体功能实现) → ConfigFS层(用户空间配置接口)
2.2 UDC驱动:硬件与内核的桥梁
UDC(USB Device Controller)驱动是框架中最底层的软件实体,直接与SoC或外置USB控制器硬件交互。它负责处理USB协议层面的细节,包括:端点(Endpoint)管理、传输请求(Request)调度、中断处理、以及USB总线状态(如复位、挂起、恢复)的响应。
| 特性 | 描述 |
|---|---|
| 核心职责 | 初始化硬件控制器、配置端点、处理USB标准请求(如Get Descriptor、Set Address) |
| 关键数据结构 | struct usb_gadget(代表一个UDC实例)、struct usb_ep(代表端点)、struct usb_request(代表一次数据传输) |
| 注册接口 | usb_add_gadget_udc() 将UDC驱动注册到内核,并绑定到对应的平台设备 |
| 常见实现 | DWC2(广泛用于STM32、Raspberry Pi)、DWC3(用于高通、TI平台)、Chipidea(用于i.MX系列)、MUSB(用于OMAP平台) |
/sys/kernel/debug/usb/udc 目录下的调试接口查看当前UDC的状态、已注册的端点以及待处理的请求队列。
2.3 Gadget Function驱动:功能的原子单元
Gadget Function驱动是框架中实现具体USB功能的模块。每个Function驱动封装了一种USB设备类的行为,例如:Mass Storage Function(模拟U盘)、Ethernet Function(模拟USB网卡,如ECM/RNDIS)、Serial Function(模拟USB串口,如ACM)、HID Function(模拟键盘、鼠标等HID设备)。
Function驱动通过Gadget API层提供的回调函数与上层交互。一个复合设备可以同时加载多个Function,每个Function占用一组端点,并通过接口(Interface)和端点描述符向主机声明自身能力。
2.4 ConfigFS接口:用户空间的动态装配
ConfigFS(Configuration File System)是Linux内核提供的一种基于文件系统的配置机制,它允许用户空间程序通过创建目录、写入文件来动态地构建和配置USB Gadget设备。这是实现多功能复合设备最灵活、最推荐的方式。
ConfigFS将Gadget的配置过程映射为文件系统操作,主要涉及以下目录结构:
/sys/kernel/config/usb_gadget/
└── g1/ # 一个Gadget实例
├── UDC # 绑定到哪个UDC控制器(写入UDC名称)
├── bcdUSB # USB版本号
├── idVendor # 厂商ID
├── idProduct # 产品ID
├── strings/ # 字符串描述符
│ └── 0x409/
│ ├── manufacturer # 厂商字符串
│ ├── product # 产品字符串
│ └── serialnumber # 序列号
├── configs/ # 配置描述符
│ └── c.1/ # 配置1
│ ├── strings/0x409/
│ │ └── configuration # 配置描述字符串
│ └── f1/ # 链接到Function实例
└── functions/ # Function实例目录
├── mass_storage.usb0/ # Mass Storage Function
│ └── lun.0/
│ └── file # 指定 backing file
├── ecm.usb0/ # Ethernet ECM Function
└── acm.usb0/ # Serial ACM Function
1. 创建Gadget目录并设置VID/PID。
2. 创建字符串描述符。
3. 创建配置目录。
4. 创建Function实例并配置参数(如Mass Storage的backing file)。
5. 将Function符号链接到配置目录下。
6. 最后将UDC名称写入UDC文件以激活Gadget。
以下是一个典型的ConfigFS配置脚本片段,用于创建一个同时包含Mass Storage和Ethernet功能的复合设备:
#!/bin/sh
# 创建Gadget实例
mkdir /sys/kernel/config/usb_gadget/g1
cd /sys/kernel/config/usb_gadget/g1
# 设置USB标识
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
# 创建字符串描述符
mkdir strings/0x409
echo "MyCompany" > strings/0x409/manufacturer
echo "MultiFunction Device" > strings/0x409/product
echo "1234567890" > strings/0x409/serialnumber
# 创建配置
mkdir configs/c.1
mkdir configs/c.1/strings/0x409
echo "Config 1: MS+ECM" > configs/c.1/strings/0x409/configuration
# 创建Mass Storage Function
mkdir functions/mass_storage.usb0
echo /dev/mmcblk0p1 > functions/mass_storage.usb0/lun.0/file
# 创建Ethernet Function
mkdir functions/ecm.usb0
# 将Function链接到配置
ln -s functions/mass_storage.usb0 configs/c.1
ln -s functions/ecm.usb0 configs/c.1
# 激活Gadget(绑定到UDC)
echo ci_hdrc.0 > UDC
/sys/kernel/config/usb_gadget/g1/UDC 文件是否已成功写入UDC名称。其次,通过 lsusb -v 查看主机侧是否枚举出正确的厂商ID、产品ID以及配置描述符中的接口数量。
2.5 层次间协作流程
当主机通过USB线缆连接设备时,框架各层按照以下流程协同工作:
- 硬件检测: UDC驱动检测到VBUS插入,触发中断。
- Gadget绑定: UDC驱动调用Gadget API层的
usb_gadget_probe_driver(),找到已注册的Gadget驱动(由ConfigFS创建)。 - Function绑定: Gadget驱动遍历配置中的所有Function,调用每个Function的
bind()回调,分配端点并准备描述符。 - 主机枚举: 主机发送标准请求(Get Descriptor、Set Configuration),Gadget驱动通过UDC驱动返回组合后的描述符。
- 数据传输: 主机选择配置后,各Function驱动通过
usb_ep_queue()提交请求,UDC驱动负责实际的硬件传输。