blob: 93749294422a04e02c337bf713b917a3399e56f7 [file] [log] [blame]
/*
* 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;
}