Chinaunix首页 | 论坛 | 博客
  • 博客访问: 831578
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: LINUX

2015-11-16 16:40:35

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函数里面完成!

阅读(579) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~