| /* |
| * Powerdebug : power debugging tool |
| * |
| * Copyright (C) 2016, Linaro Limited. |
| * |
| * Author: |
| * Daniel Lezcano <daniel.lezcano@linaro.org> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| #define SYSFS_REGULATOR "/sys/class/regulator" |
| #define VALUE_MAX 16 |
| |
| #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 "display.h" |
| #include "powerdebug.h" |
| #include "tree.h" |
| #include "utils.h" |
| |
| struct regulator_info { |
| char name[NAME_MAX]; |
| char state[VALUE_MAX]; |
| char status[VALUE_MAX]; |
| char type[VALUE_MAX]; |
| char opmode[VALUE_MAX]; |
| int microvolts; |
| int min_microvolts; |
| int max_microvolts; |
| int microamps; |
| int min_microamps; |
| int max_microamps; |
| int requested_microamps; |
| int num_users; |
| }; |
| |
| struct regulator_data { |
| const char *name; |
| const char *ifmt; |
| const char *ofmt; |
| bool derefme; |
| }; |
| |
| static struct regulator_data regdata[] = { |
| { "name", "%s", "\tname: %s\n" }, |
| { "status", "%s", "\tstatus: %s\n" }, |
| { "state", "%s", "\tstate: %s\n" }, |
| { "type", "%s", "\ttype: %s\n" }, |
| { "num_users", "%d", "\tnum_users: %d\n", true }, |
| { "microvolts", "%d", "\tmicrovolts: %d\n", true }, |
| { "max_microvolts", "%d", "\tmax_microvolts: %d\n", true }, |
| { "min_microvolts", "%d", "\tmin_microvolts: %d\n", true }, |
| }; |
| |
| static struct tree *reg_tree; |
| |
| static struct regulator_info *regulator_alloc(void) |
| { |
| struct regulator_info *regi; |
| |
| regi = malloc(sizeof(*regi)); |
| if (regi) |
| memset(regi, 0, sizeof(*regi)); |
| |
| return regi; |
| } |
| |
| static int regulator_dump_cb(struct tree *tree, void *data) |
| { |
| int i; |
| char buffer[NAME_MAX]; |
| size_t nregdata = sizeof(regdata) / sizeof(regdata[0]); |
| |
| if (!strncmp("regulator.", tree->name, strlen("regulator."))) |
| printf("\n%s:\n", tree->name); |
| |
| for (i = 0; i < nregdata; i++) { |
| int val; |
| |
| if (file_read_value(tree->path, regdata[i].name, |
| regdata[i].ifmt, buffer)) |
| continue; |
| |
| if (regdata[i].derefme) { |
| val = atoi(buffer); |
| printf(regdata[i].ofmt, val); |
| } else |
| printf(regdata[i].ofmt, buffer); |
| } |
| |
| return 0; |
| } |
| |
| int regulator_dump(void) |
| { |
| printf("\nRegulator Information:\n"); |
| printf("*********************\n\n"); |
| |
| return tree_for_each(reg_tree, regulator_dump_cb, NULL); |
| } |
| |
| static int regulator_display_cb(struct tree *t, void *data) |
| { |
| struct regulator_info *reg = t->private; |
| int *line = data; |
| char *buf; |
| |
| /* we skip the root node of the tree */ |
| if (!t->parent) |
| return 0; |
| |
| if (!strlen(reg->name)) |
| return 0; |
| |
| if (asprintf(&buf, "%-11s %-11s %-11s %-11s %-11d %-11d %-11d %-12d", |
| reg->name, reg->status, reg->state, reg->type, |
| reg->num_users, reg->microvolts, reg->min_microvolts, |
| reg->max_microvolts) < 0) |
| return -1; |
| |
| display_print_line(REGULATOR, *line, buf, reg->num_users, t); |
| |
| (*line)++; |
| |
| free(buf); |
| |
| return 0; |
| } |
| |
| static int regulator_print_header(void) |
| { |
| char *buf; |
| int ret; |
| |
| if (asprintf(&buf, "%-11s %-11s %-11s %-11s %-11s %-11s %-11s %-12s", |
| "Name", "Status", "State", "Type", "Users", "Microvolts", |
| "Min u-volts", "Max u-volts") < 0) |
| return -1; |
| |
| ret = display_column_name(buf); |
| |
| free(buf); |
| |
| return ret; |
| |
| } |
| |
| static int regulator_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, "device")) |
| return 1; |
| |
| if (!strcmp(name, "subsystem")) |
| return 1; |
| |
| if (!strcmp(name, "driver")) |
| return 1; |
| |
| return 0; |
| } |
| |
| static inline int read_regulator_cb(struct tree *t, void *data) |
| { |
| struct regulator_info *reg = t->private; |
| |
| file_read_value(t->path, "name", "%s", reg->name); |
| file_read_value(t->path, "state", "%s", reg->state); |
| file_read_value(t->path, "status", "%s", reg->status); |
| file_read_value(t->path, "type", "%s", reg->type); |
| file_read_value(t->path, "opmode", "%s", reg->opmode); |
| file_read_value(t->path, "num_users", "%d", ®->num_users); |
| file_read_value(t->path, "microvolts", "%d", ®->microvolts); |
| file_read_value(t->path, "min_microvolts", "%d", ®->min_microvolts); |
| file_read_value(t->path, "max_microvolts", "%d", ®->max_microvolts); |
| file_read_value(t->path, "microamps", "%d", ®->microamps); |
| file_read_value(t->path, "min_microamps", "%d", ®->min_microamps); |
| file_read_value(t->path, "max_microamps", "%d", ®->max_microamps); |
| |
| return 0; |
| } |
| |
| static int read_regulator_info(struct tree *tree) |
| { |
| return tree_for_each(tree, read_regulator_cb, NULL); |
| } |
| |
| static int regulator_print_info(struct tree *tree) |
| { |
| int ret, line = 0; |
| |
| display_reset_cursor(REGULATOR); |
| |
| regulator_print_header(); |
| |
| ret = tree_for_each(tree, regulator_display_cb, &line); |
| |
| display_refresh_pad(REGULATOR); |
| |
| return ret; |
| } |
| |
| static int fill_regulator_cb(struct tree *t, void *data) |
| { |
| struct regulator_info *reg; |
| |
| reg = regulator_alloc(); |
| if (!reg) { |
| printf("error: unable to allocate memory for regulator\n"); |
| return -1; |
| } |
| t->private = reg; |
| |
| /* we skip the root node but we set it expanded for its children */ |
| if (!t->parent) |
| return 0; |
| |
| return read_regulator_cb(t, data); |
| } |
| |
| static int fill_regulator_tree(void) |
| { |
| return tree_for_each(reg_tree, fill_regulator_cb, NULL); |
| } |
| |
| static int regulator_info_load(void) |
| { |
| if (reg_tree) |
| return 0; |
| |
| reg_tree = tree_load(SYSFS_REGULATOR, regulator_filter_cb, false); |
| if (!reg_tree) |
| return -1; |
| |
| if (fill_regulator_tree()) |
| return -1; |
| |
| return 0; |
| } |
| |
| static int regulator_display(bool refresh) |
| { |
| if (regulator_info_load()) { |
| display_print_error(REGULATOR, 0, "Failed to read regulator info"); |
| return 0; /* we don't want this to be a critical error */ |
| } |
| |
| if (refresh && read_regulator_info(reg_tree)) |
| return -1; |
| |
| return regulator_print_info(reg_tree); |
| } |
| |
| static struct display_ops regulator_ops = { |
| .display = regulator_display, |
| }; |
| |
| int regulator_init(struct powerdebug_options *options) |
| { |
| if (!(options->flags & REGULATOR_OPTION)) |
| return 0; |
| |
| return display_register(REGULATOR, ®ulator_ops); |
| } |