linux内核演进中设备驱动关于电源管理方式的变更
来源:互联网 发布:数据库集群是什么意思 编辑:程序博客网 时间:2024/05/17 22:58
转自http://blog.csdn.net/lizhiguo0532/article/details/6453567
The suspend/resume will not be called if they are defined in
device_driver directly,
PM core will only use suspend/resume function in dev_pm_ops. Thus we shall
mark the old suspend/resume deprecated and make them scheduled for removal.
新版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函数方法结构体中的对应函数指针。
再看platform_pm_suspend和platform_pm_resume。
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函数(是struct device_driver没有做默认实现,platform_driver也不没有使用platform_drv_suspend/platform_drv_resume之类去回调i2c_driver.suspend,而是由platform_pm_suspend去调用,纳入PM core管理),其余三个函数可以在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); }
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; };
platform driver和i2c_driver结构体都是实现了suspend和resume函数,并没有使用新式的电源管理方式。
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; };
- linux内核演进中设备驱动关于电源管理方式的变更
- linux内核演进中设备驱动关于电源管理方式的变更 .
- 新版linux系统设备架构中关于电源管理方式的变更
- 新版linux系统设备架构中关于电源管理方式的变更
- 新版linux系统设备架构中关于电源管理方式的变更 .
- 新版linux系统设备架构中关于电源管理方式的变更
- 新版linux系统设备架构中关于电源管理方式的变更
- 新版linux系统设备架构中关于电源管理方式的变更
- 新版linux系统设备架构中关于电源管理方式的变更
- 新版linux系统设备架构中关于电源管理方式的变更
- 新版linux系统设备架构中关于电源管理方式的变更
- 新版linux系统设备架构中关于电源管理方式的变更
- 新版linux系统设备架构中关于电源管理方式的变更
- 新版linux系统设备架构中关于电源管理方式的变更
- 新版linux系统设备架构中关于电源管理方式的变更
- 新版linux系统设备架构中关于电源管理方式的变更
- 新版linux系统设备架构中关于电源管理方式的变更
- Linux电源管理(14)_从设备驱动的角度看电源管理
- Java菜鸟学习笔记--面向对象篇(九):Oerride与super限定
- javascript 55个小技巧
- UVa 106 - Fermat vs. Pythagoras
- wifi 直连 wifi p2p
- Ubuntu下将VIM改造成IDE
- linux内核演进中设备驱动关于电源管理方式的变更
- 事务机制
- 搜集的一些关于CreateWaitableTimer的资料_等待定时器
- Oracle Memory Architecture - Oracle 体系结构篇 1
- u-boot源代码下载
- 可以直接拿来用的15个jQuery代码片段
- 如何积累人脉
- android 滑动菜单SlidingMenu的实现
- linux下串口工具minicom使用