| /* |
| * 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 |