电源管理之睡眠和唤醒流程

这里没有大堆的文字说明,只是罗列了一下睡眠和唤醒的主要流程,具体细节还需要自己去分析。

关键的一点:

Android上层和底层间“通信”是通过它的虚拟文件系统中的属性,上层对该属性执行写操作,底层对应的调用该属性的store方法。

 一、上层的一些流程

updatePowerStateLocked();
    updateSuspendBlockerLocked();
        setHalAutoSuspendModeLocked();
            nativeSetAutoSuspend();
                autosuspend_enable();
                    autosuspend_init();
                        autosuspend_ops->enable();
                            autosuspend_earlysuspend_enable();
                                #define EARLYSUSPEND_SYS_POWER_STATE "/sys/power/state"
                                static const char *pwr_state_mem = "mem";
                                sPowerStatefd = open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR);
                                write(sPowerStatefd, pwr_state_mem, strlen(pwr_state_mem));

 二、底层函数执行流程

                power_attr(state);            // /kernel/power/main.c                                                                                                                                                                  
#ifdef CONFIG_MTK_LDVT                    //在现有的代码中,走的是下面的函数                                                                                                                                                     
                state_store();{                                                                                                                                                                                               
                    decode_state();                //根据写入的字符串,转换得到状态                                                                                                                                                          
                    pm_suspend(state);{                                                                                                                                                                                          
                        enter_state(state);{                                                                                                                                                                                        
                            suspend_prepare(state);{    //这个不是我想要的重点,忽略它                                                                                                                                                    
                                                                                                                                                                                                                                          
                            }                                                                                                                                                                                                          
                            suspend_devices_and_enter(state);{                                                                                                                                                                         
                                suspend_console();            //挂起终端,非重点                                                                                                                                                                   
                                dpm_suspend_start(PMSG_SUSPEND);{                                                                                                                                                                         
                                    dpm_prepare(state);{                                                                                                                                                                                     
                                        while (!list_empty(&dpm_list)){        //如果 dpm_list 链表不为空                                                                                                                                             
                                            struct device *dev = to_device(dpm_list.next);        //根据链表节点获取对应的设备                                                                                                                           
                                            device_prepare(dev, state);        //执行电源管理操作函数集中的prepare函数,具体执行的顺序见下面列表。                                                                                                       
                                            list_move_tail(&dev->power.entry, &dpm_prepared_list);    //把dev元素,移到dpm_prepared_list链表后面                                                                                                      
                                        }                                                                                                                                                                                                       
                                    }                                                                                                                                                                                                        
                                    dpm_suspend(state);    {                                                                                                                                                                                    
                                        while (!list_empty(&dpm_prepared_list))    {    //遍历dpm_prepared_list链表                                                                                                                                   
                                            struct device *dev = to_device(dpm_prepared_list.prev);                                                                                                                                                
                                            device_suspend(dev);{    //分为同步和异步执行,此处分析同步执行                                                                                                                                           
                                                __device_suspend(dev, pm_transition, false);    //执行电源管理操作函数集中的suspend函数,具体执行的顺序见下面列表。                                                                                      
                                            }                                                                                                                                                                                                      
                                            list_move(&dev->power.entry, &dpm_suspended_list);                                                                                                                                                     
                                        }                                                                                                                                                                                                       
                                    }                                                                                                                                                                                                        
                                }                                                                                                                                                                                                         
                                suspend_enter(state, &wakeup);{                                                                                                                                                                           
                                    suspend_ops->prepare();                                                                                                                                                                                  
                                    dpm_suspend_end(PMSG_SUSPEND);{                                                                                                                                                                          
                                        dpm_suspend_late(state);{                                                                                                                                                                               
                                            while (!list_empty(&dpm_suspended_list)){    //遍历链表dpm_suspended_list                                                                                                                                 
                                                struct device *dev = to_device(dpm_suspended_list.prev);                                                                                                                                              
                                                device_suspend_late(dev, state); //执行电源管理操作函数集中的suspend_late函数,具体执行顺序见下面说明。                                                                                               
                                                list_move(&dev->power.entry, &dpm_late_early_list);                                                                                                                                                   
                                            }                                                                                                                                                                                                      
                                        }                                                                                                                                                                                                       
                                        dpm_suspend_noirq(state);{                                                                                                                                                                              
                                            cpuidle_pause();                                                                                                                                                                                       
                                            suspend_device_irqs();                                                                                                                                                                                 
                                            while (!list_empty(&dpm_late_early_list)){                                                                                                                                                             
                                                struct device *dev = to_device(dpm_late_early_list.prev);                                                                                                                                             
                                                device_suspend_noirq(dev, state);    //执行电源管理操作函数集中的suspend_noirq函数,具体执行顺序见下面说明。                                                                                             
                                                list_move(&dev->power.entry, &dpm_noirq_list);                                                                                                                                                        
                                            }                                                                                                                                                                                                      
                                        }                                                                                                                                                                                                       
                                    }                                                                                                                                                                                                        
                                    suspend_ops->prepare_late();                                                                                                                                                                             
                                    disable_nonboot_cpus();                                                                                                                                                                                  
                                    arch_suspend_disable_irqs();                                                                                                                                                                             
                                    syscore_suspend();{                                                                                                                                                                                      
                                        list_for_each_entry_reverse(ops, &syscore_ops_list, node){                                                                                                                                              
                                            ops->suspend();                                                                                                                                                                                        
                                        }                                                                                                                                                                                                       
                                    }                                                                                                                                                                                                        
                                                                                                                                                                                                                                             
                                    pm_wakeup_pending();                                                                                                                                                                                     
                                    suspend_ops->enter(state);                                                                                                                                                                               
                                                                                                                                                                                                                                             
                                    //后续环节即为唤醒流程                                                                                                                                                                                   
                                    syscore_resume();                                                                                                                                                                                        
                                    arch_suspend_enable_irqs();                                                                                                                                                                              
                                    enable_nonboot_cpus();                                                                                                                                                                                   
                                    suspend_ops->wake();                                                                                                                                                                                     
                                    dpm_resume_start(PMSG_RESUME);                                                                                                                                                                           
                                    suspend_ops->finish();                                                                                                                                                                                   
                                }                                                                                                                                                                                                         
                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                          
                                //睡眠被打断之后继续执行其后的唤醒流程                                                                                                                                                                    
                                dpm_resume_end(PMSG_RESUME);                                                                                                                                                                              
                                    dpm_resume(state);                                                                                                                                                                                       
                                        while (!list_empty(&dpm_suspended_list))    //遍历链表dpm_suspended_list                                                                                                                                   
                                            device_resume(dev, state, false);                                                                                                                                                                      
                                                pm_op(dev->driver->pm, state);    //可能有多种情形,视具体情况而定                                                                                                                                       
                                                    ops->resume;                                                                                                                                                                                         
                                    dpm_complete(state);                                                                                                                                                                                     
                                        while (!list_empty(&dpm_prepared_list))                                                                                                                                                                 
                                            device_complete(dev, state);                                                                                                                                                                           
                                                dev->driver->pm->complete;    //可能有多种情形,视具体情况而定                                                                                                                                           
                                resume_console();                                                                                                                                                                                         
                            }                                                                                                                                                                                                          
                        }                                                                                                                                                                                                           
                    }                                                                                                                                                                                                            
                }                                                                                                                                                                                                             
