2. 开发环境搭建

Platform 驱动开发与普通应用开发最大的区别在于:驱动代码运行在内核空间,编译目标不是本机 x86 环境,而是目标开发板或 QEMU 模拟的 ARM/ARM64 平台。因此,开发环境搭建的核心是建立一条“宿主机编译 → 目标机运行”的完整链路。这条链路包含四个关键环节:交叉编译工具链、内核源码与配置、目标运行环境、驱动编译与加载。只有这四个环节全部打通,后续的字符设备、platform_driver、设备树匹配等实验才能顺利开展。

一、交叉编译工具链:从 x86 到 ARM 的桥梁

交叉编译工具链是一组运行在宿主机(通常是 x86_64 Linux)上、但生成目标平台(如 ARM32 或 ARM64)可执行代码的工具集合。它至少包含 gcc、ld、objcopy、strip 等组件。对于 Linux 内核和驱动开发,最常用的是 Linaro 或 ARM 官方提供的 GNU 工具链,命名通常形如 arm-linux-gnueabihf- 或 aarch64-linux-gnu-。

安装方式有两种:一是通过发行版包管理器安装,例如 Ubuntu 下执行 sudo apt install gcc-arm-linux-gnueabihf;二是下载官方预编译工具链并解压到 /opt 目录,再将其 bin 路径加入 PATH。安装完成后必须验证版本与目标架构是否匹配。

# 查看交叉编译器版本
arm-linux-gnueabihf-gcc -v

# 验证能否生成 ARM 目标文件
echo 'int main(){return 0;}' > test.c
arm-linux-gnueabihf-gcc test.c -o test
file test
# 输出应包含: ELF 32-bit LSB executable, ARM, EABI5
工具链的 ABI 必须与内核配置一致。例如内核使用 EABI hard-float,工具链也应选择 gnueabihf,否则驱动模块加载时会出现 “invalid module format” 或符号版本不匹配。

二、内核源码获取与配置

驱动必须针对某一具体内核版本编译,因为内核模块与内核之间的符号、结构体布局、配置选项强相关。获取内核源码有三种常见途径:从 kernel.org 下载主线源码、从 SoC 厂商获取 BSP 内核、或使用开发板厂商提供的 SDK。对于 QEMU 实验,推荐使用与 QEMU 机器类型匹配的主线内核,例如 linux-6.6。

获取后先解压,再进入源码目录进行配置。配置的目标是生成 .config 文件,它决定了哪些功能被编译进内核、哪些编译为模块。常用命令如下:

tar -xvf linux-6.6.tar.xz
cd linux-6.6

# 指定交叉编译器和目标架构
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

# 使用默认配置或开发板 defconfig
make vexpress_defconfig

# 打开菜单配置,按需开启模块支持
make menuconfig

在 menuconfig 中,必须确认 Enable loadable module support 已开启,否则无法生成 .ko 文件。同时要确保 Module unloading 和 Module versioning support 按实验需求选择。配置完成后执行 make -j$(nproc) 编译内核,生成 arch/arm/boot/zImage 和 vmlinux。

三、QEMU 或开发板环境准备

目标运行环境有两种选择:QEMU 模拟器和真实开发板。QEMU 的优势是无需硬件、启动快、便于反复实验;开发板的优势是能验证真实外设和时序。对于 Platform 驱动入门,建议先用 QEMU 的 vexpress-a9 或 virt 机器跑通流程,再迁移到开发板。

QEMU 环境需要准备三样东西:内核镜像 zImage、设备树文件 vexpress-v2p-ca9.dtb、根文件系统 rootfs。根文件系统可以用 BusyBox 制作,也可以直接使用发行版提供的 initramfs。启动命令示例如下:

qemu-system-arm \
  -M vexpress-a9 \
  -m 512M \
  -kernel arch/arm/boot/zImage \
  -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
  -append "root=/dev/mmcblk0 rw console=ttyAMA0" \
  -sd rootfs.ext4 \
  -nographic

启动后若能看到内核日志并进入 shell,说明目标环境已就绪。此时可以通过 uname -a 确认内核版本,通过 ls /lib/modules 确认模块目录是否存在。

四、驱动编译与加载流程

驱动编译有两种方式:独立编译(out-of-tree)和内核树内编译。项目实战中通常采用独立编译,便于快速迭代。一个最小的驱动 Makefile 如下:

obj-m += hello_platform.o
KDIR := /home/user/linux-6.6
PWD := $(shell pwd)

all:
	make -C $(KDIR) M=$(PWD) modules

clean:
	make -C $(KDIR) M=$(PWD) clean

执行 make 后生成 hello_platform.ko。将该文件拷贝到目标机,使用 insmod 加载、lsmod 查看、rmmod 卸载。加载时内核会进行版本校验和符号解析,若失败可通过 dmesg 查看具体原因。

# 在目标机上操作
insmod hello_platform.ko
lsmod | grep hello
dmesg | tail -20
rmmod hello_platform
常见误区:宿主机内核版本与目标机不一致、CROSS_COMPILE 未导出、Makefile 中 KDIR 指向错误、模块签名未关闭。这些问题都会导致 insmod 报错,排查时应优先看 dmesg 的最后几行。

五、环境搭建检查清单

环节 关键检查项 验证命令
交叉工具链 版本、ABI、PATH 生效 arm-linux-gnueabihf-gcc -v
内核源码 ARCH、CROSS_COMPILE、.config make vexpress_defconfig
内核编译 zImage、dtb、模块支持 make -j$(nproc)
QEMU/开发板 启动日志、shell、/lib/modules uname -a
驱动加载 insmod、lsmod、dmesg insmod hello_platform.ko

下面用一张信号链式的环境拓扑图,把宿主机、工具链、内核源码、目标机与驱动模块之间的关系串联起来,帮助建立整体认知。

Platform 驱动开发环境拓扑与数据流 宿主机 (x86_64 Linux) • 编辑器 / Git / Make • 交叉编译工具链 • 内核源码树 • QEMU 模拟器 交叉编译工具链 arm-linux-gnueabihf-gcc ld / objcopy / strip ARCH=arm CROSS_COMPILE 前缀 内核源码与配置 linux-6.6/ .config / defconfig zImage + dtb Module support = y 目标运行环境 QEMU vexpress-a9 或 ARM 开发板 zImage + dtb + rootfs /lib/modules/<ver> 驱动模块 .ko hello_platform.ko insmod / rmmod lsmod / dmesg platform_driver 注册 调用 编译 生成 .ko 部署 启动 核心链路:宿主机交叉编译 → 内核源码提供构建框架 → 目标机加载 .ko → platform_driver 与设备树匹配