1*32002227SRobert Mustacchi /*
2*32002227SRobert Mustacchi * This file and its contents are supplied under the terms of the
3*32002227SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4*32002227SRobert Mustacchi * You may only use this file in accordance with the terms of version
5*32002227SRobert Mustacchi * 1.0 of the CDDL.
6*32002227SRobert Mustacchi *
7*32002227SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8*32002227SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9*32002227SRobert Mustacchi * http://www.illumos.org/license/CDDL.
10*32002227SRobert Mustacchi */
11*32002227SRobert Mustacchi
12*32002227SRobert Mustacchi /*
13*32002227SRobert Mustacchi * Copyright 2025 Oxide Computer Company
14*32002227SRobert Mustacchi */
15*32002227SRobert Mustacchi
16*32002227SRobert Mustacchi /*
17*32002227SRobert Mustacchi * i2cadm mux related operations.
18*32002227SRobert Mustacchi */
19*32002227SRobert Mustacchi
20*32002227SRobert Mustacchi #include <stdarg.h>
21*32002227SRobert Mustacchi #include <string.h>
22*32002227SRobert Mustacchi #include <err.h>
23*32002227SRobert Mustacchi #include <sys/sysmacros.h>
24*32002227SRobert Mustacchi #include <ofmt.h>
25*32002227SRobert Mustacchi
26*32002227SRobert Mustacchi #include "i2cadm.h"
27*32002227SRobert Mustacchi
28*32002227SRobert Mustacchi static void
i2cadm_mux_list_usage(FILE * f)29*32002227SRobert Mustacchi i2cadm_mux_list_usage(FILE *f)
30*32002227SRobert Mustacchi {
31*32002227SRobert Mustacchi (void) fprintf(f, "\ti2cadm mux list [-H] [-o field,[...] [-p]] "
32*32002227SRobert Mustacchi "[filter]\n");
33*32002227SRobert Mustacchi }
34*32002227SRobert Mustacchi
35*32002227SRobert Mustacchi static void
i2cadm_mux_list_help(const char * fmt,...)36*32002227SRobert Mustacchi i2cadm_mux_list_help(const char *fmt, ...)
37*32002227SRobert Mustacchi {
38*32002227SRobert Mustacchi if (fmt != NULL) {
39*32002227SRobert Mustacchi va_list ap;
40*32002227SRobert Mustacchi
41*32002227SRobert Mustacchi va_start(ap, fmt);
42*32002227SRobert Mustacchi vwarnx(fmt, ap);
43*32002227SRobert Mustacchi va_end(ap);
44*32002227SRobert Mustacchi }
45*32002227SRobert Mustacchi
46*32002227SRobert Mustacchi (void) fprintf(stderr, "Usage: i2cadm mux list [-H] "
47*32002227SRobert Mustacchi "[-o field[,...] [-p]] [filter...]\n\n");
48*32002227SRobert Mustacchi (void) fprintf(stderr, "List multiplexors in the system. Each <filter> "
49*32002227SRobert Mustacchi "selects a multiplexor based " "on\nits device's name, device "
50*32002227SRobert Mustacchi "driver, or the mux's name. When multiple filters are\nspecified, "
51*32002227SRobert Mustacchi "they are treated like an OR. It is an error if a filter isn't "
52*32002227SRobert Mustacchi "used.\n\n"
53*32002227SRobert Mustacchi "\t-H\t\tomit the column header\n"
54*32002227SRobert Mustacchi "\t-o field\toutput fields to print\n"
55*32002227SRobert Mustacchi "\t-p\t\tparseable output (requires -o)\n");
56*32002227SRobert Mustacchi (void) fprintf(stderr, "\nThe following fields are supported:\n"
57*32002227SRobert Mustacchi "\tdevice\t\tthe name of the device that powers the mux\n"
58*32002227SRobert Mustacchi "\tnports\t\tthe number of ports on the mux\n"
59*32002227SRobert Mustacchi "\tname\t\tthe name of the mux\n"
60*32002227SRobert Mustacchi "\tinstance\tthe instance of the driver for the mux\n"
61*32002227SRobert Mustacchi "\tpath\t\tthe I2C path of the mux\n");
62*32002227SRobert Mustacchi }
63*32002227SRobert Mustacchi
64*32002227SRobert Mustacchi typedef enum {
65*32002227SRobert Mustacchi I2CADM_MUX_LIST_DEVICE,
66*32002227SRobert Mustacchi I2CADM_MUX_LIST_NAME,
67*32002227SRobert Mustacchi I2CADM_MUX_LIST_NPORTS,
68*32002227SRobert Mustacchi I2CADM_MUX_LIST_INSTANCE,
69*32002227SRobert Mustacchi I2CADM_MUX_LIST_PATH
70*32002227SRobert Mustacchi } i2cadm_mux_list_otype_t;
71*32002227SRobert Mustacchi
72*32002227SRobert Mustacchi static boolean_t
i2cadm_mux_list_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)73*32002227SRobert Mustacchi i2cadm_mux_list_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
74*32002227SRobert Mustacchi {
75*32002227SRobert Mustacchi const i2c_mux_disc_t *disc = ofarg->ofmt_cbarg;
76*32002227SRobert Mustacchi size_t len;
77*32002227SRobert Mustacchi di_node_t dn;
78*32002227SRobert Mustacchi
79*32002227SRobert Mustacchi switch (ofarg->ofmt_id) {
80*32002227SRobert Mustacchi case I2CADM_MUX_LIST_DEVICE:
81*32002227SRobert Mustacchi dn = di_parent_node(i2c_mux_disc_devi(disc));
82*32002227SRobert Mustacchi len = snprintf(buf, buflen, "%s", di_node_name(dn));
83*32002227SRobert Mustacchi break;
84*32002227SRobert Mustacchi case I2CADM_MUX_LIST_NAME:
85*32002227SRobert Mustacchi len = strlcpy(buf, i2c_mux_disc_name(disc), buflen);
86*32002227SRobert Mustacchi break;
87*32002227SRobert Mustacchi case I2CADM_MUX_LIST_NPORTS:
88*32002227SRobert Mustacchi len = snprintf(buf, buflen, "%u", i2c_mux_disc_nports(disc));
89*32002227SRobert Mustacchi break;
90*32002227SRobert Mustacchi case I2CADM_MUX_LIST_INSTANCE:
91*32002227SRobert Mustacchi /*
92*32002227SRobert Mustacchi * Because a mux exists here, we know our parent instance must
93*32002227SRobert Mustacchi * be active and attached.
94*32002227SRobert Mustacchi */
95*32002227SRobert Mustacchi dn = di_parent_node(i2c_mux_disc_devi(disc));
96*32002227SRobert Mustacchi len = snprintf(buf, buflen, "%s%d", di_driver_name(dn),
97*32002227SRobert Mustacchi di_instance(dn));
98*32002227SRobert Mustacchi break;
99*32002227SRobert Mustacchi case I2CADM_MUX_LIST_PATH:
100*32002227SRobert Mustacchi len = strlcpy(buf, i2c_mux_disc_path(disc), buflen);
101*32002227SRobert Mustacchi break;
102*32002227SRobert Mustacchi default:
103*32002227SRobert Mustacchi return (B_FALSE);
104*32002227SRobert Mustacchi }
105*32002227SRobert Mustacchi
106*32002227SRobert Mustacchi return (len < buflen);
107*32002227SRobert Mustacchi }
108*32002227SRobert Mustacchi
109*32002227SRobert Mustacchi static const char *i2cadm_mux_list_fields = "device,nports,name,instance,path";
110*32002227SRobert Mustacchi static const ofmt_field_t i2cadm_mux_list_ofmt[] = {
111*32002227SRobert Mustacchi { "DEVICE", 12, I2CADM_MUX_LIST_DEVICE, i2cadm_mux_list_ofmt_cb },
112*32002227SRobert Mustacchi { "NAME", 12, I2CADM_MUX_LIST_NAME, i2cadm_mux_list_ofmt_cb },
113*32002227SRobert Mustacchi { "NPORTS", 12, I2CADM_MUX_LIST_NPORTS, i2cadm_mux_list_ofmt_cb },
114*32002227SRobert Mustacchi { "INSTANCE", 16, I2CADM_MUX_LIST_INSTANCE, i2cadm_mux_list_ofmt_cb },
115*32002227SRobert Mustacchi { "PATH", 40, I2CADM_MUX_LIST_PATH, i2cadm_mux_list_ofmt_cb },
116*32002227SRobert Mustacchi { NULL, 0, 0, NULL }
117*32002227SRobert Mustacchi };
118*32002227SRobert Mustacchi
119*32002227SRobert Mustacchi static int
i2cadm_mux_list(int argc,char * argv[])120*32002227SRobert Mustacchi i2cadm_mux_list(int argc, char *argv[])
121*32002227SRobert Mustacchi {
122*32002227SRobert Mustacchi int c, ret = EXIT_SUCCESS;
123*32002227SRobert Mustacchi uint_t flags = 0;
124*32002227SRobert Mustacchi boolean_t parse = B_FALSE;
125*32002227SRobert Mustacchi const char *fields = NULL;
126*32002227SRobert Mustacchi bool *filts = NULL, print = false;
127*32002227SRobert Mustacchi ofmt_status_t oferr;
128*32002227SRobert Mustacchi ofmt_handle_t ofmt;
129*32002227SRobert Mustacchi const i2c_mux_disc_t *disc;
130*32002227SRobert Mustacchi i2c_mux_iter_t *iter;
131*32002227SRobert Mustacchi i2c_iter_t iret;
132*32002227SRobert Mustacchi
133*32002227SRobert Mustacchi while ((c = getopt(argc, argv, ":Ho:p")) != -1) {
134*32002227SRobert Mustacchi switch (c) {
135*32002227SRobert Mustacchi case 'H':
136*32002227SRobert Mustacchi flags |= OFMT_NOHEADER;
137*32002227SRobert Mustacchi break;
138*32002227SRobert Mustacchi case 'o':
139*32002227SRobert Mustacchi fields = optarg;
140*32002227SRobert Mustacchi break;
141*32002227SRobert Mustacchi case 'p':
142*32002227SRobert Mustacchi parse = B_TRUE;
143*32002227SRobert Mustacchi flags |= OFMT_PARSABLE;
144*32002227SRobert Mustacchi break;
145*32002227SRobert Mustacchi case ':':
146*32002227SRobert Mustacchi i2cadm_mux_list_help("option -%c requires an "
147*32002227SRobert Mustacchi "argument", optopt);
148*32002227SRobert Mustacchi exit(EXIT_USAGE);
149*32002227SRobert Mustacchi case '?':
150*32002227SRobert Mustacchi i2cadm_mux_list_help("unknown option: -%c",
151*32002227SRobert Mustacchi optopt);
152*32002227SRobert Mustacchi exit(EXIT_USAGE);
153*32002227SRobert Mustacchi }
154*32002227SRobert Mustacchi }
155*32002227SRobert Mustacchi
156*32002227SRobert Mustacchi if (parse && fields == NULL) {
157*32002227SRobert Mustacchi errx(EXIT_USAGE, "-p requires fields specified with -o");
158*32002227SRobert Mustacchi }
159*32002227SRobert Mustacchi
160*32002227SRobert Mustacchi if (!parse) {
161*32002227SRobert Mustacchi flags |= OFMT_WRAP;
162*32002227SRobert Mustacchi }
163*32002227SRobert Mustacchi
164*32002227SRobert Mustacchi if (fields == NULL) {
165*32002227SRobert Mustacchi fields = i2cadm_mux_list_fields;
166*32002227SRobert Mustacchi }
167*32002227SRobert Mustacchi
168*32002227SRobert Mustacchi argc -= optind;
169*32002227SRobert Mustacchi argv += optind;
170*32002227SRobert Mustacchi
171*32002227SRobert Mustacchi if (argc > 0) {
172*32002227SRobert Mustacchi filts = calloc(argc, sizeof (bool));
173*32002227SRobert Mustacchi if (filts == NULL) {
174*32002227SRobert Mustacchi err(EXIT_FAILURE, "failed to allocate memory for "
175*32002227SRobert Mustacchi "filter tracking");
176*32002227SRobert Mustacchi }
177*32002227SRobert Mustacchi }
178*32002227SRobert Mustacchi
179*32002227SRobert Mustacchi oferr = ofmt_open(fields, i2cadm_mux_list_ofmt, flags, 0, &ofmt);
180*32002227SRobert Mustacchi ofmt_check(oferr, parse, ofmt, i2cadm_ofmt_errx, warnx);
181*32002227SRobert Mustacchi
182*32002227SRobert Mustacchi
183*32002227SRobert Mustacchi if (!i2c_mux_discover_init(i2cadm.i2c_hdl, &iter)) {
184*32002227SRobert Mustacchi i2cadm_fatal("failed to in initialize mux walk");
185*32002227SRobert Mustacchi }
186*32002227SRobert Mustacchi
187*32002227SRobert Mustacchi while ((iret = i2c_mux_discover_step(iter, &disc)) ==
188*32002227SRobert Mustacchi I2C_ITER_VALID) {
189*32002227SRobert Mustacchi if (argc > 0) {
190*32002227SRobert Mustacchi const char *name = i2c_mux_disc_name(disc);
191*32002227SRobert Mustacchi di_node_t dn = di_parent_node(i2c_mux_disc_devi(disc));
192*32002227SRobert Mustacchi const char *drv = di_driver_name(dn);
193*32002227SRobert Mustacchi const char *pname = di_node_name(dn);
194*32002227SRobert Mustacchi bool match = false;
195*32002227SRobert Mustacchi
196*32002227SRobert Mustacchi for (int i = 0; i < argc; i++) {
197*32002227SRobert Mustacchi if (strcmp(argv[i], name) == 0 ||
198*32002227SRobert Mustacchi strcmp(argv[i], drv) == 0 ||
199*32002227SRobert Mustacchi strcmp(argv[i], pname) == 0) {
200*32002227SRobert Mustacchi match = true;
201*32002227SRobert Mustacchi filts[i] = true;
202*32002227SRobert Mustacchi }
203*32002227SRobert Mustacchi }
204*32002227SRobert Mustacchi
205*32002227SRobert Mustacchi if (!match) {
206*32002227SRobert Mustacchi continue;
207*32002227SRobert Mustacchi }
208*32002227SRobert Mustacchi }
209*32002227SRobert Mustacchi
210*32002227SRobert Mustacchi ofmt_print(ofmt, (void *)disc);
211*32002227SRobert Mustacchi print = true;
212*32002227SRobert Mustacchi }
213*32002227SRobert Mustacchi
214*32002227SRobert Mustacchi if (iret == I2C_ITER_ERROR) {
215*32002227SRobert Mustacchi i2cadm_warn("failed to iterate muxes");
216*32002227SRobert Mustacchi ret = EXIT_FAILURE;
217*32002227SRobert Mustacchi }
218*32002227SRobert Mustacchi
219*32002227SRobert Mustacchi for (int i = 0; i < argc; i++) {
220*32002227SRobert Mustacchi if (!filts[i]) {
221*32002227SRobert Mustacchi warnx("filter '%s' did not match any muxes", argv[i]);
222*32002227SRobert Mustacchi ret = EXIT_FAILURE;
223*32002227SRobert Mustacchi }
224*32002227SRobert Mustacchi }
225*32002227SRobert Mustacchi
226*32002227SRobert Mustacchi if (!print && argc == 0) {
227*32002227SRobert Mustacchi warnx("no I2C muxes found");
228*32002227SRobert Mustacchi ret = EXIT_FAILURE;
229*32002227SRobert Mustacchi }
230*32002227SRobert Mustacchi
231*32002227SRobert Mustacchi free(filts);
232*32002227SRobert Mustacchi ofmt_close(ofmt);
233*32002227SRobert Mustacchi return (ret);
234*32002227SRobert Mustacchi }
235*32002227SRobert Mustacchi
236*32002227SRobert Mustacchi static i2cadm_cmdtab_t i2cadm_mux_cmds[] = {
237*32002227SRobert Mustacchi { "list", i2cadm_mux_list, i2cadm_mux_list_usage },
238*32002227SRobert Mustacchi };
239*32002227SRobert Mustacchi
240*32002227SRobert Mustacchi int
i2cadm_mux(int argc,char * argv[])241*32002227SRobert Mustacchi i2cadm_mux(int argc, char *argv[])
242*32002227SRobert Mustacchi {
243*32002227SRobert Mustacchi return (i2cadm_walk_tab(i2cadm_mux_cmds, ARRAY_SIZE(i2cadm_mux_cmds),
244*32002227SRobert Mustacchi argc, argv));
245*32002227SRobert Mustacchi }
246*32002227SRobert Mustacchi
247*32002227SRobert Mustacchi void
i2cadm_mux_usage(FILE * f)248*32002227SRobert Mustacchi i2cadm_mux_usage(FILE *f)
249*32002227SRobert Mustacchi {
250*32002227SRobert Mustacchi i2cadm_walk_usage(i2cadm_mux_cmds, ARRAY_SIZE(i2cadm_mux_cmds), f);
251*32002227SRobert Mustacchi }
252