当前位置:网站首页>驱动开发中platform设备驱动架构详解
驱动开发中platform设备驱动架构详解
2022-07-07 00:12:00 【嵌入式悦翔园】
1、什么是platform总线
从Linux2.6
开始Linux加入了一套驱动管理和注册机制—platform
总线驱动模型。platform
总线是一条虚拟总线(只有一条),这类总线没有对应的硬件结构。platform_device
为相应的设备,platform_driver
为相应的驱动。与传统的bus/device/driver
机制相比,platform
由内核统一进行管理,提高了代码的可移植性和安全性。
所谓的platform_device
并不是与字符设备、块设备和网络设备并列的概念,而是Linux系统提供的一种附加手段。Linux总线设备驱动模型的框架如下图所示:
从图中我们可以很清楚的看出Linux platform
总线设备驱动模型的整体架构。在总线设备驱动模型中,需关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。
当向内核注册驱动程序时,要调用platform_driver_register
函数将驱动程序注册到总线,并将其放入所属总线的drv
链表中,注册驱动的时候还会调用所属总线的match
函数寻找该总线上与之匹配的设备,如果找到与之匹配的设备则会调用相应的probe函数将相应的设备和驱动进行绑定,这一匹配过程是由总线自动完成的。
参考:Linux总线、设备、驱动模型_babyzhaoshu521的博客-CSDN博客_linux总线驱动模型
2、Platform介绍
Platform
平台设备驱动模型的作用是将驱动的实现和资源分离,是一个虚拟的总线平台。这其中存在三个成员platform_bus
,platform_device
,platform_driver
。platform_device
和platform_driver
注册不分先后顺序。
platform_bus:由链表实现,不对应实际的物理总线。
platform_device:驱动的资源比如一些 I/O端口,中断号之类的。
platform_driver:驱动的功能实现比如 注册驱动,实现file_operations 等
3、platform_driver结构体
用于描述驱动的实现。通过platform_driver
的name
成员匹配上device
后probe
函数会被调用,在设备拔出时系统会调用remove
成员做清理工作。
struct platform_driver {
int (*probe)(struct platform_device *); // device和driver的name匹配成功后调用probe函数
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; // id_table->name没有设置时,使用driver->name 进行匹配
const struct platform_device_id *id_table;
};
struct platform_device_id {
char name[PLATFORM_NAME_SIZE]; // 用来和platform_device->name进行匹配
kernel_ulong_t driver_data;
};
4、platform主要函数
/* platform_device注册和卸载函数。 */
int platform_device_register(struct platform_device *pdev);
void platform_device_unregister(struct platform_device *pdev);
/* platform_driver注册和卸载函数。 */
int platform_driver_register(struct platform_driver *drv);
void platform_driver_unregister(struct platform_driver *drv);
/* 获取platform_device中保存的资源 */
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num);
/* 获取platform_device的中断资源 */
int platform_get_irq(struct platform_device *dev, unsigned int num)
/* 用于批量注册平台设备 */
int platform_add_devices(struct platform_device **devs, int num)
5、举个栗子
这是一个LED灯的驱动,我们就以LED中的platform_driver
为例进行分析:
struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "myled",
.of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */
}
};
probe
函数,当驱动与设备匹配成功以后 probe
函数就会执行,非常重要的函数。一般驱动的提供者会编写,如果自己要编写一个全新的驱动,那么 probe
就需要自行实现。
remove
函数,platform_driver
结构体中的 remove
成员变量,当关闭 platform
设备驱动的时候此函数就会执行,以前在驱动卸载 exit
函数里面要做的事情就放到此函数中来。比如,使用 iounmap
释放内存、删除 cdev
,注销设备号等等。
driver
成员,为 device_driver
结构体变量,Linux 内核里面大量使用到了面向对象的思维,device_driver
相当于基类,提供了最基础的驱动框架。plaform_driver
继承了这个基类,然后在此基础上又添加了一些特有的成员变量。
6、platform 驱动框架
platform
驱动框架如下所示:
/* 设备结构体 */
struct xxx_dev{
struct cdev cdev;
/* 设备结构体其他具体内容 */
};
struct xxx_dev xxxdev; /* 定义个设备结构体变量 */
static int xxx_open(struct inode *inode, struct file *filp)
{
/* 函数具体内容 */
return 0;
}
static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
/* 函数具体内容 */
return 0;
}
/* * 字符设备驱动操作集 */
static struct file_operations xxx_fops = {
.owner = THIS_MODULE,
.open = xxx_open,
.write = xxx_write,
};
/* * platform 驱动的 probe 函数 * 驱动与设备匹配成功以后此函数就会执行 */
static int xxx_probe(struct platform_device *dev)
{
......
cdev_init(&xxxdev.cdev, &xxx_fops); /* 注册字符设备驱动 */
/* 函数具体内容 */
return 0;
}
static int xxx_remove(struct platform_device *dev)
{
......
cdev_del(&xxxdev.cdev);/* 删除 cdev */
/* 函数具体内容 */
return 0;
}
/* 匹配列表 */
static const struct of_device_id xxx_of_match[] = {
{
.compatible = "xxx-gpio" },
{
/* Sentinel */ }
};
/* * platform 平台驱动结构体 */
static struct platform_driver xxx_driver = {
.driver = {
.name = "xxx",
.of_match_table = xxx_of_match,
},
.probe = xxx_probe,
.remove = xxx_remove,
};
/* 驱动模块加载 */
static int __init xxxdriver_init(void)
{
return platform_driver_register(&xxx_driver);
}
/* 驱动模块卸载 */
static void __exit xxxdriver_exit(void)
{
platform_driver_unregister(&xxx_driver);
}
module_init(xxxdriver_init);
module_exit(xxxdriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jamesbin");
第 1~27 行,传统的字符设备驱动,所谓的 platform 驱动并不是独立于字符设备驱动、块设备驱动和网络设备驱动之外的其他种类的驱动。platform 只是为了驱动的分离与分层而提出来的一种框架,其驱动的具体实现还是需要字符设备驱动、块设备驱动或网络设备驱动。
第 33~39 行,xxx_probe 函数,当驱动和设备匹配成功以后此函数就会执行,以前在驱动入口 init 函数里面编写的字符设备驱动程序就全部放到此 probe 函数里面。比如注册字符设备驱动、添加 cdev、创建类等等。
第 41~47 行,xxx_remove 函数,platform_driver 结构体中的 remove 成员变量,当关闭 platform设备驱动的时候此函数就会执行,以前在驱动卸载 exit 函数里面要做的事情就放到此函数中来。比如,使用 iounmap 释放内存、删除 cdev,注销设备号等等。
第 50~53 行,xxx_of_match 匹配表,如果使用设备树的话将通过此匹配表进行驱动和设备的匹配。 第 51 行设置了一个匹配项,此匹配项的 compatible 值为“xxx-gpio”,因此当设备树中设备节点的 compatible 属性值为“xxx-gpio”的时候此设备就会与此驱动匹配。
第 52 行是一个标记,of_device_id 表最后一个匹配项必须是空的。
第 58 ~ 65 行,定义一个 platform_driver 结构体变量 xxx_driver,表示 platform 驱动,第 59~62行设置 paltform_driver 中的 device_driver 成员变量的 name 和 of_match_table 这两个属性。其中name 属性用于传统的驱动与设备匹配,也就是检查驱动和设备的 name 字段是不是相同。of_match_table 属性就是用于设备树下的驱动与设备检查。对于一个完整的驱动程序,必须提供有设备树和无设备树两种匹配方法。
第63 和 64 这两行设置 probe 和 remove 这两成员变量。
第68~71行,驱动入口函数,调用platform_driver_register函数向Linux内核注册一个platform驱动,也就是上面定义的 xxx_driver 结构体变量。
第 74~77 行,驱动出口函数,调用 platform_driver_unregister 函数卸载前面注册的 platform驱动。
7、platform总结
1)定义一个 platform_driver 结构体变量。
2)实现probe函数。
3)实现remove函数。
4)实现of_match_table。
5)调用platform_driver_register 函数向 Linux 内核注册一个 platform 驱动。
6)调用platform_driver_unregister 函数卸载 platform 驱动。
8、platform_device注册过程
platform_device
的注册过程可以简化为以下过程:
struct platform_device *pdev; // 定义一个平台设备并初始化
platform_device_register(pdev) // 注册
->>platform_device_add(pdev)
->>device_add(&pdev->dev)
->>bus_probe_device(&pdev->dev)
->>device_attach(&pdev->dev)
->>bus_for_each_drv(&(pdev->dev)->bus, NULL, &pdev->dev, __device_attach)
->>__device_attach(drv, &pdev->dev)
->>driver_probe_device(drv, &pdev->dev)
->>really_probe(&pdev->dev, drv)
->>&pdev->dev->bus->probe(dev) 或 drv->probe(dev)
到此Linux内核的总线设备驱动模型分析完毕。从上面的分析过程可以看出,所谓的platform_device
并不是与字符设备、块设备和网络设备并列的概念,而是Linux系统提供的一种附加手段。
边栏推荐
- Wechat applet Bluetooth connects hardware devices and communicates. Applet Bluetooth automatically reconnects due to abnormal distance. JS realizes CRC check bit
- Leakage relay jd1-100
- sql优化常用技巧及理解
- zabbix_ Get test database failed
- 消息队列:重复消息如何处理?
- MySQL-CentOS7通过YUM安装MySQL
- Différenciation et introduction des services groupés, distribués et microservices
- Message queue: how to handle repeated messages?
- Codeforces Round #416 (Div. 2) D. Vladik and Favorite Game
- 软件测试面试技巧
猜你喜欢
随机推荐
Differences and introduction of cluster, distributed and microservice
Explication contextuelle du langage Go
Taobao store release API interface (New), Taobao oauth2.0 store commodity API interface, Taobao commodity release API interface, Taobao commodity launch API interface, a complete set of launch store i
论文阅读【Semantic Tag Augmented XlanV Model for Video Captioning】
What is message queuing?
Educational Codeforces Round 22 B. The Golden Age
5阶多项式轨迹
[reading of the paper] a multi branch hybrid transformer network for channel terminal cell segmentation
Leakage relay jd1-100
Message queuing: how to ensure that messages are not lost
How does mapbox switch markup languages?
Leakage relay llj-100fs
软件测试面试技巧
纪念下,我从CSDN搬家到博客园啦!
消息队列:消息积压如何处理?
In memory, I moved from CSDN to blog park!
The year of the tiger is coming. Come and make a wish. I heard that the wish will come true
WEB架构设计过程
Taobao Commodity details page API interface, Taobao Commodity List API interface, Taobao Commodity sales API interface, Taobao app details API interface, Taobao details API interface
5. Data access - entityframework integration