Home | History | Annotate | Download | only in thermal
      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