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>
33255629Ssbruno#include <paths.h>
34213238Sgonzo#include <stdio.h>
35213238Sgonzo#include <stdarg.h>
36213238Sgonzo#include <stdlib.h>
37213238Sgonzo#include <string.h>
38213238Sgonzo#include <unistd.h>
39213238Sgonzo
40213238Sgonzo#include <sys/gpio.h>
41213238Sgonzo
42213238Sgonzostruct flag_desc {
43213238Sgonzo	const char *name;
44213238Sgonzo	uint32_t flag;
45213238Sgonzo};
46213238Sgonzo
47241737Sedstatic struct flag_desc gpio_flags[] = {
48213238Sgonzo	{ "IN", GPIO_PIN_INPUT },
49213238Sgonzo	{ "OUT", GPIO_PIN_OUTPUT },
50213238Sgonzo	{ "OD", GPIO_PIN_OPENDRAIN },
51213238Sgonzo	{ "PP", GPIO_PIN_PUSHPULL },
52213238Sgonzo	{ "TS", GPIO_PIN_TRISTATE },
53213238Sgonzo	{ "PU", GPIO_PIN_PULLUP },
54213238Sgonzo	{ "PD", GPIO_PIN_PULLDOWN },
55213238Sgonzo	{ "II", GPIO_PIN_INVIN },
56213238Sgonzo	{ "IO", GPIO_PIN_INVOUT },
57213238Sgonzo	{ "PULSE", GPIO_PIN_PULSATE },
58213238Sgonzo	{ NULL, 0 },
59213238Sgonzo};
60213238Sgonzo
61213238Sgonzoint str2cap(const char *str);
62213238Sgonzo
63213238Sgonzostatic void
64213238Sgonzousage(void)
65213238Sgonzo{
66213238Sgonzo	fprintf(stderr, "Usage:\n");
67255629Ssbruno	fprintf(stderr, "\tgpioctl [-f ctldev] -l [-v]\n");
68255629Ssbruno	fprintf(stderr, "\tgpioctl [-f ctldev] -t pin\n");
69255629Ssbruno	fprintf(stderr, "\tgpioctl [-f ctldev] -c pin flag ...\n");
70255629Ssbruno	fprintf(stderr, "\tgpioctl [-f ctldev] pin [0|1]\n");
71213238Sgonzo	exit(1);
72213238Sgonzo}
73213238Sgonzo
74213238Sgonzostatic const char *
75213238Sgonzocap2str(uint32_t cap)
76213238Sgonzo{
77213238Sgonzo	struct flag_desc * pdesc = gpio_flags;
78213238Sgonzo	while (pdesc->name) {
79213238Sgonzo		if (pdesc->flag == cap)
80213238Sgonzo			return pdesc->name;
81213238Sgonzo		pdesc++;
82213238Sgonzo	}
83213238Sgonzo
84213238Sgonzo	return "UNKNOWN";
85213238Sgonzo}
86213238Sgonzo
87213238Sgonzoint
88213238Sgonzostr2cap(const char *str)
89213238Sgonzo{
90213238Sgonzo	struct flag_desc * pdesc = gpio_flags;
91213238Sgonzo	while (pdesc->name) {
92213238Sgonzo		if (strcasecmp(str, pdesc->name) == 0)
93213238Sgonzo			return pdesc->flag;
94213238Sgonzo		pdesc++;
95213238Sgonzo	}
96213238Sgonzo
97213238Sgonzo	return (-1);
98213238Sgonzo}
99213238Sgonzo
100213238Sgonzo/*
101213238Sgonzo * Our handmade function for converting string to number
102213238Sgonzo */
103213238Sgonzostatic int
104213238Sgonzostr2int(const char *s, int *ok)
105213238Sgonzo{
106213238Sgonzo	char *endptr;
107213238Sgonzo	int res = strtod(s, &endptr);
108213238Sgonzo	if (endptr != s + strlen(s) )
109213238Sgonzo		*ok = 0;
110213238Sgonzo	else
111213238Sgonzo		*ok = 1;
112213238Sgonzo
113213238Sgonzo	return res;
114213238Sgonzo}
115213238Sgonzo
116213238Sgonzostatic void
117213238Sgonzoprint_caps(int caps)
118213238Sgonzo{
119213238Sgonzo	int i, need_coma;
120213238Sgonzo
121213238Sgonzo	need_coma = 0;
122213238Sgonzo	printf("<");
123213238Sgonzo	for (i = 0; i < 32; i++) {
124213238Sgonzo		if (caps & (1 << i)) {
125213238Sgonzo			if (need_coma)
126213238Sgonzo				printf(",");
127213238Sgonzo			printf("%s", cap2str(1 << i));
128213238Sgonzo			need_coma = 1;
129213238Sgonzo		}
130213238Sgonzo	}
131213238Sgonzo	printf(">");
132213238Sgonzo}
133213238Sgonzo
134213238Sgonzostatic void
135213238Sgonzodump_pins(int fd, int verbose)
136213238Sgonzo{
137213238Sgonzo	int i, maxpin;
138213238Sgonzo	struct gpio_pin pin;
139213238Sgonzo	struct gpio_req req;
140213238Sgonzo
141213238Sgonzo	if (ioctl(fd, GPIOMAXPIN, &maxpin) < 0) {
142213238Sgonzo		perror("ioctl(GPIOMAXPIN)");
143213238Sgonzo		exit(1);
144213238Sgonzo	}
145213238Sgonzo
146213238Sgonzo	for (i = 0; i <= maxpin; i++) {
147213238Sgonzo		pin.gp_pin = i;
148213238Sgonzo		if (ioctl(fd, GPIOGETCONFIG, &pin) < 0)
149213238Sgonzo			/* For some reason this pin is inaccessible */
150213238Sgonzo			continue;
151213238Sgonzo
152213238Sgonzo		req.gp_pin = i;
153213238Sgonzo		if (ioctl(fd, GPIOGET, &req) < 0) {
154213238Sgonzo			/* Now, that's wrong */
155213238Sgonzo			perror("ioctl(GPIOGET)");
156213238Sgonzo			exit(1);
157213238Sgonzo		}
158213238Sgonzo
159213238Sgonzo		printf("pin %02d:\t%d\t%s", pin.gp_pin, req.gp_value,
160213238Sgonzo		    pin.gp_name);
161213238Sgonzo
162213238Sgonzo		print_caps(pin.gp_flags);
163213238Sgonzo
164213238Sgonzo		if (verbose) {
165213238Sgonzo			printf(", caps:");
166213238Sgonzo			print_caps(pin.gp_caps);
167213238Sgonzo		}
168213238Sgonzo		printf("\n");
169213238Sgonzo	}
170213238Sgonzo}
171213238Sgonzo
172213238Sgonzostatic void
173213238Sgonzofail(const char *fmt, ...)
174213238Sgonzo{
175213238Sgonzo	va_list ap;
176213238Sgonzo
177213238Sgonzo	va_start(ap, fmt);
178213238Sgonzo	vfprintf(stderr, fmt, ap);
179213238Sgonzo	va_end(ap);
180213238Sgonzo	exit(1);
181213238Sgonzo}
182213238Sgonzo
183213238Sgonzoint
184213238Sgonzomain(int argc, char **argv)
185213238Sgonzo{
186213238Sgonzo	int i;
187213238Sgonzo	struct gpio_pin pin;
188213238Sgonzo	struct gpio_req req;
189255629Ssbruno	char defctlfile[] = _PATH_DEVGPIOC "0";
190213238Sgonzo	char *ctlfile = NULL;
191213238Sgonzo	int pinn, pinv, fd, ch;
192213238Sgonzo	int flags, flag, ok;
193213238Sgonzo	int config, toggle, verbose, list;
194213238Sgonzo
195213238Sgonzo	config = toggle = verbose = list = pinn = 0;
196213238Sgonzo
197213238Sgonzo	while ((ch = getopt(argc, argv, "c:f:lt:v")) != -1) {
198213238Sgonzo		switch (ch) {
199213238Sgonzo		case 'c':
200213238Sgonzo			config = 1;
201213238Sgonzo			pinn = str2int(optarg, &ok);
202213238Sgonzo			if (!ok)
203213238Sgonzo				fail("Invalid pin number: %s\n", optarg);
204213238Sgonzo			break;
205213238Sgonzo		case 'f':
206213238Sgonzo			ctlfile = optarg;
207213238Sgonzo			break;
208213238Sgonzo		case 'l':
209213238Sgonzo			list = 1;
210213238Sgonzo			break;
211213238Sgonzo		case 't':
212213238Sgonzo			toggle = 1;
213213238Sgonzo			pinn = str2int(optarg, &ok);
214213238Sgonzo			if (!ok)
215213238Sgonzo				fail("Invalid pin number: %s\n", optarg);
216213238Sgonzo			break;
217213238Sgonzo		case 'v':
218213238Sgonzo			verbose = 1;
219213238Sgonzo			break;
220213238Sgonzo		default:
221213238Sgonzo			usage();
222213238Sgonzo			break;
223213238Sgonzo		}
224213238Sgonzo	}
225213238Sgonzo	argv += optind;
226213238Sgonzo	argc -= optind;
227213238Sgonzo	for (i = 0; i < argc; i++)
228213238Sgonzo		printf("%d/%s\n", i, argv[i]);
229213238Sgonzo
230213238Sgonzo	if (ctlfile == NULL)
231255629Ssbruno		ctlfile = defctlfile;
232213238Sgonzo
233213238Sgonzo	fd = open(ctlfile, O_RDONLY);
234213238Sgonzo	if (fd < 0) {
235213238Sgonzo		perror("open");
236213238Sgonzo		exit(1);
237213238Sgonzo	}
238213238Sgonzo
239213238Sgonzo	if (list) {
240213238Sgonzo		dump_pins(fd, verbose);
241213238Sgonzo		close(fd);
242213238Sgonzo		exit(0);
243213238Sgonzo	}
244213238Sgonzo
245213238Sgonzo	if (toggle) {
246213238Sgonzo		/*
247213238Sgonzo		 * -t pin assumes no additional arguments
248213238Sgonzo		 */
249213238Sgonzo		if(argc > 0) {
250213238Sgonzo			usage();
251213238Sgonzo			exit(1);
252213238Sgonzo		}
253213238Sgonzo
254213238Sgonzo		req.gp_pin = pinn;
255213238Sgonzo		if (ioctl(fd, GPIOTOGGLE, &req) < 0) {
256213238Sgonzo			perror("ioctl(GPIOTOGGLE)");
257213238Sgonzo			exit(1);
258213238Sgonzo		}
259213238Sgonzo
260213238Sgonzo		close(fd);
261213238Sgonzo		exit (0);
262213238Sgonzo	}
263213238Sgonzo
264213238Sgonzo	if (config) {
265213238Sgonzo		flags = 0;
266213238Sgonzo		for (i = 0; i < argc; i++) {
267213238Sgonzo			flag = 	str2cap(argv[i]);
268213238Sgonzo			if (flag < 0)
269213238Sgonzo				fail("Invalid flag: %s\n", argv[i]);
270213238Sgonzo			flags |= flag;
271213238Sgonzo		}
272213238Sgonzo
273213238Sgonzo		pin.gp_pin = pinn;
274213238Sgonzo		pin.gp_flags = flags;
275213238Sgonzo		if (ioctl(fd, GPIOSETCONFIG, &pin) < 0) {
276213238Sgonzo			perror("ioctl(GPIOSETCONFIG)");
277213238Sgonzo			exit(1);
278213238Sgonzo		}
279213238Sgonzo
280213238Sgonzo		exit(0);
281213238Sgonzo	}
282213238Sgonzo
283213238Sgonzo	/*
284213238Sgonzo	 * Last two cases - set value or print value
285213238Sgonzo	 */
286213238Sgonzo	if ((argc == 0) || (argc > 2)) {
287213238Sgonzo		usage();
288213238Sgonzo		exit(1);
289213238Sgonzo	}
290213238Sgonzo
291213238Sgonzo	pinn = str2int(argv[0], &ok);
292213238Sgonzo	if (!ok)
293213238Sgonzo		fail("Invalid pin number: %s\n", argv[0]);
294213238Sgonzo
295213238Sgonzo	/*
296213238Sgonzo	 * Read pin value
297213238Sgonzo	 */
298213238Sgonzo	if (argc == 1) {
299213238Sgonzo		req.gp_pin = pinn;
300213238Sgonzo		if (ioctl(fd, GPIOGET, &req) < 0) {
301213238Sgonzo			perror("ioctl(GPIOGET)");
302213238Sgonzo			exit(1);
303213238Sgonzo		}
304213238Sgonzo		printf("%d\n", req.gp_value);
305213238Sgonzo		exit (0);
306213238Sgonzo	}
307213238Sgonzo
308213238Sgonzo	/* Is it valid number (0 or 1) ? */
309213238Sgonzo	pinv = str2int(argv[1], &ok);
310213238Sgonzo	if (!ok || ((pinv != 0) && (pinv != 1)))
311213238Sgonzo		fail("Invalid pin value: %s\n", argv[1]);
312213238Sgonzo
313213238Sgonzo	/*
314213238Sgonzo	 * Set pin value
315213238Sgonzo	 */
316213238Sgonzo	req.gp_pin = pinn;
317213238Sgonzo	req.gp_value = pinv;
318213238Sgonzo	if (ioctl(fd, GPIOSET, &req) < 0) {
319213238Sgonzo		perror("ioctl(GPIOSET)");
320213238Sgonzo		exit(1);
321213238Sgonzo	}
322213238Sgonzo
323213238Sgonzo	close(fd);
324213238Sgonzo	exit(0);
325213238Sgonzo}
326