blob: 702434726b2696e1f4ccd5166f54d5aa5be36797 [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
265static int gpio_display(bool refresh)
266{
267 if (refresh && read_gpio_info(gpio_tree))
268 return -1;
269
270 return gpio_print_info(gpio_tree);
271}
272
Shaojie Sun8ac4a2e2013-07-29 20:11:46 +0800273static int gpio_change(int keyvalue)
274{
275 struct tree *t = display_get_row_data(GPIO);
276 struct gpio_info *gpio = t->private;
277
278 if (!t || !gpio)
279 return -1;
280
281 switch (keyvalue) {
282 case 'D':
283 /* Only change direction when gpio interrupt not set.*/
284 if (!strstr(gpio->edge, "none"))
285 return 0;
286
287 if (strstr(gpio->direction, "in"))
288 strcpy(gpio->direction, "out");
289 else if (strstr(gpio->direction, "out"))
290 strcpy(gpio->direction, "in");
291 file_write_value(t->path, "direction", "%s", &gpio->direction);
292 file_read_value(t->path, "direction", "%s", &gpio->direction);
Shaojie Suned973182013-09-17 14:15:19 +0800293 file_read_value(t->path, "value", "%d", &gpio->value);
Shaojie Sun8ac4a2e2013-07-29 20:11:46 +0800294
295 break;
296
297 case 'V':
298 /* Only change value when gpio direction is out. */
299 if (!strstr(gpio->edge, "none")
300 || !strstr(gpio->direction, "out"))
301 return 0;
302
303 if (gpio->value)
304 file_write_value(t->path, "direction", "%s", &"low");
305 else
306 file_write_value(t->path, "direction", "%s", &"high");
307 file_read_value(t->path, "value", "%d", &gpio->value);
308
309 break;
310
311 default:
312 return -1;
313 }
314
315 return 0;
316}
317
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200318static struct display_ops gpio_ops = {
319 .display = gpio_display,
Shaojie Sun8ac4a2e2013-07-29 20:11:46 +0800320 .change = gpio_change,
Daniel Lezcano960b4e92011-08-25 15:46:13 +0200321};
322
Sanjay Singh Rawat75eadf32014-06-19 17:55:09 +0530323void export_free_gpios(void)
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530324{
325 FILE *fgpio, *fgpio_export;
Sanjay Singh Rawat75eadf32014-06-19 17:55:09 +0530326 int i, gpio_max = 0;
Sanjay Singh Rawat03fdbc02014-05-27 16:52:27 +0530327 char *line = NULL;
Daniel Lezcano627d28d2016-02-22 10:49:16 +0000328 ssize_t nrread;
Daniel Lezcano83b28d02016-02-18 13:49:55 +0000329 size_t len = 0;
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530330
331 fgpio = fopen("/sys/kernel/debug/gpio", "r");
332 if (!fgpio) {
333 printf("failed to read debugfs gpio file\n");
Daniel Lezcano83b28d02016-02-18 13:49:55 +0000334 return;
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530335 }
336
337 fgpio_export = fopen("/sys/class/gpio/export", "w");
338 if (!fgpio_export) {
339 printf("failed to write open gpio-export file\n");
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530340 goto out;
341 }
342
343 /* export the gpios */
Daniel Lezcano627d28d2016-02-22 10:49:16 +0000344 while ((nrread = getline(&line, &len, fgpio)) != -1) {
Sanjay Singh Rawat75eadf32014-06-19 17:55:09 +0530345 if (strstr(line, "GPIOs"))
Daniel Lezcano83b28d02016-02-18 13:49:55 +0000346 sscanf(line, "%*[^-]-%d", &gpio_max);
Sanjay Singh Rawat75eadf32014-06-19 17:55:09 +0530347 }
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530348
Sanjay Singh Rawat75eadf32014-06-19 17:55:09 +0530349 printf("log: total gpios = %d\n", gpio_max);
350 for (i = 0 ; i <= gpio_max ; i++) {
351 char command[50] = "";
352
353 sprintf(command, "echo %d > /sys/class/gpio/export", i);
354 if (system(command) < 0)
355 printf("error: failed to export gpio-%d\n", i);
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530356 }
Meraje33055c2014-06-30 16:11:45 +0530357
358 free(line);
Daniel Lezcano83b28d02016-02-18 13:49:55 +0000359
Meraje33055c2014-06-30 16:11:45 +0530360 if (fgpio)
361 fclose(fgpio);
Daniel Lezcano83b28d02016-02-18 13:49:55 +0000362out:
Meraje33055c2014-06-30 16:11:45 +0530363 if (fgpio_export)
364 fclose(fgpio_export);
365
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530366 return;
367}
368
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200369/*
370 * Initialize the gpio framework
371 */
Daniel Lezcanob4eec7e2016-02-18 16:44:55 +0000372int gpio_init(struct powerdebug_options *options)
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200373{
Daniel Lezcanob4eec7e2016-02-18 16:44:55 +0000374 if (!(options->flags & GPIO_OPTION))
375 return 0;
376
Sanjay Singh Rawat75eadf32014-06-19 17:55:09 +0530377 export_free_gpios();
Sanjay Singh Rawatb2120542014-05-16 10:58:47 +0530378
Daniel Lezcano9d8475b2011-08-25 15:46:13 +0200379 gpio_tree = tree_load(SYSFS_GPIO, gpio_filter_cb, false);
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200380 if (!gpio_tree)
381 return -1;
382
383 if (fill_gpio_tree())
384 return -1;
385
Daniel Lezcano1eedd482016-02-22 11:05:51 +0000386 return display_register(GPIO, &gpio_ops);
Daniel Lezcano03fc66b2011-08-25 15:46:13 +0200387}