platform总线分析

来源:互联网 发布:脑死亡知乎 编辑:程序博客网 时间:2024/06/04 23:31
推荐阅读
http://www.linuxidc.com/Linux/2011-10/44627.htm 讲的很好
http://blog.csdn.net/qb_2008/article/category/896982


☆ platform总线简介
==>总线的产生的意义是让设备(硬件被抽象成一个结构体来代表一个设备)和驱动分离
==>linux内核中常见的的总线有I2C总线,PCI总线,串口总线,SPI总线,PCI总线,CAN总线,单总线等,所以有些设备和驱动就可以挂在这些总线上,然后通过总线上的match进行设备和驱动的匹配。但是有的设备并不属于这些常见总线,所以我们引入了一种虚拟总线,也就是platform总线的概念,对应的设备叫做platform设备,对应的驱动叫做platform驱动


☆ platform总线分析
==>先从使用方法入手分析,我们的驱动代码如果想用platform总线,主要分两个步骤:
① 抽象硬件为一个设备结构体,struct platform_device ,填充后调用 platform_device_register 将设备注册到总线上去
② 驱动部分定义一个结构体 struct platform_driver,填充后调用 platform_driver_register  将驱动注册到总线上去
注 : 比较常用的几个元素是:
      platform_device 结构体的 name 用于去适配驱动
 platform_device 结构体的 dev.platform_data 包含设备所使用的资源(io口 中断号等)
 platform_driver 结构体的 driver.name   和 platform_device_id 用于去适配设备
 platform_driver 结构体的 probe函数  是设备和驱动适配成功后去执行的函数
==>x210的调用逻辑分析举例
① 在mach-x210.c 文件中定义了platform_device *smdkc110_devices[]结构体数组,
   包含了板级移植着给封装的各类设备(以后我们添加新设备的时候也推荐在这里添加)
   这个数组被 smdkc110_machine_init(前面博客已分析如何被start_kernel调用)中的
   platform_add_devices函数调用,将数组中的设备依次调用platform_device_register 添加到platform总线上去
② 我们随便分析一个驱动,以dm9000网卡驱动程序为例
   module_init(dm9000_init);
       platform_driver_register(&dm9000_driver);
   当网卡设备和网卡驱动的名字一致的时候就会调用 dm9000_driver 结构体的 .probe 函数;


☆ 是怎么样能够执行.probe函数的
    platform总线的设计思想实现设备和驱动分离,驱动和设备分开之后就会有先后加载的顺序,所以platform总线实现了
无论是先加载设备还是先加载驱动,都会对比name,合适后调用.probe 函数,下面来分析这个过程
==> 其实这个过程就是分析 platform_bus_init , platform_device_register 和 platform_driver_register 函数的实现
==>platform_bus_init 是内核启动后被调用的 被调用的流程是 
start_kernel
    rest_init();
   do_basic_setup();
   driver_init()
   platform_bus_init();
先定义了两个结构体
struct device platform_bus = {
       .init_name       = "platform",
};
struct bus_type platform_bus_type = {
       .name             = "platform",
       .dev_attrs       = platform_dev_attrs,
       .match           = platform_match,
       .uevent           = platform_uevent,
       .pm         = &platform_dev_pm_ops,
};
然后分析platform_bus_init()函数
platform_bus_init()
    device_register(&platform_bus);   
改函数把设备名为platform 的设备platform_bus注册到系统中,其他的platform的设备都会以它为parent(后面会看到)。
它在sysfs中目录下.即 /sys/devices/platform。

bus_register(&platform_bus_type);   
注册了platform_bus_type总线,(如果设备要注册到platform总线上,要把设备device结构体的bus赋值为platform_bus_type,
然后调用device_add后,系统就会知道这个设备要挂到platform总线上)
这样platform设备的父设备有了,platform bus总线也有了,后面其他设备和驱动就可以挂在platform bus总线上了




==>分析platform_device_register(pdev) 一般是移植人员提供platform_device结构体,然后在板级文件中这个函数被调用,(新版本内核用了驱动树,这里会不一样)
    device_initialize(&pdev->dev)   初始化platform_device 的device 结构体关键内容
platform_device_add(pdev)       
   pdev->dev.parent = &platform_bus;      这个就是前面说的,把platform_bus作为父节点
个人理解之所以用父节点这个东西,方便sys虚拟文件系统下生成树状的文件结构
pdev->dev.bus = &platform_bus_type;    这个就是告诉内核,我们要把这个设备去挂载到platform总线上面
device_add(&pdev->dev);                这个就是真正的挂载设备(猜测里面肯定依据bus到挂载不同的设备总线上)
..................      一系列的其他操作
bus_probe_device(dev)
device_attach 设备去挨个匹配驱动
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
参数__device_attach
driver_match_device(drv, dev)    如果没匹配成功则return
drv->bus->match(dev, drv)   这里真正调用的就是platform_bus_type中的.match(也就是platform_match)函数
driver_probe_device(drv, dev)    调用到这里说明匹配成功了
really_probe(dev, drv);
drv->probe(dev)         这里调用就是驱动提供的probe函数

==>分析platform_driver_register(drv) 一般是由驱动开发者提供platform_driver结构体然后调用的,
drv->driver.bus = &platform_bus_type;  这个就是告诉内核,我们要把这个驱动去挂载到platform总线上面
.....................                      然后给probe remove 和 shutdown 函数赋默认值(如果没有提供函数)
driver_register(&drv->driver);             这个就是真正的挂载驱动
bus_add_driver                         Add a driver to the bus.
driver_attach   驱动去挨个匹配设备
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
参数__driver_attach
driver_match_device    如果没匹配成功则return
drv->bus->match(dev, drv)   这里真正调用的就是platform_bus_type中的.match(也就是platform_match)函数
driver_probe_device    调用到这里说明匹配成功了
really_probe(dev, drv);
drv->probe(dev)         这里调用就是驱动提供的probe函数
注:关于really_probe的代码实现里有个细节
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;
}
也就是如果platform_bus_type结构体如果定义了probe函数 则使用它的probe函数,没有定义的情况下才使用驱动开发者提供的probe函数
0 0
原创粉丝点击