linux驱动程序之电源管理之新版linux系统设备架构中关于电源管理方式的变更

新版linux系统设备架构中关于电源管理方式的变更 based on linux-2.6.32

一、设备模型各数据结构中电源管理的部分

linux的设备模型通过诸多结构体来联合描述,如struct device,struct device_type,struct class, struct device_driver,struct bus_type等。

@kernel/include/linux/devices.h中有这几中结构体的定义,这里只列出和PM有关的项,其余查看源码:

struct device{

...

struct dev_pm_info power;

...

}

struct device_type {

...

int (*uevent)(struct device *dev, struct kobj_uevent_env *env);

char *(*devnode)(struct device *dev, mode_t *mode);

void (*release)(struct device *dev);

const struct dev_pm_ops *pm;

};

struct class {

...

void (*class_release)(struct class *class);

void (*dev_release)(struct device *dev);

int (*suspend)(struct device *dev, pm_message_t state);

int (*resume)(struct device *dev);

const struct dev_pm_ops *pm;

...

};

struct device_driver {

...

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 dev_pm_ops *pm;

...

};

struct bus_type {

...

int (*match)(struct device *dev, struct device_driver *drv);

int (*uevent)(struct device *dev, struct kobj_uevent_env *env);

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 dev_pm_ops *pm;

...

};

以上可以看出和电源管理相关的两个结构体是struct dev_pm_info和struct dev_pm_ops,他们定义于文件   @kernel/include/linux/pm.h

struct dev_pm_info {

pm_message_t  power_state;

unsigned int  can_wakeup:1;

unsigned int  should_wakeup:1;

enum dpm_state  status;  /* Owned by the PM core - 表示该设备当前的PM状态*/

#ifdef CONFIG_PM_SLEEP

struct list_head entry;  /* 链接到dpm_list全局链表中的连接体 */

#endif

#ifdef CONFIG_PM_RUNTIME   // undef

struct timer_list suspend_timer;

unsigned long  timer_expires;

struct work_struct work;

wait_queue_head_t wait_queue;

spinlock_t  lock;

atomic_t  usage_count;

atomic_t  child_count;

unsigned int  disable_depth:3;

unsigned int  ignore_children:1;

unsigned int  idle_notification:1;

unsigned int  request_pending:1;

unsigned int  deferred_resume:1;

enum rpm_request request;

enum rpm_status  runtime_status;

int   runtime_error;

#endif

};

struct dev_pm_ops {

int (*prepare)(struct device *dev);

void (*complete)(struct device *dev);

int (*suspend)(struct device *dev);

int (*resume)(struct device *dev);

int (*freeze)(struct device *dev);

int (*thaw)(struct device *dev);

int (*poweroff)(struct device *dev);

int (*restore)(struct device *dev);

int (*suspend_noirq)(struct device *dev);

int (*resume_noirq)(struct device *dev);

int (*freeze_noirq)(struct device *dev);

int (*thaw_noirq)(struct device *dev);

int (*poweroff_noirq)(struct device *dev);

int (*restore_noirq)(struct device *dev);

int (*runtime_suspend)(struct device *dev);

int (*runtime_resume)(struct device *dev);

int (*runtime_idle)(struct device *dev);

};

二、device中的dev_pm_info结构体

device结构体中的power项用来将该设备纳入电源管理的范围,记录电源管理的一些信息。   在注册设备的时候调用函数device_add()来向sysfs系统添加power接口和注册进电源管理系统,代码片段如下:

...

error = dpm_sysfs_add(dev);  @kernel/drivers/base/power/sysfs.c

if (error)

goto DPMError;

device_pm_add(dev);      @kernel/drivers/base/power/main.c

...

其中dpm_sysfs_add()函数用来向sysfs文件系统中添加相应设备的power接口文件,如注册mt6516_tpd paltform device的时候,会在sysfs中出现如下目录和文件:

#pwd

/sys/devices/platform/mt6516-tpd

#cd mt6516-tpd

#ls -l

-rw-r--r-- root     root         4096 2010-01-02 06:35 uevent

-r--r--r-- root     root         4096 2010-01-02 06:39 modalias

