1 /*
2 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
3 * Not a contribution
4 * Copyright (C) 2016 The Android Open Source Project
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <dirent.h>
24
25 #define LOG_TAG "ThermalHAL-UTIL"
26 #include <utils/Log.h>
27
28 #include <hardware/hardware.h>
29 #include <hardware/thermal.h>
30 #include "thermal_common.h"
31
32 #define MAX_LENGTH 50
33 #define MAX_PATH (256)
34 #define CPU_LABEL "CPU%d"
35 #define THERMAL_SYSFS "/sys/devices/virtual/thermal"
36 #define TZ_DIR_NAME "thermal_zone"
37 #define TZ_DIR_FMT "thermal_zone%d"
38 #define THERMAL_TYPE "/sys/devices/virtual/thermal/%s/type"
39 #define TEMPERATURE_FILE_FORMAT "/sys/class/thermal/thermal_zone%d/temp"
40
41 static char **cpu_label;
42 static struct vendor_temperature *sensors;
43 static unsigned int sensor_cnt;
44
45 /**
46 * Get number of cpus of target.
47 *
48 * @return number of cpus on success or 0 on error.
49 */
get_num_cpus()50 size_t get_num_cpus() {
51 ALOGD("Entering %s",__func__);
52 static int ncpus;
53
54 if (!ncpus) {
55 ncpus = (int)sysconf(_SC_NPROCESSORS_CONF);
56 if (ncpus < 1)
57 ALOGE("%s: Error retrieving number of cores", __func__);
58 }
59 return ncpus;
60 }
61
62 /**
63 * Get cpu label for a given cpu.
64 *
65 * @param cpu_num: cpu number.
66 *
67 * @return cpu label string on success or NULL on error.
68 */
get_cpu_label(unsigned int cpu_num)69 const char *get_cpu_label(unsigned int cpu_num) {
70 ALOGD("Entering %s",__func__);
71 unsigned int cpu = 0;
72
73 if (cpu_label == NULL) {
74 cpu_label= (char**)calloc(get_num_cpus(), sizeof(char *));
75 if (!cpu_label)
76 return NULL;
77 for(cpu = 0; cpu < get_num_cpus(); cpu++) {
78 cpu_label[cpu] = (char *)calloc(sizeof("CPUN"), sizeof(char));
79 if(!cpu_label[cpu])
80 return NULL;
81 snprintf(cpu_label[cpu], sizeof("CPUN"), CPU_LABEL, cpu);
82 }
83 }
84 if(cpu_num >= get_num_cpus())
85 return NULL;
86
87 return cpu_label[cpu_num];
88 }
89
90 /**
91 * Read data from a target sysfs file.
92 *
93 * @param path: Absolute path for a file to be read.
94 * @param buf: Char buffer to store data from file.
95 * @param count: Size of data buffer.
96 *
97 * @return number of bytes read on success or negative value on error.
98 */
read_line_from_file(const char * path,char * buf,size_t count)99 int read_line_from_file(const char *path, char *buf, size_t count)
100 {
101 char * fgets_ret;
102 FILE * fd;
103 int rv;
104
105 fd = fopen(path, "r");
106 if (fd == NULL)
107 return -1;
108
109 fgets_ret = fgets(buf, (int)count, fd);
110 if (NULL != fgets_ret) {
111 rv = (int)strlen(buf);
112 } else {
113 rv = ferror(fd);
114 }
115
116 fclose(fd);
117
118 return rv;
119 }
120
121 /**
122 * Function to get thermal zone id from sensor name.
123 *
124 * @param sensor_name: Name of sensor.
125 *
126 * @return positive integer on success or negative value on error.
127 */
get_tzn(const char * sensor_name)128 static int get_tzn(const char *sensor_name)
129 {
130 DIR *tdir = NULL;
131 struct dirent *tdirent = NULL;
132 int found = -1;
133 int tzn = 0;
134 char name[MAX_PATH] = {0};
135 char cwd[MAX_PATH] = {0};
136 int ret = 0;
137
138 if (!getcwd(cwd, sizeof(cwd)))
139 return found;
140
141 /* Change dir to read the entries. Doesnt work otherwise */
142 ret = chdir(THERMAL_SYSFS);
143 if (ret) {
144 ALOGE("Unable to change to %s\n", THERMAL_SYSFS);
145 return found;
146 }
147 tdir = opendir(THERMAL_SYSFS);
148 if (!tdir) {
149 ALOGE("Unable to open %s\n", THERMAL_SYSFS);
150 return found;
151 }
152
153 while ((tdirent = readdir(tdir))) {
154 char buf[50];
155 struct dirent *tzdirent;
156 DIR *tzdir = NULL;
157
158 if (strncmp(tdirent->d_name, TZ_DIR_NAME,
159 strlen(TZ_DIR_NAME)) != 0)
160 continue;
161
162 tzdir = opendir(tdirent->d_name);
163 if (!tzdir)
164 continue;
165 while ((tzdirent = readdir(tzdir))) {
166 if (strcmp(tzdirent->d_name, "type"))
167 continue;
168 snprintf(name, MAX_PATH, THERMAL_TYPE,
169 tdirent->d_name);
170 ret = read_line_from_file(name, buf, sizeof(buf));
171 if (ret <= 0) {
172 ALOGE("%s: sensor name read error for tz:%s\n",
173 __func__, tdirent->d_name);
174 break;
175 }
176 if (buf[ret - 1] == '\n')
177 buf[ret - 1] = '\0';
178 else
179 buf[ret] = '\0';
180
181 if (!strcmp(buf, sensor_name)) {
182 found = 1;
183 break;
184 }
185 }
186 closedir(tzdir);
187 if (found == 1)
188 break;
189 }
190
191 if (found == 1) {
192 sscanf(tdirent->d_name, TZ_DIR_FMT, &tzn);
193 ALOGD("Sensor %s found at tz: %d\n",
194 sensor_name, tzn);
195 found = tzn;
196 }
197
198 closedir(tdir);
199 /* Restore current working dir */
200 ret = chdir(cwd);
201
202 return found;
203 }
204
205 /**
206 * Helper function for sensor intialization.
207 *
208 * @param v_sen_t: pointer to a sensor static config.
209 * @param sensor: pointer to a sensor vendor_temperature structure.
210 * @param type: Type of sensor ie cpu, battery, gpu, skin etc.
211 * @param sens_idx: Index for sensor of same type.
212 *
213 * @return 0 on success or negative value -errno on error.
214 */
initialize_sensor(struct target_therm_cfg * v_sen_t,struct vendor_temperature * sensor,enum temperature_type type,int sens_idx)215 static int initialize_sensor(struct target_therm_cfg *v_sen_t,
216 struct vendor_temperature *sensor,
217 enum temperature_type type,
218 int sens_idx)
219 {
220 if (v_sen_t == NULL || sensor == NULL ||
221 sens_idx < 0) {
222 ALOGE("%s:Invalid input, sens_idx%d\n", __func__, sens_idx);
223 return -1;
224 }
225
226 sensor->tzn = get_tzn(v_sen_t->sensor_list[sens_idx]);
227 if (sensor->tzn < 0) {
228 ALOGE("No thermal zone for sensor: %s, ret:%d\n",
229 v_sen_t->sensor_list[sens_idx], sensor->tzn);
230 return -1;
231 }
232 if (type == DEVICE_TEMPERATURE_CPU)
233 sensor->t.name = get_cpu_label(sens_idx);
234 else
235 sensor->t.name = v_sen_t->label;
236
237 sensor->t.type = v_sen_t->type;
238 sensor->mult = v_sen_t->mult;
239
240 if (v_sen_t->throt_thresh != 0)
241 sensor->t.throttling_threshold = v_sen_t->throt_thresh;
242 else
243 sensor->t.throttling_threshold = UNKNOWN_TEMPERATURE;
244
245 if (v_sen_t->shutdwn_thresh != 0)
246 sensor->t.shutdown_threshold = v_sen_t->shutdwn_thresh;
247 else
248 sensor->t.shutdown_threshold = UNKNOWN_TEMPERATURE;
249
250 if (v_sen_t->vr_thresh != 0)
251 sensor->t.vr_throttling_threshold = v_sen_t->vr_thresh;
252 else
253 sensor->t.vr_throttling_threshold = UNKNOWN_TEMPERATURE;
254
255 return 0;
256 }
257
258 /**
259 * Initialize all sensors.
260 *
261 * @param v_sen_t: Base pointer to array of target specific sensor configs.
262 * @param cfg_cnt: Number of set of config for a given target.
263 *
264 * @return number of sensor on success or negative value or zero on error.
265 */
thermal_zone_init(struct target_therm_cfg * v_sen_t,int cfg_cnt)266 int thermal_zone_init(struct target_therm_cfg *v_sen_t, int cfg_cnt)
267 {
268 unsigned int idx = 0, cpu = 0;
269 int j = 0;
270
271 if (sensors != NULL && sensor_cnt != 0)
272 return sensor_cnt;
273
274 if (v_sen_t == NULL || cfg_cnt == 0) {
275 ALOGE("%s:Invalid input\n", __func__);
276 return -1;
277 }
278 sensors = calloc(get_num_cpus() + cfg_cnt - 1,
279 sizeof(struct vendor_temperature));
280
281 for (j = 0, idx = 0; j < cfg_cnt &&
282 idx < (get_num_cpus() + cfg_cnt - 1); j++) {
283 if (v_sen_t[j].type == DEVICE_TEMPERATURE_CPU) {
284 /* Initialize cpu thermal zone id */
285 for (cpu = 0; cpu < get_num_cpus() &&
286 idx < (get_num_cpus() + cfg_cnt - 1); cpu++, idx++) {
287 if (initialize_sensor(&v_sen_t[j], &sensors[idx],
288 v_sen_t[j].type, cpu)) {
289 free(sensors);
290 return -1;
291 }
292 }
293 } else {
294 /* Initialize misc thermal zone id */
295 if (initialize_sensor(&v_sen_t[j], &sensors[idx],
296 v_sen_t[j].type, 0)) {
297 free(sensors);
298 return -1;
299 }
300 idx++;
301 }
302 }
303 sensor_cnt = idx;
304
305 return sensor_cnt;
306 }
307
308 /**
309 * Reads sensor temperature.
310 *
311 * @param sensor_num: thermal zone id for the sensor to be read
312 * @param type: Device temperature type.
313 * @param name: Device temperature name.
314 * @param mult: Multiplier used to translate temperature to Celsius.
315 * @param throttling_threshold: Throttling threshold for the sensor.
316 * @param shutdown_threshold: Shutdown threshold for the sensor.
317 * @param out: Pointer to temperature_t structure that will be filled with
318 * temperature values.
319 *
320 * @return 0 on success or negative value -errno on error.
321 */
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)322 static ssize_t read_temperature(int sensor_num, int type, const char *name,
323 float mult, float throttling_threshold, float shutdown_threshold,
324 float vr_throttling_threshold,
325 temperature_t *out) {
326 ALOGD("Entering %s",__func__);
327 char file_name[MAX_LENGTH];
328 float temp;
329 char buf[16] = {0};
330 int ret = 0;
331
332 snprintf(file_name, sizeof(file_name), TEMPERATURE_FILE_FORMAT, sensor_num);
333 ret = read_line_from_file(file_name, buf, sizeof(buf));
334 if (ret <= 0) {
335 ALOGE("Temperature read error: %d for sensor[%d]:%s\n",
336 ret, sensor_num, name);
337 return -1;
338 }
339 temp = atof(buf);
340
341 (*out) = (temperature_t) {
342 .type = type,
343 .name = name,
344 .current_value = temp * mult,
345 .throttling_threshold = throttling_threshold,
346 .shutdown_threshold = shutdown_threshold,
347 .vr_throttling_threshold = vr_throttling_threshold
348 };
349
350 return 0;
351 }
352
353 /**
354 * Reads all sensor temperature.
355 *
356 * @param list: Base pointer to array of temperature_t structure that will be
357 * filled with temperature values.
358 * @param size: Number of sensor temperature needs to be filled in list.
359 *
360 * @return number of sensor data filled on success or 0 or negative value
361 * -errno on error.
362 */
get_temperature_for_all(temperature_t * list,size_t size)363 ssize_t get_temperature_for_all(temperature_t *list, size_t size)
364 {
365 ALOGD("Entering %s",__func__);
366 size_t idx;
367
368 if (sensors == NULL) {
369 ALOGE("No sensor configured\n");
370 return 0;
371 }
372
373 for (idx = 0; idx < sensor_cnt && idx < size; idx++) {
374 ssize_t result = read_temperature(sensors[idx].tzn, sensors[idx].t.type,
375 sensors[idx].t.name, sensors[idx].mult,
376 sensors[idx].t.throttling_threshold,
377 sensors[idx].t.shutdown_threshold,
378 sensors[idx].t.vr_throttling_threshold,
379 &list[idx]);
380 if (result != 0)
381 return result;
382 }
383 return idx;
384 }
385
386