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 SKIN_TEMPERATURE_FILE "/sys/class/hwmon/hwmon2/device/xo_therm" 34 #define SKIN_TEMPERATURE_FORMAT "Result:%f Raw:%*d\n" 35 #define CPU_ONLINE_FILE_FORMAT "/sys/devices/system/cpu/cpu%d/online" 36 37 #define BATTERY_SENSOR_NUM 1 38 #define GPU_SENSOR_NUM 12 39 40 const int CPU_SENSORS[] = {8, 8, 9, 10, 13, 14}; 41 42 #define CPU_NUM (sizeof(CPU_SENSORS) / sizeof(int)) 43 #define TEMPERATURE_NUM 9 44 45 // qcom, therm-reset-temp 46 #define CPU_SHUTDOWN_THRESHOLD 115 47 //qcom, limit-temp 48 #define CPU_THROTTLING_THRESHOLD 60 49 50 #define BATTERY_SHUTDOWN_THRESHOLD 60 51 // vendor/lge/bullhead/proprietary/thermal-engine/thermal-engine-8992.conf 52 #define SKIN_THROTTLING_THRESHOLD 37 53 54 #define GPU_LABEL "GPU" 55 #define BATTERY_LABEL "battery" 56 #define SKIN_LABEL "skin" 57 58 const char *CPU_LABEL[] = {"CPU0", "CPU1", "CPU2", "CPU3", "CPU4", "CPU5"}; 59 60 /** 61 * Reads device temperature. 62 * 63 * @param file_name Name of file with temperature. 64 * @param temperature_format Format of temperature file. 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 values. 71 * 72 * @return 0 on success or negative value -errno on error. 73 */ 74 static ssize_t read_temperature(const char *file_name, const char *temperature_format, int type, 75 const char *name, float mult, float throttling_threshold, float shutdown_threshold, 76 temperature_t *out) { 77 FILE *file; 78 float temp; 79 80 file = fopen(file_name, "r"); 81 if (file == NULL) { 82 ALOGE("%s: failed to open: %s", __func__, strerror(errno)); 83 return -errno; 84 } 85 if (1 != fscanf(file, temperature_format, &temp)) { 86 fclose(file); 87 ALOGE("%s: failed to read a float: %s", __func__, strerror(errno)); 88 return errno ? -errno : -EIO; 89 } 90 91 fclose(file); 92 93 (*out) = (temperature_t) { 94 .type = type, 95 .name = name, 96 .current_value = temp * mult, 97 .throttling_threshold = throttling_threshold, 98 .shutdown_threshold = shutdown_threshold, 99 .vr_throttling_threshold = UNKNOWN_TEMPERATURE 100 }; 101 102 return 0; 103 } 104 105 static ssize_t get_cpu_temperatures(temperature_t *list, size_t size) { 106 size_t cpu; 107 108 for (cpu = 0; cpu < CPU_NUM; cpu++) { 109 char file_name[MAX_LENGTH]; 110 111 if (cpu >= size) { 112 break; 113 } 114 115 sprintf(file_name, TEMPERATURE_FILE_FORMAT, CPU_SENSORS[cpu]); 116 // tsens_tz_sensor[7, 7, 9, 10, 13, 14]: temperature in Celsius. 117 ssize_t result = read_temperature(file_name, "%f", DEVICE_TEMPERATURE_CPU, CPU_LABEL[cpu], 118 1, CPU_THROTTLING_THRESHOLD, CPU_SHUTDOWN_THRESHOLD, &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 char file_name[MAX_LENGTH]; 130 131 if (list == NULL) { 132 return TEMPERATURE_NUM; 133 } 134 135 result = get_cpu_temperatures(list, size); 136 if (result < 0) { 137 return result; 138 } 139 current_index += result; 140 141 // GPU temperautre. 142 if (current_index < size) { 143 // tsens_tz_sensor12: temperature in Celsius. 144 sprintf(file_name, TEMPERATURE_FILE_FORMAT, GPU_SENSOR_NUM); 145 result = read_temperature(file_name, "%f", DEVICE_TEMPERATURE_GPU, GPU_LABEL, 1, 146 UNKNOWN_TEMPERATURE, UNKNOWN_TEMPERATURE, &list[current_index]); 147 if (result != 0) { 148 return result; 149 } 150 current_index++; 151 } 152 153 // Battery temperautre. 154 if (current_index < size) { 155 // hwmon sensor: battery: temperature in millidegrees Celsius. 156 sprintf(file_name, TEMPERATURE_FILE_FORMAT, BATTERY_SENSOR_NUM); 157 result = read_temperature(file_name, "%f", DEVICE_TEMPERATURE_BATTERY, BATTERY_LABEL, 158 0.001, UNKNOWN_TEMPERATURE, BATTERY_SHUTDOWN_THRESHOLD, &list[current_index]); 159 if (result != 0) { 160 return result; 161 } 162 current_index++; 163 } 164 165 // Skin temperature. 166 if (current_index < size) { 167 // xo_therm: temperature in Celsius. 168 result = read_temperature(SKIN_TEMPERATURE_FILE, SKIN_TEMPERATURE_FORMAT, 169 DEVICE_TEMPERATURE_SKIN, SKIN_LABEL, 1, SKIN_THROTTLING_THRESHOLD, 170 UNKNOWN_TEMPERATURE, &list[current_index]); 171 if (result != 0) { 172 return result; 173 } 174 current_index++; 175 } 176 return TEMPERATURE_NUM; 177 } 178 179 static ssize_t get_cpu_usages(thermal_module_t *module, cpu_usage_t *list) { 180 int vals, cpu_num, online; 181 ssize_t read; 182 uint64_t user, nice, system, idle, active, total; 183 char *line = NULL; 184 size_t len = 0; 185 size_t size = 0; 186 char file_name[MAX_LENGTH]; 187 FILE *file; 188 FILE *cpu_file; 189 190 if (list == NULL) { 191 return CPU_NUM; 192 } 193 194 file = fopen(CPU_USAGE_FILE, "r"); 195 if (file == NULL) { 196 ALOGE("%s: failed to open: %s", __func__, strerror(errno)); 197 return -errno; 198 } 199 200 while ((read = getline(&line, &len, file)) != -1) { 201 // Skip non "cpu[0-9]" lines. 202 if (strnlen(line, read) < 4 || strncmp(line, "cpu", 3) != 0 || !isdigit(line[3])) { 203 free(line); 204 line = NULL; 205 len = 0; 206 continue; 207 } 208 209 vals = sscanf(line, "cpu%d %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64, &cpu_num, &user, 210 &nice, &system, &idle); 211 212 free(line); 213 line = NULL; 214 len = 0; 215 216 if (vals != 5 || size == CPU_NUM) { 217 if (vals != 5) { 218 ALOGE("%s: failed to read CPU information from file: %s", __func__, 219 strerror(errno)); 220 } else { 221 ALOGE("/proc/stat file has incorrect format."); 222 } 223 fclose(file); 224 return errno ? -errno : -EIO; 225 } 226 227 active = user + nice + system; 228 total = active + idle; 229 230 // Read online CPU information. 231 snprintf(file_name, MAX_LENGTH, CPU_ONLINE_FILE_FORMAT, cpu_num); 232 cpu_file = fopen(file_name, "r"); 233 online = 0; 234 if (cpu_file == NULL) { 235 ALOGE("%s: failed to open file: %s (%s)", __func__, file_name, strerror(errno)); 236 fclose(file); 237 return -errno; 238 } 239 if (1 != fscanf(cpu_file, "%d", &online)) { 240 ALOGE("%s: failed to read CPU online information from file: %s (%s)", __func__, 241 file_name, strerror(errno)); 242 fclose(file); 243 fclose(cpu_file); 244 return errno ? -errno : -EIO; 245 } 246 fclose(cpu_file); 247 248 list[size] = (cpu_usage_t) { 249 .name = CPU_LABEL[size], 250 .active = active, 251 .total = total, 252 .is_online = online 253 }; 254 255 size++; 256 } 257 258 fclose(file); 259 260 if (size != CPU_NUM) { 261 ALOGE("/proc/stat file has incorrect format."); 262 return -EIO; 263 } 264 265 return CPU_NUM; 266 } 267 268 static struct hw_module_methods_t thermal_module_methods = { 269 .open = NULL, 270 }; 271 272 thermal_module_t HAL_MODULE_INFO_SYM = { 273 .common = { 274 .tag = HARDWARE_MODULE_TAG, 275 .module_api_version = THERMAL_HARDWARE_MODULE_API_VERSION_0_1, 276 .hal_api_version = HARDWARE_HAL_API_VERSION, 277 .id = THERMAL_HARDWARE_MODULE_ID, 278 .name = "Bullhead Thermal HAL", 279 .author = "The Android Open Source Project", 280 .methods = &thermal_module_methods, 281 }, 282 .getTemperatures = get_temperatures, 283 .getCpuUsages = get_cpu_usages, 284 }; 285