lrwxrwxrwx root     root              2010-01-02 06:39 subsystem -> ../../../bus/platform

drwxr-xr-x root     root              2010-01-02 06:35 power

lrwxrwxrwx root     root              2010-01-02 06:39 driver -> ../../../bus/platform/drivers/mt6516-tpd

#cd power

#ls -l

-rw-r--r-- root     root         4096 2010-01-02 06:39 wakeup

源码片段:

static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);

static struct attribute * power_attrs[] = {

&dev_attr_wakeup.attr,

NULL,

};

static struct attribute_group pm_attr_group = {

.name = "power",  // attribute_group结构体的name域不为NULL的话,都会已name建立一个属性目录的

.attrs = power_attrs,

};

int dpm_sysfs_add(struct device * dev)   {

return sysfs_create_group(&dev->kobj, &pm_attr_group); //在当前device的kobject结构体对应的目录下建立

}

其中的device_pm_add()函数会将该设备插入到电源管理的核心链表dpm_list中统一管理。

值得一提的是,在函数device_initialize()会调用函数device_pm_init()来初始化该device结构体的power域:

dev->power.status = DPM_ON;

void device_pm_add(struct device *dev)   {

...

mutex_lock(&dpm_list_mtx);

if (dev->parent) {

if (dev->parent->power.status >= DPM_SUSPENDING)       // 如果某设备处于DPM_SUSPENDING极其之后的状态,此时不允许以该设备为父设备注册子设备

dev_warn(dev, "parent %s should not be sleeping/n", dev_name(dev->parent));

}

else if (transition_started) { // transition_started全局变量包含在PM transition期间不允许注册设备

/*

* We refuse to register parentless devices while a PM

* transition is in progress in order to avoid leaving them

* unhandled down the road

*/

dev_WARN(dev, "Parentless device registered during a PM transaction/n");

}

list_add_tail(&dev->power.entry, &dpm_list); // 将device结构体通过power.entry项链接进dpm_list

mutex_unlock(&dpm_list_mtx);

}

void device_pm_remove(struct device *dev)   {

...

mutex_lock(&dpm_list_mtx);

list_del_init(&dev->power.entry);

mutex_unlock(&dpm_list_mtx);

pm_runtime_remove(dev);

}

举例说明:

我们熟知的platform bus在系统中也是作为一种设备注册进了系统,在sysfs文件系统中的位置是:   /sys/devices/platform。使用函数device_register(&platform_bus)进行注册,调用device_add()函数,

注册ok之后,也会出现目录/sys/devices/platform/power。最后也会将其添加进dpm_list中。

i2c控制器外设代表的设备是注册在platform总线上的,也就是说它的父设备是platform。

最终在platform_device_add()中会调用函数device_add()函数来添加设备,最终也会在mt6516-i2c.0/   mt6516-i2c.1/mt6516-i2c.2中出现一个power目录,同时这3个platform设备会依靠   platform_device.dev.power.entry连接件链接到电源管理核心链表dpm_list中。

/sys/devices/platform/mt6516-i2c.2/power

每一个i2c控制器都会在系统中至少注册成一个适配器(adapter),该结构体将会间接提供给i2c设备的驱动来使用,以避免直接使用i2c控制器结构体。

这个适配器没有对应的driver,在错综复杂的i2c架构中,相对于只起到了一个承上启下的作用,上接i2c控制器的结构体及driver,下接i2c设备的结构体i2c_client和特点的driver。

adapter.dev.parent为i2c控制器对应的device,所以就会出现名为i2c-0/1/2的设备kobject,只是该设备的bus总线和device_type是:   adap->dev.bus = &i2c_bus_type;

adap->dev.type = &i2c_adapter_type;

然后调用函数device_register(&adap->dev);来注册这个device,所以在对应的i2c-0/1/2目录下也会出现power目录。   /sys/devices/platform/mt6516-i2c.2/i2c-2/power

i2c设备会通过自动检测或者事先静态描述的方式来注册进系统,不管什么方式,都会调用到函数:

i2c_new_device()

struct i2c_client *client;

