platform总线设备驱动模型内核源码简要分析

platform总线设备驱动模型代码简要分析

平台总线设备驱动程序采用了分层分离的机制,我们先简述一下其大体上的原理,然后在从具体代码出发进行分析。

大体原理:在内核中存在平台设备总线,我们要把设备挂载到总线设备列表中,同时也要把驱动挂在到总线驱动列表中,但是相匹配的设备和驱动的名字要保持一致,因为总线会根据设备和驱动的名字是否一致,来决定他们是否匹配,一旦设备和驱动匹配起来,就会调用驱动程序里的probe函数进行处理。

代码分析,以/dirvers/mtd/nand/s3c2410.c为例来分析:

platform_driver_register(&s3c2410_nand_driver);//驱动名为s3c2410-nand

        drv->driver.bus = &platform_bus_type;//platform_bus_type里存在match函数,待会会分析

        driver_register(&drv->driver);

                 bus_add_driver(drv);//将驱动添加到平台总线驱动列表中

我们搜索名“s3c2410-nand”的设备,最终在/arch/arm/plat-s3c24xx/devs.c中发现了线索:

struct platform_device s3c_device_nand = {

 .name   = "s3c2410-nand",

 .id    = -1,

 .num_resources   = ARRAY_SIZE(s3c_nand_resource),

 .resource   = s3c_nand_resource,

};

那么这个设备是如何添加到总线上的呢?

.init_machine = osiris_init,

       platform_add_devices(osiris_devices, ARRAY_SIZE(osiris_devices));//关于osiris_devices的定义见注释1

                platform_device_register(devs[i]);

                        platform_device_add(pdev);

                                device_add(&pdev->dev);//将设备添加到总线

 

这样设备和驱动都注册到了总线上面,那么总线要做什么呢?

我们还记得注册驱动的时候有这么一句吧:

drv->driver.bus = &platform_bus_type;

我们来看看platform_bus_type的定义:

struct bus_type platform_bus_type = {

 .name  = "platform",

 .dev_attrs = platform_dev_attrs,

 .match  = platform_match,

 .uevent  = platform_uevent,

 .suspend = platform_suspend,

 .suspend_late = platform_suspend_late,

 .resume_early = platform_resume_early,

 .resume  = platform_resume,

};

这里面有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);

}

我们看到了,确实是根据驱动和设备的名字是否一致,来判断是否匹配的。

一旦匹配就会调用probe函数。

注释1

static struct platform_device *osiris_devices[] __initdata = {

 &s3c_device_i2c,

 &s3c_device_wdt,

 &s3c_device_nand,

 &osiris_pcmcia,

};

 

 

platform_driver_register驱动注册函数分析:

platform_driver_register(struct platform_driver *drv)

     drv->driver.bus = &platform_bus_type;

     drv->driver.probe = platform_drv_probe;

     drv->driver.remove = platform_drv_remove;

     drv->driver.shutdown = platform_drv_shutdown;

     driver_register(&drv->driver);

          bus_add_driver(drv);

              //drv加入平台总线下的驱动链表

             kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name);

              //下面的代码试图将drv绑定到dev

             driver_attach(drv);

              //对平台总线下设备链表进行遍历,调用__driver_attach以找到与drv匹配的设备

             bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

                  __driver_attach

                          driver_match_device(drv, dev)     

                               //调用drv->bus->match来判断drv与设备是否匹配

                               drv->bus->match ? drv->bus->match(dev, drv) : 1;

                                platform_match

                                    //根据devdrv的名字是否一致来判断是否匹配

                                    strcmp(pdev->name, drv->name)

                                 //如果匹配继续执行

                                driver_probe_device(drv, dev);

                                     really_probe(dev, drv);

                                           //我们看到调用了drv里面注册的probe函数

                                          drv->probe(dev);

 

platform_device_registe设备注册函数分析:

platform_device_register(struct platform_device *pdev)

      platform_device_add(pdev);

          device_add(&pdev->dev);

               //dev加入平台总线下的设备链表

              bus_add_device(dev);

              bus_probe_device(dev);

                  //对平台总线下驱动链表进行遍历,调用 __device_attach以找到与dev匹配的驱动

                 bus_for_each_drv(dev->bus, NULL, dev, __device_attach);

                       __device_attach

                            driver_match_device(drv, dev)

                                  //依旧是调用drv->bus->match来找到名字一样的设备与驱动

                                 drv->bus->match ? drv->bus->match(dev, drv) : 1;

 

platform总线注册分析:

我们从入口函数head.S说起:

b start_kernel

     rest_init();

         kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

         kernel_init

             do_basic_setup();

                  driver_init();

                       platform_bus_init();

                            bus_register(&platform_bus_type);

 

如此一来这个总线、设备、驱动模型就很清楚了,我们再来总结一下:

首先在系统启动的时候会注册平台总线,当我们注册设备的时候,就会将该设备加入到平台总线旗下的驱动列表中,并且遍历驱动看是否有跟设备名字相同的驱动,如果有的话,就会调用驱动的probe函数。同样在注册驱动的时候,会将该驱动加入到平台总线旗下的驱动列表中,并且遍历设备看是否有跟驱动名字相同的设备,如果有的话,就会调用驱动的probe函数。

 

所有编写平台总线、设备、驱动模型的驱动程序,最起始的工作要在probe函数里面完成!

转处:
http://blog.sina.com.cn/s/blog_7943319e01018vrg.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值