文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-12-02 13:05

关注

想了解更多内容,请访问:

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

https://harmonyos.51cto.com

1 简介

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

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

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

1.1 电源管理相关

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

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

1.2 OpenHarmony架构图


1.3 电源管理子系统架构图


2 知识准备


2.1 电源状态

  1. On (on) S0-Working
  2. Standby (standby) S1- CPU and RAM are powed but not executed
  3. Suspend to Ram (mem) S2- RAM is powered and the running content is saved to RAM
  4. 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功耗由大到小,唤醒速度由快到慢。

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

  1. //ubuntu系统: 
  2. #cat /sys/power/state  
  3. freeze standby mem disk 
  4. //rk3566和自研板 
  5. # cat /sys/power/state 
  6. freeze mem 

 切换为睡眠模式:

  1. #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

  1. 一个sysfs文件:/sys/power/wake_lock,用户程序向文件写入一个字符串,即可创建一个wakelock,该字符串就是wakelock的名字。该wakelock可以阻止系统进入低功耗模式。
  2. 一个sysfs文件:/sys/power/wake_unlock,用户程序向文件写入相同的字符串,即可注销一个wakelock。
  3. 当系统中所有的wakelock都注销后,系统可以自动进入低功耗状态。
  4. 向内核其它driver也提供了wakelock的创建和注销接口,允许driver创建wakelock以阻止睡眠、注销wakelock以允许睡眠。

3 整体流程代码

3.1 NAPI接口

在原来power模块增加休眠和唤醒接口

  1. static napi_value PowerInit(napi_env env, napi_value exports) 
  2.     POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: enter", __func__); 
  3.     napi_property_descriptor desc[] = { 
  4.         DECLARE_NAPI_FUNCTION("shutdownDevice", ShutdownDevice), 
  5.         DECLARE_NAPI_FUNCTION("rebootDevice", RebootDevice), 
  6.         DECLARE_NAPI_FUNCTION("isScreenOn", IsScreenOn), 
  7.         DECLARE_NAPI_FUNCTION("wakeupDevice", WakeupDevice),   //新增的唤醒接口 
  8.         DECLARE_NAPI_FUNCTION("suspendDevice", SuspendDevice), //新增的休眠接口 
  9.     }; 
  10.     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); 
  11.     POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: exit", __func__); 
  12.  
  13.     return exports; 
  14. //唤醒接口实现,应用在调用此接口前先调用power模块已提供的IsScreenOn接口 
  15. static napi_value WakeupDevice(napi_env env, napi_callback_info info) 
  16.     POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: enter, %{public}s", __func__, "wake up device"); 
  17.     size_t argc = 1; 
  18.     napi_value args[1] = { 0 }; 
  19.     napi_value jsthis; 
  20.     void *data = nullptr; 
  21.  
  22.     napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data); 
  23.     NAPI_ASSERT(env, (status == napi_ok) && (argc >= 1), "failed to get cb info"); 
  24.     napi_valuetype type = napi_undefined; 
  25.     NAPI_CALL(env, napi_typeof(env, args[0], &type)); 
  26.     NAPI_ASSERT(env, type == napi_string, "wrong argument type. string expected."); 
  27.  
  28.     char reason[REASON_MAX] = { 0 }; 
  29.     size_t reasonLen = 0; 
  30.     status = napi_get_value_string_utf8(env, args[0], reason, REASON_MAX - 1, &reasonLen); 
  31.     if (status != napi_ok) { 
  32.         POWER_HILOGE(MODULE_JS_NAPI, "%{public}s: get reason failed", __func__); 
  33.         return nullptr; 
  34.     } 
  35.     g_powerMgrClient.WakeupDevice(WakeupDeviceType::WAKEUP_DEVICE_APPLICATION,std::string(reason)); 
  36.     POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: reason %{public}s, exit", __func__, reason); 
  37.     return nullptr; 
  38. //休眠接口,应用在调用此接口前先调用power模块已提供的IsScreenOn接口 
  39. static napi_value SuspendDevice(napi_env env, napi_callback_info info) 
  40.     POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: enter, %{public}s", __func__, "suspend device"); 
  41.     size_t argc = 1; 
  42.     napi_value args[1] = { 0 }; 
  43.     napi_value jsthis; 
  44.     void *data = nullptr; 
  45.  
  46.     napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data); 
  47.     NAPI_ASSERT(env, (status == napi_ok) && (argc >= 1), "failed to get cb info"); 
  48.     napi_valuetype type = napi_undefined; 
  49.     NAPI_CALL(env, napi_typeof(env, args[0], &type)); 
  50.     NAPI_ASSERT(env, type == napi_string, "wrong argument type. string expected."); 
  51.  
  52.     char reason[REASON_MAX] = { 0 }; 
  53.     size_t reasonLen = 0; 
  54.     status = napi_get_value_string_utf8(env, args[0], reason, REASON_MAX - 1, &reasonLen); 
  55.     if (status != napi_ok) { 
  56.         POWER_HILOGE(MODULE_JS_NAPI, "%{public}s: get reason failed", __func__); 
  57.         return nullptr; 
  58.     } 
  59.     g_powerMgrClient.SuspendDevice(SuspendDeviceType::SUSPEND_DEVICE_REASON_APPLICATION,true); 
  60.     POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: reason %{public}s, exit", __func__, reason); 
  61.     return nullptr; 

