3. RNDIS Gadget驱动源码结构

RNDIS Gadget 驱动的核心实现位于 drivers/usb/gadget/function/f_rndis.c。该文件实现了 USB 设备侧 RNDIS 协议的全部逻辑,包括控制请求处理、数据传输通道建立、暂停/恢复流程以及网络设备接口的绑定。本章将深入分析该文件的结构、核心数据结构以及 rndis_data_connect、rndis_halt 等关键函数的实现细节。

3.1 文件整体架构与模块依赖

f_rndis.c 位于 USB Gadget 功能驱动层,其上层依赖 drivers/net/usb/rndis_host.c(主机侧)和 drivers/usb/gadget/legacy 中的复合设备构造逻辑。文件内部按功能划分为以下模块:

模块 对应函数/结构体 职责
初始化/去初始化 rndis_bind() / rndis_unbind() 分配资源、注册网络设备、配置端点
控制请求处理 rndis_setup() / rndis_control() 解析 USB 控制传输、处理 OID 查询/设置
数据连接管理 rndis_data_connect() 建立 RNDIS 数据通道、配置过滤器
暂停/恢复 rndis_halt() / rndis_resume() 暂停数据通信、释放资源、恢复链路
数据收发 rndis_rx_fixup() / rndis_tx_fixup() USB 数据包与网络 skb 之间的格式转换
关键依赖: f_rndis.c 通过 rndis_common.h 共享 RNDIS 消息结构体定义,并通过 u_ether.h 获取网络设备操作接口。性能优化时需同时关注这两个头文件中的数据结构布局。

3.2 核心数据结构:f_rndis_opts 与 rndis_data_context

驱动使用两个核心结构体管理实例状态:

  • f_rndis_opts:功能选项结构体,保存绑定参数、网络设备引用和引用计数。
  • rndis_data_context:数据通道上下文,管理连接状态、过滤器掩码和暂停标志。
// f_rndis_opts 结构体(简化)
struct f_rndis_opts {
    struct usb_function_instance    func_inst;
    struct net_device              *net;
    struct rndis_params            *params;
    bool                           bound;
    refcount_t                     refcnt;
};

// rndis_data_context 结构体(简化)
struct rndis_data_context {
    struct usb_ep                  *in_ep, *out_ecm, *out_rndis;
    struct usb_request             *req;
    u32                            filter;
    bool                           connected;
    bool                           halted;
    spinlock_t                     lock;
};
性能提示: rndis_data_context 中的 filter 字段在数据连接建立时由主机设置,驱动应避免在数据路径中频繁读取该字段。建议在 rndis_data_connect() 中缓存到本地变量。

3.3 rndis_data_connect():数据通道建立流程

rndis_data_connect() 是 RNDIS 数据通信的起点,由主机发送 REMOTE_NDIS_INITIALIZE_MSG 或 REMOTE_NDIS_SET_MSG(OID_GEN_CURRENT_PACKET_FILTER)触发。其核心流程如下:

  1. 参数校验:检查 RNDIS 消息长度、版本号、MTU 是否在合理范围内。
  2. 过滤器配置:从消息中提取 PacketFilter 字段,写入 rndis_data_context.filter。
  3. 端点准备:为 in_ep(主机到设备)和 out_rndis(设备到主机)分配 USB 请求块(URB)。
  4. 状态更新:设置 connected = true,唤醒等待数据通道的网络协议栈。
  5. 响应构造:生成 REMOTE_NDIS_INITIALIZE_CMPLT 或 REMOTE_NDIS_SET_CMPLT 消息,通过控制端点返回。
rndis_data_connect() 执行流程 收到 RNDIS 连接请求 校验消息长度/版本/MTU 提取 PacketFilter 字段 分配端点 URB 设置 connected=true 构造并返回响应消息 校验失败时直接返回 RNDIS_STATUS_FAILURE 不会进入后续步骤 URB 分配失败时 回滚已设置的 filter
常见错误: 在 rndis_data_connect() 中如果 URB 分配失败,必须回滚 filter 设置并返回错误码。部分早期驱动版本遗漏了此回滚,导致后续数据包被错误过滤。

3.4 rndis_halt():暂停与资源释放

rndis_halt() 由主机发送 REMOTE_NDIS_HALT_MSG 触发,或在 USB 总线复位时由 Gadget 框架自动调用。该函数执行以下操作:

  • 暂停标志设置:将 rndis_data_context.halted 置为 true。
  • URB 回收:释放所有已分配但未完成的 USB 请求块,防止内存泄漏。
  • 端点禁用:调用 usb_ep_disable() 禁用 in_ep 和 out_rndis 端点。
  • 状态清理:将 connected 置为 false,清空过滤器掩码。
  • 通知上层:通过 netif_carrier_off() 通知网络协议栈链路断开。
static int rndis_halt(struct usb_function *f)
{
    struct rndis_data_context *ctx = get_rndis_ctx(f);
    unsigned long flags;

    spin_lock_irqsave(&ctx->lock, flags);
    ctx->halted = true;
    ctx->connected = false;
    ctx->filter = 0;
    spin_unlock_irqrestore(&ctx->lock, flags);

    /* 回收所有未完成的请求 */
    rndis_free_requests(ctx);

    /* 禁用端点 */
    usb_ep_disable(ctx->in_ep);
    usb_ep_disable(ctx->out_rndis);

    /* 通知网络层 */
    netif_carrier_off(ctx->net);

    return 0;
}
优化要点: rndis_halt() 中 spin_lock_irqsave 的使用确保了在中断上下文也能安全调用。但在高吞吐场景下,锁持有时间应尽量缩短——建议将 URB 回收和端点禁用操作移出临界区,仅在临界区更新状态标志。

3.5 关键函数对比:rndis_data_connect vs rndis_halt

对比维度 rndis_data_connect rndis_halt
触发条件 主机发送 INITIALIZE / SET 消息 主机发送 HALT 消息 / 总线复位
核心操作 分配资源、配置过滤器 回收资源、禁用端点
状态变更 connected = true halted = true, connected = false
网络层影响 netif_carrier_on() netif_carrier_off()
可重入性 不可重入(需外部互斥) 可重入(自旋锁保护)
性能敏感度 中等(仅在连接时调用) 低(仅在断开时调用)

3.6 数据路径中的关键辅助函数

除了连接/暂停函数,f_rndis.c 中还有两个对性能至关重要的辅助函数:

  • rndis_rx_fixup():将 USB 接收到的 RNDIS 消息解析为网络 skb,处理消息分片和重组。
  • rndis_tx_fixup():将网络 skb 封装为 RNDIS 数据消息,添加头部并提交到 USB 端点。

这两个函数在数据路径中被频繁调用,是性能优化的主要目标。后续章节将专门讨论它们的零拷贝改造和批量提交策略。

f_rndis.c 函数调用关系 USB Gadget 框架 rndis_data_connect rndis_halt rndis_setup rndis_rx_fixup rndis_tx_fixup rndis_control

3.7 本章小结

f_rndis.c 是 RNDIS Gadget 驱动的核心实现文件,其设计遵循 USB Gadget 功能驱动的标准架构。本章重点分析了:

  • 文件模块划分与核心数据结构 f_rndis_opts 和 rndis_data_context。
  • rndis_data_connect() 的 5 步执行流程及其错误回滚机制。
  • rndis_halt() 的暂停与资源释放逻辑,以及锁的使用优化建议。
  • 数据路径函数 rndis_rx_fixup 和 rndis_tx_fixup 的定位与后续优化方向。