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

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 = ; if (!drv)
return ; 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,
};

探究platform_driver中“多态”思想的更多相关文章

  1. 探究platform_driver中的shutdown用途

    http://blog.csdn.net/moxiaomomo/article/details/7897943 "quiesce" 说的也不太明确,我的猜测是:比如系统中有一个大功 ...

  2. 深入Java核心 Java中多态的实现机制(1)

    在疯狂java中,多态是这样解释的: 多态:相同类型的变量,调用同一个方法时,呈现出多中不同的行为特征, 这就是多态. 加上下面的解释:(多态四小类:强制的,重载的,参数的和包含的) 同时, 还用人这 ...

  3. spring框架中AOP思想与各种配置详解

    Spring中提供两种AOP支持:   1.基于代理的经典AOP   2.Aspectj注解配置AOP    首先我们先了解什么是AOP,AOP(Aspect Oriented Programming ...

  4. 编写Java程序,以继承和多态思想模拟饲养员喂养不同动物的不同行为

    返回本章节 返回作业目录 需求说明: 以继承和多态思想模拟饲养员喂养不同动物的不同行为 动物园有饲养员和动物,其中动物有老虎.马.猴子.羊.狼等. 饲养员对不同的动物有不同的喂养行为. 实现思路: 以 ...

  5. 关于java中多态的理解

    java三大特性:封装,继承,多态. 多态是java的非常重要的一个特性: 那么问题来了:什么是多态呢? 定义:指允许不同类的对象对同一消息做出响应.即同一消息可以根据发送对象的不同而采用多种不同的行 ...

  6. 个人对Java中多态的一些简单理解

    什么是多态 面向对象的三大特性:封装.继承.多态.从一定角度来看,封装和继承几乎都是为多态而准备的.这是我们最后一个概念,也是最重要的知识点. 多态的定义:指允许不同类的对象对同一消息做出响应.即同一 ...

  7. Java中多态的一些简单理解

    什么是多态 .面向对象的三大特性:封装.继承.多态.从一定角度来看,封装和继承几乎都是为多态而准备的.这是我们最后一个概念,也是最重要的知识点. .多态的定义:指允许不同类的对象对同一消息做出响应.即 ...

  8. 探究JavaScript中的五种事件处理程序

    探究JavaScript中的五种事件处理程序 我们知道JavaScript与HTML之间的交互是通过事件实现的.事件最早是在IE3和Netscape Navigator 2中出现的,当时是作为分担服务 ...

  9. 初步探究java中程序退出、GC垃圾回收时,socket tcp连接的行为

    初步探究java中程序退出.GC垃圾回收时,socket tcp连接的行为 今天在项目开发中需要用到socket tcp连接相关(作为tcp客户端),在思考中发觉需要理清socket主动.被动关闭时发 ...

随机推荐

  1. win8 内置管理员账号既能使用metro应用又能使用默认共享

    一.内置管理员账号打开METRO应用 1.按WIN+R 弹出运行框. 2.输入gpedit.msc点确定,打开本地组策略编辑器. 3.逐级选择"计算机配置","Windo ...

  2. sys模块 logging模块 序列化模块

    一 :sys模块 sys.argv 命令行参数List,第一个元素是程序本身路径 sys.exit(n) 退出程序,正常退出时exit(0) sys.version 获取Python解释程序的版本信息 ...

  3. Python3+Selenium3自动化测试-(五)

    这里来说一说selenium中的等待方式,其实在webdriver只有两种类型等待方式,显式等待和隐式等待,之前是在程序运行过程中使用time模块中的sleep进行代码的休眠进行强制等待,是显式等待中 ...

  4. 010-shiro与spring web项目整合【四】缓存Ehcache

    一.Ehcache shiro每次授权都会通过realm获取权限信息,为了提高访问速度需要添加缓存,第一次从realm中读取权限数据,之后不再读取,这里Shiro和Ehcache整合. 1.添加Ehc ...

  5. vue项目如何打包扔向服务器

    vue项目如何打包扔向服务器   当我们将 vue 项目完成后,面临的就是如何将项目进行打包上线,放到服务器中.我使用的是 vue-cli(simple) 脚手架,所以就讲一下如何将项目进行打包,并放 ...

  6. go——结构体(二)

    Go语言是一种静态类型的编程语言.这意味着,编译器需要在编译时知晓程序里每个值的类型. 如果提前知道类型信息,编译器就可以确保程序合理的使用值. 这有助于减少潜在的内存异常和bug,并且使编译器有机会 ...

  7. LCT(link cut tree) 动态树

    模板参考:https://blog.csdn.net/saramanda/article/details/55253627 综合各位大大博客后整理的模板: #include<iostream&g ...

  8. Visual Studio 2012的Windows Service服务安装方式

    windows服务应用程序是一种长期运行在操作系统后台的程序,它对于服务器环境特别适合,它没有用户界面,不会产生任何可视输出,任何用户输出都回被写进windows事件日志.计算机启动时,服务会自动开始 ...

  9. java 获取request参数集

    request里有两个方法 request.getParameterMap(); request.getParameterNames(); 我想用这两种方法获取. 1.用request.getPara ...

  10. SVN插件下载地址及更新地址

    SVN插件下载地址及更新地址,你根据需要选择你需要的版本.现在最新是1.8.xLinks for 1.8.x Release:Eclipse update site URL: http://subcl ...