| /* |
| * Copyright (c) 2018, The Linux Foundation. All rights reserved. |
| * Not a contribution |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <dirent.h> |
| |
| #define LOG_TAG "ThermalHAL-UTIL" |
| #include <utils/Log.h> |
| |
| #include <hardware/hardware.h> |
| #include <hardware/thermal.h> |
| #include "thermal_common.h" |
| |
| #define MAX_LENGTH 50 |
| #define MAX_PATH (256) |
| #define CPU_LABEL "CPU%d" |
| #define THERMAL_SYSFS "/sys/devices/virtual/thermal" |
| #define TZ_DIR_NAME "thermal_zone" |
| #define TZ_DIR_FMT "thermal_zone%d" |
| #define THERMAL_TYPE "/sys/devices/virtual/thermal/%s/type" |
| #define TEMPERATURE_FILE_FORMAT "/sys/class/thermal/thermal_zone%d/temp" |
| |
| static char **cpu_label; |
| static struct vendor_temperature *sensors; |
| static unsigned int sensor_cnt; |
| |
| /** |
| * Get number of cpus of target. |
| * |
| * @return number of cpus on success or 0 on error. |
| */ |
| size_t get_num_cpus() { |
| ALOGD("Entering %s",__func__); |
| static int ncpus; |
| |
| if (!ncpus) { |
| ncpus = (int)sysconf(_SC_NPROCESSORS_CONF); |
| if (ncpus < 1) |
| ALOGE("%s: Error retrieving number of cores", __func__); |
| } |
| return ncpus; |
| } |
| |
| /** |
| * Get cpu label for a given cpu. |
| * |
| * @param cpu_num: cpu number. |
| * |
| * @return cpu label string on success or NULL on error. |
| */ |
| const char *get_cpu_label(unsigned int cpu_num) { |
| ALOGD("Entering %s",__func__); |
| unsigned int cpu = 0; |
| |
| if (cpu_label == NULL) { |
| cpu_label= (char**)calloc(get_num_cpus(), sizeof(char *)); |
| if (!cpu_label) |
| return NULL; |
| for(cpu = 0; cpu < get_num_cpus(); cpu++) { |
| cpu_label[cpu] = (char *)calloc(sizeof("CPUN"), sizeof(char)); |
| if(!cpu_label[cpu]) |
| return NULL; |
| snprintf(cpu_label[cpu], sizeof("CPUN"), CPU_LABEL, cpu); |
| } |
| } |
| if(cpu_num >= get_num_cpus()) |
| return NULL; |
| |
| return cpu_label[cpu_num]; |
| } |
| |
| /** |
| * Read data from a target sysfs file. |
| * |
| * @param path: Absolute path for a file to be read. |
| * @param buf: Char buffer to store data from file. |
| * @param count: Size of data buffer. |
| * |
| * @return number of bytes read on success or negative value on error. |
| */ |
| int read_line_from_file(const char *path, char *buf, size_t count) |
| { |
| char * fgets_ret; |
| FILE * fd; |
| int rv; |
| |
| fd = fopen(path, "r"); |
| if (fd == NULL) |
| return -1; |
| |
| fgets_ret = fgets(buf, (int)count, fd); |
| if (NULL != fgets_ret) { |
| rv = (int)strlen(buf); |
| } else { |
| rv = ferror(fd); |
| } |
| |
| fclose(fd); |
| |
| return rv; |
| } |
| |
| /** |
| * Function to get thermal zone id from sensor name. |
| * |
| * @param sensor_name: Name of sensor. |
| * |
| * @return positive integer on success or negative value on error. |
| */ |
| static int get_tzn(const char *sensor_name) |
| { |
| DIR *tdir = NULL; |
| struct dirent *tdirent = NULL; |
| int found = -1; |
| int tzn = 0; |
| char name[MAX_PATH] = {0}; |
| char cwd[MAX_PATH] = {0}; |
| int ret = 0; |
| |
| if (!getcwd(cwd, sizeof(cwd))) |
| return found; |
| |
| /* Change dir to read the entries. Doesnt work otherwise */ |
| ret = chdir(THERMAL_SYSFS); |
| if (ret) { |
| ALOGE("Unable to change to %s\n", THERMAL_SYSFS); |
| return found; |
| } |
| tdir = opendir(THERMAL_SYSFS); |
| if (!tdir) { |
| ALOGE("Unable to open %s\n", THERMAL_SYSFS); |
| return found; |
| } |
| |
| while ((tdirent = readdir(tdir))) { |
| char buf[50]; |
| struct dirent *tzdirent; |
| DIR *tzdir = NULL; |
| |
| if (strncmp(tdirent->d_name, TZ_DIR_NAME, |
| strlen(TZ_DIR_NAME)) != 0) |
| continue; |
| |
| tzdir = opendir(tdirent->d_name); |
| if (!tzdir) |
| continue; |
| while ((tzdirent = readdir(tzdir))) { |
| if (strcmp(tzdirent->d_name, "type")) |
| continue; |
| snprintf(name, MAX_PATH, THERMAL_TYPE, |
| tdirent->d_name); |
| ret = read_line_from_file(name, buf, sizeof(buf)); |
| if (ret <= 0) { |
| ALOGE("%s: sensor name read error for tz:%s\n", |
| __func__, tdirent->d_name); |
| break; |
| } |
| if (buf[ret - 1] == '\n') |
| buf[ret - 1] = '\0'; |
| else |
| buf[ret] = '\0'; |
| |
| if (!strcmp(buf, sensor_name)) { |
| found = 1; |
| break; |
| } |
| } |
| closedir(tzdir); |
| if (found == 1) |
| break; |
| } |
| |
| if (found == 1) { |
| sscanf(tdirent->d_name, TZ_DIR_FMT, &tzn); |
| ALOGD("Sensor %s found at tz: %d\n", |
| sensor_name, tzn); |
| found = tzn; |
| } |
| |
| closedir(tdir); |
| /* Restore current working dir */ |
| ret = chdir(cwd); |
| |
| return found; |
| } |
| |
| /** |
| * Helper function for sensor intialization. |
| * |
| * @param v_sen_t: pointer to a sensor static config. |
| * @param sensor: pointer to a sensor vendor_temperature structure. |
| * @param type: Type of sensor ie cpu, battery, gpu, skin etc. |
| * @param sens_idx: Index for sensor of same type. |
| * |
| * @return 0 on success or negative value -errno on error. |
| */ |
| static int initialize_sensor(struct target_therm_cfg *v_sen_t, |
| struct vendor_temperature *sensor, |
| enum temperature_type type, |
| int sens_idx) |
| { |
| if (v_sen_t == NULL || sensor == NULL || |
| sens_idx < 0) { |
| ALOGE("%s:Invalid input, sens_idx%d\n", __func__, sens_idx); |
| return -1; |
| } |
| |
| sensor->tzn = get_tzn(v_sen_t->sensor_list[sens_idx]); |
| if (sensor->tzn < 0) { |
| ALOGE("No thermal zone for sensor: %s, ret:%d\n", |
| v_sen_t->sensor_list[sens_idx], sensor->tzn); |
| return -1; |
| } |
| if (type == DEVICE_TEMPERATURE_CPU) |
| sensor->t.name = get_cpu_label(sens_idx); |
| else |
| sensor->t.name = v_sen_t->label; |
| |
| sensor->t.type = v_sen_t->type; |
| sensor->mult = v_sen_t->mult; |
| |
| if (v_sen_t->throt_thresh != 0) |
| sensor->t.throttling_threshold = v_sen_t->throt_thresh; |
| else |
| sensor->t.throttling_threshold = UNKNOWN_TEMPERATURE; |
| |
| if (v_sen_t->shutdwn_thresh != 0) |
| sensor->t.shutdown_threshold = v_sen_t->shutdwn_thresh; |
| else |
| sensor->t.shutdown_threshold = UNKNOWN_TEMPERATURE; |
| |
| if (v_sen_t->vr_thresh != 0) |
| sensor->t.vr_throttling_threshold = v_sen_t->vr_thresh; |
| else |
| sensor->t.vr_throttling_threshold = UNKNOWN_TEMPERATURE; |
| |
| return 0; |
| } |
| |
| /** |
| * Initialize all sensors. |
| * |
| * @param v_sen_t: Base pointer to array of target specific sensor configs. |
| * @param cfg_cnt: Number of set of config for a given target. |
| * |
| * @return number of sensor on success or negative value or zero on error. |
| */ |
| int thermal_zone_init(struct target_therm_cfg *v_sen_t, int cfg_cnt) |
| { |
| unsigned int idx = 0, cpu = 0; |
| int j = 0; |
| |
| if (sensors != NULL && sensor_cnt != 0) |
| return sensor_cnt; |
| |
| if (v_sen_t == NULL || cfg_cnt == 0) { |
| ALOGE("%s:Invalid input\n", __func__); |
| return -1; |
| } |
| sensors = calloc(get_num_cpus() + cfg_cnt - 1, |
| sizeof(struct vendor_temperature)); |
| |
| for (j = 0, idx = 0; j < cfg_cnt && |
| idx < (get_num_cpus() + cfg_cnt - 1); j++) { |
| if (v_sen_t[j].type == DEVICE_TEMPERATURE_CPU) { |
| /* Initialize cpu thermal zone id */ |
| for (cpu = 0; cpu < get_num_cpus() && |
| idx < (get_num_cpus() + cfg_cnt - 1); cpu++, idx++) { |
| if (initialize_sensor(&v_sen_t[j], &sensors[idx], |
| v_sen_t[j].type, cpu)) { |
| free(sensors); |
| return -1; |
| } |
| } |
| } else { |
| /* Initialize misc thermal zone id */ |
| if (initialize_sensor(&v_sen_t[j], &sensors[idx], |
| v_sen_t[j].type, 0)) { |
| free(sensors); |
| return -1; |
| } |
| idx++; |
| } |
| } |
| sensor_cnt = idx; |
| |
| return sensor_cnt; |
| } |
| |
| /** |
| * Reads sensor temperature. |
| * |
| * @param sensor_num: thermal zone id for the sensor to be read |
| * @param type: Device temperature type. |
| * @param name: Device temperature name. |
| * @param mult: Multiplier used to translate temperature to Celsius. |
| * @param throttling_threshold: Throttling threshold for the sensor. |
| * @param shutdown_threshold: Shutdown threshold for the sensor. |
| * @param out: Pointer to temperature_t structure that will be filled with |
| * temperature values. |
| * |
| * @return 0 on success or negative value -errno on error. |
| */ |
| static ssize_t read_temperature(int sensor_num, int type, const char *name, |
| float mult, float throttling_threshold, float shutdown_threshold, |
| float vr_throttling_threshold, |
| temperature_t *out) { |
| ALOGD("Entering %s",__func__); |
| char file_name[MAX_LENGTH]; |
| float temp; |
| char buf[16] = {0}; |
| int ret = 0; |
| |
| snprintf(file_name, sizeof(file_name), TEMPERATURE_FILE_FORMAT, sensor_num); |
| ret = read_line_from_file(file_name, buf, sizeof(buf)); |
| if (ret <= 0) { |
| ALOGE("Temperature read error: %d for sensor[%d]:%s\n", |
| ret, sensor_num, name); |
| return -1; |
| } |
| temp = atof(buf); |
| |
| (*out) = (temperature_t) { |
| .type = type, |
| .name = name, |
| .current_value = temp * mult, |
| .throttling_threshold = throttling_threshold, |
| .shutdown_threshold = shutdown_threshold, |
| .vr_throttling_threshold = vr_throttling_threshold |
| }; |
| |
| return 0; |
| } |
| |
| /** |
| * Reads all sensor temperature. |
| * |
| * @param list: Base pointer to array of temperature_t structure that will be |
| * filled with temperature values. |
| * @param size: Number of sensor temperature needs to be filled in list. |
| * |
| * @return number of sensor data filled on success or 0 or negative value |
| * -errno on error. |
| */ |
| ssize_t get_temperature_for_all(temperature_t *list, size_t size) |
| { |
| ALOGD("Entering %s",__func__); |
| size_t idx; |
| |
| if (sensors == NULL) { |
| ALOGE("No sensor configured\n"); |
| return 0; |
| } |
| |
| for (idx = 0; idx < sensor_cnt && idx < size; idx++) { |
| ssize_t result = read_temperature(sensors[idx].tzn, sensors[idx].t.type, |
| sensors[idx].t.name, sensors[idx].mult, |
| sensors[idx].t.throttling_threshold, |
| sensors[idx].t.shutdown_threshold, |
| sensors[idx].t.vr_throttling_threshold, |
| &list[idx]); |
| if (result != 0) |
| return result; |
| } |
| return idx; |
| } |
| |