blob: 36dedd1fb7e57d869b3c225435ac51c6eed03b3f [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;
Sanjay Singh Rawat96f6e052014-05-26 11:35:02 +053054static bool gpio_error = false;
Daniel Lezcano03fc66b2011-08-25 15:46:13 +020055
Daniel Lezcano9d8475b2011-08-25 15:46:13 +020056static struct gpio_info *gpio_alloc(void)
57{
58 struct gpio_info *gi;
59
60 gi = malloc(sizeof(*gi));
Daniel Lezcano5e267282011-08-25 15:46:13 +020061 if (gi) {
62 memset(gi, -1, sizeof(*gi));
Shaojie Sun564a1fe2013-07-25 19:02:08 +080063 memset(gi->direction, 0, MAX_VALUE_BYTE);
64 memset(gi->edge, 0, MAX_VALUE_BYTE);
Daniel Lezcano5e267282011-08-25 15:46:13 +020065 gi->prefix = NULL;
66 }
Daniel Lezcano9d8475b2011-08-25 15:46:13 +020067
68 return gi;
69}
70
Daniel Lezcano9d8475b2011-08-25 15:46:13 +020071static int gpio_filter_cb(const char *name)
72{
73 /* let's ignore some directories in order to avoid to be
74 * pulled inside the sysfs circular symlinks mess/hell
75 * (choose the word which fit better)
76 */
77 if (!strcmp(name, "device"))
78 return 1;
79
80 if (!strcmp(name, "subsystem"))
81 return 1;
82
83 if (!strcmp(name, "driver"))
84 return 1;
85
86 /* we want to ignore the gpio chips */
87 if (strstr(name, "chip"))
88 return 1;
89
90 /* we are not interested by the power value */
91 if (!strcmp(name, "power"))
92 return 1;
93
94 return 0;
95}
96
Daniel Lezcano03fc66b2011-08-25 15:46:13 +020097static inline int read_gpio_cb(struct tree *t, void *data)
98{
Daniel Lezcano9d8475b2011-08-25 15:46:13 +020099 struct gpio_info *gpio = t->private;
100
101 file_read_value(t->path, "active_low", "%d", &gpio->active_low);
102 file_read_value(t->path, "value", "%d", &gpio->value);
Shaojie Sun564a1fe2013-07-25 19:02:08 +0800103 file_read_value(t->path, "edge", "%8s", &gpio->edge);
104 file_read_value(t->path, "direction", "%4s", &gpio->direction);
Daniel Lezcano9d8475b2011-08-25 15:46:13 +0200105
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200106 return 0;
107}
108
109static int read_gpio_info(struct tree *tree)
110{
Daniel Lezcano9d8475b2011-08-25 15:46:13 +0200111 return tree_for_each(tree, read_gpio_cb, NULL);
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200112}
113
114static int fill_gpio_cb(struct tree *t, void *data)
115{
Daniel Lezcano9d8475b2011-08-25 15:46:13 +0200116 struct gpio_info *gpio;
117
118 gpio = gpio_alloc();
119 if (!gpio)
120 return -1;
121 t->private = gpio;
122
123 /* we skip the root node but we set it expanded for its children */
124 if (!t->parent) {
125 gpio->expanded = true;
126 return 0;
127 }
128
129 return read_gpio_cb(t, data);
130
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200131}
132
133static int fill_gpio_tree(void)
134{
Daniel Lezcano9d8475b2011-08-25 15:46:13 +0200135 return tree_for_each(gpio_tree, fill_gpio_cb, NULL);
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200136}
137
Daniel Lezcano5e267282011-08-25 15:46:13 +0200138static int dump_gpio_cb(struct tree *t, void *data)
139{
140 struct gpio_info *gpio = t->private;
141 struct gpio_info *pgpio;
142
143 if (!t->parent) {
144 printf("/\n");
145 gpio->prefix = "";
146 return 0;
147 }
148
149 pgpio = t->parent->private;
150
151 if (!gpio->prefix)
152 if (asprintf(&gpio->prefix, "%s%s%s", pgpio->prefix,
153 t->depth > 1 ? " ": "", t->next ? "|" : " ") < 0)
154 return -1;
155
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200156 printf("%s%s-- %s (", gpio->prefix, !t->next ? "`" : "", t->name);
157
158 if (gpio->active_low != -1)
159 printf(" active_low:%d", gpio->active_low);
160
161 if (gpio->value != -1)
162 printf(", value:%d", gpio->value);
163
Shaojie Sun564a1fe2013-07-25 19:02:08 +0800164 if (gpio->edge[0] != 0)
165 printf(", edge:%s", gpio->edge);
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200166
Shaojie Sun564a1fe2013-07-25 19:02:08 +0800167 if (gpio->direction[0] != 0)
168 printf(", direction:%s", gpio->direction);
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200169
170 printf(" )\n");
Daniel Lezcano5e267282011-08-25 15:46:13 +0200171
172 return 0;
173}
174
175int dump_gpio_info(void)
176{
177 return tree_for_each(gpio_tree, dump_gpio_cb, NULL);
178}
179
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200180int gpio_dump(void)
181{
Daniel Lezcano5e267282011-08-25 15:46:13 +0200182 int ret;
183
184 printf("\nGpio Tree :\n");
185 printf("***********\n");
186 ret = dump_gpio_info();
187 printf("\n\n");
188
189 return ret;
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200190}
191
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200192static char *gpio_line(struct tree *t)
193{
194 struct gpio_info *gpio = t->private;
195 char *gpioline;
196
Shaojie Sun564a1fe2013-07-25 19:02:08 +0800197 if (asprintf(&gpioline, "%-20s %-10d %-10d %-10s %-10s", t->name,
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200198 gpio->value, gpio->active_low, gpio->edge, gpio->direction) < 0)
199 return NULL;
200
201 return gpioline;
202}
203
204static int _gpio_print_info_cb(struct tree *t, void *data)
205{
206 int *line = data;
207 char *buffer;
208
209 /* we skip the root node of the tree */
210 if (!t->parent)
211 return 0;
212
213 buffer = gpio_line(t);
214 if (!buffer)
215 return -1;
216
217 display_print_line(GPIO, *line, buffer, 0, t);
218
219 (*line)++;
220
221 free(buffer);
222
223 return 0;
224}
225
226static int gpio_print_info_cb(struct tree *t, void *data)
227{
228 /* we skip the root node of the tree */
229 if (!t->parent)
230 return 0;
231
232 return _gpio_print_info_cb(t, data);
233}
234
235static int gpio_print_header(void)
236{
237 char *buf;
238 int ret;
239
240 if (asprintf(&buf, "%-20s %-10s %-10s %-10s %-10s",
241 "Name", "Value", "Active_low", "Edge", "Direction") < 0)
242 return -1;
243
244 ret = display_column_name(buf);
245
246 free(buf);
247
248 return ret;
249}
250
251static int gpio_print_info(struct tree *tree)
252{
253 int ret, line = 0;
254
255 display_reset_cursor(GPIO);
256
257 gpio_print_header();
258
259 ret = tree_for_each(tree, gpio_print_info_cb, &line);
260
261 display_refresh_pad(GPIO);
262
263 return ret;
264}
265
266static int gpio_display(bool refresh)
267{
Sanjay Singh Rawat96f6e052014-05-26 11:35:02 +0530268 if (gpio_error) {
269 display_message(GPIO, "error: path " SYSFS_GPIO " not found");
270 return -2;
271 }
272
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200273 if (refresh && read_gpio_info(gpio_tree))
274 return -1;
275
276 return gpio_print_info(gpio_tree);
277}
278
Shaojie Sun8ac4a2e2013-07-29 20:11:46 +0800279static int gpio_change(int keyvalue)
280{
281 struct tree *t = display_get_row_data(GPIO);
282 struct gpio_info *gpio = t->private;
283
284 if (!t || !gpio)
285 return -1;
286
287 switch (keyvalue) {
288 case 'D':
289 /* Only change direction when gpio interrupt not set.*/
290 if (!strstr(gpio->edge, "none"))
291 return 0;
292
293 if (strstr(gpio->direction, "in"))
294 strcpy(gpio->direction, "out");
295 else if (strstr(gpio->direction, "out"))
296 strcpy(gpio->direction, "in");
297 file_write_value(t->path, "direction", "%s", &gpio->direction);
298 file_read_value(t->path, "direction", "%s", &gpio->direction);
Shaojie Suned973182013-09-17 14:15:19 +0800299 file_read_value(t->path, "value", "%d", &gpio->value);
Shaojie Sun8ac4a2e2013-07-29 20:11:46 +0800300
301 break;
302
303 case 'V':
304 /* Only change value when gpio direction is out. */
305 if (!strstr(gpio->edge, "none")
306 || !strstr(gpio->direction, "out"))
307 return 0;
308
309 if (gpio->value)
310 file_write_value(t->path, "direction", "%s", &"low");
311 else
312 file_write_value(t->path, "direction", "%s", &"high");
313 file_read_value(t->path, "value", "%d", &gpio->value);
314
315 break;
316
317 default:
318 return -1;
319 }
320
321 return 0;
322}
323
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200324static struct display_ops gpio_ops = {
325 .display = gpio_display,
Shaojie Sun8ac4a2e2013-07-29 20:11:46 +0800326 .change = gpio_change,
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200327};
328
Sanjay Singh Rawat75eadf32014-06-19 17:55:09 +0530329void export_free_gpios(void)
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530330{
331 FILE *fgpio, *fgpio_export;
Sanjay Singh Rawat75eadf32014-06-19 17:55:09 +0530332 int i, gpio_max = 0;
Sanjay Singh Rawat03fdbc02014-05-27 16:52:27 +0530333 char *line = NULL;
Daniel Lezcano83b28d02016-02-18 13:49:55 +0000334 ssize_t read;
335 size_t len = 0;
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530336
337 fgpio = fopen("/sys/kernel/debug/gpio", "r");
338 if (!fgpio) {
339 printf("failed to read debugfs gpio file\n");
Daniel Lezcano83b28d02016-02-18 13:49:55 +0000340 return;
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530341 }
342
343 fgpio_export = fopen("/sys/class/gpio/export", "w");
344 if (!fgpio_export) {
345 printf("failed to write open gpio-export file\n");
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530346 goto out;
347 }
348
349 /* export the gpios */
Sanjay Singh Rawat75eadf32014-06-19 17:55:09 +0530350 while ((read = getline(&line, &len, fgpio)) != -1) {
351 if (strstr(line, "GPIOs"))
Daniel Lezcano83b28d02016-02-18 13:49:55 +0000352 sscanf(line, "%*[^-]-%d", &gpio_max);
Sanjay Singh Rawat75eadf32014-06-19 17:55:09 +0530353 }
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530354
Sanjay Singh Rawat75eadf32014-06-19 17:55:09 +0530355 printf("log: total gpios = %d\n", gpio_max);
356 for (i = 0 ; i <= gpio_max ; i++) {
357 char command[50] = "";
358
359 sprintf(command, "echo %d > /sys/class/gpio/export", i);
360 if (system(command) < 0)
361 printf("error: failed to export gpio-%d\n", i);
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530362 }
Meraje33055c2014-06-30 16:11:45 +0530363
364 free(line);
Daniel Lezcano83b28d02016-02-18 13:49:55 +0000365
Meraje33055c2014-06-30 16:11:45 +0530366 if (fgpio)
367 fclose(fgpio);
Daniel Lezcano83b28d02016-02-18 13:49:55 +0000368out:
Meraje33055c2014-06-30 16:11:45 +0530369 if (fgpio_export)
370 fclose(fgpio_export);
371
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530372 return;
373}
374
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200375/*
376 * Initialize the gpio framework
377 */
Daniel Lezcanob4eec7e2016-02-18 16:44:55 +0000378int gpio_init(struct powerdebug_options *options)
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200379{
Sanjay Singh Rawat96f6e052014-05-26 11:35:02 +0530380 int ret = 0;
381
Daniel Lezcanob4eec7e2016-02-18 16:44:55 +0000382 if (!(options->flags & GPIO_OPTION))
383 return 0;
384
Sanjay Singh Rawat96f6e052014-05-26 11:35:02 +0530385 ret = display_register(GPIO, &gpio_ops);
Sanjay Singh Rawat03fdbc02014-05-27 16:52:27 +0530386 if (ret)
Sanjay Singh Rawat96f6e052014-05-26 11:35:02 +0530387 printf("error: gpio display register failed");
388
389 if (access(SYSFS_GPIO, F_OK))
390 gpio_error = true; /* set the flag */
391
Sanjay Singh Rawat75eadf32014-06-19 17:55:09 +0530392 export_free_gpios();
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530393
Daniel Lezcano9d8475b2011-08-25 15:46:13 +0200394 gpio_tree = tree_load(SYSFS_GPIO, gpio_filter_cb, false);
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200395 if (!gpio_tree)
396 return -1;
397
398 if (fill_gpio_tree())
399 return -1;
400
Sanjay Singh Rawat96f6e052014-05-26 11:35:02 +0530401 return ret;
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200402}