client->dev.parent = &client->adapter->dev;

client->dev.bus = &i2c_bus_type;

client->dev.type = &i2c_client_type;

dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),client->addr);

status = device_register(&client->dev);

可以看得出来名字是什么了,例如:2-00aa

#ls -l /sys/devices/platform/mt6516-i2c.2/i2c-2/2-00aa

-rw-r--r-- root     root         4096 2010-01-02 06:35 uevent

-r--r--r-- root     root         4096 2010-01-02 06:38 name   -

r--r--r-- root     root         4096 2010-01-02 06:38 modalias

lrwxrwxrwx root     root              2010-01-02 06:38 subsystem -> ../../../../../bus/i2c

drwxr-xr-x root     root              2010-01-02 06:35 power

lrwxrwxrwx root     root              2010-01-02 06:38 driver -> ../../../../../bus/i2c/drivers/mt6516-tpd

三、bus_type、device_driver、device_type、class中的dev_pm_ops方法结构体

在新的linux内核中,已不再有subsystem数据结构了,他的功能被kset代替。

全局变量bus_kset初始化:

kernel_init()-->do_basic_setup()-->driver_init()-->buses_init()

bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);

1. 总线类型结构体:

bus_type,以platform和i2c总线为例:

@kernel/drivers/base/platform.c

static const struct dev_pm_ops platform_dev_pm_ops = {

.prepare = platform_pm_prepare,   //

.complete = platform_pm_complete,  //

.suspend = platform_pm_suspend,   //

.resume = platform_pm_resume,    //

.freeze = platform_pm_freeze,

.thaw = platform_pm_thaw,

.poweroff = platform_pm_poweroff,  //

.restore = platform_pm_restore,

.suspend_noirq = platform_pm_suspend_noirq,

.resume_noirq = platform_pm_resume_noirq,

.freeze_noirq = platform_pm_freeze_noirq,

.thaw_noirq = platform_pm_thaw_noirq,

.poweroff_noirq = platform_pm_poweroff_noirq,

.restore_noirq = platform_pm_restore_noirq,

.runtime_suspend = platform_pm_runtime_suspend,

.runtime_resume = platform_pm_runtime_resume,

.runtime_idle = platform_pm_runtime_idle,

};

struct bus_type platform_bus_type = {

.name  = "platform",

.dev_attrs = platform_dev_attrs,

.match  = platform_match,

.uevent  = platform_uevent,

.pm  = &platform_dev_pm_ops,

};

从上面的dev_pm_ops结构体中拿出最普遍使用的函数指针来说明一下,对于bus_type它的电源管理是如何实现的。

static int platform_pm_prepare(struct device *dev)   {

struct device_driver *drv = dev->driver;

int ret = 0;

if (drv && drv->pm && drv->pm->prepare)

ret = drv->pm->prepare(dev);

return ret;

}

static void platform_pm_complete(struct device *dev)   {

struct device_driver *drv = dev->driver;

if (drv && drv->pm && drv->pm->complete)

drv->pm->complete(dev);

}

可以看出这两个函数都最终是利用了device_driver结构体中的dev_pm_ops函数方法结构体中的对应函数指针。

static int platform_legacy_suspend(struct device *dev, pm_message_t mesg)   {

struct platform_driver *pdrv = to_platform_driver(dev->driver);

struct platform_device *pdev = to_platform_device(dev);

int ret = 0;

if (dev->driver && pdrv->suspend)

ret = pdrv->suspend(pdev, mesg);

return ret;

}

static int platform_legacy_resume(struct device *dev)   {

struct platform_driver *pdrv = to_platform_driver(dev->driver);

struct platform_device *pdev = to_platform_device(dev);

int ret = 0;

if (dev->driver && pdrv->resume)

ret = pdrv->resume(pdev);

return ret;

}

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

}

static int platform_pm_resume(struct device *dev)   {

struct device_driver *drv = dev->driver;

int ret = 0;

if (!drv)

return 0;

if (drv->pm) {

if (drv->pm->resume)

ret = drv->pm->resume(dev);

} else {

ret = platform_legacy_resume(dev);

}

return ret;

}

