| /* |
| * 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 <stdio.h> |
| #undef _GNU_SOURCE |
| #include <sys/types.h> |
| #include <stdbool.h> |
| #include <dirent.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| #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; |
| } |
| |
| int sensor_dump(void) |
| { |
| printf("\nSensor Information:\n"); |
| printf("*******************\n\n"); |
| |
| return tree_for_each(sensor_tree, sensor_dump_cb, NULL); |
| } |
| |
| 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; |
| } |
| |
| 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); |
| } |