从电源模块的初始化函数(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);
#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.c
const 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);