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