# # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2022 Oxide Computer Company # PROG= gpioadm include ../Makefile.cmd include ../Makefile.cmd.64 include ../Makefile.ctf CFLAGS += $(CCVERBOSE) CSTD = $(CSTD_GNU99) OBJS = gpioadm.o gpioadm_controller.o gpioadm_dpio.o gpioadm_gpio.o LDLIBS += -ldevinfo -lofmt -lxpio .KEEP_STATE: $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) all: $(PROG) install: all $(ROOTUSRSBINPROG) clean: $(RM) $(OBJS) include ../Makefile.targ /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2022 Oxide Computer Company */ /* * This command implements the basics of administering general purpose and * dedicated purpose I/O (GPIO and DPIO). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpioadm.h" gpioadm_t gpioadm; static void gpioadm_vwarn(const char *fmt, va_list ap) { xpio_t *xpio = gpioadm.gpio_xpio; (void) fprintf(stderr, "%s: ", gpioadm.gpio_progname); (void) vfprintf(stderr, fmt, ap); (void) fprintf(stderr, ": %s: %s (libxpio: 0x%x, sys: %u)\n", xpio_errmsg(xpio), xpio_err2str(xpio, xpio_err(xpio)), xpio_err(xpio), xpio_syserr(xpio)); } void gpioadm_warn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); gpioadm_vwarn(fmt, ap); va_end(ap); } void __NORETURN gpioadm_fatal(const char *fmt, ...) { va_list ap; va_start(ap, fmt); gpioadm_vwarn(fmt, ap); va_end(ap); exit(EXIT_FAILURE); } void __NORETURN gpioadm_update_fatal(xpio_gpio_update_t *update, const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void) fprintf(stderr, "%s: ", gpioadm.gpio_progname); (void) vfprintf(stderr, fmt, ap); (void) fprintf(stderr, ": %s: %s (libxpio: 0x%x, sys: %u)\n", xpio_update_errmsg(update), xpio_update_err2str(update, xpio_update_err(update)), xpio_update_err(update), xpio_update_syserr(update)); va_end(ap); exit(EXIT_FAILURE); } void gpioadm_ofmt_errx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); verrx(EXIT_FAILURE, fmt, ap); } void gpioadm_ctrl_gpio_init(const char *target, xpio_ctrl_t **ctrlp, xpio_gpio_info_t **gpiop) { char *eptr, *slash, *dup; const char *ctrl_name, *gpio_name; uint32_t gpio_num; xpio_ctrl_t *ctrl; xpio_gpio_info_t *gpio; dup = strdup(target); if (dup == NULL) { err(EXIT_FAILURE, "failed to allocate memory for target string " "processing"); } slash = strchr(dup, '/'); if (slash == NULL) { errx(EXIT_FAILURE, "invalid target: %s, missing '/' delimiter " "to separate controller and gpio", target); } ctrl_name = dup; gpio_name = slash + 1; *slash = '\0'; if (!xpio_ctrl_init_by_name(gpioadm.gpio_xpio, ctrl_name, &ctrl)) { gpioadm_fatal("failed to initialize gpio controller %s", ctrl_name); } /* * We always attempt to look up and translate the name to a GPIO ID * first. This way if a controller does something like just name the * pins 1, 2, 3, 4, etc. but has a weird relationship to the IDs, we're * more likely to get what the user intended (hopefully). */ if (!xpio_gpio_lookup_id(ctrl, gpio_name, &gpio_num)) { long long l; if (xpio_err(gpioadm.gpio_xpio) != XPIO_ERR_NO_LOOKUP_MATCH) { gpioadm_fatal("failed to look up name %s on " "controller %s", ctrl_name, gpio_name); } /* * At this point, attempt to parse it as an intger. */ errno = 0; l = strtoll(gpio_name, &eptr, 0); if (errno != 0 || *eptr != '\0') { errx(EXIT_FAILURE, "failed to parse gpio number: %s", gpio_name); } if (l < 0 || l > UINT32_MAX) { errx(EXIT_FAILURE, "gpio number is outside of valid " "range: %s", gpio_name); } gpio_num = (uint32_t)l; } if (!xpio_gpio_info(ctrl, gpio_num, &gpio)) { gpioadm_fatal("failed to get gpio %u on controller %s", gpio_num, ctrl_name); } *gpiop = gpio; *ctrlp = ctrl; free(dup); } void gpioadm_walk_usage(const gpioadm_cmdtab_t *tab, FILE *f) { for (; tab->gpct_name != NULL; tab++) { tab->gpct_use(f); } } static void gpioadm_usage(const gpioadm_cmdtab_t *tab, const char *format, ...) { if (format != NULL) { va_list ap; va_start(ap, format); vwarnx(format, ap); va_end(ap); } if (tab == NULL) return; fprintf(stderr, "Usage: gpioadm ... \n\n"); gpioadm_walk_usage(tab, stderr); } int gpioadm_walk_tab(const gpioadm_cmdtab_t *tab, int argc, char *argv[]) { uint32_t cmd; if (argc == 0) { gpioadm_usage(tab, "missing required sub-command"); return (EXIT_FAILURE); } for (cmd = 0; tab[cmd].gpct_name != NULL; cmd++) { if (strcmp(argv[0], tab[cmd].gpct_name) == 0) { break; } } if (tab[cmd].gpct_name == NULL) { gpioadm_usage(tab, "unknown subcommand %s", argv[0]); return (EXIT_USAGE); } argc--; argv++; optind = 0; return (tab[cmd].gpct_func(argc, argv)); } static const gpioadm_cmdtab_t gpioadm_cmds[] = { { "controller", gpioadm_controller, gpioadm_controller_usage }, { "dpio", gpioadm_dpio, gpioadm_dpio_usage }, { "gpio", gpioadm_gpio, gpioadm_gpio_usage }, { NULL, NULL, NULL } }; int main(int argc, char *argv[]) { gpioadm.gpio_progname = basename(argv[0]); if (argc < 2) { gpioadm_usage(gpioadm_cmds, "missing required sub-command"); exit(EXIT_USAGE); } argc--; argv++; gpioadm.gpio_xpio = xpio_init(); if (gpioadm.gpio_xpio == NULL) { err(EXIT_FAILURE, "failed to initialize libxpio"); } return (gpioadm_walk_tab(gpioadm_cmds, argc, argv)); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2022 Oxide Computer Company */ #ifndef _GPIOADM_H #define _GPIOADM_H /* * gpioadm(8) interfaces */ #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define EXIT_USAGE 2 typedef struct { const char *gpio_progname; xpio_t *gpio_xpio; } gpioadm_t; typedef struct gpioadm_cmdtab { const char *gpct_name; int (*gpct_func)(int, char *[]); void (*gpct_use)(FILE *); } gpioadm_cmdtab_t; extern gpioadm_t gpioadm; extern void gpioadm_warn(const char *, ...) __PRINTFLIKE(1); extern void gpioadm_fatal(const char *, ...) __PRINTFLIKE(1) __NORETURN; extern void gpioadm_update_fatal(xpio_gpio_update_t *, const char *, ...) __PRINTFLIKE(2) __NORETURN; extern void gpioadm_ofmt_errx(const char *, ...) __PRINTFLIKE(1) __NORETURN; extern void gpioadm_ctrl_gpio_init(const char *, xpio_ctrl_t **, xpio_gpio_info_t **); extern void gpioadm_walk_usage(const gpioadm_cmdtab_t *, FILE *); extern int gpioadm_walk_tab(const gpioadm_cmdtab_t *, int, char *[]); extern int gpioadm_controller(int, char *[]); extern void gpioadm_controller_usage(FILE *); extern int gpioadm_dpio(int, char *[]); extern void gpioadm_dpio_usage(FILE *); extern int gpioadm_gpio(int, char *[]); extern void gpioadm_gpio_usage(FILE *); #ifdef __cplusplus } #endif #endif /* _GPIOADM_H */ /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2022 Oxide Computer Company */ #include #include #include #include #include #include #include #include #include "gpioadm.h" static void gpioadm_controller_list_usage(FILE *f) { (void) fprintf(f, "\tgpioadm controller list [-H] [-o field[,...] " "[-p]] [filter...]\n"); } static void __PRINTFLIKE(1) gpioadm_controller_list_help(const char *fmt, ...) { if (fmt != NULL) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } (void) fprintf(stderr, "Usage: gpioadm controller list [-H] [-o " "field[,...] [-p]] [filter...]\n"); (void) fprintf(stderr, "\nList GPIO controllers in the system and " "associated information.\n\n" "\t-H\t\tomit the column header\n" "\t-o field\toutput fields to print\n" "\t-p\t\tparsable output (requires -o)\n\n" "The following fields are supported:\n" "\tcontroller\tthe name of the controller\n" "\tngpios\t\tthe number of GPIOs the controller has\n" "\tndpios\t\tthe number of DPIOs the controller has\n" "\tpath\t\tthe path to the minor node of the controller\n" "\tprovider\tthe /devices path of the provider\n\n" "Filters restrict output to the named controllers. Each filter is " "treated\nlike an OR allowing one to limit output to specific " "controllers. It is\nan error if a controller isn't found.\n"); } typedef enum gpioadm_controller_list_otype { GPIOADM_CTRL_LIST_CTRLR, GPIOADM_CTRL_LIST_NGPIO, GPIOADM_CTRL_LIST_NDPIO, GPIOADM_CTRL_LIST_PATH, GPIOADM_CTRL_LIST_PROVIDER } gpioadm_controllier_list_otype_t; typedef struct gpioadm_controller_list_ofmt { const char *gclo_minor; const char *gclo_devpath; char *gclo_path; uint32_t gclo_ngpio; uint32_t gclo_ndpio; } gpioadm_controller_list_ofmt_t; static boolean_t gpioadm_controller_list_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen) { gpioadm_controller_list_ofmt_t *gclo = ofarg->ofmt_cbarg; switch (ofarg->ofmt_id) { case GPIOADM_CTRL_LIST_CTRLR: if (strlcpy(buf, gclo->gclo_minor, buflen) >= buflen) { return (B_FALSE); } break; case GPIOADM_CTRL_LIST_NGPIO: if (snprintf(buf, buflen, "%u", gclo->gclo_ngpio) >= buflen) { return (B_FALSE); } break; case GPIOADM_CTRL_LIST_NDPIO: if (snprintf(buf, buflen, "%u", gclo->gclo_ndpio) >= buflen) { return (B_FALSE); } break; case GPIOADM_CTRL_LIST_PROVIDER: if (strlcpy(buf, gclo->gclo_devpath, buflen) >= buflen) { return (B_FALSE); } break; case GPIOADM_CTRL_LIST_PATH: /* * Asking for the minor path can fail. So if we have nothing, * just ignore this. */ if (gclo->gclo_path == NULL) break; if (strlcpy(buf, gclo->gclo_path, buflen) >= buflen) { return (B_FALSE); } break; default: abort(); } return (B_TRUE); } static const char *gpioadm_controller_list_fields = "controller,ngpios,ndpios," "provider"; static const ofmt_field_t gpioadm_controller_list_ofmt[] = { { "CONTROLLER", 16, GPIOADM_CTRL_LIST_CTRLR, gpioadm_controller_list_ofmt_cb }, { "NGPIOS", 8, GPIOADM_CTRL_LIST_NGPIO, gpioadm_controller_list_ofmt_cb }, { "NDPIOS", 8, GPIOADM_CTRL_LIST_NDPIO, gpioadm_controller_list_ofmt_cb }, { "PROVIDER", 42, GPIOADM_CTRL_LIST_PROVIDER, gpioadm_controller_list_ofmt_cb }, { "PATH", 42, GPIOADM_CTRL_LIST_PATH, gpioadm_controller_list_ofmt_cb }, { NULL, 0, 0, NULL } }; typedef struct { ofmt_handle_t gcl_ofmt; uint32_t gcl_nprint; bool gcl_err; int gcl_nfilts; char **gcl_filts; bool *gcl_used; } gpioadm_controller_list_t; static bool gpioadm_controller_list_cb(xpio_t *xpio, xpio_ctrl_disc_t *disc, void *arg) { xpio_ctrl_t *ctrl; xpio_ctrl_info_t *info; gpioadm_controller_list_ofmt_t list; gpioadm_controller_list_t *gcl = arg; const char *mname = di_minor_name(disc->xcd_minor); if (gcl->gcl_nfilts > 0) { bool match = false; for (int i = 0; i < gcl->gcl_nfilts; i++) { if (strcmp(mname, gcl->gcl_filts[i]) == 0) { gcl->gcl_used[i] = true; match = true; break; } } if (!match) { return (true); } } if (!xpio_ctrl_init(xpio, disc->xcd_minor, &ctrl)) { gpioadm_warn("failed to initialize controller %s", mname); gcl->gcl_err = B_TRUE; return (true); } if (!xpio_ctrl_info(ctrl, &info)) { gpioadm_warn("failed to get controller info for %s", mname); xpio_ctrl_fini(ctrl); gcl->gcl_err = B_TRUE; return (true); } bzero(&list, sizeof (list)); list.gclo_minor = mname; list.gclo_ngpio = xpio_ctrl_info_ngpios(info); list.gclo_ndpio = xpio_ctrl_info_ndpios(info); list.gclo_devpath = xpio_ctrl_info_devpath(info); list.gclo_path = di_devfs_minor_path(disc->xcd_minor); ofmt_print(gcl->gcl_ofmt, &list); gcl->gcl_nprint++; di_devfs_path_free(list.gclo_path); xpio_ctrl_info_free(info); xpio_ctrl_fini(ctrl); return (true); } static int gpioadm_controller_list(int argc, char *argv[]) { int c; uint_t flags = 0; boolean_t parse = B_FALSE; const char *fields = NULL; ofmt_status_t oferr; ofmt_handle_t ofmt; gpioadm_controller_list_t gcl; (void) memset(&gcl, 0, sizeof (gcl)); while ((c = getopt(argc, argv, ":Ho:p")) != -1) { switch (c) { case 'H': flags |= OFMT_NOHEADER; break; case 'o': fields = optarg; break; case 'p': parse = B_TRUE; flags |= OFMT_PARSABLE; break; case ':': gpioadm_controller_list_help("option -%c requires an " "argument", optopt); exit(EXIT_USAGE); case '?': gpioadm_controller_list_help("unknown option: -%c", optopt); exit(EXIT_USAGE); } } if (parse && fields == NULL) { errx(EXIT_USAGE, "-p requires fields specified with -o"); } if (fields == NULL) { fields = gpioadm_controller_list_fields; } argc -= optind; argv += optind; if (argc > 0) { gcl.gcl_nfilts = argc; gcl.gcl_filts = argv; gcl.gcl_used = calloc(argc, sizeof (bool)); if (gcl.gcl_used == NULL) { err(EXIT_FAILURE, "failed to allocate filter tracking " "memory"); } } oferr = ofmt_open(fields, gpioadm_controller_list_ofmt, flags, 0, &ofmt); ofmt_check(oferr, parse, ofmt, gpioadm_ofmt_errx, warnx); gcl.gcl_nprint = 0; gcl.gcl_err = B_FALSE; gcl.gcl_ofmt = ofmt; xpio_ctrl_discover(gpioadm.gpio_xpio, gpioadm_controller_list_cb, &gcl); for (int i = 0; i < gcl.gcl_nfilts; i++) { if (!gcl.gcl_used[i]) { warnx("filter '%s' did not match any controllers", gcl.gcl_filts[i]); gcl.gcl_err = true; } } if (gcl.gcl_nprint == 0) { /* * We only bother to warn about no controllers being found when * there are no filters as otherwise the user would have gotten * a message about unmatched filters just above. */ if (gcl.gcl_nfilts == 0) { warnx("no gpio controllers found"); } gcl.gcl_err = true; } return (gcl.gcl_err ? EXIT_FAILURE : EXIT_SUCCESS); } static const gpioadm_cmdtab_t gpioadm_cmds_ctrl[] = { { "list", gpioadm_controller_list, gpioadm_controller_list_usage }, { NULL, NULL, NULL } }; int gpioadm_controller(int argc, char *argv[]) { return (gpioadm_walk_tab(gpioadm_cmds_ctrl, argc, argv)); } void gpioadm_controller_usage(FILE *f) { gpioadm_walk_usage(gpioadm_cmds_ctrl, f); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2022 Oxide Computer Company */ #include #include #include #include #include #include #include #include "gpioadm.h" static void gpioadm_dpio_list_usage(FILE *f) { (void) fprintf(f, "\tgpioadm dpio list [-H] [-o field[,...] [-p]] " "[filter...]\n"); } static void __PRINTFLIKE(1) gpioadm_dpio_list_help(const char *fmt, ...) { if (fmt != NULL) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } (void) fprintf(stderr, "Usage: gpioadm dpio list [-H] [-o " "field[,...] [-p]] [filter...]\n"); (void) fprintf(stderr, "\nList information about DPIOs\n\n" "\t-H\t\tomit the column header\n" "\t-o field\toutput fields to print\n" "\t-p\t\tparsable output (requires -o)\n\n" "The following fields are supported:\n" "\tdpio\t\tthe name of the DPIO\n" "\tcontroller\tthe name of the underlying GPIO controller\n" "\tgpionum\t\tthe number of the underlying GPIO\n" "\tcaps\t\tDPIO capabilities\n" "\tflags\t\tDPIO flags that effect its behavior\n" "Filters restrict output to the named DPIOs. Each filter is " "treated\nlike an OR allowing one to limit output to specific " "controllers. It is\nan error if a DPIO isn't found.\n"); } typedef enum gpioadm_dpio_list_otype { GPIOADM_DPIO_LIST_DPIO, GPIOADM_DPIO_LIST_CTRL, GPIOADM_DPIO_LIST_GPIONUM, GPIOADM_DPIO_LIST_CAPS, GPIOADM_DPIO_LIST_FLAGS } gpioadm_dpio_list_otype_t; static boolean_t gpioadm_dpio_list_ofmt_caps(char *buf, uint_t buflen, xpio_dpio_info_t *info) { boolean_t first = B_TRUE; dpio_caps_t caps = xpio_dpio_info_caps(info); dpio_caps_t bits[3] = { DPIO_C_READ, DPIO_C_WRITE, DPIO_C_POLL }; const char *strs[3] = { "read", "write", "poll" }; uintptr_t off = 0; for (size_t i = 0; i < ARRAY_SIZE(bits); i++) { int len; if ((caps & bits[i]) == 0) continue; len = snprintf(buf + off, buflen - off, "%s%s", first ? "" : ",", strs[i]); if (len >= (buflen - off)) { return (B_FALSE); } off += len; first = B_FALSE; } return (B_TRUE); } static boolean_t gpioadm_dpio_list_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen) { xpio_dpio_info_t *info = ofarg->ofmt_cbarg; dpio_flags_t flags; switch (ofarg->ofmt_id) { case GPIOADM_DPIO_LIST_DPIO: if (strlcpy(buf, xpio_dpio_info_name(info), buflen) >= buflen) { return (B_FALSE); } break; case GPIOADM_DPIO_LIST_CTRL: if (strlcpy(buf, xpio_dpio_info_ctrl(info), buflen) >= buflen) { return (B_FALSE); } break; case GPIOADM_DPIO_LIST_GPIONUM: if (snprintf(buf, buflen, "%u", xpio_dpio_info_gpionum(info)) >= buflen) { return (B_FALSE); } break; case GPIOADM_DPIO_LIST_CAPS: return (gpioadm_dpio_list_ofmt_caps(buf, buflen, info)); case GPIOADM_DPIO_LIST_FLAGS: flags = xpio_dpio_info_flags(info); if (flags == 0) { if (strlcpy(buf, "-", buflen) >= buflen) { return (B_FALSE); } } else { if (strlcpy(buf, "K", buflen) >= buflen) { return (B_FALSE); } } break; default: return (B_FALSE); } return (B_TRUE); } static const char *gpioadm_dpio_list_fields = "dpio,caps,flags,controller," "gpionum"; static const ofmt_field_t gpioadm_dpio_list_ofmt[] = { { "DPIO", 16, GPIOADM_DPIO_LIST_DPIO, gpioadm_dpio_list_ofmt_cb }, { "CAPS", 16, GPIOADM_DPIO_LIST_CAPS, gpioadm_dpio_list_ofmt_cb }, { "FLAGS", 8, GPIOADM_DPIO_LIST_FLAGS, gpioadm_dpio_list_ofmt_cb }, { "CONTROLLER", 16, GPIOADM_DPIO_LIST_CTRL, gpioadm_dpio_list_ofmt_cb }, { "GPIONUM", 8, GPIOADM_DPIO_LIST_GPIONUM, gpioadm_dpio_list_ofmt_cb }, { NULL, 0, 0, NULL } }; typedef struct { ofmt_handle_t gdl_ofmt; uint32_t gdl_nprint; bool gdl_err; int gdl_nfilts; char **gdl_filts; bool *gdl_used; } gpioadm_dpio_list_t; static bool gpioadm_dpio_list_cb(xpio_t *xpio, xpio_dpio_disc_t *disc, void *arg) { gpioadm_dpio_list_t *gdl = arg; /* Strip out the kernel mandated 'dpio:' */ const char *name = di_minor_name(disc->xdd_minor) + 5; xpio_dpio_info_t *info; if (gdl->gdl_nfilts > 0) { bool found = false; for (int i = 0; i < gdl->gdl_nfilts; i++) { if (strcmp(name, gdl->gdl_filts[i]) == 0) { found = true; gdl->gdl_used[i] = true; break; } } if (!found) { return (true); } } gdl->gdl_nprint++; if (!xpio_dpio_info(gpioadm.gpio_xpio, disc->xdd_minor, &info)) { gpioadm_warn("failed to get controller info for %s", name); gdl->gdl_err = true; return (true); } ofmt_print(gdl->gdl_ofmt, info); xpio_dpio_info_free(info); return (true); } static int gpioadm_dpio_list(int argc, char *argv[]) { int c; uint_t flags = 0; boolean_t parse = B_FALSE; const char *fields = NULL; ofmt_status_t oferr; ofmt_handle_t ofmt; gpioadm_dpio_list_t gdl; (void) memset(&gdl, 0, sizeof (gdl)); while ((c = getopt(argc, argv, ":Ho:p")) != -1) { switch (c) { case 'H': flags |= OFMT_NOHEADER; break; case 'o': fields = optarg; break; case 'p': parse = B_TRUE; flags |= OFMT_PARSABLE; break; case ':': gpioadm_dpio_list_help("option -%c requires an " "argument", optopt); exit(EXIT_USAGE); case '?': gpioadm_dpio_list_help("unknown option: -%c", optopt); exit(EXIT_USAGE); } } if (parse && fields == NULL) { errx(EXIT_USAGE, "-p requires fields specified with -o"); } if (fields == NULL) { fields = gpioadm_dpio_list_fields; } argc -= optind; argv += optind; if (argc > 0) { gdl.gdl_nfilts = argc; gdl.gdl_filts = argv; gdl.gdl_used = calloc(argc, sizeof (bool)); if (gdl.gdl_used == NULL) { err(EXIT_FAILURE, "failed to allocate filter tracking " "memory"); } } oferr = ofmt_open(fields, gpioadm_dpio_list_ofmt, flags, 0, &ofmt); ofmt_check(oferr, parse, ofmt, gpioadm_ofmt_errx, warnx); gdl.gdl_nprint = 0; gdl.gdl_err = B_FALSE; gdl.gdl_ofmt = ofmt; xpio_dpio_discover(gpioadm.gpio_xpio, gpioadm_dpio_list_cb, &gdl); for (int i = 0; i < gdl.gdl_nfilts; i++) { if (!gdl.gdl_used[i]) { warnx("filter '%s' did not match any DPIOs", gdl.gdl_filts[i]); gdl.gdl_err = true; } } if (gdl.gdl_nprint == 0) { /* * We only bother to warn about no DPIOs being found when there * are no filters as otherwise the user would have gotten a * message about unmatched filters just above. */ if (gdl.gdl_nfilts == 0) { warnx("no DPIOs found"); } gdl.gdl_err = true; } return (gdl.gdl_err ? EXIT_FAILURE : EXIT_SUCCESS); } static void gpioadm_dpio_define_usage(FILE *f) { (void) fprintf(f, "\tgpioadm dpio define [-r] [-w] [-K] " "controller/gpio name\n"); } static void __PRINTFLIKE(1) gpioadm_dpio_define_help(const char *fmt, ...) { if (fmt != NULL) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } (void) fprintf(stderr, "Usage: gpioadm dpio define [-r] [-w] [-K] " "controller/gpio name\n"); (void) fprintf(stderr, "\nCreate a new DPIO from the specified GPIO\n\n" "\t-r\t\tthe DPIO is allowed to read the GPIO input value\n" "\t-w\t\tthe DPIO is allowed to set the GPIO output value\n" "\t-K\t\tthe DPIO should only be accessible from the kernel\n"); } static int gpioadm_dpio_define(int argc, char *argv[]) { int c; xpio_ctrl_t *ctrl; xpio_gpio_info_t *gpio; xpio_dpio_features_t feats = 0; while ((c = getopt(argc, argv, ":rwK")) != -1) { switch (c) { case 'r': feats |= XPIO_DPIO_F_READ; break; case 'K': feats |= XPIO_DPIO_F_KERNEL; break; case 'w': feats |= XPIO_DPIO_F_WRITE; break; case ':': gpioadm_dpio_define_help("option -%c requires an " "argument", optopt); exit(EXIT_USAGE); case '?': gpioadm_dpio_define_help("unknown option: -%c", optopt); exit(EXIT_USAGE); } } argc -= optind; argv += optind; if (argc == 0) { errx(EXIT_FAILURE, "missing required gpio specifier and dpio " "name"); } else if (argc == 1) { errx(EXIT_FAILURE, "missing required dpio name"); } else if (argc > 2) { errx(EXIT_FAILURE, "encountered extraneous arguments beginning " "with '%s'", argv[2]); } gpioadm_ctrl_gpio_init(argv[0], &ctrl, &gpio); if (!xpio_dpio_create(ctrl, gpio, argv[1], feats)) { gpioadm_fatal("failed to create dpio %s from gpio %s", argv[1], argv[0]); } return (EXIT_SUCCESS); } static void gpioadm_dpio_undefine_usage(FILE *f) { (void) fprintf(f, "\tgpioadm dpio undefine controller/gpio\n"); } static void __PRINTFLIKE(1) gpioadm_dpio_undefine_help(const char *fmt, ...) { if (fmt != NULL) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } (void) fprintf(stderr, "Usage: gpioadm dpio undefine " "controller/gpio\n"); (void) fprintf(stderr, "\nRemove a DPIO from the system backed by the " "specified controller and GPIO\n"); } static int gpioadm_dpio_undefine(int argc, char *argv[]) { int c; xpio_ctrl_t *ctrl; xpio_gpio_info_t *gpio; while ((c = getopt(argc, argv, ":")) != -1) { switch (c) { case ':': gpioadm_dpio_undefine_help("option -%c requires an " "argument", optopt); exit(EXIT_USAGE); case '?': gpioadm_dpio_undefine_help("unknown option: -%c", optopt); exit(EXIT_USAGE); } } argc -= optind; argv += optind; if (argc < 1) { errx(EXIT_FAILURE, "missing required gpio specifier"); } else if (argc > 1) { errx(EXIT_FAILURE, "encountered extraneous arguments beginning " "with '%s'", argv[1]); } gpioadm_ctrl_gpio_init(argv[0], &ctrl, &gpio); if (!xpio_dpio_destroy(ctrl, gpio)) { gpioadm_fatal("failed to release gpio %s from being a DPIO", argv[0]); } return (EXIT_SUCCESS); } static const gpioadm_cmdtab_t gpioadm_cmds_dpio[] = { { "list", gpioadm_dpio_list, gpioadm_dpio_list_usage }, { "define", gpioadm_dpio_define, gpioadm_dpio_define_usage }, { "undefine", gpioadm_dpio_undefine, gpioadm_dpio_undefine_usage }, { NULL, NULL, NULL } }; int gpioadm_dpio(int argc, char *argv[]) { return (gpioadm_walk_tab(gpioadm_cmds_dpio, argc, argv)); } void gpioadm_dpio_usage(FILE *f) { gpioadm_walk_usage(gpioadm_cmds_dpio, f); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2022 Oxide Computer Company */ #include #include #include #include #include #include #include #include #include "gpioadm.h" static void gpioadm_gpio_attr_get_usage(FILE *f) { (void) fprintf(f, "\tgpioadm gpio attr get [-H] [-o field[,...] [-p]] " "controller/gpio [filter...]\n"); } static void __PRINTFLIKE(1) gpioadm_gpio_attr_get_help(const char *fmt, ...) { if (fmt != NULL) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } (void) fprintf(stderr, "Usage: gpioadm gpio attr get [-H] " "[-o field[,...] [-p]] controller/gpio\n\t\t\t [filter...]\n"); (void) fprintf(stderr, "\nList attributes of a specific GPIO\n\n" "\t-H\t\tomit the column header\n" "\t-o field\toutput fields to print\n" "\t-p\t\tparsable output (requires -o)\n\n" "The following fields are supported:\n" "\tattr\t\tthe name of the attribute\n" "\tvalue\t\tthe human-readable value of the attribute\n" "\traw\t\tan untranslated value of the attribute (e.g. " "underlying\n\t\t\tenum)\n" "\tperm\t\tthe permissions of the attribute\n" "\tpossible\tthe possible values the attribute may take\n\n" "Supported filters are the names of attributes. An attribute " "will be printed\nas long as it matches a single filter (they " "function as an OR). If any\nfilter does not match, then a non-" "zero exit status is returned.\n"); } typedef enum gpioadm_gpio_attr_get_otype { GPIOADM_GPIO_ATTR_GET_ATTR, GPIOADM_GPIO_ATTR_GET_VALUE, GPIOADM_GPIO_ATTR_GET_RAW, GPIOADM_GPIO_ATTR_GET_PERM, GPIOADM_GPIO_ATTR_GET_POSSIBLE, } gpioadm_gpio_attr_get_otype_t; typedef struct gpioadm_gpio_attr_get_ofmt { xpio_gpio_info_t *ggag_info; xpio_gpio_attr_t *ggag_attr; } gpioadm_gpio_attr_get_ofmt_t; static boolean_t gpioadm_gpio_attr_get_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen) { const char *str; uint32_t u32; uint32_t *u32_arr; const char **str_arr; uint_t count; uintptr_t off = 0; gpioadm_gpio_attr_get_ofmt_t *ggag = ofarg->ofmt_cbarg; xpio_gpio_info_t *info = ggag->ggag_info; xpio_gpio_attr_t *attr = ggag->ggag_attr; switch (ofarg->ofmt_id) { case GPIOADM_GPIO_ATTR_GET_ATTR: if (strlcpy(buf, xpio_gpio_attr_name(info, attr), buflen) >= buflen) { return (B_FALSE); } break; case GPIOADM_GPIO_ATTR_GET_VALUE: switch (xpio_gpio_attr_type(info, attr)) { case XPIO_ATTR_TYPE_STRING: if (!xpio_gpio_attr_value_string(attr, &str)) { return (B_FALSE); } if (strlcpy(buf, str, buflen) >= buflen) { return (B_FALSE); } break; case XPIO_ATTR_TYPE_UINT32: if (!xpio_gpio_attr_xlate_to_str(info, attr, buf, buflen)) { return (B_FALSE); } break; } break; case GPIOADM_GPIO_ATTR_GET_RAW: switch (xpio_gpio_attr_type(info, attr)) { case XPIO_ATTR_TYPE_STRING: if (!xpio_gpio_attr_value_string(attr, &str)) { return (B_FALSE); } if (strlcpy(buf, str, buflen) >= buflen) { return (B_FALSE); } break; case XPIO_ATTR_TYPE_UINT32: if (!xpio_gpio_attr_value_uint32(attr, &u32)) { return (B_FALSE); } if (snprintf(buf, buflen, "0x%x", u32) >= buflen) { return (B_FALSE); } break; } break; case GPIOADM_GPIO_ATTR_GET_PERM: switch (xpio_gpio_attr_prot(info, attr)) { case XPIO_ATTR_PROT_RO: if (strlcpy(buf, "r-", buflen) >= buflen) { return (B_FALSE); } break; case XPIO_ATTR_PROT_RW: if (strlcpy(buf, "rw", buflen) >= buflen) { return (B_FALSE); } break; } break; case GPIOADM_GPIO_ATTR_GET_POSSIBLE: switch (xpio_gpio_attr_type(info, attr)) { case XPIO_ATTR_TYPE_STRING: xpio_gpio_attr_possible_string(info, attr, &str_arr, &count); for (uint_t i = 0; i < count; i++) { int len = snprintf(buf + off, buflen - off, "%s%s", i > 0 ? "," : "", str_arr[i]); if (len >= (buflen - off)) { return (B_FALSE); } off += len; } break; case XPIO_ATTR_TYPE_UINT32: xpio_gpio_attr_possible_uint32(info, attr, &u32_arr, &count); for (uint_t i = 0; i < count; i++) { char xlate[512]; if (!xpio_gpio_attr_xlate_uint32_to_str(info, attr, u32_arr[i], xlate, sizeof (xlate))) { return (B_FALSE); } int len = snprintf(buf + off, buflen - off, "%s%s", i > 0 ? "," : "", xlate); if (len >= (buflen - off)) { return (B_FALSE); } off += len; } break; } break; default: abort(); } return (B_TRUE); } static const char *gpioadm_gpio_attr_get_fields = "attr,perm,value,possible"; static const ofmt_field_t gpioadm_gpio_attr_get_ofmt[] = { { "ATTR", 22, GPIOADM_GPIO_ATTR_GET_ATTR, gpioadm_gpio_attr_get_ofmt_cb }, { "PERM", 6, GPIOADM_GPIO_ATTR_GET_PERM, gpioadm_gpio_attr_get_ofmt_cb }, { "VALUE", 24, GPIOADM_GPIO_ATTR_GET_VALUE, gpioadm_gpio_attr_get_ofmt_cb }, { "RAW", 16, GPIOADM_GPIO_ATTR_GET_RAW, gpioadm_gpio_attr_get_ofmt_cb }, { "POSSIBLE", 24, GPIOADM_GPIO_ATTR_GET_POSSIBLE, gpioadm_gpio_attr_get_ofmt_cb }, { NULL, 0, 0, NULL } }; static int gpioadm_gpio_attr_get(int argc, char *argv[]) { int c, ret; uint_t flags = 0; boolean_t parse = B_FALSE; const char *fields = NULL, *target = NULL; ofmt_status_t oferr; ofmt_handle_t ofmt; xpio_ctrl_t *ctrl; xpio_gpio_info_t *gpio; bool *filts = NULL; while ((c = getopt(argc, argv, ":Ho:p")) != -1) { switch (c) { case 'H': flags |= OFMT_NOHEADER; break; case 'o': fields = optarg; break; case 'p': parse = B_TRUE; flags |= OFMT_PARSABLE; break; case ':': gpioadm_gpio_attr_get_help("option -%c requires an " "argument", optopt); exit(EXIT_USAGE); case '?': gpioadm_gpio_attr_get_help("unknown option: -%c", optopt); exit(EXIT_USAGE); } } if (parse && fields == NULL) { errx(EXIT_USAGE, "-p requires fields specified with -o"); } if (!parse) { flags |= OFMT_WRAP; } if (fields == NULL) { fields = gpioadm_gpio_attr_get_fields; } argc -= optind; argv += optind; if (argc == 0) { errx(EXIT_FAILURE, "missing required controller and gpio"); } target = argv[0]; argc--; argv++; if (argc > 0) { filts = calloc(argc, sizeof (bool)); if (filts == NULL) { err(EXIT_FAILURE, "failed to allocate memory for " "filter tracking"); } } oferr = ofmt_open(fields, gpioadm_gpio_attr_get_ofmt, flags, 0, &ofmt); ofmt_check(oferr, parse, ofmt, gpioadm_ofmt_errx, warnx); gpioadm_ctrl_gpio_init(target, &ctrl, &gpio); for (xpio_gpio_attr_t *attr = xpio_gpio_attr_next(gpio, NULL); attr != NULL; attr = xpio_gpio_attr_next(gpio, attr)) { gpioadm_gpio_attr_get_ofmt_t ggag; if (argc > 0) { const char *aname = xpio_gpio_attr_name(gpio, attr); bool match = false; for (int i = 0; i < argc; i++) { if (strcmp(argv[i], aname) == 0) { match = true; filts[i] = true; } } if (!match) { continue; } } ggag.ggag_info = gpio; ggag.ggag_attr = attr; ofmt_print(ofmt, &ggag); } ret = EXIT_SUCCESS; for (int i = 0; i < argc; i++) { if (!filts[i]) { warnx("filter '%s' did not match any attributes", argv[i]); ret = EXIT_FAILURE; } } free(filts); return (ret); } static void gpioadm_gpio_attr_set_usage(FILE *f) { (void) fprintf(f, "\tgpioadm gpio attr set controller/gpio attr=value " "[attr=value...]\n"); } static void __PRINTFLIKE(1) gpioadm_gpio_attr_set_help(const char *fmt, ...) { if (fmt != NULL) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } (void) fprintf(stderr, "Usage: gpioadm gpio attr set controller/gpio " "attr=value [attr=value...]\n"); (void) fprintf(stderr, "\nSets the attributes of a single GPIO. " "All specified attributes are\napplied at once.\n"); } static int gpioadm_gpio_attr_set(int argc, char *argv[]) { int c; const char *target; xpio_ctrl_t *ctrl; xpio_gpio_info_t *gpio; xpio_gpio_update_t *update; while ((c = getopt(argc, argv, ":")) != -1) { switch (c) { case ':': gpioadm_gpio_attr_set_help("option -%c requires an " "argument", optopt); exit(EXIT_USAGE); case '?': gpioadm_gpio_attr_set_help("unknown option: -%c", optopt); exit(EXIT_USAGE); } } argc -= optind; argv += optind; if (argc == 0) { errx(EXIT_USAGE, "missing required controller/gpio target"); } if (argc == 1) { errx(EXIT_USAGE, "missing required attribute settings"); } target = argv[0]; gpioadm_ctrl_gpio_init(target, &ctrl, &gpio); if (!xpio_gpio_update_init(gpioadm.gpio_xpio, gpio, &update)) { gpioadm_fatal("failed to initialize update"); } for (int i = 1; i < argc; i++) { char *eq = strchr(argv[i], '='); const char *name, *value; xpio_gpio_attr_t *attr; if (eq == NULL) { errx(EXIT_FAILURE, "invalid attribute: missing equals " "sign for value: %s", argv[i]); } name = argv[i]; value = eq + 1; *eq = '\0'; attr = xpio_gpio_attr_find(gpio, name); if (attr == NULL) { errx(EXIT_FAILURE, "invalid attribute: no attribute " "named %s exists for GPIO %s", name, target); } if (!xpio_gpio_attr_from_str(update, attr, value)) { gpioadm_update_fatal(update, "failed to set attribute " "%s to %s on GPIO %s", name, value, target); } } if (!xpio_gpio_update(ctrl, update)) { if (xpio_err(gpioadm.gpio_xpio) != XPIO_ERR_BAD_UPDATE) { gpioadm_fatal("failed to update GPIO %s", target); } gpioadm_warn("failed to update GPIO %s", target); for (xpio_gpio_attr_err_t *err = xpio_gpio_attr_err_next(update, NULL); err != NULL; err = xpio_gpio_attr_err_next(update, err)) { xpio_update_err_t uerr = xpio_gpio_attr_err_err(err); (void) fprintf(stderr, "\tattribute %s -- %s (0x%x)\n", xpio_gpio_attr_err_name(err), xpio_update_err2str(update, uerr), uerr); } } return (EXIT_SUCCESS); } static const gpioadm_cmdtab_t gpioadm_cmds_gpio_attr[] = { { "get", gpioadm_gpio_attr_get, gpioadm_gpio_attr_get_usage }, { "set", gpioadm_gpio_attr_set, gpioadm_gpio_attr_set_usage }, { NULL, NULL, NULL } }; static void gpioadm_gpio_attr_usage(FILE *f) { gpioadm_walk_usage(gpioadm_cmds_gpio_attr, f); } static int gpioadm_gpio_attr(int argc, char *argv[]) { return (gpioadm_walk_tab(gpioadm_cmds_gpio_attr, argc, argv)); } static void gpioadm_gpio_list_usage(FILE *f) { (void) fprintf(f, "\tgpioadm gpio list [-H] [-o field[,...] [-p]] " "[-1] [filter...]\n"); } static void __PRINTFLIKE(1) gpioadm_gpio_list_help(const char *fmt, ...) { if (fmt != NULL) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } (void) fprintf(stderr, "Usage: gpioadm gpio list [-H] [-o " "field[,...] [-p]] [-1] [filter...]\n"); (void) fprintf(stderr, "\nList GPIOs in the system.\n\n" "\t-H\t\tomit the column header\n" "\t-o field\toutput fields to print\n" "\t-p\t\tparsable output (requires -o)\n" "\t-1\t\terror if more than one GPIO is listed\n\n" "The following fields are supported:\n" "\tcontroller\tthe name of the controller\n" "\tgpio\t\tthe name of the gpio\n" "\tid\t\tthe GPIO's numeric id\n" "Filters can be used to constrain the GPIOs that are listed. If a " "filter is\npresent, it will be an error if it is unused. Filters " "can specify either an\nentire controller, a specific GPIO on a " "controller, or all GPIOs with a given\nname. The controller and " "GPIO are separated with a '/' character. For example:\n\n" "\tgpio_sim0\t\tThis would match all GPIOs on the controller\n" "\t\t\t\t'gpio_sim0'.\n" "\tzen_gpio0/EGPIO9_3\tThis would match the specific GPIO, " "EGPIO9_3,\n\t\t\t\ton the specified controller, zen_gpio0.\n" "\t*/gpio3\t\t\tThis would match all GPIOs named 'gpio3' on any\n" "\t\t\t\tcontroller.\n"); } typedef enum gpioadm_gpio_list_otype { GPIOADM_GPIO_LIST_CTRL, GPIOADM_GPIO_LIST_NAME, GPIOADM_GPIO_LIST_ID } gpioadm_gpio_list_otype_t; typedef struct gpioadm_gpio_list_ofmt { const char *gglo_minor; const char *gglo_name; uint32_t gglo_id; uint32_t gglo_flags; } gpioadm_gpio_list_ofmt_t; static boolean_t gpioadm_gpio_list_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen) { gpioadm_gpio_list_ofmt_t *gglo = ofarg->ofmt_cbarg; switch (ofarg->ofmt_id) { case GPIOADM_GPIO_LIST_CTRL: if (strlcpy(buf, gglo->gglo_minor, buflen) >= buflen) { return (B_FALSE); } break; case GPIOADM_GPIO_LIST_NAME: if (strlcpy(buf, gglo->gglo_name, buflen) >= buflen) { return (B_FALSE); } break; case GPIOADM_GPIO_LIST_ID: if (snprintf(buf, buflen, "%u", gglo->gglo_id) >= buflen) { return (B_FALSE); } break; default: abort(); } return (B_TRUE); } static const char *gpioadm_gpio_list_fields = "controller,gpio,id"; static const ofmt_field_t gpioadm_gpio_list_ofmt[] = { { "CONTROLLER", 16, GPIOADM_GPIO_LIST_CTRL, gpioadm_gpio_list_ofmt_cb }, { "GPIO", 20, GPIOADM_GPIO_LIST_NAME, gpioadm_gpio_list_ofmt_cb }, { "ID", 8, GPIOADM_GPIO_LIST_ID, gpioadm_gpio_list_ofmt_cb }, { NULL, 0, 0, NULL } }; typedef struct { bool ggl_err; bool ggl_one; uint_t ggl_nprint; ofmt_handle_t ggl_ofmt; int ggl_nfilts; char *const *ggl_filts; bool *ggl_used; } gpioadm_gpio_list_t; static bool gpioadm_gpio_list_match(const char *ctrl, const char *gpio, gpioadm_gpio_list_t *ggl) { if (ggl->ggl_nfilts <= 0) { return (true); } for (int i = 0; i < ggl->ggl_nfilts; i++) { const char *filt = ggl->ggl_filts[i]; const char *slash = strchr(filt, '/'); bool all_ctrls; size_t ctrl_len; /* * This is just a controller filter. */ if (slash == NULL) { if (strcmp(ctrl, filt) == 0) { ggl->ggl_used[i] = true; return (true); } } ctrl_len = (uintptr_t)slash - (uintptr_t)filt; if (ctrl_len == 0) { return (false); } all_ctrls = ctrl_len == 1 && filt[0] == '*'; if (!all_ctrls && (strlen(ctrl) != ctrl_len || strncmp(ctrl, filt, ctrl_len) != 0)) { continue; } if (strcmp(slash + 1, gpio) == 0) { ggl->ggl_used[i] = true; return (true); } } return (false); } static bool gpioadm_gpio_list_cb(xpio_t *xpio, xpio_ctrl_disc_t *disc, void *arg) { xpio_ctrl_t *ctrl; xpio_ctrl_info_t *info; uint32_t ngpios; const char *mname = di_minor_name(disc->xcd_minor); gpioadm_gpio_list_t *ggl = arg; if (!xpio_ctrl_init(xpio, disc->xcd_minor, &ctrl)) { gpioadm_warn("failed to initialize controller %s", mname); ggl->ggl_err = true; return (true); } if (!xpio_ctrl_info(ctrl, &info)) { gpioadm_warn("failed to get controller info for %s", mname); xpio_ctrl_fini(ctrl); ggl->ggl_err = true; return (true); } ngpios = xpio_ctrl_info_ngpios(info); for (uint32_t i = 0; i < ngpios; i++) { gpioadm_gpio_list_ofmt_t list; xpio_gpio_info_t *gpio_info; xpio_gpio_attr_t *attr; if (!xpio_gpio_info(ctrl, i, &gpio_info)) { ggl->ggl_err = true; gpioadm_warn("failed to get gpio info for %s:%u", mname, i); continue; } attr = xpio_gpio_attr_find(gpio_info, KGPIO_ATTR_NAME); if (attr == NULL || !xpio_gpio_attr_value_string(attr, &list.gglo_name)) { warnx("GPIO %s/%u missing name attribute", mname, i); goto skip; } list.gglo_minor = mname; list.gglo_id = i; list.gglo_flags = 0; if (!gpioadm_gpio_list_match(mname, list.gglo_name, ggl)) { goto skip; } ggl->ggl_nprint++; ofmt_print(ggl->ggl_ofmt, &list); skip: xpio_gpio_info_free(gpio_info); } xpio_ctrl_fini(ctrl); return (true); } static int gpioadm_gpio_list(int argc, char *argv[]) { int c; uint_t flags = 0; boolean_t parse = B_FALSE; const char *fields = NULL; ofmt_status_t oferr; ofmt_handle_t ofmt; gpioadm_gpio_list_t ggl; (void) memset(&ggl, 0, sizeof (ggl)); while ((c = getopt(argc, argv, ":Ho:p1")) != -1) { switch (c) { case 'H': flags |= OFMT_NOHEADER; break; case 'o': fields = optarg; break; case 'p': parse = B_TRUE; flags |= OFMT_PARSABLE; break; case '1': ggl.ggl_one = true; break; case ':': gpioadm_gpio_list_help("option -%c requires an " "argument", optopt); exit(EXIT_USAGE); case '?': gpioadm_gpio_list_help("unknown option: -%c", optopt); exit(EXIT_USAGE); } } if (parse && fields == NULL) { errx(EXIT_USAGE, "-p requires fields specified with -o"); } if (fields == NULL) { fields = gpioadm_gpio_list_fields; } argc -= optind; argv += optind; if (argc > 0) { ggl.ggl_nfilts = argc; ggl.ggl_filts = argv; ggl.ggl_used = calloc(argc, sizeof (bool)); if (ggl.ggl_used == NULL) { err(EXIT_FAILURE, "failed to allocate memory for " "filter tracking"); } } oferr = ofmt_open(fields, gpioadm_gpio_list_ofmt, flags, 0, &ofmt); ofmt_check(oferr, parse, ofmt, gpioadm_ofmt_errx, warnx); ggl.ggl_err = false; ggl.ggl_ofmt = ofmt; xpio_ctrl_discover(gpioadm.gpio_xpio, gpioadm_gpio_list_cb, &ggl); for (int i = 0; i < ggl.ggl_nfilts; i++) { if (!ggl.ggl_used[i]) { warnx("filter '%s' did not match any GPIOs", ggl.ggl_filts[i]); ggl.ggl_err = true; } } if (ggl.ggl_one && ggl.ggl_nprint > 1) { warnx("-1 specified, but %u GPIOs printed", ggl.ggl_nprint); ggl.ggl_err = true; } if (ggl.ggl_nprint == 0) { if (ggl.ggl_nfilts == 0) { warnx("no GPIOs found"); } ggl.ggl_err = true; } return (ggl.ggl_err ? EXIT_FAILURE : EXIT_SUCCESS); } static const gpioadm_cmdtab_t gpioadm_cmds_gpio[] = { { "attr", gpioadm_gpio_attr, gpioadm_gpio_attr_usage }, { "list", gpioadm_gpio_list, gpioadm_gpio_list_usage }, { NULL, NULL, NULL } }; int gpioadm_gpio(int argc, char *argv[]) { return (gpioadm_walk_tab(gpioadm_cmds_gpio, argc, argv)); } void gpioadm_gpio_usage(FILE *f) { gpioadm_walk_usage(gpioadm_cmds_gpio, f); }