/* * Power debug tool (powerdebug) * * Copyright (C) 2016, Linaro Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #define _GNU_SOURCE #include #undef _GNU_SOURCE #include #include #include #include #include #include #include "powerdebug.h" #include "display.h" #include "tree.h" #include "utils.h" #define SYSFS_SENSOR "/sys/class/hwmon" static struct tree *sensor_tree; struct temp_info { char name[NAME_MAX]; int temp; }; struct fan_info { char name[NAME_MAX]; int rpms; }; struct sensor_info { char name[NAME_MAX]; struct temp_info *temperatures; struct fan_info *fans; short nrtemps; short nrfans; }; static int sensor_dump_cb(struct tree *tree, void *data) { int i; struct sensor_info *sensor = tree->private; if (!strlen(sensor->name)) return 0; printf("%s\n", sensor->name); for (i = 0; i < sensor->nrtemps; i++) printf(" %s %.1f °C/V\n", sensor->temperatures[i].name, (float)sensor->temperatures[i].temp / 1000); for (i = 0; i < sensor->nrfans; i++) printf(" %s %d rpm\n", sensor->fans[i].name, sensor->fans[i].rpms); return 0; } static struct sensor_info *sensor_alloc(void) { struct sensor_info *sensor; sensor = malloc(sizeof(*sensor)); if (sensor) memset(sensor, 0, sizeof(*sensor)); return sensor; } static int read_sensor_cb(struct tree *tree, void *data) { DIR *dir; int value; struct dirent dirent, *direntp; struct sensor_info *sensor = tree->private; int nrtemps = 0; int nrfans = 0; dir = opendir(tree->path); if (!dir) return -1; file_read_value(tree->path, "name", "%s", sensor->name); while (!readdir_r(dir, &dirent, &direntp)) { if (!direntp) break; if (direntp->d_type != DT_REG) continue; if (!strncmp(direntp->d_name, "temp", 4)) { if (file_read_value(tree->path, direntp->d_name, "%d", &value)) continue; sensor->temperatures = realloc(sensor->temperatures, sizeof(struct temp_info) * (nrtemps + 1)); if (!sensor->temperatures) continue; strcpy(sensor->temperatures[nrtemps].name, direntp->d_name); sensor->temperatures[nrtemps].temp = value; nrtemps++; } if (!strncmp(direntp->d_name, "fan", 3)) { if (file_read_value(tree->path, direntp->d_name, "%d", &value)) continue; sensor->fans = realloc(sensor->fans, sizeof(struct temp_info) * (nrfans + 1)); if (!sensor->fans) continue; strcpy(sensor->fans[nrfans].name, direntp->d_name); sensor->fans[nrfans].rpms = value; nrfans++; } } sensor->nrtemps = nrtemps; sensor->nrfans = nrfans; closedir(dir); return 0; } static int read_sensor_info(struct tree *tree) { return tree_for_each(tree, read_sensor_cb, NULL); } static int fill_sensor_cb(struct tree *t, void *data) { struct sensor_info *sensor; sensor = sensor_alloc(); if (!sensor) return -1; t->private = sensor; if (!t->parent) return 0; return read_sensor_cb(t, data); } static int fill_sensor_tree(void) { return tree_for_each(sensor_tree, fill_sensor_cb, NULL); } static int sensor_filter_cb(const char *name) { /* let's ignore some directories in order to avoid to be * pulled inside the sysfs circular symlinks mess/hell * (choose the word which fit better) */ if (!strcmp(name, "subsystem")) return 1; if (!strcmp(name, "driver")) return 1; if (!strcmp(name, "hwmon")) return 1; if (!strcmp(name, "power")) return 1; return 0; } static int sensor_display_cb(struct tree *t, void *data) { struct sensor_info *sensor = t->private; int *line = data; char buf[1024]; int i; if (!strlen(sensor->name)) return 0; sprintf(buf, "%s", sensor->name); display_print_line(SENSOR, *line, buf, 1, t); (*line)++; for (i = 0; i < sensor->nrtemps; i++) { sprintf(buf, " %-35s%.1f", sensor->temperatures[i].name, (float)sensor->temperatures[i].temp / 1000); display_print_line(SENSOR, *line, buf, 0, t); (*line)++; } for (i = 0; i < sensor->nrfans; i++) { sprintf(buf, " %-35s%d rpm", sensor->fans[i].name, sensor->fans[i].rpms); display_print_line(SENSOR, *line, buf, 0, t); (*line)++; } return 0; } static int sensor_print_header(void) { char *buf; int ret; if (asprintf(&buf, "%-36s%s", "Name", "Value") < 0) return -1; ret = display_column_name(buf); free(buf); return ret; } static int sensor_print_info(struct tree *tree) { int ret, line = 0; display_reset_cursor(SENSOR); sensor_print_header(); ret = tree_for_each(tree, sensor_display_cb, &line); display_refresh_pad(SENSOR); return ret; } static int sensor_load_info(void) { if (sensor_tree) return 0; sensor_tree = tree_load(SYSFS_SENSOR, sensor_filter_cb, false); if (!sensor_tree) return -1; if (fill_sensor_tree()) return -1; return 0; } int sensor_dump(void) { if (sensor_load_info()) return -1; printf("\nSensor Information:\n"); printf("*******************\n\n"); return tree_for_each(sensor_tree, sensor_dump_cb, NULL); } static int sensor_display(bool refresh) { if (sensor_load_info()) { display_print_error(SENSOR, 0, "Failed to read sensor info"); return 0; } if (refresh && read_sensor_info(sensor_tree)) return -1; return sensor_print_info(sensor_tree); } static struct display_ops sensor_ops = { .display = sensor_display, }; int sensor_init(struct powerdebug_options *options) { if (!(options->flags & SENSOR_OPTION)) return 0; return display_register(SENSOR, &sensor_ops); }