blob: d7df0e3b5cf2e13065c0fe72f317bbc367a29d12 [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 +0200179int gpio_dump(void)
180{
Daniel Lezcano5e267282011-08-25 15:46:13 +0200181 int ret;
182
183 printf("\nGpio Tree :\n");
184 printf("***********\n");
185 ret = dump_gpio_info();
186 printf("\n\n");
187
188 return ret;
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200189}
190
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200191static char *gpio_line(struct tree *t)
192{
193 struct gpio_info *gpio = t->private;
194 char *gpioline;
195
Shaojie Sun564a1fe2013-07-25 19:02:08 +0800196 if (asprintf(&gpioline, "%-20s %-10d %-10d %-10s %-10s", t->name,
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200197 gpio->value, gpio->active_low, gpio->edge, gpio->direction) < 0)
198 return NULL;
199
200 return gpioline;
201}
202
203static int _gpio_print_info_cb(struct tree *t, void *data)
204{
205 int *line = data;
206 char *buffer;
207
208 /* we skip the root node of the tree */
209 if (!t->parent)
210 return 0;
211
212 buffer = gpio_line(t);
213 if (!buffer)
214 return -1;
215
216 display_print_line(GPIO, *line, buffer, 0, t);
217
218 (*line)++;
219
220 free(buffer);
221
222 return 0;
223}
224
225static int gpio_print_info_cb(struct tree *t, void *data)
226{
227 /* we skip the root node of the tree */
228 if (!t->parent)
229 return 0;
230
231 return _gpio_print_info_cb(t, data);
232}
233
234static int gpio_print_header(void)
235{
236 char *buf;
237 int ret;
238
239 if (asprintf(&buf, "%-20s %-10s %-10s %-10s %-10s",
240 "Name", "Value", "Active_low", "Edge", "Direction") < 0)
241 return -1;
242
243 ret = display_column_name(buf);
244
245 free(buf);
246
247 return ret;
248}
249
250static int gpio_print_info(struct tree *tree)
251{
252 int ret, line = 0;
253
254 display_reset_cursor(GPIO);
255
256 gpio_print_header();
257
258 ret = tree_for_each(tree, gpio_print_info_cb, &line);
259
260 display_refresh_pad(GPIO);
261
262 return ret;
263}
264
Daniel Lezcanod42d7ad2016-02-22 15:02:57 +0100265void export_free_gpios(void)
266{
267 FILE *fgpio, *fgpio_export;
268 int i, gpio_max = 0;
269 char *line = NULL;
270 ssize_t nrread;
271 size_t len = 0;
272
273 fgpio = fopen("/sys/kernel/debug/gpio", "r");
274 if (!fgpio) {
275 printf("failed to read debugfs gpio file\n");
276 return;
277 }
278
279 fgpio_export = fopen("/sys/class/gpio/export", "w");
280 if (!fgpio_export) {
281 printf("failed to write open gpio-export file\n");
282 goto out;
283 }
284
285 /* export the gpios */
286 while ((nrread = getline(&line, &len, fgpio)) != -1) {
287 if (strstr(line, "GPIOs"))
288 sscanf(line, "%*[^-]-%d", &gpio_max);
289 }
290
291 printf("log: total gpios = %d\n", gpio_max);
292 for (i = 0 ; i <= gpio_max ; i++) {
293 char command[50] = "";
294
295 sprintf(command, "echo %d > /sys/class/gpio/export", i);
296 if (system(command) < 0)
297 printf("error: failed to export gpio-%d\n", i);
298 }
299
300 free(line);
301
302 if (fgpio)
303 fclose(fgpio);
304out:
305 if (fgpio_export)
306 fclose(fgpio_export);
307
308 return;
309}
310
311static int gpio_load_info(void)
312{
313 if (gpio_tree)
314 return 0;
315
316 export_free_gpios();
317
318 gpio_tree = tree_load(SYSFS_GPIO, gpio_filter_cb, false);
319 if (!gpio_tree)
320 return -1;
321
322 if (fill_gpio_tree())
323 return -1;
324
325 return 0;
326}
327
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200328static int gpio_display(bool refresh)
329{
Daniel Lezcanod42d7ad2016-02-22 15:02:57 +0100330 if (gpio_load_info()) {
331 display_print_error(GPIO, 0, "Failed to read gpio info");
332 return 0; /* we don't want this to be a critical error */
333 }
334
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200335 if (refresh && read_gpio_info(gpio_tree))
336 return -1;
337
338 return gpio_print_info(gpio_tree);
339}
340
Shaojie Sun8ac4a2e2013-07-29 20:11:46 +0800341static int gpio_change(int keyvalue)
342{
343 struct tree *t = display_get_row_data(GPIO);
344 struct gpio_info *gpio = t->private;
345
346 if (!t || !gpio)
347 return -1;
348
349 switch (keyvalue) {
350 case 'D':
351 /* Only change direction when gpio interrupt not set.*/
352 if (!strstr(gpio->edge, "none"))
353 return 0;
354
355 if (strstr(gpio->direction, "in"))
356 strcpy(gpio->direction, "out");
357 else if (strstr(gpio->direction, "out"))
358 strcpy(gpio->direction, "in");
359 file_write_value(t->path, "direction", "%s", &gpio->direction);
360 file_read_value(t->path, "direction", "%s", &gpio->direction);
Shaojie Suned973182013-09-17 14:15:19 +0800361 file_read_value(t->path, "value", "%d", &gpio->value);
Shaojie Sun8ac4a2e2013-07-29 20:11:46 +0800362
363 break;
364
365 case 'V':
366 /* Only change value when gpio direction is out. */
367 if (!strstr(gpio->edge, "none")
368 || !strstr(gpio->direction, "out"))
369 return 0;
370
371 if (gpio->value)
372 file_write_value(t->path, "direction", "%s", &"low");
373 else
374 file_write_value(t->path, "direction", "%s", &"high");
375 file_read_value(t->path, "value", "%d", &gpio->value);
376
377 break;
378
379 default:
380 return -1;
381 }
382
383 return 0;
384}
385
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200386static struct display_ops gpio_ops = {
387 .display = gpio_display,
Shaojie Sun8ac4a2e2013-07-29 20:11:46 +0800388 .change = gpio_change,
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200389};
390
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200391/*
392 * Initialize the gpio framework
393 */
Daniel Lezcanob4eec7e2016-02-18 16:44:55 +0000394int gpio_init(struct powerdebug_options *options)
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200395{
Daniel Lezcanob4eec7e2016-02-18 16:44:55 +0000396 if (!(options->flags & GPIO_OPTION))
397 return 0;
398
Daniel Lezcano1eedd482016-02-22 11:05:51 +0000399 return display_register(GPIO, &gpio_ops);
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200400}