welcom ! Handel home

2014年6月24日 星期二

[Android] 休眠與喚醒驅動流程分析

android休眠與喚醒驅動流程分析
標準linux休眠過程:
l  power management notifiers are executed with PM_SUSPEND_PREPARE
l  tasks are frozen
l  target system sleep state is announced to the platform-handling code
l  devices are suspended
l  platform-specific global suspend preparation methods are executed
l  non-boot CPUs are taken off-line
l  interrupts are disabled on the remaining (main) CPU
l  late suspend of devices is carried out (一般有一些BUS driver的動作進行)
l  platform-specific global methods are invoked to put the system to sleep

標準linux喚醒過程:
l      the main CPU is switched to the appropriate mode, if necessary
l  early resume of devices is carried out (一般有一些BUS driver的動作進行)
l  interrupts are enabled on the main CPU
l  non-boot CPUs are enabled
l  platform-specific global resume preparation methods are invoked
l  devices are woken up
l  tasks are thawed
l  power management notifiers are executed with PM_POST_SUSPEND

使用者可以通過sys檔案系統控制系統進入休眠:



查看系統支援的休眠方式:
#cat /sys/power/state
常見有standby(suspend to RAM)mem(suspend to RAM)disk(suspend to disk),只是standby耗電更多,返回到正常工作狀態的時間更短。
通過 #echo mem > /sys/power/state  讓系統進入休眠。

Android休眠與喚醒
android是在傳統的linux內核電源管理設計的基礎上,結合手機設計的實際需求而進化出的一套電源管理系統,其核心內容有:wakelock early_suspendlate_resume
wakelockAndroid的電源管理系統中扮演一個核心的角色。wakelock是一種鎖的機制, 只要有人拿著這個鎖,系統就無法進入休眠,可以被使用者態程式和內核獲得。這個鎖可以是有超時的或者是沒有超時的,超時的鎖會在時間過去以後自動解鎖。如果沒有鎖了或者超時了,內核就會啟動休眠的那套機制來進入休眠。
當系統在啟動完畢後,會自己去加一把名為“main“的鎖,而當系統有意願去睡眠時則會先去釋放這把“main”鎖,在android中,在early_suspend的最後一步會去釋放“main”鎖(wake_unlock: main)。釋放完後則會去檢查是否還有其他存在的鎖,如果沒有則直接進入睡眠過程。
它的缺點是,如果有某一應用獲鎖而不釋放或者因一直在執行某種操作而沒時間來釋放的話,則會導致系統一直進入不了睡眠狀態,功耗過大。

early_suspend:先與linux內核的睡眠過程被調用。一般在手機系統的設計中對背光的操作等採用此類方法,因為背光需要的能耗過大。當然此操作與late_resume是配套使用的。一些在內核中要預先進行處理的事件可以先註冊上early_suspend函數,當系統要進入睡眠之前會首先調用這些註冊的函數。

本文中,linux kernel版本為 linux-2.6.29android版本為 android 2.1
android休眠喚醒主要相關的檔主要有:
l  linux_source/kernel/power/main.c
l  linux_source/kernel/power/earlysuspend.c
l  linux_source/kernel/power/wakelock.c
l  linux_source/kernel/power/process.c
l  linux_source/driver/base/power/main.c
l  linux_source/arch/xxx/mach-xxx/pm.clinux_source/arch/xxx/plat-xxx/pm.c


Android 休眠過程如下:
當用戶讀寫/sys/power/state時,linux_source/kernel/power/main.c中的state_store()函數會被調用。其中,androidearly_suspend會執行request_suspend_state(state); 而標準的linux休眠則執行error = enter_state(state);
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);
            }
#else
#endif
#endif
 Exit:
    return error ? error : n;
}

request_suspend_state(state)函數中,會調用early_suspend_work的工作隊列,從而進入early_suspend()函數中。
static DECLARE_WORK(early_suspend_work, early_suspend);
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;
            queue_work(suspend_work_queue, &early_suspend_work);
    } else if (old_sleep && new_state == PM_SUSPEND_ON) {
            state &= ~SUSPEND_REQUESTED;
            wake_lock(&main_wake_lock);
    }
    requested_suspend_state = new_state;
    spin_unlock_irqrestore(&state_lock, irqflags);
}