这里suspend和resume函数也是最终都是调用了device_driver结构体的dev_pm_ops方法结构体中的对应函数指针(device_driver.pm项被初始化),否则使用老式的方法:platform_legacy_suspend(dev, PMSG_SUSPEND)和platform_legacy_resume(dev)。根据这两个函数的源码可以看出。一般地,在我们的platform device的platform driver定义中,都是实现了pdrv.suspend和pdrv.resume函数,而并没有实现pdrv.driver.suspend和pdrv.driver.resume函数,其余三个函数可以在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);

}

i2c总线注册没有使用新式的电源管理方法:dev_pm_ops,仍然使用老式的方式:

@kernel/drivers/i2c/i2c-core.c

struct bus_type i2c_bus_type = {

.name  = "i2c",

.match  = i2c_device_match,

.probe  = i2c_device_probe,

.remove  = i2c_device_remove,

.shutdown = i2c_device_shutdown,

.suspend = i2c_device_suspend,

.resume  = i2c_device_resume,

};

static int i2c_device_suspend(struct device *dev, pm_message_t mesg)   {

struct i2c_client *client = i2c_verify_client(dev);

struct i2c_driver *driver;

if (!client || !dev->driver)

return 0;

driver = to_i2c_driver(dev->driver);

if (!driver->suspend)

return 0;

return driver->suspend(client, mesg);

}

static int i2c_device_resume(struct device *dev)   {

struct i2c_client *client = i2c_verify_client(dev);

struct i2c_driver *driver;

if (!client || !dev->driver)

return 0;

driver = to_i2c_driver(dev->driver);

if (!driver->resume)

return 0;

return driver->resume(client);

}   // 实际上都是调用的i2c_driver结构体的suspend和resume函数。

2. device_type结构体暂时还没有找到有哪一个模块使用了新式了dev_pm_ops电源管理方法,一般都是没有实现这部分。

3. class结构体也没有找到使用dev_pm_ops方法结构体的地方,先暂时放一放。

4. device_driver

struct device_driver {

const char  *name;

struct bus_type  *bus;

...

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;

};

struct i2c_driver {

...

/* driver model interfaces that don't relate to enumeration  */

void (*shutdown)(struct i2c_client *);

int (*suspend)(struct i2c_client *, pm_message_t mesg);

int (*resume)(struct i2c_client *);

...

struct device_driver driver;

const struct i2c_device_id *id_table;

/* Device detection callback for automatic device creation */

int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);

const struct i2c_client_address_data *address_data;

struct list_head clients;

};

一般都是实现了platform driver和i2c_driver结构体的suspend和resume函数,并没有使用新式的电源管理方式。

posted on 2014-08-25 21:08 Shimejing 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/0822vaj/p/3935863.html

