1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "thermal_zone_manager.h"
17
18 #include <cstdio>
19 #include <cstring>
20 #include <vector>
21 #include <string>
22 #include <iostream>
23 #include <dirent.h>
24 #include <fcntl.h>
25 #include <climits>
26 #include <securec.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29
30 #include "osal_mem.h"
31 #include "thermal_hdf_utils.h"
32 #include "thermal_log.h"
33
34 using namespace std;
35
36 namespace OHOS {
37 namespace HDI {
38 namespace Thermal {
39 namespace V1_1 {
40 namespace {
41 const int32_t MAX_SYSFS_SIZE = 128;
42 const std::string THERMAL_SYSFS = "/sys/devices/virtual/thermal";
43 const std::string THERMAL_ZONE_DIR_NAME = "thermal_zone%d";
44 const std::string COOLING_DEVICE_DIR_NAME = "cooling_device%d";
45 const std::string THERMAL_ZONE_DIR_PATH = "/sys/class/thermal/%s";
46 const std::string THERMAL_TEMPERATURE_PATH = "/sys/class/thermal/%s/temp";
47 const std::string THEERMAL_TYPE_PATH = "/sys/class/thermal/%s/type";
48 const std::string CDEV_DIR_NAME = "cooling_device";
49 const std::string THERMAL_ZONE_TEMP_PATH_NAME = "/sys/class/thermal/thermal_zone%d/temp";
50 const uint32_t ARG_0 = 0;
51 const int32_t NUM_ZERO = 0;
52 }
53
Init()54 void ThermalZoneManager::Init()
55 {
56 std::lock_guard<std::mutex> lock(mutex_);
57 pollingMap_ = ThermalHdfConfig::GetInstance().GetPollingConfig();
58 }
59
FormatThermalPaths(char * path,size_t size,const char * format,const char * name)60 void ThermalZoneManager::FormatThermalPaths(char *path, size_t size, const char *format, const char* name)
61 {
62 if (snprintf_s(path, size, size - 1, format, name) < EOK) {
63 THERMAL_HILOGW(COMP_HDI, "failed to format path of %{public}s", name);
64 }
65 }
66
FormatThermalSysfsPaths(struct ThermalSysfsPathInfo * pTSysPathInfo)67 void ThermalZoneManager::FormatThermalSysfsPaths(struct ThermalSysfsPathInfo *pTSysPathInfo)
68 {
69 // Format Paths for thermal path
70 FormatThermalPaths(pTSysPathInfo->thermalZonePath, sizeof(pTSysPathInfo->thermalZonePath),
71 THERMAL_ZONE_DIR_PATH.c_str(), pTSysPathInfo->name);
72 // Format paths for thermal zone node
73 tzSysPathInfo_.name = pTSysPathInfo->name;
74 FormatThermalPaths(tzSysPathInfo_.temperturePath, sizeof(tzSysPathInfo_.temperturePath),
75 THERMAL_TEMPERATURE_PATH.c_str(), pTSysPathInfo->name);
76
77 FormatThermalPaths(tzSysPathInfo_.typePath, sizeof(tzSysPathInfo_.typePath),
78 THEERMAL_TYPE_PATH.c_str(), pTSysPathInfo->name);
79
80 tzSysPathInfo_.fd = pTSysPathInfo->fd;
81 lTzSysPathInfo_.push_back(tzSysPathInfo_);
82 }
83
InitThermalZoneSysfs()84 int32_t ThermalZoneManager::InitThermalZoneSysfs()
85 {
86 DIR *dir = NULL;
87 struct dirent *entry = NULL;
88 int32_t index = 0;
89 int32_t id = 0;
90
91 dir = opendir(THERMAL_SYSFS.c_str());
92 if (dir == NULL) {
93 THERMAL_HILOGE(COMP_HDI, "cannot open thermal zone path");
94 return HDF_ERR_IO;
95 }
96
97 while (true) {
98 entry = readdir(dir);
99 if (entry == NULL) {
100 break;
101 }
102
103 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
104 continue;
105 }
106
107 if (strncmp(entry->d_name, CDEV_DIR_NAME.c_str(), CDEV_DIR_NAME.size()) == 0) {
108 continue;
109 }
110
111 if (entry->d_type == DT_DIR || entry->d_type == DT_LNK) {
112 struct ThermalSysfsPathInfo sysfsInfo = {0};
113 sysfsInfo.name = entry->d_name;
114 THERMAL_HILOGI(COMP_HDI, "init sysfs info of %{public}s", sysfsInfo.name);
115 int32_t ret = sscanf_s(sysfsInfo.name, THERMAL_ZONE_DIR_NAME.c_str(), &id);
116 if (ret < HDF_SUCCESS) {
117 closedir(dir);
118 return ret;
119 }
120
121 THERMAL_HILOGI(COMP_HDI, "Sensor %{public}s found at tz: %{public}d", sysfsInfo.name, id);
122 sysfsInfo.fd = id;
123 if (index > MAX_SYSFS_SIZE) {
124 THERMAL_HILOGE(COMP_HDI, "too many plugged types");
125 break;
126 }
127
128 FormatThermalSysfsPaths(&sysfsInfo);
129 index++;
130 }
131 }
132 closedir(dir);
133 return HDF_SUCCESS;
134 }
135
ParseThermalZoneInfo()136 int32_t ThermalZoneManager::ParseThermalZoneInfo()
137 {
138 int32_t ret;
139 THERMAL_HILOGD(COMP_HDI, "start to parse thermal zone");
140
141 ret = InitThermalZoneSysfs();
142 if (ret != HDF_SUCCESS) {
143 THERMAL_HILOGE(COMP_HDI, "failed to init thermal zone node");
144 }
145 std::map<std::string, std::string> tzPathMap;
146 if (!lTzSysPathInfo_.empty()) {
147 THERMAL_HILOGI(COMP_HDI, "thermal_zone size: %{public}zu", GetLTZPathInfo().size());
148 for (auto iter = lTzSysPathInfo_.begin(); iter != lTzSysPathInfo_.end(); iter++) {
149 std::string tzType;
150 if (!ThermalHdfUtils::ReadNode(iter->typePath, tzType)) {
151 THERMAL_HILOGE(COMP_HDI, "read tz type failed");
152 continue;
153 }
154 tzPathMap.insert(std::make_pair(tzType, iter->temperturePath));
155 }
156 }
157 return UpdateThermalZoneData(tzPathMap);
158 }
159
UpdateDataType(XMLThermalZoneInfo & tzIter,ReportedThermalData & data)160 void ThermalZoneManager::UpdateDataType(XMLThermalZoneInfo& tzIter, ReportedThermalData& data)
161 {
162 if (tzIter.isReplace) {
163 data.type = tzIter.isReplace;
164 } else {
165 data.type = tzIter.type;
166 }
167 }
168
UpdateThermalZoneData(std::map<std::string,std::string> & tzPathMap)169 int32_t ThermalZoneManager::UpdateThermalZoneData(std::map<std::string, std::string> &tzPathMap)
170 {
171 {
172 // Multi-thread access to pollingMap_ require lock
173 std::lock_guard<std::mutex> lock(mutex_);
174 for (auto &polling : pollingMap_) {
175 for (auto &group : polling.second) {
176 auto tzInfoList = group.second->GetXMLThermalZoneInfo();
177 auto tnInfoList = group.second->GetXMLThermalNodeInfo();
178 group.second->thermalDataList_.clear();
179 for (auto tzIter : tzInfoList) {
180 if (tzPathMap.empty()) {
181 break;
182 }
183 auto typeIter = tzPathMap.find(tzIter.type);
184 if (typeIter != tzPathMap.end()) {
185 ReportedThermalData data;
186 UpdateDataType(tzIter, data);
187 data.tempPath = typeIter->second;
188 group.second->thermalDataList_.push_back(data);
189 }
190 }
191 for (auto tnIter : tnInfoList) {
192 ReportedThermalData data;
193 data.type = tnIter.type;
194 if (access(tnIter.path.c_str(), 0) == NUM_ZERO) {
195 THERMAL_HILOGD(COMP_HDI, "This directory already exists.");
196 data.tempPath = tnIter.path;
197 }
198 group.second->thermalDataList_.push_back(data);
199 }
200 }
201 }
202 }
203 CalculateMaxCd();
204 return HDF_SUCCESS;
205 }
206
CalculateMaxCd()207 void ThermalZoneManager::CalculateMaxCd()
208 {
209 std::lock_guard<std::mutex> lock(mutex_);
210
211 if (pollingMap_.empty()) {
212 THERMAL_HILOGE(COMP_HDI, "configured sensor info is empty");
213 return;
214 }
215 std::vector<int32_t> intervalList;
216 for (auto &polling : pollingMap_) {
217 for (auto &group : polling.second) {
218 intervalList.emplace_back(group.second->GetInterval());
219 }
220 }
221
222 maxCd_ = GetIntervalCommonDivisor(intervalList);
223 if (maxCd_ == 0) {
224 return;
225 }
226
227 int32_t maxMultiple = 0;
228 for (auto &polling : pollingMap_) {
229 for (auto &group : polling.second) {
230 group.second->multiple_ = group.second->GetInterval() / maxCd_;
231 maxMultiple = std::max(maxMultiple, group.second->multiple_);
232 }
233 }
234 maxReportTime_ = maxMultiple;
235
236 THERMAL_HILOGI(COMP_HDI, "maxCd_ %{public}d maxReportTime_ %{public}d", maxCd_, maxReportTime_);
237 return;
238 }
239
GetMaxCommonDivisor(int32_t a,int32_t b)240 int32_t ThermalZoneManager::GetMaxCommonDivisor(int32_t a, int32_t b)
241 {
242 if (b == 0) {
243 return NUM_ZERO;
244 }
245
246 if (a % b == 0) {
247 return b;
248 } else {
249 return GetMaxCommonDivisor(b, a % b);
250 }
251 }
252
GetIntervalCommonDivisor(std::vector<int32_t> intervalList)253 int32_t ThermalZoneManager::GetIntervalCommonDivisor(std::vector<int32_t> intervalList)
254 {
255 if (intervalList.empty()) {
256 return ARG_0;
257 }
258
259 int32_t count = intervalList.size();
260 int32_t commonDivisor = intervalList[0];
261 for (int32_t i = 1; i < count; i++) {
262 commonDivisor = GetMaxCommonDivisor(commonDivisor, intervalList[i]);
263 }
264 return commonDivisor;
265 }
266
CollectCallbackInfo(HdfThermalCallbackInfo & callbackInfo,const std::shared_ptr<SensorInfoConfig> & sensorInfo,int32_t reportTime)267 void ThermalZoneManager::CollectCallbackInfo(
268 HdfThermalCallbackInfo &callbackInfo, const std::shared_ptr<SensorInfoConfig> &sensorInfo, int32_t reportTime)
269 {
270 if (sensorInfo->multiple_ == NUM_ZERO) {
271 return;
272 }
273
274 if (reportTime % (sensorInfo->multiple_) == NUM_ZERO) {
275 for (auto iter : sensorInfo->thermalDataList_) {
276 ThermalZoneInfo info;
277 info.type = iter.type;
278 info.temp = ThermalHdfUtils::ReadNodeToInt(iter.tempPath);
279 THERMAL_HILOGD(COMP_HDI, "type: %{public}s temp: %{public}d", iter.type.c_str(), info.temp);
280 callbackInfo.info.emplace_back(info);
281 }
282 }
283
284 return;
285 }
286
ReportThermalZoneData(int32_t reportTime)287 void ThermalZoneManager::ReportThermalZoneData(int32_t reportTime)
288 {
289 std::lock_guard<std::mutex> lock(mutex_);
290
291 for (auto &polling : pollingMap_) {
292 HdfThermalCallbackInfo callbackInfo;
293 for (auto &group : polling.second) {
294 CollectCallbackInfo(callbackInfo, group.second, reportTime);
295 }
296 if (!callbackInfo.info.empty()) {
297 CallbackOnEvent(polling.first, callbackInfo);
298 }
299 }
300
301 return;
302 }
303
CallbackOnEvent(std::string name,HdfThermalCallbackInfo & info)304 void ThermalZoneManager::CallbackOnEvent(std::string name, HdfThermalCallbackInfo &info)
305 {
306 if (name == "thermal") {
307 if (thermalCb_ != nullptr) {
308 thermalCb_->OnThermalDataEvent(info);
309 }
310 } else if (name == "fan") {
311 if (fanCb_ != nullptr) {
312 fanCb_->OnFanDataEvent(info);
313 }
314 }
315
316 return;
317 }
318
GetCallbackInfo()319 HdfThermalCallbackInfo ThermalZoneManager::GetCallbackInfo()
320 {
321 HdfThermalCallbackInfo callbackInfo;
322 std::lock_guard<std::mutex> lock(mutex_);
323
324 for (auto &polling : pollingMap_) {
325 if (polling.first == "fan") {
326 continue;
327 }
328 for (auto &group : polling.second) {
329 for (auto iter : group.second->thermalDataList_) {
330 ThermalZoneInfo info;
331 info.type = iter.type;
332 info.temp = ThermalHdfUtils::ReadNodeToInt(iter.tempPath);
333 callbackInfo.info.emplace_back(info);
334 }
335 }
336 }
337
338 return callbackInfo;
339 }
340
DumpPollingInfo()341 void ThermalZoneManager::DumpPollingInfo()
342 {
343 std::lock_guard<std::mutex> lock(mutex_);
344
345 for (auto &polling : pollingMap_) {
346 THERMAL_HILOGI(COMP_HDI, "pollingName %{public}s", polling.first.c_str());
347 for (auto &group : polling.second) {
348 THERMAL_HILOGI(COMP_HDI, "groupName %{public}s, interval %{public}d, multiple %{public}d",
349 group.first.c_str(), group.second->GetInterval(), group.second->multiple_);
350 for (auto tzIter : group.second->GetXMLThermalZoneInfo()) {
351 THERMAL_HILOGI(COMP_HDI, "type %{public}s, replace %{public}s", tzIter.type.c_str(),
352 tzIter.replace.c_str());
353 }
354 for (auto tnIter : group.second->GetXMLThermalNodeInfo()) {
355 THERMAL_HILOGI(COMP_HDI, "type %{public}s", tnIter.type.c_str());
356 }
357 for (auto dataIter : group.second->thermalDataList_) {
358 THERMAL_HILOGI(COMP_HDI, "data type %{public}s", dataIter.type.c_str());
359 }
360 }
361 }
362 }
363 } // V1_1
364 } // Thermal
365 } // HDI
366 } // OHOS
367