blob: 9f323eb1ba70e009dccba2bddb5f3a06a68e576c [file] [log] [blame]
Daniel Lezcano7c15fad2016-02-18 15:57:43 +00001/*
2 * Power debug tool (powerdebug)
Daniel Lezcano03fc66b2011-08-25 15:46:13 +02003 *
Daniel Lezcano7c15fad2016-02-18 15:57:43 +00004 * Copyright (C) 2016, Linaro Limited.
Daniel Lezcano03fc66b2011-08-25 15:46:13 +02005 *
Daniel Lezcano7c15fad2016-02-18 15:57:43 +00006 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
Daniel Lezcano03fc66b2011-08-25 15:46:13 +020010 *
Daniel Lezcano7c15fad2016-02-18 15:57:43 +000011 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 */
Daniel Lezcano03fc66b2011-08-25 15:46:13 +020021
22#ifndef _GNU_SOURCE
23#define _GNU_SOURCE
24#include <stdio.h>
25#undef _GNU_SOURCE
26#endif
27#include <mntent.h>
28#include <string.h>
29#include <stdbool.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <sys/param.h>
33#include <sys/stat.h>
34
35#include "powerdebug.h"
36#include "display.h"
37#include "tree.h"
38#include "utils.h"
39
40#define SYSFS_GPIO "/sys/class/gpio"
41
Shaojie Sun564a1fe2013-07-25 19:02:08 +080042#define MAX_VALUE_BYTE 10
43
Daniel Lezcano9d8475b2011-08-25 15:46:13 +020044struct gpio_info {
45 bool expanded;
46 int active_low;
47 int value;
Shaojie Sun564a1fe2013-07-25 19:02:08 +080048 char direction[MAX_VALUE_BYTE];
49 char edge[MAX_VALUE_BYTE];
Daniel Lezcano5e267282011-08-25 15:46:13 +020050 char *prefix;
Daniel Lezcano9d8475b2011-08-25 15:46:13 +020051} *gpios_info;
52
Daniel Lezcano03fc66b2011-08-25 15:46:13 +020053static struct tree *gpio_tree = NULL;
54
Daniel Lezcano9d8475b2011-08-25 15:46:13 +020055static struct gpio_info *gpio_alloc(void)
56{
57 struct gpio_info *gi;
58
59 gi = malloc(sizeof(*gi));
Daniel Lezcano5e267282011-08-25 15:46:13 +020060 if (gi) {
61 memset(gi, -1, sizeof(*gi));
Shaojie Sun564a1fe2013-07-25 19:02:08 +080062 memset(gi->direction, 0, MAX_VALUE_BYTE);
63 memset(gi->edge, 0, MAX_VALUE_BYTE);
Daniel Lezcano5e267282011-08-25 15:46:13 +020064 gi->prefix = NULL;
65 }
Daniel Lezcano9d8475b2011-08-25 15:46:13 +020066
67 return gi;
68}
69
Daniel Lezcano9d8475b2011-08-25 15:46:13 +020070static int gpio_filter_cb(const char *name)
71{
72 /* let's ignore some directories in order to avoid to be
73 * pulled inside the sysfs circular symlinks mess/hell
74 * (choose the word which fit better)
75 */
76 if (!strcmp(name, "device"))
77 return 1;
78
79 if (!strcmp(name, "subsystem"))
80 return 1;
81
82 if (!strcmp(name, "driver"))
83 return 1;
84
85 /* we want to ignore the gpio chips */
86 if (strstr(name, "chip"))
87 return 1;
88
89 /* we are not interested by the power value */
90 if (!strcmp(name, "power"))
91 return 1;
92
93 return 0;
94}
95
Daniel Lezcano03fc66b2011-08-25 15:46:13 +020096static inline int read_gpio_cb(struct tree *t, void *data)
97{
Daniel Lezcano9d8475b2011-08-25 15:46:13 +020098 struct gpio_info *gpio = t->private;
99
100 file_read_value(t->path, "active_low", "%d", &gpio->active_low);
101 file_read_value(t->path, "value", "%d", &gpio->value);
Shaojie Sun564a1fe2013-07-25 19:02:08 +0800102 file_read_value(t->path, "edge", "%8s", &gpio->edge);
103 file_read_value(t->path, "direction", "%4s", &gpio->direction);
Daniel Lezcano9d8475b2011-08-25 15:46:13 +0200104
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200105 return 0;
106}
107
108static int read_gpio_info(struct tree *tree)
109{
Daniel Lezcano9d8475b2011-08-25 15:46:13 +0200110 return tree_for_each(tree, read_gpio_cb, NULL);
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200111}
112
113static int fill_gpio_cb(struct tree *t, void *data)
114{
Daniel Lezcano9d8475b2011-08-25 15:46:13 +0200115 struct gpio_info *gpio;
116
117 gpio = gpio_alloc();
118 if (!gpio)
119 return -1;
120 t->private = gpio;
121
122 /* we skip the root node but we set it expanded for its children */
123 if (!t->parent) {
124 gpio->expanded = true;
125 return 0;
126 }
127
128 return read_gpio_cb(t, data);
129
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200130}
131
132static int fill_gpio_tree(void)
133{
Daniel Lezcano9d8475b2011-08-25 15:46:13 +0200134 return tree_for_each(gpio_tree, fill_gpio_cb, NULL);
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200135}
136
Daniel Lezcano5e267282011-08-25 15:46:13 +0200137static int dump_gpio_cb(struct tree *t, void *data)
138{
139 struct gpio_info *gpio = t->private;
140 struct gpio_info *pgpio;
141
142 if (!t->parent) {
143 printf("/\n");
144 gpio->prefix = "";
145 return 0;
146 }
147
148 pgpio = t->parent->private;
149
150 if (!gpio->prefix)
151 if (asprintf(&gpio->prefix, "%s%s%s", pgpio->prefix,
152 t->depth > 1 ? " ": "", t->next ? "|" : " ") < 0)
153 return -1;
154
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200155 printf("%s%s-- %s (", gpio->prefix, !t->next ? "`" : "", t->name);
156
157 if (gpio->active_low != -1)
158 printf(" active_low:%d", gpio->active_low);
159
160 if (gpio->value != -1)
161 printf(", value:%d", gpio->value);
162
Shaojie Sun564a1fe2013-07-25 19:02:08 +0800163 if (gpio->edge[0] != 0)
164 printf(", edge:%s", gpio->edge);
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200165
Shaojie Sun564a1fe2013-07-25 19:02:08 +0800166 if (gpio->direction[0] != 0)
167 printf(", direction:%s", gpio->direction);
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200168
169 printf(" )\n");
Daniel Lezcano5e267282011-08-25 15:46:13 +0200170
171 return 0;
172}
173
174int dump_gpio_info(void)
175{
176 return tree_for_each(gpio_tree, dump_gpio_cb, NULL);
177}
178
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200179
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200180static char *gpio_line(struct tree *t)
181{
182 struct gpio_info *gpio = t->private;
183 char *gpioline;
184
Shaojie Sun564a1fe2013-07-25 19:02:08 +0800185 if (asprintf(&gpioline, "%-20s %-10d %-10d %-10s %-10s", t->name,
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200186 gpio->value, gpio->active_low, gpio->edge, gpio->direction) < 0)
187 return NULL;
188
189 return gpioline;
190}
191
192static int _gpio_print_info_cb(struct tree *t, void *data)
193{
194 int *line = data;
195 char *buffer;
196
197 /* we skip the root node of the tree */
198 if (!t->parent)
199 return 0;
200
201 buffer = gpio_line(t);
202 if (!buffer)
203 return -1;
204
205 display_print_line(GPIO, *line, buffer, 0, t);
206
207 (*line)++;
208
209 free(buffer);
210
211 return 0;
212}
213
214static int gpio_print_info_cb(struct tree *t, void *data)
215{
216 /* we skip the root node of the tree */
217 if (!t->parent)
218 return 0;
219
220 return _gpio_print_info_cb(t, data);
221}
222
223static int gpio_print_header(void)
224{
225 char *buf;
226 int ret;
227
228 if (asprintf(&buf, "%-20s %-10s %-10s %-10s %-10s",
229 "Name", "Value", "Active_low", "Edge", "Direction") < 0)
230 return -1;
231
232 ret = display_column_name(buf);
233
234 free(buf);
235
236 return ret;
237}
238
239static int gpio_print_info(struct tree *tree)
240{
241 int ret, line = 0;
242
243 display_reset_cursor(GPIO);
244
245 gpio_print_header();
246
247 ret = tree_for_each(tree, gpio_print_info_cb, &line);
248
249 display_refresh_pad(GPIO);
250
251 return ret;
252}
253
Daniel Lezcanod42d7ad2016-02-22 15:02:57 +0100254void export_free_gpios(void)
255{
256 FILE *fgpio, *fgpio_export;
257 int i, gpio_max = 0;
258 char *line = NULL;
259 ssize_t nrread;
260 size_t len = 0;
261
262 fgpio = fopen("/sys/kernel/debug/gpio", "r");
263 if (!fgpio) {
264 printf("failed to read debugfs gpio file\n");
265 return;
266 }
267
268 fgpio_export = fopen("/sys/class/gpio/export", "w");
269 if (!fgpio_export) {
270 printf("failed to write open gpio-export file\n");
271 goto out;
272 }
273
274 /* export the gpios */
275 while ((nrread = getline(&line, &len, fgpio)) != -1) {
276 if (strstr(line, "GPIOs"))
277 sscanf(line, "%*[^-]-%d", &gpio_max);
278 }
279
280 printf("log: total gpios = %d\n", gpio_max);
281 for (i = 0 ; i <= gpio_max ; i++) {
282 char command[50] = "";
283
284 sprintf(command, "echo %d > /sys/class/gpio/export", i);
285 if (system(command) < 0)
286 printf("error: failed to export gpio-%d\n", i);
287 }
288
289 free(line);
290
291 if (fgpio)
292 fclose(fgpio);
293out:
294 if (fgpio_export)
295 fclose(fgpio_export);
296
297 return;
298}
299
300static int gpio_load_info(void)
301{
302 if (gpio_tree)
303 return 0;
304
305 export_free_gpios();
306
307 gpio_tree = tree_load(SYSFS_GPIO, gpio_filter_cb, false);
308 if (!gpio_tree)
309 return -1;
310
311 if (fill_gpio_tree())
312 return -1;
313
314 return 0;
315}
316
Thara Gopinathd78818c2017-07-14 13:15:45 -0400317int gpio_dump(void)
318{
319 int ret;
320
321 if (gpio_load_info())
322 return -1;
323
324 printf("\nGpio Tree :\n");
325 printf("***********\n");
326 ret = dump_gpio_info();
327 printf("\n\n");
328
329 return ret;
330}
331
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200332static int gpio_display(bool refresh)
333{
Daniel Lezcanod42d7ad2016-02-22 15:02:57 +0100334 if (gpio_load_info()) {
335 display_print_error(GPIO, 0, "Failed to read gpio info");
336 return 0; /* we don't want this to be a critical error */
337 }
338
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200339 if (refresh && read_gpio_info(gpio_tree))
340 return -1;
341
342 return gpio_print_info(gpio_tree);
343}
344
Shaojie Sun8ac4a2e2013-07-29 20:11:46 +0800345static int gpio_change(int keyvalue)
346{
347 struct tree *t = display_get_row_data(GPIO);
348 struct gpio_info *gpio = t->private;
349
350 if (!t || !gpio)
351 return -1;
352
353 switch (keyvalue) {
354 case 'D':
355 /* Only change direction when gpio interrupt not set.*/
356 if (!strstr(gpio->edge, "none"))
357 return 0;
358
359 if (strstr(gpio->direction, "in"))
360 strcpy(gpio->direction, "out");
361 else if (strstr(gpio->direction, "out"))
362 strcpy(gpio->direction, "in");
363 file_write_value(t->path, "direction", "%s", &gpio->direction);
364 file_read_value(t->path, "direction", "%s", &gpio->direction);
Shaojie Suned973182013-09-17 14:15:19 +0800365 file_read_value(t->path, "value", "%d", &gpio->value);
Shaojie Sun8ac4a2e2013-07-29 20:11:46 +0800366
367 break;
368
369 case 'V':
370 /* Only change value when gpio direction is out. */
371 if (!strstr(gpio->edge, "none")
372 || !strstr(gpio->direction, "out"))
373 return 0;
374
375 if (gpio->value)
376 file_write_value(t->path, "direction", "%s", &"low");
377 else
378 file_write_value(t->path, "direction", "%s", &"high");
379 file_read_value(t->path, "value", "%d", &gpio->value);
380
381 break;
382
383 default:
384 return -1;
385 }
386
387 return 0;
388}
389
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200390static struct display_ops gpio_ops = {
391 .display = gpio_display,
Shaojie Sun8ac4a2e2013-07-29 20:11:46 +0800392 .change = gpio_change,
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200393};
394
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200395/*
396 * Initialize the gpio framework
397 */
Daniel Lezcanob4eec7e2016-02-18 16:44:55 +0000398int gpio_init(struct powerdebug_options *options)
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200399{
Daniel Lezcanob4eec7e2016-02-18 16:44:55 +0000400 if (!(options->flags & GPIO_OPTION))
401 return 0;
402
Daniel Lezcano1eedd482016-02-22 11:05:51 +0000403 return display_register(GPIO, &gpio_ops);
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200404}