1 /* 2 * Copyright (C) 2016 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 <ctype.h> 18 #include <errno.h> 19 #include <inttypes.h> 20 #include <stdlib.h> 21 #include <string.h> 22 23 #define LOG_TAG "ThermalHAL" 24 #include <utils/Log.h> 25 26 #include <hardware/hardware.h> 27 #include <hardware/thermal.h> 28 29 #define MAX_LENGTH 50 30 31 #define CPU_USAGE_FILE "/proc/stat" 32 #define TEMPERATURE_FILE_FORMAT "/sys/class/thermal/thermal_zone%d/temp" 33 #define CPU_ONLINE_FILE_FORMAT "/sys/devices/system/cpu/cpu%d/online" 34 35 #define BATTERY_SENSOR_NUM 29 36 #define GPU_SENSOR_NUM 14 37 #define SKIN_SENSOR_NUM 24 38 39 const int CPU_SENSORS[] = {4, 6, 9, 11}; 40 41 #define CPU_NUM (sizeof(CPU_SENSORS) / sizeof(int)) 42 // Sum of CPU_NUM + 3 for GPU, BATTERY, and SKIN. 43 #define TEMPERATURE_NUM 7 44 45 //qcom, therm-reset-temp 46 #define CPU_SHUTDOWN_THRESHOLD 115 47 //qcom,freq-mitigation-temp 48 #define CPU_THROTTLING_THRESHOLD 95 49 #define BATTERY_SHUTDOWN_THRESHOLD 60 50 // device/google/marlin/thermal-engine-marlin.conf 51 #define SKIN_THROTTLING_THRESHOLD 47 52 #define SKIN_SHUTDOWN_THRESHOLD 60 53 #define VR_THROTTLED_BELOW_MIN 58 54 55 #define GPU_LABEL "GPU" 56 #define BATTERY_LABEL "battery" 57 #define SKIN_LABEL "skin" 58 59 const char *CPU_LABEL[] = {"CPU0", "CPU1", "CPU2", "CPU3"}; 60 61 /** 62 * Reads device temperature. 63 * 64 * @param sensor_num Number of sensor file with temperature. 65 * @param type Device temperature type. 66 * @param name Device temperature name. 67 * @param mult Multiplier used to translate temperature to Celsius. 68 * @param throttling_threshold Throttling threshold for the temperature. 69 * @param shutdown_threshold Shutdown threshold for the temperature. 70 * @param out Pointer to temperature_t structure that will be filled with current 71 * values. 72 * 73 * @return 0 on success or negative value -errno on error. 74 */ 75 static ssize_t read_temperature(int sensor_num, int type, const char *name, float mult, 76 float throttling_threshold, float shutdown_threshold, float vr_throttling_threshold, 77 temperature_t *out) { 78 FILE *file; 79 char file_name[MAX_LENGTH]; 80 float temp; 81 82 sprintf(file_name, TEMPERATURE_FILE_FORMAT, sensor_num); 83 file = fopen(file_name, "r"); 84 if (file == NULL) { 85 ALOGE("%s: failed to open: %s", __func__, strerror(errno)); 86 return -errno; 87 } 88 if (1 != fscanf(file, "%f", &temp)) { 89 fclose(file); 90 ALOGE("%s: failed to read a float: %s", __func__, strerror(errno)); 91 return errno ? -errno : -EIO; 92 } 93 94 fclose(file); 95 96 (*out) = (temperature_t) { 97 .type = type, 98 .name = name, 99 .current_value = temp * mult, 100 .throttling_threshold = throttling_threshold, 101 .shutdown_threshold = shutdown_threshold, 102 .vr_throttling_threshold = vr_throttling_threshold 103 }; 104 105 return 0; 106 } 107 108 static ssize_t get_cpu_temperatures(temperature_t *list, size_t size) { 109 size_t cpu; 110 111 for (cpu = 0; cpu < CPU_NUM; cpu++) { 112 if (cpu >= size) { 113 break; 114 } 115 // tsens_tz_sensor[4,6,9,11]: temperature in decidegrees Celsius. 116 ssize_t result = read_temperature(CPU_SENSORS[cpu], DEVICE_TEMPERATURE_CPU, CPU_LABEL[cpu], 117 0.1, CPU_THROTTLING_THRESHOLD, CPU_SHUTDOWN_THRESHOLD, CPU_THROTTLING_THRESHOLD, 118 &list[cpu]); 119 if (result != 0) { 120 return result; 121 } 122 } 123 return cpu; 124 } 125 126 static ssize_t get_temperatures(thermal_module_t *module, temperature_t *list, size_t size) { 127 ssize_t result = 0; 128 size_t current_index = 0; 129 130 if (list == NULL) { 131 return TEMPERATURE_NUM; 132 } 133 134 result = get_cpu_temperatures(list, size); 135 if (result < 0) { 136 return result; 137 } 138 current_index += result; 139 140 // GPU temperature. 141 if (current_index < size) { 142 // tsens_tz_sensor14: temperature in decidegrees Celsius. 143 result = read_temperature(GPU_SENSOR_NUM, DEVICE_TEMPERATURE_GPU, GPU_LABEL, 0.1, 144 UNKNOWN_TEMPERATURE, UNKNOWN_TEMPERATURE, UNKNOWN_TEMPERATURE, 145 &list[current_index]); 146 if (result < 0) { 147 return result; 148 } 149 current_index++; 150 } 151 152 // Battery temperature. 153 if (current_index < size) { 154 // tsens_tz_sensor29: battery: temperature in millidegrees Celsius. 155 result = read_temperature(BATTERY_SENSOR_NUM, DEVICE_TEMPERATURE_BATTERY, BATTERY_LABEL, 156 0.001, UNKNOWN_TEMPERATURE, BATTERY_SHUTDOWN_THRESHOLD, UNKNOWN_TEMPERATURE, 157 &list[current_index]); 158 if (result < 0) { 159 return result; 160 } 161 current_index++; 162 } 163 164 // Skin temperature. 165 if (current_index < size) { 166 // tsens_tz_sensor24: temperature in Celsius. 167 result = read_temperature(SKIN_SENSOR_NUM, DEVICE_TEMPERATURE_SKIN, SKIN_LABEL, 1., 168 SKIN_THROTTLING_THRESHOLD, SKIN_SHUTDOWN_THRESHOLD, VR_THROTTLED_BELOW_MIN, 169 &list[current_index]); 170 if (result < 0) { 171 return result; 172 } 173 current_index++; 174 } 175 return TEMPERATURE_NUM; 176 } 177 178 static ssize_t get_cpu_usages(thermal_module_t *module, cpu_usage_t *list) { 179 int vals, cpu_num, online; 180 ssize_t read; 181 uint64_t user, nice, system, idle, active, total; 182 char *line = NULL; 183 size_t len = 0; 184 size_t size = 0; 185 char file_name[MAX_LENGTH]; 186 FILE *file; 187 FILE *cpu_file; 188 189 if (list == NULL) { 190 return CPU_NUM; 191 } 192 193 file = fopen(CPU_USAGE_FILE, "r"); 194 if (file == NULL) { 195 ALOGE("%s: failed to open: %s", __func__, strerror(errno)); 196 return -errno; 197 } 198 199 while ((read = getline(&line, &len, file)) != -1) { 200 // Skip non "cpu[0-9]" lines. 201 if (strnlen(line, read) < 4 || strncmp(line, "cpu", 3) != 0 || !isdigit(line[3])) { 202 free(line); 203 line = NULL; 204 len = 0; 205 continue; 206 } 207 208 vals = sscanf(line, "cpu%d %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64, &cpu_num, &user, 209 &nice, &system, &idle); 210 211 free(line); 212 line = NULL; 213 len = 0; 214 215 if (vals != 5 || size == CPU_NUM) { 216 if (vals != 5) { 217 ALOGE("%s: failed to read CPU information from file: %s", __func__, 218 strerror(errno)); 219 } else { 220 ALOGE("/proc/stat file has incorrect format."); 221 } 222 fclose(file); 223 return errno ? -errno : -EIO; 224 } 225 226 active = user + nice + system; 227 total = active + idle; 228 229 // Read online CPU information. 230 snprintf(file_name, MAX_LENGTH, CPU_ONLINE_FILE_FORMAT, cpu_num); 231 cpu_file = fopen(file_name, "r"); 232 online = 0; 233 if (cpu_file == NULL) { 234 ALOGE("%s: failed to open file: %s (%s)", __func__, file_name, strerror(errno)); 235 fclose(file); 236 return -errno; 237 } 238 if (1 != fscanf(cpu_file, "%d", &online)) { 239 ALOGE("%s: failed to read CPU online information from file: %s (%s)", __func__, 240 file_name, strerror(errno)); 241 fclose(file); 242 fclose(cpu_file); 243 return errno ? -errno : -EIO; 244 } 245 fclose(cpu_file); 246 247 list[size] = (cpu_usage_t) { 248 .name = CPU_LABEL[size], 249 .active = active, 250 .total = total, 251 .is_online = online 252 }; 253 254 size++; 255 } 256 fclose(file); 257 258 if (size != CPU_NUM) { 259 ALOGE("/proc/stat file has incorrect format."); 260 return -EIO; 261 } 262 return CPU_NUM; 263 } 264 265 static struct hw_module_methods_t thermal_module_methods = { 266 .open = NULL, 267 }; 268 269 thermal_module_t HAL_MODULE_INFO_SYM = { 270 .common = { 271 .tag = HARDWARE_MODULE_TAG, 272 .module_api_version = THERMAL_HARDWARE_MODULE_API_VERSION_0_1, 273 .hal_api_version = HARDWARE_HAL_API_VERSION, 274 .id = THERMAL_HARDWARE_MODULE_ID, 275 .name = "Marlin / Sailfish Thermal HAL", 276 .author = "The Android Open Source Project", 277 .methods = &thermal_module_methods, 278 }, 279 .getTemperatures = get_temperatures, 280 .getCpuUsages = get_cpu_usages, 281 }; 282