xref: /illumos-gate/usr/src/cmd/gpioadm/gpioadm_controller.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 #include <string.h>
17 #include <err.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <ofmt.h>
21 #include <libdevinfo.h>
22 #include <strings.h>
23 #include <sys/debug.h>
24 
25 #include "gpioadm.h"
26 
27 static void
gpioadm_controller_list_usage(FILE * f)28 gpioadm_controller_list_usage(FILE *f)
29 {
30 	(void) fprintf(f, "\tgpioadm controller list [-H] [-o field[,...] "
31 	    "[-p]] [filter...]\n");
32 }
33 
34 static void __PRINTFLIKE(1)
gpioadm_controller_list_help(const char * fmt,...)35 gpioadm_controller_list_help(const char *fmt, ...)
36 {
37 	if (fmt != NULL) {
38 		va_list ap;
39 
40 		va_start(ap, fmt);
41 		vwarnx(fmt, ap);
42 		va_end(ap);
43 	}
44 
45 	(void) fprintf(stderr, "Usage:  gpioadm controller list [-H] [-o "
46 	    "field[,...] [-p]] [filter...]\n");
47 	(void) fprintf(stderr, "\nList GPIO controllers in the system and "
48 	    "associated information.\n\n"
49 	    "\t-H\t\tomit the column header\n"
50 	    "\t-o field\toutput fields to print\n"
51 	    "\t-p\t\tparsable output (requires -o)\n\n"
52 	    "The following fields are supported:\n"
53 	    "\tcontroller\tthe name of the controller\n"
54 	    "\tngpios\t\tthe number of GPIOs the controller has\n"
55 	    "\tndpios\t\tthe number of DPIOs the controller has\n"
56 	    "\tpath\t\tthe path to the minor node of the controller\n"
57 	    "\tprovider\tthe /devices path of the provider\n\n"
58 	    "Filters restrict output to the named controllers. Each filter is "
59 	    "treated\nlike an OR allowing one to limit output to specific "
60 	    "controllers. It is\nan error if a controller isn't found.\n");
61 }
62 
63 typedef enum gpioadm_controller_list_otype {
64 	GPIOADM_CTRL_LIST_CTRLR,
65 	GPIOADM_CTRL_LIST_NGPIO,
66 	GPIOADM_CTRL_LIST_NDPIO,
67 	GPIOADM_CTRL_LIST_PATH,
68 	GPIOADM_CTRL_LIST_PROVIDER
69 } gpioadm_controllier_list_otype_t;
70 
71 typedef struct gpioadm_controller_list_ofmt {
72 	const char *gclo_minor;
73 	const char *gclo_devpath;
74 	char *gclo_path;
75 	uint32_t gclo_ngpio;
76 	uint32_t gclo_ndpio;
77 } gpioadm_controller_list_ofmt_t;
78 
79 static boolean_t
gpioadm_controller_list_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)80 gpioadm_controller_list_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
81 {
82 	gpioadm_controller_list_ofmt_t *gclo = ofarg->ofmt_cbarg;
83 
84 	switch (ofarg->ofmt_id) {
85 	case GPIOADM_CTRL_LIST_CTRLR:
86 		if (strlcpy(buf, gclo->gclo_minor, buflen) >= buflen) {
87 			return (B_FALSE);
88 		}
89 		break;
90 	case GPIOADM_CTRL_LIST_NGPIO:
91 		if (snprintf(buf, buflen, "%u", gclo->gclo_ngpio) >= buflen) {
92 			return (B_FALSE);
93 		}
94 		break;
95 	case GPIOADM_CTRL_LIST_NDPIO:
96 		if (snprintf(buf, buflen, "%u", gclo->gclo_ndpio) >= buflen) {
97 			return (B_FALSE);
98 		}
99 		break;
100 	case GPIOADM_CTRL_LIST_PROVIDER:
101 		if (strlcpy(buf, gclo->gclo_devpath, buflen) >= buflen) {
102 			return (B_FALSE);
103 		}
104 		break;
105 	case GPIOADM_CTRL_LIST_PATH:
106 		/*
107 		 * Asking for the minor path can fail. So if we have nothing,
108 		 * just ignore this.
109 		 */
110 		if (gclo->gclo_path == NULL)
111 			break;
112 		if (strlcpy(buf, gclo->gclo_path, buflen) >= buflen) {
113 			return (B_FALSE);
114 		}
115 		break;
116 	default:
117 		abort();
118 	}
119 	return (B_TRUE);
120 }
121 
122 static const char *gpioadm_controller_list_fields = "controller,ngpios,ndpios,"
123 	"provider";
124 static const ofmt_field_t gpioadm_controller_list_ofmt[] = {
125 	{ "CONTROLLER", 16, GPIOADM_CTRL_LIST_CTRLR,
126 	    gpioadm_controller_list_ofmt_cb },
127 	{ "NGPIOS", 8, GPIOADM_CTRL_LIST_NGPIO,
128 	    gpioadm_controller_list_ofmt_cb },
129 	{ "NDPIOS", 8, GPIOADM_CTRL_LIST_NDPIO,
130 	    gpioadm_controller_list_ofmt_cb },
131 	{ "PROVIDER", 42, GPIOADM_CTRL_LIST_PROVIDER,
132 	    gpioadm_controller_list_ofmt_cb },
133 	{ "PATH", 42, GPIOADM_CTRL_LIST_PATH,
134 	    gpioadm_controller_list_ofmt_cb },
135 	{ NULL, 0, 0, NULL }
136 };
137 
138 typedef struct {
139 	ofmt_handle_t gcl_ofmt;
140 	uint32_t gcl_nprint;
141 	bool gcl_err;
142 	int gcl_nfilts;
143 	char **gcl_filts;
144 	bool *gcl_used;
145 } gpioadm_controller_list_t;
146 
147 static bool
gpioadm_controller_list_cb(xpio_t * xpio,xpio_ctrl_disc_t * disc,void * arg)148 gpioadm_controller_list_cb(xpio_t *xpio, xpio_ctrl_disc_t *disc, void *arg)
149 {
150 	xpio_ctrl_t *ctrl;
151 	xpio_ctrl_info_t *info;
152 	gpioadm_controller_list_ofmt_t list;
153 	gpioadm_controller_list_t *gcl = arg;
154 	const char *mname = di_minor_name(disc->xcd_minor);
155 
156 	if (gcl->gcl_nfilts > 0) {
157 		bool match = false;
158 
159 		for (int i = 0; i < gcl->gcl_nfilts; i++) {
160 			if (strcmp(mname, gcl->gcl_filts[i]) == 0) {
161 				gcl->gcl_used[i] = true;
162 				match = true;
163 				break;
164 			}
165 		}
166 
167 		if (!match) {
168 			return (true);
169 		}
170 	}
171 
172 	if (!xpio_ctrl_init(xpio, disc->xcd_minor, &ctrl)) {
173 		gpioadm_warn("failed to initialize controller %s", mname);
174 		gcl->gcl_err = B_TRUE;
175 		return (true);
176 	}
177 
178 	if (!xpio_ctrl_info(ctrl, &info)) {
179 		gpioadm_warn("failed to get controller info for %s", mname);
180 		xpio_ctrl_fini(ctrl);
181 		gcl->gcl_err = B_TRUE;
182 		return (true);
183 	}
184 
185 	bzero(&list, sizeof (list));
186 	list.gclo_minor = mname;
187 	list.gclo_ngpio = xpio_ctrl_info_ngpios(info);
188 	list.gclo_ndpio = xpio_ctrl_info_ndpios(info);
189 	list.gclo_devpath = xpio_ctrl_info_devpath(info);
190 	list.gclo_path = di_devfs_minor_path(disc->xcd_minor);
191 
192 	ofmt_print(gcl->gcl_ofmt, &list);
193 	gcl->gcl_nprint++;
194 
195 	di_devfs_path_free(list.gclo_path);
196 	xpio_ctrl_info_free(info);
197 	xpio_ctrl_fini(ctrl);
198 	return (true);
199 }
200 
201 static int
gpioadm_controller_list(int argc,char * argv[])202 gpioadm_controller_list(int argc, char *argv[])
203 {
204 	int c;
205 	uint_t flags = 0;
206 	boolean_t parse = B_FALSE;
207 	const char *fields = NULL;
208 	ofmt_status_t oferr;
209 	ofmt_handle_t ofmt;
210 	gpioadm_controller_list_t gcl;
211 
212 	(void) memset(&gcl, 0, sizeof (gcl));
213 
214 	while ((c = getopt(argc, argv, ":Ho:p")) != -1) {
215 		switch (c) {
216 		case 'H':
217 			flags |= OFMT_NOHEADER;
218 			break;
219 		case 'o':
220 			fields = optarg;
221 			break;
222 		case 'p':
223 			parse = B_TRUE;
224 			flags |= OFMT_PARSABLE;
225 			break;
226 		case ':':
227 			gpioadm_controller_list_help("option -%c requires an "
228 			    "argument", optopt);
229 			exit(EXIT_USAGE);
230 		case '?':
231 			gpioadm_controller_list_help("unknown option: -%c",
232 			    optopt);
233 			exit(EXIT_USAGE);
234 		}
235 	}
236 
237 	if (parse && fields == NULL) {
238 		errx(EXIT_USAGE, "-p requires fields specified with -o");
239 	}
240 
241 	if (fields == NULL) {
242 		fields = gpioadm_controller_list_fields;
243 	}
244 
245 	argc -= optind;
246 	argv += optind;
247 	if (argc > 0) {
248 		gcl.gcl_nfilts = argc;
249 		gcl.gcl_filts = argv;
250 		gcl.gcl_used = calloc(argc, sizeof (bool));
251 		if (gcl.gcl_used == NULL) {
252 			err(EXIT_FAILURE, "failed to allocate filter tracking "
253 			    "memory");
254 		}
255 	}
256 	oferr = ofmt_open(fields, gpioadm_controller_list_ofmt, flags, 0,
257 	    &ofmt);
258 	ofmt_check(oferr, parse, ofmt, gpioadm_ofmt_errx, warnx);
259 
260 	gcl.gcl_nprint = 0;
261 	gcl.gcl_err = B_FALSE;
262 	gcl.gcl_ofmt = ofmt;
263 	xpio_ctrl_discover(gpioadm.gpio_xpio, gpioadm_controller_list_cb,
264 	    &gcl);
265 
266 	for (int i = 0; i < gcl.gcl_nfilts; i++) {
267 		if (!gcl.gcl_used[i]) {
268 			warnx("filter '%s' did not match any controllers",
269 			    gcl.gcl_filts[i]);
270 			gcl.gcl_err = true;
271 		}
272 	}
273 
274 	if (gcl.gcl_nprint == 0) {
275 		/*
276 		 * We only bother to warn about no controllers being found when
277 		 * there are no filters as otherwise the user would have gotten
278 		 * a message about unmatched filters just above.
279 		 */
280 		if (gcl.gcl_nfilts == 0) {
281 			warnx("no gpio controllers found");
282 		}
283 		gcl.gcl_err = true;
284 	}
285 
286 	return (gcl.gcl_err ? EXIT_FAILURE : EXIT_SUCCESS);
287 }
288 
289 static const gpioadm_cmdtab_t gpioadm_cmds_ctrl[] = {
290 	{ "list", gpioadm_controller_list, gpioadm_controller_list_usage },
291 	{ NULL, NULL, NULL }
292 };
293 
294 int
gpioadm_controller(int argc,char * argv[])295 gpioadm_controller(int argc, char *argv[])
296 {
297 	return (gpioadm_walk_tab(gpioadm_cmds_ctrl, argc, argv));
298 }
299 
300 void
gpioadm_controller_usage(FILE * f)301 gpioadm_controller_usage(FILE *f)
302 {
303 	gpioadm_walk_usage(gpioadm_cmds_ctrl, f);
304 }
305