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
41struct flag_desc {
42	const char *name;
43	uint32_t flag;
44};
45
46struct 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
60int str2cap(const char *str);
61
62static void
63usage(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
73static const char *
74cap2str(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
86int
87str2cap(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 */
102static int
103str2int(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
115static void
116print_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
133static void
134dump_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
171static void
172fail(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
182int
183main(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