early_suspend()函數中,首先要判斷當前請求的狀態是否還是suspend,若不是,則直接退出了;若是,函數會調用已經註冊的early_suspend的函數。然後同步檔案系統,最後釋放main_wake_lock
static void early_suspend(struct work_struct *work)
{
    struct early_suspend *pos;
    unsigned long irqflags;
    int abort = 0;

    mutex_lock(&early_suspend_lock);
    spin_lock_irqsave(&state_lock, irqflags);
    if (state == SUSPEND_REQUESTED)
            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) {
            if (pos->suspend != NULL)
                    pos->suspend(pos);
    }
    mutex_unlock(&early_suspend_lock);

    if (debug_mask & DEBUG_SUSPEND)
            pr_info("early_suspend: sync\n");

    sys_sync();
abort:
    spin_lock_irqsave(&state_lock, irqflags);
    if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
            wake_unlock(&main_wake_lock);
    spin_unlock_irqrestore(&state_lock, irqflags);
}

wake_unlock(),刪除鏈表中wake_lock節點,判斷當前是否存在wake_lock,若wake_lock的數目為0,則調用工作隊列suspend_work,進入suspend狀態。
static DECLARE_WORK(suspend_work, suspend);
void wake_unlock(struct wake_lock *lock)
{
    int type;
    unsigned long irqflags;
    spin_lock_irqsave(&list_lock, irqflags);
    type = lock->flags & WAKE_LOCK_TYPE_MASK;
#ifdef CONFIG_WAKELOCK_STAT
    wake_unlock_stat_locked(lock, 0);
#endif
    if (debug_mask & DEBUG_WAKE_LOCK)
            pr_info("wake_unlock: %s\n", lock->name);
    lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
    list_del(&lock->link);
    list_add(&lock->link, &inactive_locks);
    if (type == WAKE_LOCK_SUSPEND) {
            long has_lock = has_wake_lock_locked(type);
            if (has_lock > 0) {
                    if (debug_mask & DEBUG_EXPIRE)
                            pr_info("wake_unlock: %s, start expire timer, "
                                    "%ld\n", lock->name, has_lock);
                    mod_timer(&expire_timer, jiffies + has_lock);
            } else {
                    if (del_timer(&expire_timer))
                            if (debug_mask & DEBUG_EXPIRE)
                                    pr_info("wake_unlock: %s, stop expire "
                                            "timer\n", lock->name);
                    if (has_lock == 0)
                            queue_work(suspend_work_queue, &suspend_work);
            }
            if (lock == &main_wake_lock) {
                    if (debug_mask & DEBUG_SUSPEND)
                            print_active_locks(WAKE_LOCK_SUSPEND);
#ifdef CONFIG_WAKELOCK_STAT
                    update_sleep_wait_stats_locked(0);
#endif
            }
    }
    spin_unlock_irqrestore(&list_lock, irqflags);
}

suspend()函數中,先判斷當前是否有wake_lock,若有,則退出;然後同步檔案系統,最後調用pm_suspend()函數。
static void suspend(struct work_struct *work)
{
    int ret;
    int entry_event_num;

    if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
            if (debug_mask & DEBUG_SUSPEND)
                    pr_info("suspend: abort suspend\n");
            return;
    }

    entry_event_num = current_event_num;
    sys_sync();
    if (debug_mask & DEBUG_SUSPEND)
            pr_info("suspend: enter suspend\n");
    ret = pm_suspend(requested_suspend_state);
    if (debug_mask & DEBUG_EXIT_SUSPEND) {
            struct timespec ts;
            struct rtc_time tm;
            getnstimeofday(&ts);
            rtc_time_to_tm(ts.tv_sec, &tm);
            pr_info("suspend: exit suspend, ret = %d "
                    "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
                    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
                    tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
    }
    if (current_event_num == entry_event_num) {
            if (debug_mask & DEBUG_SUSPEND)
                    pr_info("suspend: pm_suspend returned with no event\n");
            wake_lock_timeout(&unknown_wakeup, HZ / 2);
    }
}


pm_suspend()函數中,enter_state()函數被調用,從而進入標準linux休眠過程。
int pm_suspend(suspend_state_t state)
{
    if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
            return enter_state(state);
    return -EINVAL;
}

