Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * sysfs.c sysfs ABI access functions for TMON program
0004  *
0005  * Copyright (C) 2013 Intel Corporation. All rights reserved.
0006  *
0007  * Author: Jacob Pan <jacob.jun.pan@linux.intel.com>
0008  */
0009 #include <unistd.h>
0010 #include <stdio.h>
0011 #include <stdlib.h>
0012 #include <string.h>
0013 #include <stdint.h>
0014 #include <dirent.h>
0015 #include <libintl.h>
0016 #include <limits.h>
0017 #include <ctype.h>
0018 #include <time.h>
0019 #include <syslog.h>
0020 #include <sys/time.h>
0021 #include <errno.h>
0022 
0023 #include "tmon.h"
0024 
0025 struct tmon_platform_data ptdata;
0026 const char *trip_type_name[] = {
0027     "critical",
0028     "hot",
0029     "passive",
0030     "active",
0031 };
0032 
0033 int sysfs_set_ulong(char *path, char *filename, unsigned long val)
0034 {
0035     FILE *fd;
0036     int ret = -1;
0037     char filepath[PATH_MAX + 2]; /* NUL and '/' */
0038 
0039     snprintf(filepath, sizeof(filepath), "%s/%s", path, filename);
0040 
0041     fd = fopen(filepath, "w");
0042     if (!fd) {
0043         syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath);
0044         return ret;
0045     }
0046     ret = fprintf(fd, "%lu", val);
0047     fclose(fd);
0048 
0049     return 0;
0050 }
0051 
0052 /* history of thermal data, used for control algo */
0053 #define NR_THERMAL_RECORDS 3
0054 struct thermal_data_record trec[NR_THERMAL_RECORDS];
0055 int cur_thermal_record; /* index to the trec array */
0056 
0057 static int sysfs_get_ulong(char *path, char *filename, unsigned long *p_ulong)
0058 {
0059     FILE *fd;
0060     int ret = -1;
0061     char filepath[PATH_MAX + 2]; /* NUL and '/' */
0062 
0063     snprintf(filepath, sizeof(filepath), "%s/%s", path, filename);
0064 
0065     fd = fopen(filepath, "r");
0066     if (!fd) {
0067         syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath);
0068         return ret;
0069     }
0070     ret = fscanf(fd, "%lu", p_ulong);
0071     fclose(fd);
0072 
0073     return 0;
0074 }
0075 
0076 static int sysfs_get_string(char *path, char *filename, char *str)
0077 {
0078     FILE *fd;
0079     int ret = -1;
0080     char filepath[PATH_MAX + 2]; /* NUL and '/' */
0081 
0082     snprintf(filepath, sizeof(filepath), "%s/%s", path, filename);
0083 
0084     fd = fopen(filepath, "r");
0085     if (!fd) {
0086         syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath);
0087         return ret;
0088     }
0089     ret = fscanf(fd, "%256s", str);
0090     fclose(fd);
0091 
0092     return ret;
0093 }
0094 
0095 /* get states of the cooling device instance */
0096 static int probe_cdev(struct cdev_info *cdi, char *path)
0097 {
0098     sysfs_get_string(path, "type", cdi->type);
0099     sysfs_get_ulong(path, "max_state",  &cdi->max_state);
0100     sysfs_get_ulong(path, "cur_state", &cdi->cur_state);
0101 
0102     syslog(LOG_INFO, "%s: %s: type %s, max %lu, curr %lu inst %d\n",
0103         __func__, path,
0104         cdi->type, cdi->max_state, cdi->cur_state, cdi->instance);
0105 
0106     return 0;
0107 }
0108 
0109 static int str_to_trip_type(char *name)
0110 {
0111     int i;
0112 
0113     for (i = 0; i < NR_THERMAL_TRIP_TYPE; i++) {
0114         if (!strcmp(name, trip_type_name[i]))
0115             return i;
0116     }
0117 
0118     return -ENOENT;
0119 }
0120 
0121 /* scan and fill in trip point info for a thermal zone and trip point id */
0122 static int get_trip_point_data(char *tz_path, int tzid, int tpid)
0123 {
0124     char filename[256];
0125     char temp_str[256];
0126     int trip_type;
0127 
0128     if (tpid >= MAX_NR_TRIP)
0129         return -EINVAL;
0130     /* check trip point type */
0131     snprintf(filename, sizeof(filename), "trip_point_%d_type", tpid);
0132     sysfs_get_string(tz_path, filename, temp_str);
0133     trip_type = str_to_trip_type(temp_str);
0134     if (trip_type < 0) {
0135         syslog(LOG_ERR, "%s:%s no matching type\n", __func__, temp_str);
0136         return -ENOENT;
0137     }
0138     ptdata.tzi[tzid].tp[tpid].type = trip_type;
0139     syslog(LOG_INFO, "%s:tz:%d tp:%d:type:%s type id %d\n", __func__, tzid,
0140         tpid, temp_str, trip_type);
0141 
0142     /* TODO: check attribute */
0143 
0144     return 0;
0145 }
0146 
0147 /* return instance id for file format such as trip_point_4_temp */
0148 static int get_instance_id(char *name, int pos, int skip)
0149 {
0150     char *ch;
0151     int i = 0;
0152 
0153     ch = strtok(name, "_");
0154     while (ch != NULL) {
0155         ++i;
0156         syslog(LOG_INFO, "%s:%s:%s:%d", __func__, name, ch, i);
0157         ch = strtok(NULL, "_");
0158         if (pos == i)
0159             return atol(ch + skip);
0160     }
0161 
0162     return -1;
0163 }
0164 
0165 /* Find trip point info of a thermal zone */
0166 static int find_tzone_tp(char *tz_name, char *d_name, struct tz_info *tzi,
0167             int tz_id)
0168 {
0169     int tp_id;
0170     unsigned long temp_ulong;
0171 
0172     if (strstr(d_name, "trip_point") &&
0173         strstr(d_name, "temp")) {
0174         /* check if trip point temp is non-zero
0175          * ignore 0/invalid trip points
0176          */
0177         sysfs_get_ulong(tz_name, d_name, &temp_ulong);
0178         if (temp_ulong < MAX_TEMP_KC) {
0179             tzi->nr_trip_pts++;
0180             /* found a valid trip point */
0181             tp_id = get_instance_id(d_name, 2, 0);
0182             syslog(LOG_DEBUG, "tzone %s trip %d temp %lu tpnode %s",
0183                 tz_name, tp_id, temp_ulong, d_name);
0184             if (tp_id < 0 || tp_id >= MAX_NR_TRIP) {
0185                 syslog(LOG_ERR, "Failed to find TP inst %s\n",
0186                     d_name);
0187                 return -1;
0188             }
0189             get_trip_point_data(tz_name, tz_id, tp_id);
0190             tzi->tp[tp_id].temp = temp_ulong;
0191         }
0192     }
0193 
0194     return 0;
0195 }
0196 
0197 /* check cooling devices for binding info. */
0198 static int find_tzone_cdev(struct dirent *nl, char *tz_name,
0199             struct tz_info *tzi, int tz_id, int cid)
0200 {
0201     unsigned long trip_instance = 0;
0202     char cdev_name_linked[256];
0203     char cdev_name[PATH_MAX];
0204     char cdev_trip_name[PATH_MAX];
0205     int cdev_id;
0206 
0207     if (nl->d_type == DT_LNK) {
0208         syslog(LOG_DEBUG, "TZ%d: cdev: %s cid %d\n", tz_id, nl->d_name,
0209             cid);
0210         tzi->nr_cdev++;
0211         if (tzi->nr_cdev > ptdata.nr_cooling_dev) {
0212             syslog(LOG_ERR, "Err: Too many cdev? %d\n",
0213                 tzi->nr_cdev);
0214             return -EINVAL;
0215         }
0216         /* find the link to real cooling device record binding */
0217         snprintf(cdev_name, sizeof(cdev_name) - 2, "%s/%s",
0218              tz_name, nl->d_name);
0219         memset(cdev_name_linked, 0, sizeof(cdev_name_linked));
0220         if (readlink(cdev_name, cdev_name_linked,
0221                 sizeof(cdev_name_linked) - 1) != -1) {
0222             cdev_id = get_instance_id(cdev_name_linked, 1,
0223                         sizeof("device") - 1);
0224             syslog(LOG_DEBUG, "cdev %s linked to %s : %d\n",
0225                 cdev_name, cdev_name_linked, cdev_id);
0226             tzi->cdev_binding |= (1 << cdev_id);
0227 
0228             /* find the trip point in which the cdev is binded to
0229              * in this tzone
0230              */
0231             snprintf(cdev_trip_name, sizeof(cdev_trip_name) - 1,
0232                 "%s%s", nl->d_name, "_trip_point");
0233             sysfs_get_ulong(tz_name, cdev_trip_name,
0234                     &trip_instance);
0235             /* validate trip point range, e.g. trip could return -1
0236              * when passive is enabled
0237              */
0238             if (trip_instance > MAX_NR_TRIP)
0239                 trip_instance = 0;
0240             tzi->trip_binding[cdev_id] |= 1 << trip_instance;
0241             syslog(LOG_DEBUG, "cdev %s -> trip:%lu: 0x%lx %d\n",
0242                 cdev_name, trip_instance,
0243                 tzi->trip_binding[cdev_id],
0244                 cdev_id);
0245 
0246 
0247         }
0248         return 0;
0249     }
0250 
0251     return -ENODEV;
0252 }
0253 
0254 
0255 
0256 /*****************************************************************************
0257  * Before calling scan_tzones, thermal sysfs must be probed to determine
0258  * the number of thermal zones and cooling devices.
0259  * We loop through each thermal zone and fill in tz_info struct, i.e.
0260  * ptdata.tzi[]
0261 root@jacob-chiefriver:~# tree -d /sys/class/thermal/thermal_zone0
0262 /sys/class/thermal/thermal_zone0
0263 |-- cdev0 -> ../cooling_device4
0264 |-- cdev1 -> ../cooling_device3
0265 |-- cdev10 -> ../cooling_device7
0266 |-- cdev11 -> ../cooling_device6
0267 |-- cdev12 -> ../cooling_device5
0268 |-- cdev2 -> ../cooling_device2
0269 |-- cdev3 -> ../cooling_device1
0270 |-- cdev4 -> ../cooling_device0
0271 |-- cdev5 -> ../cooling_device12
0272 |-- cdev6 -> ../cooling_device11
0273 |-- cdev7 -> ../cooling_device10
0274 |-- cdev8 -> ../cooling_device9
0275 |-- cdev9 -> ../cooling_device8
0276 |-- device -> ../../../LNXSYSTM:00/device:62/LNXTHERM:00
0277 |-- power
0278 `-- subsystem -> ../../../../class/thermal
0279 *****************************************************************************/
0280 static int scan_tzones(void)
0281 {
0282     DIR *dir;
0283     struct dirent **namelist;
0284     char tz_name[256];
0285     int i, j, n, k = 0;
0286 
0287     if (!ptdata.nr_tz_sensor)
0288         return -1;
0289 
0290     for (i = 0; i <= ptdata.max_tz_instance; i++) {
0291         memset(tz_name, 0, sizeof(tz_name));
0292         snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE, i);
0293 
0294         dir = opendir(tz_name);
0295         if (!dir) {
0296             syslog(LOG_INFO, "Thermal zone %s skipped\n", tz_name);
0297             continue;
0298         }
0299         /* keep track of valid tzones */
0300         n = scandir(tz_name, &namelist, 0, alphasort);
0301         if (n < 0)
0302             syslog(LOG_ERR, "scandir failed in %s",  tz_name);
0303         else {
0304             sysfs_get_string(tz_name, "type", ptdata.tzi[k].type);
0305             ptdata.tzi[k].instance = i;
0306             /* detect trip points and cdev attached to this tzone */
0307             j = 0; /* index for cdev */
0308             ptdata.tzi[k].nr_cdev = 0;
0309             ptdata.tzi[k].nr_trip_pts = 0;
0310             while (n--) {
0311                 char *temp_str;
0312 
0313                 if (find_tzone_tp(tz_name, namelist[n]->d_name,
0314                             &ptdata.tzi[k], k))
0315                     break;
0316                 temp_str = strstr(namelist[n]->d_name, "cdev");
0317                 if (!temp_str) {
0318                     free(namelist[n]);
0319                     continue;
0320                 }
0321                 if (!find_tzone_cdev(namelist[n], tz_name,
0322                             &ptdata.tzi[k], i, j))
0323                     j++; /* increment cdev index */
0324                 free(namelist[n]);
0325             }
0326             free(namelist);
0327         }
0328         /*TODO: reverse trip points */
0329         closedir(dir);
0330         syslog(LOG_INFO, "TZ %d has %d cdev\n", i,
0331             ptdata.tzi[k].nr_cdev);
0332         k++;
0333     }
0334 
0335     return 0;
0336 }
0337 
0338 static int scan_cdevs(void)
0339 {
0340     DIR *dir;
0341     struct dirent **namelist;
0342     char cdev_name[256];
0343     int i, n, k = 0;
0344 
0345     if (!ptdata.nr_cooling_dev) {
0346         fprintf(stderr, "No cooling devices found\n");
0347         return 0;
0348     }
0349     for (i = 0; i <= ptdata.max_cdev_instance; i++) {
0350         memset(cdev_name, 0, sizeof(cdev_name));
0351         snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, CDEV, i);
0352 
0353         dir = opendir(cdev_name);
0354         if (!dir) {
0355             syslog(LOG_INFO, "Cooling dev %s skipped\n", cdev_name);
0356             /* there is a gap in cooling device id, check again
0357              * for the same index.
0358              */
0359             continue;
0360         }
0361 
0362         n = scandir(cdev_name, &namelist, 0, alphasort);
0363         if (n < 0)
0364             syslog(LOG_ERR, "scandir failed in %s",  cdev_name);
0365         else {
0366             sysfs_get_string(cdev_name, "type", ptdata.cdi[k].type);
0367             ptdata.cdi[k].instance = i;
0368             if (strstr(ptdata.cdi[k].type, ctrl_cdev)) {
0369                 ptdata.cdi[k].flag |= CDEV_FLAG_IN_CONTROL;
0370                 syslog(LOG_DEBUG, "control cdev id %d\n", i);
0371             }
0372             while (n--)
0373                 free(namelist[n]);
0374             free(namelist);
0375         }
0376         closedir(dir);
0377         k++;
0378     }
0379     return 0;
0380 }
0381 
0382 
0383 int probe_thermal_sysfs(void)
0384 {
0385     DIR *dir;
0386     struct dirent **namelist;
0387     int n;
0388 
0389     dir = opendir(THERMAL_SYSFS);
0390     if (!dir) {
0391         fprintf(stderr, "\nNo thermal sysfs, exit\n");
0392         return -1;
0393     }
0394     n = scandir(THERMAL_SYSFS, &namelist, 0, alphasort);
0395     if (n < 0)
0396         syslog(LOG_ERR, "scandir failed in thermal sysfs");
0397     else {
0398         /* detect number of thermal zones and cooling devices */
0399         while (n--) {
0400             int inst;
0401 
0402             if (strstr(namelist[n]->d_name, CDEV)) {
0403                 inst = get_instance_id(namelist[n]->d_name, 1,
0404                         sizeof("device") - 1);
0405                 /* keep track of the max cooling device since
0406                  * there may be gaps.
0407                  */
0408                 if (inst > ptdata.max_cdev_instance)
0409                     ptdata.max_cdev_instance = inst;
0410 
0411                 syslog(LOG_DEBUG, "found cdev: %s %d %d\n",
0412                     namelist[n]->d_name,
0413                     ptdata.nr_cooling_dev,
0414                     ptdata.max_cdev_instance);
0415                 ptdata.nr_cooling_dev++;
0416             } else if (strstr(namelist[n]->d_name, TZONE)) {
0417                 inst = get_instance_id(namelist[n]->d_name, 1,
0418                         sizeof("zone") - 1);
0419                 if (inst > ptdata.max_tz_instance)
0420                     ptdata.max_tz_instance = inst;
0421 
0422                 syslog(LOG_DEBUG, "found tzone: %s %d %d\n",
0423                     namelist[n]->d_name,
0424                     ptdata.nr_tz_sensor,
0425                     ptdata.max_tz_instance);
0426                 ptdata.nr_tz_sensor++;
0427             }
0428             free(namelist[n]);
0429         }
0430         free(namelist);
0431     }
0432     syslog(LOG_INFO, "found %d tzone(s), %d cdev(s), target zone %d\n",
0433         ptdata.nr_tz_sensor, ptdata.nr_cooling_dev,
0434         target_thermal_zone);
0435     closedir(dir);
0436 
0437     if (!ptdata.nr_tz_sensor) {
0438         fprintf(stderr, "\nNo thermal zones found, exit\n\n");
0439         return -1;
0440     }
0441 
0442     ptdata.tzi = calloc(ptdata.max_tz_instance+1, sizeof(struct tz_info));
0443     if (!ptdata.tzi) {
0444         fprintf(stderr, "Err: allocate tz_info\n");
0445         return -1;
0446     }
0447 
0448     /* we still show thermal zone information if there is no cdev */
0449     if (ptdata.nr_cooling_dev) {
0450         ptdata.cdi = calloc(ptdata.max_cdev_instance + 1,
0451                 sizeof(struct cdev_info));
0452         if (!ptdata.cdi) {
0453             free(ptdata.tzi);
0454             fprintf(stderr, "Err: allocate cdev_info\n");
0455             return -1;
0456         }
0457     }
0458 
0459     /* now probe tzones */
0460     if (scan_tzones())
0461         return -1;
0462     if (scan_cdevs())
0463         return -1;
0464     return 0;
0465 }
0466 
0467 /* convert sysfs zone instance to zone array index */
0468 int zone_instance_to_index(int zone_inst)
0469 {
0470     int i;
0471 
0472     for (i = 0; i < ptdata.nr_tz_sensor; i++)
0473         if (ptdata.tzi[i].instance == zone_inst)
0474             return i;
0475     return -ENOENT;
0476 }
0477 
0478 /* read temperature of all thermal zones */
0479 int update_thermal_data()
0480 {
0481     int i;
0482     int next_thermal_record = cur_thermal_record + 1;
0483     char tz_name[256];
0484     static unsigned long samples;
0485 
0486     if (!ptdata.nr_tz_sensor) {
0487         syslog(LOG_ERR, "No thermal zones found!\n");
0488         return -1;
0489     }
0490 
0491     /* circular buffer for keeping historic data */
0492     if (next_thermal_record >= NR_THERMAL_RECORDS)
0493         next_thermal_record = 0;
0494     gettimeofday(&trec[next_thermal_record].tv, NULL);
0495     if (tmon_log) {
0496         fprintf(tmon_log, "%lu ", ++samples);
0497         fprintf(tmon_log, "%3.1f ", p_param.t_target);
0498     }
0499     for (i = 0; i < ptdata.nr_tz_sensor; i++) {
0500         memset(tz_name, 0, sizeof(tz_name));
0501         snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE,
0502             ptdata.tzi[i].instance);
0503         sysfs_get_ulong(tz_name, "temp",
0504                 &trec[next_thermal_record].temp[i]);
0505         if (tmon_log)
0506             fprintf(tmon_log, "%lu ",
0507                 trec[next_thermal_record].temp[i] / 1000);
0508     }
0509     cur_thermal_record = next_thermal_record;
0510     for (i = 0; i < ptdata.nr_cooling_dev; i++) {
0511         char cdev_name[256];
0512         unsigned long val;
0513 
0514         snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, CDEV,
0515             ptdata.cdi[i].instance);
0516         probe_cdev(&ptdata.cdi[i], cdev_name);
0517         val = ptdata.cdi[i].cur_state;
0518         if (val > 1000000)
0519             val = 0;
0520         if (tmon_log)
0521             fprintf(tmon_log, "%lu ", val);
0522     }
0523 
0524     if (tmon_log) {
0525         fprintf(tmon_log, "\n");
0526         fflush(tmon_log);
0527     }
0528 
0529     return 0;
0530 }
0531 
0532 void set_ctrl_state(unsigned long state)
0533 {
0534     char ctrl_cdev_path[256];
0535     int i;
0536     unsigned long cdev_state;
0537 
0538     if (no_control)
0539         return;
0540     /* set all ctrl cdev to the same state */
0541     for (i = 0; i < ptdata.nr_cooling_dev; i++) {
0542         if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) {
0543             if (ptdata.cdi[i].max_state < 10) {
0544                 strcpy(ctrl_cdev, "None.");
0545                 return;
0546             }
0547             /* scale to percentage of max_state */
0548             cdev_state = state * ptdata.cdi[i].max_state/100;
0549             syslog(LOG_DEBUG,
0550                 "ctrl cdev %d set state %lu scaled to %lu\n",
0551                 ptdata.cdi[i].instance, state, cdev_state);
0552             snprintf(ctrl_cdev_path, 256, "%s/%s%d", THERMAL_SYSFS,
0553                 CDEV, ptdata.cdi[i].instance);
0554             syslog(LOG_DEBUG, "ctrl cdev path %s", ctrl_cdev_path);
0555             sysfs_set_ulong(ctrl_cdev_path, "cur_state",
0556                     cdev_state);
0557         }
0558     }
0559 }
0560 
0561 void get_ctrl_state(unsigned long *state)
0562 {
0563     char ctrl_cdev_path[256];
0564     int ctrl_cdev_id = -1;
0565     int i;
0566 
0567     /* TODO: take average of all ctrl types. also consider change based on
0568      * uevent. Take the first reading for now.
0569      */
0570     for (i = 0; i < ptdata.nr_cooling_dev; i++) {
0571         if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) {
0572             ctrl_cdev_id = ptdata.cdi[i].instance;
0573             syslog(LOG_INFO, "ctrl cdev %d get state\n",
0574                 ptdata.cdi[i].instance);
0575             break;
0576         }
0577     }
0578     if (ctrl_cdev_id == -1) {
0579         *state = 0;
0580         return;
0581     }
0582     snprintf(ctrl_cdev_path, 256, "%s/%s%d", THERMAL_SYSFS,
0583         CDEV, ctrl_cdev_id);
0584     sysfs_get_ulong(ctrl_cdev_path, "cur_state", state);
0585 }
0586 
0587 void free_thermal_data(void)
0588 {
0589     free(ptdata.tzi);
0590     free(ptdata.cdi);
0591 }