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 num_lines, |
26 | unsigned int loops) |
27 | { |
28 | struct gpio_v2_line_values values; |
29 | struct gpio_v2_line_config config; |
30 | char swirr[] = "-\\|/" ; |
31 | int fd; |
32 | int ret; |
33 | int i, j; |
34 | unsigned int iteration = 0; |
35 | |
36 | memset(&config, 0, sizeof(config)); |
37 | config.flags = GPIO_V2_LINE_FLAG_OUTPUT; |
38 | |
39 | ret = gpiotools_request_line(device_name, lines, num_lines, |
40 | config: &config, consumer: "gpio-hammer" ); |
41 | if (ret < 0) |
42 | goto exit_error; |
43 | else |
44 | fd = ret; |
45 | |
46 | values.mask = 0; |
47 | values.bits = 0; |
48 | for (i = 0; i < num_lines; i++) |
49 | gpiotools_set_bit(b: &values.mask, n: i); |
50 | |
51 | ret = gpiotools_get_values(fd, values: &values); |
52 | if (ret < 0) |
53 | goto exit_close_error; |
54 | |
55 | fprintf(stdout, "Hammer lines [" ); |
56 | for (i = 0; i < num_lines; i++) { |
57 | fprintf(stdout, "%d" , lines[i]); |
58 | if (i != (num_lines - 1)) |
59 | fprintf(stdout, ", " ); |
60 | } |
61 | fprintf(stdout, "] on %s, initial states: [" , device_name); |
62 | for (i = 0; i < num_lines; i++) { |
63 | fprintf(stdout, "%d" , gpiotools_test_bit(b: values.bits, n: i)); |
64 | if (i != (num_lines - 1)) |
65 | fprintf(stdout, ", " ); |
66 | } |
67 | fprintf(stdout, "]\n" ); |
68 | |
69 | /* Hammertime! */ |
70 | j = 0; |
71 | while (1) { |
72 | /* Invert all lines so we blink */ |
73 | for (i = 0; i < num_lines; i++) |
74 | gpiotools_change_bit(b: &values.bits, n: i); |
75 | |
76 | ret = gpiotools_set_values(fd, values: &values); |
77 | if (ret < 0) |
78 | goto exit_close_error; |
79 | |
80 | /* Re-read values to get status */ |
81 | ret = gpiotools_get_values(fd, values: &values); |
82 | if (ret < 0) |
83 | goto exit_close_error; |
84 | |
85 | fprintf(stdout, "[%c] " , swirr[j]); |
86 | j++; |
87 | if (j == sizeof(swirr) - 1) |
88 | j = 0; |
89 | |
90 | fprintf(stdout, "[" ); |
91 | for (i = 0; i < num_lines; i++) { |
92 | fprintf(stdout, "%d: %d" , lines[i], |
93 | gpiotools_test_bit(b: values.bits, n: i)); |
94 | if (i != (num_lines - 1)) |
95 | fprintf(stdout, ", " ); |
96 | } |
97 | fprintf(stdout, "]\r" ); |
98 | fflush(stdout); |
99 | sleep(1); |
100 | iteration++; |
101 | if (loops && iteration == loops) |
102 | break; |
103 | } |
104 | fprintf(stdout, "\n" ); |
105 | ret = 0; |
106 | |
107 | exit_close_error: |
108 | gpiotools_release_line(fd); |
109 | exit_error: |
110 | return ret; |
111 | } |
112 | |
113 | void print_usage(void) |
114 | { |
115 | fprintf(stderr, "Usage: gpio-hammer [options]...\n" |
116 | "Hammer GPIO lines, 0->1->0->1...\n" |
117 | " -n <name> Hammer GPIOs on a named device (must be stated)\n" |
118 | " -o <n> Offset[s] to hammer, at least one, several can be stated\n" |
119 | " [-c <n>] Do <n> loops (optional, infinite loop if not stated)\n" |
120 | " -? This helptext\n" |
121 | "\n" |
122 | "Example:\n" |
123 | "gpio-hammer -n gpiochip0 -o 4\n" |
124 | ); |
125 | } |
126 | |
127 | int main(int argc, char **argv) |
128 | { |
129 | const char *device_name = NULL; |
130 | unsigned int lines[GPIOHANDLES_MAX]; |
131 | unsigned int loops = 0; |
132 | int num_lines; |
133 | int c; |
134 | int i; |
135 | |
136 | i = 0; |
137 | while ((c = getopt(argc, argv, "c:n:o:?" )) != -1) { |
138 | switch (c) { |
139 | case 'c': |
140 | loops = strtoul(optarg, NULL, 10); |
141 | break; |
142 | case 'n': |
143 | device_name = optarg; |
144 | break; |
145 | case 'o': |
146 | /* |
147 | * Avoid overflow. Do not immediately error, we want to |
148 | * be able to accurately report on the amount of times |
149 | * '-o' was given to give an accurate error message |
150 | */ |
151 | if (i < GPIOHANDLES_MAX) |
152 | lines[i] = strtoul(optarg, NULL, 10); |
153 | |
154 | i++; |
155 | break; |
156 | case '?': |
157 | print_usage(); |
158 | return -1; |
159 | } |
160 | } |
161 | |
162 | if (i >= GPIOHANDLES_MAX) { |
163 | fprintf(stderr, |
164 | "Only %d occurrences of '-o' are allowed, %d were found\n" , |
165 | GPIOHANDLES_MAX, i + 1); |
166 | return -1; |
167 | } |
168 | |
169 | num_lines = i; |
170 | |
171 | if (!device_name || !num_lines) { |
172 | print_usage(); |
173 | return -1; |
174 | } |
175 | return hammer_device(device_name, lines: lines, num_lines, loops); |
176 | } |
177 | |