第三章:开发环境搭建
好,咱们正式开始动手了。这一章我带你搭好开发环境。说实话,很多初学者卡在第一步——环境没配好,后面全是坑。我自己带过不少新人,发现环境问题能占掉调试时间的 30%。所以这一章,咱们一次搞定。
3.1 Linux 驱动开发环境配置
做存储芯片驱动,首选 Linux。为什么?因为 Linux 内核本身就是为嵌入式设计的,驱动框架成熟,工具链齐全。我个人习惯用 Ubuntu 20.04 LTS,稳定,社区支持好。
你需要准备一台 Linux 主机,或者虚拟机也行。我建议直接装双系统,虚拟机跑编译会慢一些。嗯,这里要注意:千万别用 WSL1,它缺少很多内核头文件,后面编译驱动时会报一堆莫名其妙的错。
核心依赖包安装:
sudo apt-get update
sudo apt-get install build-essential
sudo apt-get install linux-headers-$(uname -r)
sudo apt-get install git fakeroot ncurses-dev xz-utils libssl-dev bc flex bison
为什么要装 linux-headers?因为驱动编译需要内核的构建系统。说白了,它提供了 Kbuild 框架和一堆宏定义。我曾经遇到过一位同事,装完系统直接编译驱动,结果报 Cannot use CONFIG_CC_STACKPROTECTOR_STRONG,就是缺了内核头文件。
小技巧: 装完后运行 ls /lib/modules/$(uname -r)/build,如果能看到一个软链接指向内核源码目录,说明环境就绪了。
3.2 交叉编译工具链安装
你想想看,我们的存储芯片通常跑在 ARM 或 RISC-V 架构上,而你的开发机是 x86。这就需要一个「翻译官」——交叉编译工具链。
我个人推荐用 Linaro 的 GCC 工具链,或者 ARM 官方的 gcc-arm-none-eabi。对于存储芯片,我常用的是 aarch64-linux-gnu- 系列,因为很多主控芯片是 64 位 ARM Cortex-A 系列。
安装步骤:
# 下载 ARM64 工具链
wget https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz
# 解压到 /opt
sudo tar -xf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt/
# 添加环境变量
echo 'export PATH=$PATH:/opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin' >> ~/.bashrc
source ~/.bashrc
# 验证安装
aarch64-none-linux-gnu-gcc --version
这里有个坑:工具链版本要和内核版本匹配。比如你编译 Linux 5.10 内核,最好用 GCC 10.x 系列。版本差太多,编译出来的驱动可能无法加载。我曾经用 GCC 9 编译 5.15 内核的驱动,结果 insmod 时报 Unknown symbol,查了半天才发现是 ABI 不兼容。
避坑指南: 不要用系统自带的 gcc 去编译嵌入式驱动。交叉编译工具链的前缀一定要写对,比如 aarch64-none-linux-gnu-gcc 和 arm-linux-gnueabihf-gcc 是两回事,前者是 64 位,后者是 32 位。
3.3 QEMU 模拟器与开发板选择
做驱动开发,你不可能每次都往真板子上烧写。调试一次几分钟,效率太低。所以我们需要 QEMU 模拟器。
QEMU 能模拟完整的 ARM 或 RISC-V 开发板,包括中断控制器、定时器、甚至 NAND Flash 控制器。我刚开始做存储驱动时,就是在 QEMU 上把 NAND 驱动调通了,才敢往真板子上烧。
安装 QEMU:
sudo apt-get install qemu-system-arm qemu-system-aarch64
然后你需要一个设备树文件(.dtb)和内核镜像(zImage)。我一般用 vexpress-a9 板型来模拟,它支持 4 个 Cortex-A9 核心,足够跑存储驱动测试。
# 启动 QEMU 模拟 vexpress-a9 开发板
qemu-system-arm -M vexpress-a9 -kernel zImage -dtb vexpress-v2p-ca9.dtb -nographic -append "console=ttyAMA0"
至于真开发板的选择,我建议从 STM32MP157 或 Raspberry Pi 3B+ 入手。为什么?
| 开发板 | 架构 | 存储接口 | 适合场景 |
|---|---|---|---|
| STM32MP157 | ARM Cortex-A7 | SDIO、NAND、QSPI | 入门级存储驱动 |
| Raspberry Pi 3B+ | ARM Cortex-A53 | SDHCI、USB | SD/eMMC 驱动开发 |
| Allwinner V3s | ARM Cortex-A7 | NAND、SPI NAND | NAND Flash 驱动 |
我个人推荐先玩 QEMU,把驱动框架跑通,再上真板。因为 QEMU 可以打断点、单步调试,真板一旦死机就只能按复位键。嗯,这里有个经验:QEMU 上调试好的驱动,上真板后至少还要改 20% 的代码,因为时序和硬件寄存器细节不同。
小技巧: 在 QEMU 上调试存储驱动时,可以用 -drive file=flash.img,if=pflash,format=raw 参数挂载一个模拟的 Flash 镜像,这样就能测试读写擦除了。
3.4 环境验证:写一个最小驱动
环境搭好了,咱们写个最简单的驱动验证一下。这个驱动什么都不做,只打印一条消息。
// hello_drv.c
#include <linux/module.h>
#include <linux/kernel.h>
static int __init hello_init(void)
{
printk(KERN_INFO "Hello, Storage Driver World!\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "Goodbye, Storage Driver!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
Makefile 怎么写?注意,交叉编译时要用 CROSS_COMPILE 指定工具链前缀。
obj-m := hello_drv.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
如果是交叉编译,改成这样:
ARCH := arm64
CROSS_COMPILE := aarch64-none-linux-gnu-
KDIR := /path/to/kernel/source
编译后得到 hello_drv.ko,用 insmod 加载,dmesg 查看输出。如果看到 "Hello, Storage Driver World!",恭喜你,环境搭建成功!
避坑指南: 如果 insmod 报 Invalid module format,八成是内核版本不匹配。检查一下 modinfo hello_drv.ko 显示的 vermagic 是否和当前内核一致。
好,环境搭建就到这里。下一章咱们开始写真正的存储驱动——先从一个简单的字符设备驱动入手,把读写接口打通。你准备好了吗?