zoukankan      html  css  js  c++  java
  • 探究platform_driver中“多态”思想

    问题最初是下面的两段代码引出的:

    static struct platform_driver sonypi_driver = {
        .driver        = {
            .name    = "sonypi",
            .owner    = THIS_MODULE,
        },
        .probe        = sonypi_probe,
        .remove        = __devexit_p(sonypi_remove),
        .shutdown    = sonypi_shutdown,
        .suspend    = sonypi_suspend,
        .resume        = sonypi_resume,
    };
    static const struct dev_pm_ops aa5302_pm_ops = {
        .suspend    = aa5302_suspend,
        .resume        = aa5302_resume,
    };
    #endif
    
    static struct platform_driver aa5302_driver = {
        .driver = {
            .name  = "aa5302",
            .owner = THIS_MODULE,
    #ifdef CONFIG_PM
            .pm    = &aa5302_pm_ops,
    #endif
        },
        .probe = aa5302_probe,
        .remove = __devexit_p(aa5302_remove),
        .shutdown = aa5302_shutdown,
    };

    注意到这两个驱动都是platform driver,但是对于电源管理的定义方式却不同:前者直接赋值platform_driver中的suspend/resume字段,后者间接赋值driver.pm字段。一直以来,我接触到的原厂驱动,大部分是后面的定义方式,一直觉得其定义方式很罗嗦,这次要好好研究一下。

    这里http://lists.kernelnewbies.org/pipermail/kernelnewbies/2013-October/009074.html有人问了一个类似的问题,可惜的是没有回复!

    我们先看看kernel/include/linux/platform_device.h中的定义:

    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;
    };

    再看看kernel/include/linux/device.h中的定义:

    struct device_driver {
        const char        *name;
        struct bus_type        *bus;
        struct module        *owner;
        const char        *mod_name;    /* used for built-in modules */
        bool suppress_bind_attrs;    /* disables bind/unbind via sysfs */
        const struct of_device_id    *of_match_table;
        int (*probe) (struct device *dev);
        int (*remove) (struct device *dev);
        void (*shutdown) (struct device *dev);
        int (*suspend) (struct device *dev, pm_message_t state);
        int (*resume) (struct device *dev);
        const struct attribute_group **groups;
        const struct dev_pm_ops *pm;
        struct driver_private *p;
    };

    初看,它们都定义了probe, remove ... 等函数,但platform_driver里面包含了device_driver结构体,也就是说platform_driver中实际上有2套probe, remove ... 为什么会这样的?我们再看看drivers/base/platform.c中关于platform_driver_register的定义:

    int platform_driver_register(struct platform_driver *drv)
    {
        drv->driver.bus = &platform_bus_type;
        if (drv->probe)
            drv->driver.probe = platform_drv_probe;
        if (drv->remove)
            drv->driver.remove = platform_drv_remove;
        if (drv->shutdown)
            drv->driver.shutdown = platform_drv_shutdown;
    
        return driver_register(&drv->driver);
    }

    至此,你可能会更纳闷,这不是画蛇添足吗?把platform_driver中的probe, remove字段又赋值给了driver中的相应字段,最终效果是,这2套probe, remove...实际上指向的分别是相同的回调函数!其实,如果细看platform_driver和device_driver中的probe, remove指针的定义,你会发现,他们还是有差别的:

    int (*probe)(struct platform_device *);
    int (*probe) (struct device *dev);

    那就是,参数类型不同哦!前者是platform_device,后者是device!如果你学习过C++语言,头脑中有“继承”和“多态”的概念,我相信你已经知道为什么要这么做了:)

    这么说吧,device和device_driver分别是驱动中的基类,而platform_device和platform_driver分别是PLATFORM BUS体系中对应的派生类;device_driver中的probe是基类的虚函数,platform_driver中的probe是派生类中的重载函数。如果我们要写的是platform设备驱动,那么应该按照platform_driver中的回调函数声明格式来定义自己的回调函数。

    因此sonypi驱动的shutdown函数是这样定义的:

    static void sonypi_shutdown(struct platform_device *dev)
    {
        sonypi_disable();
    }

    而不是:

    static void sonypi_shutdown(struct device *dev)
    {
        sonypi_disable();
    }

    如果你非要用这个“基类的函数”,那也是有办法的,在声明platform_driver的时候,用下面的方式,纯属猜测,没有验证哈。

    static struct platform_driver sonypi_driver = {
        .driver        = {
            .name    = "sonypi",
            .owner    = THIS_MODULE,
            .shutdown = sonypi_shutdown,
        },
        .probe        = sonypi_probe,
        .remove        = __devexit_p(sonypi_remove),
        .suspend    = sonypi_suspend,
        .resume        = sonypi_resume,
    };

    ---------------------------------------------------------华丽的分割线-----------------------------------------------------------

    下面再说说电源管理的问题,sonypi驱动使用了platform_driver.suspend/resume的方式来定义回调函数,而aa5302驱动使用platform_driver.driver.pm.suspend/resume的方式定义回调,这和上面说所讲的“多态”似乎不完全契合。确实如此,这里涉及到另外一个legacy的问题,stackoverflow上的一篇解释说的很明白:http://stackoverflow.com/questions/19462639/which-suspend-resume-pointer-is-the-right-one-to-use 我们看看platform_pm_suspend的实现,如果driver.pm字段不为空,则使用.pm提供的回调,否则使用platform_driver.suspend定义的lagacy方式的回调。

    int platform_pm_suspend(struct device *dev)
    {
        struct device_driver *drv = dev->driver;
        int ret = 0;
    
        if (!drv)
            return 0;
    
        if (drv->pm) {
            if (drv->pm->suspend)
                ret = drv->pm->suspend(dev);
        } else {
            ret = platform_legacy_suspend(dev, PMSG_SUSPEND);
        }
    
        return ret;
    }

    device_driver.suspend/resume是旧的方式,他们在dev_pm_ops诞生以前就存在了,我们新的驱动中应该使用dev_pm_ops来定义回调函数。stackoverflow中提到的i2c驱动,跟这里的2个platform_driver还不完全一样,i2c_driver仅仅提供一组通信接口,其并不提供设备的控制逻辑,在音频codec中体现的非常明显,音频codec本质上是一个I2C芯片,在probe函数中注册了一个更加“高级”的控制设备:codec,控制逻辑由codec来完成。因此其i2c_driver中并没有提供电源管理功能:

    static struct i2c_driver wm8900_i2c_driver = {
        .driver = {
            .name = "WM8900",
            .owner = THIS_MODULE,
        },
        .probe = wm8900_i2c_probe,
        .remove = __devexit_p(wm8900_i2c_remove),
        .shutdown = wm8900_i2c_shutdown,
        .id_table = wm8900_i2c_id,
    };

    而是转移到了codec中:

    static struct snd_soc_codec_driver soc_codec_dev_wm8900 = {
        .probe =    wm8900_probe,
        .remove =    wm8900_remove,
        .suspend =    wm8900_suspend,
        .resume =    wm8900_resume,
        .set_bias_level = wm8900_set_bias_level,
        .volatile_register = wm8900_volatile_register,
        .reg_cache_size = ARRAY_SIZE(wm8900_reg_defaults),
        .reg_word_size = sizeof(u16),
        .reg_cache_default = wm8900_reg_defaults,
    };
  • 相关阅读:
    随机算法之舍伍德算法
    KMP算法笔记
    分治法求解最大子段和问题
    递归与分治之间的关系
    微信公众号开发学习笔记(一)
    关于echo$PATH中的PTAH来源
    【Leetcode】807. Max Increase to Keep City Skyline
    【Leetcode】804. Unique Morse Code Words
    【Leetcode】709. To Lower Case
    【Leetcode】Jewels and Stones
  • 原文地址:https://www.cnblogs.com/swnuwangyun/p/4233821.html
Copyright © 2011-2022 走看看