Linux驱动开发13之再论platform平台总线

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangdapao12138/article/details/81840650

1.平台总线体系工作流程

第一步:定义初始化platform bus,并注册platform到内核,在/sys目录下出现各种相关设备

第二步:内核移植人员提供platform_devices,定义各种platform_devices,然后注册

第三步:写驱动的人负责提供platform_driver,定义相关的platform_driver,然后注册

第四步:platform的match函数发现driver和device匹配后,调用driver的probe函数来完成驱动的初始化和安装,然后设备就工作起来了

第五步:操作相关的设备

2.第一步:platform本身怎么注册的?什么时候注册的?

答案:系统启动的时候注册的!

我们来看下图:

01020: int __init platform_bus_init(void)
01021: {
01022: int error;
01024: early_platform_cleanup();      //清除platform设备链表

01026: error = device_register(&platform_bus);       //该函数把设备名为platform 的设备platform_bus注册到系统中,其他的platform的设备都会以它为parent。它在sysfs中目录下. /sys/devices/platformplatform_bus总线也是设备,所以也要进行设备的注册.

       //struct device platform_bus = {

       //.init_name = "platform",

       }

//将平台bus作为一个设备注册,出现在sys文件系统的devices目录


01027: if (error)
01028: return error;
01029: error = bus_register(&platform_bus_type);    //接着bus_register(&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,

                    .pm = PLATFORM_PM_OPS_PTR,

                };

       */

       //默认platform_bus_type中没有定义probe函数;

//注册平台类型的bus,将出现sys文件系统在bus目录下,创建一个platform的目录,以及相关属性文件


01030: if (error)
01031: device_unregister(&platform_bus); //注册平台类型的bus,将出现sys文件系统在bus目录下,创建一个platform的目录,以及相关属性文件


01032: return error;
01033: }

3.第二步:platform_devices的定义与注册--------驱动工程师进行实例化


3.1定义与开放的接口

struct platform_device {

    const char  * name;         // 平台总线下设备的名字

    int     id;

    struct device   dev;        // 所有设备通用的属性部分

    u32     num_resources;      // 设备使用到的resource的个数

    struct resource * resource; // 设备使用到的资源数组的首地址

    const struct platform_device_id *id_entry;  // 设备ID

    /* arch specific additions */

    struct pdev_archdata    archdata;           // 自留地,用来提供扩展性的

};

实例化由内核驱动开发工程师和普通驱动开发工程师添加,主要是添加内核struct i2c_board_info。具体可参考上一章节的再论I2C驱动模型之device

3.2 platform_device的注册

platform_device_register和其他总线方法的特殊注册方法,但最终还是调用的device_register

0324: int platform_device_register(struct platform_device *pdev)
00325: {
00326: device_initialize(&pdev->dev);
00327: return platform_device_add(pdev);
00328:}

我们在设备模型的分析中知道了把设备添加到系统要调用device_initialize()platform_device_add(pdev)函数。

Platform设备的注册分两种方式:

a -- 对于platform设备的初注册,内核源码提供了platform_device_add()函数,输入参数platform_device可以是静态的全局设备,它是进行一系列的操作后调用device_add()将设备注册到相应的总线(platform总线)上,内核代码中platform设备的其他注册函数都是基于这个函数,如platform_device_register()platform_device_register_simple()platform_device_register_data()等。

 

b -- 另外一种机制就是动态申请platform_device_alloc()一个platform_device设备,然后通过platform_device_add_resourcesplatform_device_add_data等添加相关资源和属性。

 

     无论哪一种platform_device,最终都将通过platform_device_add这册到platform总线上。区别在于第二步:其实platform_device_add()包括device_add(),不过要先注册resources,然后将设备挂接到特定的platform总线。

 

4.第四步,platform_drver的定义与注册----驱动工程师进行调用

 

4.1 定于与开放的接口

struct platform_driver {

    int (*probe)(struct platform_device *);     // 驱动探测函数

    int (*remove)(struct platform_device *);    // 去掉一个设备

    void (*shutdown)(struct platform_device *); // 关闭一个设备

    int (*suspend)(struct platform_device *, pm_message_t state);//挂起一个设备

    int (*resume)(struct platform_device *);

    struct device_driver driver;                // 所有设备共用的一些属性

    const struct platform_device_id *id_table;  // 设备ID

};

4.2 platform_driver的注册