enter_state()函數中,首先檢查一些狀態參數,再同步檔案系統,然後調用suspend_prepare()來凍結進程,最後調用suspend_devices_and_enter()讓外設進入休眠。
static 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]);
    error = suspend_devices_and_enter(state);

 Finish:
    pr_debug("PM: Finishing wakeup.\n");
    suspend_finish();
 Unlock:
    mutex_unlock(&pm_mutex);
    return error;
}

suspend_prepare()函數中,先通過pm_prepare_console();suspend分配一個虛擬終端來輸出資訊,再廣播一個系統進入suspend的通報,關閉使用者態的helper進程,然後調用suspend_freeze_processes()來凍結進程,最後會嘗試釋放一些記憶體。
static int suspend_prepare(void)
{
    int error;
    unsigned int free_pages;

    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();
    if (error)
            goto Finish;

    if (suspend_freeze_processes()) {
            error = -EAGAIN;
            goto Thaw;
    }

    free_pages = global_page_state(NR_FREE_PAGES);
    if (free_pages < FREE_PAGE_NUMBER) {
            pr_debug("PM: free some memory\n");
            shrink_all_memory(FREE_PAGE_NUMBER - free_pages);
            if (nr_free_pages() < FREE_PAGE_NUMBER) {
                    error = -ENOMEM;
                    printk(KERN_ERR "PM: No enough memory\n");
            }
    }
    if (!error)
            return 0;

 Thaw:
    suspend_thaw_processes();
    usermodehelper_enable();
 Finish:
    pm_notifier_call_chain(PM_POST_SUSPEND);
    pm_restore_console();
    return error;
}

suspend_freeze_processes()函數中調用了freeze_processes()函數,而freeze_processes()函數中又調用了try_to_freeze_tasks()來完成凍結任務。在凍結過程中,會判斷當前進程是否有wake_lock,若有,則凍結失敗,函數會放棄凍結。
static int try_to_freeze_tasks(bool sig_only)
{
    struct task_struct *g, *p;
    unsigned long end_time;
    unsigned int todo;
    struct timeval start, end;
    u64 elapsed_csecs64;
    unsigned int elapsed_csecs;
    unsigned int wakeup = 0;

    do_gettimeofday(&start);

    end_time = jiffies + TIMEOUT;
    do {
            todo = 0;
            read_lock(&tasklist_lock);
            do_each_thread(g, p) {
                    if (frozen(p) || !freezeable(p))
                            continue;

                    if (!freeze_task(p, sig_only))
                            continue;

                    /*
                     * Now that we've done set_freeze_flag, don't
                     * perturb a task in TASK_STOPPED or TASK_TRACED.
                     * It is "frozen enough".  If the task does wake
                     * up, it will immediately call try_to_freeze.
                     */
                    if (!task_is_stopped_or_traced(p) &&
                        !freezer_should_skip(p))
                            todo++;
            } while_each_thread(g, p);
            read_unlock(&tasklist_lock);
            yield();                      /* Yield is okay here */
            if (todo && has_wake_lock(WAKE_LOCK_SUSPEND)) {
                    wakeup = 1;
                    break;
            }
            if (time_after(jiffies, end_time))
                    break;
    } while (todo);

    do_gettimeofday(&end);
    elapsed_csecs64 = timeval_to_ns(&end) - timeval_to_ns(&start);
    do_div(elapsed_csecs64, NSEC_PER_SEC / 100);
    elapsed_csecs = elapsed_csecs64;

    if (todo) {
            /* This does not unfreeze processes that are already frozen
             * (we have slightly ugly calling convention in that respect,
             * and caller must call thaw_processes() if something fails),
             * but it cleans up leftover PF_FREEZE requests.
             */
            if(wakeup) {
                    printk("\n");
                    printk(KERN_ERR "Freezing of %s aborted\n",
                                    sig_only ? "user space " : "tasks ");
            }
            else {
                    printk("\n");
                    printk(KERN_ERR "Freezing of tasks failed after %d.%02d seconds "
                                    "(%d tasks refusing to freeze):\n",
                                    elapsed_csecs / 100, elapsed_csecs % 100, todo);
                    show_state();
            }
            read_lock(&tasklist_lock);
            do_each_thread(g, p) {
                    task_lock(p);
                    if (freezing(p) && !freezer_should_skip(p))
                            printk(KERN_ERR " %s\n", p->comm);
                    cancel_freezing(p);
                    task_unlock(p);
            } while_each_thread(g, p);
            read_unlock(&tasklist_lock);
    } else {
            printk("(elapsed %d.%02d seconds) ", elapsed_csecs / 100,
                    elapsed_csecs % 100);
    }
    return todo ? -EBUSY : 0;
}

