在linux内核源码中,我们经常看到module_platform_driver 与 module_init这两个宏定义,有时候在这个驱动中用module_platform_driver,有时候用module_init,那这两个宏定义之间有什么差异吗?还是说可以随便用呢?这就需要我们旭跟踪代码,来看看这两个宏定义到底什么东西?

首先,介绍下module_init,module_init对于做驱动的人应该不陌生,linux内核是一个宏内核,宏内核就是驱动和内核打包在一起的。由于驱动是作为内核模块挂载在内核上的,而内核对于模块的接口就是module_init和module_exit,所以你要加载一个内核模块的时候,必须使用module_init来进行,而卸载一个内核模块的时候,必须使用module_exit来进行。例如:

module_init(xxxx_init);       //加载xxxx模块

module_exit(xxxx_exit);     //卸载xxxx模块

那是不是就有一个疑问了,既然是所有模块都是通过这两个函数来进行加载和卸载的,那应该所有驱动都用module_init函数啊,为什么还会有module_platform_driver?这不是多此一举吗?醒醒吧,少年,Linux社区那么多大神在更新,会出现这种错误吗?

那么我们先看看module_platform_driver的宏定义是个啥?

#define module_platform_driver(__platform_driver) \module_driver(__platform_driver, platform_driver_register, \platform_driver_unregister)
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

从代码中看到,module_platform_driver 追根溯源,发现最终还是调用了module_init,但是,又不仅仅是调用了module_init,还调用了platform_driver_register和platform_driver_unregister,这两个函数的作用就是注册和卸载平台驱动。

一般某个设备或某个控制器挂载在处理器上时,肯定是通过某种总线来连接的,比喻说常见总线有SPI总线、IIC总线等等,但控制器一般是通过内部总线挂载处理器端的,对于这一类设备,linux抽象出了一个平台总线,来包含所有的处理器总线挂载的设备。而这类总线需要注册到内核中去,就需要用platform_driver_register来实现,平台总线是抽象出来的,所以所有通过总线直接连在处理器上的设备是不需要关心平台总线怎么运作的,因此这个平台总线的注册和注销都是通用的,所以在加载总线设备驱动时,直接调用module_platform_driver,就可以将平台驱动注册函数和卸载函数、以及总线设备加载一次性运行完,避免了总线驱动在每次加载驱动时都需要手动注册平台总线。

其实说了这么多,module_platform_driver就是对module_init进一步的封装,在module_init之外添加了一些功能,对于平台总线设备而言,直接调用module_platform_driver就可以避免在module_init函数中去注册平台驱动了,使得平台设备驱动的加载变得更方便了。

那是不是说module_init可以与module_platform_driver就可以通用呢?当然不是,能用module_platform_driver的地方肯定可以用module_init,但能用module_init却不一定能用module_platform_driver,这主要看设备是不是通过平台总线的方式挂载处理器上的。

函数名 非平台总线设备 平台总线设备
module_platform_driver 不可用 可用
module_init 可用 可用

对于平台总线设备,可以用两种方法来加载

static int __init xxxx_init(void)
{return platform_driver_register(&xxxx_driver);
}static void __exit xxxx_exit(void)
{platform_driver_unregister(&xxxx_driver);
}module_init(xxxx_init);
module_exit(xxxx_exit);
module_platform_driver(xxxx_driver);

这两种方式的效果是一样的,但明显module_platform_driver比较简洁。

但对于其他总线的设备,就不能使用module_platform_driver了,必须使用module_init,在module_init函数中再注册一遍其他总线,例如SPI总线设备。

