L15. 休眠唤醒

作者: 拂去尘世尘 | 来源:发表于2020-08-16 11:27 被阅读0次

    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.总结

    1. 在实际应用中,需要按键实现休眠唤醒,只需要在platform_driver->driver->pm下实例suspend和resume成员函数即可。然后在suspend和resume中增加按键中断唤醒使能和按键唤醒失能。
    2. 流程:在linux要执行休眠时,换遍历一遍所有注册到内核驱动的suspend函数,执行suspend内部代码;在被唤醒时会遍历resume函数,执行内部代码。
    3. 至于为什么都要执行中断唤醒失能?网上的一种说法是如果在执行enable_irq_wake(irq)之前,中断已经处于可唤醒使能,会出现报错。所以在每次唤醒前先disable_irq_wake(irq),休眠时enable_irq_wake(irq)。
    4. 对于休眠唤醒,Linux内核实现起来很复杂,但是对于驱动开发来讲,使用起来较为方便,这也是操作系统的意义所在:严格的分层思想,复杂的流程由内核实现,并提供API供开发人员使用。学习内核的具体实现对编程功力有很大帮助,后续继续分章节介绍其内核休眠唤醒机制具体的内核实现流程。

    参考文章:1.Linux电源管理-休眠与唤醒

    如有技术交流需要,请关注“开源519”公众号。


    开源519.jpg

    相关文章

      网友评论

        本文标题:L15. 休眠唤醒

        本文链接:https://www.haomeiwen.com/subject/izjtjktx.html