diff options
Diffstat (limited to 'thermal_common.c')
-rw-r--r-- | thermal_common.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/thermal_common.c b/thermal_common.c new file mode 100644 index 0000000..9374929 --- /dev/null +++ b/thermal_common.c @@ -0,0 +1,385 @@ +/* + * 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; +} + |