最简平台设备驱动实战(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.symvers和scripts/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.kofile hello_pdrv.ko 显示 ARM ELF relocatablemodinfo 输出 license、author、vermagic 信息设备树中存在
compatible = "myvendor,hello-device" 节点insmod 后 dmesg 显示 probe called!rmmod 后 dmesg 显示 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. 下一步扩展方向
完成本最小验证后,可逐步增加能力:
- 读取设备树属性 — 在
probe中用of_property_read_string/of_property_read_u32解析自定义属性
- 注册字符设备 — 在
probe中调用cdev_add+device_create,提供/dev/hello用户态接口
- 接入真实硬件资源 — 使用
platform_get_resource获取 MMIO / IRQ,操作寄存器
- 对接子系统 — 将平台驱动桥接到 IIO / Input / LED 等 Linux 子系统







