从电源模块的初始化函数(pm_init)开始分析:
/kernel/power/main.c
static int __init pm_init(void)
{
int error = pm_start_workqueue();
if (error)
return error;
hibernate_image_size_init();
hibernate_reserved_size_init();
power_kobj = kobject_create_and_add("power", NULL);//创建一个power的kobject
if (!power_kobj)
return -ENOMEM;
return sysfs_create_group(power_kobj, &attr_group);//生成power的sys文件并返回
}
core_initcall(pm_init);
pm_init函数首先通过kobject_create_and_add函数动态创建一个power的kobject结构体,并将其初始化加入到kobject层次中,最终返回所创建的kobject指针。
调用sysfs_create_group在指定目录下生成sysfs文件。
static struct attribute_group attr_group = {
.attrs = g,
};
static struct attribute * g[] = {
&state_attr.attr,
#ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr,
&pm_trace_dev_match_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP
&pm_async_attr.attr,
&wakeup_count_attr.attr,
#ifdef CONFIG_PM_DEBUG
&pm_test_attr.attr,
#endif
#ifdef CONFIG_USER_WAKELOCK
&wake_lock_attr.attr,
&wake_unlock_attr.attr,
#endif
#endif
&phone_actived_attr.attr,
NULL,
};
至此,电源管理模块的初始化函数就已经完成,用户可以通过/sys/power/state来实现控制系统的电源状态。这里的sys文件操作使用了power_attr函数来声明。
#define power_attr(_name) \
static struct kobj_attribute _name##_attr = { \
.attr = { \
.name = __stringify(_name), \
.mode = 0644, \
}, \
.show = _name##_show, \
.store = _name##_store, \
}
所以我们可以看出其文件操作包括显示(show)和写入(store)。下面来看看其具体的操作函数。
/* If set, devices may be suspended and resumed asynchronously. */
int pm_async_enabled = 1;
static ssize_t pm_async_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", pm_async_enabled); //通过sprintf函数直接输出其内容
}
static ssize_t pm_async_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
unsigned long val;
if (strict_strtoul(buf, 10, &val))
return -EINVAL;
if (val > 1)
return -EINVAL;
pm_async_enabled = val;
return n;
}
power_attr(pm_async);
我们暂且看一下strict_strtoul这个函数 ./linux-3.3/include/linux/kernel.h
#define strict_strtoul kstrtoul
static inline int __must_check kstrtoul(const char *s, unsigned int base, unsigned long *res)
{
/*
* We want to shortcut function call, but
* __builtin_types_compatible_p(unsigned long, unsigned long long) = 0.
*/
if (sizeof(unsigned long) == sizeof(unsigned long long) &&
__alignof__(unsigned long) == __alignof__(unsigned long long))
return kstrtoull(s, base, (unsigned long long *)res);
else
return _kstrtoul(s, base, res);
}
/* Internal, do not use. */
int _kstrtoul(const char *s, unsigned int base, unsigned long *res)
{
unsigned long long tmp;
int rv;
rv = kstrtoull(s, base, &tmp);
if (rv < 0)
return rv;
if (tmp != (unsigned long long)(unsigned long)tmp)
return -ERANGE;
*res = tmp;
return 0;
}
EXPORT_SYMBOL(_kstrtoul);
strict_strtoul converts a string to an unsigned long only if the string is really an unsigned long string, any string containing any invalid char at the tail will be rejected and -EINVAL is returned, only a newline char at the tail is acceptible
because people generally
change a module parameter in the following way:
echo 1024 > /sys/module/e1000/parameters/copybreak
echo will append a newline to the tail.
It returns 0 if conversion is successful and *res is set to the converted value, otherwise it returns -EINVAL and *res is set to 0.
现在来看电源管理状态操作的核心部分——power_attr(state)操作
struct kobject *power_kobj;
/**
* state - control system power state.
*
* show() returns what states are supported, which is hard-coded to
* 'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and
* 'disk' (Suspend-to-Disk).
*
* store() accepts one of those strings, translates it into the
* proper enumerated value, and initiates a suspend transition.
*/
static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
#ifdef CONFIG_SUSPEND
int i;
for (i = 0; i < PM_SUSPEND_MAX; i++) {
if (pm_states[i] && valid_state(i))
s += sprintf(s,"%s ", pm_states[i]);
}
#endif
#ifdef CONFIG_HIBERNATION
s += sprintf(s, "%s\n", "disk");
#else
if (s != buf)
/* convert the last space to a newline */
*(s-1) = '\n';
#endif
return (s - buf);
}
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
#ifdef CONFIG_SUSPEND
#ifdef CONFIG_EARLYSUSPEND
suspend_state_t state = PM_SUSPEND_ON;
#else
suspend_state_t state = PM_SUSPEND_STANDBY;
#endif
const char * const *s;
#endif
char *p;
int len;
int error = -EINVAL;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
/* First, check if we are requested to hibernate */
if (len == 4 && !strncmp(buf, "disk", len)) {
error = hibernate();
goto Exit;
}
#ifdef CONFIG_SUSPEND
for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
break;
}
if (state < PM_SUSPEND_MAX && *s) {
#ifdef CONFIG_EARLYSUSPEND
if (state == PM_SUSPEND_ON || valid_state(state)) { //请求中断状态
error = 0;
request_suspend_state(state); //============Android系统从这个地方进入
}
#else
error = enter_state(state);
if (error) {
suspend_stats.fail++;
dpm_save_failed_errno(error);
} else
suspend_stats.success++;
#endif
}
#endif
Exit:
return error ? error : n;
}
power_attr(state);
显示函数很简单,就是根据各种宏定义来读取当前的状态字符串,并将其显示出来。对于写入函数,用户可以写入const char * const pm_state[]中定义的字符串:./linux-3.3/kernel/power/suspend.cconst char *const pm_states[PM_SUSPEND_MAX] = {
#ifdef CONFIG_EARLYSUSPEND
[PM_SUSPEND_ON] = "on",
#endif
[PM_SUSPEND_STANDBY] = "standby",
[PM_SUSPEND_MEM] = "mem",
[PM_SUSPEND_BOOTFAST] = "bootfast",
};
有"on", "standby", "mem", "bootfast"这几种状态.在Android中会调用request_suspend_state函数进入early suspend状态。刚才说到了请求中断状态,来看一下电源的中断和恢复驱动的具体实现,根据各平台的不同略有差异(driver/power/*)。
static struct platform_driver axp_battery_driver = {
.driver = {
.name = "axp22-supplyer",
.owner = THIS_MODULE,
},
.probe = axp_battery_probe,
.remove = axp_battery_remove,
.suspend = axp22_suspend,
.resume = axp22_resume,
.shutdown = axp22_shutdown,
};
static int axp_battery_init(void)
{
int ret =0;
ret = platform_driver_register(&axp_battery_driver);
return ret;
}
static void axp_battery_exit(void)
{
platform_driver_unregister(&axp_battery_driver);
}
subsys_initcall(axp_battery_init);
module_exit(axp_battery_exit);
接着看request_suspend_state的实现void request_suspend_state(suspend_state_t new_state)
{
unsigned long irqflags;
int old_sleep;
spin_lock_irqsave(&state_lock, irqflags);//保存本地中断状态并关闭,获取自旋锁
old_sleep = state & SUSPEND_REQUESTED;
if (debug_mask & DEBUG_USER_STATE) {
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec, &tm);
pr_info("request_suspend_state: %s (%d->%d) at %lld "
"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
requested_suspend_state, new_state,
ktime_to_ns(ktime_get()),
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
}
if (!old_sleep && new_state != PM_SUSPEND_ON) {
state |= SUSPEND_REQUESTED;
#ifdef CONFIG_EARLYSUSPEND_DELAY
/* delay 5 seconds to enter suspend */
wake_unlock(&ealysuspend_delay_work);
wake_lock_timeout(&ealysuspend_delay_work, HZ * 5);
#endif
queue_work(suspend_work_queue, &early_suspend_work);//请求中断,则将early_suspend_work加入到队列排队,
//调用early_suspend_work完成一级休眠工作
} else if (old_sleep && new_state == PM_SUSPEND_ON) { //中断之后的恢复
state &= ~SUSPEND_REQUESTED;
wake_lock(&main_wake_lock);
queue_work(suspend_work_queue, &late_resume_work); //late_resume_work加入到队列
}
requested_suspend_state = new_state;
spin_unlock_irqrestore(&state_lock, irqflags);//完成释放锁、恢复本地中断到之前的状态
}
static DECLARE_WORK(early_suspend_work, early_suspend);
static DECLARE_WORK(late_resume_work, late_resume);
从上面可知early_suspend_work、late_resume_work的定义分别指向了early_suspend、late_resume。因此,最终处理中断和恢复的工作将在early_suspend和late_resume中进行。
void register_early_suspend(struct early_suspend *handler)
{
struct list_head *pos;
mutex_lock(&early_suspend_lock);
list_for_each(pos, &early_suspend_handlers) {
struct early_suspend *e;
e = list_entry(pos, struct early_suspend, link);
if (e->level > handler->level)
break;
}
list_add_tail(&handler->link, pos);
if ((state & SUSPENDED) && handler->suspend)
handler->suspend(handler);
mutex_unlock(&early_suspend_lock);
}
EXPORT_SYMBOL(register_early_suspend);
void unregister_early_suspend(struct early_suspend *handler)
{
mutex_lock(&early_suspend_lock);
list_del(&handler->link);
mutex_unlock(&early_suspend_lock);
}
EXPORT_SYMBOL(unregister_early_suspend);
在注册函数中将支持中断设备的early_suspend的handler添加到early_suspend_handlers链表。然后在注销函数中将early_suspend_handlers链表清除即可。
=====early_suspend=====完成一级休眠工作
static void early_suspend(struct work_struct *work)
{
struct early_suspend *pos;
unsigned long irqflags;
int abort = 0;
ktime_t calltime;
u64 usecs64;
int usecs;
ktime_t starttime;
mutex_lock(&early_suspend_lock);
spin_lock_irqsave(&state_lock, irqflags);
if (state == SUSPEND_REQUESTED)//检查请求状态是否是suspend
state |= SUSPENDED;
else
abort = 1;
spin_unlock_irqrestore(&state_lock, irqflags);
if (abort) {
if (debug_mask & DEBUG_SUSPEND)
pr_info("early_suspend: abort, state %d\n", state);
mutex_unlock(&early_suspend_lock);
goto abort;
}
if (debug_mask & DEBUG_SUSPEND)
pr_info("early_suspend: call handlers\n");
list_for_each_entry(pos, &early_suspend_handlers, link) {//遍历链表依次调用每个驱动的handler
if (pos->suspend != NULL) {
if (debug_mask & DEBUG_VERBOSE){
pr_info("early_suspend: calling %pf\n", pos->suspend);
starttime = ktime_get();
}
pos->suspend(pos);//中断,调用二级休眠入口函数suspend
if (debug_mask & DEBUG_VERBOSE){
calltime = ktime_get();
usecs64 = ktime_to_ns(ktime_sub(calltime, starttime));
do_div(usecs64, NSEC_PER_USEC);
usecs = usecs64;
if (usecs == 0)
usecs = 1;
pr_info("early_suspend: %pf complete after %ld.%03ld msecs\n",
pos->suspend, usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC);
}
}
}
standby_level = STANDBY_WITH_POWER;
mutex_unlock(&early_suspend_lock);
if (debug_mask & DEBUG_SUSPEND)
pr_info("early_suspend: sync\n");
queue_work(sync_work_queue, &sync_system_work);//同步文件系统sync_system
abort:
spin_lock_irqsave(&state_lock, irqflags);
if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
wake_unlock(&main_wake_lock);//释放main_wake_lock,这是个没有超时的锁,若不释放系统无法进入休眠
spin_unlock_irqrestore(&state_lock, irqflags);
}
suspend调用pm_suspend,再调用enter_state(这里才是真正的二级待机入口)
唤醒恢复
唤醒通常有以下几种原因:
1):来电。如果是来电,那么Modem会通过RILD守护进程主动上报给Android系统,并通知WindowManager有来电响应,这样就会远程调用PowerManagerService将"on"写入到//sys/power/state中,从而启用late resume的设。
2):用户按键。用户按键事件会发送到WindowManager中,WindowManager会处理这些按键事件。按键分为几种情况,如果按键不是唤醒键,那么WindowManager会主动放弃WakeLock来使用系统进入再次休眠状态;如果是唤醒键,那么WindowManager就会调用PowerManagerService中的接口来执行Late Resume.
static void late_resume(struct work_struct *work)
{
struct early_suspend *pos;
unsigned long irqflags;
int abort = 0;
ktime_t calltime;
u64 usecs64;
int usecs;
ktime_t starttime;
mutex_lock(&early_suspend_lock);
spin_lock_irqsave(&state_lock, irqflags);
if (state == SUSPENDED)
state &= ~SUSPENDED;
else
abort = 1;
spin_unlock_irqrestore(&state_lock, irqflags);
if (abort) {
if (debug_mask & DEBUG_SUSPEND)
pr_info("late_resume: abort, state %d\n", state);
goto abort;
}
if (debug_mask & DEBUG_SUSPEND)
pr_info("late_resume: call handlers\n");
list_for_each_entry_reverse(pos, &early_suspend_handlers, link) {//遍历链表
if (pos->resume != NULL) {
if (debug_mask & DEBUG_VERBOSE){
pr_info("late_resume: calling %pf\n", pos->resume);
starttime = ktime_get();
}
pos->resume(pos);//恢复
if (debug_mask & DEBUG_VERBOSE){
calltime = ktime_get();
usecs64 = ktime_to_ns(ktime_sub(calltime, starttime));
do_div(usecs64, NSEC_PER_USEC);
usecs = usecs64;
if (usecs == 0)
usecs = 1;
pr_info("late_resume: %pf complete after %ld.%03ld msecs\n",
pos->resume, usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC);
}
}
}
if (debug_mask & DEBUG_SUSPEND)
pr_info("late_resume: done\n");
standby_level = STANDBY_INITIAL;
abort:
mutex_unlock(&early_suspend_lock);
}
下面来分析从非中断状态直接进入enter_state函数所执行的操作,该函数的定义如下:
/**
* enter_state - Do common work of entering low-power state.
* @state: pm_state structure for state we're entering.
*
* Make sure we're the only ones trying to enter a sleep state. Fail
* if someone has beat us to it, since we don't want anything weird to
* happen when we wake up.
* Then, do the setup for suspend, enter the state, and cleaup (after
* we've woken up).
*/
int enter_state(suspend_state_t state)
{
int error;
if (!valid_state(state)) //检测状态是否有效
return -ENODEV;
if (!mutex_trylock(&pm_mutex)) //加锁
return -EBUSY;
printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync(); //同步更新文件系统
printk("done.\n");
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
error = suspend_prepare(); //中断准备
if (error)
goto Unlock;
if (suspend_test(TEST_FREEZER)) //中断检测
goto Finish;
pr_debug("PM: Entering %s sleep\n", pm_states[state]);
pm_restrict_gfp_mask();
error = suspend_devices_and_enter(state); //中断外部设备
pm_restore_gfp_mask();
Finish:
pr_debug("PM: Finishing wakeup.\n");
suspend_finish(); //中断完成
Unlock:
mutex_unlock(&pm_mutex);
return error;
}
用户对/sys/power/state执行读和写操作时会调用main.c中的state_store()函数,用户可以写入const char * const pm_state[]中定义的字符串。state_store会调用enter_state(),它首先会检查一些状态参数,然后同步文件系统。首先使用valid_state()函数检测状态是否有效,然后通过suspend_prepare函数来准备中断,接着使用suspend_test函数来检测中断是否可行,最后会在suspend_devices_and_enter()函数中执行关于外部设备的中断操作。下面来分析准备中断的过程执行了哪些操作:
/**
* suspend_prepare - Do prep work before entering low-power state.
*
* This is common code that is called for each state that we're entering.
* Run suspend notifiers, allocate a console and stop all processes.
*/
static int suspend_prepare(void)
{
int error;
if (!suspend_ops || !suspend_ops->enter)
return -EPERM;
pm_prepare_console();//分配一个虚拟终端来输出信息
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);//运行暂停、中断通知器
if (error)
goto Finish;
error = usermodehelper_disable();//关闭用户态的helper进程
if (error)
goto Finish;
error = suspend_freeze_processes();//进程冻结,并保存所有进程的当前状态
if (!error)
return 0;
suspend_stats.failed_freeze++; //冻结进程失败
dpm_save_failed_step(SUSPEND_FREEZE);
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);//发送中断请求
pm_restore_console();//重置控制台
return error;
}
设备中断
/**
* suspend_devices_and_enter - suspend devices and enter the desired system
* sleep state.
* @state: state to enter
*/
int suspend_devices_and_enter(suspend_state_t state)
{
int error;
bool wakeup = false;
if (!suspend_ops)
return -ENOSYS;
trace_machine_suspend(state);
if (suspend_ops->begin) {//开始
error = suspend_ops->begin(state);
if (error)
goto Close;
}
suspend_console(); //控制台子系统进入休眠
suspend_test_start(); //中断测试开始
error = dpm_suspend_start(PMSG_SUSPEND);//Device suspend
if (error) {
printk(KERN_ERR "PM: Some devices failed to suspend\n");
goto Recover_platform;
}
suspend_test_finish("suspend devices");//中断测试结束
if (suspend_test(TEST_DEVICES))//测试要中断的设备
goto Recover_platform;
do {
error = suspend_enter(state, &wakeup);//中断处理
} while (!error && !wakeup
&& suspend_ops->suspend_again && suspend_ops->suspend_again());
Resume_devices:
suspend_test_start();
dpm_resume_end(PMSG_RESUME);
suspend_test_finish("resume devices");
resume_console();
Close:
if (suspend_ops->end)
suspend_ops->end();
trace_machine_suspend(PWR_EVENT_EXIT);
return error;
Recover_platform:
if (suspend_ops->recover)
suspend_ops->recover();
goto Resume_devices;
}
/**
* dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices.
* @state: PM transition of the system being carried out.
*/
int dpm_suspend(pm_message_t state)//遍历链表调用设备的prepare函数
{
ktime_t starttime = ktime_get();
int error = 0;
might_sleep();
mutex_lock(&dpm_list_mtx);
pm_transition = state;
async_error = 0;
while (!list_empty(&dpm_prepared_list)) {
struct device *dev = to_device(dpm_prepared_list.prev);
get_device(dev);
mutex_unlock(&dpm_list_mtx);
error = device_suspend(dev);
mutex_lock(&dpm_list_mtx);
if (error) {
pm_dev_err(dev, state, "", error);
dpm_save_failed_dev(dev_name(dev));
put_device(dev);
break;
}
if (!list_empty(&dev->power.entry))
list_move(&dev->power.entry, &dpm_suspended_list);
put_device(dev);
if (async_error)
break;
}
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
if (!error)
error = async_error;
if (error) {
suspend_stats.failed_suspend++;
dpm_save_failed_step(SUSPEND_SUSPEND);
} else
dpm_show_time(starttime, state, NULL);
return error;
}
/**
* dpm_prepare - Prepare all non-sysdev devices for a system PM transition.
* @state: PM transition of the system being carried out.
*
* Execute the ->prepare() callback(s) for all devices.
*/
int dpm_prepare(pm_message_t state)//遍历链表调用设备的prepare函数
{
int error = 0;
might_sleep();
mutex_lock(&dpm_list_mtx);
while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.next);
get_device(dev);
mutex_unlock(&dpm_list_mtx);
error = device_prepare(dev, state);
mutex_lock(&dpm_list_mtx);
if (error) {
if (error == -EAGAIN) {
put_device(dev);
error = 0;
continue;
}
printk(KERN_INFO "PM: Device %s not prepared "
"for power transition: code %d\n",
dev_name(dev), error);
put_device(dev);
break;
}
dev->power.is_prepared = true;
if (!list_empty(&dev->power.entry))
list_move_tail(&dev->power.entry, &dpm_prepared_list);
put_device(dev);
}
mutex_unlock(&dpm_list_mtx);
return error;
}
/**
* dpm_suspend_start - Prepare devices for PM transition and suspend them.
* @state: PM transition of the system being carried out.
*
* Prepare all non-sysdev devices for system PM transition and execute "suspend"
* callbacks for them.
*/
int dpm_suspend_start(pm_message_t state)
{
int error;
error = dpm_prepare(state);
if (error) {
suspend_stats.failed_prepare++;
dpm_save_failed_step(SUSPEND_PREPARE);
} else
error = dpm_suspend(state);
return error;
}
EXPORT_SYMBOL_GPL(dpm_suspend_start);