3. 开发环境搭建
嵌入式Linux驱动开发与原生PC开发最大的区别在于交叉编译。目标板(ARM、RISC-V等架构)资源有限,无法直接运行本地编译器,因此需要在性能强劲的宿主机(x86_64)上安装交叉编译工具链,生成目标架构的可执行文件。本章将系统讲解Gadget驱动开发环境的完整搭建流程,包括工具链安装、内核源码获取、内核配置与编译,为后续的虚拟网卡(Ethernet Gadget)和虚拟串口(Serial Gadget)实战打下坚实基础。
3.1 交叉编译工具链安装
交叉编译工具链是一套完整的工具集合,包含编译器(gcc)、汇编器(as)、链接器(ld)、库文件(glibc/uclibc)以及调试器(gdb)。根据目标架构的不同,工具链的命名规则通常为:arch-vendor-os-abi-。例如,ARM 架构的交叉编译器前缀为 arm-linux-gnueabihf-,其中 hf 表示硬浮点(Hard Float)。
3.1.1 工具链选择与下载
推荐使用 Linaro 提供的预编译工具链,稳定且社区支持广泛。也可以使用 Buildroot 或 Yocto 自行构建,但初次搭建建议直接下载预编译版本。
| 工具链名称 | 目标架构 | ABI | 适用场景 |
|---|---|---|---|
| gcc-arm-10.2-2020.11-x86_64-arm-linux-gnueabihf | ARMv7-A | gnueabihf (硬浮点) | 全志H3/H5、树莓派2/3 |
| gcc-arm-10.2-2020.11-x86_64-aarch64-linux-gnu | ARMv8-A (64位) | gnu (软浮点) | 树莓派4/5、RK3399 |
| riscv64-unknown-linux-gnu- | RISC-V 64位 | gnu | SiFive、K210 |
3.1.2 安装步骤
- 下载工具链:从 Linaro 官方仓库或镜像站下载对应版本。
- 解压到指定目录:建议统一放置在
/opt/toolchains/下。 - 配置环境变量:将工具链的 bin 目录添加到
PATH中。 - 验证安装:使用
arm-linux-gnueabihf-gcc --version检查版本。
# 下载 ARM 32位硬浮点工具链(约 200MB)
wget https://releases.linaro.org/components/toolchain/binaries/10.2-2020.11/arm-linux-gnueabihf/gcc-linaro-10.2.1-2020.11-x86_64_arm-linux-gnueabihf.tar.xz
# 解压到 /opt/toolchains
sudo mkdir -p /opt/toolchains
sudo tar -xvf gcc-linaro-10.2.1-2020.11-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/toolchains/
# 添加环境变量(写入 ~/.bashrc 或 ~/.zshrc)
export PATH=$PATH:/opt/toolchains/gcc-linaro-10.2.1-2020.11-x86_64_arm-linux-gnueabihf/bin
# 重新加载配置
source ~/.bashrc
# 验证
arm-linux-gnueabihf-gcc --version
# 输出示例:arm-linux-gnueabihf-gcc (Linaro GCC 10.2-2020.11) 10.2.1 20201103
arm-linux-gnueabihf-gcc --version 提示 "command not found",请确认 PATH 路径是否正确,以及解压后的 bin 目录是否存在。可以使用 ls /opt/toolchains/gcc-linaro-10.2.1-2020.11-x86_64_arm-linux-gnueabihf/bin/ 查看。
3.1.3 交叉编译测试
编写一个简单的 Hello World 程序,使用交叉编译器编译,并通过 file 命令验证生成的可执行文件是否为 ARM 架构。
# 创建测试文件
cat > hello.c << EOF
#include <stdio.h>
int main(void)
{
printf("Hello from ARM Gadget!\n");
return 0;
}
EOF
# 交叉编译
arm-linux-gnueabihf-gcc -o hello_arm hello.c
# 查看文件类型
file hello_arm
# 输出:hello_arm: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, not stripped
-static 参数编译静态可执行文件,避免目标板上缺少动态库。
3.2 Linux内核源码获取
Gadget 驱动是 Linux 内核的一部分,因此必须获取内核源码并进行配置编译。推荐使用 Linux 内核主线版本或长期支持(LTS)版本。本课程基于 Linux 6.1 LTS,该版本对 USB Gadget 框架支持完善,且社区文档丰富。
3.2.1 源码获取方式
| 方式 | 命令/链接 | 适用场景 |
|---|---|---|
| 主线内核 tar 包 | wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.76.tar.xz |
快速获取,无需 git 历史 |
| git 克隆主线仓库 | git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git |
需要跟踪最新提交或打补丁 |
| 厂商 BSP 内核 | 全志、NXP、Rockchip 等官方 SDK | 适配特定开发板,包含板级补丁 |
# 方式一:下载 tar 包(推荐,体积小、速度快)
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.76.tar.xz
tar -xvf linux-6.1.76.tar.xz
cd linux-6.1.76
# 方式二:git 克隆(需要安装 git)
git clone --depth=1 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git -b v6.1.76
cd linux
3.2.2 内核源码目录结构
理解内核源码的目录结构有助于快速定位 Gadget 相关代码。下表列出了与 USB Gadget 开发密切相关的目录:
| 目录路径 | 说明 |
|---|---|
drivers/usb/gadget/ |
USB Gadget 核心框架及所有 Function 驱动(Ethernet、Serial、Mass Storage 等) |
drivers/usb/gadget/legacy/ |
旧版 Gadget 驱动(g_ether、g_serial 等),基于 Legacy 框架 |
drivers/usb/gadget/function/ |
新版 ConfigFS 框架下的 Function 驱动(u_ether.c、u_serial.c 等) |
drivers/usb/gadget/udc/ |
UDC(USB Device Controller)驱动,与具体硬件平台相关 |
include/linux/usb/gadget.h |
Gadget 核心 API 头文件,定义 usb_gadget、usb_request 等关键结构体 |
Documentation/usb/gadget_configfs.rst |
ConfigFS 接口的官方文档,是编写 Gadget 脚本的重要参考 |
3.3 内核配置与编译基础
内核配置是决定哪些功能被编译进内核或编译为模块的关键步骤。对于 Gadget 驱动开发,需要确保 USB Gadget 框架、ConfigFS、以及具体的 Function 驱动(Ethernet、Serial)被正确启用。
3.3.1 配置方法概述
Linux 内核提供多种配置界面,推荐使用 menuconfig(基于 ncurses 的文本菜单界面),操作直观且支持搜索。
| 配置命令 | 界面类型 | 适用场景 |
|---|---|---|
make menuconfig |
文本菜单(推荐) | SSH 远程开发、无图形界面环境 |
make xconfig |
图形界面(Qt) | 本地桌面环境,搜索更方便 |
make defconfig |
默认配置 | 快速生成基础配置,再手动调整 |
make savedefconfig |
精简配置 | 生成最小化 .config,便于版本管理 |
3.3.2 Gadget 相关配置项
在 menuconfig 中,需要依次进入以下路径并启用对应选项。下表列出了关键配置项及其含义:
| 配置路径 | 配置项 | 推荐值 | 说明 |
|---|---|---|---|
| Device Drivers → USB support → USB Gadget Support | CONFIG_USB_GADGET |
Y (编译进内核) | USB Gadget 核心框架,必须启用 |
| USB Gadget Support → USB Gadget Drivers | CONFIG_USB_GADGETFS |
M (模块) | GadgetFS 用户空间驱动接口 |
| USB Gadget Support → USB Gadget Drivers | CONFIG_USB_CONFIGFS |
M (模块) | ConfigFS 接口,用于动态配置 Gadget |
| USB Gadget Support → USB Gadget Drivers → USB ConfigFS | CONFIG_USB_CONFIGFS_ACM |
M (模块) | 虚拟串口(ACM)Function |
| USB Gadget Support → USB Gadget Drivers → USB ConfigFS | CONFIG_USB_CONFIGFS_ECM |
M (模块) | 虚拟网卡(ECM)Function |
| USB Gadget Support → USB Gadget Drivers → USB ConfigFS | CONFIG_USB_CONFIGFS_EEM |
M (模块) | EEM 网卡 Function(ECM 的增强版) |
| USB Gadget Support → USB Gadget Drivers → USB ConfigFS | CONFIG_USB_CONFIGFS_RNDIS |
M (模块) | RNDIS 网卡 Function(Windows 兼容) |
| USB Gadget Support → USB Gadget Drivers → USB ConfigFS | CONFIG_USB_CONFIGFS_SERIAL |
M (模块) | 通用串口 Function |
/ 键可以搜索配置项名称。例如输入 CONFIG_USB_CONFIGFS 即可快速定位到该选项,无需逐层查找。
3.3.3 编译内核与模块
配置完成后,执行编译命令。对于交叉编译,需要指定目标架构和交叉编译器前缀。
# 设置环境变量(每次编译前执行,或写入 ~/.bashrc)
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
# 生成默认配置(基于 ARM 架构)
make defconfig
# 进入 menuconfig 手动调整 Gadget 配置
make menuconfig
# 编译内核(使用 -jN 并行编译,N 为 CPU 核心数)
make -j4 zImage
# 编译设备树
make -j4 dtbs
# 编译内核模块
make -j4 modules
# 安装模块到目标目录(可选,用于后续打包)
make modules_install INSTALL_MOD_PATH=./modules_install
make M=drivers/usb/gadget 仅编译该子目录,节省时间。例如:make -j4 M=drivers/usb/gadget modules。
3.3.4 编译产物说明
编译完成后,在源码根目录下会生成以下关键文件:
| 文件/目录 | 说明 |
|---|---|
arch/arm/boot/zImage |
压缩后的内核镜像,用于引导目标板 |
arch/arm/boot/dts/ |
编译生成的设备树文件(.dtb) |
drivers/usb/gadget/ |
编译生成的 Gadget 模块(.ko 文件) |
modules_install/ |
安装后的模块目录,包含 lib/modules/6.1.76/ |
.config |
当前使用的内核配置文件,建议备份 |
3.4 开发环境架构总览
下面通过一张 SVG 架构图展示宿主机与目标板之间的交叉编译关系,以及内核源码、工具链、模块之间的数据流。
3.5 常见问题与避坑指南
原因:环境变量未正确设置,或工具链路径不存在。
解决:执行
echo $PATH 检查是否包含工具链 bin 目录,使用绝对路径测试:/opt/toolchains/.../bin/arm-linux-gnueabihf-gcc --version。
原因:未正确设置
ARCH=arm 环境变量,导致 menuconfig 显示的是 x86 架构的配置。解决:确保在执行
make menuconfig 前已导出 export ARCH=arm。
~/.bashrc 或创建一个独立的编译脚本 build.sh,避免每次手动设置:#!/bin/bash
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
export PATH=$PATH:/opt/toolchains/gcc-linaro-10.2.1-2020.11-x86_64_arm-linux-gnueabihf/bin
make -j4 zImage dtbs modules
3.6 本章小结
本章完成了 Gadget 驱动开发环境的完整搭建:
- 交叉编译工具链:安装并验证了
arm-linux-gnueabihf-工具链,能够编译 ARM 架构的可执行文件。 - 内核源码获取:下载了 Linux 6.1 LTS 源码,并熟悉了 Gadget 相关目录结构。
- 内核配置与编译:通过 menuconfig 启用了 USB Gadget 框架、ConfigFS、ECM 网卡和 ACM 串口功能,并成功编译出内核镜像和模块。