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