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)触发。其核心流程如下:
- 参数校验:检查 RNDIS 消息长度、版本号、MTU 是否在合理范围内。
- 过滤器配置:从消息中提取
PacketFilter字段,写入rndis_data_context.filter。 - 端点准备:为
in_ep(主机到设备)和out_rndis(设备到主机)分配 USB 请求块(URB)。 - 状态更新:设置
connected = true,唤醒等待数据通道的网络协议栈。 - 响应构造:生成 REMOTE_NDIS_INITIALIZE_CMPLT 或 REMOTE_NDIS_SET_CMPLT 消息,通过控制端点返回。
常见错误: 在
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 端点。
这两个函数在数据路径中被频繁调用,是性能优化的主要目标。后续章节将专门讨论它们的零拷贝改造和批量提交策略。
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的定位与后续优化方向。