到現在,所有的進程(也包括workqueue/kthread) 都已經停止了,內核態進程有可能在停止的時候握有一些信號量,所以如果這時候在外設裡面去解鎖這個信號量有可能會發生鎖死, 所以在外設suspend()函數裡面作lock/unlock鎖要非常小心,建議不要在外設的suspend()裡面等待鎖。而且suspend的過程中,有一些log是無法輸出的,所以一旦出現問題,非常難調試。

回到enter_state()函數中,再凍結進程完成後,調用suspend_devices_and_enter()函數讓外設進入休眠。該函數中,首先休眠串口(之後不能再顯示log,解決方法為在kernel配置選項的cmd_line中,添加”no_console_suspend”選項),再通過device_suspend()函式呼叫各驅動的suspend函數。
當外設進入休眠後,suspend_ops->prepare()被調用,suspend_ops是板級的PM操作(本文中粉紅色的函數,依賴於具體的平臺),以s3c6410為例,其註冊在linux_source/arch/arm/plat-s3c64xx/pm.c中,只定義了suspend_ops->enter()函數。
static struct platform_suspend_ops s3c6410_pm_ops = {
    .enter                = s3c6410_pm_enter,
    .valid         = suspend_valid_only_mem,
};
接下來,多CPU中的非啟動CPU被關閉。
int suspend_devices_and_enter(suspend_state_t state)
{
    int error;

    if (!suspend_ops)
            return -ENOSYS;

    if (suspend_ops->begin) {
            error = suspend_ops->begin(state);
            if (error)
                    goto Close;
    }
    suspend_console();
    suspend_test_start();
    error = device_suspend(PMSG_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;

    if (suspend_ops->prepare) {
            error = suspend_ops->prepare();
            if (error)
                    goto Resume_devices;
    }

    if (suspend_test(TEST_PLATFORM))
            goto Finish;

    error = disable_nonboot_cpus();
    if (!error && !suspend_test(TEST_CPUS))
            suspend_enter(state);

    enable_nonboot_cpus();
 Finish:
    if (suspend_ops->finish)
            suspend_ops->finish();
 Resume_devices:
    suspend_test_start();
    device_resume(PMSG_RESUME);
    suspend_test_finish("resume devices");
    resume_console();
 Close:
    if (suspend_ops->end)
            suspend_ops->end();
    return error;

 Recover_platform:
    if (suspend_ops->recover)
            suspend_ops->recover();
    goto Resume_devices;
}

接下來suspend_enter()被調用,該函數首先關閉IRQ,然後調用device_power_down(), 它會調用suspend_late()函數, 這個函數是系統真正進入休眠最後調用的函數, 通常會在這個函數中作最後的檢查,接下來休眠所有的系統設備和匯流排。最後調用 suspend_pos->enter() 來使CPU進入省電狀態。這時候,整個休眠過程完成,代碼的執行也就停在這裡了。
static int suspend_enter(suspend_state_t state)
{
    int error = 0;

    device_pm_lock();
#ifdef CONFIG_CPU_FREQ
    cpufreq_get_cpufreq_name(0);
    strcpy(governor_name, cpufreq_governor_name);
    if(strnicmp(governor_name, userspace_governor, CPUFREQ_NAME_LEN)) {
            cpufreq_set_policy(0, "performance");
    }
#endif /* CONFIG_CPU_FREQ */
    arch_suspend_disable_irqs();
    BUG_ON(!irqs_disabled());

    if ((error = device_power_down(PMSG_SUSPEND))) {
            printk(KERN_ERR "PM: Some devices failed to power down\n");
            goto Done;
    }

    error = sysdev_suspend(PMSG_SUSPEND);
    if (!error) {
            if (!suspend_test(TEST_CORE))
                    error = suspend_ops->enter(state);
            sysdev_resume();
    }

    device_power_up(PMSG_RESUME);
 Done:
    arch_suspend_enable_irqs();
#ifdef CONFIG_CPU_FREQ
    if(strnicmp(governor_name, userspace_governor, CPUFREQ_NAME_LEN)) {
            cpufreq_set_policy(0, governor_name);
    }
#endif /* CONFIG_CPU_FREQ */
    BUG_ON(irqs_disabled());
    device_pm_unlock();
    return error;
}

suspend_pos->enter()所對應的函數中,代碼最終停止在pm_cpu_sleep();處。
static int s3c6410_pm_enter(suspend_state_t state)
{
 ……
    s3c6410_pm_do_save(gpio_save, ARRAY_SIZE(gpio_save));
    s3c6410_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
    s3c6410_pm_do_save(core_save, ARRAY_SIZE(core_save));
    s3c6410_pm_do_save(sromc_save, ARRAY_SIZE(sromc_save));

    /* Clear WAKEUP_STAT register for next wakeup -jc.lee */
    /* If this register do not be cleared, Wakeup will be failed */
    __raw_writel(__raw_readl(S3C_WAKEUP_STAT), S3C_WAKEUP_STAT);


#ifdef CONFIG_MACH_SMDK6410
    /* ALL sub block "ON" before enterring sleep mode - EVT0 bug*/
    __raw_writel(0xffffff00, S3C_NORMAL_CFG);

    /* Open all clock gate to enter sleep mode - EVT0 bug*/
    __raw_writel(0xffffffff, S3C_HCLK_GATE);
    __raw_writel(0xffffffff, S3C_PCLK_GATE);
    __raw_writel(0xffffffff, S3C_SCLK_GATE);
        ……
    /* s3c6410_cpu_save will also act as our return point from when
     * we resume as it saves its own register state, so use the return
     * code to differentiate return from save and return from sleep */

    if (s3c6410_cpu_save(regs_save) == 0) {
            flush_cache_all();
            pm_cpu_sleep();
    }

    /* restore the cpu state */
    cpu_init();

    __raw_writel(s3c_eint_mask_val, S3C_EINT_MASK);

    /* restore the system state */
    s3c6410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));
    s3c6410_pm_do_restore(sromc_save, ARRAY_SIZE(sromc_save));
    ……
    }

