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