#else                                                                                                                                                                                                             
                state_store();{                                                                                                                                                                                               
                    #ifdef CONFIG_EARLYSUSPEND    //现有的代码中,有该定义,所以没有使用enter_state函数                                                                                                                             
                request_suspend_state(new_state);{                //Earlysuspend.c (kernel-3.10\kernel\power)                                                                                                                    
                    if (new_state != PM_SUSPEND_ON){                //该状态分支执行挂起操作,即suspend                                                                                                                             
                        queue_work(sys_sync_work_queue, &early_sys_sync_work);{        //挂起操作之前,先执行同步操作。                                                                                                          
                            static DECLARE_WORK(early_sys_sync_work, early_sys_sync);                                                                                                                                         
                            early_sys_sync(struct work_struct *work){                                                                                                                                                         
                                sys_sync();                                                                                                                                                                                      
                            }                                                                                                                                                                                                 
                        }                                                                                                                                                                                                  
                        queue_work(suspend_work_queue, &early_suspend_work);{                                                                                                                                              
                            static DECLARE_WORK(early_suspend_work, early_suspend);                                                                                                                                           
                            early_suspend(struct work_struct *work){                                                                                                                                                          
                                list_for_each_entry(pos, &early_suspend_handlers, link){                                                                                                                                         
                                    pos->suspend(pos);                                                                                                                                                                              
                                }                                                                                                                                                                                                
                            }                                                                                                                                                                                                 
                        }                                                                                                                                                                                                  
                    }else if(new_state == PM_SUSPEND_ON){        //该状态分支执行恢复操作,即resume                                                                                                                           
                        queue_work(suspend_work_queue, &late_resume_work);{                                                                                                                                                
                            static DECLARE_WORK(late_resume_work, late_resume);                                                                                                                                               
                            late_resume(struct work_struct *work){                                                                                                                                                            
                                list_for_each_entry_reverse(pos, &early_suspend_handlers, link){                                                                                                                                 
                                    pos->resume(pos);                                                                                                                                                                               
                                }                                                                                                                                                                                                
                            }                                                                                                                                                                                                 
                        }                                                                                                                                                                                                  
                    }                                                                                                                                                                                                   
                }                                                                                                                                                                                                    
                    #else                                                                                                                                                                                                        
                        enter_state(state);                                                                                                                                                                                      
                    #endif                                                                                                                                                                                                       
                }                                                                                                                                                                                                             
