2. USB Gadget框架基础

在深入 RNDIS 网络性能优化之前,必须透彻理解 USB Gadget 框架的层次结构。Linux 内核的 USB Gadget 子系统采用分层设计,将硬件控制、协议实现与用户配置解耦。本章将逐层剖析其核心组件:UDC 驱动层、Gadget 功能驱动层、ConfigFS 接口以及贯穿其中的 API 架构。

2.1 USB Gadget 整体架构分层

USB Gadget 框架从底层硬件到上层用户空间共分为四个主要层次。每一层都有明确的职责边界,通过标准化的 API 进行交互。

USB Gadget 整体架构分层图 USB Gadget 框架四层架构 用户空间层 (User Space) ConfigFS 接口 · 用户态工具 (gadget-tools, libusb) Gadget 功能驱动层 (Function Driver) RNDIS · ECM · NCM · Mass Storage · Serial · HID UDC 核心层 (UDC Core) Gadget API · 端点管理 · 请求队列 · 事件处理 UDC 硬件驱动层 (UDC Driver) DWC2 · DWC3 · Chipidea · Renesas · 平台特定实现 数据流方向:用户空间 → 功能驱动 → UDC 核心 → 硬件驱动 → USB 总线
架构要点:上层驱动不直接操作硬件寄存器,下层驱动不关心上层协议细节。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 功能驱动的生命周期

  1. 绑定(Bind):分配端点、注册回调函数、初始化协议状态机。
  2. 设置配置(Set Configuration):主机选择配置后,激活端点并准备接收数据。
  3. 数据传输(Data Transfer):通过 Gadget API 提交 USB 请求(usb_request),处理完成回调。
  4. 解绑(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 典型配置流程

  1. 挂载 ConfigFS:mount -t configfs configfs /sys/kernel/config
  2. 创建 Gadget 目录:mkdir /sys/kernel/config/usb_gadget/g1
  3. 设置 VID/PID:echo 0x0525 > idVendor,echo 0xa4a2 > idProduct
  4. 创建 RNDIS 功能:mkdir functions/rndis.usb0
  5. 配置 MAC 地址:echo "02:11:22:33:44:55" > functions/rndis.usb0/dev_addr
  6. 创建配置并链接功能:mkdir configs/c.1,ln -s functions/rndis.usb0 configs/c.1/
  7. 绑定 UDC:echo "dwc3.0.auto" > UDC
ConfigFS 配置流程与内核交互图 ConfigFS 用户空间配置与内核驱动交互流程 用户空间 echo / mkdir / ln ConfigFS 内核层 sysfs 操作回调 Gadget 核心 usb_gadget_probe_driver mkdir functions/rndis.usb0 → ConfigFS 调用 usb_function_register → 分配端点、初始化协议 ln -s functions/rndis.usb0 configs/c.1/ → 绑定功能到配置,生成描述符 echo "dwc3.0.auto" > UDC → Gadget 核心调用 UDC 驱动 bind,硬件端点就绪 设备就绪,等待主机枚举 主机发送 SET_CONFIGURATION → 功能驱动 set_alt 被调用 → 数据传输开始
常见陷阱: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 接口允许用户空间动态组合功能,无需重新编译内核。