3.2 PowerMgr接口

接口处理的后续流程比较通用,powermgrclient->powermgrserviceproxy->powermgrservicestub->powermgrservice。

powermgr内部流转到suspend和wakeup:

  1. base\powermgr\power_manager\services\native\src\actions\default\device_state_action.cpp: 
  2. //系统挂起时先灭屏再休眠 
  3. void DeviceStateAction::Suspend(int64_t callTimeMs, SuspendDeviceType type, uint32_t flags) 
  4.     DisplayManager::SetScreenState(ScreenState::SCREEN_STATE_OFF);//调用displaymgr的屏幕关闭 
  5.     SystemSuspendController::GetInstance().EnableSuspend();//系统休眠 
  6. //唤醒系统时先唤醒再亮屏 
  7. void DeviceStateAction::Wakeup(int64_t callTimeMs, WakeupDeviceType type, const string& details, 
  8.     const string& pkgName) 
  9.     SystemSuspendController::GetInstance().DisableSuspend();//系统唤醒 
  10.     DisplayManager::SetScreenState(ScreenState::SCREEN_STATE_ON);//调用displaymgr的屏幕打开 

 上面的suspend/wakeup函数分两支,其中一支是调用DisplayManager的屏幕打开、关闭功能实现不完整,Display驱动部分引用的动态库,代码部分和实际日志不符。另一支是系统休眠、唤醒处理。

3.3 系统休眠&唤醒

