Commit | Line | Data |
---|---|---|
94f69966 JP |
1 | /* |
2 | * sysfs.c sysfs ABI access functions for TMON program | |
3 | * | |
4 | * Copyright (C) 2013 Intel Corporation. All rights reserved. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License version | |
8 | * 2 or later as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * Author: Jacob Pan <jacob.jun.pan@linux.intel.com> | |
16 | * | |
17 | */ | |
18 | #include <unistd.h> | |
19 | #include <stdio.h> | |
20 | #include <stdlib.h> | |
21 | #include <string.h> | |
22 | #include <stdint.h> | |
23 | #include <dirent.h> | |
24 | #include <libintl.h> | |
25 | #include <ctype.h> | |
26 | #include <time.h> | |
27 | #include <syslog.h> | |
28 | #include <sys/time.h> | |
29 | #include <errno.h> | |
30 | ||
31 | #include "tmon.h" | |
32 | ||
33 | struct tmon_platform_data ptdata; | |
34 | const char *trip_type_name[] = { | |
35 | "critical", | |
36 | "hot", | |
37 | "passive", | |
38 | "active", | |
39 | }; | |
40 | ||
41 | int sysfs_set_ulong(char *path, char *filename, unsigned long val) | |
42 | { | |
43 | FILE *fd; | |
44 | int ret = -1; | |
45 | char filepath[256]; | |
46 | ||
47 | snprintf(filepath, 256, "%s/%s", path, filename); | |
48 | ||
49 | fd = fopen(filepath, "w"); | |
50 | if (!fd) { | |
51 | syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath); | |
52 | return ret; | |
53 | } | |
54 | ret = fprintf(fd, "%lu", val); | |
55 | fclose(fd); | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
60 | /* history of thermal data, used for control algo */ | |
61 | #define NR_THERMAL_RECORDS 3 | |
62 | struct thermal_data_record trec[NR_THERMAL_RECORDS]; | |
63 | int cur_thermal_record; /* index to the trec array */ | |
64 | ||
65 | static int sysfs_get_ulong(char *path, char *filename, unsigned long *p_ulong) | |
66 | { | |
67 | FILE *fd; | |
68 | int ret = -1; | |
69 | char filepath[256]; | |
70 | ||
71 | snprintf(filepath, 256, "%s/%s", path, filename); | |
72 | ||
73 | fd = fopen(filepath, "r"); | |
74 | if (!fd) { | |
75 | syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath); | |
76 | return ret; | |
77 | } | |
78 | ret = fscanf(fd, "%lu", p_ulong); | |
79 | fclose(fd); | |
80 | ||
81 | return 0; | |
82 | } | |
83 | ||
84 | static int sysfs_get_string(char *path, char *filename, char *str) | |
85 | { | |
86 | FILE *fd; | |
87 | int ret = -1; | |
88 | char filepath[256]; | |
89 | ||
90 | snprintf(filepath, 256, "%s/%s", path, filename); | |
91 | ||
92 | fd = fopen(filepath, "r"); | |
93 | if (!fd) { | |
94 | syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath); | |
95 | return ret; | |
96 | } | |
97 | ret = fscanf(fd, "%256s", str); | |
98 | fclose(fd); | |
99 | ||
100 | return ret; | |
101 | } | |
102 | ||
103 | /* get states of the cooling device instance */ | |
104 | static int probe_cdev(struct cdev_info *cdi, char *path) | |
105 | { | |
106 | sysfs_get_string(path, "type", cdi->type); | |
107 | sysfs_get_ulong(path, "max_state", &cdi->max_state); | |
108 | sysfs_get_ulong(path, "cur_state", &cdi->cur_state); | |
109 | ||
110 | syslog(LOG_INFO, "%s: %s: type %s, max %lu, curr %lu inst %d\n", | |
111 | __func__, path, | |
112 | cdi->type, cdi->max_state, cdi->cur_state, cdi->instance); | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
117 | static int str_to_trip_type(char *name) | |
118 | { | |
119 | int i; | |
120 | ||
121 | for (i = 0; i < NR_THERMAL_TRIP_TYPE; i++) { | |
122 | if (!strcmp(name, trip_type_name[i])) | |
123 | return i; | |
124 | } | |
125 | ||
126 | return -ENOENT; | |
127 | } | |
128 | ||
129 | /* scan and fill in trip point info for a thermal zone and trip point id */ | |
130 | static int get_trip_point_data(char *tz_path, int tzid, int tpid) | |
131 | { | |
132 | char filename[256]; | |
133 | char temp_str[256]; | |
134 | int trip_type; | |
135 | ||
136 | if (tpid >= MAX_NR_TRIP) | |
137 | return -EINVAL; | |
138 | /* check trip point type */ | |
139 | snprintf(filename, sizeof(filename), "trip_point_%d_type", tpid); | |
140 | sysfs_get_string(tz_path, filename, temp_str); | |
141 | trip_type = str_to_trip_type(temp_str); | |
142 | if (trip_type < 0) { | |
143 | syslog(LOG_ERR, "%s:%s no matching type\n", __func__, temp_str); | |
144 | return -ENOENT; | |
145 | } | |
146 | ptdata.tzi[tzid].tp[tpid].type = trip_type; | |
147 | syslog(LOG_INFO, "%s:tz:%d tp:%d:type:%s type id %d\n", __func__, tzid, | |
148 | tpid, temp_str, trip_type); | |
149 | ||
150 | /* TODO: check attribute */ | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
155 | /* return instance id for file format such as trip_point_4_temp */ | |
156 | static int get_instance_id(char *name, int pos, int skip) | |
157 | { | |
158 | char *ch; | |
159 | int i = 0; | |
160 | ||
161 | ch = strtok(name, "_"); | |
162 | while (ch != NULL) { | |
163 | ++i; | |
164 | syslog(LOG_INFO, "%s:%s:%s:%d", __func__, name, ch, i); | |
165 | ch = strtok(NULL, "_"); | |
166 | if (pos == i) | |
167 | return atol(ch + skip); | |
168 | } | |
169 | ||
170 | return -1; | |
171 | } | |
172 | ||
173 | /* Find trip point info of a thermal zone */ | |
174 | static int find_tzone_tp(char *tz_name, char *d_name, struct tz_info *tzi, | |
175 | int tz_id) | |
176 | { | |
177 | int tp_id; | |
178 | unsigned long temp_ulong; | |
179 | ||
180 | if (strstr(d_name, "trip_point") && | |
181 | strstr(d_name, "temp")) { | |
182 | /* check if trip point temp is non-zero | |
183 | * ignore 0/invalid trip points | |
184 | */ | |
185 | sysfs_get_ulong(tz_name, d_name, &temp_ulong); | |
186 | if (temp_ulong < MAX_TEMP_KC) { | |
187 | tzi->nr_trip_pts++; | |
188 | /* found a valid trip point */ | |
189 | tp_id = get_instance_id(d_name, 2, 0); | |
190 | syslog(LOG_DEBUG, "tzone %s trip %d temp %lu tpnode %s", | |
191 | tz_name, tp_id, temp_ulong, d_name); | |
192 | if (tp_id < 0 || tp_id >= MAX_NR_TRIP) { | |
193 | syslog(LOG_ERR, "Failed to find TP inst %s\n", | |
194 | d_name); | |
195 | return -1; | |
196 | } | |
197 | get_trip_point_data(tz_name, tz_id, tp_id); | |
198 | tzi->tp[tp_id].temp = temp_ulong; | |
199 | } | |
200 | } | |
201 | ||
202 | return 0; | |
203 | } | |
204 | ||
205 | /* check cooling devices for binding info. */ | |
206 | static int find_tzone_cdev(struct dirent *nl, char *tz_name, | |
207 | struct tz_info *tzi, int tz_id, int cid) | |
208 | { | |
209 | unsigned long trip_instance = 0; | |
210 | char cdev_name_linked[256]; | |
211 | char cdev_name[256]; | |
212 | char cdev_trip_name[256]; | |
213 | int cdev_id; | |
214 | ||
215 | if (nl->d_type == DT_LNK) { | |
216 | syslog(LOG_DEBUG, "TZ%d: cdev: %s cid %d\n", tz_id, nl->d_name, | |
217 | cid); | |
218 | tzi->nr_cdev++; | |
219 | if (tzi->nr_cdev > ptdata.nr_cooling_dev) { | |
220 | syslog(LOG_ERR, "Err: Too many cdev? %d\n", | |
221 | tzi->nr_cdev); | |
222 | return -EINVAL; | |
223 | } | |
224 | /* find the link to real cooling device record binding */ | |
225 | snprintf(cdev_name, 256, "%s/%s", tz_name, nl->d_name); | |
226 | memset(cdev_name_linked, 0, sizeof(cdev_name_linked)); | |
227 | if (readlink(cdev_name, cdev_name_linked, | |
228 | sizeof(cdev_name_linked) - 1) != -1) { | |
229 | cdev_id = get_instance_id(cdev_name_linked, 1, | |
230 | sizeof("device") - 1); | |
231 | syslog(LOG_DEBUG, "cdev %s linked to %s : %d\n", | |
232 | cdev_name, cdev_name_linked, cdev_id); | |
233 | tzi->cdev_binding |= (1 << cdev_id); | |
234 | ||
235 | /* find the trip point in which the cdev is binded to | |
236 | * in this tzone | |
237 | */ | |
238 | snprintf(cdev_trip_name, 256, "%s%s", nl->d_name, | |
239 | "_trip_point"); | |
240 | sysfs_get_ulong(tz_name, cdev_trip_name, | |
241 | &trip_instance); | |
242 | /* validate trip point range, e.g. trip could return -1 | |
243 | * when passive is enabled | |
244 | */ | |
245 | if (trip_instance > MAX_NR_TRIP) | |
246 | trip_instance = 0; | |
247 | tzi->trip_binding[cdev_id] |= 1 << trip_instance; | |
248 | syslog(LOG_DEBUG, "cdev %s -> trip:%lu: 0x%lx %d\n", | |
249 | cdev_name, trip_instance, | |
250 | tzi->trip_binding[cdev_id], | |
251 | cdev_id); | |
252 | ||
253 | ||
254 | } | |
255 | return 0; | |
256 | } | |
257 | ||
258 | return -ENODEV; | |
259 | } | |
260 | ||
261 | ||
262 | ||
263 | /***************************************************************************** | |
264 | * Before calling scan_tzones, thermal sysfs must be probed to determine | |
265 | * the number of thermal zones and cooling devices. | |
266 | * We loop through each thermal zone and fill in tz_info struct, i.e. | |
267 | * ptdata.tzi[] | |
268 | root@jacob-chiefriver:~# tree -d /sys/class/thermal/thermal_zone0 | |
269 | /sys/class/thermal/thermal_zone0 | |
270 | |-- cdev0 -> ../cooling_device4 | |
271 | |-- cdev1 -> ../cooling_device3 | |
272 | |-- cdev10 -> ../cooling_device7 | |
273 | |-- cdev11 -> ../cooling_device6 | |
274 | |-- cdev12 -> ../cooling_device5 | |
275 | |-- cdev2 -> ../cooling_device2 | |
276 | |-- cdev3 -> ../cooling_device1 | |
277 | |-- cdev4 -> ../cooling_device0 | |
278 | |-- cdev5 -> ../cooling_device12 | |
279 | |-- cdev6 -> ../cooling_device11 | |
280 | |-- cdev7 -> ../cooling_device10 | |
281 | |-- cdev8 -> ../cooling_device9 | |
282 | |-- cdev9 -> ../cooling_device8 | |
283 | |-- device -> ../../../LNXSYSTM:00/device:62/LNXTHERM:00 | |
284 | |-- power | |
285 | `-- subsystem -> ../../../../class/thermal | |
286 | *****************************************************************************/ | |
287 | static int scan_tzones(void) | |
288 | { | |
289 | DIR *dir; | |
290 | struct dirent **namelist; | |
291 | char tz_name[256]; | |
292 | int i, j, n, k = 0; | |
293 | ||
294 | if (!ptdata.nr_tz_sensor) | |
295 | return -1; | |
296 | ||
297 | for (i = 0; i <= ptdata.max_tz_instance; i++) { | |
298 | memset(tz_name, 0, sizeof(tz_name)); | |
299 | snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE, i); | |
300 | ||
301 | dir = opendir(tz_name); | |
302 | if (!dir) { | |
303 | syslog(LOG_INFO, "Thermal zone %s skipped\n", tz_name); | |
304 | continue; | |
305 | } | |
306 | /* keep track of valid tzones */ | |
307 | n = scandir(tz_name, &namelist, 0, alphasort); | |
308 | if (n < 0) | |
309 | syslog(LOG_ERR, "scandir failed in %s", tz_name); | |
310 | else { | |
311 | sysfs_get_string(tz_name, "type", ptdata.tzi[k].type); | |
312 | ptdata.tzi[k].instance = i; | |
313 | /* detect trip points and cdev attached to this tzone */ | |
314 | j = 0; /* index for cdev */ | |
315 | ptdata.tzi[k].nr_cdev = 0; | |
316 | ptdata.tzi[k].nr_trip_pts = 0; | |
317 | while (n--) { | |
318 | char *temp_str; | |
319 | ||
320 | if (find_tzone_tp(tz_name, namelist[n]->d_name, | |
321 | &ptdata.tzi[k], k)) | |
322 | break; | |
323 | temp_str = strstr(namelist[n]->d_name, "cdev"); | |
324 | if (!temp_str) { | |
325 | free(namelist[n]); | |
326 | continue; | |
327 | } | |
328 | if (!find_tzone_cdev(namelist[n], tz_name, | |
329 | &ptdata.tzi[k], i, j)) | |
330 | j++; /* increment cdev index */ | |
331 | free(namelist[n]); | |
332 | } | |
333 | free(namelist); | |
334 | } | |
335 | /*TODO: reverse trip points */ | |
336 | closedir(dir); | |
337 | syslog(LOG_INFO, "TZ %d has %d cdev\n", i, | |
338 | ptdata.tzi[k].nr_cdev); | |
339 | k++; | |
340 | } | |
341 | ||
342 | return 0; | |
343 | } | |
344 | ||
345 | static int scan_cdevs(void) | |
346 | { | |
347 | DIR *dir; | |
348 | struct dirent **namelist; | |
349 | char cdev_name[256]; | |
350 | int i, n, k = 0; | |
351 | ||
352 | if (!ptdata.nr_cooling_dev) { | |
353 | fprintf(stderr, "No cooling devices found\n"); | |
354 | return 0; | |
355 | } | |
356 | for (i = 0; i <= ptdata.max_cdev_instance; i++) { | |
357 | memset(cdev_name, 0, sizeof(cdev_name)); | |
358 | snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, CDEV, i); | |
359 | ||
360 | dir = opendir(cdev_name); | |
361 | if (!dir) { | |
362 | syslog(LOG_INFO, "Cooling dev %s skipped\n", cdev_name); | |
363 | /* there is a gap in cooling device id, check again | |
364 | * for the same index. | |
365 | */ | |
366 | continue; | |
367 | } | |
368 | ||
369 | n = scandir(cdev_name, &namelist, 0, alphasort); | |
370 | if (n < 0) | |
371 | syslog(LOG_ERR, "scandir failed in %s", cdev_name); | |
372 | else { | |
373 | sysfs_get_string(cdev_name, "type", ptdata.cdi[k].type); | |
374 | ptdata.cdi[k].instance = i; | |
375 | if (strstr(ptdata.cdi[k].type, ctrl_cdev)) { | |
376 | ptdata.cdi[k].flag |= CDEV_FLAG_IN_CONTROL; | |
377 | syslog(LOG_DEBUG, "control cdev id %d\n", i); | |
378 | } | |
379 | while (n--) | |
380 | free(namelist[n]); | |
381 | free(namelist); | |
382 | } | |
383 | closedir(dir); | |
384 | k++; | |
385 | } | |
386 | return 0; | |
387 | } | |
388 | ||
389 | ||
390 | int probe_thermal_sysfs(void) | |
391 | { | |
392 | DIR *dir; | |
393 | struct dirent **namelist; | |
394 | int n; | |
395 | ||
396 | dir = opendir(THERMAL_SYSFS); | |
397 | if (!dir) { | |
398 | fprintf(stderr, "\nNo thermal sysfs, exit\n"); | |
399 | return -1; | |
400 | } | |
401 | n = scandir(THERMAL_SYSFS, &namelist, 0, alphasort); | |
402 | if (n < 0) | |
403 | syslog(LOG_ERR, "scandir failed in thermal sysfs"); | |
404 | else { | |
405 | /* detect number of thermal zones and cooling devices */ | |
406 | while (n--) { | |
407 | int inst; | |
408 | ||
409 | if (strstr(namelist[n]->d_name, CDEV)) { | |
410 | inst = get_instance_id(namelist[n]->d_name, 1, | |
411 | sizeof("device") - 1); | |
412 | /* keep track of the max cooling device since | |
413 | * there may be gaps. | |
414 | */ | |
415 | if (inst > ptdata.max_cdev_instance) | |
416 | ptdata.max_cdev_instance = inst; | |
417 | ||
418 | syslog(LOG_DEBUG, "found cdev: %s %d %d\n", | |
419 | namelist[n]->d_name, | |
420 | ptdata.nr_cooling_dev, | |
421 | ptdata.max_cdev_instance); | |
422 | ptdata.nr_cooling_dev++; | |
423 | } else if (strstr(namelist[n]->d_name, TZONE)) { | |
424 | inst = get_instance_id(namelist[n]->d_name, 1, | |
425 | sizeof("zone") - 1); | |
426 | if (inst > ptdata.max_tz_instance) | |
427 | ptdata.max_tz_instance = inst; | |
428 | ||
429 | syslog(LOG_DEBUG, "found tzone: %s %d %d\n", | |
430 | namelist[n]->d_name, | |
431 | ptdata.nr_tz_sensor, | |
432 | ptdata.max_tz_instance); | |
433 | ptdata.nr_tz_sensor++; | |
434 | } | |
435 | free(namelist[n]); | |
436 | } | |
437 | free(namelist); | |
438 | } | |
439 | syslog(LOG_INFO, "found %d tzone(s), %d cdev(s), target zone %d\n", | |
440 | ptdata.nr_tz_sensor, ptdata.nr_cooling_dev, | |
441 | target_thermal_zone); | |
442 | closedir(dir); | |
443 | ||
444 | if (!ptdata.nr_tz_sensor) { | |
445 | fprintf(stderr, "\nNo thermal zones found, exit\n\n"); | |
446 | return -1; | |
447 | } | |
448 | ||
e4e458b4 | 449 | ptdata.tzi = calloc(ptdata.max_tz_instance+1, sizeof(struct tz_info)); |
94f69966 JP |
450 | if (!ptdata.tzi) { |
451 | fprintf(stderr, "Err: allocate tz_info\n"); | |
452 | return -1; | |
453 | } | |
454 | ||
455 | /* we still show thermal zone information if there is no cdev */ | |
456 | if (ptdata.nr_cooling_dev) { | |
e4e458b4 AS |
457 | ptdata.cdi = calloc(ptdata.max_cdev_instance + 1, |
458 | sizeof(struct cdev_info)); | |
94f69966 JP |
459 | if (!ptdata.cdi) { |
460 | free(ptdata.tzi); | |
461 | fprintf(stderr, "Err: allocate cdev_info\n"); | |
462 | return -1; | |
463 | } | |
464 | } | |
465 | ||
466 | /* now probe tzones */ | |
467 | if (scan_tzones()) | |
468 | return -1; | |
469 | if (scan_cdevs()) | |
470 | return -1; | |
471 | return 0; | |
472 | } | |
473 | ||
474 | /* convert sysfs zone instance to zone array index */ | |
475 | int zone_instance_to_index(int zone_inst) | |
476 | { | |
477 | int i; | |
478 | ||
479 | for (i = 0; i < ptdata.nr_tz_sensor; i++) | |
480 | if (ptdata.tzi[i].instance == zone_inst) | |
481 | return i; | |
482 | return -ENOENT; | |
483 | } | |
484 | ||
485 | /* read temperature of all thermal zones */ | |
486 | int update_thermal_data() | |
487 | { | |
488 | int i; | |
489 | char tz_name[256]; | |
490 | static unsigned long samples; | |
491 | ||
492 | if (!ptdata.nr_tz_sensor) { | |
493 | syslog(LOG_ERR, "No thermal zones found!\n"); | |
494 | return -1; | |
495 | } | |
496 | ||
497 | /* circular buffer for keeping historic data */ | |
498 | if (cur_thermal_record >= NR_THERMAL_RECORDS) | |
499 | cur_thermal_record = 0; | |
500 | gettimeofday(&trec[cur_thermal_record].tv, NULL); | |
501 | if (tmon_log) { | |
502 | fprintf(tmon_log, "%lu ", ++samples); | |
503 | fprintf(tmon_log, "%3.1f ", p_param.t_target); | |
504 | } | |
505 | for (i = 0; i < ptdata.nr_tz_sensor; i++) { | |
506 | memset(tz_name, 0, sizeof(tz_name)); | |
507 | snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE, | |
508 | ptdata.tzi[i].instance); | |
509 | sysfs_get_ulong(tz_name, "temp", | |
510 | &trec[cur_thermal_record].temp[i]); | |
511 | if (tmon_log) | |
512 | fprintf(tmon_log, "%lu ", | |
513 | trec[cur_thermal_record].temp[i]/1000); | |
514 | } | |
515 | for (i = 0; i < ptdata.nr_cooling_dev; i++) { | |
516 | char cdev_name[256]; | |
517 | unsigned long val; | |
518 | ||
519 | snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, CDEV, | |
520 | ptdata.cdi[i].instance); | |
521 | probe_cdev(&ptdata.cdi[i], cdev_name); | |
522 | val = ptdata.cdi[i].cur_state; | |
523 | if (val > 1000000) | |
524 | val = 0; | |
525 | if (tmon_log) | |
526 | fprintf(tmon_log, "%lu ", val); | |
527 | } | |
528 | ||
529 | if (tmon_log) { | |
530 | fprintf(tmon_log, "\n"); | |
531 | fflush(tmon_log); | |
532 | } | |
533 | ||
534 | return 0; | |
535 | } | |
536 | ||
537 | void set_ctrl_state(unsigned long state) | |
538 | { | |
539 | char ctrl_cdev_path[256]; | |
540 | int i; | |
541 | unsigned long cdev_state; | |
542 | ||
543 | if (no_control) | |
544 | return; | |
545 | /* set all ctrl cdev to the same state */ | |
546 | for (i = 0; i < ptdata.nr_cooling_dev; i++) { | |
547 | if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) { | |
548 | if (ptdata.cdi[i].max_state < 10) { | |
549 | strcpy(ctrl_cdev, "None."); | |
550 | return; | |
551 | } | |
552 | /* scale to percentage of max_state */ | |
553 | cdev_state = state * ptdata.cdi[i].max_state/100; | |
554 | syslog(LOG_DEBUG, | |
555 | "ctrl cdev %d set state %lu scaled to %lu\n", | |
556 | ptdata.cdi[i].instance, state, cdev_state); | |
557 | snprintf(ctrl_cdev_path, 256, "%s/%s%d", THERMAL_SYSFS, | |
558 | CDEV, ptdata.cdi[i].instance); | |
559 | syslog(LOG_DEBUG, "ctrl cdev path %s", ctrl_cdev_path); | |
560 | sysfs_set_ulong(ctrl_cdev_path, "cur_state", | |
561 | cdev_state); | |
562 | } | |
563 | } | |
564 | } | |
565 | ||
566 | void get_ctrl_state(unsigned long *state) | |
567 | { | |
568 | char ctrl_cdev_path[256]; | |
569 | int ctrl_cdev_id = -1; | |
570 | int i; | |
571 | ||
572 | /* TODO: take average of all ctrl types. also consider change based on | |
573 | * uevent. Take the first reading for now. | |
574 | */ | |
575 | for (i = 0; i < ptdata.nr_cooling_dev; i++) { | |
576 | if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) { | |
577 | ctrl_cdev_id = ptdata.cdi[i].instance; | |
578 | syslog(LOG_INFO, "ctrl cdev %d get state\n", | |
579 | ptdata.cdi[i].instance); | |
580 | break; | |
581 | } | |
582 | } | |
583 | if (ctrl_cdev_id == -1) { | |
584 | *state = 0; | |
585 | return; | |
586 | } | |
587 | snprintf(ctrl_cdev_path, 256, "%s/%s%d", THERMAL_SYSFS, | |
588 | CDEV, ctrl_cdev_id); | |
589 | sysfs_get_ulong(ctrl_cdev_path, "cur_state", state); | |
590 | } | |
591 | ||
592 | void free_thermal_data(void) | |
593 | { | |
594 | free(ptdata.tzi); | |
595 | free(ptdata.cdi); | |
596 | } |