2、驱动开发环境搭建:交叉编译工具链配置、Linux内核源码获取与编译、设备树(Device Tree)中GPU节点的配置、根文件系统集成驱动模块。
好,咱们直接进入正题。驱动开发,说白了就是让硬件在操作系统里“活”起来。但嵌入式环境跟你的PC不一样,你不能直接在开发板上写代码、编译——性能不够,工具链也不全。所以,交叉编译是第一步。
2.1 交叉编译工具链配置
我个人习惯用Linaro提供的GCC工具链,稳定,社区支持也好。你想想看,如果你的目标芯片是ARM Cortex-A系列,那你就需要arm-linux-gnueabihf-这套前缀的工具。
安装步骤其实不复杂:
- 下载对应架构的交叉编译器(比如gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf.tar.xz)。
- 解压到
/opt/目录下。 - 把
bin目录加入PATH环境变量。
export PATH=/opt/gcc-arm-9.2/bin:$PATH
export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm
我在项目中遇到过一个问题:工具链版本太新,导致内核编译报错。嗯,这里要注意,内核版本和工具链版本要匹配。比如Linux 4.19内核,用gcc 9.x系列就挺稳。太新的gcc 12.x可能会引入一些语法警告,甚至直接编译失败。
2.2 Linux内核源码获取与编译
内核源码怎么拿?我建议直接从官方主线拉取,或者用芯片厂商提供的BSP包。比如树莓派就用raspberrypi/linux这个仓库。
git clone --depth=1 -b rpi-4.19.y https://github.com/raspberrypi/linux.git
cd linux
make bcm2709_defconfig
make -j4 zImage modules dtbs
这里bcm2709_defconfig是树莓派3的默认配置。你换成自己的板子,比如全志H3就用sun8i_defconfig。编译的时候加-j4,利用多核加速,不然等得你怀疑人生。
编译完成后,你会得到三个关键产物:
zImage:压缩后的内核镜像*.ko:内核模块文件*.dtb:设备树二进制文件
说白了,这三样东西就是驱动运行的“三驾马车”。少一个,你的GPU都跑不起来。
2.3 设备树(Device Tree)中GPU节点的配置
设备树,我刚开始接触时觉得它很玄乎。其实就是用文本描述硬件信息,告诉内核“我这里有个GPU,它挂在哪条总线上,中断号是多少,寄存器地址在哪”。
以Mali-400 GPU为例,设备树节点大概长这样:
gpu: gpu@1c40000 {
compatible = "arm,mali-400", "arm,mali-utgard";
reg = <0x01c40000 0x10000>;
interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "IRQGP", "IRQGPMMU", "IRQPP0", "IRQPPMMU0";
clocks = <>;
clock-names = "bus", "core";
};
这里有几个关键点:
- compatible:驱动匹配的关键字符串,驱动里会用它来绑定设备。
- reg:GPU的寄存器基地址和范围。别写错了,否则内核访问不到硬件。
- interrupts:GPU的中断号。我见过有人把中断号写反,结果GPU一干活就死机。
dtc -I dtb -O dts反编译dtb文件,看看实际加载的配置对不对。我曾经靠这招抓到一个“地址偏移量少写了一个0”的bug。
2.4 根文件系统集成驱动模块
驱动编译成模块(.ko文件)后,怎么让它开机自动加载?你得把它放到根文件系统里。
我常用的方法是:
- 编译内核模块:
make modules_install INSTALL_MOD_PATH=./rootfs - 这会把所有模块安装到
rootfs/lib/modules/$(KERNEL_VERSION)/目录下。 - 然后打包成根文件系统镜像,比如用
mkfs.ext4或者genimage工具。
如果你用的是Buildroot或Yocto,那就更简单了。直接在配置里把GPU驱动选上,它们会自动帮你集成。
但手动集成时,有个坑:模块依赖关系。比如Mali驱动依赖drm和dma-buf,你得确保这些模块也一起打包进去。否则insmod时会报“Unknown symbol”。
depmod -a -b ./rootfs $(KERNEL_VERSION)
运行depmod会生成modules.dep文件,告诉内核模块之间的依赖顺序。这一步很多人会忘,结果模块加载失败,还以为是驱动代码写错了。
好,这一章就到这里。下一章我们聊聊GPU驱动的核心数据结构,以及如何注册一个platform driver。到时候见。