1213238Sgonzo/*-
2213238Sgonzo * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3213238Sgonzo * All rights reserved.
4213238Sgonzo *
5213238Sgonzo * Redistribution and use in source and binary forms, with or without
6213238Sgonzo * modification, are permitted provided that the following conditions
7213238Sgonzo * are met:
8213238Sgonzo * 1. Redistributions of source code must retain the above copyright
9213238Sgonzo *    notice unmodified, this list of conditions, and the following
10213238Sgonzo *    disclaimer.
11213238Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
12213238Sgonzo *    notice, this list of conditions and the following disclaimer in the
13213238Sgonzo *    documentation and/or other materials provided with the distribution.
14213238Sgonzo *
15213238Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16213238Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17213238Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18213238Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19213238Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20213238Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21213238Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22213238Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23213238Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24213238Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25213238Sgonzo * SUCH DAMAGE.
26213238Sgonzo */
27213238Sgonzo
28213238Sgonzo#include <sys/cdefs.h>
29213238Sgonzo__FBSDID("$FreeBSD$");
30213238Sgonzo
31213238Sgonzo#include <fcntl.h>
32213238Sgonzo#include <getopt.h>
33213238Sgonzo#include <stdio.h>
34213238Sgonzo#include <stdarg.h>
35213238Sgonzo#include <stdlib.h>
36213238Sgonzo#include <string.h>
37213238Sgonzo#include <unistd.h>
38213238Sgonzo
39213238Sgonzo#include <sys/gpio.h>
40213238Sgonzo
41213238Sgonzostruct flag_desc {
42213238Sgonzo	const char *name;
43213238Sgonzo	uint32_t flag;
44213238Sgonzo};
45213238Sgonzo
46213238Sgonzostruct flag_desc gpio_flags[] = {
47213238Sgonzo	{ "IN", GPIO_PIN_INPUT },
48213238Sgonzo	{ "OUT", GPIO_PIN_OUTPUT },
49213238Sgonzo	{ "OD", GPIO_PIN_OPENDRAIN },
50213238Sgonzo	{ "PP", GPIO_PIN_PUSHPULL },
51213238Sgonzo	{ "TS", GPIO_PIN_TRISTATE },
52213238Sgonzo	{ "PU", GPIO_PIN_PULLUP },
53213238Sgonzo	{ "PD", GPIO_PIN_PULLDOWN },
54213238Sgonzo	{ "II", GPIO_PIN_INVIN },
55213238Sgonzo	{ "IO", GPIO_PIN_INVOUT },
56213238Sgonzo	{ "PULSE", GPIO_PIN_PULSATE },
57213238Sgonzo	{ NULL, 0 },
58213238Sgonzo};
59213238Sgonzo
60213238Sgonzoint str2cap(const char *str);
61213238Sgonzo
62213238Sgonzostatic void
63213238Sgonzousage(void)
64213238Sgonzo{
65213238Sgonzo	fprintf(stderr, "Usage:\n");
66213238Sgonzo	fprintf(stderr, "\tgpioctl -f ctldev -l [-v]\n");
67213238Sgonzo	fprintf(stderr, "\tgpioctl -f ctldev -t pin\n");
68213238Sgonzo	fprintf(stderr, "\tgpioctl -f ctldev -c pin flag ...\n");
69213238Sgonzo	fprintf(stderr, "\tgpioctl -f ctldev pin [0|1]\n");
70213238Sgonzo	exit(1);
71213238Sgonzo}
72213238Sgonzo
73213238Sgonzostatic const char *
74213238Sgonzocap2str(uint32_t cap)
75213238Sgonzo{
76213238Sgonzo	struct flag_desc * pdesc = gpio_flags;
77213238Sgonzo	while (pdesc->name) {
78213238Sgonzo		if (pdesc->flag == cap)
79213238Sgonzo			return pdesc->name;
80213238Sgonzo		pdesc++;
81213238Sgonzo	}
82213238Sgonzo
83213238Sgonzo	return "UNKNOWN";
84213238Sgonzo}
85213238Sgonzo
86213238Sgonzoint
87213238Sgonzostr2cap(const char *str)
88213238Sgonzo{
89213238Sgonzo	struct flag_desc * pdesc = gpio_flags;
90213238Sgonzo	while (pdesc->name) {
91213238Sgonzo		if (strcasecmp(str, pdesc->name) == 0)
92213238Sgonzo			return pdesc->flag;
93213238Sgonzo		pdesc++;
94213238Sgonzo	}
95213238Sgonzo
96213238Sgonzo	return (-1);
97213238Sgonzo}
98213238Sgonzo
99213238Sgonzo/*
100213238Sgonzo * Our handmade function for converting string to number
101213238Sgonzo */
102213238Sgonzostatic int
103213238Sgonzostr2int(const char *s, int *ok)
104213238Sgonzo{
105213238Sgonzo	char *endptr;
106213238Sgonzo	int res = strtod(s, &endptr);
107213238Sgonzo	if (endptr != s + strlen(s) )
108213238Sgonzo		*ok = 0;
109213238Sgonzo	else
110213238Sgonzo		*ok = 1;
111213238Sgonzo
112213238Sgonzo	return res;
113213238Sgonzo}
114213238Sgonzo
115213238Sgonzostatic void
116213238Sgonzoprint_caps(int caps)
117213238Sgonzo{
118213238Sgonzo	int i, need_coma;
119213238Sgonzo
120213238Sgonzo	need_coma = 0;
121213238Sgonzo	printf("<");
122213238Sgonzo	for (i = 0; i < 32; i++) {
123213238Sgonzo		if (caps & (1 << i)) {
124213238Sgonzo			if (need_coma)
125213238Sgonzo				printf(",");
126213238Sgonzo			printf("%s", cap2str(1 << i));
127213238Sgonzo			need_coma = 1;
128213238Sgonzo		}
129213238Sgonzo	}
130213238Sgonzo	printf(">");
131213238Sgonzo}
132213238Sgonzo
133213238Sgonzostatic void
134213238Sgonzodump_pins(int fd, int verbose)
135213238Sgonzo{
136213238Sgonzo	int i, maxpin;
137213238Sgonzo	struct gpio_pin pin;
138213238Sgonzo	struct gpio_req req;
139213238Sgonzo
140213238Sgonzo	if (ioctl(fd, GPIOMAXPIN, &maxpin) < 0) {
141213238Sgonzo		perror("ioctl(GPIOMAXPIN)");
142213238Sgonzo		exit(1);
143213238Sgonzo	}
144213238Sgonzo
145213238Sgonzo	for (i = 0; i <= maxpin; i++) {
146213238Sgonzo		pin.gp_pin = i;
147213238Sgonzo		if (ioctl(fd, GPIOGETCONFIG, &pin) < 0)
148213238Sgonzo			/* For some reason this pin is inaccessible */
149213238Sgonzo			continue;
150213238Sgonzo
151213238Sgonzo		req.gp_pin = i;
152213238Sgonzo		if (ioctl(fd, GPIOGET, &req) < 0) {
153213238Sgonzo			/* Now, that's wrong */
154213238Sgonzo			perror("ioctl(GPIOGET)");
155213238Sgonzo			exit(1);
156213238Sgonzo		}
157213238Sgonzo
158213238Sgonzo		printf("pin %02d:\t%d\t%s", pin.gp_pin, req.gp_value,
159213238Sgonzo		    pin.gp_name);
160213238Sgonzo
161213238Sgonzo		print_caps(pin.gp_flags);
162213238Sgonzo
163213238Sgonzo		if (verbose) {
164213238Sgonzo			printf(", caps:");
165213238Sgonzo			print_caps(pin.gp_caps);
166213238Sgonzo		}
167213238Sgonzo		printf("\n");
168213238Sgonzo	}
169213238Sgonzo}
170213238Sgonzo
171213238Sgonzostatic void
172213238Sgonzofail(const char *fmt, ...)
173213238Sgonzo{
174213238Sgonzo	va_list ap;
175213238Sgonzo
176213238Sgonzo	va_start(ap, fmt);
177213238Sgonzo	vfprintf(stderr, fmt, ap);
178213238Sgonzo	va_end(ap);
179213238Sgonzo	exit(1);
180213238Sgonzo}
181213238Sgonzo
182213238Sgonzoint
183213238Sgonzomain(int argc, char **argv)
184213238Sgonzo{
185213238Sgonzo	int i;
186213238Sgonzo	struct gpio_pin pin;
187213238Sgonzo	struct gpio_req req;
188213238Sgonzo	char *ctlfile = NULL;
189213238Sgonzo	int pinn, pinv, fd, ch;
190213238Sgonzo	int flags, flag, ok;
191213238Sgonzo	int config, toggle, verbose, list;
192213238Sgonzo
193213238Sgonzo	config = toggle = verbose = list = pinn = 0;
194213238Sgonzo
195213238Sgonzo	while ((ch = getopt(argc, argv, "c:f:lt:v")) != -1) {
196213238Sgonzo		switch (ch) {
197213238Sgonzo		case 'c':
198213238Sgonzo			config = 1;
199213238Sgonzo			pinn = str2int(optarg, &ok);
200213238Sgonzo			if (!ok)
201213238Sgonzo				fail("Invalid pin number: %s\n", optarg);
202213238Sgonzo			break;
203213238Sgonzo		case 'f':
204213238Sgonzo			ctlfile = optarg;
205213238Sgonzo			break;
206213238Sgonzo		case 'l':
207213238Sgonzo			list = 1;
208213238Sgonzo			break;
209213238Sgonzo		case 't':
210213238Sgonzo			toggle = 1;
211213238Sgonzo			pinn = str2int(optarg, &ok);
212213238Sgonzo			if (!ok)
213213238Sgonzo				fail("Invalid pin number: %s\n", optarg);
214213238Sgonzo			break;
215213238Sgonzo		case 'v':
216213238Sgonzo			verbose = 1;
217213238Sgonzo			break;
218213238Sgonzo		default:
219213238Sgonzo			usage();
220213238Sgonzo			break;
221213238Sgonzo		}
222213238Sgonzo	}
223213238Sgonzo	argv += optind;
224213238Sgonzo	argc -= optind;
225213238Sgonzo	for (i = 0; i < argc; i++)
226213238Sgonzo		printf("%d/%s\n", i, argv[i]);
227213238Sgonzo
228213238Sgonzo	if (ctlfile == NULL)
229213238Sgonzo		fail("No gpioctl device provided\n");
230213238Sgonzo
231213238Sgonzo	fd = open(ctlfile, O_RDONLY);
232213238Sgonzo	if (fd < 0) {
233213238Sgonzo		perror("open");
234213238Sgonzo		exit(1);
235213238Sgonzo	}
236213238Sgonzo
237213238Sgonzo	if (list) {
238213238Sgonzo		dump_pins(fd, verbose);
239213238Sgonzo		close(fd);
240213238Sgonzo		exit(0);
241213238Sgonzo	}
242213238Sgonzo
243213238Sgonzo	if (toggle) {
244213238Sgonzo		/*
245213238Sgonzo		 * -t pin assumes no additional arguments
246213238Sgonzo		 */
247213238Sgonzo		if(argc > 0) {
248213238Sgonzo			usage();
249213238Sgonzo			exit(1);
250213238Sgonzo		}
251213238Sgonzo
252213238Sgonzo		req.gp_pin = pinn;
253213238Sgonzo		if (ioctl(fd, GPIOTOGGLE, &req) < 0) {
254213238Sgonzo			perror("ioctl(GPIOTOGGLE)");
255213238Sgonzo			exit(1);
256213238Sgonzo		}
257213238Sgonzo
258213238Sgonzo		close(fd);
259213238Sgonzo		exit (0);
260213238Sgonzo	}
261213238Sgonzo
262213238Sgonzo	if (config) {
263213238Sgonzo		flags = 0;
264213238Sgonzo		for (i = 0; i < argc; i++) {
265213238Sgonzo			flag = 	str2cap(argv[i]);
266213238Sgonzo			if (flag < 0)
267213238Sgonzo				fail("Invalid flag: %s\n", argv[i]);
268213238Sgonzo			flags |= flag;
269213238Sgonzo		}
270213238Sgonzo
271213238Sgonzo		pin.gp_pin = pinn;
272213238Sgonzo		pin.gp_flags = flags;
273213238Sgonzo		if (ioctl(fd, GPIOSETCONFIG, &pin) < 0) {
274213238Sgonzo			perror("ioctl(GPIOSETCONFIG)");
275213238Sgonzo			exit(1);
276213238Sgonzo		}
277213238Sgonzo
278213238Sgonzo		exit(0);
279213238Sgonzo	}
280213238Sgonzo
281213238Sgonzo	/*
282213238Sgonzo	 * Last two cases - set value or print value
283213238Sgonzo	 */
284213238Sgonzo	if ((argc == 0) || (argc > 2)) {
285213238Sgonzo		usage();
286213238Sgonzo		exit(1);
287213238Sgonzo	}
288213238Sgonzo
289213238Sgonzo	pinn = str2int(argv[0], &ok);
290213238Sgonzo	if (!ok)
291213238Sgonzo		fail("Invalid pin number: %s\n", argv[0]);
292213238Sgonzo
293213238Sgonzo	/*
294213238Sgonzo	 * Read pin value
295213238Sgonzo	 */
296213238Sgonzo	if (argc == 1) {
297213238Sgonzo		req.gp_pin = pinn;
298213238Sgonzo		if (ioctl(fd, GPIOGET, &req) < 0) {
299213238Sgonzo			perror("ioctl(GPIOGET)");
300213238Sgonzo			exit(1);
301213238Sgonzo		}
302213238Sgonzo		printf("%d\n", req.gp_value);
303213238Sgonzo		exit (0);
304213238Sgonzo	}
305213238Sgonzo
306213238Sgonzo	/* Is it valid number (0 or 1) ? */
307213238Sgonzo	pinv = str2int(argv[1], &ok);
308213238Sgonzo	if (!ok || ((pinv != 0) && (pinv != 1)))
309213238Sgonzo		fail("Invalid pin value: %s\n", argv[1]);
310213238Sgonzo
311213238Sgonzo	/*
312213238Sgonzo	 * Set pin value
313213238Sgonzo	 */
314213238Sgonzo	req.gp_pin = pinn;
315213238Sgonzo	req.gp_value = pinv;
316213238Sgonzo	if (ioctl(fd, GPIOSET, &req) < 0) {
317213238Sgonzo		perror("ioctl(GPIOSET)");
318213238Sgonzo		exit(1);
319213238Sgonzo	}
320213238Sgonzo
321213238Sgonzo	close(fd);
322213238Sgonzo	exit(0);
323213238Sgonzo}
324