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
initializeSensors()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 */
initThermal()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
fillTemperatures(hidl_vec<Temperature> * temperatures)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
fillCpuUsages(hidl_vec<CpuUsage> * cpuUsages)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
getTargetSkinSensorType()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