1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
5 * Copyright (c) 2014, Rui Paulo <rpaulo@FreeBSD.org>
6 * Copyright (c) 2015, Emmanuel Vadot <manu@bidouilliste.com>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice unmodified, this list of conditions, and the following
14 *    disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#include <fcntl.h>
34#include <getopt.h>
35#include <paths.h>
36#include <stdio.h>
37#include <stdarg.h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41
42#include <libgpio.h>
43
44#define PIN_TYPE_NUMBER		1
45#define PIN_TYPE_NAME		2
46
47struct flag_desc {
48	const char *name;
49	uint32_t flag;
50};
51
52static struct flag_desc gpio_flags[] = {
53	{ "IN", GPIO_PIN_INPUT },
54	{ "OUT", GPIO_PIN_OUTPUT },
55	{ "OD", GPIO_PIN_OPENDRAIN },
56	{ "PP", GPIO_PIN_PUSHPULL },
57	{ "TS", GPIO_PIN_TRISTATE },
58	{ "PU", GPIO_PIN_PULLUP },
59	{ "PD", GPIO_PIN_PULLDOWN },
60	{ "II", GPIO_PIN_INVIN },
61	{ "IO", GPIO_PIN_INVOUT },
62	{ "PULSE", GPIO_PIN_PULSATE },
63	{ "INTRLL", GPIO_INTR_LEVEL_LOW},
64	{ "INTRLH", GPIO_INTR_LEVEL_HIGH},
65	{ "INTRER", GPIO_INTR_EDGE_RISING},
66	{ "INTREF", GPIO_INTR_EDGE_FALLING},
67	{ "INTREB", GPIO_INTR_EDGE_BOTH},
68	{ NULL, 0 },
69};
70
71int str2cap(const char *str);
72
73static void
74usage(void)
75{
76	fprintf(stderr, "Usage:\n");
77	fprintf(stderr, "\tgpioctl [-f ctldev] -l [-v]\n");
78	fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] -t pin\n");
79	fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] -c pin flag ...\n");
80	fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] -n pin pin-name\n");
81	fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] pin [0|1]\n");
82	exit(1);
83}
84
85static const char *
86cap2str(uint32_t cap)
87{
88	struct flag_desc * pdesc = gpio_flags;
89	while (pdesc->name) {
90		if (pdesc->flag == cap)
91			return pdesc->name;
92		pdesc++;
93	}
94
95	return "UNKNOWN";
96}
97
98int
99str2cap(const char *str)
100{
101	struct flag_desc * pdesc = gpio_flags;
102	while (pdesc->name) {
103		if (strcasecmp(str, pdesc->name) == 0)
104			return pdesc->flag;
105		pdesc++;
106	}
107
108	return (-1);
109}
110
111/*
112 * Our handmade function for converting string to number
113 */
114static int
115str2int(const char *s, int *ok)
116{
117	char *endptr;
118	int res = strtod(s, &endptr);
119	if (endptr != s + strlen(s) )
120		*ok = 0;
121	else
122		*ok = 1;
123
124	return res;
125}
126
127static void
128print_caps(int caps)
129{
130	int i, need_coma;
131
132	need_coma = 0;
133	printf("<");
134	for (i = 0; i < 32; i++) {
135		if (caps & (1 << i)) {
136			if (need_coma)
137				printf(",");
138			printf("%s", cap2str(1 << i));
139			need_coma = 1;
140		}
141	}
142	printf(">");
143}
144
145static void
146dump_pins(gpio_handle_t handle, int verbose)
147{
148	int i, maxpin, pinv;
149	gpio_config_t *cfgs;
150	gpio_config_t *pin;
151
152	maxpin = gpio_pin_list(handle, &cfgs);
153	if (maxpin < 0) {
154		perror("gpio_pin_list");
155		exit(1);
156	}
157
158	for (i = 0; i <= maxpin; i++) {
159		pin = cfgs + i;
160		pinv = gpio_pin_get(handle, pin->g_pin);
161		printf("pin %02d:\t%d\t%s", pin->g_pin, pinv,
162		    pin->g_name);
163
164		print_caps(pin->g_flags);
165
166		if (verbose) {
167			printf(", caps:");
168			print_caps(pin->g_caps);
169		}
170		printf("\n");
171	}
172	free(cfgs);
173}
174
175static int
176get_pinnum_by_name(gpio_handle_t handle, const char *name) {
177	int i, maxpin, pinn;
178	gpio_config_t *cfgs;
179	gpio_config_t *pin;
180
181	pinn = -1;
182	maxpin = gpio_pin_list(handle, &cfgs);
183	if (maxpin < 0) {
184		perror("gpio_pin_list");
185		exit(1);
186	}
187
188	for (i = 0; i <= maxpin; i++) {
189		pin = cfgs + i;
190		gpio_pin_get(handle, pin->g_pin);
191		if (!strcmp(name, pin->g_name)) {
192			pinn = i;
193			break;
194		}
195	}
196	free(cfgs);
197
198	return pinn;
199}
200
201static void
202fail(const char *fmt, ...)
203{
204	va_list ap;
205
206	va_start(ap, fmt);
207	vfprintf(stderr, fmt, ap);
208	va_end(ap);
209	exit(1);
210}
211
212int
213main(int argc, char **argv)
214{
215	int i;
216	gpio_config_t pin;
217	gpio_handle_t handle;
218	char *ctlfile = NULL;
219	int pinn, pinv, pin_type, ch;
220	int flags, flag, ok;
221	int config, list, name, toggle, verbose;
222
223	config = toggle = verbose = list = name = pin_type = 0;
224
225	while ((ch = getopt(argc, argv, "cf:lntvNp")) != -1) {
226		switch (ch) {
227		case 'c':
228			config = 1;
229			break;
230		case 'f':
231			ctlfile = optarg;
232			break;
233		case 'l':
234			list = 1;
235			break;
236		case 'n':
237			name = 1;
238			break;
239		case 'N':
240			pin_type = PIN_TYPE_NAME;
241			break;
242		case'p':
243			pin_type = PIN_TYPE_NUMBER;
244			break;
245		case 't':
246			toggle = 1;
247			break;
248		case 'v':
249			verbose = 1;
250			break;
251		default:
252			usage();
253			break;
254		}
255	}
256	argv += optind;
257	argc -= optind;
258	if (ctlfile == NULL)
259		handle = gpio_open(0);
260	else
261		handle = gpio_open_device(ctlfile);
262	if (handle == GPIO_INVALID_HANDLE) {
263		perror("gpio_open");
264		exit(1);
265	}
266
267	if (list) {
268		dump_pins(handle, verbose);
269		gpio_close(handle);
270		exit(0);
271	}
272
273	if (argc == 0)
274		usage();
275
276	/* Find the pin number by the name */
277	switch (pin_type) {
278	default:
279		/* First test if it is a pin number */
280		pinn = str2int(argv[0], &ok);
281		if (ok) {
282			/* Test if we have any pin named by this number and tell the user */
283			if (get_pinnum_by_name(handle, argv[0]) != -1)
284				fail("%s is also a pin name, use -p or -N\n", argv[0]);
285		} else {
286			/* Test if it is a name */
287			if ((pinn = get_pinnum_by_name(handle, argv[0])) == -1)
288				fail("Can't find pin named \"%s\"\n", argv[0]);
289		}
290		break;
291	case PIN_TYPE_NUMBER:
292		pinn = str2int(argv[0], &ok);
293		if (!ok)
294			fail("Invalid pin number: %s\n", argv[0]);
295		break;
296	case PIN_TYPE_NAME:
297		if ((pinn = get_pinnum_by_name(handle, argv[0])) == -1)
298			fail("Can't find pin named \"%s\"\n", argv[0]);
299		break;
300	}
301
302	/* Set the pin name. */
303	if (name) {
304		if (argc != 2)
305			usage();
306		if (gpio_pin_set_name(handle, pinn, argv[1]) < 0) {
307			perror("gpio_pin_set_name");
308			exit(1);
309		}
310		exit(0);
311	}
312
313	if (toggle) {
314		/*
315                * -t pin assumes no additional arguments
316                */
317		if (argc > 1)
318			usage();
319		if (gpio_pin_toggle(handle, pinn) < 0) {
320			perror("gpio_pin_toggle");
321			exit(1);
322		}
323		gpio_close(handle);
324		exit(0);
325	}
326
327	if (config) {
328		flags = 0;
329		for (i = 1; i < argc; i++) {
330			flag = 	str2cap(argv[i]);
331			if (flag < 0)
332				fail("Invalid flag: %s\n", argv[i]);
333			else if ((flag & GPIO_INTR_MASK) != 0)
334				fail("Interrupt capability %s cannot be set as configuration flag\n", argv[i]);
335			flags |= flag;
336		}
337		pin.g_pin = pinn;
338		pin.g_flags = flags;
339		if (gpio_pin_set_flags(handle, &pin) < 0) {
340			perror("gpio_pin_set_flags");
341			exit(1);
342		}
343		exit(0);
344	}
345
346	/*
347	 * Last two cases - set value or print value
348	 */
349	if ((argc == 0) || (argc > 2))
350		usage();
351
352	/*
353	 * Read pin value
354	 */
355	if (argc == 1) {
356		pinv = gpio_pin_get(handle, pinn);
357		if (pinv < 0) {
358			perror("gpio_pin_get");
359			exit(1);
360		}
361		printf("%d\n", pinv);
362		exit(0);
363	}
364
365	/* Is it valid number (0 or 1) ? */
366	pinv = str2int(argv[1], &ok);
367	if (ok == 0 || ((pinv != 0) && (pinv != 1)))
368		fail("Invalid pin value: %s\n", argv[1]);
369
370	/*
371	 * Set pin value
372	 */
373	if (gpio_pin_set(handle, pinn, pinv) < 0) {
374		perror("gpio_pin_set");
375		exit(1);
376	}
377
378	gpio_close(handle);
379	exit(0);
380}
381