gpioctl.c revision 275500
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: stable/10/usr.sbin/gpioctl/gpioctl.c 275500 2014-12-05 07:51:50Z rpaulo $");
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
42struct flag_desc {
43	const char *name;
44	uint32_t flag;
45};
46
47static 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
61int str2cap(const char *str);
62
63static void
64usage(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
74static const char *
75cap2str(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
87int
88str2cap(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 */
103static int
104str2int(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
116static void
117print_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
134static void
135dump_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
172static void
173fail(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
183int
184main(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