00483: int platform_driver_register(struct
00483: platform_driver *drv)
00484: {
00485: drv->driver.bus = &platform_bus_type;
00486: if (drv->probe)
00487: drv->driver.probe = platform_drv_probe;
00488: if (drv->remove)
00489: drv->driver.remove = platform_drv_remove;
00490: if (drv->shutdown)
00491: drv->driver.shutdown = platform_drv_shutdown;
00492:
00493: return driver_register(&drv->driver);
00494: }

platform_driver_register和其他总线方法的特殊注册方法,但最终还是调用的driver_register

我们在设备驱动模型的分析中已经知道驱动在注册要调用driver_register()platform driver的注册函数platform_driver_register()同样也是进行其它的一些初始化后调用driver_register()将驱动注册到platform_bus_type总线上. 然后设定了platform_driver内嵌的driverproberemoveshutdown函数。

 

5.第四步:platformmatch函数发现driverdevice匹配

00671: static int platform_match(struct device *dev, struct device_driver *drv)
00672: {
00673: struct platform_device *pdev = to_platform_device(dev);
00674: struct platform_driver *pdrv = to_platform_driver(drv);
00675:
00676:
/* match against the id table first */
00677: if (pdrv->id_table)
00678: return platform_match_id(pdrv->id_table, pdev)!= NULL;
00680: /* fall-back to driver name match */
00681: return (strcmp(pdev->name, drv->name) == 0);
00682: }

前面提到,实现platform模型的过程就是总线对设备和驱动的匹配过程 。打个比方,就好比相亲,总线是红娘,设备是男方,驱动是女方:

a -- 红娘(总线)负责男方(设备)和女方(驱动)的撮合;    

b -- 男方(女方)找到红娘,说我来登记一下,看有没有合适的姑娘(汉子)—— 设备或驱动的注册;

c -- 红娘这时候就需要看看有没有八字(二者的name 字段)匹配的姑娘(汉子)——match 函数进行匹配,看name是否相同;

d -- 如果八字不合,就告诉男方(女方)没有合适的对象,先等着,别急着乱做事 —— 设备和驱动会等待,直到匹配成功;

e -- 终于遇到八字匹配的了,那就结婚呗!接完婚,男方就向女方交代,我有多少存款,我的房子在哪,钱放在哪等等( struct resource    *resource),女方说好啊,于是去房子里拿钱,去给男方买菜啦,给自己买衣服、化妆品、首饰啊等等(int (*probe)(struct platform_device *) 匹配成功后驱动执行的第一个函数),当然如果男的跟小三跑了(设备卸载),女方也不会继续待下去的(  int (*remove)(struct platform_device *))。

platform_match函数就是平台总线的匹配方法。该函数的工作方法是:如果有id_table就说明驱动可能支持多个设备,所以这时候要去对比id_table中所有的name,只要找到一个相同的就匹配上了不再找了,如果找完id_table都还没找到就说明每匹配上;如果没有id_table或者没匹配上,那就直接对比device和driver的name,如果匹配上就匹配上了,如果还没匹配上那就匹配失败。

总结:

1、从这三个函数的代码可以看到,又找到了相应的platform_driverplatform_device,然后调用platform_driverproberemoveshutdown函数。这是一种高明的做法:

在不针对某个驱动具体的proberemoveshutdown指向的函数,而通过上三个过度函数来找到platform_driver,然后调用proberemoveshutdown接口。

如果设备和驱动都注册了,就可以通过bus ->matchbus->probedriver->probe进行设备驱动匹配了。
2
、驱动注册的时候platform_driver_register()->driver_register()->bus_add_driver()->driver_attach()->bus_for_each_dev()

    对每个挂在虚拟的platform bus的设备作__driver_attach()->driver_probe_device()->drv->bus->match()==platform_match()->比较strncmp(pdev->name, drv->name, BUS_ID_SIZE),如果相符就调用platform_drv_probe()->driver->probe(),如果probe成功则绑定该设备到该驱动。

6.为什么需要platform驱动,有什么好处?不用platform驱动可以吗?

引入platform模型符合Linux 设备模型 —— 总线、设备、驱动,设备模型中配套的sysfs节点都可以用,方便我们的开发;当然你也可以选择不用,不过就失去了一些platform带来的便利;

设备驱动中引入platform 概念,隔离BSP和驱动。在BSP中定义platform设备和设备使用的资源、设备的具体匹配信息。而在驱动中,只需要通过API去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。

5.整个platform总线驱动模型怎么工作的?从系统启动开始

 

猜你喜欢

转载自blog.csdn.net/wangdapao12138/article/details/81840650
今日推荐