xref: /illumos-gate/usr/src/cmd/gpioadm/gpioadm_gpio.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_gpio_attr_get_usage(FILE * f)28 gpioadm_gpio_attr_get_usage(FILE *f)
29 {
30 	(void) fprintf(f, "\tgpioadm gpio attr get [-H] [-o field[,...] [-p]] "
31 	    "controller/gpio [filter...]\n");
32 }
33 
34 static void __PRINTFLIKE(1)
gpioadm_gpio_attr_get_help(const char * fmt,...)35 gpioadm_gpio_attr_get_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 gpio attr get [-H] "
46 	    "[-o field[,...] [-p]] controller/gpio\n\t\t\t      [filter...]\n");
47 	(void) fprintf(stderr, "\nList attributes of a specific GPIO\n\n"
48 	    "\t-H\t\tomit the column header\n"
49 	    "\t-o field\toutput fields to print\n"
50 	    "\t-p\t\tparsable output (requires -o)\n\n"
51 	    "The following fields are supported:\n"
52 	    "\tattr\t\tthe name of the attribute\n"
53 	    "\tvalue\t\tthe human-readable value of the attribute\n"
54 	    "\traw\t\tan untranslated value of the attribute (e.g. "
55 	    "underlying\n\t\t\tenum)\n"
56 	    "\tperm\t\tthe permissions of the attribute\n"
57 	    "\tpossible\tthe possible values the attribute may take\n\n"
58 	    "Supported filters are the names of attributes. An attribute "
59 	    "will be printed\nas long as it matches a single filter (they "
60 	    "function as an OR). If any\nfilter does not match, then a non-"
61 	    "zero exit status is returned.\n");
62 }
63 
64 typedef enum gpioadm_gpio_attr_get_otype {
65 	GPIOADM_GPIO_ATTR_GET_ATTR,
66 	GPIOADM_GPIO_ATTR_GET_VALUE,
67 	GPIOADM_GPIO_ATTR_GET_RAW,
68 	GPIOADM_GPIO_ATTR_GET_PERM,
69 	GPIOADM_GPIO_ATTR_GET_POSSIBLE,
70 } gpioadm_gpio_attr_get_otype_t;
71 
72 typedef struct gpioadm_gpio_attr_get_ofmt {
73 	xpio_gpio_info_t *ggag_info;
74 	xpio_gpio_attr_t *ggag_attr;
75 } gpioadm_gpio_attr_get_ofmt_t;
76 
77 static boolean_t
gpioadm_gpio_attr_get_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)78 gpioadm_gpio_attr_get_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
79 {
80 	const char *str;
81 	uint32_t u32;
82 	uint32_t *u32_arr;
83 	const char **str_arr;
84 	uint_t count;
85 	uintptr_t off = 0;
86 
87 	gpioadm_gpio_attr_get_ofmt_t *ggag = ofarg->ofmt_cbarg;
88 	xpio_gpio_info_t *info = ggag->ggag_info;
89 	xpio_gpio_attr_t *attr = ggag->ggag_attr;
90 
91 	switch (ofarg->ofmt_id) {
92 	case GPIOADM_GPIO_ATTR_GET_ATTR:
93 		if (strlcpy(buf, xpio_gpio_attr_name(info, attr), buflen) >=
94 		    buflen) {
95 			return (B_FALSE);
96 		}
97 		break;
98 	case GPIOADM_GPIO_ATTR_GET_VALUE:
99 		switch (xpio_gpio_attr_type(info, attr)) {
100 		case XPIO_ATTR_TYPE_STRING:
101 			if (!xpio_gpio_attr_value_string(attr, &str)) {
102 				return (B_FALSE);
103 			}
104 
105 			if (strlcpy(buf, str, buflen) >= buflen) {
106 				return (B_FALSE);
107 			}
108 			break;
109 		case XPIO_ATTR_TYPE_UINT32:
110 			if (!xpio_gpio_attr_xlate_to_str(info, attr, buf,
111 			    buflen)) {
112 				return (B_FALSE);
113 			}
114 			break;
115 		}
116 		break;
117 	case GPIOADM_GPIO_ATTR_GET_RAW:
118 		switch (xpio_gpio_attr_type(info, attr)) {
119 		case XPIO_ATTR_TYPE_STRING:
120 			if (!xpio_gpio_attr_value_string(attr, &str)) {
121 				return (B_FALSE);
122 			}
123 
124 			if (strlcpy(buf, str, buflen) >= buflen) {
125 				return (B_FALSE);
126 			}
127 			break;
128 		case XPIO_ATTR_TYPE_UINT32:
129 			if (!xpio_gpio_attr_value_uint32(attr, &u32)) {
130 				return (B_FALSE);
131 			}
132 
133 			if (snprintf(buf, buflen, "0x%x", u32) >= buflen) {
134 				return (B_FALSE);
135 			}
136 			break;
137 		}
138 		break;
139 	case GPIOADM_GPIO_ATTR_GET_PERM:
140 		switch (xpio_gpio_attr_prot(info, attr)) {
141 		case XPIO_ATTR_PROT_RO:
142 			if (strlcpy(buf, "r-", buflen) >= buflen) {
143 				return (B_FALSE);
144 			}
145 			break;
146 		case XPIO_ATTR_PROT_RW:
147 			if (strlcpy(buf, "rw", buflen) >= buflen) {
148 				return (B_FALSE);
149 			}
150 			break;
151 		}
152 		break;
153 	case GPIOADM_GPIO_ATTR_GET_POSSIBLE:
154 		switch (xpio_gpio_attr_type(info, attr)) {
155 		case XPIO_ATTR_TYPE_STRING:
156 			xpio_gpio_attr_possible_string(info, attr, &str_arr,
157 			    &count);
158 			for (uint_t i = 0; i < count; i++) {
159 				int len = snprintf(buf + off, buflen - off,
160 				    "%s%s", i > 0 ? "," : "", str_arr[i]);
161 				if (len >= (buflen - off)) {
162 					return (B_FALSE);
163 				}
164 				off += len;
165 			}
166 			break;
167 		case XPIO_ATTR_TYPE_UINT32:
168 			xpio_gpio_attr_possible_uint32(info, attr, &u32_arr,
169 			    &count);
170 			for (uint_t i = 0; i < count; i++) {
171 				char xlate[512];
172 				if (!xpio_gpio_attr_xlate_uint32_to_str(info,
173 				    attr, u32_arr[i], xlate, sizeof (xlate))) {
174 					return (B_FALSE);
175 				}
176 				int len = snprintf(buf + off, buflen - off,
177 				    "%s%s", i > 0 ? "," : "", xlate);
178 				if (len >= (buflen - off)) {
179 					return (B_FALSE);
180 				}
181 				off += len;
182 			}
183 			break;
184 		}
185 		break;
186 	default:
187 		abort();
188 	}
189 
190 	return (B_TRUE);
191 }
192 
193 static const char *gpioadm_gpio_attr_get_fields = "attr,perm,value,possible";
194 static const ofmt_field_t gpioadm_gpio_attr_get_ofmt[] = {
195 	{ "ATTR", 22, GPIOADM_GPIO_ATTR_GET_ATTR,
196 	    gpioadm_gpio_attr_get_ofmt_cb },
197 	{ "PERM", 6, GPIOADM_GPIO_ATTR_GET_PERM,
198 	    gpioadm_gpio_attr_get_ofmt_cb },
199 	{ "VALUE", 24, GPIOADM_GPIO_ATTR_GET_VALUE,
200 	    gpioadm_gpio_attr_get_ofmt_cb },
201 	{ "RAW", 16, GPIOADM_GPIO_ATTR_GET_RAW,
202 	    gpioadm_gpio_attr_get_ofmt_cb },
203 	{ "POSSIBLE", 24, GPIOADM_GPIO_ATTR_GET_POSSIBLE,
204 	    gpioadm_gpio_attr_get_ofmt_cb },
205 	{ NULL, 0, 0, NULL }
206 };
207 
208 static int
gpioadm_gpio_attr_get(int argc,char * argv[])209 gpioadm_gpio_attr_get(int argc, char *argv[])
210 {
211 	int c, ret;
212 	uint_t flags = 0;
213 	boolean_t parse = B_FALSE;
214 	const char *fields = NULL, *target = NULL;
215 	ofmt_status_t oferr;
216 	ofmt_handle_t ofmt;
217 	xpio_ctrl_t *ctrl;
218 	xpio_gpio_info_t *gpio;
219 	bool *filts = NULL;
220 
221 	while ((c = getopt(argc, argv, ":Ho:p")) != -1) {
222 		switch (c) {
223 		case 'H':
224 			flags |= OFMT_NOHEADER;
225 			break;
226 		case 'o':
227 			fields = optarg;
228 			break;
229 		case 'p':
230 			parse = B_TRUE;
231 			flags |= OFMT_PARSABLE;
232 			break;
233 		case ':':
234 			gpioadm_gpio_attr_get_help("option -%c requires an "
235 			    "argument", optopt);
236 			exit(EXIT_USAGE);
237 		case '?':
238 			gpioadm_gpio_attr_get_help("unknown option: -%c",
239 			    optopt);
240 			exit(EXIT_USAGE);
241 		}
242 	}
243 
244 	if (parse && fields == NULL) {
245 		errx(EXIT_USAGE, "-p requires fields specified with -o");
246 	}
247 
248 	if (!parse) {
249 		flags |= OFMT_WRAP;
250 	}
251 
252 	if (fields == NULL) {
253 		fields = gpioadm_gpio_attr_get_fields;
254 	}
255 
256 	argc -= optind;
257 	argv += optind;
258 	if (argc == 0) {
259 		errx(EXIT_FAILURE, "missing required controller and gpio");
260 	}
261 	target = argv[0];
262 	argc--;
263 	argv++;
264 
265 	if (argc > 0) {
266 		filts = calloc(argc, sizeof (bool));
267 		if (filts == NULL) {
268 			err(EXIT_FAILURE, "failed to allocate memory for "
269 			    "filter tracking");
270 		}
271 	}
272 	oferr = ofmt_open(fields, gpioadm_gpio_attr_get_ofmt, flags, 0,
273 	    &ofmt);
274 	ofmt_check(oferr, parse, ofmt, gpioadm_ofmt_errx, warnx);
275 
276 	gpioadm_ctrl_gpio_init(target, &ctrl, &gpio);
277 
278 	for (xpio_gpio_attr_t *attr = xpio_gpio_attr_next(gpio, NULL);
279 	    attr != NULL; attr = xpio_gpio_attr_next(gpio, attr)) {
280 		gpioadm_gpio_attr_get_ofmt_t ggag;
281 
282 		if (argc > 0) {
283 			const char *aname = xpio_gpio_attr_name(gpio, attr);
284 			bool match = false;
285 			for (int i = 0; i < argc; i++) {
286 				if (strcmp(argv[i], aname) == 0) {
287 					match = true;
288 					filts[i] = true;
289 				}
290 			}
291 
292 			if (!match) {
293 				continue;
294 			}
295 		}
296 
297 		ggag.ggag_info = gpio;
298 		ggag.ggag_attr = attr;
299 		ofmt_print(ofmt, &ggag);
300 	}
301 
302 	ret = EXIT_SUCCESS;
303 	for (int i = 0; i < argc; i++) {
304 		if (!filts[i]) {
305 			warnx("filter '%s' did not match any attributes",
306 			    argv[i]);
307 			ret = EXIT_FAILURE;
308 		}
309 	}
310 
311 	free(filts);
312 	return (ret);
313 }
314 
315 static void
gpioadm_gpio_attr_set_usage(FILE * f)316 gpioadm_gpio_attr_set_usage(FILE *f)
317 {
318 	(void) fprintf(f, "\tgpioadm gpio attr set controller/gpio attr=value "
319 	    "[attr=value...]\n");
320 }
321 
322 static void __PRINTFLIKE(1)
gpioadm_gpio_attr_set_help(const char * fmt,...)323 gpioadm_gpio_attr_set_help(const char *fmt, ...)
324 {
325 	if (fmt != NULL) {
326 		va_list ap;
327 
328 		va_start(ap, fmt);
329 		vwarnx(fmt, ap);
330 		va_end(ap);
331 	}
332 
333 	(void) fprintf(stderr, "Usage:  gpioadm gpio attr set controller/gpio "
334 	    "attr=value [attr=value...]\n");
335 	(void) fprintf(stderr, "\nSets the attributes of a single GPIO. "
336 	    "All specified attributes are\napplied at once.\n");
337 }
338 
339 static int
gpioadm_gpio_attr_set(int argc,char * argv[])340 gpioadm_gpio_attr_set(int argc, char *argv[])
341 {
342 	int c;
343 	const char *target;
344 	xpio_ctrl_t *ctrl;
345 	xpio_gpio_info_t *gpio;
346 	xpio_gpio_update_t *update;
347 
348 	while ((c = getopt(argc, argv, ":")) != -1) {
349 		switch (c) {
350 		case ':':
351 			gpioadm_gpio_attr_set_help("option -%c requires an "
352 			    "argument", optopt);
353 			exit(EXIT_USAGE);
354 		case '?':
355 			gpioadm_gpio_attr_set_help("unknown option: -%c",
356 			    optopt);
357 			exit(EXIT_USAGE);
358 		}
359 	}
360 
361 	argc -= optind;
362 	argv += optind;
363 
364 	if (argc == 0) {
365 		errx(EXIT_USAGE, "missing required controller/gpio target");
366 	}
367 
368 	if (argc == 1) {
369 		errx(EXIT_USAGE, "missing required attribute settings");
370 	}
371 
372 	target = argv[0];
373 	gpioadm_ctrl_gpio_init(target, &ctrl, &gpio);
374 	if (!xpio_gpio_update_init(gpioadm.gpio_xpio, gpio, &update)) {
375 		gpioadm_fatal("failed to initialize update");
376 	}
377 
378 	for (int i = 1; i < argc; i++) {
379 		char *eq = strchr(argv[i], '=');
380 		const char *name, *value;
381 		xpio_gpio_attr_t *attr;
382 
383 		if (eq == NULL) {
384 			errx(EXIT_FAILURE, "invalid attribute: missing equals "
385 			    "sign for value: %s", argv[i]);
386 		}
387 		name = argv[i];
388 		value = eq + 1;
389 		*eq = '\0';
390 
391 		attr = xpio_gpio_attr_find(gpio, name);
392 		if (attr == NULL) {
393 			errx(EXIT_FAILURE, "invalid attribute: no attribute "
394 			    "named %s exists for GPIO %s", name, target);
395 		}
396 
397 		if (!xpio_gpio_attr_from_str(update, attr, value)) {
398 			gpioadm_update_fatal(update, "failed to set attribute "
399 			    "%s to %s on GPIO %s", name, value, target);
400 		}
401 	}
402 
403 	if (!xpio_gpio_update(ctrl, update)) {
404 		if (xpio_err(gpioadm.gpio_xpio) != XPIO_ERR_BAD_UPDATE) {
405 			gpioadm_fatal("failed to update GPIO %s", target);
406 		}
407 
408 		gpioadm_warn("failed to update GPIO %s", target);
409 
410 		for (xpio_gpio_attr_err_t *err =
411 		    xpio_gpio_attr_err_next(update, NULL); err != NULL;
412 		    err = xpio_gpio_attr_err_next(update, err)) {
413 			xpio_update_err_t uerr = xpio_gpio_attr_err_err(err);
414 
415 			(void) fprintf(stderr, "\tattribute %s -- %s (0x%x)\n",
416 			    xpio_gpio_attr_err_name(err),
417 			    xpio_update_err2str(update, uerr), uerr);
418 		}
419 	}
420 
421 	return (EXIT_SUCCESS);
422 }
423 
424 static const gpioadm_cmdtab_t gpioadm_cmds_gpio_attr[] = {
425 	{ "get", gpioadm_gpio_attr_get, gpioadm_gpio_attr_get_usage },
426 	{ "set", gpioadm_gpio_attr_set, gpioadm_gpio_attr_set_usage },
427 	{ NULL, NULL, NULL }
428 };
429 
430 static void
gpioadm_gpio_attr_usage(FILE * f)431 gpioadm_gpio_attr_usage(FILE *f)
432 {
433 	gpioadm_walk_usage(gpioadm_cmds_gpio_attr, f);
434 }
435 
436 static int
gpioadm_gpio_attr(int argc,char * argv[])437 gpioadm_gpio_attr(int argc, char *argv[])
438 {
439 	return (gpioadm_walk_tab(gpioadm_cmds_gpio_attr, argc, argv));
440 }
441 
442 static void
gpioadm_gpio_list_usage(FILE * f)443 gpioadm_gpio_list_usage(FILE *f)
444 {
445 	(void) fprintf(f, "\tgpioadm gpio list [-H] [-o field[,...] [-p]] "
446 	    "[-1] [filter...]\n");
447 }
448 
449 static void __PRINTFLIKE(1)
gpioadm_gpio_list_help(const char * fmt,...)450 gpioadm_gpio_list_help(const char *fmt, ...)
451 {
452 	if (fmt != NULL) {
453 		va_list ap;
454 
455 		va_start(ap, fmt);
456 		vwarnx(fmt, ap);
457 		va_end(ap);
458 	}
459 
460 	(void) fprintf(stderr, "Usage:  gpioadm gpio list [-H] [-o "
461 	    "field[,...] [-p]] [-1] [filter...]\n");
462 	(void) fprintf(stderr, "\nList GPIOs in the system.\n\n"
463 	    "\t-H\t\tomit the column header\n"
464 	    "\t-o field\toutput fields to print\n"
465 	    "\t-p\t\tparsable output (requires -o)\n"
466 	    "\t-1\t\terror if more than one GPIO is listed\n\n"
467 	    "The following fields are supported:\n"
468 	    "\tcontroller\tthe name of the controller\n"
469 	    "\tgpio\t\tthe name of the gpio\n"
470 	    "\tid\t\tthe GPIO's numeric id\n"
471 	    "Filters can be used to constrain the GPIOs that are listed. If a "
472 	    "filter is\npresent, it will be an error if it is unused. Filters "
473 	    "can specify either an\nentire controller, a specific GPIO on a "
474 	    "controller, or all GPIOs with a given\nname. The controller and "
475 	    "GPIO are separated with a '/' character. For example:\n\n"
476 	    "\tgpio_sim0\t\tThis would match all GPIOs on the controller\n"
477 	    "\t\t\t\t'gpio_sim0'.\n"
478 	    "\tzen_gpio0/EGPIO9_3\tThis would match the specific GPIO, "
479 	    "EGPIO9_3,\n\t\t\t\ton the specified controller, zen_gpio0.\n"
480 	    "\t*/gpio3\t\t\tThis would match all GPIOs named 'gpio3' on any\n"
481 	    "\t\t\t\tcontroller.\n");
482 }
483 
484 typedef enum gpioadm_gpio_list_otype {
485 	GPIOADM_GPIO_LIST_CTRL,
486 	GPIOADM_GPIO_LIST_NAME,
487 	GPIOADM_GPIO_LIST_ID
488 } gpioadm_gpio_list_otype_t;
489 
490 typedef struct gpioadm_gpio_list_ofmt {
491 	const char *gglo_minor;
492 	const char *gglo_name;
493 	uint32_t gglo_id;
494 	uint32_t gglo_flags;
495 } gpioadm_gpio_list_ofmt_t;
496 
497 static boolean_t
gpioadm_gpio_list_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)498 gpioadm_gpio_list_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
499 {
500 	gpioadm_gpio_list_ofmt_t *gglo = ofarg->ofmt_cbarg;
501 
502 	switch (ofarg->ofmt_id) {
503 	case GPIOADM_GPIO_LIST_CTRL:
504 		if (strlcpy(buf, gglo->gglo_minor, buflen) >= buflen) {
505 			return (B_FALSE);
506 		}
507 		break;
508 	case GPIOADM_GPIO_LIST_NAME:
509 		if (strlcpy(buf, gglo->gglo_name, buflen) >= buflen) {
510 			return (B_FALSE);
511 		}
512 		break;
513 	case GPIOADM_GPIO_LIST_ID:
514 		if (snprintf(buf, buflen, "%u", gglo->gglo_id) >= buflen) {
515 			return (B_FALSE);
516 		}
517 		break;
518 	default:
519 		abort();
520 	}
521 	return (B_TRUE);
522 }
523 
524 static const char *gpioadm_gpio_list_fields = "controller,gpio,id";
525 static const ofmt_field_t gpioadm_gpio_list_ofmt[] = {
526 	{ "CONTROLLER", 16, GPIOADM_GPIO_LIST_CTRL,
527 	    gpioadm_gpio_list_ofmt_cb },
528 	{ "GPIO", 20, GPIOADM_GPIO_LIST_NAME,
529 	    gpioadm_gpio_list_ofmt_cb },
530 	{ "ID", 8, GPIOADM_GPIO_LIST_ID,
531 	    gpioadm_gpio_list_ofmt_cb },
532 	{ NULL, 0, 0, NULL }
533 };
534 
535 typedef struct {
536 	bool ggl_err;
537 	bool ggl_one;
538 	uint_t ggl_nprint;
539 	ofmt_handle_t ggl_ofmt;
540 	int ggl_nfilts;
541 	char *const *ggl_filts;
542 	bool *ggl_used;
543 } gpioadm_gpio_list_t;
544 
545 static bool
gpioadm_gpio_list_match(const char * ctrl,const char * gpio,gpioadm_gpio_list_t * ggl)546 gpioadm_gpio_list_match(const char *ctrl, const char *gpio,
547     gpioadm_gpio_list_t *ggl)
548 {
549 	if (ggl->ggl_nfilts <= 0) {
550 		return (true);
551 	}
552 
553 	for (int i = 0; i < ggl->ggl_nfilts; i++) {
554 		const char *filt = ggl->ggl_filts[i];
555 		const char *slash = strchr(filt, '/');
556 		bool all_ctrls;
557 		size_t ctrl_len;
558 
559 		/*
560 		 * This is just a controller filter.
561 		 */
562 		if (slash == NULL) {
563 			if (strcmp(ctrl, filt) == 0) {
564 				ggl->ggl_used[i] = true;
565 				return (true);
566 			}
567 		}
568 
569 		ctrl_len = (uintptr_t)slash - (uintptr_t)filt;
570 		if (ctrl_len == 0) {
571 			return (false);
572 		}
573 
574 		all_ctrls = ctrl_len == 1 && filt[0] == '*';
575 		if (!all_ctrls && (strlen(ctrl) != ctrl_len ||
576 		    strncmp(ctrl, filt, ctrl_len) != 0)) {
577 			continue;
578 		}
579 
580 		if (strcmp(slash + 1, gpio) == 0) {
581 			ggl->ggl_used[i] = true;
582 			return (true);
583 		}
584 	}
585 
586 	return (false);
587 }
588 
589 static bool
gpioadm_gpio_list_cb(xpio_t * xpio,xpio_ctrl_disc_t * disc,void * arg)590 gpioadm_gpio_list_cb(xpio_t *xpio, xpio_ctrl_disc_t *disc, void *arg)
591 {
592 	xpio_ctrl_t *ctrl;
593 	xpio_ctrl_info_t *info;
594 	uint32_t ngpios;
595 	const char *mname = di_minor_name(disc->xcd_minor);
596 	gpioadm_gpio_list_t *ggl = arg;
597 
598 	if (!xpio_ctrl_init(xpio, disc->xcd_minor, &ctrl)) {
599 		gpioadm_warn("failed to initialize controller %s", mname);
600 		ggl->ggl_err = true;
601 		return (true);
602 	}
603 
604 	if (!xpio_ctrl_info(ctrl, &info)) {
605 		gpioadm_warn("failed to get controller info for %s", mname);
606 		xpio_ctrl_fini(ctrl);
607 		ggl->ggl_err = true;
608 		return (true);
609 	}
610 
611 	ngpios = xpio_ctrl_info_ngpios(info);
612 	for (uint32_t i = 0; i < ngpios; i++) {
613 		gpioadm_gpio_list_ofmt_t list;
614 		xpio_gpio_info_t *gpio_info;
615 		xpio_gpio_attr_t *attr;
616 
617 		if (!xpio_gpio_info(ctrl, i, &gpio_info)) {
618 			ggl->ggl_err = true;
619 			gpioadm_warn("failed to get gpio info for %s:%u",
620 			    mname, i);
621 			continue;
622 		}
623 
624 		attr = xpio_gpio_attr_find(gpio_info, KGPIO_ATTR_NAME);
625 		if (attr == NULL || !xpio_gpio_attr_value_string(attr,
626 		    &list.gglo_name)) {
627 			warnx("GPIO %s/%u missing name attribute",
628 			    mname, i);
629 			goto skip;
630 		}
631 		list.gglo_minor = mname;
632 		list.gglo_id = i;
633 		list.gglo_flags = 0;
634 
635 		if (!gpioadm_gpio_list_match(mname, list.gglo_name, ggl)) {
636 			goto skip;
637 		}
638 
639 		ggl->ggl_nprint++;
640 		ofmt_print(ggl->ggl_ofmt, &list);
641 
642 skip:
643 		xpio_gpio_info_free(gpio_info);
644 	}
645 
646 	xpio_ctrl_fini(ctrl);
647 	return (true);
648 }
649 
650 static int
gpioadm_gpio_list(int argc,char * argv[])651 gpioadm_gpio_list(int argc, char *argv[])
652 {
653 	int c;
654 	uint_t flags = 0;
655 	boolean_t parse = B_FALSE;
656 	const char *fields = NULL;
657 	ofmt_status_t oferr;
658 	ofmt_handle_t ofmt;
659 	gpioadm_gpio_list_t ggl;
660 
661 	(void) memset(&ggl, 0, sizeof (ggl));
662 	while ((c = getopt(argc, argv, ":Ho:p1")) != -1) {
663 		switch (c) {
664 		case 'H':
665 			flags |= OFMT_NOHEADER;
666 			break;
667 		case 'o':
668 			fields = optarg;
669 			break;
670 		case 'p':
671 			parse = B_TRUE;
672 			flags |= OFMT_PARSABLE;
673 			break;
674 		case '1':
675 			ggl.ggl_one = true;
676 			break;
677 		case ':':
678 			gpioadm_gpio_list_help("option -%c requires an "
679 			    "argument", optopt);
680 			exit(EXIT_USAGE);
681 		case '?':
682 			gpioadm_gpio_list_help("unknown option: -%c", optopt);
683 			exit(EXIT_USAGE);
684 		}
685 	}
686 
687 	if (parse && fields == NULL) {
688 		errx(EXIT_USAGE, "-p requires fields specified with -o");
689 	}
690 
691 	if (fields == NULL) {
692 		fields = gpioadm_gpio_list_fields;
693 	}
694 
695 	argc -= optind;
696 	argv += optind;
697 	if (argc > 0) {
698 		ggl.ggl_nfilts = argc;
699 		ggl.ggl_filts = argv;
700 		ggl.ggl_used = calloc(argc, sizeof (bool));
701 		if (ggl.ggl_used == NULL) {
702 			err(EXIT_FAILURE, "failed to allocate memory for "
703 			    "filter tracking");
704 		}
705 	}
706 	oferr = ofmt_open(fields, gpioadm_gpio_list_ofmt, flags, 0, &ofmt);
707 	ofmt_check(oferr, parse, ofmt, gpioadm_ofmt_errx, warnx);
708 
709 	ggl.ggl_err = false;
710 	ggl.ggl_ofmt = ofmt;
711 	xpio_ctrl_discover(gpioadm.gpio_xpio, gpioadm_gpio_list_cb, &ggl);
712 
713 	for (int i = 0; i < ggl.ggl_nfilts; i++) {
714 		if (!ggl.ggl_used[i]) {
715 			warnx("filter '%s' did not match any GPIOs",
716 			    ggl.ggl_filts[i]);
717 			ggl.ggl_err = true;
718 		}
719 	}
720 
721 	if (ggl.ggl_one && ggl.ggl_nprint > 1) {
722 		warnx("-1 specified, but %u GPIOs printed", ggl.ggl_nprint);
723 		ggl.ggl_err = true;
724 	}
725 
726 	if (ggl.ggl_nprint == 0) {
727 		if (ggl.ggl_nfilts == 0) {
728 			warnx("no GPIOs found");
729 		}
730 		ggl.ggl_err = true;
731 	}
732 
733 	return (ggl.ggl_err ? EXIT_FAILURE : EXIT_SUCCESS);
734 }
735 
736 static const gpioadm_cmdtab_t gpioadm_cmds_gpio[] = {
737 	{ "attr", gpioadm_gpio_attr, gpioadm_gpio_attr_usage },
738 	{ "list", gpioadm_gpio_list, gpioadm_gpio_list_usage },
739 	{ NULL, NULL, NULL }
740 };
741 
742 int
gpioadm_gpio(int argc,char * argv[])743 gpioadm_gpio(int argc, char *argv[])
744 {
745 	return (gpioadm_walk_tab(gpioadm_cmds_gpio, argc, argv));
746 }
747 
748 void
gpioadm_gpio_usage(FILE * f)749 gpioadm_gpio_usage(FILE *f)
750 {
751 	gpioadm_walk_usage(gpioadm_cmds_gpio, f);
752 }
753