先看系统休眠、唤醒处理,目前自研板支持两种模式freeze mem 一级待机和二级待机,而powermgr是使用的mem模式。

  1. //base\powermgr\power_manager\services\native\src\actions\default\system_suspend_controller.cpp 
  2. //目前系统目录没有下面这两个文件影响休眠和唤醒功能: 
  3. 1)/sys/power/wake_lock  //该wakelock可以阻止系统进入低功耗模式。 
  4. 2)/sys/power/wake_unlock //写入相同的字符串,即可注销一个wakelock 
  5. //挂起,系统进入低功耗模式 
  6. void SystemSuspendController::EnableSuspend() 
  7.     std::lock_guard lock(mutex_); 
  8.     sc_->EnableSuspend();//睡眠的具体操作。 
  9.     if (!suspendEnabled_) { 
  10.         rlh_->Release(WAKEUP_HOLDER);  //wake_unlock写入"OHOSPowerMgr.WakeupHolder";允许睡眠 
  11.         suspendEnabled_ = true
  12.     } 
  13. //唤醒,即阻止系统进入低功耗模式。 
  14. void SystemSuspendController::DisableSuspend() 
  15.     std::lock_guard lock(mutex_); 
  16.     if (suspendEnabled_) { 
  17.         rlh_->Acquire(WAKEUP_HOLDER);  //wake_lock写入"OHOSPowerMgr.WakeupHolder";阻止睡眠 
  18.         suspendEnabled_ = false
  19.     } 
  20. //base\powermgr\power_manager\services\native\src\actions\default\suspend\suspend_controller.cpp: 
  21. static constexpr const char * const SUSPEND_STATE = "mem"
  22. static constexpr const char * const SUSPEND_STATE_PATH = "/sys/power/state"
  23. static constexpr const char * const WAKEUP_COUNT_PATH = "/sys/power/wakeup_count"
  24. void SuspendController::EnableSuspend() 
  25.     suspend_->Start();//启动AutoSuspendLoop线程 
  26.     POWER_HILOGI(MODULE_SERVICE, "AutoSuspend enabled"); 
  27. void SuspendController::AutoSuspend::AutoSuspendLoop() 
  28.     while (true) { 
  29.         std::this_thread::sleep_for(waitTime_); 
  30.         const std::string wakeupCount = WaitWakeupCount();  //在rk3566和自研板上阻塞在读取/sys/power/wakeup_count文件,应该是系统不支持 
  31.         if (wakeupCount.empty()) { 
  32.             continue
  33.         } 
  34.         waitingFunc_();//阻塞函数,只有等到休眠条件才会继续执行。 
  35.         if (!WriteWakeupCount(wakeupCount)) {//写入wakeupcount 
  36.             continue
  37.         } 
  38.         bool success = SuspendEnter();//写入mem 
  39.         if (!success) { 
  40.             POWER_HILOGE(MODULE_SERVICE, "Start suspend failed!"); 
  41.         } 
  42.     } 
  43. //读取wakeupcount 
  44. std::string SuspendController::AutoSuspend::WaitWakeupCount() 
  45.     if (wakeupCountFd < 0) { 
  46.         wakeupCountFd = UniqueFd(TEMP_FAILURE_RETRY(open(WAKEUP_COUNT_PATH, O_RDWR | O_CLOEXEC))); 
  47.     } 
  48.     std::string wakeupCount; 
  49.     bool ret = LoadStringFromFd(wakeupCountFd, wakeupCount); 
  50.     if (!ret) { 
  51.         POWER_HILOGW(MODULE_SERVICE, "Read wakeup count failed!"); 
  52.         return std::string(); 
  53.     } 
  54.     return wakeupCount; 
  55. //写入wakeupcount 
  56. bool SuspendController::AutoSuspend::WriteWakeupCount(std::string wakeupCount) 
  57.     if (wakeupCountFd < 0) { 
  58.         return false
  59.     } 
  60.     bool ret = SaveStringToFd(wakeupCountFd, wakeupCount.c_str()); 
  61.     if (!ret) { 
  62.         POWER_HILOGE(MODULE_SERVICE, "Failed to write the wakeup count!"); 
  63.     } 
  64.     return ret; 
  65. //通过向/sys/power/state写入mem实现挂起功能。 
  66. bool SuspendController::AutoSuspend::SuspendEnter() 
  67.     static bool inited = false
  68.     static UniqueFd suspendStateFd(TEMP_FAILURE_RETRY(open(SUSPEND_STATE_PATH, O_RDWR | O_CLOEXEC))); 
  69.     if (!inited) { 
  70.         if (suspendStateFd < 0) { 
  71.             POWER_HILOGE(MODULE_SERVICE, "Failed to open the suspending state fd!"); 
  72.             return false
  73.         } 
  74.         inited = true
  75.     } 
  76.     bool ret = SaveStringToFd(suspendStateFd, SUSPEND_STATE); 
  77.     if (!ret) { 
  78.         POWER_HILOGE(MODULE_SERVICE, "Failed to write the suspending state!"); 
  79.     } 
  80.     return ret; 

3.4 屏幕开关

再看调用DisplayManager的屏幕打开、关闭功能实现,有一部分是私有实现,只提供了动态库,最终会调用hdf_disp驱动接口进行屏幕的打开、关闭操作。

  1. //base\powermgr\display_manager\service\native\src\screen_action.cpp: 
  2. bool ScreenAction::SetPowerState(ScreenState state __attribute__((__unused__))) 
  3.     int32_t dispErr_ = DISPLAY_SUCCESS; 
  4.     if (!hdiFuncs_) { 
  5.         DISPLAY_HILOGE(MODULE_SERVICE, "Invalid device functions"); 
  6.         return false
  7.     } 
  8.     if (ScreenState::SCREEN_STATE_ON == state) 
  9.     { 
  10.         dispErr_ = hdiFuncs_->SetDisplayPowerStatus(0,DispPowerStatus::POWER_STATUS_ON); 
  11.     } 
  12.     else 
  13.     { 
  14.         dispErr_ = hdiFuncs_->SetDisplayPowerStatus(0,DispPowerStatus::POWER_STATUS_OFF); 
  15.     }     
  16.     DISPLAY_HILOGE(MODULE_SERVICE, "SetDisplayPowerStatus:%{public}u result:%{public}d",ToUnderlying(state),dispErr_); 
  17.     return dispErr_ == DISPLAY_SUCCESS; 
  18. //drivers\peripheral\display\interfaces\include\display_device.h: 
  19. SetDisplayPowerStatus:接口说明: 
  20.      
  21.     int32_t (*SetDisplayPowerStatus)(uint32_t devId, DispPowerStatus status); 

