1.休眠方式
在内核中,休眠方式有很多种,可以通过下面命令查看
# cat /sys/power/state //来得到内核支持哪几种休眠方式.
常用的休眠方式有 freeze,standby, mem, disk
- freeze: 冻结I/O设备,将它们置于低功耗状态,使处理器进入空闲状态。唤醒最快,耗电比其它standby, mem,disk方式高。
- standby: 除了冻结I/O设备外,还会暂停系统,唤醒较快,耗电比其它 mem, disk方式高
- mem: 将运行状态数据存到内存,并关闭外设,进入等待模式,唤醒较慢,耗电比disk方式高
- disk: 将运行状态数据存到硬盘,然后关机,唤醒最慢
示例:
# echo standby > /sys/power/state // 命令系统进入standby休眠.
2.唤醒方式
当我们休眠时,如果想唤醒,则需要添加中断唤醒源,使得在休眠时,这些中断是设为开启的,当有中断来,则会退出唤醒,常见的中断源有按键,USB等。
3.底层实现
代码参考: kernel/drivers/input/keyboard/gpio_keys.c
static int __maybe_unused gpio_keys_suspend(struct device *dev)
{
……
enable_irq_wake(irq);
……
return 0;
}
static int __maybe_unused gpio_keys_resume(struct device *dev)
{
……
disable_irq_wake(irq);
……
return 0;
}
static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.shutdown = gpio_keys_shutdown,
.driver = {
.name = "gpio-keys",
.pm = &gpio_keys_pm_ops,
.of_match_table = gpio_keys_of_match,
.dev_groups = gpio_keys_groups,
}
};
注: 上面代码中,gpio_keys_suspend,gpio_keys_resume中没有直接出现enable_irq_wake和disable_irq_wake,但是最终是会调用这俩API。
通过实例发现:休眠唤醒的设计,只需要在gpio_keys_device_driver 中实例driver成员的pm成员。SIMPLE_DEV_PM_OPS是Linux封装的一层结构体:
#ifdef CONFIG_PM_SLEEP
#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \
.suspend = suspend_fn, \
.resume = resume_fn, \
.freeze = suspend_fn, \
.thaw = resume_fn, \
.poweroff = suspend_fn, \
.restore = resume_fn,
#else
#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn)
#endif
#define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \
const struct dev_pm_ops name = { \
SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \
}
将源代码宏展开:
//static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
const struct dev_pm_ops name = {
.suspend = gpio_keys_suspend,
.resume = gpio_keys_resume,
}
3.总结
- 在实际应用中,需要按键实现休眠唤醒,只需要实现platform_driver->driver->pm下suspend和resume成员函数即可。然后在suspend和resume中增加按键中断唤醒使能和按键唤醒失能。
- 流程:在linux要执行休眠时,换遍历一遍所有注册到内核驱动的suspend函数,执行suspend内部代码;在被唤醒时会遍历resume函数,执行内部代码。
- 至于为什么都要执行中断唤醒失能?网上的一种说法是如果在执行enable_irq_wake(irq)之前,中断已经处于可唤醒使能,会出现报错。所以在每次唤醒前先disable_irq_wake(irq),休眠时enable_irq_wake(irq)。
- 对于休眠唤醒,Linux内核实现起来很复杂,但是对于驱动开发来讲,使用起来较为方便,这也是操作系统的意义所在:严格的分层思想,复杂的流程由内核实现,并提供API供开发人员使用。学习内核的具体实现对编程功力有很大帮助,后续继续分章节介绍其内核休眠唤醒机制具体的内核实现流程。
引用文章:1.Linux电源管理-休眠与唤醒