xref: /openbmc/linux/tools/gpio/gpio-hammer.c (revision b285d2ae)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * gpio-hammer - example swiss army knife to shake GPIO lines on a system
4  *
5  * Copyright (C) 2016 Linus Walleij
6  *
7  * Usage:
8  *	gpio-hammer -n <device-name> -o <offset1> -o <offset2>
9  */
10 
11 #include <unistd.h>
12 #include <stdlib.h>
13 #include <stdbool.h>
14 #include <stdio.h>
15 #include <dirent.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <poll.h>
19 #include <fcntl.h>
20 #include <getopt.h>
21 #include <sys/ioctl.h>
22 #include <linux/gpio.h>
23 #include "gpio-utils.h"
24 
25 int hammer_device(const char *device_name, unsigned int *lines, int nlines,
26 		  unsigned int loops)
27 {
28 	struct gpiohandle_data data;
29 	char swirr[] = "-\\|/";
30 	int fd;
31 	int ret;
32 	int i, j;
33 	unsigned int iteration = 0;
34 
35 	memset(&data.values, 0, sizeof(data.values));
36 	ret = gpiotools_request_linehandle(device_name, lines, nlines,
37 					   GPIOHANDLE_REQUEST_OUTPUT, &data,
38 					   "gpio-hammer");
39 	if (ret < 0)
40 		goto exit_error;
41 	else
42 		fd = ret;
43 
44 	ret = gpiotools_get_values(fd, &data);
45 	if (ret < 0)
46 		goto exit_close_error;
47 
48 	fprintf(stdout, "Hammer lines [");
49 	for (i = 0; i < nlines; i++) {
50 		fprintf(stdout, "%d", lines[i]);
51 		if (i != (nlines - 1))
52 			fprintf(stdout, ", ");
53 	}
54 	fprintf(stdout, "] on %s, initial states: [", device_name);
55 	for (i = 0; i < nlines; i++) {
56 		fprintf(stdout, "%d", data.values[i]);
57 		if (i != (nlines - 1))
58 			fprintf(stdout, ", ");
59 	}
60 	fprintf(stdout, "]\n");
61 
62 	/* Hammertime! */
63 	j = 0;
64 	while (1) {
65 		/* Invert all lines so we blink */
66 		for (i = 0; i < nlines; i++)
67 			data.values[i] = !data.values[i];
68 
69 		ret = gpiotools_set_values(fd, &data);
70 		if (ret < 0)
71 			goto exit_close_error;
72 
73 		/* Re-read values to get status */
74 		ret = gpiotools_get_values(fd, &data);
75 		if (ret < 0)
76 			goto exit_close_error;
77 
78 		fprintf(stdout, "[%c] ", swirr[j]);
79 		j++;
80 		if (j == sizeof(swirr) - 1)
81 			j = 0;
82 
83 		fprintf(stdout, "[");
84 		for (i = 0; i < nlines; i++) {
85 			fprintf(stdout, "%d: %d", lines[i], data.values[i]);
86 			if (i != (nlines - 1))
87 				fprintf(stdout, ", ");
88 		}
89 		fprintf(stdout, "]\r");
90 		fflush(stdout);
91 		sleep(1);
92 		iteration++;
93 		if (loops && iteration == loops)
94 			break;
95 	}
96 	fprintf(stdout, "\n");
97 	ret = 0;
98 
99 exit_close_error:
100 	gpiotools_release_linehandle(fd);
101 exit_error:
102 	return ret;
103 }
104 
105 void print_usage(void)
106 {
107 	fprintf(stderr, "Usage: gpio-hammer [options]...\n"
108 		"Hammer GPIO lines, 0->1->0->1...\n"
109 		"  -n <name>  Hammer GPIOs on a named device (must be stated)\n"
110 		"  -o <n>     Offset[s] to hammer, at least one, several can be stated\n"
111 		" [-c <n>]    Do <n> loops (optional, infinite loop if not stated)\n"
112 		"  -?         This helptext\n"
113 		"\n"
114 		"Example:\n"
115 		"gpio-hammer -n gpiochip0 -o 4\n"
116 	);
117 }
118 
119 int main(int argc, char **argv)
120 {
121 	const char *device_name = NULL;
122 	unsigned int lines[GPIOHANDLES_MAX];
123 	unsigned int loops = 0;
124 	int nlines;
125 	int c;
126 	int i;
127 
128 	i = 0;
129 	while ((c = getopt(argc, argv, "c:n:o:?")) != -1) {
130 		switch (c) {
131 		case 'c':
132 			loops = strtoul(optarg, NULL, 10);
133 			break;
134 		case 'n':
135 			device_name = optarg;
136 			break;
137 		case 'o':
138 			/*
139 			 * Avoid overflow. Do not immediately error, we want to
140 			 * be able to accurately report on the amount of times
141 			 * '-o' was given to give an accurate error message
142 			 */
143 			if (i < GPIOHANDLES_MAX)
144 				lines[i] = strtoul(optarg, NULL, 10);
145 
146 			i++;
147 			break;
148 		case '?':
149 			print_usage();
150 			return -1;
151 		}
152 	}
153 
154 	if (i >= GPIOHANDLES_MAX) {
155 		fprintf(stderr,
156 			"Only %d occurrences of '-o' are allowed, %d were found\n",
157 			GPIOHANDLES_MAX, i + 1);
158 		return -1;
159 	}
160 
161 	nlines = i;
162 
163 	if (!device_name || !nlines) {
164 		print_usage();
165 		return -1;
166 	}
167 	return hammer_device(device_name, lines, nlines, loops);
168 }
169