
整体结构一共 3 部分设备树节点DTS内核驱动平台驱动 字符设备最简版用户层测试APP环境Linux 5.x / 6.xZYNQ / STM32MP1 通用。一、第一步添加设备树节点 dts打开板级 dts在根节点下添加myhello_dev { compatible mycompany,hello_dev; status okay; };compatible是驱动和设备匹配的关键字必须两边一致。本例只做字符设备不需要寄存器地址所以不用 reg。编译生成 dtb更新到开发板。二、第二步驱动代码 hello_dev.c两层最简封装#include linux/module.h #include linux/platform_device.h #include linux/cdev.h #include linux/fs.h #include linux/device.h // ---------------- 字符设备变量 ---------------- #define DEV_NAME hello_dev static dev_t devno; static struct cdev cdev; static struct class *dev_class; // ---------------- 文件操作接口应用调用open/read/write ---------------- static int hello_open(struct file *file) { pr_info(hello dev opened\n); return 0; } static ssize_t hello_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { char k_buf[] Hello World from kernel driver!\n; int len strlen(k_buf); // 内核数据拷贝到用户空间 copy_to_user(buf, k_buf, len); return len; } static int hello_release(struct file *file) { pr_info(hello dev closed\n); return 0; } static struct file_operations hello_fops { .owner THIS_MODULE, .open hello_open, .read hello_read, .release hello_release, }; // ---------------- 平台驱动设备匹配成功后执行probe ---------------- static int hello_probe(struct platform_device *pdev) { int ret; // 1. 申请设备号 ret alloc_chrdev_region(devno, 0, 1, DEV_NAME); if (ret 0) { pr_err(alloc devno failed\n); return ret; } // 2. 初始化字符设备绑定文件操作集 cdev_init(cdev, hello_fops); cdev_add(cdev, devno, 1); // 3. 自动在 /dev 下生成设备节点 dev_class class_create(THIS_MODULE, DEV_NAME); device_create(dev_class, NULL, devno, NULL, DEV_NAME); pr_info(hello driver probed success\n); return 0; } static int hello_remove(struct platform_device *pdev) { // 销毁设备节点 device_destroy(dev_class, devno); class_destroy(dev_class); // 删除字符设备 cdev_del(cdev); unregister_chrdev_region(devno, 1); pr_info(hello driver removed\n); return 0; } // 匹配表和dts里compatible严格对应 static const struct of_device_id hello_of_match[] { { .compatible mycompany,hello_dev }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, hello_of_match); // 注册平台驱动 static struct platform_driver hello_driver { .probe hello_probe, .remove hello_remove, .driver { .name hello_plat_driver, .of_match_table hello_of_match, }, }; module_platform_driver(hello_driver); MODULE_LICENSE(GPL); MODULE_DESCRIPTION(Hello world platform char driver); MODULE_AUTHOR(Newbie);分层说明底层封装file_operations向上给应用提供 open/read上层框架封装platform_driver和设备树做匹配实现软硬件分离硬件信息只留在DTSC代码里没有写死任何硬件地址。三、Makefileobj-m hello_dev.o KERNELDIR ? /home/xxx/linux-kernel PWD : $(shell pwd) all: $(MAKE) -C $(KERNELDIR) M$(PWD) modules clean: $(MAKE) -C $(KERNELDIR) M$(PWD) clean编译得到hello_dev.ko四、第三步用户层应用 app.c#include stdio.h #include fcntl.h #include unistd.h #include errno.h int main(void) { int fd; char buf[128]; fd open(/dev/hello_dev, O_RDONLY); if (fd 0) { perror(open); return -1; } read(fd, buf, sizeof(buf)); printf(Recv: %s\n, buf); close(fd); return 0; }交叉编译arm-linux-gnueabihf-gcc app.c -o app五、开发板上完整测试流程加载驱动insmod hello_dev.kodts匹配成功会自动执行 probe自动生成/dev/hello_dev运行应用程序chmod x app ./app运行结果终端打印Recv: Hello World from kernel driver!dmesg 查看内核日志hello dev opened hello dev closed卸载驱动rmmod hello_dev六、软硬件分离关键点复盘硬件描述只放在 DTScompatible mycompany,hello_dev;驱动不写死硬件信息只依靠of_device_id做字符串匹配一旦匹配成功内核自动调用 probe 函数初始化字符设备应用只操作/dev/xxx文件完全不关心底层硬件。