xref: /illumos-gate/usr/src/cmd/i2cadm/i2cadm_mux.c (revision 32002227574cf0a435dc03de622191ca53724f0a)
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