下面是调用hdf_disp驱动部分,依然属于私有实现,这部分可以看到执行日志,但是代码还不完整:

  1. //drivers\peripheral\display\hal\disp_hal.c: 
  2. static int32_t DispCmdSend(const uint32_t cmd, struct HdfSBuf *reqData, struct HdfSBuf *respData) 
  3.     struct HdfIoService *dispService = NULL
  4.     HDF_LOGE("%s:add by wangkui", __func__); 
  5.     dispService = HdfIoServiceBind(DISP_SERVICE_NAME);//此处是找hdf_disp驱动服务。 
  6.     if ((dispService == NULL) || (dispService->dispatcher == NULL) || (dispService->dispatcher->Dispatch == NULL)) { 
  7.         HDF_LOGE("%s:bad remote service found modified by wangkui", __func__); 
  8.         goto EXIT; 
  9.     } 
  10.     int32_t ret = dispService->dispatcher->Dispatch(&dispService->object, cmd, reqData, respData); 
  11.     if (ret != DISPLAY_SUCCESS) { 
  12.         HDF_LOGE("%s: cmd=%u, ret=%d", __func__, cmd, ret); 
  13.         goto EXIT; 
  14.     } 
  15.     HDF_LOGI("%s: cmd=%u, ret=%d", __func__, cmd, ret); 
  16.     HdfIoServiceRecycle(dispService); 
  17.     return DISPLAY_SUCCESS; 
  18.  
  19. EXIT: 
  20.     HdfIoServiceRecycle(dispService); 
  21.     return DISPLAY_FAILURE; 
  22. //drivers\framework\core\shared\src\hdf_io_service.c: 
  23. struct HdfIoService *HdfIoServiceBind(const char *serviceName) 
  24.     return HdfIoServiceAdapterObtain(serviceName); 
  25. //drivers\framework\core\adapter\syscall\src\hdf_syscall_adapter.c: 
  26. struct HdfIoService *HdfIoServiceAdapterObtain(const char *serviceName) 
  27.     struct HdfSyscallAdapter *adapter = NULL
  28.     struct HdfIoService *ioService = NULL
  29.     char *devNodePath = NULL
  30.     char *realPath = NULL
  31.  
  32.     const char *devPath = DEV_NODE_PATH; 
  33.     if (access(DEV_NODE_PATH, F_OK) != 0) { 
  34.         devPath = DEV_PATH; 
  35.     } 
  36.  
  37.     devNodePath = OsalMemCalloc(PATH_MAX); 
  38.     realPath = OsalMemCalloc(PATH_MAX); 
  39.     if (devNodePath == NULL || realPath == NULL) { 
  40.         HDF_LOGE("%s: out of memory", __func__); 
  41.         goto out
  42.     } 
  43.  
  44.     if (sprintf_s(devNodePath, PATH_MAX - 1, "%s%s", devPath, serviceName) < 0) { 
  45.         HDF_LOGE("Failed to get the node path"); 
  46.         goto out
  47.     } 
  48.  
  49.     if (realpath(devNodePath, realPath) == NULL) {//此处发现找不到hdf_disp文件 
  50.         HDF_LOGE("%s: file name %{public}s is invalid", __func__, devNodePath); 
  51.         if (HdfLoadDriverByServiceName(serviceName) != HDF_SUCCESS) {//通过hdf_disp服务名查找 
  52.             HDF_LOGE("%s: load %{public}s driver failed,", __func__, serviceName); 
  53.             goto out
  54.         } 
  55.         if (realpath(devNodePath, realPath) == NULL) { 
  56.             HDF_LOGE("%s: file name %{public}s is invalid", __func__, devNodePath); 
  57.             goto out
  58.         } 
  59.     } 
  60.  
  61.     adapter = (struct HdfSyscallAdapter *)OsalMemCalloc(sizeof(struct HdfSyscallAdapter)); 
  62.     if (adapter == NULL) { 
  63.         HDF_LOGE("Failed to allocate SyscallAdapter"); 
  64.         goto out
  65.     } 
  66.  
  67.     DListHeadInit(&adapter->listenerList); 
  68.     if (OsalMutexInit(&adapter->mutex)) { 
  69.         HDF_LOGE("%s: Failed to create mutex", __func__); 
  70.         OsalMemFree(adapter); 
  71.         goto out
  72.     } 
  73.  
  74.     adapter->fd = open(realPath, O_RDWR); 
  75.     if (adapter->fd < 0) { 
  76.         HDF_LOGE("Open file node %{public}s failed, (%d)%{public}s", realPath, errno, strerror(errno)); 
  77.         OsalMutexDestroy(&adapter->mutex); 
  78.         OsalMemFree(adapter); 
  79.         goto out
  80.     } 
  81.     ioService = &adapter->super; 
  82.     static struct HdfIoDispatcher dispatch = { 
  83.         .Dispatch = HdfSyscallAdapterDispatch, 
  84.     }; 
  85.     ioService->dispatcher = &dispatch; 
  86. out
  87.     OsalMemFree(devNodePath); 
  88.     OsalMemFree(realPath); 
  89.     return ioService; 
  90. int32_t HdfLoadDriverByServiceName(const char *serviceName) 
  91.     int32_t ret = HDF_FAILURE; 
  92.     struct HdfSBuf *data = NULL
  93.     if (serviceName == NULL || strcmp(serviceName, DEV_MGR_NODE) == 0) { 
  94.         HDF_LOGE("failed to get %s service,call self!!!", serviceName);//如果是dev_mgr则返回,防止死循环。 
  95.         return ret; 
  96.     } 
  97.     struct HdfIoService *ioService = HdfIoServiceBind(DEV_MGR_NODE);//这里找dev_mgr,根据日志看也没找到,所以没发现hdf_disp服务。 
  98.     if (ioService == NULL) { 
  99.         HDF_LOGE("failed to get %s service", DEV_MGR_NODE); 
  100.         return ret; 
  101.     } 
  102.     data = HdfSBufObtainDefaultSize(); 
  103.     if (data == NULL) { 
  104.         HDF_LOGE("failed to obtain sbuf data"); 
  105.         ret = HDF_DEV_ERR_NO_MEMORY; 
  106.         goto out
  107.     } 
  108.     if (!HdfSbufWriteString(data, serviceName)) { 
  109.         HDF_LOGE("failed to write sbuf"); 
  110.         ret = HDF_FAILURE; 
  111.         goto out
  112.     } 
  113.     ret = ioService->dispatcher->Dispatch(&ioService->object, DEVMGR_LOAD_SERVICE, data, NULL); 
  114.     if (ret != HDF_SUCCESS) { 
  115.         HDF_LOGE("failed to load khdf driver %s", serviceName); 
  116.     } 
  117. out
  118.     HdfIoServiceRecycle(ioService); 
  119.     HdfSBufRecycle(data); 
  120.     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目录下的文件都是按照此方法注册,且根据版本构建的宏定义开关可以有选择性的创建。

  1. #define power_attr(_name) \ 
  2. static struct kobj_attribute _name##_attr = {   \ 
  3.     .attr   = {             \ 
  4.         .name = __stringify(_name), \ 
  5.         .mode = 0644,           \ 
  6.     },                  \ 
  7.     .show   = _name##_show,         \ 
  8.     .store  = _name##_store,        \ 
  9.  
  10. power_attr(wake_lock); 
  11. power_attr(wake_unlock); 
  12. //根据宏定义开关注册sleep、autosleep、suspend、wakelock等。 
  13. static struct attribute * g[] = { 
  14.     &state_attr.attr, 
  15. #ifdef CONFIG_PM_TRACE 
  16.     &pm_trace_attr.attr, 
  17.     &pm_trace_dev_match_attr.attr, 
  18. #endif 
  19. #ifdef CONFIG_PM_SLEEP 
  20.     &pm_async_attr.attr, 
  21.     &wakeup_count_attr.attr, 
  22. #ifdef CONFIG_SUSPEND 
  23.     &mem_sleep_attr.attr, 
  24. #endif 
  25. #ifdef CONFIG_PM_AUTOSLEEP 
  26.     &autosleep_attr.attr, 
  27. #endif 
  28. #ifdef CONFIG_PM_WAKELOCKS //宏定义必须打开才会创建 
  29.     &wake_lock_attr.attr, 
  30.     &wake_unlock_attr.attr, 
  31. #endif 
  32. #ifdef CONFIG_PM_SLEEP_DEBUG 
  33.     &pm_test_attr.attr, 
  34.     &pm_print_times_attr.attr, 
  35.     &pm_wakeup_irq_attr.attr, 
  36.     &pm_debug_messages_attr.attr, 
  37. #endif 
  38. #endif 
  39. #ifdef CONFIG_FREEZER 
  40.     &pm_freeze_timeout_attr.attr, 
  41. #endif 
  42.     NULL
  43. }; 
  44. static const struct attribute_group attr_group = { 
  45.     .attrs = g, 
  46. }; 
  47. static const struct attribute_group *attr_groups[] = { 
  48.     &attr_group, 
  49. #ifdef CONFIG_PM_SLEEP 
  50.     &suspend_attr_group, 
  51. #endif 
  52.     NULL
  53. }; 
  54. static int __init pm_init(void) 
  55.     int error = pm_start_workqueue(); 
  56.     if (error) 
  57.         return error; 
  58.     hibernate_image_size_init(); 
  59.     hibernate_reserved_size_init(); 
  60.     pm_states_init(); 
  61.     power_kobj = kobject_create_and_add("power"NULL);//先创建power目录 
  62.     if (!power_kobj) 
  63.         return -ENOMEM; 
  64.     error = sysfs_create_groups(power_kobj, attr_groups);//创建power目录下的其它设备节点。 
  65.     if (error) 
  66.         return error; 
  67.     pm_print_times_init(); 
  68.     return pm_autosleep_init(); 
  69.  
  70. core_initcall(pm_init); 

