本篇博客纯属学习笔记记录,主要理清学习platform设备驱动的疑问,有参考其他博客:
什么是platform总线
platform总线是区别于实体总线USB、 I2C、SPI 、PIC总线的虚拟总线,一些usb设备选址的话需要通过USB总线来进行寻址,而有些类似于SoC内部外设如led 看门狗 定时器是直接通过内存的寻址空间来进行寻址的,cpu与这些设备通信是不需要总线的,2.6内核以后要对所有设备进行统一管理,通过kset、kobject来建立层次关系,对这些直接通过内存寻址的设备虚拟了一种总线即platform总线,在硬件上实际是没有这个总线;platform内核纯软件的总线,所有的直接通过内存寻址的设备都映射到这条总线上;
platform总线的优点
1.可以通过platform总线,可以遍历所有的platform总线设备;platform本质其实也是kset、kobject,具有kobject的特性
2.实现设备与驱动的分离,通过platform总线,设备与驱动是分开注册的,通过platform总线的probe来随时检测与设备匹配的驱动,如匹配上即进行这个设备的驱动注册;
3.由于上面这个优势,一个驱动可以供同类的几个设备使用;
代码总框架
————————————————
原文链接:https://blog.csdn.net/q921374795/article/details/88886171
此处先附上platform总线代码框架:
初始化platform总线
platform_bus_init()
--> device_register(&platform_bus)
--> bus_register(&platform_bus_type)
--> platform_bus_type ==> platform_match(dev和drv的配对原则)
注册device
platform_device_register(struct platform_device *dev)
--> platform_device_add(pdev)
--> device_add(&pdev->dev)
--> bus_attach_device(dev)
--> __device_attach(dev, true)
--> bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver)
--> __device_attach_driver
--> driver_match_device(drv, dev) ==> 匹配driver,匹配成功才能往下走
--> driver_probe_device(drv, dev)
--> really_probe(dev, drv)
--> dev->bus->probe(dev) / drv->probe(dev)
注册driver
platform_driver_register(struct platform_driver *drv)
--> __platform_driver_register(drv, THIS_MODULE)
--> driver_register(&drv->driver)
--> bus_add_driver(drv)
--> driver_attach(drv)
--> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
--> __driver_attach
--> driver_match_device(drv, dev) ==> 匹配driver,匹配成功才能往下走
--> driver_probe_device(drv, dev)
--> really_probe(dev, drv)
--> dev->bus->probe(dev) / drv->probe(dev)
platform总线疑问
1.platform设备驱动框架分为 dev,driver 和 platform bus;
dev和driver分别由platform_device_register()和platform_driver_register()注册,那么,bus在哪里注册呢?
-->kernel_init() //内核初始化函数
-->do_basic_setup()
-->driver_init()
-->platform_bus_init()
在内核初始化时,便注册了平台总线,在实际使用中,dev由内核启动时自动注册,driver则在驱动模块的加载函数中使用platform_driver_register()注册;dev在内核中注册的位置,应在bus初始化之后;
2.dev资源问题
dev初始化代码
static struct resource led_resource[] = {
[0] = {
.start = 0x56000050,
.end = 0x56000050 + 8 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 5,
.end = 5,
.flags = IORESOURCE_IRQ,
}
};
static struct platform_device led_dev = {
.name = "myled",
.id = -1,
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource,
.dev = {
.release = led_release,
},
};
static int led_dev_init(void)
{
platform_device_register(&led_dev);
return 0;
}
int platform_device_add(struct platform_device *pdev)
{
....
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = pdev->dev.bus_id;
p = r->parent;
if (!p) {
if (r->flags & IORESOURCE_MEM)
p = &iomem_resource;
else if (r->flags & IORESOURCE_IO)
p = &ioport_resource;
}
if (p && insert_resource(p, r)) {
printk(KERN_ERR
"%s: failed to claim resource %d\n",
pdev->dev.bus_id, i);
ret = -EBUSY;
goto failed;
}
}
....
led_resource是我们注册的,这里我们设置了两个类型,IORESOURCE_MEM和IORESOURCE_IRQ;但是在platform_device_add()函数注册的资源的时候,只是对IORESOURCE_MEM和IORESOURCE_IO这两种类型做处理而已,这样的话,IORESOURCE_IRQ会被处理吗?
有谁知道这个IORESOURCE_IRQ等其他类型是如何注册的吗?知道的大兄弟麻烦在评论区说明一下!!!
3.dev是如何和drv连接的?
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
pr_debug("%s: Matched Device %s with Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
ret = really_probe(dev, drv);
done:
return ret;
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
....
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
....
}
先看driver_probe_device(),如果drv->bus->match有定义的话,就跑drv->bus->match连接函数,此函数是判断pdev->name和 drv->name是否一致,只当name一致的时候,才会真正的去链接dev和drv;
此处的 drv->bus 在之前的platform_device_add()中定义,等于&platform_bus_type;
platform_bus_type->match定义如下:
static int platform_match(struct device * dev, struct device_driver * drv)
{
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}
4.drv的流程和dev的流程类似,看 代码总框架 就行了
5.linux3.5版本后的 platform_bus_type->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) //优先比较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); //pdev->name和 drv->name的优先级最低
}
韦东山教程使用的Linux2.6,match函数只比较pdev->name和 drv->name是否一致;
在linux3之后的板本,platform总线的match优先比较 pdrv->id_table->name 和 pdev->name,pdev->name和 drv->name的比较优先级低于id_table的优先级;
另外,linux3之后引入了设备树的概念,of_driver_match_device(dev, drv) 此接口判断设备数据节点node,优先级更高于 id_table。
如果本文章对您有帮助,请点个赞呗!!!