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 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