linux驱动程序之电源管理之新版linux系统设备架构中关于电源管理方式的变更相关推荐

  1. 电源管理 第二部分 ---- Windows CE设备驱动开发之电源管理

    Windows CE设备驱动开发之电源管理      第二部分 4.7.2.电源状态 电源管理器期望所有被管理的设备能支持一个或多个设备电源状态.设备电源状态的数量是有限的.设备必须通知电源管理器其功 ...

  2. linux系统安装应用商店失败,在Deepin/UOS系统应用商店中安装KiCad失败的解决方法...

    在Deepin/UOS系统应用商店中搜索并安装KiCad可能会提示安装失败,原因就是缺少必要的依赖包及KiCad的封装库文件.下面分享解决方法及附上Deepin系统安装KiCad5的方法.参考深度商店 ...

  3. 数据化管理洞悉零售及电子商务——零售策略中的数据化管理

    零售策略中的数据化管理 渠道策略的数据化管理 渠道分类 按代理-批发-零售三级渠道标准分类 代理商 按区域划分 国代 省代 区域代理 按渠道功能划分 KA渠道代理 餐饮渠道代理 特殊通路代理 按重要性 ...

  4. 数据化管理洞悉零售及电子商务运营——电子商务中的数据化管理

    电子商务中的数据化管理 数据分析 电子商务和传统零售的区别 传统零售是利用二八法则生存,电商是靠长尾理论积累销售 电商是大数据,传统是小数据 电商:流量数据.会员数据.消费行为数据 传统零售:主要集中 ...

  5. 计算机中如何设置电源计划为节能,如何在Windows 10中创建电源选项快捷图标,快速设置笔记本电脑的节能模式...

    原标题:如何在Windows 10中创建电源选项快捷图标,快速设置笔记本电脑的节能模式 在笔记本电脑上「电源计划」的设定,除了火力全开的高性能运作外,也可以设定成省电模式来节省电量,不过一般都需要到「 ...

  6. Windows10安装深度Linux,可能是最漂亮的国产Linux,windows下安装深度操作系统步骤...

    国产操作系统都是基于Linux进行的二次开发,有很多国产系统只是在Linux基础上进行一些美化.内置几个软件就声称自己是操作系统了.而为什么深度操作系统deepin Linux一直深受用户喜爱呢?虽然 ...

  7. linux 设备驱动阻塞,深入浅出:Linux设备驱动中的阻塞和非阻塞I/O

    今天写的是Linux设备驱动中的阻塞和非阻塞I/0,何谓阻塞与非阻塞I/O?简单来说就是对I/O操作的两种不同的方式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式. 一.基本概念: 阻塞操作 ...

  8. Linux设备驱动中的阻塞与非阻塞I/O

    阻塞和非阻塞I/O是设备访问的两种不同模式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式 本例子讲述了这两者的区别 并实现I/O的等待队列机制, 并进行了用户空间的验证 基本概念: 1> ...

  9. 【CyberSecurityLearning 3】批处理、用户与组管理、服务器远程管理、破解Windows系统密码

    目录 一.批处理编写 1.1.批处理作用 1.2.如何创建批处理 1.3.批处理基本语法 1.3.1.@echo off 1.3.2.pause 1.3.3.title 1.3.4.echo. 1.3 ...

  10. 擎天出口退税软件_新版申报系统退税申报注意事项

    前言:随着税务信息化建设"后金三"时代的不断深入,2019年年底国家税务总局启动出口退税管理系统整合(进入金税三期)项目,经过广东.大连两个地区试点,即将于今年年底前在全国完成金税 ...

最新文章

  1. Java常见数据结构以及特点、使用场景
  2. MATLAB R2018a 输入中文却显示方框问号的问题
  3. 洛谷 P4245 【模板】MTT
  4. mysql怎么定位错误信息_Mysql主从复制异常,定位具体的SQL
  5. 获取Servlet原生的API
  6. python socket编程实现的简单tcp迭代server
  7. Windows五种IO模型性能分析和Linux五种IO模型性能分析
  8. Pandas使用DataFrame进行数据分析比赛进阶之路(一)
  9. detectmultiscale函数参数含义_OpenCV人脸识别--detectMultiScale函数
  10. php想做一个无刷新弹窗,php+ajax实现无刷新的新闻留言系统
  11. 北京黑马程序员javaEE第19期课程
  12. devcon 用法2
  13. IDEA利用wsdl文件生成WebService调用接口的方法
  14. 8086cpu标志寄存器
  15. 详述GPS原理及RTK技术应用
  16. 专访Riverbed CEO:私有化和出售业务瘦身后的Riverbed更专注
  17. ubuntu18.4 浏览器无法上网
  18. simulink中找不到CarSim S-Function图标
  19. Java基于ssm的超市多商家系自提系统
  20. RHEL-7.4-Server系统安装

热门文章

  1. DNS Flood Detector让DNS更安全
  2. 动态提交使用jQuery 完成ajax 文件下载----后端php
  3. 通过反编译深入理解Java String及intern
  4. 树莓派命令行发送邮件
  5. 2015级C++第7周项目 友元、共享数据保护、多文件结构
  6. Spring学习篇01-Spring容器相关基本概念
  7. 网络基础---网络层
  8. (转)程序员如何快速准备面试中的算法
  9. ACCP学习旅程之----- 使用Dreamweaver制作网页
  10. 通过Callable和FutureTask创建线程