3. 开发环境搭建:交叉编译工具链配置、内核源码获取、内核配置选项启用
嵌入式 Linux USB Gadget 开发与普通桌面开发的最大区别在于交叉编译。目标设备(ARM、RISC-V、MIPS 等)资源有限,无法直接运行本地编译工具链,因此必须在性能强劲的宿主机(x86_64)上搭建交叉编译环境,生成目标架构的可执行文件与内核模块。本章将系统讲解从工具链安装、内核源码获取到 Gadget 相关内核配置选项启用的完整流程。
宿主机:Ubuntu 22.04 LTS x86_64
目标板:TI AM335x (ARM Cortex-A8) / Raspberry Pi 3 (ARM Cortex-A53)
交叉工具链:gcc-arm-linux-gnueabihf (GCC 12.2)
内核版本:Linux 6.1 LTS
3.1 交叉编译工具链配置
交叉编译工具链是一套完整的编译、链接、调试工具集合,运行在宿主机上,生成目标架构的二进制代码。推荐使用官方预编译工具链,避免从源码构建的复杂性与兼容性问题。
3.1.1 工具链安装(APT 方式)
# 安装 ARM 32位硬浮点交叉工具链
sudo apt update
sudo apt install -y gcc-arm-linux-gnueabihf binutils-arm-linux-gnueabihf \
g++-arm-linux-gnueabihf libc6-dev-armhf-cross
# 验证安装
arm-linux-gnueabihf-gcc --version
# 输出示例:arm-linux-gnueabihf-gcc (Ubuntu 12.2.0-3ubuntu1) 12.2.0
3.1.2 工具链环境变量设置
为简化后续编译命令,建议将工具链前缀写入环境变量,并加入 ~/.bashrc 永久生效:
export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm
# 验证环境变量
echo $CROSS_COMPILE # 输出:arm-linux-gnueabihf-
echo $ARCH # 输出:arm
3.1.3 测试编译:最小可执行程序
# 编写测试文件 test.c
#include <stdio.h>
int main() {
printf("USB Gadget Dev Env OK\n");
return 0;
}
# 交叉编译
${CROSS_COMPILE}gcc -o test_arm test.c
# 查看目标架构
file test_arm
# 输出:ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked ...
• 若提示
arm-linux-gnueabihf-gcc: command not found,检查 PATH 是否包含 /usr/bin。• 若链接失败提示
cannot find -lc,安装 libc6-dev-armhf-cross 补全 C 库。• 目标板为 ARM64 (aarch64) 时,安装
gcc-aarch64-linux-gnu 并设置 CROSS_COMPILE=aarch64-linux-gnu-。
3.2 内核源码获取
USB Gadget 驱动深度依赖内核源码树,必须获取完整的内核源码(含头文件、Kconfig、Makefile),不能仅使用内核头文件包。
3.2.1 获取方式对比
| 方式 | 命令/URL | 适用场景 | 大小 |
|---|---|---|---|
| 主线内核 tarball | wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.xz |
通用开发、最新特性 | ~140 MB |
| 厂商 BSP 源码 | TI SDK / Raspberry Pi 官方 GitHub | 板级适配、外设驱动 | ~200 MB~1 GB |
| git 克隆主线仓库 | git clone --depth=1 -b v6.1 https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git |
持续跟踪、补丁管理 | ~2 GB (完整) |
3.2.2 解压与目录结构
# 下载并解压主线内核
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.xz
tar -xf linux-6.1.tar.xz
cd linux-6.1
# 关键目录说明
ls -F | grep /
arch/ # 架构相关代码 (arch/arm/, arch/arm64/)
drivers/ # 设备驱动 (drivers/usb/gadget/)
include/ # 内核头文件 (include/linux/usb/)
Documentation/ # 内核文档 (Documentation/usb/gadget_configfs.rst)
drivers/usb/gadget/ — Gadget API 框架drivers/usb/gadget/function/ — 各个功能驱动(f_mass_storage.c, f_serial.c, f_ecm.c 等)drivers/usb/gadget/legacy/ — 旧版 Gadget 驱动(已弃用,建议使用 configfs)
3.3 内核配置选项启用(CONFIG_USB_GADGET 等)
USB Gadget 功能通过内核 Kconfig 选项控制。必须正确启用 CONFIG_USB_GADGET 及其子选项,才能编译出支持 Gadget 设备的内核与模块。
3.3.1 配置流程总览
下图展示了从默认配置到 Gadget 功能启用的完整配置流程:
3.3.2 关键 Kconfig 选项详解
以下表格列出了 USB Gadget 开发必须启用的核心配置选项及其依赖关系:
| 配置选项 | 类型 | 依赖 | 说明 |
|---|---|---|---|
CONFIG_USB_GADGET |
bool | USB_SUPPORT |
USB Gadget 核心框架,必须启用 |
CONFIG_USB_GADGET_DEBUG |
bool | USB_GADGET |
Gadget 调试信息输出,开发阶段建议开启 |
CONFIG_USB_GADGET_DEBUG_FILES |
bool | USB_GADGET |
在 debugfs 中暴露 Gadget 状态 |
CONFIG_USB_GADGET_DEBUG_FS |
bool | USB_GADGET |
启用 debugfs 接口 |
CONFIG_USB_GADGET_VBUS_DRAW |
int | USB_GADGET |
设备从 VBUS 获取的最大电流 (mA),默认 100 |
CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS |
int | USB_GADGET |
Mass Storage 功能使用的缓冲区数量,默认 2 |
CONFIG_USB_CONFIGFS |
bool | USB_GADGET |
启用 ConfigFS 接口(推荐方式) |
CONFIG_USB_CONFIGFS_UEVENT |
bool | USB_CONFIGFS |
ConfigFS 事件通知 |
3.3.3 功能驱动选项(Function Drivers)
根据实际需求选择以下功能驱动(以 CONFIG_USB_CONFIGFS 为基础):
| 功能驱动选项 | 描述 | 典型应用 |
|---|---|---|
CONFIG_USB_F_MASS_STORAGE |
USB 大容量存储设备 | U 盘、SD 卡读卡器 |
CONFIG_USB_F_SERIAL |
USB 串口(ACM) | 串口调试、Modem |
CONFIG_USB_F_ECM |
USB 以太网控制模型 | RNDIS 替代、网络共享 |
CONFIG_USB_F_RNDIS |
Windows 远程 NDIS | Windows 主机网络共享 |
CONFIG_USB_F_HID |
USB 人机交互设备 | 键盘、鼠标、游戏手柄 |
CONFIG_USB_F_ACM |
USB 抽象控制模型 | 传统串口仿真 |
CONFIG_USB_F_UVC |
USB 视频类设备 | USB 摄像头 |
CONFIG_USB_F_AUDIO_SRC |
USB 音频源设备 | USB 麦克风/扬声器 |
3.3.4 实际操作:配置内核
以下命令以 TI AM335x 平台为例,演示如何启用 USB Gadget 相关选项:
# 1. 设置环境变量
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
# 2. 加载厂商默认配置(以 TI AM335x 为例)
make am335x_evm_defconfig
# 3. 进入菜单配置界面
make menuconfig
# 4. 在 menuconfig 中依次进入以下路径并启用选项:
# Device Drivers --->
# [*] USB support --->
# <*> USB Gadget Support --->
# [*] Debugging interface (DEVELOPMENT)
# [*] Debug filesystem
# (100) Maximum VBUS Draw (mA)
# <*> USB Gadget Drivers --->
# <*> USB Gadget Functions (ConfigFS) --->
# <M> Mass Storage Gadget
# <M> Serial Gadget
# <M> Ethernet Gadget (ECM)
# <M> RNDIS
# <M> HID Gadget
# 5. 保存退出后,验证配置
grep -E "CONFIG_USB_GADGET|CONFIG_USB_CONFIGFS|CONFIG_USB_F_" .config
3.3.5 验证配置结果
执行上述 grep 命令后,应看到类似输出:
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
CONFIG_USB_GADGET_VBUS_DRAW=100
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_UEVENT=y
CONFIG_USB_F_MASS_STORAGE=m
CONFIG_USB_F_SERIAL=m
CONFIG_USB_F_ECM=m
CONFIG_USB_F_RNDIS=m
CONFIG_USB_F_HID=m
•
CONFIG_USB_GADGET 必须为 y(编译进内核),不能为 m(模块),否则系统启动时无法注册 Gadget 设备。• 功能驱动建议编译为模块(
m),便于调试时单独加载/卸载,减少内核体积。• 若目标平台使用 Device Tree,需确保 DTS 中 USB 控制器节点状态为
okay,且 dr_mode 设置为 "peripheral" 或 "otg"。
3.4 编译内核与模块
配置完成后,执行编译命令生成内核镜像与 Gadget 功能模块:
# 编译内核(使用 4 核并行)
make -j4 zImage
# 编译设备树
make -j4 dtbs
# 编译模块
make -j4 modules
# 安装模块到目标文件系统(假设挂载在 /mnt/rootfs)
make modules_install INSTALL_MOD_PATH=/mnt/rootfs
# 输出文件位置
# 内核镜像:arch/arm/boot/zImage
# 设备树:arch/arm/boot/dts/am335x-evm.dtb
# 模块:/mnt/rootfs/lib/modules/6.1.0+/
编译完成后,可在宿主机上使用
modinfo 查看模块信息:modinfo drivers/usb/gadget/function/usb_f_mass_storage.ko输出应包含描述、作者、依赖、参数等元数据,确认模块编译正确。
3.5 开发环境验证清单
完成上述步骤后,使用以下清单逐项确认环境就绪:
| 检查项 | 验证方法 | 预期结果 |
|---|---|---|
| 交叉工具链 | arm-linux-gnueabihf-gcc --version |
显示 GCC 版本号 |
| 内核源码 | ls linux-6.1/Makefile |
文件存在 |
| Gadget 配置 | grep CONFIG_USB_GADGET .config |
=y |
| 功能驱动 | grep CONFIG_USB_F_MASS_STORAGE .config |
=m |
| 内核编译 | ls arch/arm/boot/zImage |
文件存在且非空 |
| 模块编译 | find . -name "usb_f_mass_storage.ko" |
找到 .ko 文件 |
至此,嵌入式 Linux USB Gadget 开发环境搭建完毕。下一阶段将深入 Gadget 驱动框架与 ConfigFS 接口的使用。