2. USB Gadget框架基础
在深入 RNDIS 网络性能优化之前,必须透彻理解 USB Gadget 框架的层次结构。Linux 内核的 USB Gadget 子系统采用分层设计,将硬件控制、协议实现与用户配置解耦。本章将逐层剖析其核心组件:UDC 驱动层、Gadget 功能驱动层、ConfigFS 接口以及贯穿其中的 API 架构。
2.1 USB Gadget 整体架构分层
USB Gadget 框架从底层硬件到上层用户空间共分为四个主要层次。每一层都有明确的职责边界,通过标准化的 API 进行交互。
架构要点:上层驱动不直接操作硬件寄存器,下层驱动不关心上层协议细节。RNDIS 功能驱动只需调用 Gadget API 分配端点、提交请求,由 UDC 核心层完成与硬件驱动的交互。
2.2 UDC 驱动层:硬件抽象与端点管理
UDC(USB Device Controller)驱动层是框架中最接近硬件的部分。它直接操作 USB 控制器寄存器,负责物理层信号处理、端点配置、DMA 传输以及中断处理。
2.2.1 UDC 驱动核心职责
- 端点资源管理:枚举并暴露硬件支持的端点数量、类型(Bulk/Interrupt/Isochronous/Control)及最大包长。
- 请求队列调度:维护每个端点的请求链表,按 FIFO 顺序提交给硬件,处理完成回调。
- 事件中断处理:响应 USB 总线事件(复位、挂起、恢复、连接断开),并向上层传递状态变化。
- DMA 映射管理:为上层请求分配 DMA 缓冲区,处理 Cache 一致性。
2.2.2 主流 UDC 驱动对比
| UDC 驱动 | 对应硬件 | 端点数量 | DMA 支持 | 常见平台 |
|---|---|---|---|---|
| dwc2 | Synopsys DWC2 | 16 IN + 16 OUT | 有限 | Raspberry Pi, Allwinner |
| dwc3 | Synopsys DWC3 | 32 IN + 32 OUT | 完整 | 高通、TI、Xilinx |
| chipidea | NXP Chipidea | 8 IN + 8 OUT | 有限 | i.MX 系列 |
| renesas_usb3 | Renesas USB3.0 | 30 IN + 30 OUT | 完整 | R-Car 系列 |
性能提示:RNDIS 使用 Bulk 传输,端点的最大包长和 FIFO 深度直接影响吞吐量。DWC3 支持 1024 字节 Bulk 包长和独立 FIFO 配置,是高性能 RNDIS 的首选硬件。
2.3 Gadget 功能驱动层:协议实现的核心
Gadget 功能驱动层是 USB 设备功能的实际实现者。每个功能驱动对应一种 USB 设备类协议,例如 RNDIS 对应网络通信设备类(CDC)的以太网子类。
2.3.1 功能驱动的生命周期
- 绑定(Bind):分配端点、注册回调函数、初始化协议状态机。
- 设置配置(Set Configuration):主机选择配置后,激活端点并准备接收数据。
- 数据传输(Data Transfer):通过 Gadget API 提交 USB 请求(usb_request),处理完成回调。
- 解绑(Unbind):释放端点、清理资源、注销回调。
2.3.2 关键数据结构:usb_function
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 *c, struct usb_function *f);
void (*unbind)(struct usb_configuration *c, struct usb_function *f);
int (*set_alt)(struct usb_function *f, unsigned interface, unsigned alt);
int (*get_alt)(struct usb_function *f, unsigned interface);
void (*disable)(struct usb_function *f);
int (*setup)(struct usb_function *f, const struct usb_ctrlrequest *ctrl);
bool (*req_match)(struct usb_function *f, const struct usb_endpoint_descriptor *ep,
struct usb_request *req);
};
RNDIS 实现要点:RNDIS 功能驱动需要实现
bind 分配一个 Bulk IN 和一个 Bulk OUT 端点,setup 处理 CDC 类请求(如 SEND_ENCAPSULATED_COMMAND),disable 在主机断开时停止网络队列。
2.4 ConfigFS 接口:用户空间的配置利器
ConfigFS(Configuration File System)是 Linux 内核提供的一种基于 sysfs 的用户空间配置接口,用于动态组合 USB Gadget 功能。它挂载在 /sys/kernel/config/usb_gadget/ 目录下。
2.4.1 ConfigFS 目录结构
/sys/kernel/config/usb_gadget/
└── g1/ # Gadget 实例
├── UDC # 绑定到哪个 UDC 控制器
├── bcdUSB # USB 版本号
├── idVendor # 厂商 ID
├── idProduct # 产品 ID
├── strings/0x409/ # 字符串描述符
│ ├── manufacturer
│ ├── product
│ └── serialnumber
├── configs/c.1/ # 配置实例
│ ├── strings/0x409/
│ │ └── configuration
│ ├── MaxPower # 最大电流 (mA)
│ └── rndis.usb0/ # 功能实例符号链接
└── functions/ # 功能目录
└── rndis.usb0/ # RNDIS 功能实例
├── dev_addr # 设备 MAC 地址
├── host_addr # 主机 MAC 地址
└── qmult # 队列深度倍数
2.4.2 典型配置流程
- 挂载 ConfigFS:
mount -t configfs configfs /sys/kernel/config - 创建 Gadget 目录:
mkdir /sys/kernel/config/usb_gadget/g1 - 设置 VID/PID:
echo 0x0525 > idVendor,echo 0xa4a2 > idProduct - 创建 RNDIS 功能:
mkdir functions/rndis.usb0 - 配置 MAC 地址:
echo "02:11:22:33:44:55" > functions/rndis.usb0/dev_addr - 创建配置并链接功能:
mkdir configs/c.1,ln -s functions/rndis.usb0 configs/c.1/ - 绑定 UDC:
echo "dwc3.0.auto" > UDC
常见陷阱:ConfigFS 操作顺序不可颠倒。必须先创建功能实例并配置参数,再创建配置并链接功能,最后写入 UDC。如果先写 UDC 再链接功能,会导致绑定失败。
2.5 Gadget API 核心接口
Gadget API 是连接功能驱动与 UDC 核心的桥梁。功能驱动通过以下核心接口与底层交互:
| API 函数 | 所属结构 | 功能描述 |
|---|---|---|
usb_ep_alloc_request |
usb_ep | 分配一个 USB 请求结构体 |
usb_ep_queue |
usb_ep | 将请求提交到端点队列,等待硬件传输 |
usb_ep_dequeue |
usb_ep | 从队列中移除一个待处理的请求 |
usb_ep_enable |
usb_ep | 激活端点,使其可接收数据 |
usb_ep_disable |
usb_ep | 停用端点,清空队列 |
usb_gadget_wakeup |
usb_gadget | 向主机发送远程唤醒信号 |
usb_gadget_vbus_connect |
usb_gadget | 模拟 VBUS 连接事件 |
2.5.1 请求生命周期示例
// 1. 分配请求
struct usb_request *req = usb_ep_alloc_request(ep, GFP_ATOMIC);
req->buf = kmalloc(packet_size, GFP_ATOMIC);
req->length = packet_size;
req->complete = my_complete_callback;
req->context = my_private_data;
// 2. 提交请求
usb_ep_queue(ep, req, GFP_ATOMIC);
// 3. 完成回调(中断上下文)
static void my_complete_callback(struct usb_ep *ep, struct usb_request *req) {
if (req->status == 0) {
// 传输成功,处理数据
process_received_data(req->buf, req->actual);
}
// 重新提交请求以接收下一包
usb_ep_queue(ep, req, GFP_ATOMIC);
}
性能关键:在 RNDIS 驱动中,Bulk OUT 端点通常使用 N 个请求的环形队列(N 由 qmult 参数控制)。请求完成回调中立即重新提交,可以最大化总线利用率,减少空闲等待时间。
2.6 本章小结
USB Gadget 框架的四层架构为 RNDIS 驱动提供了清晰的抽象边界:
- UDC 驱动层负责硬件细节,对上层屏蔽寄存器操作。
- UDC 核心层提供统一的 Gadget API,管理端点与请求生命周期。
- 功能驱动层实现 RNDIS 协议逻辑,通过 API 与核心层交互。
- ConfigFS 接口允许用户空间动态组合功能,无需重新编译内核。