标准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_suspend与late_resume。
wakelock在Android的电源管理系统中扮演一个核心的角色。wakelock是一种锁的机制, 只要有人拿着这个锁,系统就无法进入休眠,可以被用户态程序和内核获得。这个锁可以是有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁。如果没有锁了或者超时了,内核就会启动休眠的那套机制来进入休眠。
当系统在启动完毕后,会自己去加一把名为“main“的锁,而当系统有意愿去睡眠时则会先去释放这把“main”锁,在android中,在early_suspend的最后一步会去释放“main”锁(wake_unlock: main)。释放完后则会去检查是否还有其他存在的锁,如果没有则直接进入睡眠过程。
它的缺点是,如果有某一应用获锁而不释放或者因一直在执行某种操作而没时间来释放的话,则会导致系统一直进入不了睡眠状态,功耗过大。
 
early_suspend:先与linux内核的睡眠过程被调用。一般在手机系统的设计中对背光的操作等采用此类方法,因为背光需要的能耗过大。当然此操作与late_resume是配套使用的。一些在内核中要预先进行处理的事件可以先注册上early_suspend函数,当系统要进入睡眠之前会首先调用这些注册的函数。
 
本文中,linux kernel版本为 linux-2.6.29,android版本为 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.c或linux_source/arch/xxx/plat-xxx/pm.c
 
 
Android 休眠过程如下:
当用户读写/sys/power/state时,linux_source/kernel/power/main.c中的state_store()函数会被调用。其中,android的early_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机制,下面从代码的角度进行介绍。
       wakelock有3种类型,常用为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);
}
 
对于wakelock,timeout = 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

android休眠唤醒驱动流程分析【转】的更多相关文章

  1. Android休眠唤醒机制简介(二)

    本文转载自:http://blog.csdn.net/zhaoxiaoqiang10_/article/details/24408911 Android休眠唤醒机制简介(二)************* ...

  2. Android休眠唤醒机制简介(一)【转】

    本文转载自:http://blog.csdn.net/zhaoxiaoqiang10_/article/details/24408129 Android休眠唤醒机制简介(一) ************ ...

  3. Gradle之Android Gradle Plugin 主要流程分析(二)

    [Android 修炼手册]Gradle 篇 -- Android Gradle Plugin 主要流程分析 预备知识 理解 gradle 的基本开发 了解 gradle task 和 plugin ...

  4. android 休眠唤醒机制分析(二) — early_suspend

    本文转自:http://blog.csdn.net/g_salamander/article/details/7982170 early_suspend是Android休眠流程的第一阶段即浅度休眠,不 ...

  5. android 休眠唤醒机制分析(一) — wake_lock

    本文转自:http://blog.csdn.net/g_salamander/article/details/7978772 Android的休眠唤醒主要基于wake_lock机制,只要系统中存在任一 ...

  6. android 休眠唤醒机制分析(一) — wake_lock【转】

    Android的休眠唤醒主要基于wake_lock机制,只要系统中存在任一有效的wake_lock,系统就不能进入深度休眠,但可以进行设备的浅度休眠操作.wake_lock一般在关闭lcd.tp但系统 ...

  7. Android休眠唤醒机制

    有四种方式可以引起休眠 ①在wake_unlock()中, 如果发现解锁以后没有任何其他的wake lock了, 就开始休眠 ②在定时器到时间以后, 定时器的回调函数会查看是否有其他的wake loc ...

  8. Android 4.4KitKat AudioRecord 流程分析

    Android是架构分为三层: 底层      Linux Kernel 中间层  主要由C++实现 (Android 60%源码都是C++实现) 应用层  主要由JAVA开发的应用程序 应用程序执行 ...

  9. Android FART脱壳机流程分析

    本文首发于安全客 链接:https://www.anquanke.com/post/id/219094 0x1 前言 在Android平台上,程序员编写的Java代码最终将被编译成字节码在Androi ...

随机推荐

  1. idea 使用教程

    最智能的IDE IDEA相对于eclipse来说最大的优点就是它比eclipse聪明.聪明到什么程度呢?我们先来看几个简单的例子. 智能提示重构代码 如果你写的代码过于复杂,或者有更好的方式来替代你写 ...

  2. “数学口袋精灵”第二个Sprint计划(第三天)

    “数学口袋精灵”第二个Sprint计划----第三天进度 任务分配: 冯美欣:欢迎界面的背景音乐完善 吴舒婷:游戏界面的动作条,选择答案后的音效 林欢雯:代码算法设计 进度:   冯美欣:欢迎界面背景 ...

  3. Alpha冲刺——day10

    Alpha冲刺--day10 作业链接 Alpha冲刺随笔集 github地址 团队成员 031602636 许舒玲(队长) 031602237 吴杰婷 031602220 雷博浩 031602634 ...

  4. Linux命令(十四) 查看工作目录文件 ls

    目录 1.命令简介 2.常用参数介绍 3.实例 4.直达底部 命令简介 ls 命令是 Linux 下最常用的命令. ls 就是 list 的缩写.默认情况下 ls 命令用来打印出当前目录的清单, 如果 ...

  5. 洛谷P4831 Scarlet loves WenHuaKe

    这道题告诉我们推式子的时候头要够铁. 题意 问一个\(n\times m\)的棋盘,摆上\(n\times 2\)个中国象棋的炮使其两两不能攻击的方案数,对\(998244353\)取模. \((n\ ...

  6. 【刷题】BZOJ 4805 欧拉函数求和

    Description 给出一个数字N,求sigma(phi(i)),1<=i<=N Input 正整数N.N<=2*10^9 Output 输出答案. Sample Input 1 ...

  7. BZOJ 3167: [Heoi2013]Sao

    3167: [Heoi2013]Sao Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 96  Solved: 36[Submit][Status][D ...

  8. luogu1525 [NOIp2011]关押罪犯 (并查集)

    先从大到小排序,看到哪个的时候安排不开了 给每个人拆成两个,如果x和y有矛盾,就给x和y‘.y和x’连边:如果a和b(或a'和b')在同一个集合里,说明他们一定要在同一个监狱里. #include&l ...

  9. POJ 3268 Silver Cow Party (最短路径)

    POJ 3268 Silver Cow Party (最短路径) Description One cow from each of N farms (1 ≤ N ≤ 1000) convenientl ...

  10. spring BasicDataSource 数据源配置 sqlserver数据库 oracle数据库 mysql数据jdbc配置

    spring BasicDataSource 数据源配置 sqlserver数据库 oracle数据库 mysql数据jdbc配置 jdbc.properties 文件信息如下: ---------- ...