Linux kernel 中模块化的平台驱动代码介绍

介绍

在linux kernel中通过module_platform_driver来实现模块化平台驱动。大量的设备驱动程序都基于该种方式来实现,使用频次非常的高,在linux kernel 5.4.124的代码中搜索module_platform_driver共有2356次引用。

27d5d7f17551b86f8a9dea37cdfbc131.png这个宏的使用方式大相径庭,有一套成熟的代码书写方式,将驱动程序入口符号作为宏的参数,基本格式如下:

151e391633780156dd418fa268fec085.png

历史

它的定义在include/linux/platform_device.h中,从文件的名字来看可知它存在的意义是基于platform_device的。platform_device.h这个文件在2005年的linux-2.6.15就存在了。

93aa416b6a04af8be22fd6038cd6fe6d.png

platform_device.h在创建初期并没有现在这么多丰富的功能,通过platform_xxx_register来注册驱动和设备,并没有提供module_platform_driver这个辅助宏。

/** platform_device.h - generic, centralized driver model** Copyright (c) 2001-2003 Patrick Mochel <mochel@osdl.org>** This file is released under the GPLv2** See Documentation/driver-model/ for more information.*/#ifndef _PLATFORM_DEVICE_H_
#define _PLATFORM_DEVICE_H_#include <linux/device.h>struct platform_device {const char * name;u32  id;struct device dev;u32  num_resources;struct resource * resource;
};#define to_platform_device(x) container_of((x), struct platform_device, dev)extern int platform_device_register(struct platform_device *);
extern void platform_device_unregister(struct platform_device *);extern struct bus_type platform_bus_type;
extern struct device platform_bus;extern struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);
extern int platform_get_irq(struct platform_device *, unsigned int);
extern struct resource *platform_get_resource_byname(struct platform_device *, unsigned int, char *);
extern int platform_get_irq_byname(struct platform_device *, char *);
extern int platform_add_devices(struct platform_device **, int);extern struct platform_device *platform_device_register_simple(char *, unsigned int, struct resource *, unsigned int);extern struct platform_device *platform_device_alloc(const char *name, unsigned int id);
extern int platform_device_add_resources(struct platform_device *pdev, struct resource *res, unsigned int num);
extern int platform_device_add_data(struct platform_device *pdev, void *data, size_t size);
extern int platform_device_add(struct platform_device *pdev);
extern void platform_device_put(struct platform_device *pdev);struct platform_driver {int (*probe)(struct platform_device *);int (*remove)(struct platform_device *);void (*shutdown)(struct platform_device *);int (*suspend)(struct platform_device *, pm_message_t state);int (*resume)(struct platform_device *);struct device_driver driver;
};extern int platform_driver_register(struct platform_driver *);
extern void platform_driver_unregister(struct platform_driver *);#define platform_get_drvdata(_dev) dev_get_drvdata(&(_dev)->dev)
#define platform_set_drvdata(_dev,data) dev_set_drvdata(&(_dev)->dev, (data))#endif /* _PLATFORM_DEVICE_H_ */

platform_xxx_register这类宏在linux kernel 5.4.124中也在使用。

a305bc9293d72e95bf21bc8c4f2c8cda.png

不能通过引用计数少或者版本迭代的原因来评价这两类宏谁好谁坏,各自有各自的应用场景。当使用platform_xxx_register时,基本格式也是比较固定的,例如:

static int __init ehci_platform_init(void)
{if (usb_disabled())return -ENODEV;ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides);return platform_driver_register(&ehci_mv_driver);
}
module_init(ehci_platform_init);static void __exit ehci_platform_cleanup(void)
{platform_driver_unregister(&ehci_mv_driver);
}
module_exit(ehci_platform_cleanup);MODULE_DESCRIPTION("Marvell EHCI driver");
MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>");
MODULE_AUTHOR("Neil Zhang <zhangwm@marvell.com>");
MODULE_ALIAS("mv-ehci");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(of, ehci_mv_dt_ids);

从2012年linux kernel 3.xx开始增加了module_platform_driver这个宏,一直延续至今。从module_platform_driver的定义处可以发现,它是platform_driver_register的一个封装应用。

#define module_platform_driver(__platform_driver) \module_driver(__platform_driver, platform_driver_register, \platform_driver_unregister)

存在的意义和原理

正如前面介绍的module_init这个宏,在使用它的时候要定义两个函数以及生命两个宏。而使用了module_platform_driver这个宏之后,只需要一行代码就可以实现这些功能。将module_platform_driver这个宏展开之后,就是module_init这一部分代码内容。

#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);

驱动的注册与卸载方法采用了platform.c中提供的通用API。

