#星光计划2.0# OpenHarmony 源码解析之电源管理亮灭屏功能

您所在的位置:网站首页 鸿蒙电池管理系统怎么关闭 #星光计划2.0# OpenHarmony 源码解析之电源管理亮灭屏功能

#星光计划2.0# OpenHarmony 源码解析之电源管理亮灭屏功能

2024-07-06 17:09| 来源: 网络整理| 查看: 265

作者:王奎

【本文正在参与51CTO HarmonyOS技术社区创作者激励计划-星光计划2.0】

1 简介

电源管理子系统是OpenHarmony的基本能力子系统,有电池服务组件、显示控制组件和电源管理服务组件,主要提供如下功能:

重启系统。 管理休眠运行锁。 系统电源状态查询。 充电和电池状态查询和上报。 亮灭屏管理和亮度调节。

本文重点分析亮灭屏功能,包括NAPI接口、PowerMgr和Kernel层的实现。

1.1 电源管理相关

《OpenHarmony 源码解析之电源管理子系统 》

《OpenHarmony 源码解析之电源管理亮灭屏功能》

1.2 OpenHarmony架构图

OpenHarmony.png

1.3 电源管理子系统架构图

PowerMgr.png

2 知识准备

wakeupevent.png

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功耗由大到小,唤醒速度由快到慢。

模式 描述 freeze 冻结I/O设备,将它们置于低功耗状态,使处理器进入空闲状态,唤醒最快,耗电比其它standby, mem, disk方式高 standby 除了冻结I/O设备外,还会暂停系统,唤醒较快,耗电比其它 mem, disk方式高 mem 将运行状态数据存到内存,并关闭外设,进入等待模式,唤醒较慢,耗电比disk方式高 disk 将运行状态数据存到硬盘,然后关机,唤醒最慢. 对于嵌入式系统,由于没有硬盘,所以一般不支持

查看系统支持的睡眠方式:

//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:接口说明: /** * @brief Sets the power status. * * When the OS enters the sleep mode or wakes up from the sleep mode, the display service or * the power management module can set the power status of the display device, so that the driver IC * of the device can normally enter the specified state. * * @param devId Indicates the ID of a display device. The value ranges from 0 to 4, where 0 indicates * the first display device and 4 indicates the last display device. * @param status Indicates the power status to set. The display service determines whether to set the * display device to the on or off state based on this setting. For details, see @link PowerStatus}. * * @return Returns 0 if the operation is successful; returns an error code defined in {@link DispErrCode} otherwise. * @since 1.0 * @version 1.0 */ 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 4.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); 4.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') { /* Find out if there's a valid timeout string appended. */ 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; } 4.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 4.2.1 Kernel节点文件创建

参考wakelocks。

4.2.2 读取wakeup count

从/sys/power/wakeup_count读取wakeup_count,当有正在处理的event事件此处可能会阻塞。

/** *pm_get_wakeup_count-读取已注册的wakeup事件数。 *@count:用于存储值的地址。 *@block:是否阻塞。 *将注册的wakeup事件数存储在@count中。如果@block为true,阻止直到当前的wakeup事件数已处理的值为零。 *如果当前正在处理的wakeup事件数为非零范围“false”。否则返回“true”。 */ 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); *inpr = comb & MAX_IN_PROGRESS; } 4.2.3 写入wakeup count

上面读取的wakeup_count写入到/sys/power/wakeup_count,如果count不同,则说明有新的event事件产生,或者有正在处理的event事件,即inpr不为零,则说明此时不适宜执行suspend。

/** *pm_save_wakeup_count-保存当前已注册的wakeup事件数。 *@count:与当前已注册wakeup事件数进行比较的值。 *如果@count等于当前注册的wakeup事件数,且当前正在处理的wakeup事件数为零,并将当前的wakeup count保存在saved count变量中,启用wakeup事件检测。 *否则禁用wakeup事件检测并返回“false”。 */ 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其它子系统的分析,请关注后续文章。

更多原创内容请关注:开鸿 HarmonyOS 学院

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

想了解更多关于鸿蒙的内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com/#bkwz

::: hljs-center

21_9.jpg

:::



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3