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)
📂 USB Gadget 驱动源码位置
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 功能启用的完整配置流程:

USB Gadget 内核配置流程 USB Gadget 内核配置流程 1. 加载默认配置 2. 选择 ARM 架构 3. 启用 USB_GADGET 4. 选择功能驱动 5. 编译内核/模块 配置说明 • 默认配置:defconfig • 架构选择:ARCH=arm • Gadget 核心:CONFIG_USB_GADGET • 功能驱动:f_mass_storage 等

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+/
🔧 快速验证 Gadget 模块
编译完成后,可在宿主机上使用 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 接口的使用。