Android 喚醒過程如下:
        如果在休眠中系統被中斷或者其他事件喚醒,接下來的代碼就從suspend完成的地方開始執行,以s3c6410為例,即pm.c中的s3c6410_pm_enter()中的cpu_init(),然後執行suspend_enter()sysdev_resume()函數,喚醒系統設備和匯流排,使能系統中斷。
static int suspend_enter(suspend_state_t state)
{
    int error = 0;

    device_pm_lock();
#ifdef CONFIG_CPU_FREQ
    cpufreq_get_cpufreq_name(0);
    strcpy(governor_name, cpufreq_governor_name);
    if(strnicmp(governor_name, userspace_governor, CPUFREQ_NAME_LEN)) {
            cpufreq_set_policy(0, "performance");
    }
#endif /* CONFIG_CPU_FREQ */
    arch_suspend_disable_irqs();
    BUG_ON(!irqs_disabled());

    if ((error = device_power_down(PMSG_SUSPEND))) {
            printk(KERN_ERR "PM: Some devices failed to power down\n");
            goto Done;
    }

    error = sysdev_suspend(PMSG_SUSPEND);
    if (!error) {
            if (!suspend_test(TEST_CORE))
                    error = suspend_ops->enter(state);  //suspend過程完成處
            sysdev_resume();
    }

    device_power_up(PMSG_RESUME);
 Done:
    arch_suspend_enable_irqs();
#ifdef CONFIG_CPU_FREQ
    if(strnicmp(governor_name, userspace_governor, CPUFREQ_NAME_LEN)) {
            cpufreq_set_policy(0, governor_name);
    }
#endif /* CONFIG_CPU_FREQ */
    BUG_ON(irqs_disabled());
    device_pm_unlock();
    return error;
}