/*** __platform_driver_register - register a driver for platform-level devices* @drv: platform driver structure* @owner: owning module/driver*/
int __platform_driver_register(struct platform_driver *drv,struct module *owner)
{drv->driver.owner = owner;drv->driver.bus = &platform_bus_type;drv->driver.probe = platform_drv_probe;drv->driver.remove = platform_drv_remove;drv->driver.shutdown = platform_drv_shutdown;return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__platform_driver_register);

module_init这个宏在include/linux/module.h中定义,在kernel初始化过程中调用do_initcall()或插入驱动ko文件时得到执行。每个驱动模块仅需实现一个module_init与module_exit即可。驱动代码在使用module_platform_driver注册驱动时,经过编译后的文件内容如下:

fcab2ed1954b8009271238d62e87a370.png

module_init宏最终是调用了__initcall(x),定义了程序链接时的初始化等级为1。

#define module_init(x) __initcall(x);
#define __initcall(fn) __define_initcall("1", fn)

关于initcall:

#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
#define ___define_initcall(fn, id, __sec)   \__ADDRESSABLE(fn)     \asm(".section \"" #__sec ".init\", \"a\" \n" \"__initcall_" #fn #id ":   \n" \".long " #fn " - .   \n" \".previous     \n");
#else
#define ___define_initcall(fn, id, __sec) \static initcall_t __initcall_##fn##id __used \__attribute__((__section__(#__sec ".init"))) = fn;
#endif

而通过module_init定义的驱动API编译后的符号表示都增加了initcall的前缀

857d1c9cf8a4118fc7b03e501eade2bc.png

最后,透过一张图看清module_platform_driver声明的驱动调用流程:

6bee68b396fd03f906bb8700396267c6.png

END


推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

d76203e252b42ae4066d198f654c8a21.png

嵌入式Linux

微信扫描二维码,关注我的公众号

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/465009.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Golang切片的三种简单使用方式及区别

概念 切片&#xff08;slice&#xff09;是建立在数组之上的更方便&#xff0c;更灵活&#xff0c;更强大的数据结构。切片并不存储任何元素而只是对现有数组的引用。 三种方式及细节案例 ①定义一个切片&#xff0c;然后让切片去引用一个已经创建好的数组 package main import…

这样给数组初始化,你肯定没有见过

今天在朋友圈看到一个朋友发的图片是给数组初始化的代码截图如下&#xff1a;我看到这个代码&#xff0c;也让我着实惊了一下&#xff0c;写代码这么久&#xff0c;也没有见过这样子初始化的。这个代码是出自海思的SDK里面的&#xff0c;华为的大牛还是很多啊&#xff0c;学习C…

如何让自己升薪

1) 经理应该好好看一看年度工作总结&#xff0c;这样他就能知道我对同事提供了多大的帮助。 2)如果一个程序员的工作很容易完成&#xff0c;那就不要等他主动来索取更多的任务。相反&#xff0c;积极交给他更加有挑战性的复杂的任务。 3) 在谈话中不要阻止建设性的批评。这个经…

ARM知识点

1、ARM有几种工作模式&#xff1f; 答&#xff1a;有7个基本工作模式&#xff1a;用户模式&#xff08;user&#xff09;、系统模式&#xff08;system&#xff09;、数据访问中止模式&#xff08;abort&#xff09;、未定义指令中止模式&#xff08;undef&#xff09;、快速中…

《你不知道的JavaScript》中卷 KYLE SIMPSON 著 单业 姜南 译

一部分 类型和语法 1.内置类型&#xff08;7&#xff09; null、undefined、boolean、number、string、object、symbol(ES6新增&#xff0c;符号) 2.toString()和Json.stringify() (1)字符串、数字、布尔值和null的JSON.stringify()规则与toString基本相同 (2)如果传递给JSON.…

CP命令引发的思考

转自&#xff1a;OSC开源社区今天同事用 cp 命令&#xff0c;把他给惊到了&#xff01;背景是这样的&#xff1a;他用 cp 拷贝了一个 100 G的文件&#xff0c;竟然一秒不到就拷贝完成了&#xff01;用 ls 看一把文件&#xff0c;显示文件确实是 100 G。sh-4.4# ls -lh -rw-r-…

安装windows XP或者2003时提示找不到硬盘驱动器的解决办法(总结)转载

&#xff08;本文属于转载&#xff09; 由于SATA接口在近年才推出&#xff0c;Windows XP问世的时候&#xff0c;还没有它&#xff0c;所以安装Windows XP时&#xff0c;需要调用SATA驱动程序&#xff0c;而你又没有提供相应的驱动&#xff0c;就会出现无法识别硬盘的错误。 不…

C宏定义注意事项

