Android休眠与唤醒流程(linux-2.6.29)

标准linux休眠过程:

l power management notifiers areexecuted with PM_SUSPEND_PREPARE

l tasks are frozen

l target system sleep state isannounced to the platform-handling code

l devices are suspended

l platform-specific globalsuspend preparation methods are executed

l non-boot CPUs are takenoff-line

l interrupts are disabled on theremaining (main) CPU

l late suspend of devices iscarried out (一般有一些BUSdriver的动作进行)‏

l platform-specific globalmethods are invoked to put the system to sleep

 

标准linux唤醒过程:

l    the main CPU is switched to the appropriatemode, if necessary

l earlyresume of devices is carried out (一般有一些BUS driver的动作进行)‏

l  interrupts are enabled on the mainCPU

l  non-boot CPUs are enabled

l  platform-specific global resumepreparation methods are invoked

l  devices are woken up

l  tasks are thawed

l powermanagement 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 arerequested 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

         error =enter_state(state);

#endif

#endif

 Exit:

  return error ? error : n;

}

 

在request_suspend_state(state)函数中,会调用early_suspend_work的工作队列,从而进入early_suspend()函数中。

staticDECLARE_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);

         queue_work(suspend_work_queue, &late_resume_work);

  }

  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状态。

staticDECLARE_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_tstate)

{

  int error;

 

  if (!valid_state(state))

         return -ENODEV;

 

  if(!mutex_trylock(&pm_mutex))

         return -EBUSY;

 

  printk(KERN_INFO "PM: Syncingfilesystems ... ");

  sys_sync();

  printk("done.\n");

 

  pr_debug("PM: Preparingsystem 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: Finishingwakeup.\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 orTASK_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 inthat respect,

          * and caller must call thaw_processes() ifsomething fails),

          * but it cleans up leftover PF_FREEZErequests.

          */

         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 "

                              "(%dtasks 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("suspenddevices");

  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("resumedevices");

  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 registerfor next wakeup -jc.lee */

  /* If this register do not becleared, 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 toenter 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 alsoact as our return point from when

   * we resume as it saves its own registerstate, so use the return

   * code to differentiate return from save andreturn from sleep */

 

  if(s3c6410_cpu_save(regs_save) == 0) {

         flush_cache_all();

         pm_cpu_sleep();

  }

 

  /* restorethe 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("suspenddevices");

  if(suspend_test(TEST_DEVICES))

         goto Recover_platform;

 

  if (suspend_ops->prepare){

         error = suspend_ops->prepare();

         if (error)

                gotoResume_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("resumedevices");

  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: Preparingsystem 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: Finishingwakeup.\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的设备。

staticDECLARE_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, /* Preventsuspend */

  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, longtimeout, 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("wakeupwake 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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值