1.2 创建一个wakelock

入口在kernel/power/main.c中实现:

  1. static ssize_t wake_lock_store(struct kobject *kobj, 
  2.                    struct kobj_attribute *attr, 
  3.                    const char *buf, size_t n) 
  4.     int error = pm_wake_lock(buf); 
  5.     return error ? error : n; 

 上面直接调用kernel\linux-4.19\kernel\power\wakelock.c中的pm_wake_lock:

  1. //用于上报一个wakeup event,输入参数为一个字符串,如:"wakelocktest 1000" 
  2. int pm_wake_lock(const char *buf) 
  3.     const char *str = buf; 
  4.     struct wakelock *wl; 
  5.     u64 timeout_ns = 0; 
  6.     size_t len; 
  7.     int ret = 0; 
  8.  
  9.     //检查调用的进程是否有执行阻止系统休眠的权限,PowerMgr打包在foundation进程,所以需要修改foundation进程的权限,增加BLOCK_SUSPEND 
  10.     //./foundation/appexecfwk/standard/sa_profile/foundation.rc:    capabilities SYS_PTRACE KILL SYS_BOOT 
  11.     //./foundation/appexecfwk/standard/sa_profile/foundation.cfg:            "caps" : ["SYS_PTRACE""KILL""SYS_BOOT"
  12.     if (!capable(CAP_BLOCK_SUSPEND)) 
  13.         return -EPERM; 
  14.     //解析字符串,取出wakelock的名字和超时时间,超时时间可以不带。 
  15.     while (*str && !isspace(*str)) 
  16.         str++; 
  17.  
  18.     len = str - buf; 
  19.     if (!len) 
  20.         return -EINVAL; 
  21.  
  22.     if (*str && *str != '\n') { 
  23.          
  24.         ret = kstrtou64(skip_spaces(str), 10, &timeout_ns); 
  25.         if (ret) 
  26.             return -EINVAL; 
  27.     } 
  28.  
  29.     mutex_lock(&wakelocks_lock); 
  30.     //向wakelock维护的红黑树tree中添加新的wakelock,且注册wakeup source,name就是入参的buf。 
  31.     wl = wakelock_lookup_add(buf, len, true); 
  32.     if (IS_ERR(wl)) { 
  33.         ret = PTR_ERR(wl); 
  34.         goto out
  35.     } 
  36.     //1)__pm_stay_awake,通知PM core,注册的wakeup source产生了wakeup event,且正在处理,因此不允许系统suspend; 
  37.     //2)__pm_relax,通知PM core,注册的wakeup source没有正在处理的wakeup event,允许系统suspend; 
  38.     //3)__pm_wakeup_event,为上边两个接口的功能组合,通知PM core,注册的wakeup source产生了wakeup event,会在timeout_ms毫秒内处理结束,由PM core自动调用__pm_relax。 
  39.     if (timeout_ns) { 
  40.         u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1; 
  41.  
  42.         do_div(timeout_ms, NSEC_PER_MSEC); 
  43.         __pm_wakeup_event(wl->ws, timeout_ms); 
  44.     } else { 
  45.         __pm_stay_awake(wl->ws); 
  46.     } 
  47.  
  48.     wakelocks_lru_most_recent(wl);//wakelocks有GC机制,通过LRU策略进行淘汰长时间不活动的wakelock 
  49.  
  50.  out
  51.     mutex_unlock(&wakelocks_lock); 
  52.     return ret; 

