1213238Sgonzo/*-
2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330449Seadler *
4213238Sgonzo * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
5275395Srpaulo * Copyright (c) 2014, Rui Paulo <rpaulo@FreeBSD.org>
6296682Sgonzo * Copyright (c) 2015, Emmanuel Vadot <manu@bidouilliste.com>
7213238Sgonzo * All rights reserved.
8213238Sgonzo *
9213238Sgonzo * Redistribution and use in source and binary forms, with or without
10213238Sgonzo * modification, are permitted provided that the following conditions
11213238Sgonzo * are met:
12213238Sgonzo * 1. Redistributions of source code must retain the above copyright
13213238Sgonzo *    notice unmodified, this list of conditions, and the following
14213238Sgonzo *    disclaimer.
15213238Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
16213238Sgonzo *    notice, this list of conditions and the following disclaimer in the
17213238Sgonzo *    documentation and/or other materials provided with the distribution.
18213238Sgonzo *
19213238Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20213238Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21213238Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22213238Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23213238Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24213238Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25213238Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26213238Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27213238Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28213238Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29213238Sgonzo * SUCH DAMAGE.
30213238Sgonzo */
31213238Sgonzo
32213238Sgonzo#include <sys/cdefs.h>
33213238Sgonzo__FBSDID("$FreeBSD: stable/11/usr.sbin/gpioctl/gpioctl.c 330449 2018-03-05 07:26:05Z eadler $");
34213238Sgonzo
35213238Sgonzo#include <fcntl.h>
36213238Sgonzo#include <getopt.h>
37255629Ssbruno#include <paths.h>
38213238Sgonzo#include <stdio.h>
39213238Sgonzo#include <stdarg.h>
40213238Sgonzo#include <stdlib.h>
41213238Sgonzo#include <string.h>
42213238Sgonzo#include <unistd.h>
43213238Sgonzo
44275395Srpaulo#include <libgpio.h>
45213238Sgonzo
46296682Sgonzo#define PIN_TYPE_NUMBER		1
47296682Sgonzo#define PIN_TYPE_NAME		2
48296682Sgonzo
49213238Sgonzostruct flag_desc {
50213238Sgonzo	const char *name;
51213238Sgonzo	uint32_t flag;
52213238Sgonzo};
53213238Sgonzo
54241737Sedstatic struct flag_desc gpio_flags[] = {
55213238Sgonzo	{ "IN", GPIO_PIN_INPUT },
56213238Sgonzo	{ "OUT", GPIO_PIN_OUTPUT },
57213238Sgonzo	{ "OD", GPIO_PIN_OPENDRAIN },
58213238Sgonzo	{ "PP", GPIO_PIN_PUSHPULL },
59213238Sgonzo	{ "TS", GPIO_PIN_TRISTATE },
60213238Sgonzo	{ "PU", GPIO_PIN_PULLUP },
61213238Sgonzo	{ "PD", GPIO_PIN_PULLDOWN },
62213238Sgonzo	{ "II", GPIO_PIN_INVIN },
63213238Sgonzo	{ "IO", GPIO_PIN_INVOUT },
64213238Sgonzo	{ "PULSE", GPIO_PIN_PULSATE },
65213238Sgonzo	{ NULL, 0 },
66213238Sgonzo};
67213238Sgonzo
68213238Sgonzoint str2cap(const char *str);
69213238Sgonzo
70213238Sgonzostatic void
71213238Sgonzousage(void)
72213238Sgonzo{
73213238Sgonzo	fprintf(stderr, "Usage:\n");
74255629Ssbruno	fprintf(stderr, "\tgpioctl [-f ctldev] -l [-v]\n");
75296682Sgonzo	fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] -t pin\n");
76296682Sgonzo	fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] -c pin flag ...\n");
77296682Sgonzo	fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] -n pin pin-name\n");
78296682Sgonzo	fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] pin [0|1]\n");
79213238Sgonzo	exit(1);
80213238Sgonzo}
81213238Sgonzo
82213238Sgonzostatic const char *
83213238Sgonzocap2str(uint32_t cap)
84213238Sgonzo{
85213238Sgonzo	struct flag_desc * pdesc = gpio_flags;
86213238Sgonzo	while (pdesc->name) {
87213238Sgonzo		if (pdesc->flag == cap)
88213238Sgonzo			return pdesc->name;
89213238Sgonzo		pdesc++;
90213238Sgonzo	}
91213238Sgonzo
92213238Sgonzo	return "UNKNOWN";
93213238Sgonzo}
94213238Sgonzo
95213238Sgonzoint
96213238Sgonzostr2cap(const char *str)
97213238Sgonzo{
98213238Sgonzo	struct flag_desc * pdesc = gpio_flags;
99213238Sgonzo	while (pdesc->name) {
100213238Sgonzo		if (strcasecmp(str, pdesc->name) == 0)
101213238Sgonzo			return pdesc->flag;
102213238Sgonzo		pdesc++;
103213238Sgonzo	}
104213238Sgonzo
105213238Sgonzo	return (-1);
106213238Sgonzo}
107213238Sgonzo
108213238Sgonzo/*
109213238Sgonzo * Our handmade function for converting string to number
110213238Sgonzo */
111275395Srpaulostatic int
112213238Sgonzostr2int(const char *s, int *ok)
113213238Sgonzo{
114213238Sgonzo	char *endptr;
115213238Sgonzo	int res = strtod(s, &endptr);
116213238Sgonzo	if (endptr != s + strlen(s) )
117213238Sgonzo		*ok = 0;
118213238Sgonzo	else
119213238Sgonzo		*ok = 1;
120213238Sgonzo
121213238Sgonzo	return res;
122213238Sgonzo}
123213238Sgonzo
124213238Sgonzostatic void
125213238Sgonzoprint_caps(int caps)
126213238Sgonzo{
127213238Sgonzo	int i, need_coma;
128213238Sgonzo
129213238Sgonzo	need_coma = 0;
130213238Sgonzo	printf("<");
131213238Sgonzo	for (i = 0; i < 32; i++) {
132213238Sgonzo		if (caps & (1 << i)) {
133213238Sgonzo			if (need_coma)
134213238Sgonzo				printf(",");
135213238Sgonzo			printf("%s", cap2str(1 << i));
136213238Sgonzo			need_coma = 1;
137213238Sgonzo		}
138213238Sgonzo	}
139213238Sgonzo	printf(">");
140213238Sgonzo}
141213238Sgonzo
142213238Sgonzostatic void
143275395Srpaulodump_pins(gpio_handle_t handle, int verbose)
144213238Sgonzo{
145275395Srpaulo	int i, maxpin, pinv;
146275395Srpaulo	gpio_config_t *cfgs;
147275395Srpaulo	gpio_config_t *pin;
148213238Sgonzo
149275395Srpaulo	maxpin = gpio_pin_list(handle, &cfgs);
150275395Srpaulo	if (maxpin < 0) {
151275395Srpaulo		perror("gpio_pin_list");
152213238Sgonzo		exit(1);
153213238Sgonzo	}
154213238Sgonzo
155213238Sgonzo	for (i = 0; i <= maxpin; i++) {
156275395Srpaulo		pin = cfgs + i;
157275395Srpaulo		pinv = gpio_pin_get(handle, pin->g_pin);
158275395Srpaulo		printf("pin %02d:\t%d\t%s", pin->g_pin, pinv,
159275395Srpaulo		    pin->g_name);
160213238Sgonzo
161275395Srpaulo		print_caps(pin->g_flags);
162213238Sgonzo
163213238Sgonzo		if (verbose) {
164213238Sgonzo			printf(", caps:");
165275395Srpaulo			print_caps(pin->g_caps);
166213238Sgonzo		}
167213238Sgonzo		printf("\n");
168213238Sgonzo	}
169276481Sloos	free(cfgs);
170213238Sgonzo}
171213238Sgonzo
172296682Sgonzostatic int
173296682Sgonzoget_pinnum_by_name(gpio_handle_t handle, const char *name) {
174296682Sgonzo	int i, maxpin, pinn;
175296682Sgonzo	gpio_config_t *cfgs;
176296682Sgonzo	gpio_config_t *pin;
177296682Sgonzo
178296682Sgonzo	pinn = -1;
179296682Sgonzo	maxpin = gpio_pin_list(handle, &cfgs);
180296682Sgonzo	if (maxpin < 0) {
181296682Sgonzo		perror("gpio_pin_list");
182296682Sgonzo		exit(1);
183296682Sgonzo	}
184296682Sgonzo
185296682Sgonzo	for (i = 0; i <= maxpin; i++) {
186296682Sgonzo		pin = cfgs + i;
187296682Sgonzo		gpio_pin_get(handle, pin->g_pin);
188296682Sgonzo		if (!strcmp(name, pin->g_name)) {
189296682Sgonzo			pinn = i;
190296682Sgonzo			break;
191296682Sgonzo		}
192296682Sgonzo	}
193296682Sgonzo	free(cfgs);
194296682Sgonzo
195296682Sgonzo	return pinn;
196296682Sgonzo}
197296682Sgonzo
198275395Srpaulostatic void
199213238Sgonzofail(const char *fmt, ...)
200213238Sgonzo{
201213238Sgonzo	va_list ap;
202213238Sgonzo
203213238Sgonzo	va_start(ap, fmt);
204213238Sgonzo	vfprintf(stderr, fmt, ap);
205213238Sgonzo	va_end(ap);
206213238Sgonzo	exit(1);
207213238Sgonzo}
208213238Sgonzo
209275395Srpauloint
210213238Sgonzomain(int argc, char **argv)
211213238Sgonzo{
212213238Sgonzo	int i;
213275395Srpaulo	gpio_config_t pin;
214275395Srpaulo	gpio_handle_t handle;
215213238Sgonzo	char *ctlfile = NULL;
216296682Sgonzo	int pinn, pinv, pin_type, ch;
217213238Sgonzo	int flags, flag, ok;
218279761Sloos	int config, list, name, toggle, verbose;
219213238Sgonzo
220296682Sgonzo	config = toggle = verbose = list = name = pin_type = 0;
221213238Sgonzo
222296682Sgonzo	while ((ch = getopt(argc, argv, "cf:lntvNp")) != -1) {
223213238Sgonzo		switch (ch) {
224213238Sgonzo		case 'c':
225213238Sgonzo			config = 1;
226213238Sgonzo			break;
227213238Sgonzo		case 'f':
228213238Sgonzo			ctlfile = optarg;
229213238Sgonzo			break;
230213238Sgonzo		case 'l':
231213238Sgonzo			list = 1;
232213238Sgonzo			break;
233279761Sloos		case 'n':
234279761Sloos			name = 1;
235279761Sloos			break;
236296682Sgonzo		case 'N':
237296682Sgonzo			pin_type = PIN_TYPE_NAME;
238296682Sgonzo			break;
239296682Sgonzo		case'p':
240296682Sgonzo			pin_type = PIN_TYPE_NUMBER;
241296682Sgonzo			break;
242213238Sgonzo		case 't':
243213238Sgonzo			toggle = 1;
244213238Sgonzo			break;
245213238Sgonzo		case 'v':
246213238Sgonzo			verbose = 1;
247213238Sgonzo			break;
248213238Sgonzo		default:
249213238Sgonzo			usage();
250213238Sgonzo			break;
251213238Sgonzo		}
252213238Sgonzo	}
253213238Sgonzo	argv += optind;
254213238Sgonzo	argc -= optind;
255213238Sgonzo	if (ctlfile == NULL)
256275395Srpaulo		handle = gpio_open(0);
257275395Srpaulo	else
258275395Srpaulo		handle = gpio_open_device(ctlfile);
259275395Srpaulo	if (handle == GPIO_INVALID_HANDLE) {
260275395Srpaulo		perror("gpio_open");
261213238Sgonzo		exit(1);
262213238Sgonzo	}
263213238Sgonzo
264296682Sgonzo	if (list) {
265296682Sgonzo		dump_pins(handle, verbose);
266296682Sgonzo		gpio_close(handle);
267296682Sgonzo		exit(0);
268296682Sgonzo	}
269296682Sgonzo
270296682Sgonzo	if (argc == 0)
271296682Sgonzo		usage();
272296682Sgonzo
273296682Sgonzo	/* Find the pin number by the name */
274296682Sgonzo	switch (pin_type) {
275296724Sdim	default:
276296682Sgonzo		/* First test if it is a pin number */
277296682Sgonzo		pinn = str2int(argv[0], &ok);
278296682Sgonzo		if (ok) {
279296682Sgonzo			/* Test if we have any pin named by this number and tell the user */
280296682Sgonzo			if (get_pinnum_by_name(handle, argv[0]) != -1)
281296682Sgonzo				fail("%s is also a pin name, use -p or -N\n", argv[0]);
282296682Sgonzo		} else {
283296682Sgonzo			/* Test if it is a name */
284296682Sgonzo			if ((pinn = get_pinnum_by_name(handle, argv[0])) == -1)
285296682Sgonzo				fail("Can't find pin named \"%s\"\n", argv[0]);
286296682Sgonzo		}
287296682Sgonzo		break;
288296682Sgonzo	case PIN_TYPE_NUMBER:
289296682Sgonzo		pinn = str2int(argv[0], &ok);
290296682Sgonzo		if (!ok)
291296682Sgonzo			fail("Invalid pin number: %s\n", argv[0]);
292296682Sgonzo		break;
293296682Sgonzo	case PIN_TYPE_NAME:
294296682Sgonzo		if ((pinn = get_pinnum_by_name(handle, argv[0])) == -1)
295296682Sgonzo			fail("Can't find pin named \"%s\"\n", argv[0]);
296296682Sgonzo		break;
297296682Sgonzo	}
298296682Sgonzo
299279761Sloos	/* Set the pin name. */
300279761Sloos	if (name) {
301296682Sgonzo		if (argc != 2)
302279761Sloos			usage();
303296682Sgonzo		if (gpio_pin_set_name(handle, pinn, argv[1]) < 0) {
304279761Sloos			perror("gpio_pin_set_name");
305279761Sloos			exit(1);
306279761Sloos		}
307279761Sloos		exit(0);
308279761Sloos	}
309279761Sloos
310213238Sgonzo	if (toggle) {
311213238Sgonzo		/*
312296682Sgonzo                * -t pin assumes no additional arguments
313296682Sgonzo                */
314296682Sgonzo		if (argc > 1)
315213238Sgonzo			usage();
316275395Srpaulo		if (gpio_pin_toggle(handle, pinn) < 0) {
317275395Srpaulo			perror("gpio_pin_toggle");
318213238Sgonzo			exit(1);
319213238Sgonzo		}
320275395Srpaulo		gpio_close(handle);
321275395Srpaulo		exit(0);
322213238Sgonzo	}
323213238Sgonzo
324213238Sgonzo	if (config) {
325213238Sgonzo		flags = 0;
326296682Sgonzo		for (i = 1; i < argc; i++) {
327213238Sgonzo			flag = 	str2cap(argv[i]);
328213238Sgonzo			if (flag < 0)
329213238Sgonzo				fail("Invalid flag: %s\n", argv[i]);
330213238Sgonzo			flags |= flag;
331213238Sgonzo		}
332275395Srpaulo		pin.g_pin = pinn;
333275395Srpaulo		pin.g_flags = flags;
334275395Srpaulo		if (gpio_pin_set_flags(handle, &pin) < 0) {
335275395Srpaulo			perror("gpio_pin_set_flags");
336213238Sgonzo			exit(1);
337213238Sgonzo		}
338213238Sgonzo		exit(0);
339213238Sgonzo	}
340213238Sgonzo
341213238Sgonzo	/*
342213238Sgonzo	 * Last two cases - set value or print value
343213238Sgonzo	 */
344296682Sgonzo	if ((argc == 0) || (argc > 2))
345213238Sgonzo		usage();
346213238Sgonzo
347213238Sgonzo	/*
348213238Sgonzo	 * Read pin value
349213238Sgonzo	 */
350213238Sgonzo	if (argc == 1) {
351275395Srpaulo		pinv = gpio_pin_get(handle, pinn);
352275395Srpaulo		if (pinv < 0) {
353275395Srpaulo			perror("gpio_pin_get");
354213238Sgonzo			exit(1);
355213238Sgonzo		}
356275395Srpaulo		printf("%d\n", pinv);
357275395Srpaulo		exit(0);
358213238Sgonzo	}
359213238Sgonzo
360213238Sgonzo	/* Is it valid number (0 or 1) ? */
361213238Sgonzo	pinv = str2int(argv[1], &ok);
362296682Sgonzo	if (ok == 0 || ((pinv != 0) && (pinv != 1)))
363213238Sgonzo		fail("Invalid pin value: %s\n", argv[1]);
364213238Sgonzo
365213238Sgonzo	/*
366213238Sgonzo	 * Set pin value
367213238Sgonzo	 */
368275395Srpaulo	if (gpio_pin_set(handle, pinn, pinv) < 0) {
369275395Srpaulo		perror("gpio_pin_set");
370213238Sgonzo		exit(1);
371213238Sgonzo	}
372213238Sgonzo
373275395Srpaulo	gpio_close(handle);
374213238Sgonzo	exit(0);
375213238Sgonzo}
376