xref: /freebsd/usr.sbin/gpioctl/gpioctl.c (revision 3f0164abf32b9b761e0a2cb4bdca3a8b84f156d4)
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 <stdio.h>
34 #include <stdarg.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include <sys/gpio.h>
40 
41 struct flag_desc {
42 	const char *name;
43 	uint32_t flag;
44 };
45 
46 static struct flag_desc gpio_flags[] = {
47 	{ "IN", GPIO_PIN_INPUT },
48 	{ "OUT", GPIO_PIN_OUTPUT },
49 	{ "OD", GPIO_PIN_OPENDRAIN },
50 	{ "PP", GPIO_PIN_PUSHPULL },
51 	{ "TS", GPIO_PIN_TRISTATE },
52 	{ "PU", GPIO_PIN_PULLUP },
53 	{ "PD", GPIO_PIN_PULLDOWN },
54 	{ "II", GPIO_PIN_INVIN },
55 	{ "IO", GPIO_PIN_INVOUT },
56 	{ "PULSE", GPIO_PIN_PULSATE },
57 	{ NULL, 0 },
58 };
59 
60 int str2cap(const char *str);
61 
62 static void
63 usage(void)
64 {
65 	fprintf(stderr, "Usage:\n");
66 	fprintf(stderr, "\tgpioctl -f ctldev -l [-v]\n");
67 	fprintf(stderr, "\tgpioctl -f ctldev -t pin\n");
68 	fprintf(stderr, "\tgpioctl -f ctldev -c pin flag ...\n");
69 	fprintf(stderr, "\tgpioctl -f ctldev pin [0|1]\n");
70 	exit(1);
71 }
72 
73 static const char *
74 cap2str(uint32_t cap)
75 {
76 	struct flag_desc * pdesc = gpio_flags;
77 	while (pdesc->name) {
78 		if (pdesc->flag == cap)
79 			return pdesc->name;
80 		pdesc++;
81 	}
82 
83 	return "UNKNOWN";
84 }
85 
86 int
87 str2cap(const char *str)
88 {
89 	struct flag_desc * pdesc = gpio_flags;
90 	while (pdesc->name) {
91 		if (strcasecmp(str, pdesc->name) == 0)
92 			return pdesc->flag;
93 		pdesc++;
94 	}
95 
96 	return (-1);
97 }
98 
99 /*
100  * Our handmade function for converting string to number
101  */
102 static int
103 str2int(const char *s, int *ok)
104 {
105 	char *endptr;
106 	int res = strtod(s, &endptr);
107 	if (endptr != s + strlen(s) )
108 		*ok = 0;
109 	else
110 		*ok = 1;
111 
112 	return res;
113 }
114 
115 static void
116 print_caps(int caps)
117 {
118 	int i, need_coma;
119 
120 	need_coma = 0;
121 	printf("<");
122 	for (i = 0; i < 32; i++) {
123 		if (caps & (1 << i)) {
124 			if (need_coma)
125 				printf(",");
126 			printf("%s", cap2str(1 << i));
127 			need_coma = 1;
128 		}
129 	}
130 	printf(">");
131 }
132 
133 static void
134 dump_pins(int fd, int verbose)
135 {
136 	int i, maxpin;
137 	struct gpio_pin pin;
138 	struct gpio_req req;
139 
140 	if (ioctl(fd, GPIOMAXPIN, &maxpin) < 0) {
141 		perror("ioctl(GPIOMAXPIN)");
142 		exit(1);
143 	}
144 
145 	for (i = 0; i <= maxpin; i++) {
146 		pin.gp_pin = i;
147 		if (ioctl(fd, GPIOGETCONFIG, &pin) < 0)
148 			/* For some reason this pin is inaccessible */
149 			continue;
150 
151 		req.gp_pin = i;
152 		if (ioctl(fd, GPIOGET, &req) < 0) {
153 			/* Now, that's wrong */
154 			perror("ioctl(GPIOGET)");
155 			exit(1);
156 		}
157 
158 		printf("pin %02d:\t%d\t%s", pin.gp_pin, req.gp_value,
159 		    pin.gp_name);
160 
161 		print_caps(pin.gp_flags);
162 
163 		if (verbose) {
164 			printf(", caps:");
165 			print_caps(pin.gp_caps);
166 		}
167 		printf("\n");
168 	}
169 }
170 
171 static void
172 fail(const char *fmt, ...)
173 {
174 	va_list ap;
175 
176 	va_start(ap, fmt);
177 	vfprintf(stderr, fmt, ap);
178 	va_end(ap);
179 	exit(1);
180 }
181 
182 int
183 main(int argc, char **argv)
184 {
185 	int i;
186 	struct gpio_pin pin;
187 	struct gpio_req req;
188 	char *ctlfile = NULL;
189 	int pinn, pinv, fd, ch;
190 	int flags, flag, ok;
191 	int config, toggle, verbose, list;
192 
193 	config = toggle = verbose = list = pinn = 0;
194 
195 	while ((ch = getopt(argc, argv, "c:f:lt:v")) != -1) {
196 		switch (ch) {
197 		case 'c':
198 			config = 1;
199 			pinn = str2int(optarg, &ok);
200 			if (!ok)
201 				fail("Invalid pin number: %s\n", optarg);
202 			break;
203 		case 'f':
204 			ctlfile = optarg;
205 			break;
206 		case 'l':
207 			list = 1;
208 			break;
209 		case 't':
210 			toggle = 1;
211 			pinn = str2int(optarg, &ok);
212 			if (!ok)
213 				fail("Invalid pin number: %s\n", optarg);
214 			break;
215 		case 'v':
216 			verbose = 1;
217 			break;
218 		default:
219 			usage();
220 			break;
221 		}
222 	}
223 	argv += optind;
224 	argc -= optind;
225 	for (i = 0; i < argc; i++)
226 		printf("%d/%s\n", i, argv[i]);
227 
228 	if (ctlfile == NULL)
229 		fail("No gpioctl device provided\n");
230 
231 	fd = open(ctlfile, O_RDONLY);
232 	if (fd < 0) {
233 		perror("open");
234 		exit(1);
235 	}
236 
237 	if (list) {
238 		dump_pins(fd, verbose);
239 		close(fd);
240 		exit(0);
241 	}
242 
243 	if (toggle) {
244 		/*
245 		 * -t pin assumes no additional arguments
246 		 */
247 		if(argc > 0) {
248 			usage();
249 			exit(1);
250 		}
251 
252 		req.gp_pin = pinn;
253 		if (ioctl(fd, GPIOTOGGLE, &req) < 0) {
254 			perror("ioctl(GPIOTOGGLE)");
255 			exit(1);
256 		}
257 
258 		close(fd);
259 		exit (0);
260 	}
261 
262 	if (config) {
263 		flags = 0;
264 		for (i = 0; i < argc; i++) {
265 			flag = 	str2cap(argv[i]);
266 			if (flag < 0)
267 				fail("Invalid flag: %s\n", argv[i]);
268 			flags |= flag;
269 		}
270 
271 		pin.gp_pin = pinn;
272 		pin.gp_flags = flags;
273 		if (ioctl(fd, GPIOSETCONFIG, &pin) < 0) {
274 			perror("ioctl(GPIOSETCONFIG)");
275 			exit(1);
276 		}
277 
278 		exit(0);
279 	}
280 
281 	/*
282 	 * Last two cases - set value or print value
283 	 */
284 	if ((argc == 0) || (argc > 2)) {
285 		usage();
286 		exit(1);
287 	}
288 
289 	pinn = str2int(argv[0], &ok);
290 	if (!ok)
291 		fail("Invalid pin number: %s\n", argv[0]);
292 
293 	/*
294 	 * Read pin value
295 	 */
296 	if (argc == 1) {
297 		req.gp_pin = pinn;
298 		if (ioctl(fd, GPIOGET, &req) < 0) {
299 			perror("ioctl(GPIOGET)");
300 			exit(1);
301 		}
302 		printf("%d\n", req.gp_value);
303 		exit (0);
304 	}
305 
306 	/* Is it valid number (0 or 1) ? */
307 	pinv = str2int(argv[1], &ok);
308 	if (!ok || ((pinv != 0) && (pinv != 1)))
309 		fail("Invalid pin value: %s\n", argv[1]);
310 
311 	/*
312 	 * Set pin value
313 	 */
314 	req.gp_pin = pinn;
315 	req.gp_value = pinv;
316 	if (ioctl(fd, GPIOSET, &req) < 0) {
317 		perror("ioctl(GPIOSET)");
318 		exit(1);
319 	}
320 
321 	close(fd);
322 	exit(0);
323 }
324