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