/*
* 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 <cctype>
#include <cerrno>
#include <cinttypes>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <tuple>
#include <unordered_map>
#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/stringprintf.h>
#include "sensors.h"
#include "thermal-helper.h"
namespace android {
namespace hardware {
namespace thermal {
namespace V1_1 {
namespace implementation {
constexpr const char kThermalSensorsRoot[] = "/sys/class/thermal";
constexpr char kThermalZoneDirSuffix[] = "thermal_zone";
constexpr char kSensorTypeFileSuffix[] = "type";
constexpr char kTemperatureFileSuffix[] = "temp";
// This is a golden set of thermal sensor names, their types, and their
// multiplier. Used when we read in sensor values. The tuple value stored is
// formatted as such:
// <temperature type, multiplier value for reading temp>
const std::unordered_map<std::string, std::tuple<TemperatureType, float>>
kValidThermalSensorsMap = {
{"tsens_tz_sensor1", {TemperatureType::CPU, 0.1}}, // CPU0
{"tsens_tz_sensor2", {TemperatureType::CPU, 0.1}}, // CPU1
{"tsens_tz_sensor4", {TemperatureType::CPU, 0.1}}, // CPU2
{"tsens_tz_sensor3", {TemperatureType::CPU, 0.1}}, // CPU3
{"tsens_tz_sensor7", {TemperatureType::CPU, 0.1}}, // CPU4
{"tsens_tz_sensor8", {TemperatureType::CPU, 0.1}}, // CPU5
{"tsens_tz_sensor9", {TemperatureType::CPU, 0.1}}, // CPU6
{"tsens_tz_sensor10", {TemperatureType::CPU, 0.1}}, // CPU7
// GPU thermal sensor.
{"tsens_tz_sensor13", {TemperatureType::GPU, 0.1}},
// Battery thermal sensor.
{"battery", {TemperatureType::BATTERY, 0.001}},
// Skin thermal sensors. We use back_therm for walleye. For taimen we use
// bd_therm and bd_therm2.
{"back_therm", {TemperatureType::SKIN, 1.}},
{"bd_therm", {TemperatureType::SKIN, 1.}},
{"bd_therm2", {TemperatureType::SKIN, 1.}},
// USBC thermal sensor.
{"usb_port_temp", {TemperatureType::UNKNOWN, 0.1}},
};
namespace {
using ::android::hardware::thermal::V1_0::TemperatureType;
static std::string gSkinSensorType;
static unsigned int gSkinThrottlingThreshold;
static unsigned int gSkinShutdownThreshold;
static unsigned int gVrThrottledBelowMin;
Sensors gSensors;
// A map containing hardcoded thresholds per sensor type. Its not const
// because initThermal() will modify the skin sensor thresholds depending on the
// hardware type. The tuple is formatted as follows:
// <throttling threshold, shutdown threshold, vr threshold>
std::unordered_map<TemperatureType, std::tuple<float, float, float>>
gSensorTypeToThresholdsMap = {
{TemperatureType::CPU, {kCpuThrottlingThreshold, kCpuShutdownThreshold,
kCpuThrottlingThreshold}},
{TemperatureType::GPU, {NAN, NAN, NAN}},
{TemperatureType::BATTERY, {NAN, kBatteryShutdownThreshold, NAN}},
{TemperatureType::SKIN, {NAN, NAN, NAN}},
{TemperatureType::UNKNOWN, {NAN, NAN, NAN}}
};
bool initializeSensors() {
auto thermal_zone_dir = std::unique_ptr<DIR, int (*)(DIR*)>(
opendir(kThermalSensorsRoot), closedir);
struct dirent* dp;
size_t num_thermal_zones = 0;
while ((dp = readdir(thermal_zone_dir.get())) != nullptr) {
std::string dir_name(dp->d_name);
if (dir_name.find(kThermalZoneDirSuffix) != std::string::npos) {
++num_thermal_zones;
}
}
for (size_t sensor_zone_num = 0; sensor_zone_num < num_thermal_zones;
++sensor_zone_num) {
std::string path = android::base::StringPrintf("%s/%s%zu",
kThermalSensorsRoot,
kThermalZoneDirSuffix,
sensor_zone_num);
std::string sensor_name;
if (android::base::ReadFileToString(
path + "/" + kSensorTypeFileSuffix, &sensor_name)) {
sensor_name = android::base::Trim(sensor_name);
if (kValidThermalSensorsMap.find(sensor_name) !=
kValidThermalSensorsMap.end()) {
TemperatureType type = std::get<0>(
kValidThermalSensorsMap.at(sensor_name));
auto thresholds = gSensorTypeToThresholdsMap.at(type);
if (!gSensors.addSensor(
sensor_name, path + "/" + kTemperatureFileSuffix,
std::get<0>(thresholds), std::get<1>(thresholds),
std::get<2>(thresholds), type)) {
LOG(ERROR) << "Could not add " << sensor_name
<< "to sensors map";
}
}
}
}
return (gSensors.getNumSensors() == kTemperatureNum);
}
} // namespace
/**
* Initialization constants based on platform
*
* @return true on success or false on error.
*/
bool initThermal() {
std::string hardware = android::base::GetProperty("ro.hardware", "");
if (hardware == "walleye") {
LOG(ERROR) << "Initialization on Walleye";
gSkinThrottlingThreshold = kWalleyeSkinThrottlingThreshold;
gSkinShutdownThreshold = kWalleyeSkinShutdownThreshold;
gVrThrottledBelowMin = kWalleyeVrThrottledBelowMin;
} else if (hardware == "taimen") {
std::string rev = android::base::GetProperty("ro.revision", "");
if (rev == "rev_a" || rev == "rev_b") {
LOG(ERROR) << "Initialization on Taimen pre revision C";
gSkinThrottlingThreshold = kTaimenRabSkinThrottlingThreshold;
gSkinShutdownThreshold = kTaimenRabSkinShutdownThreshold;
gVrThrottledBelowMin = kTaimenRabVrThrottledBelowMin;
} else {
LOG(ERROR) << "Initialization on Taimen revision C and later";
gSkinThrottlingThreshold = kTaimenRcSkinThrottlingThreshold;
gSkinShutdownThreshold = kTaimenRcSkinShutdownThreshold;
gVrThrottledBelowMin = kTaimenRcVrThrottledBelowMin;
}
} else {
LOG(ERROR) << "Unsupported hardware: " << hardware;
return false;
}
gSensorTypeToThresholdsMap[TemperatureType::SKIN] =
std::make_tuple(gSkinThrottlingThreshold, gSkinShutdownThreshold,
gVrThrottledBelowMin);
return initializeSensors();
}
ssize_t fillTemperatures(hidl_vec<Temperature>* temperatures) {
temperatures->resize(gSensors.getNumSensors());
ssize_t current_index = 0;
for (const auto& name_type_mult_pair : kValidThermalSensorsMap) {
Temperature temp;
if (gSensors.readTemperature(name_type_mult_pair.first,
std::get<1>(name_type_mult_pair.second),
&temp)) {
(*temperatures)[current_index] = temp;
++current_index;
}
}
return current_index;
}
ssize_t fillCpuUsages(hidl_vec<CpuUsage> *cpuUsages) {
int vals, cpu_num, online;
ssize_t read;
uint64_t user, nice, system, idle, active, total;
char *line = NULL;
size_t len = 0;
size_t size = 0;
char file_name[PATH_MAX];
FILE *file;
FILE *cpu_file;
if (cpuUsages == NULL || cpuUsages->size() < kCpuNum ) {
LOG(ERROR) << "fillCpuUsages: incorrect buffer";
return -EINVAL;
}
file = fopen(kCpuUsageFile, "r");
if (file == NULL) {
PLOG(ERROR) << "fillCpuUsages: failed to open file (" << kCpuUsageFile << ")";
return -errno;
}
while ((read = getline(&line, &len, file)) != -1) {
// Skip non "cpu[0-9]" lines.
if (strnlen(line, read) < 4 || strncmp(line, "cpu", 3) != 0 || !isdigit(line[3])) {
free(line);
line = NULL;
len = 0;
continue;
}
vals = sscanf(line, "cpu%d %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64, &cpu_num, &user,
&nice, &system, &idle);
free(line);
line = NULL;
len = 0;
if (vals != 5 || size == kCpuNum) {
if (vals != 5) {
PLOG(ERROR) << "fillCpuUsages: failed to read CPU information from file ("
<< kCpuUsageFile << ")";
} else {
PLOG(ERROR) << "fillCpuUsages: file has incorrect format ("
<< kCpuUsageFile << ")";
}
fclose(file);
return errno ? -errno : -EIO;
}
active = user + nice + system;
total = active + idle;
// Read online CPU information.
snprintf(file_name, PATH_MAX, kCpuOnlineFileFormat, cpu_num);
cpu_file = fopen(file_name, "r");
online = 0;
if (cpu_file == NULL) {
PLOG(ERROR) << "fillCpuUsages: failed to open file (" << file_name << ")";
fclose(file);
return -errno;
}
if (1 != fscanf(cpu_file, "%d", &online)) {
PLOG(ERROR) << "fillCpuUsages: failed to read CPU online information from file ("
<< file_name << ")";
fclose(file);
fclose(cpu_file);
return errno ? -errno : -EIO;
}
fclose(cpu_file);
(*cpuUsages)[size].name = kCpuLabel[size];
(*cpuUsages)[size].active = active;
(*cpuUsages)[size].total = total;
(*cpuUsages)[size].isOnline = static_cast<bool>(online);
LOG(DEBUG) << "fillCpuUsages: "<< kCpuLabel[size] << ": "
<< active << " " << total << " " << online;
size++;
}
fclose(file);
if (size != kCpuNum) {
PLOG(ERROR) << "fillCpuUsages: file has incorrect format (" << kCpuUsageFile << ")";
return -EIO;
}
return kCpuNum;
}
std::string getTargetSkinSensorType() {
return gSkinSensorType;
}
} // namespace implementation
} // namespace V1_1
} // namespace thermal
} // namespace hardware
} // namespace android