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