static int __init spidev_init(void)
{int status;/* Claim our 256 reserved device numbers.  Then register a class* that will key udev/mdev to add/remove /dev nodes.  Last, register* the driver which manages those device numbers.*/BUILD_BUG_ON(N_SPI_MINORS > 256);status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);if (status < 0)return status;spidev_class = class_create(THIS_MODULE, "spidev");if (IS_ERR(spidev_class)) {unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);return PTR_ERR(spidev_class);}status = spi_register_driver(&spidev_spi_driver);if (status < 0) {class_destroy(spidev_class);unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);}return status;
}
module_init(spidev_init);static void __exit spidev_exit(void)
{spi_unregister_driver(&spidev_spi_driver);class_destroy(spidev_class);unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
module_exit(spidev_exit);

还有一种情况不能使用module_platform_driver,就是我们编写的驱动程序要使用之前某个驱动程序的功能时,就不能使用module_platform_driver,因为如果都使用module_platform_driver,内核编译链接时,并不知道哪个驱动程序会被链接再前面,也就不知道哪个驱动程序会先执行,如果使用自定义的驱动(使用其他驱动程序功能的程序)先执行,而其他驱动还未进行初始化,就会出现难以预料的问题,所以这种情况下就必须使用late_initcall

module_platform_driver 与 module_init相关推荐

  1. DRM驱动代码分析:开机过程中显示驱动做了什么

    前言: 有些信息是在网上查资料后总结的,没有去追代码验证.如果有说得不对的地方,欢迎提出指正.感谢! 手机启动的大致流程 1.长按开机键 2.执行存储在ROM里(应该是某一个固定地址或是预定义的地址) ...

  2. module_platform_driver宏解析

      该函数实际是一个宏,它在include/linux/platform_device.h中定义如下: [cpp]  view plain copy /* module_platform_driver ...

  3. module_init和init_module的区别

    今天在看CS8900的驱动时,发现其驱动的模块加载函数是init_module(),由于看到大多数的驱动用的模块加载函数大多是module_init()函数,所以一时没缓过神来,总是在找CS8900的 ...

  4. linux模块化机制,Linux模块化机制和module_init

    > 引子:模块化机制优点 模块化机制(module)是Linux系统的一大创新,是Linux驱动开发和运行的基础(当然,module并不仅仅是支撑驱动).其优点在于: 1.在系统运行动态加载模块 ...

  5. 各种initcall的执行先后顺序(module_init、postcore_initcall、arch_initcall、subsys_initcall、 fs_initcall)...

    现在以module_init为例分析initcall在内核中的调用顺序 在头文件init.h中,有如下定义: #define module_init(x)     __initcall(x); 很明显 ...

  6. 解密module_init幕后的故事

    在Linux底下写过driver模块的对这个宏一定不会陌生.module_init宏在MODULE宏有没有定义的情况下展开的内容是不同的,如果这个宏没有定义,基本上表明阁下的模块是要编译进内核的(ob ...

  7. linux驱动的入口函数module_init的加载和释放

    就像你写C程序需要包含C库的头文件那样,Linux内核编程也需要包含Kernel头文件,大多的Linux驱动程序需要包含下面三个头文件: #include <linux/init.h> # ...

  8. linux kernel中的module_init/initcall代码导读

    文章目录 1.initcall的分类 2.__initcall的调用 3.module_driver/module_i2c_driver ★★★ 友情链接 : 个人博客导读首页-点击此处 ★★★ 1. ...

  9. module_init 详解

    一直以来写linux驱动,都是按照固定格式,定义一个初始化和推出函数,书上告诉我这两个函数会被调用,至于为什么会被调用,在哪调用,一直不清楚. 偶然的一个机会,看到blob里面的代码,里面有一个初始化 ...

最新文章

  1. Facebook 的AI翻身之战!
  2. (android控件)ListView的Item中设置按钮实现
  3. Javascript笔记:(实践篇)从jQuery插件技术说起(上篇)
  4. PaddleOCR——Visual Studio 2019 环境下C++推理部署 CMake 编译解决方案
  5. elasticsearch-jdbc实现MySQL同步到ElasticSearch深入详解
  6. 认识线程 java 1615387415
  7. MTK 驱动开发(30)---Memory 移植
  8. 尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将出现此问题...
  9. linux awk 改写文件,批处理修改文件内容的问题,使用awk命令
  10. Asp.Net Core简单整理
  11. [JavaScript] DOM
  12. samba配置过程(附网络凭据的解决方法)
  13. 解决stackoverflow加载慢的插件
  14. pycharm调试步骤(详细)
  15. word回车后间距太大_word 里字体变大后再回车,两行间距太大怎么办
  16. cypher第一章背景与攻略
  17. 健康医疗数据安全指南内容
  18. 未来互联网时代的制造业
  19. 乐教乐学各关的解(3-10)
  20. RS485、RS232、TTL的电平以及数据的收发

热门文章

  1. 桌面有u盘符计算机里没有了,U盘插入电脑不显示盘符怎么办 U盘修复处理方法...
  2. 当下,什么样的程序员最吃香
  3. python内置函数打开文件_Python内置函数用来打开或创建文件并返回文件对象。
  4. html中加定时器,怎么用HTML设置一个定时器
  5. 【软考系统架构设计师】章节习题汇总系列
  6. python 批量爬取四级成绩单
  7. 银行业务知识之票据(深入浅出版)
  8. Android自定义控件开发入门与实战(15)SurfaceView,看完就能找到工作
  9. SurfaceView源码分析
  10. matlab转换为部分分式,【MATLAB用部分分式展开法资讯】MATLAB用部分分式展开法足球知识与常识 - 足球百科 - 599比分...