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
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	for (i = 0; i < argc; i++)
228		printf("%d/%s\n", i, argv[i]);
229
230	if (ctlfile == NULL)
231		ctlfile = defctlfile;
232
233	fd = open(ctlfile, O_RDONLY);
234	if (fd < 0) {
235		perror("open");
236		exit(1);
237	}
238
239	if (list) {
240		dump_pins(fd, verbose);
241		close(fd);
242		exit(0);
243	}
244
245	if (toggle) {
246		/*
247		 * -t pin assumes no additional arguments
248		 */
249		if(argc > 0) {
250			usage();
251			exit(1);
252		}
253
254		req.gp_pin = pinn;
255		if (ioctl(fd, GPIOTOGGLE, &req) < 0) {
256			perror("ioctl(GPIOTOGGLE)");
257			exit(1);
258		}
259
260		close(fd);
261		exit (0);
262	}
263
264	if (config) {
265		flags = 0;
266		for (i = 0; i < argc; i++) {
267			flag = 	str2cap(argv[i]);
268			if (flag < 0)
269				fail("Invalid flag: %s\n", argv[i]);
270			flags |= flag;
271		}
272
273		pin.gp_pin = pinn;
274		pin.gp_flags = flags;
275		if (ioctl(fd, GPIOSETCONFIG, &pin) < 0) {
276			perror("ioctl(GPIOSETCONFIG)");
277			exit(1);
278		}
279
280		exit(0);
281	}
282
283	/*
284	 * Last two cases - set value or print value
285	 */
286	if ((argc == 0) || (argc > 2)) {
287		usage();
288		exit(1);
289	}
290
291	pinn = str2int(argv[0], &ok);
292	if (!ok)
293		fail("Invalid pin number: %s\n", argv[0]);
294
295	/*
296	 * Read pin value
297	 */
298	if (argc == 1) {
299		req.gp_pin = pinn;
300		if (ioctl(fd, GPIOGET, &req) < 0) {
301			perror("ioctl(GPIOGET)");
302			exit(1);
303		}
304		printf("%d\n", req.gp_value);
305		exit (0);
306	}
307
308	/* Is it valid number (0 or 1) ? */
309	pinv = str2int(argv[1], &ok);
310	if (!ok || ((pinv != 0) && (pinv != 1)))
311		fail("Invalid pin value: %s\n", argv[1]);
312
313	/*
314	 * Set pin value
315	 */
316	req.gp_pin = pinn;
317	req.gp_value = pinv;
318	if (ioctl(fd, GPIOSET, &req) < 0) {
319		perror("ioctl(GPIOSET)");
320		exit(1);
321	}
322
323	close(fd);
324	exit(0);
325}
326