2026/4/18 8:51:29
网站建设
项目流程
网站图片如何做防盗链,合肥seo推广公司哪家好,百度推广方案,甘肃住房城乡建设厅网站驱动是如何向内核**注册#xff08;Register#xff09;**自己的#xff1f;是 register_chrdev 还是 platform_driver_register#xff1f;二者有什么区别#xff1f;为什么有了 register_chrdev 还要有 platform_driver_register#xff1f;它们到底谁才是真正的“注册…驱动是如何向内核**注册Register**自己的是 register_chrdev 还是 platform_driver_register二者有什么区别为什么有了register_chrdev还要有platform_driver_register它们到底谁才是真正的“注册”答案是它们都是注册但注册的“对象”和“目的”完全不同。简单来说platform_driver_register是为了找硬件匹配设备树。register_chrdev是为了给用户提供接口生成/dev/xxx。在现代 Linux 驱动开发中通常是两者配合使用先用 Platform 注册找到硬件然后在找到硬件的回调函数Probe里再进行字符设备注册。学习笔记Linux 驱动的两层注册机制1. 核心区别概览特性register_chrdev (字符设备注册)platform_driver_register (平台驱动注册)所属层级接口层(Interface Layer)设备模型层(Device Model Layer)核心目的告诉内核“我是一个字符设备我的操作函数是f_op”告诉内核“我就是这硬件的驱动把符合名字的设备交给我”面向谁面向用户空间(APP)面向内核/硬件(Device Tree)产出物主设备号、/dev/xxx节点配合 class_create触发probe函数的执行必须性如果你想让 APP 操作必须有 (或其变体cdev_add)不是必须的 (简单的驱动可以没有)但标准驱动都有2. 深度解析为什么要分两层第一层platform_driver_register(我是谁我去哪里上班)这是 Linux“总线-设备-驱动” (Bus-Device-Driver)模型的一部分。背景在设备树Device Tree引入之前驱动代码里写死了硬件地址比如 0x020E0068。现在硬件信息都写在设备树 (.dts) 里驱动代码里不再写死地址。作用platform_driver_register 的作用就是**“相亲”**。设备树说“我有一个硬件名字叫my-led。”驱动代码说“我是一个驱动我能支持名字叫my-led的硬件。”内核把两者撮合在一起。一旦名字匹配成功内核就会调用驱动里的probe(探测)函数。第二层register_chrdev(我能干什么)这是我们之前讲过的字符设备功能注册。作用不管你是怎么找到硬件的最终你都得让 APP 能控制它。register_chrdev 就是去申请一个主设备号并挂载 file_operations让 APP 可以通过 open/read/write 来操作。3. 它们是如何配合工作的 (标准模板)在现代驱动中这套流程是固定的“套娃”模式模块入口 (module_init)只做一件事调用platform_driver_register。匹配成功内核发现设备树有对应节点自动调用驱动的probe函数。Probe 函数这里才是真正干活的地方获取硬件资源读设备树里的寄存器地址、中断号。调用register_chrdev或者是现代的alloc_chrdev_regioncdev_add。创建设备节点 (class_create,device_create)。代码骨架可以背下来这个结构/* 1. 定义字符设备的操作函数 (给 APP 用的) */ static struct file_operations my_fops { .owner THIS_MODULE, .write my_write, .open my_open, }; /* 2. probe 函数当驱动和设备树匹配成功时内核自动调用 */ static int my_probe(struct platform_device *pdev) { printk(硬件匹配成功开始初始化...\n); // A. 获取硬件资源 (从设备树拿) // ... // B. 注册字符设备 (核心关联点) // 只有到了这一步APP 才能看到 /dev/xxx major register_chrdev(0, my_device, my_fops); // C. 创建设备节点 // class_create(...); // device_create(...); return 0; } /* 3. remove 函数卸载驱动或设备移除时调用 */ static int my_remove(struct platform_device *pdev) { // 注销字符设备 unregister_chrdev(major, my_device); return 0; } /* 4. 定义 Platform 驱动结构体 */ static const struct of_device_id my_match_table[] { { .compatible 100ask,led }, // 这里的名字必须和设备树里的一样 { }, }; static struct platform_driver my_driver { .probe my_probe, // 匹配成功调这个 .remove my_remove, // 移除时调这个 .driver { .name my_led_driver, .of_match_table my_match_table, // 指定匹配表 }, }; /* 5. 模块入口只负责注册 Platform 驱动 */ static int __init my_driver_init(void) { // 向内核注册在这个 Platform 驱动 return platform_driver_register(my_driver); } static void __exit my_driver_exit(void) { platform_driver_unregister(my_driver); } module_init(my_driver_init); module_exit(my_driver_exit); module_license(GPL);4. 图解流程insmod→ \rightarrow→执行module_init。platform_driver_register→ \rightarrow→把驱动名字放入“相亲角”。Kernel Check→ \rightarrow→发现设备树里也有个叫100ask,led的硬件。Match!→ \rightarrow→触发my_probe函数。Inside Probe→ \rightarrow→执行register_chrdev。Result→ \rightarrow→生成/dev/ledAPP 可以使用了。5. 常见疑惑解答Q1: 我可以不用platform_driver_register只写register_chrdev吗可以。这就是所谓的“老式驱动”或“非设备树驱动”。后果你必须在代码里硬编码寄存器物理地址比如0x20200000。这导致你的驱动只能在这个特定的板子上跑换个板子地址变了就得改代码重新编译。现状为了代码通用性现在几乎都用 Platform 模式。Q2: 我可以不用register_chrdev只写platform_driver_register吗不可以除非你是做 LED 子系统、输入子系统这种高级开发。如果你不注册字符设备你就没有主设备号没有/dev/xxx节点APP 根本没办法通过文件操作来访问你的驱动。platform_driver只是帮你管理硬件资源它不负责给 APP 提供接口。6. 总结platform_driver_register是管理层。负责让驱动程序和设备树里的硬件描述**“对上号”**。它让驱动更灵活、更通用。register_chrdev是业务层。负责实打实地生成用户接口。关系通常在platform_driver的probe函数中去调用register_chrdev。前者是壳后者是核。