1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <cctype> 18 #include <cerrno> 19 #include <cinttypes> 20 #include <cmath> 21 #include <cstdlib> 22 #include <cstring> 23 #include <tuple> 24 #include <unordered_map> 25 #include <vector> 26 27 #include <android-base/file.h> 28 #include <android-base/logging.h> 29 #include <android-base/properties.h> 30 #include <android-base/strings.h> 31 #include <android-base/stringprintf.h> 32 33 #include "sensors.h" 34 #include "thermal-helper.h" 35 36 namespace android { 37 namespace hardware { 38 namespace thermal { 39 namespace V1_1 { 40 namespace implementation { 41 42 constexpr const char kThermalSensorsRoot[] = "/sys/class/thermal"; 43 constexpr char kThermalZoneDirSuffix[] = "thermal_zone"; 44 constexpr char kSensorTypeFileSuffix[] = "type"; 45 constexpr char kTemperatureFileSuffix[] = "temp"; 46 // This is a golden set of thermal sensor names, their types, and their 47 // multiplier. Used when we read in sensor values. The tuple value stored is 48 // formatted as such: 49 // <temperature type, multiplier value for reading temp> 50 const std::unordered_map<std::string, std::tuple<TemperatureType, float>> 51 kValidThermalSensorsMap = { 52 {"tsens_tz_sensor1", {TemperatureType::CPU, 0.1}}, // CPU0 53 {"tsens_tz_sensor2", {TemperatureType::CPU, 0.1}}, // CPU1 54 {"tsens_tz_sensor4", {TemperatureType::CPU, 0.1}}, // CPU2 55 {"tsens_tz_sensor3", {TemperatureType::CPU, 0.1}}, // CPU3 56 {"tsens_tz_sensor7", {TemperatureType::CPU, 0.1}}, // CPU4 57 {"tsens_tz_sensor8", {TemperatureType::CPU, 0.1}}, // CPU5 58 {"tsens_tz_sensor9", {TemperatureType::CPU, 0.1}}, // CPU6 59 {"tsens_tz_sensor10", {TemperatureType::CPU, 0.1}}, // CPU7 60 // GPU thermal sensor. 61 {"tsens_tz_sensor13", {TemperatureType::GPU, 0.1}}, 62 // Battery thermal sensor. 63 {"battery", {TemperatureType::BATTERY, 0.001}}, 64 // Skin thermal sensors. We use back_therm for walleye. For taimen we use 65 // bd_therm and bd_therm2. 66 {"back_therm", {TemperatureType::SKIN, 1.}}, 67 {"bd_therm", {TemperatureType::SKIN, 1.}}, 68 {"bd_therm2", {TemperatureType::SKIN, 1.}}, 69 // USBC thermal sensor. 70 {"usb_port_temp", {TemperatureType::UNKNOWN, 0.1}}, 71 }; 72 73 namespace { 74 75 using ::android::hardware::thermal::V1_0::TemperatureType; 76 77 static std::string gSkinSensorType; 78 static unsigned int gSkinThrottlingThreshold; 79 static unsigned int gSkinShutdownThreshold; 80 static unsigned int gVrThrottledBelowMin; 81 Sensors gSensors; 82 83 // A map containing hardcoded thresholds per sensor type. Its not const 84 // because initThermal() will modify the skin sensor thresholds depending on the 85 // hardware type. The tuple is formatted as follows: 86 // <throttling threshold, shutdown threshold, vr threshold> 87 std::unordered_map<TemperatureType, std::tuple<float, float, float>> 88 gSensorTypeToThresholdsMap = { 89 {TemperatureType::CPU, {kCpuThrottlingThreshold, kCpuShutdownThreshold, 90 kCpuThrottlingThreshold}}, 91 {TemperatureType::GPU, {NAN, NAN, NAN}}, 92 {TemperatureType::BATTERY, {NAN, kBatteryShutdownThreshold, NAN}}, 93 {TemperatureType::SKIN, {NAN, NAN, NAN}}, 94 {TemperatureType::UNKNOWN, {NAN, NAN, NAN}} 95 }; 96 97 bool initializeSensors() { 98 auto thermal_zone_dir = std::unique_ptr<DIR, int (*)(DIR*)>( 99 opendir(kThermalSensorsRoot), closedir); 100 struct dirent* dp; 101 size_t num_thermal_zones = 0; 102 while ((dp = readdir(thermal_zone_dir.get())) != nullptr) { 103 std::string dir_name(dp->d_name); 104 if (dir_name.find(kThermalZoneDirSuffix) != std::string::npos) { 105 ++num_thermal_zones; 106 } 107 } 108 109 for (size_t sensor_zone_num = 0; sensor_zone_num < num_thermal_zones; 110 ++sensor_zone_num) { 111 std::string path = android::base::StringPrintf("%s/%s%zu", 112 kThermalSensorsRoot, 113 kThermalZoneDirSuffix, 114 sensor_zone_num); 115 std::string sensor_name; 116 if (android::base::ReadFileToString( 117 path + "/" + kSensorTypeFileSuffix, &sensor_name)) { 118 sensor_name = android::base::Trim(sensor_name); 119 if (kValidThermalSensorsMap.find(sensor_name) != 120 kValidThermalSensorsMap.end()) { 121 TemperatureType type = std::get<0>( 122 kValidThermalSensorsMap.at(sensor_name)); 123 auto thresholds = gSensorTypeToThresholdsMap.at(type); 124 if (!gSensors.addSensor( 125 sensor_name, path + "/" + kTemperatureFileSuffix, 126 std::get<0>(thresholds), std::get<1>(thresholds), 127 std::get<2>(thresholds), type)) { 128 LOG(ERROR) << "Could not add " << sensor_name 129 << "to sensors map"; 130 } 131 } 132 } 133 } 134 return (gSensors.getNumSensors() == kTemperatureNum); 135 } 136 137 } // namespace 138 139 /** 140 * Initialization constants based on platform 141 * 142 * @return true on success or false on error. 143 */ 144 bool initThermal() { 145 std::string hardware = android::base::GetProperty("ro.hardware", ""); 146 if (hardware == "walleye") { 147 LOG(ERROR) << "Initialization on Walleye"; 148 gSkinThrottlingThreshold = kWalleyeSkinThrottlingThreshold; 149 gSkinShutdownThreshold = kWalleyeSkinShutdownThreshold; 150 gVrThrottledBelowMin = kWalleyeVrThrottledBelowMin; 151 } else if (hardware == "taimen") { 152 std::string rev = android::base::GetProperty("ro.revision", ""); 153 if (rev == "rev_a" || rev == "rev_b") { 154 LOG(ERROR) << "Initialization on Taimen pre revision C"; 155 gSkinThrottlingThreshold = kTaimenRabSkinThrottlingThreshold; 156 gSkinShutdownThreshold = kTaimenRabSkinShutdownThreshold; 157 gVrThrottledBelowMin = kTaimenRabVrThrottledBelowMin; 158 } else { 159 LOG(ERROR) << "Initialization on Taimen revision C and later"; 160 gSkinThrottlingThreshold = kTaimenRcSkinThrottlingThreshold; 161 gSkinShutdownThreshold = kTaimenRcSkinShutdownThreshold; 162 gVrThrottledBelowMin = kTaimenRcVrThrottledBelowMin; 163 } 164 } else { 165 LOG(ERROR) << "Unsupported hardware: " << hardware; 166 return false; 167 } 168 gSensorTypeToThresholdsMap[TemperatureType::SKIN] = 169 std::make_tuple(gSkinThrottlingThreshold, gSkinShutdownThreshold, 170 gVrThrottledBelowMin); 171 return initializeSensors(); 172 } 173 174 ssize_t fillTemperatures(hidl_vec<Temperature>* temperatures) { 175 temperatures->resize(gSensors.getNumSensors()); 176 ssize_t current_index = 0; 177 for (const auto& name_type_mult_pair : kValidThermalSensorsMap) { 178 Temperature temp; 179 if (gSensors.readTemperature(name_type_mult_pair.first, 180 std::get<1>(name_type_mult_pair.second), 181 &temp)) { 182 (*temperatures)[current_index] = temp; 183 ++current_index; 184 } 185 } 186 return current_index; 187 } 188 189 ssize_t fillCpuUsages(hidl_vec<CpuUsage> *cpuUsages) { 190 int vals, cpu_num, online; 191 ssize_t read; 192 uint64_t user, nice, system, idle, active, total; 193 char *line = NULL; 194 size_t len = 0; 195 size_t size = 0; 196 char file_name[PATH_MAX]; 197 FILE *file; 198 FILE *cpu_file; 199 200 if (cpuUsages == NULL || cpuUsages->size() < kCpuNum ) { 201 LOG(ERROR) << "fillCpuUsages: incorrect buffer"; 202 return -EINVAL; 203 } 204 205 file = fopen(kCpuUsageFile, "r"); 206 if (file == NULL) { 207 PLOG(ERROR) << "fillCpuUsages: failed to open file (" << kCpuUsageFile << ")"; 208 return -errno; 209 } 210 211 while ((read = getline(&line, &len, file)) != -1) { 212 // Skip non "cpu[0-9]" lines. 213 if (strnlen(line, read) < 4 || strncmp(line, "cpu", 3) != 0 || !isdigit(line[3])) { 214 free(line); 215 line = NULL; 216 len = 0; 217 continue; 218 } 219 220 vals = sscanf(line, "cpu%d %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64, &cpu_num, &user, 221 &nice, &system, &idle); 222 223 free(line); 224 line = NULL; 225 len = 0; 226 227 if (vals != 5 || size == kCpuNum) { 228 if (vals != 5) { 229 PLOG(ERROR) << "fillCpuUsages: failed to read CPU information from file (" 230 << kCpuUsageFile << ")"; 231 } else { 232 PLOG(ERROR) << "fillCpuUsages: file has incorrect format (" 233 << kCpuUsageFile << ")"; 234 } 235 fclose(file); 236 return errno ? -errno : -EIO; 237 } 238 239 active = user + nice + system; 240 total = active + idle; 241 242 // Read online CPU information. 243 snprintf(file_name, PATH_MAX, kCpuOnlineFileFormat, cpu_num); 244 cpu_file = fopen(file_name, "r"); 245 online = 0; 246 if (cpu_file == NULL) { 247 PLOG(ERROR) << "fillCpuUsages: failed to open file (" << file_name << ")"; 248 fclose(file); 249 return -errno; 250 } 251 if (1 != fscanf(cpu_file, "%d", &online)) { 252 PLOG(ERROR) << "fillCpuUsages: failed to read CPU online information from file (" 253 << file_name << ")"; 254 fclose(file); 255 fclose(cpu_file); 256 return errno ? -errno : -EIO; 257 } 258 fclose(cpu_file); 259 260 (*cpuUsages)[size].name = kCpuLabel[size]; 261 (*cpuUsages)[size].active = active; 262 (*cpuUsages)[size].total = total; 263 (*cpuUsages)[size].isOnline = static_cast<bool>(online); 264 265 LOG(DEBUG) << "fillCpuUsages: "<< kCpuLabel[size] << ": " 266 << active << " " << total << " " << online; 267 size++; 268 } 269 fclose(file); 270 271 if (size != kCpuNum) { 272 PLOG(ERROR) << "fillCpuUsages: file has incorrect format (" << kCpuUsageFile << ")"; 273 return -EIO; 274 } 275 return kCpuNum; 276 } 277 278 std::string getTargetSkinSensorType() { 279 return gSkinSensorType; 280 } 281 282 } // namespace implementation 283 } // namespace V1_1 284 } // namespace thermal 285 } // namespace hardware 286 } // namespace android 287