Linux2.6 platform设备驱动(学习笔记)和理清疑问

本篇博客纯属学习笔记记录,主要理清学习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。

如果本文章对您有帮助,请点个赞呗!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值