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.接下来,我们自己写个按键字符驱动,实现休眠唤醒
应用测试代码如下:
试验: