一、简介 相关文件;
/include/linux/platform_device.h /drivers/base/platform.c 在linux设备驱动中,有许多没有特定总线的外设驱动,在实际开发中,又需要使用到总线、驱动和设备模型这三个概念,故而linux提供了platform这个虚拟总线,挂接在platform总线上的驱动称为platform驱动,由struct platform_driver描述,挂接在platorm总线上的设备称为platform设备,由struct platform_device描述。
在linux内核的驱动源码中,可以看见很多基于platform驱动框架的驱动案例实现。
二、platform总线 在linux内核中,使用struct bus_type描述一个总线,为了抽象出platform这个虚拟总线,其定义如下(/drivers/base/platform.c):
struct bus_type platform_bus_type = { .name = platform, .dev_groups = platform_dev_groups, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops,}; platform总线的注册由platform_bus_init()完成:
int __init platform_bus_init(void){ int error; early_platform_cleanup(); error = device_register(&platform_bus); if (error) return error; error = bus_register(&platform_bus_type); if (error) device_unregister(&platform_bus); of_platform_register_reconfig_notifier(); return error;} 该函数在linux内核启动过程中,在driver_init()中被调用,从而向linux内核注册了platform总线。
三、platform设备和驱动的匹配过程 在定义platform总线的时候就定了该总线下设备和驱动的具体匹配过程,由platform_match()实现:
static int platform_match(struct device *dev, struct device_driver *drv){ struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); /* when driver_override is set, only bind to the matching driver */ if (pdev->driver_override) return !strcmp(pdev->driver_override, drv->name); /* attempt an of style match first */ if (of_driver_match_device(dev, drv)) return 1; /* then try acpi style match */ if (acpi_driver_match_device(dev, drv)) return 1; /* then try to match against the id table */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != null; /* fall-back to driver name match */ return (strcmp(pdev->name, drv->name) == 0);} 从上述代码可知,platform设备和驱动的匹配分为了四种方式处理:
1、基于设备树的匹配方式。 struct device_driver结构中有个名为of_match_table的成员变量,此成员变量保存着驱动的compatible匹配表,在设备树中的每个设备节点的compatible属性会和of_match_table表中的所有成员比较,查看是否存在相同的条目,如果存在则表示设备和此驱动匹配,设备和驱动匹配成功以后probe函数就会执行(这个过程是由linux设备驱动模型中的总线去完成)。
2、acpi的匹配方式。
3、id_table 匹配。
每个struct platform_driver有一个id_table成员变量,用于保存很多id信息,这些id信息存放着这个platform驱动所支持的驱动类型。
4、比较name字段 如果第三种匹配方式的id_table不存在,就直接比较驱动和设备的name字段是否相等,如果相等则匹配成功;反之匹配不成功。
一般设备驱动为了兼容性都支持设备树和无设备树两种匹配方式。也就是第一种匹配方式一般都会存在,第三种和第四种只要存在一种就可以,一般用的最多的还是第四种,也就是直接比较驱动和设备的name字段,因为这种方式最简单了。
四、platrom驱动和platform设备 前文已经提到:挂接在platform总线上的驱动称为platform驱动,由struct platform_driver描述,挂接在platorm总线上的设备称为platform设备,由struct platform_device描述。要想开发基于platform设备驱动驱动框架的驱动程序,一定离不开这两个数据结构。首先来看看platform驱动的描述者struct platform_driver,该结构定义如下(/include/linux/platform_device.h):
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; const struct platform_device_id *id_table; bool prevent_deferred_probe;}; probe:当驱动与设备匹配成功以后.probe函数就会执行,这是一个非常重要的函数,一般驱动的提供者都会设计该函数。 remove:当platform驱动移除的时候,.remove指向的函数将执行。 shutdown、suspend和resume:与电源管理相关的函数。 driver:为device_driver结构体变量,相当于c++中的基类,提供了最基础的驱动框架。plaform_driver继承了这个基类,然后在此基础上又添加了一些特有的成员变量。 id_table:描述platform设备的id_table表,platform总线匹配驱动和设备的时候会使用。 prevent_deferred_probe:布尔类型变量(内部参数),用于防止驱动程序请求延迟probe,以避免进一步的徒劳的探测尝试。 再看看platform设备的描述者struct platform_device,定义如下(/include/linux/platform_device.h):
struct platform_device { const char *name; int id; bool id_auto; struct device dev; u32 num_resources; struct resource *resource; const struct platform_device_id *id_entry; char *driver_override; /* driver name to force a match */ /* mfd cell pointer */ struct mfd_cell *mfd_cell; /* arch specific additions */ struct pdev_archdata archdata;}; name :name表示设备名字,该参数要和所使用的platform驱动的name字段相同,否则设备就无法匹配到对应的驱动。 id:设备id。 dev:linux内核面向对象的具体体现,用于描述platform_device的基类。 num_resources:表示资源的数量。 resource:表示资源,也就是设备的信息,比如外设寄存器等。linux内核使用struct resource结构体表示资源。 id_entry:platform设备对应的id匹配表实例,在platform总线匹配驱动和设备的时候会使用到。 五、platform驱动设计 platform驱动设计的总体思路分为两种:
(1)使用【struct platform_device + struct platform_driver】的方式实现。 在这种实现方式中,需要实现描述设备信息的struct platform_device结构,并需要使用platform_device来描述具体的设备信息,然后使用platform_device_register()函数将设备信息注册到 linux 内核中;如果不再使用platform了,可以通过platform_device_unregister()函数注销相应的platform设备。
这种方式在不支持设备树的linux内核中使用!
(2)使用【struct platform_driver + 设备树】的方式来实现。 在编写 platform 驱动的时候,首先定义一个struct platform_driver结构体变量,然后实现结构体中的各个成员变量,重点是实现匹配方法以及probe 函数。当驱动和设备匹配成功以后.probe函数就会执行,具体的驱动程序在 probe 函数里面编写。当定义并初始化好 platform_driver 结构体变量以后,需要在驱动入口函数里面调用platform_driver_register()函数向linux内核注册一个platform驱动。
注意,如果linux内核支持设备树,就可以不需要再使用struct platform_device来描述设备,直接使用设备树去描述设备的信息。当然,如果一定要用struct platform_device来描述设备信息也是可以的。基于新版的linux内核的platform驱动的开发,通常是通过设备树来描述设备信息,我们只需要实现对应的platform驱动即可。
六、代码示例 本小节基于【struct platform_driver + 设备树】给出一个基本的platform驱动的设计结构。
首先使用设备树描述设备的信息:
debug_device_node { compatible = iriczhao_debug; pinctrl-0 = ; pinctrl-1 = ; pinctrl-2 = ; bus-width = ; non-removable; status = okay; }; 上述代码描述了一个名为debug_device_node的设备节点,给出了compatible属性值。
platform驱动设计:
#include #include #include #include #include #include #include static int platform_demo_probe(struct platform_device *dev){ printk(>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>); printk(do platform_demo_probe); printk(>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>); return 0;}static int platform_demo_remove(struct platform_device *dev){ printk(do platform_demo_remove); return 0;}static const struct of_device_id platform_demo_id[] = { { .compatible = iriczhao_debug }, { /* sentinel */ }};module_device_table(of, platform_demo_id);static struct platform_driver platform_demo_driver = { .probe = platform_demo_probe, .remove = platform_demo_remove, .driver = { .name = dd, .of_match_table = platform_demo_id, }};static int __init platform_demo_init(void){ printk(do platform_demo_init); return platform_driver_register(&platform_demo_driver);}static void __exit platform_demo_exit(void){ printk(do platform_demo_exit); platform_driver_unregister(&platform_demo_driver);}module_init(platform_demo_init);module_exit(platform_demo_exit);module_author(iric);module_license(gpl); 以模块方式构建上述代码,运行后结果如下:
从上述结果可知:platform驱动和对应的设备匹配成功,且.probe指向的函数得以执行,当模块退出时,platform驱动将被移除,这时候.remove指向的函数得以执行。结果符合程序预期效果!
安钛克HCG850Extreme电源评测 无愧于冠以Extreme名号
管壳式换热器泄漏的原因分析与解决方法
特斯拉:基于乘客“身体”空间位置而打造车辆个性化系统及方法
联发科线上发布天玑1000+,具有多方面的强悍性能
LifeSize推出移动视频会议平台ClearSea
一文总结linux的platform驱动
从IPv6到IPv6+创新 当IPv6+成为千行百业的朋友 天下谁人不识君
晶振并联1MΩ电阻的原因分析
数字接收机中高性能ADC和射频器件的动态性能要求
单目摄像对比立体摄像头的优点
专家访谈 | 为什么新能源车企都在比拼“热管理”?
大米重金属检测仪概述/参数/特点
如何利用新型微MCU简化嵌入式系统的安全挑战
光纤通信简介
HiFi和手机结合,酷派、vivo、OPPO三家旗舰谁更强
分享一下用一只电笔检测电机好坏的办法
行业案例 | 锂电池自动封口设备中,这个工位的传感器怎么选?
据报告显示2019年的全球移动电源市场规模近107亿美元
IDT发布新一代气体传感器 可探测微量氢气、工业有机化学气体
50万奖金+京东数科offer:学生党斩获JDD-2018全球总冠军