Lazy loaded image
最简平台设备驱动实战(Platform Driver Hello World)
Words 1803Read Time 5 min
2026-3-2

最简平台设备驱动实战(Platform Driver Hello World)

本文从零开始,逐步完成一个最小可运行的 Platform Device Driver,覆盖 驱动代码 → Makefile → 编译 → 修改设备树 → 打包进 rootfs → QEMU 加载验证 全流程。
目标平台:QEMU mcimx6ul-evk(i.MX6UL Cortex-A7)+ Linux 6.1 + BusyBox rootfs

1. 前置条件

确保以下环境已就绪(参考 01_qemu_env_build/ 下的构建脚本):
交叉编译器 arm-linux-gnueabihf-gcc 可用
已编译 Linux 6.1 内核源码,且包含 modules 目标(位于 01_qemu_env_build/linux-6.1/
QEMU mcimx6ul-evk 能正常启动 BusyBox rootfs(01_qemu_env_build/05_run_qemu.sh

2. 快速开始(一键构建)

本目录已包含全部源码和自动化脚本,可直接执行:
QEMU 启动后,在串口终端中验证:
退出 QEMU:Ctrl+A 然后 X
如需了解每一步的原理,请继续阅读下文。

3. 目录结构


4. 驱动源码详解

4.1 平台设备驱动 — hello_pdrv.c

4.2 关键结构说明

元素
作用
of_device_id.compatible
与设备树节点的 compatible 字段匹配,匹配成功后内核调用 probe
platform_driver.probe
设备与驱动匹配后的初始化入口
platform_driver.remove
rmmod 卸载模块或设备解绑时的清理入口
module_platform_driver
宏展开为 module_init / module_exit,自动注册/注销 platform driver

5. Makefile

注意KERNDIR 默认指向 ../01_qemu_env_build/linux-6.1,该内核树必须已完成 zImage dtbs modules 三个目标的编译(03_build_kernel.sh 会自动完成)。仅编译 zImage dtbs 会导致缺少 Module.symversscripts/module.lds,外部模块编译将失败。

6. 手动编译模块

如果使用 build.sh 一键构建,可跳过第 6–9 节。
编译成功后产出 hello_pdrv.ko,确认架构:

7. 修改设备树

为使 probe 被调用,需要在设备树中添加一个与 compatible = "myvendor,hello-device" 匹配的节点。
有两种方式可以实现,对比如下:
Device Tree Overlay
直接修改主 DTB
原理
编译为 .dtbo,运行时叠加到基础 DTB 上
反编译 DTB → 编辑 DTS → 重新编译
优点
不侵入原始 DTB;可热插拔;适合量产环境按需加载不同外设
简单直接;无需内核/bootloader 支持 overlay 机制
缺点
需要 bootloader(如 U-Boot)或内核 configfs 支持加载;QEMU 不支持
每次修改需重新编译 DTB 并重启;会改动原始文件
适用场景
真实硬件 + U-Boot fdt apply;内核 configfs 动态加载
QEMU 开发调试;无 overlay 支持的环境
本项目使用 QEMU,不支持 overlay 动态加载,因此采用直接修改主 DTB 的方式(build.sh 已自动完成此步骤)。

7.1 手动修改 DTB

7.2 设备树 Overlay 源文件(参考)

hello-overlay.dts 仅作为参考保留,记录标准 overlay 写法:

8. 打包进 rootfs


9. QEMU 内加载验证

9.1 启动 QEMU

rcS 启动脚本已自动挂载 /proc/sys,无需手动挂载。

9.2 加载与卸载模块

如果只看到模块加载但没有 probe called!,说明设备树中缺少匹配节点,请确认第 7 步已完成。

9.3 查看 sysfs 信息


10. 验证清单

make 编译无 warning,产出 hello_pdrv.ko
file hello_pdrv.ko 显示 ARM ELF relocatable
modinfo 输出 license、author、vermagic 信息
设备树中存在 compatible = "myvendor,hello-device" 节点
insmoddmesg 显示 probe called!
rmmoddmesg 显示 remove called!
/sys/bus/platform/drivers/hello_pdrv/ 目录存在

11. 常见问题排查

现象
原因
解决方案
insmod: invalid module format
模块与内核 vermagic 不匹配
确保 Makefile 中 KERNDIR 指向 QEMU 启动时使用的同一内核树
Unknown symbol 错误
内核未开启 CONFIG_MODULES 或缺少符号导出
内核 menuconfig 中启用 Enable loadable module support
insmod 成功但无 probe 输出
设备树中无匹配的 compatible 节点
重新执行 build.sh 或按第 7 步手动修改 DTB,然后重启 QEMU
can't insert module: File exists
模块已加载
rmmod hello_pdrv 再重新 insmod
编译报 No rule to make target
KERNDIR 路径错误或内核树未编译
确认 01_qemu_env_build/linux-6.1/ 存在且已执行过 03_build_kernel.sh
编译报 No rule to make target 'scripts/module.lds'
内核树未执行 modules_prepare
在内核树中执行 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_prepare,或直接重新运行 build.sh(会自动检测并补全)
modpost 报大量 undefined! 警告
内核树缺少 Module.symvers(仅编译了 zImage dtbs,未编译 modules
在内核树中执行 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc) modules,或直接重新运行 build.sh(会自动检测并补全)
loading out-of-tree module taints kernel
模块通过 M=$(PWD) 在内核树外部编译,内核设置 taint 标志 O(OOT_MODULE)标记已加载非内核树模块
正常提示,不影响功能。所有外部编译的 .ko 都会触发。如需消除,需将模块源码放入内核树并在树内编译(开发阶段无此必要)

12. 下一步扩展方向

完成本最小验证后,可逐步增加能力:
  1. 读取设备树属性 — 在 probe 中用 of_property_read_string / of_property_read_u32 解析自定义属性
  1. 注册字符设备 — 在 probe 中调用 cdev_add + device_create,提供 /dev/hello 用户态接口
  1. 接入真实硬件资源 — 使用 platform_get_resource 获取 MMIO / IRQ,操作寄存器
  1. 对接子系统 — 将平台驱动桥接到 IIO / Input / LED 等 Linux 子系统
上一篇
Data Structure and Algorithm
下一篇
用面试拷问嵌入式技术栈

Comments
Loading...