新版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函数,并没有使用新式的电源管理方式。

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

  1. 项目日志的管理和应用 log4js-Node.js中的日志管理模块使用与封装

    开发过程中,日志记录是必不可少的事情,尤其是生产系统中经常无法调试,因此日志就成了重要的调试信息来源. Node.js,已经有现成的开源日志模块,就是log4js,源码地址:点击打开链接 项目引用方法 ...

  2. 第六章 第一个Linux驱动程序:统计单词个数

    现在进入了实战阶段,使用统计单词个数的实例让我们了解开发和测试Linux驱动程序的完整过程.第一个Linux驱动程序是统计单词个数. 这个Linux驱动程序没有访问硬件,而是利用设备文件作为介质与应用 ...

  3. 蜕变成蝶~Linux设备驱动中的阻塞和非阻塞I/O

    今天意外收到一个消息,真是惊呆我了,博客轩给我发了信息,说是俺的博客文章有特色可以出本书,,这简直让我受宠若惊,俺只是个大三的技术宅,写的博客也是自己所学的一些见解和在网上看到我一些博文以及帖子里综合 ...

  4. 微服务架构下分布式Session管理

    转载本文需注明出处:EAII企业架构创新研究院(微信号:eaworld),违者必究.如需加入微信群参与微课堂.架构设计与讨论直播请直接回复此公众号:“加群 姓名 公司 职位 微信号”. 一.应用架构变 ...

  5. Java中的会话管理——HttpServlet,Cookies,URL Rewriting(译)

    参考谷歌翻译,关键字直接使用英文,原文地址:http://www.journaldev.com/1907/java-session-management-servlet-httpsession-url ...

  6. Java 中的会话管理—— HttpServlet,Cookies,URL Rewriting(转)

    索引 1.什么是 Session? 2.Java 中的会话管理—— Cookie 3.Java Servlet 中的 Session —— HttpSession 理解 JSESSIONID Cook ...

  7. linux驱动程序之电源管理之linux的电源管理架构(3)

    设备电源管理 Copyright (c) 2010 Rafael J. Wysocki<rjw@sisk.pl>, Novell Inc. Copyright (c) 2010 Alan ...

  8. linux驱动程序之电源管理 之linux休眠与唤醒(2)

    在Linux中,休眠主要分三个主要的步骤:(1)冻结用户态进程和内核态任务:(2)调用注册的设备的suspend的回调函数:(3)按照注册顺序休眠核心设备和使CPU进入休眠态.       冻结进程是 ...

  9. linux驱动程序之电源管理之regulator机制流程 (1)

    电源管理芯片可以为多设备供电,且这些设备电压电流有所同.为这些设备提供的稳压器代码模型即为regulator. 下面通过下面三个过程分析regulartor供电机制: 1.分析regulator结构体 ...

随机推荐

  1. jquery div拖动效果示例代码

    div拖动效果想必大家都有见到过吧,实现的方法也是有很多的,下面为大家将介绍使用jquery是如何实现的,感兴趣的朋友不要错过 复制代码代码如下: <%@ page language=" ...

  2. 【BZOJ】1013: [JSOI2008]球形空间产生器sphere

    [BZOJ]1013: [JSOI2008]球形空间产生器sphere 题意:给n+1个n维的点的坐标,要你求出一个到这n+1个点距离相等的点的坐标: 思路:高斯消元即第i个点和第i+1个点处理出一个 ...

  3. .NET 轻量级 ORM 框架 - Dapper 介绍

    Dapper简单介绍: Dapper is a single file you can drop in to your project that will extend your IDbConnect ...

  4. CSS随手记

    html5模板 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <t ...

  5. 静态页面参数传递&回调函数写法&快速排序的实现方法

    相信很多人都有一种陋习,就是收藏的文章,几乎从来都没有回过头来仔细看过.这次借着这次活动的机会,在<无懈可击的web设计>一书的学习过程中,穿插着讲自己曾经收藏过的,现在觉得还有价值的文章 ...

  6. CentOS挂载新硬盘

    1.查看当前硬盘使用状况: df -h root@VM_160_34_centos:~> df -h Filesystem Size Used Avail Use% Mounted on /de ...

  7. POJ 1404 I-Keyboard (DP)

    http://poj.org/problem?id=1404 题意 :手机上的要发短信的话,“我”字需要先按一下9键,再按3下6键,所以,现在想要重新布局每个键上的字母数,让最后的那个值最小,也就是说 ...

  8. ANDROID_MARS学习笔记_S01原始版_019_SERVICE之Transact

    一.代码1.xml(1)activity_main.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/ ...

  9. 152. Maximum Product Subarray

    题目: Find the contiguous subarray within an array (containing at least one number) which has the larg ...

  10. C语言中的宏总结

    宏定义分为两种: 1.变量式宏定义,如 #define abc def #define str "string" #define num 100 2.函数式宏定义, #define ...