新版linux系统设备架构中关于电源管理方式的变更
based on linux-2.6.32
一、设备模型各数据结构中电源管理的部分
linux的设备模型通过诸多结构体来联合描述,如structdevice,struct device_type,struct class,
struct device_driver,struct bus_type等。
@kernel/include/linux/devices.h中有这几中结构体的定义,这里只列出和PM有关的项,其余查看源码:
struct device{
...
structdev_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 structdev_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 structdev_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 structdev_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);
conststruct dev_pm_ops *pm;
...
};
以上可以看出和电源管理相关的两个结构体是structdev_pm_info和struct dev_pm_ops,他们定义于文件
@kernel/include/linux/pm.h
struct dev_pm_info {
pm_message_t power_state;
unsignedint can_wakeup:1;
unsignedint should_wakeup:1;
enumdpm_state status;
#ifdef CONFIG_PM_SLEEP
structlist_head entry;
#endif
#ifdefCONFIG_PM_RUNTIME //undef
structtimer_list suspend_timer;
unsignedlong timer_expires;
structwork_struct work;
wait_queue_head_t wait_queue;
spinlock_t lock;
atomic_t usage_count;
atomic_t child_count;
unsignedint disable_depth:3;
unsignedint ignore_children:1;
unsignedint idle_notification:1;
unsignedint request_pending:1;
unsignedint deferred_resume:1;
enumrpm_request request;
enumrpm_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)
gotoDPMError;
device_pm_add(dev); @kernel/drivers/base/power/main.c
...
其中dpm_sysfs_add()函数用来向sysfs文件系统中添加相应设备的power接口文件,如注册mt6516_tpdpaltform 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
lrwxrwxrwxroot root 2010-01-02 06:39 subsystem -> ../../../bus/platform
drwxr-xr-xroot root 2010-01-02 06:35 power
lrwxrwxrwxroot 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);
staticstruct attribute * power_attrs[] = {
&dev_attr_wakeup.attr,
NULL,
};
static struct attribute_grouppm_attr_group = {
.name ="power", //attribute_group结构体的name域不为NULL的话,都会已name建立一个属性目录的
.attrs =power_attrs,
};
int dpm_sysfs_add(struct device* dev)
{
returnsysfs_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(structdevice *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全局变量包含在PMtransition期间不允许注册设备
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(structdevice *dev)
{
...
mutex_lock(&dpm_list_mtx);
list_del_init(&dev->power.entry);
mutex_unlock(&dpm_list_mtx);
pm_runtime_remove(dev);
}
举例说明:
我们熟知的platformbus在系统中也是作为一种设备注册进了系统,在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()
structi2c_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-x", 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
lrwxrwxrwxroot root 2010-01-02 06:38 subsystem -> ../../../../../bus/i2c
drwxr-xr-xroot root 2010-01-02 06:35 power
lrwxrwxrwxroot 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_opsplatform_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_typeplatform_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 intplatform_pm_prepare(struct device *dev)
{
structdevice_driver *drv = dev->driver;
int ret =0;
if (drv&& drv->pm && drv->pm->prepare)
ret= drv->pm->prepare(dev);
returnret;
}
static voidplatform_pm_complete(struct device *dev)
{
structdevice_driver *drv = dev->driver;
if (drv&& drv->pm && drv->pm->complete)
drv->pm->complete(dev);
}
可以看出这两个函数都最终是利用了device_driver结构体中的dev_pm_ops函数方法结构体中的对应函数指针。
static intplatform_legacy_suspend(struct device *dev, pm_message_tmesg)
{
structplatform_driver *pdrv = to_platform_driver(dev->driver);
structplatform_device *pdev = to_platform_device(dev);
int ret =0;
if(dev->driver && pdrv->suspend)
ret= pdrv->suspend(pdev, mesg);
returnret;
}
static intplatform_legacy_resume(struct device *dev)
{
structplatform_driver *pdrv = to_platform_driver(dev->driver);
structplatform_device *pdev = to_platform_device(dev);
int ret =0;
if(dev->driver && pdrv->resume)
ret= pdrv->resume(pdev);
returnret;
}
static intplatform_pm_suspend(struct device *dev)
{
structdevice_driver *drv = dev->driver;
int ret =0;
if(!drv)
return0;
if(drv->pm) {
if(drv->pm->suspend)
ret= drv->pm->suspend(dev);
} else{
ret= platform_legacy_suspend(dev, PMSG_SUSPEND);
}
returnret;
}
static intplatform_pm_resume(struct device *dev)
{
structdevice_driver *drv = dev->driver;
int ret =0;
if(!drv)
return0;
if(drv->pm) {
if(drv->pm->resume)
ret= drv->pm->resume(dev);
} else{
ret= platform_legacy_resume(dev);
}
returnret;
}
这里suspend和resume函数也是最终都是调用了device_driver结构体的dev_pm_ops方法结构体中的对应函数指针(device_driver.pm项被初始化),否则使用老式的方法:platform_legacy_suspend(dev,PMSG_SUSPEND)和platform_legacy_resume(dev)。根据这两个函数的源码可以看出。一般地,在我们的platform device的platformdriver定义中,都是实现了pdrv.suspend和pdrv.resume函数,而并没有实现pdrv.driver.suspend和pdrv.driver.resume函数,其余三个函数可以在platform_driver_register()函数中看出:
intplatform_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;
returndriver_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 inti2c_device_suspend(struct device *dev, pm_message_t mesg)
{
structi2c_client *client = i2c_verify_client(dev);
structi2c_driver *driver;
if (!client|| !dev->driver)
return0;
driver =to_i2c_driver(dev->driver);
if(!driver->suspend)
return0;
returndriver->suspend(client, mesg);
}
static inti2c_device_resume(struct device *dev)
{
structi2c_client *client = i2c_verify_client(dev);
structi2c_driver *driver;
if (!client|| !dev->driver)
return0;
driver =to_i2c_driver(dev->driver);
if(!driver->resume)
return0;
returndriver->resume(client);
}
//实际上都是调用的i2c_driver结构体的suspend和resume函数。
2.device_type结构体暂时还没有找到有哪一个模块使用了新式了dev_pm_ops电源管理方法,一般都是没有实现这部分。
3.class结构体也没有找到使用dev_pm_ops方法结构体的地方,先暂时放一放。
4. device_driver
structdevice_driver {
constchar *name;
structbus_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);
conststruct attribute_group **groups;
conststruct dev_pm_ops *pm;
structdriver_private *p;
};
structi2c_driver {
...
void(*shutdown)(struct i2c_client *);
int(*suspend)(struct i2c_client *, pm_message_t mesg);
int(*resume)(struct i2c_client *);
...
structdevice_driver driver;
conststruct i2c_device_id *id_table;
int(*detect)(struct i2c_client *, int kind, struct i2c_board_info*);
conststruct i2c_client_address_data *address_data;
structlist_head clients;
};
一般都是实现了platformdriver和i2c_driver结构体的suspend和resume函数,并没有使用新式的电源管理方式。
原文地址:http://blog.csdn.net/lizhiguo0532/article/details/6453567#comments