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
34using namespace std;
35
36namespace OHOS {
37namespace HDI {
38namespace Thermal {
39namespace V1_1 {
40namespace {
41const std::string THERMAL_ZONE_TEMP_PATH = "/sys/class/thermal/thermal_zone%d/temp";
42const std::string THERMAL_ZONE_TYPE_PATH = "/sys/class/thermal/thermal_zone%d/type";
43const int32_t MAX_THERMAL_ZONE_NUM = 200;
44const int32_t MAX_PATH_LEN = 64;
45const int32_t NUM_ZERO = 0;
46}
47
48void ThermalZoneManager::Init()
49{
50    std::lock_guard<std::mutex> lock(mutex_);
51    InitThermalZoneSysfs();
52    pollingMap_ = ThermalHdfConfig::GetInstance().GetPollingConfig();
53}
54
55void ThermalZoneManager::InitThermalZoneSysfs()
56{
57    int32_t maxTzNum = 0;
58    for (int32_t idx = 0; idx < MAX_THERMAL_ZONE_NUM; ++idx) {
59        char path[MAX_PATH_LEN];
60        if (sprintf_s(path, MAX_PATH_LEN, THERMAL_ZONE_TYPE_PATH.c_str(), idx) <= 0) {
61            break;
62        }
63        std::string type;
64        if (!ThermalHdfUtils::ReadNode(path, type)) {
65            break;
66        }
67        tznMap_[type] = idx;
68        maxTzNum = idx;
69    }
70    THERMAL_HILOGI(COMP_HDI, "max thermal zone num is %{public}d", maxTzNum);
71}
72
73void ThermalZoneManager::UpdateDataType(XMLThermalZoneInfo& tzIter, ReportedThermalData& data, int32_t tzn)
74{
75    if (tzIter.isReplace) {
76        data.type = tzIter.replace;
77    } else {
78        data.type = tzIter.type;
79    }
80    char path[MAX_PATH_LEN];
81    if (sprintf_s(path, MAX_PATH_LEN, THERMAL_ZONE_TEMP_PATH.c_str(), tzn) > 0) {
82        data.tempPath = path;
83    } else {
84        THERMAL_HILOGE(COMP_HDI, "thermal zone path format failed, num is %{public}d", tzn);
85    }
86}
87
88void ThermalZoneManager::UpdateThermalZoneInfo(std::shared_ptr<SensorInfoConfig> &infoConfig)
89{
90    auto tzInfoList = infoConfig->GetXMLThermalZoneInfo();
91    auto tnInfoList = infoConfig->GetXMLThermalNodeInfo();
92    infoConfig->thermalDataList_.clear();
93    for (auto tzIter : tzInfoList) {
94        if (tznMap_.empty()) {
95            break;
96        }
97        auto typeIter = tznMap_.find(tzIter.type);
98        if (typeIter != tznMap_.end()) {
99            ReportedThermalData data;
100            UpdateDataType(tzIter, data, typeIter->second);
101            infoConfig->thermalDataList_.push_back(data);
102        }
103    }
104    for (auto tnIter : tnInfoList) {
105        ReportedThermalData data;
106        data.type = tnIter.type;
107        if (access(tnIter.path.c_str(), 0) == NUM_ZERO) {
108            THERMAL_HILOGD(COMP_HDI, "This directory already exists.");
109            data.tempPath = tnIter.path;
110        }
111        infoConfig->thermalDataList_.push_back(data);
112    }
113}
114
115int32_t ThermalZoneManager::UpdateThermalZoneData()
116{
117    {
118        // Multi-thread access to pollingMap_ require lock
119        std::lock_guard<std::mutex> lock(mutex_);
120        for (auto &polling : pollingMap_) {
121            for (auto &group : polling.second) {
122                UpdateThermalZoneInfo(group.second);
123            }
124        }
125    }
126
127    CalculateMaxCd();
128    return HDF_SUCCESS;
129}
130
131void ThermalZoneManager::CalculateMaxCd()
132{
133    std::lock_guard<std::mutex> lock(mutex_);
134
135    if (pollingMap_.empty()) {
136        THERMAL_HILOGE(COMP_HDI, "configured sensor info is empty");
137        return;
138    }
139    std::vector<int32_t> intervalList;
140    for (auto &polling : pollingMap_) {
141        for (auto &group : polling.second) {
142            intervalList.emplace_back(group.second->GetInterval());
143        }
144    }
145
146    maxCd_ = GetIntervalCommonDivisor(intervalList);
147    if (maxCd_ == 0) {
148        return;
149    }
150
151    int32_t maxMultiple = 0;
152    for (auto &polling : pollingMap_) {
153        for (auto &group : polling.second) {
154            group.second->multiple_ = group.second->GetInterval() / maxCd_;
155            maxMultiple = std::max(maxMultiple, group.second->multiple_);
156        }
157    }
158    maxReportTime_ = maxMultiple;
159
160    THERMAL_HILOGI(COMP_HDI, "maxCd_ %{public}d maxReportTime_ %{public}d", maxCd_, maxReportTime_);
161    return;
162}
163
164int32_t ThermalZoneManager::GetIntervalCommonDivisor(std::vector<int32_t> intervalList)
165{
166    if (intervalList.empty()) {
167        return NUM_ZERO;
168    }
169
170    uint32_t count = intervalList.size();
171    int32_t commonDivisor = intervalList[0];
172    for (uint32_t i = 1; i < count; i++) {
173        commonDivisor = ThermalHdfUtils::GetMaxCommonDivisor(commonDivisor, intervalList[i]);
174    }
175    return commonDivisor;
176}
177
178void ThermalZoneManager::CollectCallbackInfo(
179    HdfThermalCallbackInfo &callbackInfo, const std::shared_ptr<SensorInfoConfig> &sensorInfo, int32_t reportTime)
180{
181    if (sensorInfo->multiple_ == NUM_ZERO) {
182        return;
183    }
184
185    if (reportTime % (sensorInfo->multiple_) == NUM_ZERO) {
186        for (auto iter : sensorInfo->thermalDataList_) {
187            ThermalZoneInfo info;
188            info.type = iter.type;
189            info.temp = ThermalHdfUtils::ReadNodeToInt(iter.tempPath);
190            THERMAL_HILOGD(COMP_HDI, "type: %{public}s temp: %{public}d", iter.type.c_str(), info.temp);
191            callbackInfo.info.emplace_back(info);
192        }
193    }
194
195    return;
196}
197
198void ThermalZoneManager::ReportThermalZoneData(int32_t reportTime)
199{
200    std::lock_guard<std::mutex> lock(mutex_);
201
202    for (auto &polling : pollingMap_) {
203        HdfThermalCallbackInfo callbackInfo;
204        for (auto &group : polling.second) {
205            CollectCallbackInfo(callbackInfo, group.second, reportTime);
206        }
207        if (!callbackInfo.info.empty()) {
208            CallbackOnEvent(polling.first, callbackInfo);
209        }
210    }
211
212    return;
213}
214
215void ThermalZoneManager::CallbackOnEvent(std::string name, HdfThermalCallbackInfo &info)
216{
217    if (name == "thermal") {
218        if (thermalCb_ != nullptr) {
219            thermalCb_->OnThermalDataEvent(info);
220        }
221    } else if (name == "fan") {
222        if (fanCb_ != nullptr) {
223            fanCb_->OnFanDataEvent(info);
224        }
225    }
226
227    return;
228}
229
230HdfThermalCallbackInfo ThermalZoneManager::GetCallbackInfo()
231{
232    HdfThermalCallbackInfo callbackInfo;
233    std::lock_guard<std::mutex> lock(mutex_);
234
235    for (auto &polling : pollingMap_) {
236        if (polling.first == "fan") {
237            continue;
238        }
239        for (auto &group : polling.second) {
240            for (auto iter : group.second->thermalDataList_) {
241                ThermalZoneInfo info;
242                info.type = iter.type;
243                info.temp = ThermalHdfUtils::ReadNodeToInt(iter.tempPath);
244                callbackInfo.info.emplace_back(info);
245            }
246        }
247    }
248
249    return callbackInfo;
250}
251
252void ThermalZoneManager::DumpPollingInfo()
253{
254    std::lock_guard<std::mutex> lock(mutex_);
255
256    for (auto &polling : pollingMap_) {
257        THERMAL_HILOGI(COMP_HDI, "pollingName %{public}s", polling.first.c_str());
258        for (auto &group : polling.second) {
259            THERMAL_HILOGI(COMP_HDI, "groupName %{public}s, interval %{public}d, multiple %{public}d",
260                group.first.c_str(), group.second->GetInterval(), group.second->multiple_);
261            for (auto tzIter : group.second->GetXMLThermalZoneInfo()) {
262                THERMAL_HILOGI(COMP_HDI, "type %{public}s, replace %{public}s", tzIter.type.c_str(),
263                    tzIter.replace.c_str());
264            }
265            for (auto tnIter : group.second->GetXMLThermalNodeInfo()) {
266                THERMAL_HILOGI(COMP_HDI, "type %{public}s", tnIter.type.c_str());
267            }
268            for (auto dataIter : group.second->thermalDataList_) {
269                THERMAL_HILOGI(COMP_HDI, "data type %{public}s", dataIter.type.c_str());
270            }
271        }
272    }
273}
274} // V1_1
275} // Thermal
276} // HDI
277} // OHOS
278