1 简介
电源管理子系统是OpenHarmony的基本能力子系统,有电池服务组件、显示控制组件和电源管理服务组件,主要提供如下功能:
- 重启系统。
- 管理休眠运行锁。
- 系统电源状态查询。
- 充电和电池状态查询和上报。
- 亮灭屏管理和亮度调节。
本文重点分析亮灭屏功能,包括NAPI接口、PowerMgr和Kernel层的实现。
1.1 电源管理相关
《OpenHarmony 源码解析之电源管理子系统 》
《OpenHarmony 源码解析之电源管理亮灭屏功能》
1.2 OpenHarmony架构图
1.3 电源管理子系统架构图
2 知识准备
2.1 电源状态
- On (on) S0-Working
- Standby (standby) S1- CPU and RAM are powed but not executed
- Suspend to Ram (mem) S2- RAM is powered and the running content is saved to RAM
- Suspend to Disk (disk) S3 - All content is saved to Disk and power down
S0状态也就是计算机正常工作状态。
S1状态简称standby状态,此状态下CPU处于低功耗状态,并且没有数据保存到RAM或者disk中,此状态待机和恢复通常很快。
S2状态简称STR,此状态下计算机会冻结所有的活动并将当前工作状态保存到RAM中,然后关闭屏幕进入低功耗模式,通常睡眠和唤醒需要几秒。
S3状态简称SRD或者Hibernate,代表冬眠,意识是比较长久,一般在window系统中常见到。此状态下计算机将所有活动的状态保存到磁盘中,然后处于关机状态,此模式下是不耗电的,而相比之前的模式,休眠和唤醒的速度都比较慢。
注:S0->S3功耗由大到小,唤醒速度由快到慢。
查看系统支持的睡眠方式:
- //ubuntu系统:
- #cat /sys/power/state
- freeze standby mem disk
- //rk3566和自研板
- # cat /sys/power/state
- freeze mem
切换为睡眠模式:
- #echo mem > /sys/power/state
2.2 wakeup count
wakeup count的存在,是为了解决Sleep和Wakeup之间的同步问题。
wakeup_count是内核用来保存当前wakeup event发生的计数,用户空间程序在写入state切换状态之前,应先读取wakeup_count并把获得的count写回给wakeup_count,内核会比对写回的count和当前的count是否一致,用户空间程序检测到写入正确后,可以继续对state的写入,以便发起一次状态切换。
wakeup count的系统文件:/sys/power/wakeup_count。
2.3 wakelocks
- 一个sysfs文件:/sys/power/wake_lock,用户程序向文件写入一个字符串,即可创建一个wakelock,该字符串就是wakelock的名字。该wakelock可以阻止系统进入低功耗模式。
- 一个sysfs文件:/sys/power/wake_unlock,用户程序向文件写入相同的字符串,即可注销一个wakelock。
- 当系统中所有的wakelock都注销后,系统可以自动进入低功耗状态。
- 向内核其它driver也提供了wakelock的创建和注销接口,允许driver创建wakelock以阻止睡眠、注销wakelock以允许睡眠。
3 整体流程代码
3.1 NAPI接口
在原来power模块增加休眠和唤醒接口
- static napi_value PowerInit(napi_env env, napi_value exports)
- {
- POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: enter", __func__);
- napi_property_descriptor desc[] = {
- DECLARE_NAPI_FUNCTION("shutdownDevice", ShutdownDevice),
- DECLARE_NAPI_FUNCTION("rebootDevice", RebootDevice),
- DECLARE_NAPI_FUNCTION("isScreenOn", IsScreenOn),
- DECLARE_NAPI_FUNCTION("wakeupDevice", WakeupDevice), //新增的唤醒接口
- DECLARE_NAPI_FUNCTION("suspendDevice", SuspendDevice), //新增的休眠接口
- };
- NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
- POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: exit", __func__);
-
- return exports;
- }
- //唤醒接口实现,应用在调用此接口前先调用power模块已提供的IsScreenOn接口
- static napi_value WakeupDevice(napi_env env, napi_callback_info info)
- {
- POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: enter, %{public}s", __func__, "wake up device");
- size_t argc = 1;
- napi_value args[1] = { 0 };
- napi_value jsthis;
- void *data = nullptr;
-
- napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
- NAPI_ASSERT(env, (status == napi_ok) && (argc >= 1), "failed to get cb info");
- napi_valuetype type = napi_undefined;
- NAPI_CALL(env, napi_typeof(env, args[0], &type));
- NAPI_ASSERT(env, type == napi_string, "wrong argument type. string expected.");
-
- char reason[REASON_MAX] = { 0 };
- size_t reasonLen = 0;
- status = napi_get_value_string_utf8(env, args[0], reason, REASON_MAX - 1, &reasonLen);
- if (status != napi_ok) {
- POWER_HILOGE(MODULE_JS_NAPI, "%{public}s: get reason failed", __func__);
- return nullptr;
- }
- g_powerMgrClient.WakeupDevice(WakeupDeviceType::WAKEUP_DEVICE_APPLICATION,std::string(reason));
- POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: reason %{public}s, exit", __func__, reason);
- return nullptr;
- }
- //休眠接口,应用在调用此接口前先调用power模块已提供的IsScreenOn接口
- static napi_value SuspendDevice(napi_env env, napi_callback_info info)
- {
- POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: enter, %{public}s", __func__, "suspend device");
- size_t argc = 1;
- napi_value args[1] = { 0 };
- napi_value jsthis;
- void *data = nullptr;
-
- napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
- NAPI_ASSERT(env, (status == napi_ok) && (argc >= 1), "failed to get cb info");
- napi_valuetype type = napi_undefined;
- NAPI_CALL(env, napi_typeof(env, args[0], &type));
- NAPI_ASSERT(env, type == napi_string, "wrong argument type. string expected.");
-
- char reason[REASON_MAX] = { 0 };
- size_t reasonLen = 0;
- status = napi_get_value_string_utf8(env, args[0], reason, REASON_MAX - 1, &reasonLen);
- if (status != napi_ok) {
- POWER_HILOGE(MODULE_JS_NAPI, "%{public}s: get reason failed", __func__);
- return nullptr;
- }
- g_powerMgrClient.SuspendDevice(SuspendDeviceType::SUSPEND_DEVICE_REASON_APPLICATION,true);
- POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: reason %{public}s, exit", __func__, reason);
- return nullptr;
- }
3.2 PowerMgr接口
接口处理的后续流程比较通用,powermgrclient->powermgrserviceproxy->powermgrservicestub->powermgrservice。
powermgr内部流转到suspend和wakeup:
- base\powermgr\power_manager\services\native\src\actions\default\device_state_action.cpp:
- //系统挂起时先灭屏再休眠
- void DeviceStateAction::Suspend(int64_t callTimeMs, SuspendDeviceType type, uint32_t flags)
- {
- DisplayManager::SetScreenState(ScreenState::SCREEN_STATE_OFF);//调用displaymgr的屏幕关闭
- SystemSuspendController::GetInstance().EnableSuspend();//系统休眠
- }
- //唤醒系统时先唤醒再亮屏
- void DeviceStateAction::Wakeup(int64_t callTimeMs, WakeupDeviceType type, const string& details,
- const string& pkgName)
- {
- SystemSuspendController::GetInstance().DisableSuspend();//系统唤醒
- DisplayManager::SetScreenState(ScreenState::SCREEN_STATE_ON);//调用displaymgr的屏幕打开
- }
上面的suspend/wakeup函数分两支,其中一支是调用DisplayManager的屏幕打开、关闭功能实现不完整,Display驱动部分引用的动态库,代码部分和实际日志不符。另一支是系统休眠、唤醒处理。
3.3 系统休眠&唤醒
先看系统休眠、唤醒处理,目前自研板支持两种模式freeze mem 一级待机和二级待机,而powermgr是使用的mem模式。
- //base\powermgr\power_manager\services\native\src\actions\default\system_suspend_controller.cpp
- //目前系统目录没有下面这两个文件影响休眠和唤醒功能:
- 1)/sys/power/wake_lock //该wakelock可以阻止系统进入低功耗模式。
- 2)/sys/power/wake_unlock //写入相同的字符串,即可注销一个wakelock
- //挂起,系统进入低功耗模式
- void SystemSuspendController::EnableSuspend()
- {
- std::lock_guard lock(mutex_);
- sc_->EnableSuspend();//睡眠的具体操作。
- if (!suspendEnabled_) {
- rlh_->Release(WAKEUP_HOLDER); //wake_unlock写入"OHOSPowerMgr.WakeupHolder";允许睡眠
- suspendEnabled_ = true;
- }
- }
- //唤醒,即阻止系统进入低功耗模式。
- void SystemSuspendController::DisableSuspend()
- {
- std::lock_guard lock(mutex_);
- if (suspendEnabled_) {
- rlh_->Acquire(WAKEUP_HOLDER); //wake_lock写入"OHOSPowerMgr.WakeupHolder";阻止睡眠
- suspendEnabled_ = false;
- }
- }
- //base\powermgr\power_manager\services\native\src\actions\default\suspend\suspend_controller.cpp:
- static constexpr const char * const SUSPEND_STATE = "mem";
- static constexpr const char * const SUSPEND_STATE_PATH = "/sys/power/state";
- static constexpr const char * const WAKEUP_COUNT_PATH = "/sys/power/wakeup_count";
- void SuspendController::EnableSuspend()
- {
- suspend_->Start();//启动AutoSuspendLoop线程
- POWER_HILOGI(MODULE_SERVICE, "AutoSuspend enabled");
- }
- void SuspendController::AutoSuspend::AutoSuspendLoop()
- {
- while (true) {
- std::this_thread::sleep_for(waitTime_);
- const std::string wakeupCount = WaitWakeupCount(); //在rk3566和自研板上阻塞在读取/sys/power/wakeup_count文件,应该是系统不支持
- if (wakeupCount.empty()) {
- continue;
- }
- waitingFunc_();//阻塞函数,只有等到休眠条件才会继续执行。
- if (!WriteWakeupCount(wakeupCount)) {//写入wakeupcount
- continue;
- }
- bool success = SuspendEnter();//写入mem
- if (!success) {
- POWER_HILOGE(MODULE_SERVICE, "Start suspend failed!");
- }
- }
- }
- //读取wakeupcount
- std::string SuspendController::AutoSuspend::WaitWakeupCount()
- {
- if (wakeupCountFd < 0) {
- wakeupCountFd = UniqueFd(TEMP_FAILURE_RETRY(open(WAKEUP_COUNT_PATH, O_RDWR | O_CLOEXEC)));
- }
- std::string wakeupCount;
- bool ret = LoadStringFromFd(wakeupCountFd, wakeupCount);
- if (!ret) {
- POWER_HILOGW(MODULE_SERVICE, "Read wakeup count failed!");
- return std::string();
- }
- return wakeupCount;
- }
- //写入wakeupcount
- bool SuspendController::AutoSuspend::WriteWakeupCount(std::string wakeupCount)
- {
- if (wakeupCountFd < 0) {
- return false;
- }
- bool ret = SaveStringToFd(wakeupCountFd, wakeupCount.c_str());
- if (!ret) {
- POWER_HILOGE(MODULE_SERVICE, "Failed to write the wakeup count!");
- }
- return ret;
- }
- //通过向/sys/power/state写入mem实现挂起功能。
- bool SuspendController::AutoSuspend::SuspendEnter()
- {
- static bool inited = false;
- static UniqueFd suspendStateFd(TEMP_FAILURE_RETRY(open(SUSPEND_STATE_PATH, O_RDWR | O_CLOEXEC)));
- if (!inited) {
- if (suspendStateFd < 0) {
- POWER_HILOGE(MODULE_SERVICE, "Failed to open the suspending state fd!");
- return false;
- }
- inited = true;
- }
- bool ret = SaveStringToFd(suspendStateFd, SUSPEND_STATE);
- if (!ret) {
- POWER_HILOGE(MODULE_SERVICE, "Failed to write the suspending state!");
- }
- return ret;
- }
3.4 屏幕开关
再看调用DisplayManager的屏幕打开、关闭功能实现,有一部分是私有实现,只提供了动态库,最终会调用hdf_disp驱动接口进行屏幕的打开、关闭操作。
- //base\powermgr\display_manager\service\native\src\screen_action.cpp:
- bool ScreenAction::SetPowerState(ScreenState state __attribute__((__unused__)))
- {
- int32_t dispErr_ = DISPLAY_SUCCESS;
- if (!hdiFuncs_) {
- DISPLAY_HILOGE(MODULE_SERVICE, "Invalid device functions");
- return false;
- }
- if (ScreenState::SCREEN_STATE_ON == state)
- {
- dispErr_ = hdiFuncs_->SetDisplayPowerStatus(0,DispPowerStatus::POWER_STATUS_ON);
- }
- else
- {
- dispErr_ = hdiFuncs_->SetDisplayPowerStatus(0,DispPowerStatus::POWER_STATUS_OFF);
- }
- DISPLAY_HILOGE(MODULE_SERVICE, "SetDisplayPowerStatus:%{public}u result:%{public}d",ToUnderlying(state),dispErr_);
- return dispErr_ == DISPLAY_SUCCESS;
- }
- //drivers\peripheral\display\interfaces\include\display_device.h:
- SetDisplayPowerStatus:接口说明:
-
- int32_t (*SetDisplayPowerStatus)(uint32_t devId, DispPowerStatus status);
下面是调用hdf_disp驱动部分,依然属于私有实现,这部分可以看到执行日志,但是代码还不完整:
- //drivers\peripheral\display\hal\disp_hal.c:
- static int32_t DispCmdSend(const uint32_t cmd, struct HdfSBuf *reqData, struct HdfSBuf *respData)
- {
- struct HdfIoService *dispService = NULL;
- HDF_LOGE("%s:add by wangkui", __func__);
- dispService = HdfIoServiceBind(DISP_SERVICE_NAME);//此处是找hdf_disp驱动服务。
- if ((dispService == NULL) || (dispService->dispatcher == NULL) || (dispService->dispatcher->Dispatch == NULL)) {
- HDF_LOGE("%s:bad remote service found modified by wangkui", __func__);
- goto EXIT;
- }
- int32_t ret = dispService->dispatcher->Dispatch(&dispService->object, cmd, reqData, respData);
- if (ret != DISPLAY_SUCCESS) {
- HDF_LOGE("%s: cmd=%u, ret=%d", __func__, cmd, ret);
- goto EXIT;
- }
- HDF_LOGI("%s: cmd=%u, ret=%d", __func__, cmd, ret);
- HdfIoServiceRecycle(dispService);
- return DISPLAY_SUCCESS;
-
- EXIT:
- HdfIoServiceRecycle(dispService);
- return DISPLAY_FAILURE;
- }
- //drivers\framework\core\shared\src\hdf_io_service.c:
- struct HdfIoService *HdfIoServiceBind(const char *serviceName)
- {
- return HdfIoServiceAdapterObtain(serviceName);
- }
- //drivers\framework\core\adapter\syscall\src\hdf_syscall_adapter.c:
- struct HdfIoService *HdfIoServiceAdapterObtain(const char *serviceName)
- {
- struct HdfSyscallAdapter *adapter = NULL;
- struct HdfIoService *ioService = NULL;
- char *devNodePath = NULL;
- char *realPath = NULL;
-
- const char *devPath = DEV_NODE_PATH;
- if (access(DEV_NODE_PATH, F_OK) != 0) {
- devPath = DEV_PATH;
- }
-
- devNodePath = OsalMemCalloc(PATH_MAX);
- realPath = OsalMemCalloc(PATH_MAX);
- if (devNodePath == NULL || realPath == NULL) {
- HDF_LOGE("%s: out of memory", __func__);
- goto out;
- }
-
- if (sprintf_s(devNodePath, PATH_MAX - 1, "%s%s", devPath, serviceName) < 0) {
- HDF_LOGE("Failed to get the node path");
- goto out;
- }
-
- if (realpath(devNodePath, realPath) == NULL) {//此处发现找不到hdf_disp文件
- HDF_LOGE("%s: file name %{public}s is invalid", __func__, devNodePath);
- if (HdfLoadDriverByServiceName(serviceName) != HDF_SUCCESS) {//通过hdf_disp服务名查找
- HDF_LOGE("%s: load %{public}s driver failed,", __func__, serviceName);
- goto out;
- }
- if (realpath(devNodePath, realPath) == NULL) {
- HDF_LOGE("%s: file name %{public}s is invalid", __func__, devNodePath);
- goto out;
- }
- }
-
- adapter = (struct HdfSyscallAdapter *)OsalMemCalloc(sizeof(struct HdfSyscallAdapter));
- if (adapter == NULL) {
- HDF_LOGE("Failed to allocate SyscallAdapter");
- goto out;
- }
-
- DListHeadInit(&adapter->listenerList);
- if (OsalMutexInit(&adapter->mutex)) {
- HDF_LOGE("%s: Failed to create mutex", __func__);
- OsalMemFree(adapter);
- goto out;
- }
-
- adapter->fd = open(realPath, O_RDWR);
- if (adapter->fd < 0) {
- HDF_LOGE("Open file node %{public}s failed, (%d)%{public}s", realPath, errno, strerror(errno));
- OsalMutexDestroy(&adapter->mutex);
- OsalMemFree(adapter);
- goto out;
- }
- ioService = &adapter->super;
- static struct HdfIoDispatcher dispatch = {
- .Dispatch = HdfSyscallAdapterDispatch,
- };
- ioService->dispatcher = &dispatch;
- out:
- OsalMemFree(devNodePath);
- OsalMemFree(realPath);
- return ioService;
- }
- int32_t HdfLoadDriverByServiceName(const char *serviceName)
- {
- int32_t ret = HDF_FAILURE;
- struct HdfSBuf *data = NULL;
- if (serviceName == NULL || strcmp(serviceName, DEV_MGR_NODE) == 0) {
- HDF_LOGE("failed to get %s service,call self!!!", serviceName);//如果是dev_mgr则返回,防止死循环。
- return ret;
- }
- struct HdfIoService *ioService = HdfIoServiceBind(DEV_MGR_NODE);//这里找dev_mgr,根据日志看也没找到,所以没发现hdf_disp服务。
- if (ioService == NULL) {
- HDF_LOGE("failed to get %s service", DEV_MGR_NODE);
- return ret;
- }
- data = HdfSBufObtainDefaultSize();
- if (data == NULL) {
- HDF_LOGE("failed to obtain sbuf data");
- ret = HDF_DEV_ERR_NO_MEMORY;
- goto out;
- }
- if (!HdfSbufWriteString(data, serviceName)) {
- HDF_LOGE("failed to write sbuf");
- ret = HDF_FAILURE;
- goto out;
- }
- ret = ioService->dispatcher->Dispatch(&ioService->object, DEVMGR_LOAD_SERVICE, data, NULL);
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("failed to load khdf driver %s", serviceName);
- }
- out:
- HdfIoServiceRecycle(ioService);
- HdfSBufRecycle(data);
- return ret;
- }
HDF_DISP驱动主要功能是:
1)接受和管理显示面板panel的接口注册;
2)接收HDI层发来的显示相关操作,然后调用显示面板驱动的接口进行panel的开、关、亮度调节,以及状态查看的操作。
其主要目的是屏蔽各类显示面板驱动的具体实现,向上提供统一的显示接口。
4.系统休眠&唤醒的Kernel层实现
4.1 wakelocks
1.1 Kernel节点文件创建
/sys/power/wake_lock & wake_unlock的设备节点文件的创建在kernel\linux-4.19\kernel\power\main.c文件中实现。
power目录下的文件都是按照此方法注册,且根据版本构建的宏定义开关可以有选择性的创建。
- #define power_attr(_name) \
- static struct kobj_attribute _name##_attr = { \
- .attr = { \
- .name = __stringify(_name), \
- .mode = 0644, \
- }, \
- .show = _name##_show, \
- .store = _name##_store, \
- }
-
- power_attr(wake_lock);
- power_attr(wake_unlock);
- //根据宏定义开关注册sleep、autosleep、suspend、wakelock等。
- static struct attribute * g[] = {
- &state_attr.attr,
- #ifdef CONFIG_PM_TRACE
- &pm_trace_attr.attr,
- &pm_trace_dev_match_attr.attr,
- #endif
- #ifdef CONFIG_PM_SLEEP
- &pm_async_attr.attr,
- &wakeup_count_attr.attr,
- #ifdef CONFIG_SUSPEND
- &mem_sleep_attr.attr,
- #endif
- #ifdef CONFIG_PM_AUTOSLEEP
- &autosleep_attr.attr,
- #endif
- #ifdef CONFIG_PM_WAKELOCKS //宏定义必须打开才会创建
- &wake_lock_attr.attr,
- &wake_unlock_attr.attr,
- #endif
- #ifdef CONFIG_PM_SLEEP_DEBUG
- &pm_test_attr.attr,
- &pm_print_times_attr.attr,
- &pm_wakeup_irq_attr.attr,
- &pm_debug_messages_attr.attr,
- #endif
- #endif
- #ifdef CONFIG_FREEZER
- &pm_freeze_timeout_attr.attr,
- #endif
- NULL,
- };
- static const struct attribute_group attr_group = {
- .attrs = g,
- };
- static const struct attribute_group *attr_groups[] = {
- &attr_group,
- #ifdef CONFIG_PM_SLEEP
- &suspend_attr_group,
- #endif
- NULL,
- };
- static int __init pm_init(void)
- {
- int error = pm_start_workqueue();
- if (error)
- return error;
- hibernate_image_size_init();
- hibernate_reserved_size_init();
- pm_states_init();
- power_kobj = kobject_create_and_add("power", NULL);//先创建power目录
- if (!power_kobj)
- return -ENOMEM;
- error = sysfs_create_groups(power_kobj, attr_groups);//创建power目录下的其它设备节点。
- if (error)
- return error;
- pm_print_times_init();
- return pm_autosleep_init();
- }
-
- core_initcall(pm_init);
1.2 创建一个wakelock
入口在kernel/power/main.c中实现:
- static ssize_t wake_lock_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t n)
- {
- int error = pm_wake_lock(buf);
- return error ? error : n;
- }
上面直接调用kernel\linux-4.19\kernel\power\wakelock.c中的pm_wake_lock:
- //用于上报一个wakeup event,输入参数为一个字符串,如:"wakelocktest 1000"
- int pm_wake_lock(const char *buf)
- {
- const char *str = buf;
- struct wakelock *wl;
- u64 timeout_ns = 0;
- size_t len;
- int ret = 0;
-
- //检查调用的进程是否有执行阻止系统休眠的权限,PowerMgr打包在foundation进程,所以需要修改foundation进程的权限,增加BLOCK_SUSPEND
- //./foundation/appexecfwk/standard/sa_profile/foundation.rc: capabilities SYS_PTRACE KILL SYS_BOOT
- //./foundation/appexecfwk/standard/sa_profile/foundation.cfg: "caps" : ["SYS_PTRACE", "KILL", "SYS_BOOT"]
- if (!capable(CAP_BLOCK_SUSPEND))
- return -EPERM;
- //解析字符串,取出wakelock的名字和超时时间,超时时间可以不带。
- while (*str && !isspace(*str))
- str++;
-
- len = str - buf;
- if (!len)
- return -EINVAL;
-
- if (*str && *str != '\n') {
-
- ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);
- if (ret)
- return -EINVAL;
- }
-
- mutex_lock(&wakelocks_lock);
- //向wakelock维护的红黑树tree中添加新的wakelock,且注册wakeup source,name就是入参的buf。
- wl = wakelock_lookup_add(buf, len, true);
- if (IS_ERR(wl)) {
- ret = PTR_ERR(wl);
- goto out;
- }
- //1)__pm_stay_awake,通知PM core,注册的wakeup source产生了wakeup event,且正在处理,因此不允许系统suspend;
- //2)__pm_relax,通知PM core,注册的wakeup source没有正在处理的wakeup event,允许系统suspend;
- //3)__pm_wakeup_event,为上边两个接口的功能组合,通知PM core,注册的wakeup source产生了wakeup event,会在timeout_ms毫秒内处理结束,由PM core自动调用__pm_relax。
- if (timeout_ns) {
- u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;
-
- do_div(timeout_ms, NSEC_PER_MSEC);
- __pm_wakeup_event(wl->ws, timeout_ms);
- } else {
- __pm_stay_awake(wl->ws);
- }
-
- wakelocks_lru_most_recent(wl);//wakelocks有GC机制,通过LRU策略进行淘汰长时间不活动的wakelock
-
- out:
- mutex_unlock(&wakelocks_lock);
- return ret;
- }
1.3 注销一个wakelock
入口在kernel/power/main.c中实现:
- static ssize_t wake_unlock_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t n)
- {
- int error = pm_wake_unlock(buf);
- return error ? error : n;
- }
上面直接调用kernel\linux-4.19\kernel\power\wakelock.c中的pm_wake_unlock:
- //入参为字符串,跟创建wakelock一样的名字。
- int pm_wake_unlock(const char *buf)
- {
- struct wakelock *wl;
- size_t len;
- int ret = 0;
-
- if (!capable(CAP_BLOCK_SUSPEND))//检查权限
- return -EPERM;
- //解析字符串,获取wakelock名字
- len = strlen(buf);
- if (!len)
- return -EINVAL;
-
- if (buf[len-1] == '\n')
- len--;
-
- if (!len)
- return -EINVAL;
-
- mutex_lock(&wakelocks_lock);
- //查找是否有相同name的wakelock。如果有,直接返回wakelock的指针;如果没有,退出。
- wl = wakelock_lookup_add(buf, len, false);
- if (IS_ERR(wl)) {
- ret = PTR_ERR(wl);
- goto out;
- }
- __pm_relax(wl->ws);//通知PM core,注册的wakeup source没有正在处理的wakeup event,允许系统suspend;
-
- //wakelocks有GC机制,通过LRU策略进行淘汰长时间不活动的wakelock
- wakelocks_lru_most_recent(wl);
- wakelocks_gc();//执行GC回收。
-
- out:
- mutex_unlock(&wakelocks_lock);
- return ret;
- }
还有wakelock的查看接口,本文不列出了。
4.2 wakeup_count
2.1 Kernel节点文件创建
参考wakelocks。
2.2 读取wakeup count
从/sys/power/wakeup_count读取wakeup_count,当有正在处理的event事件此处可能会阻塞。
-
- bool pm_get_wakeup_count(unsigned int *count, bool block)
- {
- unsigned int cnt, inpr;
- pr_debug("pm_get_wakeup_count block: %d\n", block);
- if (block) {
- DEFINE_WAIT(wait);
-
- for (;;) {
- prepare_to_wait(&wakeup_count_wait_queue, &wait,
- TASK_INTERRUPTIBLE);
- split_counters(&cnt, &inpr);
- if (inpr == 0 || signal_pending(current))
- break;
- pm_print_active_wakeup_sources();//debug日志打印出active的事件,以此判断阻塞在何处。
- schedule();
- }
- finish_wait(&wakeup_count_wait_queue, &wait);
- }
-
- split_counters(&cnt, &inpr);
- *count = cnt;
- return !inpr;
- }
- static atomic_t combined_event_count = ATOMIC_INIT(0);
- #define IN_PROGRESS_BITS (sizeof(int) * 4) //16
- #define MAX_IN_PROGRESS ((1 << IN_PROGRESS_BITS) - 1) //0xFFFF
- static void split_counters(unsigned int *cnt, unsigned int *inpr)
- {
- unsigned int comb = atomic_read(&combined_event_count);
- //高16位存储已处理完的wakeup个数,低16位存储正在处理(即Active)的wakeup个数
- //秒!正在处理的wakeup event和已处理的wakeup event在一个变量中维护。
- *cnt = (comb >> IN_PROGRESS_BITS);
- *inpr = comb & MAX_IN_PROGRESS;
- }
2.3 写入wakeup count
上面读取的wakeup_count写入到/sys/power/wakeup_count,如果count不同,则说明有新的event事件产生,或者有正在处理的event事件,即inpr不为零,则说明此时不适宜执行suspend。
-
- bool pm_save_wakeup_count(unsigned int count)
- {
- unsigned int cnt, inpr;
- unsigned long flags;
-
- events_check_enabled = false;
- raw_spin_lock_irqsave(&events_lock, flags);
- split_counters(&cnt, &inpr);
- if (cnt == count && inpr == 0) {
- saved_count = count;
- events_check_enabled = true;
- }
- raw_spin_unlock_irqrestore(&events_lock, flags);
- return events_check_enabled;
- }
4.3 suspend
向/sys/power/state写入mem后就进入Kernel层的suspend流程,入口还是kernel\linux-4.19\kernel\power\main.c:
- //切换休眠状态
- static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
- const char *buf, size_t n)
- {
- suspend_state_t state;
- int error;
-
- error = pm_autosleep_lock();
- if (error)
- return error;
-
- if (pm_autosleep_state() > PM_SUSPEND_ON) {
- error = -EBUSY;
- goto out;
- }
-
- state = decode_state(buf, n);//将“mem”转为对应的数字
- if (state < PM_SUSPEND_MAX) {
- if (state == PM_SUSPEND_MEM)
- state = mem_sleep_current;
-
- error = pm_suspend(state);//核心的kernel休眠流程
- } else if (state == PM_SUSPEND_MAX) {
- error = hibernate();
- } else {
- error = -EINVAL;
- }
-
- out:
- pm_autosleep_unlock();
- return error ? error : n;
- }
suspend实现非常复杂,本文只列出关键的流程:
- //kernel\linux-4.19\kernel\power\suspend.c
- int pm_suspend(suspend_state_t state)
- {
- error = enter_state(state);
- }
- static int enter_state(suspend_state_t state)
- {
- trace_suspend_resume(TPS("suspend_enter"), state, true);
- if (state == PM_SUSPEND_TO_IDLE) {
- } else if (!valid_state(state)) {//检查平台是否支持电源管理,即全局suspend_ops有没有被赋值,并调用其suspend_ops->valid()
- return -EINVAL;
- }
- error = suspend_prepare(state);//准备挂起
- error = suspend_devices_and_enter(state);//让设备进入suspend状态
- suspend_finish();
- }
- //kernel\linux-4.19\kernel\power\suspend.c
- static int suspend_prepare(suspend_state_t state)
- {
- //kernel\linux-4.19\kernel\power\console.c
- //切换挂起、恢复执行内核触发的VT开关,切换到SUSPEND_CONSOLE
- pm_prepare_console();
- //kernel/power/main.c
- //通知驱动程序准备suspend
- error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls);
- //kernel\linux-4.19\kernel\power\power.h
- //冻结App和内核线程
- error = suspend_freeze_processes();
- }
- //kernel\linux-4.19\kernel\power\suspend.c
- ////挂起设备并进入系统睡眠状态
- int suspend_devices_and_enter(suspend_state_t state)
- {
- //如果平台相关的代码有begin函数就去调用它,suspend_ops->begin(state)
- error = platform_suspend_begin(state);
- //kernel\linux-4.19\kernel\printk\printk.c
- //挂起console子系统,此时不能用printk()调试。
- suspend_console();
- suspend_test_start();
- error = dpm_suspend_start(PMSG_SUSPEND);
- suspend_test_finish("suspend devices");
-
- do {
- //关闭核心模块,如cpu等,并设置唤醒源,如果电源键按下则会进入唤醒流程。
- error = suspend_enter(state, &wakeup);
- } while (!error && !wakeup && platform_suspend_again(state));
-
- Resume_devices:
- suspend_test_start();
- dpm_resume_end(PMSG_RESUME);
- suspend_test_finish("resume devices");
- resume_console();
- }
- //kernel\linux-4.19\kernel\power\suspend.c
- static void suspend_finish(void)
- {
- //唤醒应用程序
- suspend_thaw_processes();
- //通知关注这个事件的App程序,对全局pm_chain_head->head中的每一个都调用其notifier_call()
- pm_notifier_call_chain(PM_POST_SUSPEND);
- //返回用户空间
- pm_restore_console();
- }
总结
本文主要和大家分享了OpenHarmony电源管理子系统中关于亮灭屏的实现细节,包括NAPI接口、PowerMgr的流程以及Kernel实现等,做了较为详细的代码说明,希望通过本文您能初步掌握电源管理子系统的关键功能和核心流程。关于OpenHarmony其它子系统的分析,请关注后续文章。