#endif                                                                                                                                                                                                                  

三、涉及到的几个链表结构中成员的转移关系

从左到右为suspend过程,从右到左为resume过程
dpm_list dpm_prepared_list dpm_suspended_list dpm_late_early_list dpm_noirq_list

四、dpm_list的成员添加流程:

device_create();                                                                          
    device_create_vargs();                                                                   
        device_register(struct device *dev);                                                    
            device_add(dev);                                                                       
                device_pm_add(dev);    //kernel-3.10\drivers\base\Core.c                                 
                    list_add_tail(&dev->power.entry, &dpm_list);    //kernel-3.10\drivers\base\power\Main.c 

只要调用其中的函数就可以了,没有必要说是必须调用那个函数;
只是列出了它的可能调用流程,明白那些操作可以添加dpm_list的成员。

五、suspend过程函数执行流程

prepare执行的先后顺序
if (dev->power.syscore)
        return 0;
callback = dev->pm_domain->ops.prepare;
callback = dev->type->pm->prepare;
callback = dev->class->pm->prepare;
callback = dev->bus->pm->prepare;
callback = dev->driver->pm->prepare;
error = callback(dev);

suspend的执行顺序
if (dev->power.syscore)
        goto Complete;
callback = pm_op(&dev->pm_domain->ops, state);
callback = pm_op(dev->type->pm, state);
callback = pm_op(dev->class->pm, state);
dev->class->suspend;
callback = pm_op(dev->bus->pm, state);
dev->bus->suspend;
callback = pm_op(dev->driver->pm, state);

suspend_late的执行顺序
if (dev->power.syscore)
        return 0;
callback = pm_late_early_op(&dev->pm_domain->ops, state);
callback = pm_late_early_op(dev->type->pm, state);
callback = pm_late_early_op(dev->class->pm, state);
callback = pm_late_early_op(dev->bus->pm, state);
callback = pm_late_early_op(dev->driver->pm, state);

suspend_noirq的执行顺序
if (dev->power.syscore)
        return 0;
