blob: 342b07718b687f714c818a133eb4df7792883c58 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <cstdlib>
#define LOG_TAG "ThermalHAL"
#include <log/log.h>
#include <android-base/logging.h>
#include <hardware/hardware.h>
#include <hardware/thermal.h>
#include "Thermal.h"
using namespace std;
namespace android {
namespace hardware {
namespace thermal {
namespace V2_0 {
namespace implementation {
using ::android::hardware::thermal::V1_0::ThermalStatus;
using ::android::hardware::thermal::V1_0::ThermalStatusCode;
map<string, ifstream> temperatureNodes;
map<string, vector<ifstream>> throttleNodes;
map<string, vector<ifstream>> hysteresisNodes;
map<TemperatureType, vector<string>> thermalNames;
vector<ifstream> cpuOnlineNodes;
ifstream cpuUsageNode;
map<CoolingType, vector<string>> cdevNames;
map<string, ifstream> cdevCurStates;
string thermalZonePath = "/sys/class/thermal/thermal_zone";
string coolingDevicePath = "/sys/class/thermal/cooling_device";
string cpuPath = "/sys/devices/system/cpu/cpu";
unsigned int readValue(ifstream &node) {
string buf;
node.seekg(0, node.beg);
node >> buf;
return atoi(buf.c_str());
}
unsigned int readCurTemp(string &thermalName) {
return readValue(temperatureNodes[thermalName]);
}
unsigned int readThrottleTemp(string &thermalName, int lv) {
return readValue(throttleNodes[thermalName][lv]);
}
unsigned int readCpuOnline(int cpuNum) {
return readValue(cpuOnlineNodes[cpuNum]);
}
unsigned int readCurCdevState(string &cdevName) {
return readValue(cdevCurStates[cdevName]);
}
unsigned int readHysteresisTemp(string &thermalName, int lv) {
return readValue(hysteresisNodes[thermalName][lv]);
}
void initExynosThermalHal(void) {
int i = 0;
while (true) {
string curThermalZonePath = thermalZonePath + to_string(i);
i++;
ifstream zoneTypeNode(curThermalZonePath + "/type");
if (!zoneTypeNode.is_open())
break;
string zoneName;
TemperatureType tempType;
zoneTypeNode >> zoneName;
if (zoneName == "BIG" || zoneName == "MID" || zoneName == "LITTLE") {
tempType = TemperatureType::CPU;
}
else if (zoneName == "G3D") {
tempType = TemperatureType::GPU;
}
else if (zoneName == "NPU") {
tempType = TemperatureType::NPU;
}
else {
continue;
}
// Set temperature node
temperatureNodes[zoneName].open(curThermalZonePath + "/temp");
thermalNames[tempType].push_back(zoneName);
// Set throttling node
for (int j = 1; j < 8; j++) {
throttleNodes[zoneName].emplace_back(
curThermalZonePath + "/trip_point_" + to_string(j) + "_temp");
hysteresisNodes[zoneName].emplace_back(
curThermalZonePath + "/trip_point_" + to_string(j) + "_hyst");
}
}
i = 0;
while (true) {
string curCdevPath = coolingDevicePath + to_string(i++);
ifstream cdevTypeNode(curCdevPath + "/type");
if (!cdevTypeNode.is_open())
break;
string cdevName;
CoolingType cdevType;
cdevTypeNode >> cdevName;
if (cdevName.find("cpufreq") != string::npos) {
cdevType = CoolingType::CPU;
}
else if (cdevName.find("gpufreq") != string::npos) {
cdevType = CoolingType::GPU;
}
else if (cdevName.find("npu") != string::npos) {
cdevType = CoolingType::NPU;
}
else {
continue;
}
// Set cooling device node
cdevNames[cdevType].push_back(cdevName);
cdevCurStates[cdevName].open(curCdevPath + "/cur_state");
}
i = 0;
while (true) {
string curCpuPath = cpuPath + to_string(i) + "/online";
ifstream onlineNode(curCpuPath);
if (!onlineNode.is_open())
break;
cpuOnlineNodes.emplace_back(curCpuPath);
i++;
}
}
ThermalStatus getAllTemperatures(hidl_vec<Temperature_1_0> &temperatures)
{
ThermalStatus status;
status.code = ThermalStatusCode::SUCCESS;
unsigned int cnt = 0;
temperatures.resize(temperatureNodes.size());
for (auto it = thermalNames.begin(); it != thermalNames.end(); it++) {
for (int i = 0; i < it->second.size(); i++) {
string &name = it->second[i];
temperatures[cnt].currentValue = readCurTemp(name) / 1000;
temperatures[cnt].throttlingThreshold = readThrottleTemp(name, 0);
temperatures[cnt].shutdownThreshold = readThrottleTemp(name, 6);
temperatures[cnt].vrThrottlingThreshold = UNKNOWN_TEMPERATURE;
temperatures[cnt].type = static_cast<::android::hardware::thermal::V1_0::TemperatureType>(it->first);
temperatures[cnt].name = it->second[i];
cnt++;
}
}
return status;
}
ThermalStatus getTypeTemperatures(TemperatureType tType, hidl_vec<Temperature_2_0> &temperatures)
{
ThermalStatus status;
status.code = ThermalStatusCode::SUCCESS;
vector<string> &tTypeName = thermalNames[tType];
if(tTypeName.size() == 0) {
status.code = ThermalStatusCode::FAILURE;
return status;
}
temperatures.resize(tTypeName.size());
for (int i = 0; i < tTypeName.size(); i++) {
string &name = tTypeName[i];
unsigned int temp = readCurTemp(name);
temperatures[i].value = temp / 1000;
temperatures[i].type = tType;
temperatures[i].name = tTypeName[i];
temperatures[i].throttlingStatus = ThrottlingSeverity::NONE;
// Check throttle status
for (int j = throttleNodes.size() - 1; j >= 0; j--) {
if (temp >= readThrottleTemp(name, j)) {
temperatures[i].throttlingStatus = (ThrottlingSeverity)j;
break;
}
}
}
return status;
}
ThermalStatus getCpuUsage(hidl_vec<CpuUsage> &cpuUsage) {
string str, value;
stringstream line;
ThermalStatus status;
status.code = ThermalStatusCode::SUCCESS;
bool flag = true;
string statPath = "/proc/stat";
ifstream statNode(statPath);
unsigned int cpuNum = 0;
cpuUsage.resize(cpuOnlineNodes.size());
while (!statNode.eof()) {
getline(statNode, str);
if (!str.length())
break;
line.str(str);
line >> value;
unsigned long long user, nice, system, idle, active, total;
if (value != "cpu0" && flag)
continue;
if (value.compare(0, 3, "cpu")) {
flag = true;
continue;
}
else
flag = false;
line >> user >> nice >> system >> idle;
active = user + nice + system;
total = active + idle;
cpuUsage[cpuNum].name = value;
cpuUsage[cpuNum].active = active;
cpuUsage[cpuNum].total = total;
cpuUsage[cpuNum].isOnline = 1;
cpuNum++;
}
return status;
}
/* NOT SUPPORT FAN COOLING DEVICE */
ThermalStatus coolingDevices(vector<CoolingDevice_1_0> cdevs) {
ThermalStatus status;
status.code = ThermalStatusCode::SUCCESS;
cdevs.clear();
return status;
}
ThermalStatus curCoolingDevices(CoolingType type, hidl_vec<CoolingDevice_2_0> &cdevs) {
ThermalStatus status;
status.code = ThermalStatusCode::SUCCESS;
vector<string> &typeCdevNames = cdevNames[type];
if (typeCdevNames.size() == 0) {
status.code = ThermalStatusCode::FAILURE;
return status;
}
cdevs.resize(typeCdevNames.size());
for (int i = 0; i < typeCdevNames.size(); i++) {
cdevs[i].name = typeCdevNames[i];
cdevs[i].type = type;
cdevs[i].value = readCurCdevState(typeCdevNames[i]);
}
return status;
}
ThermalStatus temperatureThresholds(TemperatureType tType, vector<TemperatureThreshold> &thresholds) {
ThermalStatus status;
status.code = ThermalStatusCode::SUCCESS;
vector<string> &zoneNames = thermalNames[tType];
if (zoneNames.size() == 0) {
status.code = ThermalStatusCode::FAILURE;
return status;
}
thresholds.resize(zoneNames.size());
for (int i = 0; i < zoneNames.size(); i++) {
thresholds[i].type = tType;
thresholds[i].name = zoneNames[i];
thresholds[i].vrThrottlingThreshold = UNKNOWN_TEMPERATURE;
for (int j = 0; j < throttleNodes[zoneNames[i]].size(); j++) {
thresholds[i].hotThrottlingThresholds[j] = readThrottleTemp(zoneNames[i], j);
thresholds[i].coldThrottlingThresholds[j] = thresholds[i].hotThrottlingThresholds[j] - readHysteresisTemp(zoneNames[i], j);
}
}
return status;
}
bool ThermalNotifier::startWatchingDeviceFiles() {
if (cb_) {
auto ret = this->run("FileNotifierThread", PRIORITY_HIGHEST);
if (ret != NO_ERROR) {
LOG(ERROR) << "ThermalNotifierThread start fail";
return false;
} else {
LOG(INFO) << "ThermalNotifierThread started";
return true;
}
}
return false;
}
bool ThermalNotifier::threadLoop() {
hidl_vec<Temperature_2_0> getTemps;
std::vector<Temperature_2_0> sendTemps;
LOG(VERBOSE) << "ThermalNotifier polling...";
this_thread::sleep_for(chrono::milliseconds(2000));
for (auto it = thermalNames.begin(); it != thermalNames.end(); it++) {
getTypeTemperatures(it->first, getTemps);
for (auto &t : getTemps) {
LOG(VERBOSE) << "ThermalNotifier push_back :" << t.name;
sendTemps.push_back(t);
}
}
if (cb_)
cb_(sendTemps);
return true;
}
} // namespace implementation
} // namespace V2_0
} // namespace thermal
} // namespace hardware
} // namespace android