kernel syscore 学习笔记
一、syscore简介
1. syscore 作为低功耗休眠唤醒流程的一部分,其涉及的文件主要有 syscore_ops.h 和 syscore.c,这一级别的回调函数是在完全屏蔽中断的场景下进行的。
2. 相关结构
//syscore_ops.h struct syscore_ops { struct list_head node; int (*suspend)(void); //syscore_suspend()中遍历调用此回调 void (*resume)(void); //syscore_resume()中遍历调用此回调 void (*shutdown)(void); //syscore_shutdown()中遍历调用此回调 };
使用时可以提供一个或多个回调函数的实现。
3. 相关函数
//syscore.c void register_syscore_ops(struct syscore_ops *ops); void unregister_syscore_ops(struct syscore_ops *ops);
二、使用方法
1. 使用时定义一个 struct syscore_ops 结构变量,然后实现感兴趣的回调函数。之后调用 register_syscore_ops() 进行注册,当移除驱动时,调用 unregister_syscore_ops() 去掉注册。
2. register_syscore_ops()/unregister_syscore_ops() 的实现
//syscore.c void register_syscore_ops(struct syscore_ops *ops) { mutex_lock(&syscore_ops_lock); list_add_tail(&ops->node, &syscore_ops_list); //注意是尾插法 mutex_unlock(&syscore_ops_lock); } EXPORT_SYMBOL_GPL(register_syscore_ops); void unregister_syscore_ops(struct syscore_ops *ops) { mutex_lock(&syscore_ops_lock); list_del(&ops->node); mutex_unlock(&syscore_ops_lock); } EXPORT_SYMBOL_GPL(unregister_syscore_ops);
三、调用时机
1. suspend 回调位置
int syscore_suspend(void) { struct syscore_ops *ops; int ret = 0; trace_suspend_resume(TPS("syscore_suspend"), 0, true); pm_pr_dbg("Checking wakeup interrupts\n"); if (pm_wakeup_pending()) return -EBUSY; WARN_ONCE(!irqs_disabled(), "Interrupts enabled before system core suspend.\n"); //注意反向遍历,也就是最后注册的suspend回调函数最先被调用 list_for_each_entry_reverse(ops, &syscore_ops_list, node) if (ops->suspend) { pm_pr_dbg("Calling %pS\n", ops->suspend); ret = ops->suspend(); if (ret) goto err_out; WARN_ONCE(!irqs_disabled(), "Interrupts enabled after %pS\n", ops->suspend); } trace_suspend_resume(TPS("syscore_suspend"), 0, false); return 0; //只要有任何一个suspend回调失败了,就完整地回调一遍resume回调,注意不是从断点位置回调的! err_out: log_suspend_abort_reason("System core suspend callback %pS failed", ops->suspend); pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend); list_for_each_entry_continue(ops, &syscore_ops_list, node) if (ops->resume) ops->resume(); return ret; } EXPORT_SYMBOL_GPL(syscore_suspend);
2. resume 回调位置
//syscore.c void syscore_resume(void) { struct syscore_ops *ops; trace_suspend_resume(TPS("syscore_resume"), 0, true); //说明是关着中断回调resume回调的 WARN_ONCE(!irqs_disabled(), "Interrupts enabled before system core resume.\n"); //注意,这个是正向遍历的,先注册的resume回调先被调用 list_for_each_entry(ops, &syscore_ops_list, node) if (ops->resume) { pm_pr_dbg("Calling %pS\n", ops->resume); ops->resume(); WARN_ONCE(!irqs_disabled(), "Interrupts enabled after %pS\n", ops->resume); } trace_suspend_resume(TPS("syscore_resume"), 0, false); } EXPORT_SYMBOL_GPL(syscore_resume);
3. shutdown 回调位置
void syscore_shutdown(void) { struct syscore_ops *ops; mutex_lock(&syscore_ops_lock); //注意,这个也是反向调用的,最后注册的最先被调用 list_for_each_entry_reverse(ops, &syscore_ops_list, node) if (ops->shutdown) { if (initcall_debug) pr_info("PM: Calling %pS\n", ops->shutdown); ops->shutdown(); } mutex_unlock(&syscore_ops_lock); }
4. 整机调用时机
echo mem > /sys/power/state state_store pm_suspend enter_state suspend_devices_and_enter suspend_enter platform_suspend_prepare dpm_suspend_late platform_suspend_prepare_late dpm_suspend_noirq platform_suspend_prepare_noirq suspend_disable_secondary_cpus //关非boot cpu arch_suspend_disable_irqs //关中断 syscore_suspend //syscore .suspend 回调 suspend_ops->enter //完全休眠下去,之后唤醒了从这里开始执行------------------- syscore_resume //syscore .resume 回调 arch_suspend_enable_irqs //开中断 suspend_enable_secondary_cpus //开非boot cpu platform_resume_noirq dpm_resume_noirq platform_resume_early dpm_resume_early
4. 调用时机汇总
这些syscore ops的回调是关中断,关非boot cpu的情况下调用的。suspend/shutdown回调注册的越早越靠后调用,resume回调注册的越早越靠前调用。syscore的suspend回调是在所有驱动的suspend回调之后被调用,syscore的resume回调在所有驱动的resume回调之前被调用。
四、依赖关系处理
在probe()驱动的时候有考虑依赖关系,若发现自己不适合被probe,probe()函数就返回 -EPROBE_DEFER 来延迟probe()。
参考:
https://blog.csdn.net/adaptiver/article/details/52013245
posted on 2022-05-05 18:18 Hello-World3 阅读(1320) 评论(0) 编辑 收藏 举报