摘要:
android系統的待機,是在linux原生待機enter_state的基礎上,添加wakelock-wakeunlock機制,對象情境是增加對屏滅但系統仍後台運行得支援。
linux原生待機
我們是linux開發人員,用code溝通最直接吧。
在linux-2.6.32以後,/sys/power節點下,建立state節點,在檔案系統調用上,write此節點,將會調用state_store函數,read此節點,將會調用state_show函數。
#define power_attr(_name) \static struct kobj_attribute _name##_attr = {\.attr= {\.name = __stringify(_name),\.mode = 0644,\},\.show= _name##_show,\.store= _name##_store,\}
在/kernel/power/main.c中
power_attr(state);
linux原生待機的入口函數就在這裡,接下來往state_store函數深入,就是待機過程。
android新增待機
android對linux待機機制的修改,是對移動可攜式裝置省電的支援。
在code上,可以追蹤紅定義HAS_EARLYSUSPEND,可以發現這個宏的開關就是wakelock-wakeunlock機制的開關。
待機的入口節點沒變,同linux原生待機一致,在/sys/power/state下,
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n){#ifdef CONFIG_SUSPEND#ifdef CONFIG_EARLYSUSPENDsuspend_state_t state = PM_SUSPEND_ON;#elsesuspend_state_t state = PM_SUSPEND_STANDBY;#endifconst char * const *s;#endifchar *p;int len;int error = -EINVAL;p = memchr(buf, '\n', n);len = p ? p - buf : n;/* First, check if we are requested to hibernate */if (len == 4 && !strncmp(buf, "disk", len)) {error = hibernate(); goto Exit;}#ifdef CONFIG_SUSPENDfor (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_EARLYSUSPENDif (state == PM_SUSPEND_ON || valid_state(state)) {error = 0;request_suspend_state(state);//區別與原生待機機制的關鍵函數}#elseerror = enter_state(state);#endif#endif Exit:return error ? error : n;}
request_suspend_state(state);函數最後也會調用到linux原生待機入口函數enter_state(state),不過在調用之前需要先完成earlysuspend,所謂的一級待機。
一級待機提供一種機制,使得某些應用可以在螢幕暗下來以後,後台繼續運行,如USB transfer、music play、wifi downloade/upload、blooth transfer。
EARLYSUSPEND
那麼,如果正確地給你的某一個模組或者應用,註冊一級待機回呼函數呢?我們以backlight為例
需要包含的標頭檔:#include <linux/earlysuspend.h>
定義一個一級待機結構體 XX_early_suspend
static struct early_suspend bl_early_suspend = {.suspend = bl_earlysuspend,.resume = bl_lateresume,.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN - 1,};
這個結構體的suspend與resume回調,就是在request_suspend_state(state)中調用,在enter_state(state)之前調用的。
level成員稱為函數優先順序,earlysuspend實際上就是一個list,在系統啟動過程中往這個list上註冊了很多的回呼函數,但是這些函數的調用需要有一個先後關係;level值越大,優先順序越低,函數越靠後被調用。
那麼,XX_earlysuspend和XX_lateresume回調,要注意函數參數,還是以backlight為例
static void bl_earlysuspend(struct early_suspend *h){}static void bl_lateresume(struct early_suspend *h){}
這樣,回呼函數和結構體就寫好了,為了規範代碼,最好能加上ealrysuspend機制的宏開關, 接下來就是註冊結構體到list
在驅動的_probe函數中,一般在靠後位置添加
#ifdef CONFIG_HAS_EARLYSUSPENDregister_early_suspend(&bl_early_suspend);#endif
對應的要在_remove函數中,在靠前位置
#ifdef CONFIG_HAS_EARLYSUSPENDunregister_early_suspend(&bl_early_suspend);#endif
wake lock/ wake unlock
接下來,如果給一個需要鎖的驅動,添加一把鎖呢?
和android上層看到的鎖類似,鎖有逾時鎖與非逾時鎖兩種。逾時鎖我們以USB為例
需要包含的標頭檔:#include <linux/wakelock.h>
在初始化函數或者_probe函數中,
wake_lock_init(&usb_wakelock, WAKE_LOCK_SUSPEND, "usb_detect");
這樣就註冊上一個name為"usb_detect"的鎖,此鎖屬性是WAKE_LOCK_SUSPEND,區別與WAKE_LOCK_IDLE。
在鎖的初始化上,是不區分逾時鎖與非逾時鎖的,而是在申請與釋放鎖。
我們下面申請一個逾時鎖,目的是在USB拔掉之後幾秒時間內,系統不要進入深度睡眠。
static irqreturn_t usb_detect_irq_handler(int irq, void *dev_id){wake_lock_timeout(&usb_wakelock, 3 * HZ);//3秒的逾時時間schedule_delayed_work(&wakeup_work, HZ / 10);return IRQ_HANDLED;}
在拔掉和插入USB後,會出發這個handler,從而申請 "usb_detect"的逾時鎖,逾時時間是3秒,3秒後,自動釋放這個鎖。
可以在串口輸入cat proc/wakelocks查看當前系統所有鎖情況。
非逾時鎖,需要手動申請,手動釋放,而且需要注意匹配使用申請與釋放。我們以alarm為例
同樣,先在_probe中初始化一個鎖
wake_lock_init(&hym8563->wake_lock, WAKE_LOCK_SUSPEND, "rtc_hym8563");
在_remove中反初始化
wake_lock_destroy(&hym8563->wake_lock);
然後在set_alarm的時候,申請鎖
wake_lock(&hym8563->wake_lock)
在alarm結束後釋放這個鎖
wake_unlock(&hym8563->wake_lock)