3. Gadget 框架核心概念:UDC、Gadget Driver、USB Function、Configuration、Interface
在 Linux USB Gadget 子系统中,设备角色的切换依赖于一套层次清晰、职责分明的软件架构。这套架构将物理硬件、设备逻辑、功能实现与枚举描述层层解耦,使得开发者能够像搭积木一样组合出不同的 USB 设备形态。理解 UDC、Gadget Driver、USB Function、Configuration 和 Interface 这五个核心概念,是掌握 ACM 串口驱动开发的基础。
3.1 五层架构总览
Gadget 框架采用自底向上的分层设计,每一层都封装了特定的职责。下面的 SVG 图展示了这五层之间的依赖与组合关系:
3.2 核心概念详解
3.2.1 UDC (USB Device Controller)
UDC 是物理 USB 设备控制器的软件抽象。它直接操作硬件寄存器,负责处理 USB 总线上的复位、挂起、唤醒等事件,以及端点(Endpoint)上的数据传输。在 Linux 内核中,UDC 由 struct usb_gadget 结构体表示,并通过 struct usb_gadget_ops 提供操作接口。
| UDC 关键职责 | 说明 |
|---|---|
| 端点管理 | 分配、释放、配置端点 0~15(IN/OUT) |
| 中断处理 | 响应 USB 总线事件(复位、SOF、连接断开) |
| DMA 传输 | 管理硬件 DMA 引擎,实现零拷贝数据路径 |
| 电源管理 | 处理挂起/唤醒信号,控制设备功耗状态 |
3.2.2 Gadget Driver
Gadget Driver 是 UDC 与上层 Function 之间的桥梁。它通过 usb_gadget_probe_driver() 注册到 UDC 核心层,负责解析主机发来的标准设备请求(如 Get Descriptor、Set Configuration),并将这些请求分发给对应的 Function 处理。每个 UDC 在同一时刻只能绑定一个 Gadget Driver。
// Gadget Driver 注册示例(伪代码)
static struct usb_gadget_driver acm_gadget_driver = {
.function = LIST_HEAD_INIT(acm_gadget_driver.function),
.speed = USB_SPEED_HIGH,
.bind = acm_bind,
.unbind = acm_unbind,
.setup = acm_setup, // 处理标准请求
.disconnect = acm_disconnect,
.suspend = acm_suspend,
.resume = acm_resume,
};
int ret = usb_gadget_probe_driver(&acm_gadget_driver);
if (ret)
pr_err("Failed to probe ACM gadget driver: %d\n", ret);
3.2.3 USB Function
USB Function 是具体设备功能的实现单元。对于 ACM 串口来说,Function 层实现了 CDC ACM 协议,包括控制端点(用于波特率设置、线路编码)和数据端点(用于串口数据收发)。Function 通过 usb_function_register() 注册到 Gadget Driver,并可以动态绑定到不同的 Configuration。
- 控制端点(EP0): 处理类特定请求,如
SEND_ENCAPSULATED_COMMAND、SET_LINE_CODING - 中断端点(IN): 上报串口状态变化(如 DSR、RI 信号)
- 批量端点(IN/OUT): 传输实际的串口数据流
3.2.4 Configuration
Configuration 是 USB 描述符体系中的一级容器,用于向主机声明设备当前的工作模式。一个 Gadget 设备可以定义多个 Configuration(例如:一个用于高速模式,一个用于全速模式),但同一时刻只能激活一个。Configuration 决定了设备的供电方式(自供电/总线供电)和最大电流消耗。
// Configuration 描述符定义示例
static struct usb_config_descriptor acm_config = {
.bLength = USB_DT_CONFIG_SIZE,
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = 0, // 由框架自动计算
.bNumInterfaces = 2, // ACM 控制接口 + 数据接口
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
.bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
};
3.2.5 Interface
Interface 是主机侧可见的最小逻辑单元。一个 Configuration 可以包含多个 Interface,每个 Interface 对应一组端点。在 ACM 设备中,通常包含两个 Interface:控制接口(Interface 0) 和 数据接口(Interface 1)。控制接口使用中断端点传输状态信息,数据接口使用批量端点传输串口数据。
| ACM Interface 布局 | 端点类型 | 传输方向 | 用途 |
|---|---|---|---|
| Interface 0 (控制) | 中断 (IN) | 设备 → 主机 | 串口状态通知 |
| Interface 1 (数据) | 批量 (IN) | 设备 → 主机 | 串口数据发送 |
| Interface 1 (数据) | 批量 (OUT) | 主机 → 设备 | 串口数据接收 |
3.3 层次协作流程
当主机枚举 ACM Gadget 设备时,五层架构按照以下顺序协同工作:
- UDC 检测到 USB 连接,触发中断,通知 Gadget Driver 进入连接状态。
- Gadget Driver 调用 bind(),分配端点资源,注册 Function 实例。
- 主机发送 Get Descriptor 请求,Gadget Driver 逐级查询 Configuration → Interface → Endpoint 描述符。
- 主机发送 Set Configuration,激活指定的 Configuration,对应的 Function 开始准备数据收发。
- Function 通过端点与主机通信,处理 ACM 协议的控制命令和数据传输。
bInterfaceClass、bInterfaceSubClass 和 bInterfaceProtocol 字段严格遵循 CDC ACM 规范(0x02、0x02、0x01)。任何字段错误都会导致主机无法正确加载 usbserial 驱动。
3.4 概念对比表
| 概念 | 内核数据结构 | 生命周期 | 与主机关联 |
|---|---|---|---|
| UDC | struct usb_gadget |
与硬件共存亡 | 物理连接 |
| Gadget Driver | struct usb_gadget_driver |
模块加载/卸载 | 枚举入口 |
| USB Function | struct usb_function |
绑定/解绑 | 协议实现 |
| Configuration | struct usb_config_descriptor |
Set Configuration | 模式选择 |
| Interface | struct usb_interface_descriptor |
枚举时确定 | 功能单元 |