1 /*-
2 * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
10 * disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <fcntl.h>
32 #include <getopt.h>
33 #include <paths.h>
34 #include <stdio.h>
35 #include <stdarg.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 #include <sys/gpio.h>
41
42 struct flag_desc {
43 const char *name;
44 uint32_t flag;
45 };
46
47 static struct flag_desc gpio_flags[] = {
48 { "IN", GPIO_PIN_INPUT },
49 { "OUT", GPIO_PIN_OUTPUT },
50 { "OD", GPIO_PIN_OPENDRAIN },
51 { "PP", GPIO_PIN_PUSHPULL },
52 { "TS", GPIO_PIN_TRISTATE },
53 { "PU", GPIO_PIN_PULLUP },
54 { "PD", GPIO_PIN_PULLDOWN },
55 { "II", GPIO_PIN_INVIN },
56 { "IO", GPIO_PIN_INVOUT },
57 { "PULSE", GPIO_PIN_PULSATE },
58 { NULL, 0 },
59 };
60
61 int str2cap(const char *str);
62
63 static void
usage(void)64 usage(void)
65 {
66 fprintf(stderr, "Usage:\n");
67 fprintf(stderr, "\tgpioctl [-f ctldev] -l [-v]\n");
68 fprintf(stderr, "\tgpioctl [-f ctldev] -t pin\n");
69 fprintf(stderr, "\tgpioctl [-f ctldev] -c pin flag ...\n");
70 fprintf(stderr, "\tgpioctl [-f ctldev] pin [0|1]\n");
71 exit(1);
72 }
73
74 static const char *
cap2str(uint32_t cap)75 cap2str(uint32_t cap)
76 {
77 struct flag_desc * pdesc = gpio_flags;
78 while (pdesc->name) {
79 if (pdesc->flag == cap)
80 return pdesc->name;
81 pdesc++;
82 }
83
84 return "UNKNOWN";
85 }
86
87 int
str2cap(const char * str)88 str2cap(const char *str)
89 {
90 struct flag_desc * pdesc = gpio_flags;
91 while (pdesc->name) {
92 if (strcasecmp(str, pdesc->name) == 0)
93 return pdesc->flag;
94 pdesc++;
95 }
96
97 return (-1);
98 }
99
100 /*
101 * Our handmade function for converting string to number
102 */
103 static int
str2int(const char * s,int * ok)104 str2int(const char *s, int *ok)
105 {
106 char *endptr;
107 int res = strtod(s, &endptr);
108 if (endptr != s + strlen(s) )
109 *ok = 0;
110 else
111 *ok = 1;
112
113 return res;
114 }
115
116 static void
print_caps(int caps)117 print_caps(int caps)
118 {
119 int i, need_coma;
120
121 need_coma = 0;
122 printf("<");
123 for (i = 0; i < 32; i++) {
124 if (caps & (1 << i)) {
125 if (need_coma)
126 printf(",");
127 printf("%s", cap2str(1 << i));
128 need_coma = 1;
129 }
130 }
131 printf(">");
132 }
133
134 static void
dump_pins(int fd,int verbose)135 dump_pins(int fd, int verbose)
136 {
137 int i, maxpin;
138 struct gpio_pin pin;
139 struct gpio_req req;
140
141 if (ioctl(fd, GPIOMAXPIN, &maxpin) < 0) {
142 perror("ioctl(GPIOMAXPIN)");
143 exit(1);
144 }
145
146 for (i = 0; i <= maxpin; i++) {
147 pin.gp_pin = i;
148 if (ioctl(fd, GPIOGETCONFIG, &pin) < 0)
149 /* For some reason this pin is inaccessible */
150 continue;
151
152 req.gp_pin = i;
153 if (ioctl(fd, GPIOGET, &req) < 0) {
154 /* Now, that's wrong */
155 perror("ioctl(GPIOGET)");
156 exit(1);
157 }
158
159 printf("pin %02d:\t%d\t%s", pin.gp_pin, req.gp_value,
160 pin.gp_name);
161
162 print_caps(pin.gp_flags);
163
164 if (verbose) {
165 printf(", caps:");
166 print_caps(pin.gp_caps);
167 }
168 printf("\n");
169 }
170 }
171
172 static void
fail(const char * fmt,...)173 fail(const char *fmt, ...)
174 {
175 va_list ap;
176
177 va_start(ap, fmt);
178 vfprintf(stderr, fmt, ap);
179 va_end(ap);
180 exit(1);
181 }
182
183 int
main(int argc,char ** argv)184 main(int argc, char **argv)
185 {
186 int i;
187 struct gpio_pin pin;
188 struct gpio_req req;
189 char defctlfile[] = _PATH_DEVGPIOC "0";
190 char *ctlfile = NULL;
191 int pinn, pinv, fd, ch;
192 int flags, flag, ok;
193 int config, toggle, verbose, list;
194
195 config = toggle = verbose = list = pinn = 0;
196
197 while ((ch = getopt(argc, argv, "c:f:lt:v")) != -1) {
198 switch (ch) {
199 case 'c':
200 config = 1;
201 pinn = str2int(optarg, &ok);
202 if (!ok)
203 fail("Invalid pin number: %s\n", optarg);
204 break;
205 case 'f':
206 ctlfile = optarg;
207 break;
208 case 'l':
209 list = 1;
210 break;
211 case 't':
212 toggle = 1;
213 pinn = str2int(optarg, &ok);
214 if (!ok)
215 fail("Invalid pin number: %s\n", optarg);
216 break;
217 case 'v':
218 verbose = 1;
219 break;
220 default:
221 usage();
222 break;
223 }
224 }
225 argv += optind;
226 argc -= optind;
227 if (ctlfile == NULL)
228 ctlfile = defctlfile;
229
230 fd = open(ctlfile, O_RDONLY);
231 if (fd < 0) {
232 perror("open");
233 exit(1);
234 }
235
236 if (list) {
237 dump_pins(fd, verbose);
238 close(fd);
239 exit(0);
240 }
241
242 if (toggle) {
243 /*
244 * -t pin assumes no additional arguments
245 */
246 if(argc > 0) {
247 usage();
248 exit(1);
249 }
250
251 req.gp_pin = pinn;
252 if (ioctl(fd, GPIOTOGGLE, &req) < 0) {
253 perror("ioctl(GPIOTOGGLE)");
254 exit(1);
255 }
256
257 close(fd);
258 exit (0);
259 }
260
261 if (config) {
262 flags = 0;
263 for (i = 0; i < argc; i++) {
264 flag = str2cap(argv[i]);
265 if (flag < 0)
266 fail("Invalid flag: %s\n", argv[i]);
267 flags |= flag;
268 }
269
270 pin.gp_pin = pinn;
271 pin.gp_flags = flags;
272 if (ioctl(fd, GPIOSETCONFIG, &pin) < 0) {
273 perror("ioctl(GPIOSETCONFIG)");
274 exit(1);
275 }
276
277 exit(0);
278 }
279
280 /*
281 * Last two cases - set value or print value
282 */
283 if ((argc == 0) || (argc > 2)) {
284 usage();
285 exit(1);
286 }
287
288 pinn = str2int(argv[0], &ok);
289 if (!ok)
290 fail("Invalid pin number: %s\n", argv[0]);
291
292 /*
293 * Read pin value
294 */
295 if (argc == 1) {
296 req.gp_pin = pinn;
297 if (ioctl(fd, GPIOGET, &req) < 0) {
298 perror("ioctl(GPIOGET)");
299 exit(1);
300 }
301 printf("%d\n", req.gp_value);
302 exit (0);
303 }
304
305 /* Is it valid number (0 or 1) ? */
306 pinv = str2int(argv[1], &ok);
307 if (!ok || ((pinv != 0) && (pinv != 1)))
308 fail("Invalid pin value: %s\n", argv[1]);
309
310 /*
311 * Set pin value
312 */
313 req.gp_pin = pinn;
314 req.gp_value = pinv;
315 if (ioctl(fd, GPIOSET, &req) < 0) {
316 perror("ioctl(GPIOSET)");
317 exit(1);
318 }
319
320 close(fd);
321 exit(0);
322 }
323