2. Linux USB Gadget框架:软件架构与核心数据结构
Linux USB Gadget 框架是内核中实现设备端(Device Side)USB 功能的核心软件层。它向上为各类 Gadget 功能驱动(如 ECM、UVC、Mass Storage)提供统一的编程接口,向下通过 UDC(USB Device Controller)驱动直接操作硬件寄存器。理解其软件架构与核心数据结构,是进行硬件适配与移植的基础。
2.1 软件架构分层
整个框架采用分层设计,从底至上依次为:UDC 驱动层、Gadget 核心层、功能驱动层。各层职责清晰,通过标准 API 交互。
架构要点: UDC 驱动直接与硬件寄存器打交道,Gadget 核心层提供通用抽象,功能驱动实现具体的 USB 逻辑(如枚举、数据传输)。硬件适配主要工作在 UDC 驱动层。
2.2 核心数据结构
Gadget 框架围绕三个核心数据结构构建:usb_gadget、usb_ep、usb_request。它们分别代表 USB 设备整体、端点(Endpoint)和传输请求(Request)。
2.2.1 usb_gadget:设备抽象
struct usb_gadget 是 USB 设备控制器的软件抽象,代表一个完整的 USB 设备实例。它包含设备状态、速度、电源管理、UDC 操作函数集等信息。
// include/linux/usb/gadget.h
struct usb_gadget {
struct device dev; // 内核设备模型
const struct usb_gadget_ops *ops; // UDC 操作函数集
struct usb_ep *ep0; // 控制端点 ep0
enum usb_device_state state; // 设备状态 (USB_STATE_*)
enum usb_device_speed speed; // 当前连接速度
unsigned is_otg:1; // 是否支持 OTG
unsigned is_a_peripheral:1; // 是否作为 A 设备
unsigned b_hnp_enable:1; // HNP 使能
unsigned a_hnp_support:1;
unsigned a_alt_hnp_support:1;
unsigned quirk_ep_out_aligned_size:1; // 硬件 quirks
// ... 其他字段
};
移植关注点: 在硬件适配时,需要实现
struct usb_gadget_ops 中的回调函数,如 udc_start、udc_stop、pullup、get_frame 等。这些回调直接操作 UDC 寄存器。
2.2.2 usb_ep:端点抽象
struct usb_ep 代表一个 USB 端点(Endpoint),包括端点号、方向、类型、最大包大小等属性。每个 UDC 驱动需要为所有可用端点创建对应的 usb_ep 实例。
// include/linux/usb/gadget.h
struct usb_ep {
void *driver_data; // 驱动私有数据
const char *name; // 端点名称 (如 "ep1in")
const struct usb_ep_ops *ops; // 端点操作函数集
struct list_head ep_list; // 链表节点
unsigned maxpacket:16; // 最大包大小
unsigned max_streams:16; // 批量流数量
unsigned mult:2; // 高带宽模式倍数
unsigned maxburst:5; // 突发传输长度
unsigned address:8; // 端点地址 (含方向位)
const struct usb_endpoint_descriptor *desc; // 描述符指针
};
| 回调函数 | 功能描述 | 硬件适配要点 |
|---|---|---|
enable |
使能端点,配置硬件寄存器 | 设置端点类型、最大包大小、FIFO 地址 |
disable |
禁用端点,释放硬件资源 | 清空 FIFO,关闭中断 |
queue |
提交传输请求到硬件 | 将 request 写入 DMA 或 FIFO,触发传输 |
dequeue |
取消未完成的传输请求 | 从硬件队列中移除,处理竞争条件 |
set_halt |
设置端点 STALL 条件 | 写端点控制寄存器的 STALL 位 |
2.2.3 usb_request:传输请求抽象
struct usb_request 描述一次 USB 数据传输请求,包含数据缓冲区、长度、完成回调等。它是 Gadget 框架中数据流动的基本单元。
// include/linux/usb/gadget.h
struct usb_request {
void *buf; // 数据缓冲区
unsigned length; // 缓冲区长度
dma_addr_t dma; // DMA 地址 (若使用 DMA)
unsigned no_interrupt:1; // 是否不产生中断
unsigned zero:1; // 是否发送零长度包
unsigned short_not_ok:1; // 是否不允许短包
unsigned stream_id:16; // 批量流 ID
void *context; // 回调上下文
void (*complete)(struct usb_ep *ep, struct usb_request *req); // 完成回调
struct list_head list; // 链表节点
};
常见错误: 在 UDC 驱动中,
usb_request 的 complete 回调必须在硬件传输真正完成后(中断上下文中)调用,且不能重复调用。错误的回调时机会导致数据丢失或内核崩溃。
2.3 数据结构关系图
以下 SVG 图展示了三个核心数据结构之间的关联关系,以及它们如何与 UDC 硬件交互。
2.4 数据流与回调链
一次典型的 USB 数据传输流程如下:
- 功能驱动调用
usb_ep_queue()提交usb_request到某个端点。 - Gadget 核心层将请求传递给 UDC 驱动的
ep->ops->queue()回调。 - UDC 驱动将请求写入硬件(DMA 或 FIFO),启动传输。
- 硬件传输完成后触发中断,UDC 驱动在中断处理中调用
req->complete()回调。 - 功能驱动在回调中处理数据,可能再次提交新请求。
性能关键路径:
queue() 和 complete() 是数据路径上的热点函数。硬件适配时,应尽量减少这两个函数中的锁竞争和内存拷贝,优先使用 DMA 传输。
2.5 硬件适配接口清单
在移植 UDC 驱动时,必须实现以下核心接口:
| 接口 | 所属结构 | 必须实现 | 说明 |
|---|---|---|---|
udc_start |
usb_gadget_ops | 是 | 启动 UDC,注册中断,使能控制器 |
udc_stop |
usb_gadget_ops | 是 | 停止 UDC,释放资源,注销中断 |
pullup |
usb_gadget_ops | 是 | 控制 D+ 上拉电阻,实现软连接/断开 |
enable |
usb_ep_ops | 是 | 配置端点寄存器,分配 FIFO |
disable |
usb_ep_ops | 是 | 禁用端点,释放 FIFO |
queue |
usb_ep_ops | 是 | 提交传输请求到硬件队列 |
dequeue |
usb_ep_ops | 是 | 取消未完成的请求 |
set_halt |
usb_ep_ops | 是 | 设置/清除端点 STALL |
get_frame |
usb_gadget_ops | 推荐 | 读取当前帧号,用于 ISO 传输 |
移植建议: 在开始编写 UDC 驱动前,先阅读内核中已有的类似控制器驱动(如 dwc3、chipidea、mv_udc),参考其
usb_gadget_ops 和 usb_ep_ops 的实现模式。大多数 UDC 硬件差异集中在端点配置和 DMA 管理上。