2. USB Gadget框架简介:Linux Gadget子系统架构、UDC驱动层、Gadget功能驱动层
在嵌入式Linux系统中,USB Gadget框架使得设备能够扮演USB外设(Device/Peripheral)角色,例如模拟成U盘、虚拟串口、以太网卡或音频设备。理解其分层架构是进行Gadget驱动开发的基础。本章将剖析Linux Gadget子系统的三层模型:Gadget功能驱动层、UDC(USB Device Controller)驱动层以及连接二者的核心层(Gadget API)。
2.1 三层架构总览
下图展示了Linux Gadget子系统的完整软件栈。用户空间通过ConfigFS或Legacy Gadget API与内核交互,内核内部则分为三个清晰的层次。
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_start | Gadget功能驱动注册时 | 使能控制器时钟、初始化端点、连接D+上拉电阻 |
udc_stop | Gadget功能驱动注销时 | 断开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);
}
ep0_maxpacket 的配置。
2.3 Gadget功能驱动层:协议逻辑与用户接口
功能驱动是Gadget框架中最接近应用逻辑的层次。它负责实现具体的USB设备类协议(如Mass Storage、CDC ACM、RNDIS),并通过端点与主机进行数据交换。
2.3.1 功能驱动的生命周期
一个典型的功能驱动需要完成以下步骤:
- 定义描述符: 实现设备描述符、配置描述符、接口描述符、端点描述符以及字符串描述符。
- 实现绑定函数: 在
bind()回调中分配端点、注册接口、初始化数据缓冲区。 - 处理控制请求: 通过
setup()回调响应主机发起的标准请求(如GET_DESCRIPTOR)和类特定请求。 - 数据传输: 使用
usb_ep_queue()提交IN/OUT请求,在完成回调中处理数据。 - 实现卸载函数: 在
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 常见功能驱动对比
| 功能驱动 | 内核模块名 | USB类 | 典型端点配置 | 主要应用场景 |
|---|---|---|---|---|
| Mass Storage | g_mass_storage.ko | Mass Storage (0x08) | 1x BULK IN + 1x BULK OUT | 模拟U盘、SD卡读卡器 |
| Serial (ACM) | g_serial.ko | CDC ACM (0x02) | 1x INT IN + 1x BULK IN + 1x BULK OUT | 虚拟串口、调试终端 |
| Ethernet (RNDIS) | g_ether.ko | CDC ECM / RNDIS | 1x INT IN + 2x BULK (IN/OUT) | USB网络共享、RNDIS |
| Audio (UAC2) | g_audio.ko | Audio (0x01) | 1x ISO IN + 1x ISO OUT | USB声卡、音频采集 |
| HID | g_hid.ko | HID (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),将类特定请求转发给功能驱动。
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控制器
libcomposite.ko 模块。如果UDC控制器名称不确定,可以通过 ls /sys/class/udc/ 查看可用的UDC实例。绑定UDC后,主机端会立即枚举设备。
2.6 本章小结
Linux USB Gadget框架通过清晰的三层架构实现了硬件无关性与功能可扩展性:
- UDC驱动层 负责与具体硬件交互,管理端点、处理中断、控制DMA传输。
- Gadget核心层 提供标准API,协调功能驱动与UDC驱动之间的数据流和控制流。
- 功能驱动层 实现USB设备类协议,通过ConfigFS或Legacy API暴露给用户空间。