電源管理之睡眠和喚醒流程


這里沒有大堆的文字說明,只是羅列了一下睡眠和喚醒的主要流程,具體細節還需要自己去分析。

關鍵的一點:

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM