1.Linux电源管理-休眠与唤醒

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.接下来,我们自己写个按键字符驱动,实现休眠唤醒

应用测试代码如下:

 试验:


文章来源:https://www.cnblogs.com/lifexy/p/9629699.html

原文地址:https://www.cnblogs.com/xiongjim/p/9673905.html