1、带参数的宏与函数的优缺点 2、宏定义一个函数时&#xff0c;需要注意每个参数都需要用括号括起来。 如&#xff1a;#define Min(a,b) ((a)<(b)?(a):(b)) 3、宏定义一年中的秒数 #define SECONDE_OF_YEAR 606024365UL &#xff08;√&#xff09; #define SECONDE_OF_YE…

不要再来北上广深了

大家好&#xff0c;我是写代码的篮球球痴最近我们的一个离职群里面讨论一个问题&#xff0c;就是在北上广深工作的性价比问题&#xff0c;我有几个同事这样描述。一线城市确实会遇到一些机会&#xff0c;也有可能会遇到一些改变命运的机会。不过对于生活来说&#xff0c;一线城…

mogndb 慢查询

0 摘要 在MySQL中&#xff0c;慢查询日志是经常作为我们优化查询的依据&#xff0c;那在MongoDB中是否有类似的功能呢&#xff1f;答案是肯定的&#xff0c;那就是开启Profiling功能。该工具在运行的实例上收集有关MongoDB的写操作&#xff0c;游标&#xff0c;数据库命令等&a…

Linux命令中正则表达式的运用

一、正则表达式简介 事实上&#xff0c;正则表达式不仅适用于linux&#xff0c;多种编程语言中也可以使用到它&#xff0c;因为它实际上是人们对于某种规律的表达方式。 如果要把他的所有应用都讲上的话那这篇文章会变的太长以至于无法阅读&#xff0c;我只介绍他在linux下和几…

程序的内存分配模式(堆栈以及静态存储区,文字常量区,代码区)

程序的内存分配模式 一个由 C/C编译的程序占用的内存分为以下几个部分&#xff1a; 1、栈区&#xff08; stack &#xff09;—由编译器自动分配释放&#xff0c;存放函数的参数值&#xff0c;局部变量的值 等。其操作方式类似于数据结构中的栈。 2、堆区&#xff08; heap&a…

稚晖君教你制作全球最迷你的自平衡机器人

摘要&#xff1a;Nano是一个小巧可爱的机器人&#xff0c;身高大约10公分&#xff0c;特点是平衡感好&#xff0c;长得很白以及善于卖萌。作为全球最迷你的自平衡机器人&#xff0c;Nano身材虽小&#xff0c;但配置有丰富的传感器—陀螺仪&#xff0c;超声波&#xff0c;Motion…

9.13作业

2、python test.py执行的三个阶段是什么&#xff1f;在哪个阶段识别文件内的python语法&#xff1f; 先运行python解释器&#xff0c;将python文件从硬盘读取到内存&#xff0c;解释执行内存读取的代码&#xff0c;开始识别python语法。 在第3个阶段识别文件内的python语法 3、…

tomcat 6.0环境, 网页超链接,文件下载另存为时,不能识别msi文件类型,另存为只能选htm和所有文件。...

问题: tomcat 6.0环境&#xff0c; 网页超链接&#xff0c;文件下载另存为时&#xff0c;不能识别msi文件类型&#xff0c;另存为只能选htm和所有文件。 解决办法:在 ...\Tomcat 6.0\conf 文件夹下的 web.xml 文件中添加以下类型声明:<mime-mapping><extension>msi…

ARM不同位数系统int字节数区别

32位和64位系统区别及int字节数 一&#xff09;64位系统和32位有什么区别&#xff1f; 1、64bit CPU拥有更大的寻址能力&#xff0c;最大支持到16GB内存&#xff0c;而32bit只支持4G内存 2、64位CPU一次可提取64位数据&#xff0c;比32位提高了一倍&#xff0c;理论上性能会…

我的广州大伯

2021年11月24日我正在办公室和同事调试项目&#xff0c;突然接到我婶的微信语音电话&#xff0c;我婶说广州的大伯过世了&#xff0c;他们现在正在去大伯家的路上。我大伯是爷爷奶奶最大的儿子&#xff0c;爷爷奶奶在世时&#xff0c;家里的很多事情&#xff0c;都会和大伯商量…

VUE 入坑系列 一 双向绑定

html代码<div id"app"><p>{{message}}</p><span>message1</span> <input v-model"message"><span>message2</span> <input v-model"message"></div> View Codejavascript代码var …

semihost/ITM机制浅析以及使用JLINK通过ITM调试stm32单片机

使用ITM机制实现调试stm32单片机&#xff0c;实现printf与scanf。 ITM简介 ITM机制是一种调试机制&#xff0c;是新一代调试方式&#xff0c;在这之前&#xff0c;有一种比较出名的调试方式&#xff0c;称为半主机&#xff08;semihosting&#xff09;方式。 在pc上编写过C语…

spring+ibatis配置

spring-dao.xml配置 <bean id"sqlMapClient" class"org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name"configLocation" value"classpath:/sql-map-config.xml"/> <property…