然後回到suspend_devices_and_enter()函數中,使能休眠時候停止掉的非啟動CPU,繼續喚醒每個設備,使能終端。
int suspend_devices_and_enter(suspend_state_t state)
{
    int error;

    if (!suspend_ops)
            return -ENOSYS;

    if (suspend_ops->begin) {
            error = suspend_ops->begin(state);
            if (error)
                    goto Close;
    }
    suspend_console();
    suspend_test_start();
    error = device_suspend(PMSG_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;

    if (suspend_ops->prepare) {
            error = suspend_ops->prepare();
            if (error)
                    goto Resume_devices;
    }

    if (suspend_test(TEST_PLATFORM))
            goto Finish;

    error = disable_nonboot_cpus();
    if (!error && !suspend_test(TEST_CPUS))
            suspend_enter(state);  //suspend過程完成處

    enable_nonboot_cpus();
 Finish:
    if (suspend_ops->finish)
            suspend_ops->finish();
 Resume_devices:
    suspend_test_start();
    device_resume(PMSG_RESUME);
    suspend_test_finish("resume devices");
    resume_console();
 Close:
    if (suspend_ops->end)
            suspend_ops->end();
    return error;

 Recover_platform:
    if (suspend_ops->recover)
            suspend_ops->recover();
    goto Resume_devices;
}

suspend_devices_and_enter()執行完成後,系統外設已經喚醒,但進程依然是凍結的狀態,返回到enter_state函數中,調用suspend_finish()函數。
static 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]);
    error = suspend_devices_and_enter(state);  //suspend過程完成處

 Finish:
    pr_debug("PM: Finishing wakeup.\n");
    suspend_finish();
 Unlock:
    mutex_unlock(&pm_mutex);
    return error;
}

suspend_finish()函數中,解凍進程和任務,使能使用者空間helper進程,廣播一個系統從suspend狀態退出的notify,喚醒終端。
static void suspend_finish(void)
{
    suspend_thaw_processes();
    usermodehelper_enable();
    pm_notifier_call_chain(PM_POST_SUSPEND);
    pm_restore_console();
}

當所有的喚醒已經結束以後,使用者進程都已經開始運行了,但沒點亮螢幕,喚醒通常會是以下的幾種原因:
如果是來電,那麼Modem會通過發送命令給rild來讓rild通知WindowManager有來電回應,這樣就會遠端調用PowerManagerService來寫”on” /sys/power/state 來調用late resume(),執行點亮螢幕等操作。
使用者按鍵事件會送到WindowManager中,WindowManager會處理這些按鍵事件,按鍵分為幾種情況,如果按鍵不是喚醒鍵,那麼WindowManager會主動放棄wakeLock來使系統進入再次休眠;如果按鍵是喚醒鍵,那麼WindowManger就會調用PowerManagerService中的介面來執行late Resume

”on”被寫入到/sys/power/state之後,同early_suspend過程,request_suspend_state()被調用,只是執行的工作隊列變為late_resume_work。在late_resume函數中,喚醒調用了early_suspend的設備。
static DECLARE_WORK(late_resume_work, late_resume);
static void late_resume(struct work_struct *work)
{
    struct early_suspend *pos;
    unsigned long irqflags;
    int abort = 0;

    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)
                    pos->resume(pos);
    if (debug_mask & DEBUG_SUSPEND)
            pr_info("late_resume: done\n");
abort:
    mutex_unlock(&early_suspend_lock);
}



關於wake_lock
        在上文中,已經介紹了wakelock機制,下面從代碼的角度進行介紹。
        wakelock3種類型,常用為WAKE_LOCK_SUSPEND,作用是防止系統進入睡眠。其他類型不是很清楚。
enum {
    WAKE_LOCK_SUSPEND, /* Prevent suspend */
    WAKE_LOCK_IDLE,    /* Prevent low power idle */
    WAKE_LOCK_TYPE_COUNT
};

        Wakelock有加鎖和解鎖2種操作,加鎖有2種方式,第一種是永久加鎖(wake_lock),這種鎖必須手動的解鎖;另一種是超時鎖(wake_lock_timeout),這種鎖在過去指定時間後,會自動解鎖。
void wake_lock(struct wake_lock *lock)
{
    wake_lock_internal(lock, 0, 0);
}

void wake_lock_timeout(struct wake_lock *lock, long timeout)
{
    wake_lock_internal(lock, timeout, 1);
}

