xref: /illumos-gate/usr/src/cmd/gpioadm/gpioadm.c (revision fd71220ba0fafcc9cf5ea0785db206f3f31336e7)
1*fd71220bSRobert Mustacchi /*
2*fd71220bSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*fd71220bSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*fd71220bSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*fd71220bSRobert Mustacchi  * 1.0 of the CDDL.
6*fd71220bSRobert Mustacchi  *
7*fd71220bSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*fd71220bSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*fd71220bSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*fd71220bSRobert Mustacchi  */
11*fd71220bSRobert Mustacchi 
12*fd71220bSRobert Mustacchi /*
13*fd71220bSRobert Mustacchi  * Copyright 2022 Oxide Computer Company
14*fd71220bSRobert Mustacchi  */
15*fd71220bSRobert Mustacchi 
16*fd71220bSRobert Mustacchi /*
17*fd71220bSRobert Mustacchi  * This command implements the basics of administering general purpose and
18*fd71220bSRobert Mustacchi  * dedicated purpose I/O (GPIO and DPIO).
19*fd71220bSRobert Mustacchi  */
20*fd71220bSRobert Mustacchi 
21*fd71220bSRobert Mustacchi #include <string.h>
22*fd71220bSRobert Mustacchi #include <err.h>
23*fd71220bSRobert Mustacchi #include <stdlib.h>
24*fd71220bSRobert Mustacchi #include <stdarg.h>
25*fd71220bSRobert Mustacchi #include <unistd.h>
26*fd71220bSRobert Mustacchi #include <ofmt.h>
27*fd71220bSRobert Mustacchi #include <libdevinfo.h>
28*fd71220bSRobert Mustacchi #include <sys/types.h>
29*fd71220bSRobert Mustacchi #include <sys/stat.h>
30*fd71220bSRobert Mustacchi #include <fcntl.h>
31*fd71220bSRobert Mustacchi #include <strings.h>
32*fd71220bSRobert Mustacchi #include <sys/debug.h>
33*fd71220bSRobert Mustacchi #include <libgen.h>
34*fd71220bSRobert Mustacchi 
35*fd71220bSRobert Mustacchi #include "gpioadm.h"
36*fd71220bSRobert Mustacchi 
37*fd71220bSRobert Mustacchi gpioadm_t gpioadm;
38*fd71220bSRobert Mustacchi 
39*fd71220bSRobert Mustacchi static void
gpioadm_vwarn(const char * fmt,va_list ap)40*fd71220bSRobert Mustacchi gpioadm_vwarn(const char *fmt, va_list ap)
41*fd71220bSRobert Mustacchi {
42*fd71220bSRobert Mustacchi 	xpio_t *xpio = gpioadm.gpio_xpio;
43*fd71220bSRobert Mustacchi 
44*fd71220bSRobert Mustacchi 	(void) fprintf(stderr, "%s: ", gpioadm.gpio_progname);
45*fd71220bSRobert Mustacchi 	(void) vfprintf(stderr, fmt, ap);
46*fd71220bSRobert Mustacchi 	(void) fprintf(stderr, ": %s: %s (libxpio: 0x%x, sys: %u)\n",
47*fd71220bSRobert Mustacchi 	    xpio_errmsg(xpio), xpio_err2str(xpio, xpio_err(xpio)),
48*fd71220bSRobert Mustacchi 	    xpio_err(xpio), xpio_syserr(xpio));
49*fd71220bSRobert Mustacchi }
50*fd71220bSRobert Mustacchi 
51*fd71220bSRobert Mustacchi void
gpioadm_warn(const char * fmt,...)52*fd71220bSRobert Mustacchi gpioadm_warn(const char *fmt, ...)
53*fd71220bSRobert Mustacchi {
54*fd71220bSRobert Mustacchi 	va_list ap;
55*fd71220bSRobert Mustacchi 
56*fd71220bSRobert Mustacchi 	va_start(ap, fmt);
57*fd71220bSRobert Mustacchi 	gpioadm_vwarn(fmt, ap);
58*fd71220bSRobert Mustacchi 	va_end(ap);
59*fd71220bSRobert Mustacchi }
60*fd71220bSRobert Mustacchi 
61*fd71220bSRobert Mustacchi void __NORETURN
gpioadm_fatal(const char * fmt,...)62*fd71220bSRobert Mustacchi gpioadm_fatal(const char *fmt, ...)
63*fd71220bSRobert Mustacchi {
64*fd71220bSRobert Mustacchi 	va_list ap;
65*fd71220bSRobert Mustacchi 
66*fd71220bSRobert Mustacchi 	va_start(ap, fmt);
67*fd71220bSRobert Mustacchi 	gpioadm_vwarn(fmt, ap);
68*fd71220bSRobert Mustacchi 	va_end(ap);
69*fd71220bSRobert Mustacchi 
70*fd71220bSRobert Mustacchi 	exit(EXIT_FAILURE);
71*fd71220bSRobert Mustacchi }
72*fd71220bSRobert Mustacchi 
73*fd71220bSRobert Mustacchi void __NORETURN
gpioadm_update_fatal(xpio_gpio_update_t * update,const char * fmt,...)74*fd71220bSRobert Mustacchi gpioadm_update_fatal(xpio_gpio_update_t *update, const char *fmt, ...)
75*fd71220bSRobert Mustacchi {
76*fd71220bSRobert Mustacchi 	va_list ap;
77*fd71220bSRobert Mustacchi 
78*fd71220bSRobert Mustacchi 	va_start(ap, fmt);
79*fd71220bSRobert Mustacchi 	(void) fprintf(stderr, "%s: ", gpioadm.gpio_progname);
80*fd71220bSRobert Mustacchi 	(void) vfprintf(stderr, fmt, ap);
81*fd71220bSRobert Mustacchi 	(void) fprintf(stderr, ": %s: %s (libxpio: 0x%x, sys: %u)\n",
82*fd71220bSRobert Mustacchi 	    xpio_update_errmsg(update),
83*fd71220bSRobert Mustacchi 	    xpio_update_err2str(update, xpio_update_err(update)),
84*fd71220bSRobert Mustacchi 	    xpio_update_err(update), xpio_update_syserr(update));
85*fd71220bSRobert Mustacchi 	va_end(ap);
86*fd71220bSRobert Mustacchi 
87*fd71220bSRobert Mustacchi 	exit(EXIT_FAILURE);
88*fd71220bSRobert Mustacchi }
89*fd71220bSRobert Mustacchi void
gpioadm_ofmt_errx(const char * fmt,...)90*fd71220bSRobert Mustacchi gpioadm_ofmt_errx(const char *fmt, ...)
91*fd71220bSRobert Mustacchi {
92*fd71220bSRobert Mustacchi 	va_list ap;
93*fd71220bSRobert Mustacchi 
94*fd71220bSRobert Mustacchi 	va_start(ap, fmt);
95*fd71220bSRobert Mustacchi 	verrx(EXIT_FAILURE, fmt, ap);
96*fd71220bSRobert Mustacchi }
97*fd71220bSRobert Mustacchi 
98*fd71220bSRobert Mustacchi void
gpioadm_ctrl_gpio_init(const char * target,xpio_ctrl_t ** ctrlp,xpio_gpio_info_t ** gpiop)99*fd71220bSRobert Mustacchi gpioadm_ctrl_gpio_init(const char *target, xpio_ctrl_t **ctrlp,
100*fd71220bSRobert Mustacchi     xpio_gpio_info_t **gpiop)
101*fd71220bSRobert Mustacchi {
102*fd71220bSRobert Mustacchi 	char *eptr, *slash, *dup;
103*fd71220bSRobert Mustacchi 	const char *ctrl_name, *gpio_name;
104*fd71220bSRobert Mustacchi 	uint32_t gpio_num;
105*fd71220bSRobert Mustacchi 	xpio_ctrl_t *ctrl;
106*fd71220bSRobert Mustacchi 	xpio_gpio_info_t *gpio;
107*fd71220bSRobert Mustacchi 
108*fd71220bSRobert Mustacchi 	dup = strdup(target);
109*fd71220bSRobert Mustacchi 	if (dup == NULL) {
110*fd71220bSRobert Mustacchi 		err(EXIT_FAILURE, "failed to allocate memory for target string "
111*fd71220bSRobert Mustacchi 		    "processing");
112*fd71220bSRobert Mustacchi 	}
113*fd71220bSRobert Mustacchi 
114*fd71220bSRobert Mustacchi 	slash = strchr(dup, '/');
115*fd71220bSRobert Mustacchi 	if (slash == NULL) {
116*fd71220bSRobert Mustacchi 		errx(EXIT_FAILURE, "invalid target: %s, missing '/' delimiter "
117*fd71220bSRobert Mustacchi 		    "to separate controller and gpio", target);
118*fd71220bSRobert Mustacchi 	}
119*fd71220bSRobert Mustacchi 	ctrl_name = dup;
120*fd71220bSRobert Mustacchi 	gpio_name = slash + 1;
121*fd71220bSRobert Mustacchi 	*slash = '\0';
122*fd71220bSRobert Mustacchi 
123*fd71220bSRobert Mustacchi 	if (!xpio_ctrl_init_by_name(gpioadm.gpio_xpio, ctrl_name, &ctrl)) {
124*fd71220bSRobert Mustacchi 		gpioadm_fatal("failed to initialize gpio controller %s",
125*fd71220bSRobert Mustacchi 		    ctrl_name);
126*fd71220bSRobert Mustacchi 	}
127*fd71220bSRobert Mustacchi 
128*fd71220bSRobert Mustacchi 	/*
129*fd71220bSRobert Mustacchi 	 * We always attempt to look up and translate the name to a GPIO ID
130*fd71220bSRobert Mustacchi 	 * first. This way if a controller does something like just name the
131*fd71220bSRobert Mustacchi 	 * pins 1, 2, 3, 4, etc. but has a weird relationship to the IDs, we're
132*fd71220bSRobert Mustacchi 	 * more likely to get what the user intended (hopefully).
133*fd71220bSRobert Mustacchi 	 */
134*fd71220bSRobert Mustacchi 	if (!xpio_gpio_lookup_id(ctrl, gpio_name, &gpio_num)) {
135*fd71220bSRobert Mustacchi 		long long l;
136*fd71220bSRobert Mustacchi 
137*fd71220bSRobert Mustacchi 		if (xpio_err(gpioadm.gpio_xpio) != XPIO_ERR_NO_LOOKUP_MATCH) {
138*fd71220bSRobert Mustacchi 			gpioadm_fatal("failed to look up name %s on "
139*fd71220bSRobert Mustacchi 			    "controller %s", ctrl_name, gpio_name);
140*fd71220bSRobert Mustacchi 		}
141*fd71220bSRobert Mustacchi 
142*fd71220bSRobert Mustacchi 		/*
143*fd71220bSRobert Mustacchi 		 * At this point, attempt to parse it as an intger.
144*fd71220bSRobert Mustacchi 		 */
145*fd71220bSRobert Mustacchi 		errno = 0;
146*fd71220bSRobert Mustacchi 		l = strtoll(gpio_name, &eptr, 0);
147*fd71220bSRobert Mustacchi 		if (errno != 0 || *eptr != '\0') {
148*fd71220bSRobert Mustacchi 			errx(EXIT_FAILURE, "failed to parse gpio number: %s",
149*fd71220bSRobert Mustacchi 			    gpio_name);
150*fd71220bSRobert Mustacchi 		}
151*fd71220bSRobert Mustacchi 
152*fd71220bSRobert Mustacchi 		if (l < 0 || l > UINT32_MAX) {
153*fd71220bSRobert Mustacchi 			errx(EXIT_FAILURE, "gpio number is outside of valid "
154*fd71220bSRobert Mustacchi 			    "range: %s", gpio_name);
155*fd71220bSRobert Mustacchi 		}
156*fd71220bSRobert Mustacchi 		gpio_num = (uint32_t)l;
157*fd71220bSRobert Mustacchi 	}
158*fd71220bSRobert Mustacchi 
159*fd71220bSRobert Mustacchi 	if (!xpio_gpio_info(ctrl, gpio_num, &gpio)) {
160*fd71220bSRobert Mustacchi 		gpioadm_fatal("failed to get gpio %u on controller %s",
161*fd71220bSRobert Mustacchi 		    gpio_num, ctrl_name);
162*fd71220bSRobert Mustacchi 	}
163*fd71220bSRobert Mustacchi 
164*fd71220bSRobert Mustacchi 	*gpiop = gpio;
165*fd71220bSRobert Mustacchi 	*ctrlp = ctrl;
166*fd71220bSRobert Mustacchi 	free(dup);
167*fd71220bSRobert Mustacchi }
168*fd71220bSRobert Mustacchi 
169*fd71220bSRobert Mustacchi void
gpioadm_walk_usage(const gpioadm_cmdtab_t * tab,FILE * f)170*fd71220bSRobert Mustacchi gpioadm_walk_usage(const gpioadm_cmdtab_t *tab, FILE *f)
171*fd71220bSRobert Mustacchi {
172*fd71220bSRobert Mustacchi 	for (; tab->gpct_name != NULL; tab++) {
173*fd71220bSRobert Mustacchi 		tab->gpct_use(f);
174*fd71220bSRobert Mustacchi 	}
175*fd71220bSRobert Mustacchi }
176*fd71220bSRobert Mustacchi 
177*fd71220bSRobert Mustacchi static void
gpioadm_usage(const gpioadm_cmdtab_t * tab,const char * format,...)178*fd71220bSRobert Mustacchi gpioadm_usage(const gpioadm_cmdtab_t *tab, const char *format, ...)
179*fd71220bSRobert Mustacchi {
180*fd71220bSRobert Mustacchi 	if (format != NULL) {
181*fd71220bSRobert Mustacchi 		va_list ap;
182*fd71220bSRobert Mustacchi 
183*fd71220bSRobert Mustacchi 		va_start(ap, format);
184*fd71220bSRobert Mustacchi 		vwarnx(format, ap);
185*fd71220bSRobert Mustacchi 		va_end(ap);
186*fd71220bSRobert Mustacchi 	}
187*fd71220bSRobert Mustacchi 
188*fd71220bSRobert Mustacchi 	if (tab == NULL)
189*fd71220bSRobert Mustacchi 		return;
190*fd71220bSRobert Mustacchi 
191*fd71220bSRobert Mustacchi 	fprintf(stderr, "Usage: gpioadm <subcommand> <args> ... \n\n");
192*fd71220bSRobert Mustacchi 	gpioadm_walk_usage(tab, stderr);
193*fd71220bSRobert Mustacchi }
194*fd71220bSRobert Mustacchi 
195*fd71220bSRobert Mustacchi int
gpioadm_walk_tab(const gpioadm_cmdtab_t * tab,int argc,char * argv[])196*fd71220bSRobert Mustacchi gpioadm_walk_tab(const gpioadm_cmdtab_t *tab, int argc, char *argv[])
197*fd71220bSRobert Mustacchi {
198*fd71220bSRobert Mustacchi 	uint32_t cmd;
199*fd71220bSRobert Mustacchi 
200*fd71220bSRobert Mustacchi 	if (argc == 0) {
201*fd71220bSRobert Mustacchi 		gpioadm_usage(tab, "missing required sub-command");
202*fd71220bSRobert Mustacchi 		return (EXIT_FAILURE);
203*fd71220bSRobert Mustacchi 	}
204*fd71220bSRobert Mustacchi 
205*fd71220bSRobert Mustacchi 	for (cmd = 0; tab[cmd].gpct_name != NULL; cmd++) {
206*fd71220bSRobert Mustacchi 		if (strcmp(argv[0], tab[cmd].gpct_name) == 0) {
207*fd71220bSRobert Mustacchi 			break;
208*fd71220bSRobert Mustacchi 		}
209*fd71220bSRobert Mustacchi 	}
210*fd71220bSRobert Mustacchi 
211*fd71220bSRobert Mustacchi 	if (tab[cmd].gpct_name == NULL) {
212*fd71220bSRobert Mustacchi 		gpioadm_usage(tab, "unknown subcommand %s", argv[0]);
213*fd71220bSRobert Mustacchi 		return (EXIT_USAGE);
214*fd71220bSRobert Mustacchi 	}
215*fd71220bSRobert Mustacchi 
216*fd71220bSRobert Mustacchi 	argc--;
217*fd71220bSRobert Mustacchi 	argv++;
218*fd71220bSRobert Mustacchi 	optind = 0;
219*fd71220bSRobert Mustacchi 
220*fd71220bSRobert Mustacchi 	return (tab[cmd].gpct_func(argc, argv));
221*fd71220bSRobert Mustacchi }
222*fd71220bSRobert Mustacchi 
223*fd71220bSRobert Mustacchi static const gpioadm_cmdtab_t gpioadm_cmds[] = {
224*fd71220bSRobert Mustacchi 	{ "controller", gpioadm_controller, gpioadm_controller_usage },
225*fd71220bSRobert Mustacchi 	{ "dpio", gpioadm_dpio, gpioadm_dpio_usage },
226*fd71220bSRobert Mustacchi 	{ "gpio", gpioadm_gpio, gpioadm_gpio_usage },
227*fd71220bSRobert Mustacchi 	{ NULL, NULL, NULL }
228*fd71220bSRobert Mustacchi };
229*fd71220bSRobert Mustacchi 
230*fd71220bSRobert Mustacchi int
main(int argc,char * argv[])231*fd71220bSRobert Mustacchi main(int argc, char *argv[])
232*fd71220bSRobert Mustacchi {
233*fd71220bSRobert Mustacchi 	gpioadm.gpio_progname = basename(argv[0]);
234*fd71220bSRobert Mustacchi 
235*fd71220bSRobert Mustacchi 	if (argc < 2) {
236*fd71220bSRobert Mustacchi 		gpioadm_usage(gpioadm_cmds, "missing required sub-command");
237*fd71220bSRobert Mustacchi 		exit(EXIT_USAGE);
238*fd71220bSRobert Mustacchi 	}
239*fd71220bSRobert Mustacchi 
240*fd71220bSRobert Mustacchi 	argc--;
241*fd71220bSRobert Mustacchi 	argv++;
242*fd71220bSRobert Mustacchi 	gpioadm.gpio_xpio = xpio_init();
243*fd71220bSRobert Mustacchi 
244*fd71220bSRobert Mustacchi 	if (gpioadm.gpio_xpio == NULL) {
245*fd71220bSRobert Mustacchi 		err(EXIT_FAILURE, "failed to initialize libxpio");
246*fd71220bSRobert Mustacchi 	}
247*fd71220bSRobert Mustacchi 
248*fd71220bSRobert Mustacchi 	return (gpioadm_walk_tab(gpioadm_cmds, argc, argv));
249*fd71220bSRobert Mustacchi }
250