callback = pm_noirq_op(&dev->pm_domain->ops, state);
callback = pm_noirq_op(dev->type->pm, state);
callback = pm_noirq_op(dev->class->pm, state);
callback = pm_noirq_op(dev->bus->pm, state);
callback = pm_noirq_op(dev->driver->pm, state);

六、resume过程函数执行流程

暂时未分析,后续补齐

七、部分结构对应的操作方法集

dev->pm_domain->ops         函数操作集合
dev->type->pm                      函数操作集合
dev->class->pm                    函数操作集合
dev->bus->pm                        函数操作集合
dev->driver->pm                    函数操作集合

struct dev_pm_ops {
    int (*prepare)(struct device *dev);
    void (*complete)(struct device *dev);
    int (*suspend)(struct device *dev);
    int (*resume)(struct device *dev);
    int (*freeze)(struct device *dev);
    int (*thaw)(struct device *dev);
    int (*poweroff)(struct device *dev);
    int (*restore)(struct device *dev);
    int (*suspend_late)(struct device *dev);
    int (*resume_early)(struct device *dev);
    int (*freeze_late)(struct device *dev);
    int (*thaw_early)(struct device *dev);
    int (*poweroff_late)(struct device *dev);
    int (*restore_early)(struct device *dev);
    int (*suspend_noirq)(struct device *dev);
    int (*resume_noirq)(struct device *dev);
    int (*freeze_noirq)(struct device *dev);
    int (*thaw_noirq)(struct device *dev);
    int (*poweroff_noirq)(struct device *dev);
    int (*restore_noirq)(struct device *dev);
    int (*runtime_suspend)(struct device *dev);
    int (*runtime_resume)(struct device *dev);
    int (*runtime_idle)(struct device *dev);
};

dev->pm_domain 函数操作集合
    无

dev->type        函数操作集合
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    char *(*devnode)(struct device *dev, umode_t *mode, kuid_t *uid, kgid_t *gid);
    void (*release)(struct device *dev);

dev->class    函数操作集合
    int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
    char *(*devnode)(struct device *dev, umode_t *mode);
    void (*class_release)(struct class *class);
    void (*dev_release)(struct device *dev);
    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);
    
    const void *(*namespace)(struct device *dev);

dev->bus        函数操作集合
    int (*match)(struct device *dev, struct device_driver *drv);
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);
    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);

dev->driver 函数操作集合
    int (*probe) (struct device *dev);
    int (*remove) (struct device *dev);
    void (*shutdown) (struct device *dev);
    int (*suspend) (struct device *dev, pm_message_t state);
    int (*resume) (struct device *dev);

八、MTK平台的register_early_suspend函数分析

struct early_suspend {
#ifdef CONFIG_HAS_EARLYSUSPEND
    struct list_head link;
    int level;
    void (*suspend) (struct early_suspend *h);
    void (*resume) (struct early_suspend *h);
#endif
};

register_early_suspend(struct early_suspend *handler)        //Earlysuspend.c (kernel-3.10\kernel\power)    
    list_for_each(pos, &early_suspend_handlers){
        //根据handler->level的等级,在链表early_suspend_handlers中选择合适的插入位置。
    }
    list_add_tail(&handler->link, pos);
    early_suspend_count++;
    

register_early_suspend函数的主要任务是把early_suspend结构体添加到early_suspend_handlers链表中。
现在存在的一个疑问是在什么时候调用链表中的相关函数。

static DECLARE_WORK(early_suspend_work, early_suspend);
static DECLARE_WORK(late_resume_work, late_resume);

static void early_suspend(struct work_struct *work){
    list_for_each_entry(pos, &early_suspend_handlers, link){
        pos->suspend(pos);
    }
}

static void late_resume(struct work_struct *work){
    list_for_each_entry_reverse(pos, &early_suspend_handlers, link){
        pos->resume(pos);
    }
}

 

posted on 2016-10-09 17:32  黑大米  阅读(3194)  评论(0编辑  收藏  举报

导航