1.3 注销一个wakelock

入口在kernel/power/main.c中实现:

  1. static ssize_t wake_unlock_store(struct kobject *kobj, 
  2.                  struct kobj_attribute *attr, 
  3.                  const char *buf, size_t n) 
  4.     int error = pm_wake_unlock(buf); 
  5.     return error ? error : n; 

 上面直接调用kernel\linux-4.19\kernel\power\wakelock.c中的pm_wake_unlock:

  1. //入参为字符串,跟创建wakelock一样的名字。 
  2. int pm_wake_unlock(const char *buf) 
  3.     struct wakelock *wl; 
  4.     size_t len; 
  5.     int ret = 0; 
  6.  
  7.     if (!capable(CAP_BLOCK_SUSPEND))//检查权限 
  8.         return -EPERM; 
  9.     //解析字符串,获取wakelock名字 
  10.     len = strlen(buf); 
  11.     if (!len) 
  12.         return -EINVAL; 
  13.  
  14.     if (buf[len-1] == '\n'
  15.         len--; 
  16.  
  17.     if (!len) 
  18.         return -EINVAL; 
  19.  
  20.     mutex_lock(&wakelocks_lock); 
  21.     //查找是否有相同name的wakelock。如果有,直接返回wakelock的指针;如果没有,退出。 
  22.     wl = wakelock_lookup_add(buf, len, false); 
  23.     if (IS_ERR(wl)) { 
  24.         ret = PTR_ERR(wl); 
  25.         goto out
  26.     } 
  27.     __pm_relax(wl->ws);//通知PM core,注册的wakeup source没有正在处理的wakeup event,允许系统suspend; 
  28.  
  29.     //wakelocks有GC机制,通过LRU策略进行淘汰长时间不活动的wakelock 
  30.     wakelocks_lru_most_recent(wl); 
  31.     wakelocks_gc();//执行GC回收。 
  32.  
  33.  out
  34.     mutex_unlock(&wakelocks_lock); 
  35.     return ret; 

还有wakelock的查看接口,本文不列出了。

4.2 wakeup_count

2.1 Kernel节点文件创建

参考wakelocks。

2.2 读取wakeup count

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

  1.  
  2. bool pm_get_wakeup_count(unsigned int *count, bool block) 
  3.     unsigned int cnt, inpr; 
  4.     pr_debug("pm_get_wakeup_count block: %d\n", block); 
  5.     if (block) { 
  6.         DEFINE_WAIT(wait); 
  7.  
  8.         for (;;) { 
  9.             prepare_to_wait(&wakeup_count_wait_queue, &wait, 
  10.                     TASK_INTERRUPTIBLE); 
  11.             split_counters(&cnt, &inpr); 
  12.             if (inpr == 0 || signal_pending(current)) 
  13.                 break; 
  14.             pm_print_active_wakeup_sources();//debug日志打印出active的事件,以此判断阻塞在何处。 
  15.             schedule(); 
  16.         } 
  17.         finish_wait(&wakeup_count_wait_queue, &wait); 
  18.     } 
  19.  
  20.     split_counters(&cnt, &inpr); 
  21.     *count = cnt; 
  22.     return !inpr; 
  23. static atomic_t combined_event_count = ATOMIC_INIT(0); 
  24. #define IN_PROGRESS_BITS    (sizeof(int) * 4)    //16 
  25. #define MAX_IN_PROGRESS     ((1 << IN_PROGRESS_BITS) - 1)    //0xFFFF 
  26. static void split_counters(unsigned int *cnt, unsigned int *inpr) 
  27.     unsigned int comb = atomic_read(&combined_event_count); 
  28.     //高16位存储已处理完的wakeup个数,低16位存储正在处理(即Active)的wakeup个数 
  29.     //秒!正在处理的wakeup event和已处理的wakeup event在一个变量中维护。 
  30.     *cnt = (comb >> IN_PROGRESS_BITS); 
  31.     *inpr = comb & MAX_IN_PROGRESS; 

2.3 写入wakeup count

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

  1.  
  2. bool pm_save_wakeup_count(unsigned int count
  3.     unsigned int cnt, inpr; 
  4.     unsigned long flags; 
  5.  
  6.     events_check_enabled = false
  7.     raw_spin_lock_irqsave(&events_lock, flags); 
  8.     split_counters(&cnt, &inpr); 
  9.     if (cnt == count && inpr == 0) { 
  10.         saved_count = count
  11.         events_check_enabled = true
  12.     } 
  13.     raw_spin_unlock_irqrestore(&events_lock, flags); 
  14.     return events_check_enabled; 

4.3 suspend

向/sys/power/state写入mem后就进入Kernel层的suspend流程,入口还是kernel\linux-4.19\kernel\power\main.c:

  1. //切换休眠状态 
  2. static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, 
  3.                const char *buf, size_t n) 
  4.     suspend_state_t state; 
  5.     int error; 
  6.  
  7.     error = pm_autosleep_lock(); 
  8.     if (error) 
  9.         return error; 
  10.  
  11.     if (pm_autosleep_state() > PM_SUSPEND_ON) { 
  12.         error = -EBUSY; 
  13.         goto out
  14.     } 
  15.  
  16.     state = decode_state(buf, n);//将“mem”转为对应的数字 
  17.     if (state < PM_SUSPEND_MAX) { 
  18.         if (state == PM_SUSPEND_MEM) 
  19.             state = mem_sleep_current; 
  20.  
  21.         error = pm_suspend(state);//核心的kernel休眠流程 
  22.     } else if (state == PM_SUSPEND_MAX) { 
  23.         error = hibernate(); 
  24.     } else { 
  25.         error = -EINVAL; 
  26.     } 
  27.  
  28.  out
  29.     pm_autosleep_unlock(); 
  30.     return error ? error : n; 

suspend实现非常复杂,本文只列出关键的流程:

  1. //kernel\linux-4.19\kernel\power\suspend.c 
  2. int pm_suspend(suspend_state_t state) 
  3.     error = enter_state(state); 
  4. static int enter_state(suspend_state_t state) 
  5.     trace_suspend_resume(TPS("suspend_enter"), state, true); 
  6.     if (state == PM_SUSPEND_TO_IDLE) { 
  7.     } else if (!valid_state(state)) {//检查平台是否支持电源管理,即全局suspend_ops有没有被赋值,并调用其suspend_ops->valid() 
  8.         return -EINVAL; 
  9.     } 
  10.     error = suspend_prepare(state);//准备挂起 
  11.     error = suspend_devices_and_enter(state);//让设备进入suspend状态 
  12.     suspend_finish();  
  13. //kernel\linux-4.19\kernel\power\suspend.c 
  14. static int suspend_prepare(suspend_state_t state) 
  15.     //kernel\linux-4.19\kernel\power\console.c 
  16.     //切换挂起、恢复执行内核触发的VT开关,切换到SUSPEND_CONSOLE 
  17.     pm_prepare_console(); 
  18.     //kernel/power/main.c 
  19.     //通知驱动程序准备suspend 
  20.     error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls); 
  21.     //kernel\linux-4.19\kernel\power\power.h 
  22.     //冻结App和内核线程 
  23.     error = suspend_freeze_processes(); 
  24. //kernel\linux-4.19\kernel\power\suspend.c 
  25. ////挂起设备并进入系统睡眠状态 
  26. int suspend_devices_and_enter(suspend_state_t state) 
  27.     //如果平台相关的代码有begin函数就去调用它,suspend_ops->begin(state) 
  28.     error = platform_suspend_begin(state); 
  29.     //kernel\linux-4.19\kernel\printk\printk.c 
  30.     //挂起console子系统,此时不能用printk()调试。 
  31.     suspend_console(); 
  32.     suspend_test_start(); 
  33.     error = dpm_suspend_start(PMSG_SUSPEND);     
  34.     suspend_test_finish("suspend devices"); 
  35.      
  36.     do { 
  37.         //关闭核心模块,如cpu等,并设置唤醒源,如果电源键按下则会进入唤醒流程。 
  38.         error = suspend_enter(state, &wakeup); 
  39.     } while (!error && !wakeup && platform_suspend_again(state)); 
  40.  
  41.  Resume_devices: 
  42.     suspend_test_start(); 
  43.     dpm_resume_end(PMSG_RESUME); 
  44.     suspend_test_finish("resume devices"); 
  45.     resume_console(); 
  46. //kernel\linux-4.19\kernel\power\suspend.c 
  47. static void suspend_finish(void) 
  48.     //唤醒应用程序 
  49.     suspend_thaw_processes(); 
  50.     //通知关注这个事件的App程序,对全局pm_chain_head->head中的每一个都调用其notifier_call() 
  51.     pm_notifier_call_chain(PM_POST_SUSPEND); 
  52.     //返回用户空间 
  53.     pm_restore_console(); 

总结

本文主要和大家分享了OpenHarmony电源管理子系统中关于亮灭屏的实现细节,包括NAPI接口、PowerMgr的流程以及Kernel实现等,做了较为详细的代码说明,希望通过本文您能初步掌握电源管理子系统的关键功能和核心流程。关于OpenHarmony其它子系统的分析,请关注后续文章。

想了解更多内容,请访问:

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

https://harmonyos.51cto.com

 

来源:鸿蒙社区内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