1.休眠方式
在內核中,休眠方式有很多種,可以通過下面命令查看
常用的休眠方式有freeze,standby, mem, disk
- freeze: 凍結I/O設備,將它們置於低功耗狀態,使處理器進入空閑狀態,喚醒最快,耗電比其它standby, mem, disk方式高
- standby:除了凍結I/O設備外,還會暫停系統,喚醒較快,耗電比其它 mem, disk方式高
- mem: 將運行狀態數據存到內存,並關閉外設,進入等待模式,喚醒較慢,耗電比disk方式高
- disk: 將運行狀態數據存到硬盤,然后關機,喚醒最慢
示例:
2.喚醒方式
當我們休眠時,如果想喚醒,則需要添加中斷喚醒源,使得在休眠時,這些中斷是設為開啟的,當有中斷來,則會退出喚醒,常見的中斷源有按鍵,USB等.
3.以按鍵驅動為例(基於內核3.10.14)
在內核中,有個input按鍵子系統"gpio-keys"(位於driver/input/keyboard/gpio.keys.c),該平台驅動platform_driver已經在內核中寫好了(后面會簡單分析)
我們只需要在內核啟動時,注冊"gpio-keys"平台設備platform_device,即可實現一個按鍵驅動.
方式1-修改對應板卡的defconfig文件,添加宏:
方式2-進入make menuconfig
3.2修改好后,接下來寫my_button.c文件,來注冊platform_device
上面的arch_initcall()表示:
會將button_base_init函數放在內核鏈接腳本.initcall3.init段中,然后在內核啟動時,會去讀鏈接腳本,然后找到button_base_init()函數,並執行它.
通常,在內核中,platform 設備的初始化(注冊)用arch_initcall()調用
而驅動的注冊則用module_init()調用,因為module_init()在arch_initcall()之后才調用
因為在init.h中定義:
3.3然后將my_button.c文件添加到Makefile中
編譯內核后,便實現一個簡單的按鍵喚醒休眠了.
接下來開始分析platform_driver(位於driver/input/keyboard/gpio.keys.c),看看是如何注冊按鍵和實現喚醒的.
4.1該文件里有常用的函數有
設置按鍵和input_dev,注冊input-key子系統
設置GPIO,設置input結構體支持的按鍵值,設置中斷,設置防抖動機制
按鍵中斷函數,如果是中斷源,則通過pm_stay_awake()通知pm子系統喚醒,英文電影推薦如果有防抖動,則延時並退出,否則通過schedule_work()來調用gpio_keys_gpio_work_func()一次
定時器超時處理函數,用來實現防抖動,里面會通過schedule_work()來調用一次gpio_keys_gpio_work_func();
處理gpio事件函數,用來上報input事件,並判斷按鍵中斷源,如果是的話,則調用pm_relax(),通知pm子系統喚醒工作結束
通知pm(power manager), 喚醒休眠
休眠函數,休眠之前會被調用
喚醒函數,喚醒之前被調用
SIMPLE_DEV_PM_OPS宏位於pm.h,它將會定義一個dev_pm_ops結構體,用來被pm子系統調用,實現休眠喚醒
<span "="" src="https://images2018.cnblogs.com/blog/1182576/201809/1182576-20180911183215927-1124377797.png">
gpio_keys_probe()函數定義如下所示:
dev->power.should_wakeup來做不同的操作
4.4 其中gpio_keys_suspend()休眠函數定義如下所示:
從上面函數可以看到,進入休眠之前,我們需要調用enable_irq_wake()來設置喚醒源
4.5 然后在中斷函數中,判斷是否需要上報喚醒事件,中斷函數如下所示:
其中gpio_keys_gpio_work_func()函數如下所示:
從上面兩個函數可以看到,喚醒休眠時,需要使用兩個函數實現:
在中斷前調用pm_stay_awake(),中斷結束時再調用一次pm_relax()函數.
4.6 如果想延時喚醒,也可以使用另一種喚醒休眠,則只需要一個函數實現:
4.7 接下來來看gpio_keys_setup_key(),如何設置按鍵的(只加了重要的部分)
通過gpio.keys.c,得出喚醒流程:
休眠時:
喚醒后:
中斷時,有兩種喚醒PM模式
模式1-使用兩個函數實現:
- 進入中斷時調用一次pm_stay_awake().
- 退出時也調用一次pm_relax(bdata->input->dev.parent);
模式2-只需一個函數實現:
- 進入中斷時調用pm_wakeup_event(struct device *dev, unsigned int msec).
5.接下來,我們自己寫個按鍵字符驅動,實現休眠喚醒
應用測試代碼如下:
試驗: