4. 模块编译与加载
在嵌入式 Linux 开发中,将 ECM Gadget 驱动编译为可动态加载的内核模块(.ko),而非直接编译进内核镜像,是调试与部署阶段的最佳实践。这种方式允许开发者在不重启系统的情况下快速迭代驱动代码、调整运行时参数,并灵活控制 USB Gadget 功能的启用与关闭。本章将详细讲解 ECM Gadget 模块的编译配置、动态加载/卸载的标准流程,以及通过 modprobe 和 insmod 传递参数的核心技巧。
4.1 编译为内核模块的配置方法
将 ECM Gadget 驱动编译为模块,需要在 Linux 内核源码树中正确配置 Kconfig 和 Makefile。对于使用标准内核源码的工程,通常通过 menuconfig 或 defconfig 进行配置。
4.1.1 Kconfig 与 Makefile 配置要点
ECM Gadget 驱动位于内核源码的 drivers/usb/gadget/function/ 目录下。其模块名称为 u_ecm.ko(核心功能模块)和 g_ecm.ko(复合设备模块)。
| 配置项 | Kconfig 宏 | 说明 |
|---|---|---|
| USB Gadget 支持 | CONFIG_USB_GADGET |
必须开启,作为 Gadget 框架基础 |
| ECM 功能模块 | CONFIG_USB_GADGET_U_ECM |
编译为 u_ecm.ko,提供 ECM 协议实现 |
| ECM 复合设备 | CONFIG_USB_ETH |
编译为 g_ecm.ko,封装为网络设备接口 |
| 模块版本支持 | CONFIG_MODVERSIONS |
建议开启,避免模块与内核版本不匹配 |
典型配置命令:
# 进入内核源码根目录
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
# 在 menuconfig 界面中依次选择:
# Device Drivers --->
# [*] USB support --->
# <M> USB Gadget Support --->
# USB Gadget Drivers --->
# <M> Ethernet Gadget (ECM subset)
# <M> USB Ethernet Gadget (ECM + RNDIS + ...)
配置完成后,执行 make modules 即可在 drivers/usb/gadget/function/ 目录下生成 u_ecm.ko 和 g_ecm.ko。
CONFIG_USB_GADGET_U_ECM 和 CONFIG_USB_ETH 设置为 m(模块),而非 y(内置)。这样可以在系统启动后按需加载,避免因驱动初始化失败导致整个 USB 子系统挂死。
4.1.2 模块依赖关系
ECM Gadget 模块存在明确的依赖链,加载时必须按顺序进行。下图展示了模块间的依赖关系:
4.2 动态加载与卸载流程
在目标板上进行 ECM Gadget 模块的动态加载与卸载,需要遵循严格的顺序,以避免资源冲突或内核崩溃。
4.2.1 标准加载流程
- 确认 USB Gadget 框架已加载: 检查
/sys/kernel/config/usb_gadget/目录是否存在,若不存在则需先加载libcomposite.ko。 - 加载 UDC 驱动: 根据硬件平台加载对应的 UDC 控制器驱动,例如
dwc2.ko或dwc3.ko。 - 加载 ECM 功能模块: 使用
modprobe或insmod加载u_ecm.ko。 - 加载复合设备模块: 加载
g_ecm.ko,该模块会自动创建网络接口(如usb0)。 - 配置 USB 设备属性: 通过 ConfigFS 设置 VID、PID、序列号等。
- 绑定 UDC: 将 Gadget 实例绑定到具体的 UDC 控制器。
完整加载脚本示例:
#!/bin/sh
# ECM Gadget 动态加载脚本
# 1. 加载 USB Gadget 核心框架
modprobe libcomposite
# 2. 加载 UDC 驱动(以 DWC2 为例)
modprobe dwc2
# 3. 加载 ECM 功能模块
modprobe u_ecm
# 4. 加载 ECM 复合设备模块
modprobe g_ecm
# 5. 通过 ConfigFS 配置设备属性
cd /sys/kernel/config/usb_gadget/
mkdir g1 && cd g1
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # ECM Gadget
echo 0x0100 > bcdDevice
echo 0x0200 > bcdUSB
# 6. 创建 ECM 功能实例
mkdir functions/ecm.usb0
# 7. 绑定 UDC
echo "dwc2.0" > UDC
echo "ECM Gadget 加载完成,网络接口 usb0 已创建"
4.2.2 标准卸载流程
卸载顺序与加载顺序相反,必须从最上层的复合设备开始,逐级释放资源。
- 解绑 UDC: 向
UDC文件写入空字符串。 - 删除 Gadget 目录: 移除 ConfigFS 中创建的目录结构。
- 卸载复合设备模块:
rmmod g_ecm。 - 卸载功能模块:
rmmod u_ecm。 - 卸载 UDC 驱动:
rmmod dwc2。 - 卸载核心框架:
rmmod libcomposite。
usb0)。可以使用 ip link set usb0 down 先关闭接口,否则卸载模块可能导致内核崩溃或内存泄漏。
4.3 modprobe 与 insmod 参数传递
在 ECM Gadget 驱动中,许多关键参数(如 MAC 地址、缓冲区大小、中断间隔)可以在模块加载时通过参数传递进行定制。正确使用参数传递机制,是性能调优的第一步。
4.3.1 参数传递方式对比
| 工具 | 参数格式 | 自动处理依赖 | 适用场景 |
|---|---|---|---|
insmod |
insmod module.ko param=value |
否 | 调试阶段,手动控制加载顺序 |
modprobe |
modprobe module param=value |
是 | 生产环境,自动解决依赖 |
4.3.2 ECM Gadget 关键参数详解
g_ecm.ko 模块支持以下可调参数,这些参数在驱动初始化阶段通过 module_param() 宏注册:
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
dev_addr |
charp | "00:11:22:33:44:55" | 设备端 MAC 地址 |
host_addr |
charp | "00:22:44:66:88:aa" | 主机端 MAC 地址 |
qmult |
int | 5 | 队列乘数,影响吞吐量 |
is_zlp_ok |
bool | 1 | 是否允许零长度包 |
使用 insmod 传递参数示例:
# 加载 u_ecm 模块(无参数)
insmod /lib/modules/5.10.0/kernel/drivers/usb/gadget/function/u_ecm.ko
# 加载 g_ecm 模块并传递 MAC 地址和队列参数
insmod /lib/modules/5.10.0/kernel/drivers/usb/gadget/function/g_ecm.ko \
dev_addr="00:11:22:33:44:66" \
host_addr="00:22:44:66:88:bb" \
qmult=10
使用 modprobe 传递参数示例:
# modprobe 会自动查找模块并解决依赖
modprobe g_ecm \
dev_addr="00:11:22:33:44:66" \
host_addr="00:22:44:66:88:bb" \
qmult=10
qmult 参数控制每个 USB 请求的 URBs 数量。在高速 USB 2.0 或 USB 3.0 环境下,将 qmult 从默认的 5 提升到 15-20,可以显著提高批量传输的吞吐量。但过高的值会消耗更多内存,需根据系统可用内存进行权衡。
4.3.3 参数持久化配置
在嵌入式产品中,通常需要将参数固化到配置文件中,避免每次手动输入。可以通过 /etc/modprobe.d/ 目录下的配置文件实现:
# 创建 /etc/modprobe.d/ecm-gadget.conf
echo "options g_ecm dev_addr=00:11:22:33:44:66 host_addr=00:22:44:66:88:bb qmult=15" \
> /etc/modprobe.d/ecm-gadget.conf
# 之后只需执行 modprobe g_ecm,参数会自动加载
modprobe g_ecm
4.4 加载状态验证与调试
模块加载完成后,需要通过系统工具验证驱动是否正常工作。
4.4.1 验证步骤
- 检查模块列表:
lsmod | grep -E "g_ecm|u_ecm|libcomposite" - 检查网络接口:
ip link show usb0 - 检查 USB 设备状态:
cat /sys/kernel/debug/usb/gadget/status - 检查内核日志:
dmesg | tail -20
正常加载后的 dmesg 输出示例:
[ 1234.567890] g_ecm: Ethernet Gadget, version: 1.0
[ 1234.567895] g_ecm: using random self ethernet address
[ 1234.567900] g_ecm: using random host ethernet address
[ 1234.567905] usb0: HOST MAC 00:22:44:66:88:bb
[ 1234.567910] usb0: MAC 00:11:22:33:44:66
[ 1234.567915] g_ecm: qmult set to 15
4.4.2 常见加载失败原因
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
modprobe: FATAL: Module not found |
模块未安装到 /lib/modules/ |
执行 make modules_install |
Unknown symbol |
依赖模块未加载或版本不匹配 | 检查 modprobe --show-depends |
device or resource busy |
UDC 已被其他 Gadget 占用 | 卸载冲突模块或重启 USB 子系统 |
usb0: Network is down |
未绑定 UDC 或绑定失败 | 检查 /sys/kernel/config/usb_gadget/ 配置 |
4.5 本章小结
将 ECM Gadget 编译为内核模块并动态加载,是嵌入式 USB 开发中的核心技能。开发者需要掌握:
- 通过 Kconfig 将
u_ecm和g_ecm配置为模块(m) - 严格遵循
libcomposite → UDC → u_ecm → g_ecm的加载顺序 - 利用
modprobe的自动依赖解析和参数传递能力简化部署 - 通过
dev_addr、host_addr、qmult等参数进行运行时调优 - 使用
lsmod、dmesg、ip link验证加载状态