對於wakelocktimeout = has_timeout = 0;直接加鎖後,然後退出;
static void wake_lock_internal(
    struct wake_lock *lock, long timeout, int has_timeout)
{
    int type;
    unsigned long irqflags;
    long expire_in;

    spin_lock_irqsave(&list_lock, irqflags);
    type = lock->flags & WAKE_LOCK_TYPE_MASK;
    BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
    BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));
#ifdef CONFIG_WAKELOCK_STAT
    if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {
            if (debug_mask & DEBUG_WAKEUP)
                    pr_info("wakeup wake lock: %s\n", lock->name);
            wait_for_wakeup = 0;
            lock->stat.wakeup_count++;
    }
    if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&
        (long)(lock->expires - jiffies) <= 0) {
            wake_unlock_stat_locked(lock, 0);
            lock->stat.last_time = ktime_get();
    }
#endif
    if (!(lock->flags & WAKE_LOCK_ACTIVE)) {
            lock->flags |= WAKE_LOCK_ACTIVE;
#ifdef CONFIG_WAKELOCK_STAT
            lock->stat.last_time = ktime_get();
#endif
    }
    list_del(&lock->link);
    if (has_timeout) {
            if (debug_mask & DEBUG_WAKE_LOCK)
                    pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",
                            lock->name, type, timeout / HZ,
                            (timeout % HZ) * MSEC_PER_SEC / HZ);
            lock->expires = jiffies + timeout;
            lock->flags |= WAKE_LOCK_AUTO_EXPIRE;
            list_add_tail(&lock->link, &active_wake_locks[type]);
    } else {
            if (debug_mask & DEBUG_WAKE_LOCK)
                    pr_info("wake_lock: %s, type %d\n", lock->name, type);
            lock->expires = LONG_MAX;
            lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;
            list_add(&lock->link, &active_wake_locks[type]);
    }
    if (type == WAKE_LOCK_SUSPEND) {
            current_event_num++;
#ifdef CONFIG_WAKELOCK_STAT
            if (lock == &main_wake_lock)
                    update_sleep_wait_stats_locked(1);
            else if (!wake_lock_active(&main_wake_lock))
                    update_sleep_wait_stats_locked(0);
#endif
            if (has_timeout)
                    expire_in = has_wake_lock_locked(type);
            else
                    expire_in = -1;
            if (expire_in > 0) {
                    if (debug_mask & DEBUG_EXPIRE)
                            pr_info("wake_lock: %s, start expire timer, "
                                    "%ld\n", lock->name, expire_in);
                    mod_timer(&expire_timer, jiffies + expire_in);
            } else {
                    if (del_timer(&expire_timer))
                            if (debug_mask & DEBUG_EXPIRE)
                                    pr_info("wake_lock: %s, stop expire timer\n",
                                            lock->name);
                    if (expire_in == 0)
                            queue_work(suspend_work_queue, &suspend_work);
            }
    }
    spin_unlock_irqrestore(&list_lock, irqflags);
}
        而對於wake_lock_timeout,在經過timeout時間後,才加鎖。再判斷當前持有wakelock時,啟動另一個計時器,在expire_timer的回呼函數中再次判斷是否持有wakelock
static void expire_wake_locks(unsigned long data)
{
    long has_lock;
    unsigned long irqflags;
    if (debug_mask & DEBUG_EXPIRE)
            pr_info("expire_wake_locks: start\n");
    spin_lock_irqsave(&list_lock, irqflags);
    if (debug_mask & DEBUG_SUSPEND)
            print_active_locks(WAKE_LOCK_SUSPEND);
    has_lock = has_wake_lock_locked(WAKE_LOCK_SUSPEND);
    if (debug_mask & DEBUG_EXPIRE)
            pr_info("expire_wake_locks: done, has_lock %ld\n", has_lock);
    if (has_lock == 0)
            queue_work(suspend_work_queue, &suspend_work);
    spin_unlock_irqrestore(&list_lock, irqflags);
}

static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);

wakelock中,有2個地方可以讓系統從early_suspend進入suspend狀態。分別是:
l  wake_unlock中,解鎖之後,若沒有其他的wakelock,則進入suspend
l  在超時鎖的計時器超時後,計時器的回呼函數,會判斷有沒有其他的wakelock,若沒有,則進入suspend



沒有留言: