xref: /illumos-gate/usr/src/cmd/i2cadm/i2cadm_port.c (revision 32002227574cf0a435dc03de622191ca53724f0a)
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 port 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 #include <sys/debug.h>
26*32002227SRobert Mustacchi 
27*32002227SRobert Mustacchi #include "i2cadm.h"
28*32002227SRobert Mustacchi 
29*32002227SRobert Mustacchi static void
i2cadm_port_map_usage(FILE * f)30*32002227SRobert Mustacchi i2cadm_port_map_usage(FILE *f)
31*32002227SRobert Mustacchi {
32*32002227SRobert Mustacchi 	(void) fprintf(f, "\ti2cadm port map [-o field,[...] [-H] [-p]] "
33*32002227SRobert Mustacchi 	    "<port>\n");
34*32002227SRobert Mustacchi }
35*32002227SRobert Mustacchi 
36*32002227SRobert Mustacchi static void
i2cadm_port_map_help(const char * fmt,...)37*32002227SRobert Mustacchi i2cadm_port_map_help(const char *fmt, ...)
38*32002227SRobert Mustacchi {
39*32002227SRobert Mustacchi 	if (fmt != NULL) {
40*32002227SRobert Mustacchi 		va_list ap;
41*32002227SRobert Mustacchi 
42*32002227SRobert Mustacchi 		va_start(ap, fmt);
43*32002227SRobert Mustacchi 		vwarnx(fmt, ap);
44*32002227SRobert Mustacchi 		va_end(ap);
45*32002227SRobert Mustacchi 	}
46*32002227SRobert Mustacchi 
47*32002227SRobert Mustacchi 	(void) fprintf(stderr, "Usage:  i2cadm port map [-o field,[...] [-H] "
48*32002227SRobert Mustacchi 	    "[-p]] <port>\n");
49*32002227SRobert Mustacchi 	(void) fprintf(stderr, "\nPrint port address usage\n\n"
50*32002227SRobert Mustacchi 	    "\t-H\t\tomit the column header (requires -o)\n"
51*32002227SRobert Mustacchi 	    "\t-o field\toutput fields to print\n"
52*32002227SRobert Mustacchi 	    "\t-p\t\tparseable output (requires -o)\n");
53*32002227SRobert Mustacchi 	(void) fprintf(stderr, "\nThe following fields are supported when "
54*32002227SRobert Mustacchi 	    "using -o:\n"
55*32002227SRobert Mustacchi 	    "\taddr\t\tthe I2C address\n"
56*32002227SRobert Mustacchi 	    "\tcount\t\tthe number of devices using address\n"
57*32002227SRobert Mustacchi 	    "\ttype\t\tdescribes how the address is being used\n"
58*32002227SRobert Mustacchi 	    "\tmajor\t\tthe major number using a shared address\n"
59*32002227SRobert Mustacchi 	    "\tdriver\t\tthe driver name using a shared address\n");
60*32002227SRobert Mustacchi }
61*32002227SRobert Mustacchi 
62*32002227SRobert Mustacchi typedef enum {
63*32002227SRobert Mustacchi 	I2CADM_MAP_TYPE_NONE,
64*32002227SRobert Mustacchi 	I2CADM_MAP_TYPE_LOCAL,
65*32002227SRobert Mustacchi 	I2CADM_MAP_TYPE_DS,
66*32002227SRobert Mustacchi 	I2CADM_MAP_TYPE_SHARED,
67*32002227SRobert Mustacchi 	I2CADM_MAP_TYPE_ERROR
68*32002227SRobert Mustacchi } i2cadm_map_type_t;
69*32002227SRobert Mustacchi 
70*32002227SRobert Mustacchi typedef struct i2cadm_map {
71*32002227SRobert Mustacchi 	i2cadm_map_type_t map_type;
72*32002227SRobert Mustacchi 	uint32_t map_count;
73*32002227SRobert Mustacchi 	major_t map_major;
74*32002227SRobert Mustacchi 	char *map_shared;
75*32002227SRobert Mustacchi } i2cadm_map_t;
76*32002227SRobert Mustacchi 
77*32002227SRobert Mustacchi typedef struct {
78*32002227SRobert Mustacchi 	major_t mn_major;
79*32002227SRobert Mustacchi 	char *mn_name;
80*32002227SRobert Mustacchi } major_to_name_t;
81*32002227SRobert Mustacchi 
82*32002227SRobert Mustacchi int
i2cadm_major_to_name_cb(di_node_t node,void * arg)83*32002227SRobert Mustacchi i2cadm_major_to_name_cb(di_node_t node, void *arg)
84*32002227SRobert Mustacchi {
85*32002227SRobert Mustacchi 	major_to_name_t *m = arg;
86*32002227SRobert Mustacchi 	if (di_driver_major(node) == m->mn_major) {
87*32002227SRobert Mustacchi 		const char *name = di_driver_name(node);
88*32002227SRobert Mustacchi 		if (name != NULL) {
89*32002227SRobert Mustacchi 			m->mn_name = strdup(name);
90*32002227SRobert Mustacchi 			if (m->mn_name == NULL) {
91*32002227SRobert Mustacchi 				err(EXIT_FAILURE, "failed to allocate memory "
92*32002227SRobert Mustacchi 				    "to duplicate driver name for major 0x%x",
93*32002227SRobert Mustacchi 				    m->mn_major);
94*32002227SRobert Mustacchi 			}
95*32002227SRobert Mustacchi 		}
96*32002227SRobert Mustacchi 		return (DI_WALK_TERMINATE);
97*32002227SRobert Mustacchi 	}
98*32002227SRobert Mustacchi 	return (DI_WALK_CONTINUE);
99*32002227SRobert Mustacchi }
100*32002227SRobert Mustacchi 
101*32002227SRobert Mustacchi /*
102*32002227SRobert Mustacchi  * Major number to name, the kind of max power way. While we could maybe parse
103*32002227SRobert Mustacchi  * /etc/name_to_major, which really should be some set of library routines to be
104*32002227SRobert Mustacchi  * honest, we're instead going to just walk a devinfo snapshot until we we find
105*32002227SRobert Mustacchi  * a node with a matching major. The thing is, the node is present and a driver
106*32002227SRobert Mustacchi  * is attached, otherwise it wouldn't have a shared address.
107*32002227SRobert Mustacchi  */
108*32002227SRobert Mustacchi static char *
i2cadm_major_to_name(major_t m)109*32002227SRobert Mustacchi i2cadm_major_to_name(major_t m)
110*32002227SRobert Mustacchi {
111*32002227SRobert Mustacchi 	major_to_name_t arg = { .mn_major = m };
112*32002227SRobert Mustacchi 	di_node_t root = di_init("/", DINFOSUBTREE);
113*32002227SRobert Mustacchi 	if (root == DI_NODE_NIL) {
114*32002227SRobert Mustacchi 		err(EXIT_FAILURE, "failed to take devinfo snapshot");
115*32002227SRobert Mustacchi 	}
116*32002227SRobert Mustacchi 
117*32002227SRobert Mustacchi 	(void) di_walk_node(root, DI_WALK_CLDFIRST, &arg,
118*32002227SRobert Mustacchi 	    i2cadm_major_to_name_cb);
119*32002227SRobert Mustacchi 
120*32002227SRobert Mustacchi 	di_fini(root);
121*32002227SRobert Mustacchi 	return (arg.mn_name);
122*32002227SRobert Mustacchi }
123*32002227SRobert Mustacchi 
124*32002227SRobert Mustacchi typedef enum {
125*32002227SRobert Mustacchi 	I2CADM_PORT_MAP_ADDR,
126*32002227SRobert Mustacchi 	I2CADM_PORT_MAP_COUNT,
127*32002227SRobert Mustacchi 	I2CADM_PORT_MAP_TYPE,
128*32002227SRobert Mustacchi 	I2CADM_PORT_MAP_MAJOR,
129*32002227SRobert Mustacchi 	I2CADM_PORT_MAP_DRIVER
130*32002227SRobert Mustacchi } i2adm_port_map_otype_t;
131*32002227SRobert Mustacchi 
132*32002227SRobert Mustacchi typedef struct {
133*32002227SRobert Mustacchi 	uint16_t ipm_addr;
134*32002227SRobert Mustacchi 	const i2cadm_map_t *ipm_map;
135*32002227SRobert Mustacchi } i2cadm_port_map_ofmt_t;
136*32002227SRobert Mustacchi 
137*32002227SRobert Mustacchi static boolean_t
i2cadm_port_map_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)138*32002227SRobert Mustacchi i2cadm_port_map_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
139*32002227SRobert Mustacchi {
140*32002227SRobert Mustacchi 	i2cadm_port_map_ofmt_t *arg = ofarg->ofmt_cbarg;
141*32002227SRobert Mustacchi 	size_t len;
142*32002227SRobert Mustacchi 	const char *str;
143*32002227SRobert Mustacchi 
144*32002227SRobert Mustacchi 	switch (ofarg->ofmt_id) {
145*32002227SRobert Mustacchi 	case I2CADM_PORT_MAP_ADDR:
146*32002227SRobert Mustacchi 		len = snprintf(buf, buflen, "%u", arg->ipm_addr);
147*32002227SRobert Mustacchi 		break;
148*32002227SRobert Mustacchi 	case I2CADM_PORT_MAP_COUNT:
149*32002227SRobert Mustacchi 		len = snprintf(buf, buflen, "%u", arg->ipm_map->map_count);
150*32002227SRobert Mustacchi 		break;
151*32002227SRobert Mustacchi 	case I2CADM_PORT_MAP_TYPE:
152*32002227SRobert Mustacchi 		switch (arg->ipm_map->map_type) {
153*32002227SRobert Mustacchi 		case I2CADM_MAP_TYPE_NONE:
154*32002227SRobert Mustacchi 			str = "none";
155*32002227SRobert Mustacchi 			break;
156*32002227SRobert Mustacchi 		case I2CADM_MAP_TYPE_LOCAL:
157*32002227SRobert Mustacchi 			str = "local";
158*32002227SRobert Mustacchi 			break;
159*32002227SRobert Mustacchi 		case I2CADM_MAP_TYPE_DS:
160*32002227SRobert Mustacchi 			str = "downstream";
161*32002227SRobert Mustacchi 			break;
162*32002227SRobert Mustacchi 		case I2CADM_MAP_TYPE_SHARED:
163*32002227SRobert Mustacchi 			str = "shared";
164*32002227SRobert Mustacchi 			break;
165*32002227SRobert Mustacchi 		case I2CADM_MAP_TYPE_ERROR:
166*32002227SRobert Mustacchi 			str = "error";
167*32002227SRobert Mustacchi 			break;
168*32002227SRobert Mustacchi 		default:
169*32002227SRobert Mustacchi 			abort();
170*32002227SRobert Mustacchi 		}
171*32002227SRobert Mustacchi 		len = strlcpy(buf, str, buflen);
172*32002227SRobert Mustacchi 		break;
173*32002227SRobert Mustacchi 	case I2CADM_PORT_MAP_MAJOR:
174*32002227SRobert Mustacchi 		if (arg->ipm_map->map_type == I2CADM_MAP_TYPE_SHARED) {
175*32002227SRobert Mustacchi 			len = snprintf(buf, buflen, "%u",
176*32002227SRobert Mustacchi 			    arg->ipm_map->map_major);
177*32002227SRobert Mustacchi 		} else {
178*32002227SRobert Mustacchi 			len = strlcpy(buf, "-", buflen);
179*32002227SRobert Mustacchi 		}
180*32002227SRobert Mustacchi 		break;
181*32002227SRobert Mustacchi 	case I2CADM_PORT_MAP_DRIVER:
182*32002227SRobert Mustacchi 		if (arg->ipm_map->map_type == I2CADM_MAP_TYPE_SHARED) {
183*32002227SRobert Mustacchi 			str = arg->ipm_map->map_shared;
184*32002227SRobert Mustacchi 			if (str == NULL)
185*32002227SRobert Mustacchi 				str = "unknown";
186*32002227SRobert Mustacchi 		} else {
187*32002227SRobert Mustacchi 			str = "-";
188*32002227SRobert Mustacchi 		}
189*32002227SRobert Mustacchi 		len = strlcpy(buf, str, buflen);
190*32002227SRobert Mustacchi 		break;
191*32002227SRobert Mustacchi 	default:
192*32002227SRobert Mustacchi 		return (B_FALSE);
193*32002227SRobert Mustacchi 	}
194*32002227SRobert Mustacchi 
195*32002227SRobert Mustacchi 	return (len < buflen);
196*32002227SRobert Mustacchi }
197*32002227SRobert Mustacchi 
198*32002227SRobert Mustacchi static const ofmt_field_t i2cadm_port_map_ofmt[] = {
199*32002227SRobert Mustacchi 	{ "ADDR", 8, I2CADM_PORT_MAP_ADDR, i2cadm_port_map_ofmt_cb },
200*32002227SRobert Mustacchi 	{ "COUNT", 8, I2CADM_PORT_MAP_COUNT, i2cadm_port_map_ofmt_cb },
201*32002227SRobert Mustacchi 	{ "TYPE", 16, I2CADM_PORT_MAP_TYPE, i2cadm_port_map_ofmt_cb },
202*32002227SRobert Mustacchi 	{ "MAJOR", 8, I2CADM_PORT_MAP_MAJOR, i2cadm_port_map_ofmt_cb },
203*32002227SRobert Mustacchi 	{ "DRIVER", 16, I2CADM_PORT_MAP_DRIVER, i2cadm_port_map_ofmt_cb },
204*32002227SRobert Mustacchi 	{ NULL, 0, 0, NULL }
205*32002227SRobert Mustacchi };
206*32002227SRobert Mustacchi 
207*32002227SRobert Mustacchi static const char *key = ""
208*32002227SRobert Mustacchi "\t- = No Device      L = Local Device\n"
209*32002227SRobert Mustacchi "\tS = Shared         v = Downstream\n"
210*32002227SRobert Mustacchi "\t                   E = Error\n";
211*32002227SRobert Mustacchi 
212*32002227SRobert Mustacchi static bool
i2cadm_port_map_table_cb(void * arg,uint16_t addr)213*32002227SRobert Mustacchi i2cadm_port_map_table_cb(void *arg, uint16_t addr)
214*32002227SRobert Mustacchi {
215*32002227SRobert Mustacchi 	const i2cadm_map_t *results = arg;
216*32002227SRobert Mustacchi 	bool shared = false;
217*32002227SRobert Mustacchi 
218*32002227SRobert Mustacchi 	switch (results[addr].map_type) {
219*32002227SRobert Mustacchi 	case I2CADM_MAP_TYPE_NONE:
220*32002227SRobert Mustacchi 		(void) printf("%3s", "-");
221*32002227SRobert Mustacchi 		break;
222*32002227SRobert Mustacchi 	case I2CADM_MAP_TYPE_LOCAL:
223*32002227SRobert Mustacchi 		(void) printf("%3s", "L");
224*32002227SRobert Mustacchi 		break;
225*32002227SRobert Mustacchi 	case I2CADM_MAP_TYPE_DS:
226*32002227SRobert Mustacchi 		(void) printf("%2uv", results[addr].map_count);
227*32002227SRobert Mustacchi 		break;
228*32002227SRobert Mustacchi 	case I2CADM_MAP_TYPE_SHARED:
229*32002227SRobert Mustacchi 		shared = true;
230*32002227SRobert Mustacchi 		(void) printf("%2uS", results[addr].map_count);
231*32002227SRobert Mustacchi 		break;
232*32002227SRobert Mustacchi 	case I2CADM_MAP_TYPE_ERROR:
233*32002227SRobert Mustacchi 		(void) printf("%3s", "E");
234*32002227SRobert Mustacchi 		break;
235*32002227SRobert Mustacchi 	}
236*32002227SRobert Mustacchi 
237*32002227SRobert Mustacchi 	return (shared);
238*32002227SRobert Mustacchi }
239*32002227SRobert Mustacchi 
240*32002227SRobert Mustacchi static void
i2cadm_port_map_table_post(void * arg,uint16_t max_addr)241*32002227SRobert Mustacchi i2cadm_port_map_table_post(void *arg, uint16_t max_addr)
242*32002227SRobert Mustacchi {
243*32002227SRobert Mustacchi 	const i2cadm_map_t *results = arg;
244*32002227SRobert Mustacchi 	(void) printf("\nShared Address Owners:\n");
245*32002227SRobert Mustacchi 
246*32002227SRobert Mustacchi 	for (uint16_t i = 0; i < max_addr; i++) {
247*32002227SRobert Mustacchi 		if (results[i].map_type != I2CADM_MAP_TYPE_SHARED)
248*32002227SRobert Mustacchi 			continue;
249*32002227SRobert Mustacchi 
250*32002227SRobert Mustacchi 		const char *name = results[i].map_shared;
251*32002227SRobert Mustacchi 		if (name == NULL)
252*32002227SRobert Mustacchi 			name = "unknown";
253*32002227SRobert Mustacchi 		if (max_addr > UINT8_MAX) {
254*32002227SRobert Mustacchi 			(void) printf("0x%03x: %s (%u)\n", i, name,
255*32002227SRobert Mustacchi 			    results[i].map_major);
256*32002227SRobert Mustacchi 		} else {
257*32002227SRobert Mustacchi 			(void) printf("0x%02x: %s (%u)\n", i, name,
258*32002227SRobert Mustacchi 			    results[i].map_major);
259*32002227SRobert Mustacchi 		}
260*32002227SRobert Mustacchi 	}
261*32002227SRobert Mustacchi }
262*32002227SRobert Mustacchi 
263*32002227SRobert Mustacchi static int
i2cadm_port_map(int argc,char * argv[])264*32002227SRobert Mustacchi i2cadm_port_map(int argc, char *argv[])
265*32002227SRobert Mustacchi {
266*32002227SRobert Mustacchi 	int c;
267*32002227SRobert Mustacchi 	i2c_port_t *port;
268*32002227SRobert Mustacchi 	i2c_port_map_t *map;
269*32002227SRobert Mustacchi 	uint16_t max_addr = 1 << 7;
270*32002227SRobert Mustacchi 	i2cadm_map_t *results;
271*32002227SRobert Mustacchi 	boolean_t parse = B_FALSE;
272*32002227SRobert Mustacchi 	uint_t flags = 0;
273*32002227SRobert Mustacchi 	const char *fields = NULL;
274*32002227SRobert Mustacchi 	ofmt_status_t oferr;
275*32002227SRobert Mustacchi 	ofmt_handle_t ofmt;
276*32002227SRobert Mustacchi 
277*32002227SRobert Mustacchi 	while ((c = getopt(argc, argv, ":Ho:p")) != -1) {
278*32002227SRobert Mustacchi 		switch (c) {
279*32002227SRobert Mustacchi 		case 'H':
280*32002227SRobert Mustacchi 			flags |= OFMT_NOHEADER;
281*32002227SRobert Mustacchi 			break;
282*32002227SRobert Mustacchi 		case 'o':
283*32002227SRobert Mustacchi 			fields = optarg;
284*32002227SRobert Mustacchi 			break;
285*32002227SRobert Mustacchi 		case 'p':
286*32002227SRobert Mustacchi 			parse = B_TRUE;
287*32002227SRobert Mustacchi 			flags |= OFMT_PARSABLE;
288*32002227SRobert Mustacchi 			break;
289*32002227SRobert Mustacchi 		case ':':
290*32002227SRobert Mustacchi 			i2cadm_port_map_help("option -%c requires an argument",
291*32002227SRobert Mustacchi 			    optopt);
292*32002227SRobert Mustacchi 			exit(EXIT_USAGE);
293*32002227SRobert Mustacchi 		case '?':
294*32002227SRobert Mustacchi 			i2cadm_port_map_help("unknown option: -%c", optopt);
295*32002227SRobert Mustacchi 			exit(EXIT_USAGE);
296*32002227SRobert Mustacchi 		}
297*32002227SRobert Mustacchi 	}
298*32002227SRobert Mustacchi 
299*32002227SRobert Mustacchi 	argv += optind;
300*32002227SRobert Mustacchi 	argc -= optind;
301*32002227SRobert Mustacchi 	if (argc == 0) {
302*32002227SRobert Mustacchi 		errx(EXIT_USAGE, "missing required port");
303*32002227SRobert Mustacchi 	} else if (argc > 1) {
304*32002227SRobert Mustacchi 		errx(EXIT_USAGE, "encountered extraneous arguments starting "
305*32002227SRobert Mustacchi 		    "with %s", argv[1]);
306*32002227SRobert Mustacchi 	}
307*32002227SRobert Mustacchi 
308*32002227SRobert Mustacchi 	if (!i2c_port_init_by_path(i2cadm.i2c_hdl, argv[0], &port)) {
309*32002227SRobert Mustacchi 		i2cadm_fatal("failed to parse port path %s", argv[0]);
310*32002227SRobert Mustacchi 	}
311*32002227SRobert Mustacchi 
312*32002227SRobert Mustacchi 	if (!i2c_port_map_snap(port, &map)) {
313*32002227SRobert Mustacchi 		i2cadm_fatal("failed to get port map");
314*32002227SRobert Mustacchi 	}
315*32002227SRobert Mustacchi 
316*32002227SRobert Mustacchi 	if (parse && fields == NULL) {
317*32002227SRobert Mustacchi 		errx(EXIT_USAGE, "-p requires fields specified with -o");
318*32002227SRobert Mustacchi 	}
319*32002227SRobert Mustacchi 
320*32002227SRobert Mustacchi 	if (flags != 0 && fields == NULL) {
321*32002227SRobert Mustacchi 		errx(EXIT_USAGE, "-H can only be used with -o");
322*32002227SRobert Mustacchi 	}
323*32002227SRobert Mustacchi 
324*32002227SRobert Mustacchi 	if (fields != NULL) {
325*32002227SRobert Mustacchi 		if (!parse) {
326*32002227SRobert Mustacchi 			flags |= OFMT_WRAP;
327*32002227SRobert Mustacchi 		}
328*32002227SRobert Mustacchi 
329*32002227SRobert Mustacchi 		oferr = ofmt_open(fields, i2cadm_port_map_ofmt, flags, 0,
330*32002227SRobert Mustacchi 		    &ofmt);
331*32002227SRobert Mustacchi 		ofmt_check(oferr, parse, ofmt, i2cadm_ofmt_errx, warnx);
332*32002227SRobert Mustacchi 	}
333*32002227SRobert Mustacchi 
334*32002227SRobert Mustacchi 	results = calloc(max_addr, sizeof (i2cadm_map_t));
335*32002227SRobert Mustacchi 	if (results == NULL) {
336*32002227SRobert Mustacchi 		err(EXIT_FAILURE, "failed to allocate port map results "
337*32002227SRobert Mustacchi 		    "tracking structure");
338*32002227SRobert Mustacchi 	}
339*32002227SRobert Mustacchi 
340*32002227SRobert Mustacchi 	for (uint16_t i = 0; i < max_addr; i++) {
341*32002227SRobert Mustacchi 		i2c_addr_t addr = { I2C_ADDR_7BIT, i };
342*32002227SRobert Mustacchi 		bool ds;
343*32002227SRobert Mustacchi 		uint32_t ndevs;
344*32002227SRobert Mustacchi 		major_t major;
345*32002227SRobert Mustacchi 
346*32002227SRobert Mustacchi 		if (!i2c_port_map_addr_info(map, &addr, &ndevs, &ds, &major)) {
347*32002227SRobert Mustacchi 			results[i].map_type = I2CADM_MAP_TYPE_ERROR;
348*32002227SRobert Mustacchi 			continue;
349*32002227SRobert Mustacchi 		}
350*32002227SRobert Mustacchi 
351*32002227SRobert Mustacchi 		if (ndevs == 0) {
352*32002227SRobert Mustacchi 			results[i].map_type = I2CADM_MAP_TYPE_NONE;
353*32002227SRobert Mustacchi 			continue;
354*32002227SRobert Mustacchi 		}
355*32002227SRobert Mustacchi 
356*32002227SRobert Mustacchi 		results[i].map_count = ndevs;
357*32002227SRobert Mustacchi 		if (major != DDI_MAJOR_T_NONE) {
358*32002227SRobert Mustacchi 			results[i].map_type = I2CADM_MAP_TYPE_SHARED;
359*32002227SRobert Mustacchi 			results[i].map_shared = i2cadm_major_to_name(major);
360*32002227SRobert Mustacchi 			results[i].map_major = major;
361*32002227SRobert Mustacchi 		} else if (ds) {
362*32002227SRobert Mustacchi 			results[i].map_type = I2CADM_MAP_TYPE_DS;
363*32002227SRobert Mustacchi 		} else {
364*32002227SRobert Mustacchi 			VERIFY3U(ndevs, ==, 1);
365*32002227SRobert Mustacchi 			results[i].map_type = I2CADM_MAP_TYPE_LOCAL;
366*32002227SRobert Mustacchi 		}
367*32002227SRobert Mustacchi 	}
368*32002227SRobert Mustacchi 
369*32002227SRobert Mustacchi 	if (fields == NULL) {
370*32002227SRobert Mustacchi 		i2cadm_table_t table = {
371*32002227SRobert Mustacchi 			.table_port = argv[0],
372*32002227SRobert Mustacchi 			.table_key = key,
373*32002227SRobert Mustacchi 			.table_msg = "Address map for",
374*32002227SRobert Mustacchi 			.table_max = max_addr,
375*32002227SRobert Mustacchi 			.table_cb = i2cadm_port_map_table_cb,
376*32002227SRobert Mustacchi 			.table_post = i2cadm_port_map_table_post
377*32002227SRobert Mustacchi 		};
378*32002227SRobert Mustacchi 		i2cadm_print_table(&table, results);
379*32002227SRobert Mustacchi 	} else {
380*32002227SRobert Mustacchi 		for (uint16_t i = 0; i < max_addr; i++) {
381*32002227SRobert Mustacchi 			i2cadm_port_map_ofmt_t arg = {
382*32002227SRobert Mustacchi 				.ipm_addr = i,
383*32002227SRobert Mustacchi 				.ipm_map = &results[i]
384*32002227SRobert Mustacchi 			};
385*32002227SRobert Mustacchi 			ofmt_print(ofmt, &arg);
386*32002227SRobert Mustacchi 		}
387*32002227SRobert Mustacchi 		ofmt_close(ofmt);
388*32002227SRobert Mustacchi 	}
389*32002227SRobert Mustacchi 
390*32002227SRobert Mustacchi 	for (uint16_t i = 0; i < max_addr; i++) {
391*32002227SRobert Mustacchi 		free(results[i].map_shared);
392*32002227SRobert Mustacchi 	}
393*32002227SRobert Mustacchi 	free(results);
394*32002227SRobert Mustacchi 	i2c_port_map_free(map);
395*32002227SRobert Mustacchi 	i2c_port_fini(port);
396*32002227SRobert Mustacchi 	return (0);
397*32002227SRobert Mustacchi }
398*32002227SRobert Mustacchi 
399*32002227SRobert Mustacchi static void
i2cadm_port_list_usage(FILE * f)400*32002227SRobert Mustacchi i2cadm_port_list_usage(FILE *f)
401*32002227SRobert Mustacchi {
402*32002227SRobert Mustacchi 	(void) fprintf(f, "\ti2cadm port list [-H] [-o field,[...] [-p]] "
403*32002227SRobert Mustacchi 	    "[filter]\n");
404*32002227SRobert Mustacchi }
405*32002227SRobert Mustacchi 
406*32002227SRobert Mustacchi typedef enum {
407*32002227SRobert Mustacchi 	I2CADM_PORT_LIST_PATH,
408*32002227SRobert Mustacchi 	I2CADM_PORT_LIST_TYPE,
409*32002227SRobert Mustacchi 	I2CADM_PORT_LIST_NAME,
410*32002227SRobert Mustacchi 	I2CADM_PORT_LIST_NUM,
411*32002227SRobert Mustacchi 	I2CADM_PORT_LIST_NDEVS,
412*32002227SRobert Mustacchi 	I2CADM_PORT_LIST_TDEVS
413*32002227SRobert Mustacchi } i2cadm_port_list_otype_tt;
414*32002227SRobert Mustacchi 
415*32002227SRobert Mustacchi typedef struct i2cadm_port_list_ofmt {
416*32002227SRobert Mustacchi 	i2c_port_t *ipl_port;
417*32002227SRobert Mustacchi 	i2c_port_map_t *ipl_map;
418*32002227SRobert Mustacchi } i2cadm_port_list_ofmt_t;
419*32002227SRobert Mustacchi 
420*32002227SRobert Mustacchi static void
i2cadm_port_list_help(const char * fmt,...)421*32002227SRobert Mustacchi i2cadm_port_list_help(const char *fmt, ...)
422*32002227SRobert Mustacchi {
423*32002227SRobert Mustacchi 	if (fmt != NULL) {
424*32002227SRobert Mustacchi 		va_list ap;
425*32002227SRobert Mustacchi 
426*32002227SRobert Mustacchi 		va_start(ap, fmt);
427*32002227SRobert Mustacchi 		vwarnx(fmt, ap);
428*32002227SRobert Mustacchi 		va_end(ap);
429*32002227SRobert Mustacchi 	}
430*32002227SRobert Mustacchi 
431*32002227SRobert Mustacchi 	(void) fprintf(stderr, "Usage:  i2cadm port list [-H] "
432*32002227SRobert Mustacchi 	    "[-o field[,...] [-p]] [filter...]\n\n");
433*32002227SRobert Mustacchi 	(void) fprintf(stderr, "List I2C ports in the system. Each <filter> "
434*32002227SRobert Mustacchi 	    "selects ports based upon its\ntype, name, or the I2C path. "
435*32002227SRobert Mustacchi 	    "Multiple filters are treated as an OR. It is an\nerror if a "
436*32002227SRobert Mustacchi 	    "filter isn't used.\n\n"
437*32002227SRobert Mustacchi 	    "\t-H\t\tomit the column header\n"
438*32002227SRobert Mustacchi 	    "\t-o field\toutput fields to print\n"
439*32002227SRobert Mustacchi 	    "\t-p\t\tparseable output (requires -o)\n");
440*32002227SRobert Mustacchi 	(void) fprintf(stderr, "\nThe following fields are supported:\n"
441*32002227SRobert Mustacchi 	    "\tpath\t\tthe I2C path of the port\n"
442*32002227SRobert Mustacchi 	    "\ttype\t\tthe type of I2C port: controller or multiplexor\n"
443*32002227SRobert Mustacchi 	    "\tname\t\tthe port's name\n"
444*32002227SRobert Mustacchi 	    "\tportno\t\tthe system's port number (zero based)\n"
445*32002227SRobert Mustacchi 	    "\tndevs\t\tthe number of device's directly attached to this port\n"
446*32002227SRobert Mustacchi 	    "\ttdevs\t\tthe total number of devices under this port\n");
447*32002227SRobert Mustacchi }
448*32002227SRobert Mustacchi 
449*32002227SRobert Mustacchi static boolean_t
i2cadm_port_list_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)450*32002227SRobert Mustacchi i2cadm_port_list_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
451*32002227SRobert Mustacchi {
452*32002227SRobert Mustacchi 	uint32_t local, ds;
453*32002227SRobert Mustacchi 	i2cadm_port_list_ofmt_t *arg = ofarg->ofmt_cbarg;
454*32002227SRobert Mustacchi 	size_t len;
455*32002227SRobert Mustacchi 
456*32002227SRobert Mustacchi 	switch (ofarg->ofmt_id) {
457*32002227SRobert Mustacchi 	case I2CADM_PORT_LIST_PATH:
458*32002227SRobert Mustacchi 		len = strlcpy(buf, i2c_port_path(arg->ipl_port), buflen);
459*32002227SRobert Mustacchi 		break;
460*32002227SRobert Mustacchi 	case I2CADM_PORT_LIST_TYPE:
461*32002227SRobert Mustacchi 		switch (i2c_port_type(arg->ipl_port)) {
462*32002227SRobert Mustacchi 		case I2C_PORT_TYPE_CTRL:
463*32002227SRobert Mustacchi 			len = strlcat(buf, "controller", buflen);
464*32002227SRobert Mustacchi 			break;
465*32002227SRobert Mustacchi 		case I2C_PORT_TYPE_MUX:
466*32002227SRobert Mustacchi 			len = strlcat(buf, "multiplexor", buflen);
467*32002227SRobert Mustacchi 			break;
468*32002227SRobert Mustacchi 		default:
469*32002227SRobert Mustacchi 			len = snprintf(buf, buflen, "unknown: 0x%x",
470*32002227SRobert Mustacchi 			    i2c_port_type(arg->ipl_port));
471*32002227SRobert Mustacchi 			break;
472*32002227SRobert Mustacchi 		}
473*32002227SRobert Mustacchi 		break;
474*32002227SRobert Mustacchi 	case I2CADM_PORT_LIST_NAME:
475*32002227SRobert Mustacchi 		len = strlcpy(buf, i2c_port_name(arg->ipl_port), buflen);
476*32002227SRobert Mustacchi 		break;
477*32002227SRobert Mustacchi 	case I2CADM_PORT_LIST_NUM:
478*32002227SRobert Mustacchi 		len = snprintf(buf, buflen, "%u",
479*32002227SRobert Mustacchi 		    i2c_port_portno(arg->ipl_port));
480*32002227SRobert Mustacchi 		break;
481*32002227SRobert Mustacchi 	case I2CADM_PORT_LIST_NDEVS:
482*32002227SRobert Mustacchi 		i2c_port_map_ndevs(arg->ipl_map, &local, NULL);
483*32002227SRobert Mustacchi 		len = snprintf(buf, buflen, "%u", local);
484*32002227SRobert Mustacchi 		break;
485*32002227SRobert Mustacchi 	case I2CADM_PORT_LIST_TDEVS:
486*32002227SRobert Mustacchi 		i2c_port_map_ndevs(arg->ipl_map, &local, &ds);
487*32002227SRobert Mustacchi 		len = snprintf(buf, buflen, "%u", local + ds);
488*32002227SRobert Mustacchi 		break;
489*32002227SRobert Mustacchi 	default:
490*32002227SRobert Mustacchi 		return (B_FALSE);
491*32002227SRobert Mustacchi 	}
492*32002227SRobert Mustacchi 
493*32002227SRobert Mustacchi 	return (len < buflen);
494*32002227SRobert Mustacchi }
495*32002227SRobert Mustacchi 
496*32002227SRobert Mustacchi static const char *i2cadm_port_list_fields = "path,type,portno,ndevs,tdevs";
497*32002227SRobert Mustacchi static const ofmt_field_t i2cadm_port_list_ofmt[] = {
498*32002227SRobert Mustacchi 	{ "NAME", 12, I2CADM_PORT_LIST_NAME, i2cadm_port_list_ofmt_cb },
499*32002227SRobert Mustacchi 	{ "TYPE", 14, I2CADM_PORT_LIST_TYPE, i2cadm_port_list_ofmt_cb },
500*32002227SRobert Mustacchi 	{ "PORTNO", 8, I2CADM_PORT_LIST_NUM, i2cadm_port_list_ofmt_cb },
501*32002227SRobert Mustacchi 	{ "NDEVS", 8, I2CADM_PORT_LIST_NDEVS, i2cadm_port_list_ofmt_cb },
502*32002227SRobert Mustacchi 	{ "TDEVS", 8, I2CADM_PORT_LIST_TDEVS, i2cadm_port_list_ofmt_cb },
503*32002227SRobert Mustacchi 	{ "PATH", 32, I2CADM_PORT_LIST_PATH, i2cadm_port_list_ofmt_cb },
504*32002227SRobert Mustacchi 	{ NULL, 0, 0, NULL }
505*32002227SRobert Mustacchi };
506*32002227SRobert Mustacchi 
507*32002227SRobert Mustacchi /*
508*32002227SRobert Mustacchi  * We accept the following filters for matching ports:
509*32002227SRobert Mustacchi  *
510*32002227SRobert Mustacchi  *  - Matching on the ports name
511*32002227SRobert Mustacchi  *  - Matching on the port's type
512*32002227SRobert Mustacchi  *  - Matching on a portion of the port's path
513*32002227SRobert Mustacchi  */
514*32002227SRobert Mustacchi static bool
i2cadm_port_list_filt(const i2cadm_port_list_ofmt_t * arg,int nfilts,char ** filts,bool * used)515*32002227SRobert Mustacchi i2cadm_port_list_filt(const i2cadm_port_list_ofmt_t *arg, int nfilts,
516*32002227SRobert Mustacchi     char **filts, bool *used)
517*32002227SRobert Mustacchi {
518*32002227SRobert Mustacchi 	bool match = false;
519*32002227SRobert Mustacchi 	const char *type, *name, *path;
520*32002227SRobert Mustacchi 
521*32002227SRobert Mustacchi 	if (nfilts == 0) {
522*32002227SRobert Mustacchi 		return (true);
523*32002227SRobert Mustacchi 	}
524*32002227SRobert Mustacchi 
525*32002227SRobert Mustacchi 	name = i2c_port_name(arg->ipl_port);
526*32002227SRobert Mustacchi 	path = i2c_port_path(arg->ipl_port);
527*32002227SRobert Mustacchi 	size_t pathlen = strlen(path);
528*32002227SRobert Mustacchi 	if (i2c_port_type(arg->ipl_port) == I2C_PORT_TYPE_CTRL) {
529*32002227SRobert Mustacchi 		type = "controller";
530*32002227SRobert Mustacchi 	} else {
531*32002227SRobert Mustacchi 		type = "multiplexor";
532*32002227SRobert Mustacchi 	}
533*32002227SRobert Mustacchi 
534*32002227SRobert Mustacchi 	for (int i = 0; i < nfilts; i++) {
535*32002227SRobert Mustacchi 		if (strcmp(filts[i], name) == 0) {
536*32002227SRobert Mustacchi 			used[i] = true;
537*32002227SRobert Mustacchi 			match = true;
538*32002227SRobert Mustacchi 			continue;
539*32002227SRobert Mustacchi 		}
540*32002227SRobert Mustacchi 
541*32002227SRobert Mustacchi 		if (strcmp(filts[i], type) == 0) {
542*32002227SRobert Mustacchi 			used[i] = true;
543*32002227SRobert Mustacchi 			match = true;
544*32002227SRobert Mustacchi 			continue;
545*32002227SRobert Mustacchi 		}
546*32002227SRobert Mustacchi 
547*32002227SRobert Mustacchi 		if (strcmp(filts[i], path) == 0) {
548*32002227SRobert Mustacchi 			used[i] = true;
549*32002227SRobert Mustacchi 			match = true;
550*32002227SRobert Mustacchi 			continue;
551*32002227SRobert Mustacchi 		}
552*32002227SRobert Mustacchi 
553*32002227SRobert Mustacchi 		size_t len = strlen(filts[i]);
554*32002227SRobert Mustacchi 		if (len < pathlen && strncmp(path, filts[i], len) == 0) {
555*32002227SRobert Mustacchi 			used[i] = true;
556*32002227SRobert Mustacchi 			match = true;
557*32002227SRobert Mustacchi 			continue;
558*32002227SRobert Mustacchi 		}
559*32002227SRobert Mustacchi 	}
560*32002227SRobert Mustacchi 
561*32002227SRobert Mustacchi 	return (match);
562*32002227SRobert Mustacchi }
563*32002227SRobert Mustacchi 
564*32002227SRobert Mustacchi static int
i2cadm_port_list(int argc,char * argv[])565*32002227SRobert Mustacchi i2cadm_port_list(int argc, char *argv[])
566*32002227SRobert Mustacchi {
567*32002227SRobert Mustacchi 	int c, ret = EXIT_SUCCESS;
568*32002227SRobert Mustacchi 	uint_t flags = 0;
569*32002227SRobert Mustacchi 	boolean_t parse = B_FALSE;
570*32002227SRobert Mustacchi 	const char *fields = NULL;
571*32002227SRobert Mustacchi 	bool *filts = NULL, print = false;
572*32002227SRobert Mustacchi 	ofmt_status_t oferr;
573*32002227SRobert Mustacchi 	ofmt_handle_t ofmt;
574*32002227SRobert Mustacchi 	i2c_port_iter_t *iter;
575*32002227SRobert Mustacchi 	i2c_iter_t iret;
576*32002227SRobert Mustacchi 	const i2c_port_disc_t *disc;
577*32002227SRobert Mustacchi 
578*32002227SRobert Mustacchi 	while ((c = getopt(argc, argv, ":Ho:p")) != -1) {
579*32002227SRobert Mustacchi 		switch (c) {
580*32002227SRobert Mustacchi 		case 'H':
581*32002227SRobert Mustacchi 			flags |= OFMT_NOHEADER;
582*32002227SRobert Mustacchi 			break;
583*32002227SRobert Mustacchi 		case 'o':
584*32002227SRobert Mustacchi 			fields = optarg;
585*32002227SRobert Mustacchi 			break;
586*32002227SRobert Mustacchi 		case 'p':
587*32002227SRobert Mustacchi 			parse = B_TRUE;
588*32002227SRobert Mustacchi 			flags |= OFMT_PARSABLE;
589*32002227SRobert Mustacchi 			break;
590*32002227SRobert Mustacchi 		case ':':
591*32002227SRobert Mustacchi 			i2cadm_port_list_help("option -%c requires an "
592*32002227SRobert Mustacchi 			    "argument", optopt);
593*32002227SRobert Mustacchi 			exit(EXIT_USAGE);
594*32002227SRobert Mustacchi 		case '?':
595*32002227SRobert Mustacchi 			i2cadm_port_list_help("unknown option: -%c",
596*32002227SRobert Mustacchi 			    optopt);
597*32002227SRobert Mustacchi 			exit(EXIT_USAGE);
598*32002227SRobert Mustacchi 		}
599*32002227SRobert Mustacchi 	}
600*32002227SRobert Mustacchi 
601*32002227SRobert Mustacchi 	if (parse && fields == NULL) {
602*32002227SRobert Mustacchi 		errx(EXIT_USAGE, "-p requires fields specified with -o");
603*32002227SRobert Mustacchi 	}
604*32002227SRobert Mustacchi 
605*32002227SRobert Mustacchi 	if (!parse) {
606*32002227SRobert Mustacchi 		flags |= OFMT_WRAP;
607*32002227SRobert Mustacchi 	}
608*32002227SRobert Mustacchi 
609*32002227SRobert Mustacchi 	if (fields == NULL) {
610*32002227SRobert Mustacchi 		fields = i2cadm_port_list_fields;
611*32002227SRobert Mustacchi 	}
612*32002227SRobert Mustacchi 
613*32002227SRobert Mustacchi 	argc -= optind;
614*32002227SRobert Mustacchi 	argv += optind;
615*32002227SRobert Mustacchi 
616*32002227SRobert Mustacchi 	if (argc > 0) {
617*32002227SRobert Mustacchi 		filts = calloc(argc, sizeof (bool));
618*32002227SRobert Mustacchi 		if (filts == NULL) {
619*32002227SRobert Mustacchi 			err(EXIT_FAILURE, "failed to allocate memory for "
620*32002227SRobert Mustacchi 			    "filter tracking");
621*32002227SRobert Mustacchi 		}
622*32002227SRobert Mustacchi 	}
623*32002227SRobert Mustacchi 
624*32002227SRobert Mustacchi 	oferr = ofmt_open(fields, i2cadm_port_list_ofmt, flags, 0, &ofmt);
625*32002227SRobert Mustacchi 	ofmt_check(oferr, parse, ofmt, i2cadm_ofmt_errx, warnx);
626*32002227SRobert Mustacchi 
627*32002227SRobert Mustacchi 
628*32002227SRobert Mustacchi 	if (!i2c_port_discover_init(i2cadm.i2c_hdl, &iter)) {
629*32002227SRobert Mustacchi 		i2cadm_fatal("failed to in initialize port discovery");
630*32002227SRobert Mustacchi 	}
631*32002227SRobert Mustacchi 
632*32002227SRobert Mustacchi 	while ((iret = i2c_port_discover_step(iter, &disc)) == I2C_ITER_VALID) {
633*32002227SRobert Mustacchi 		i2cadm_port_list_ofmt_t arg;
634*32002227SRobert Mustacchi 
635*32002227SRobert Mustacchi 		if (!i2c_port_init(i2cadm.i2c_hdl, i2c_port_disc_devi(disc),
636*32002227SRobert Mustacchi 		    &arg.ipl_port)) {
637*32002227SRobert Mustacchi 			i2cadm_warn("failed to initialize port %s",
638*32002227SRobert Mustacchi 			    i2c_port_disc_path(disc));
639*32002227SRobert Mustacchi 			continue;
640*32002227SRobert Mustacchi 		}
641*32002227SRobert Mustacchi 
642*32002227SRobert Mustacchi 		if (!i2c_port_map_snap(arg.ipl_port, &arg.ipl_map)) {
643*32002227SRobert Mustacchi 			i2cadm_warn("failed to get port map for %s",
644*32002227SRobert Mustacchi 			    i2c_port_disc_path(disc));
645*32002227SRobert Mustacchi 			i2c_port_fini(arg.ipl_port);
646*32002227SRobert Mustacchi 			continue;
647*32002227SRobert Mustacchi 		}
648*32002227SRobert Mustacchi 
649*32002227SRobert Mustacchi 		if (i2cadm_port_list_filt(&arg, argc, argv, filts)) {
650*32002227SRobert Mustacchi 			ofmt_print(ofmt, &arg);
651*32002227SRobert Mustacchi 			print = true;
652*32002227SRobert Mustacchi 		}
653*32002227SRobert Mustacchi 
654*32002227SRobert Mustacchi 		i2c_port_map_free(arg.ipl_map);
655*32002227SRobert Mustacchi 		i2c_port_fini(arg.ipl_port);
656*32002227SRobert Mustacchi 	}
657*32002227SRobert Mustacchi 
658*32002227SRobert Mustacchi 	if (iret == I2C_ITER_ERROR) {
659*32002227SRobert Mustacchi 		i2cadm_warn("failed to discover ports");
660*32002227SRobert Mustacchi 		ret = EXIT_FAILURE;
661*32002227SRobert Mustacchi 	}
662*32002227SRobert Mustacchi 
663*32002227SRobert Mustacchi 	for (int i = 0; i < argc; i++) {
664*32002227SRobert Mustacchi 		if (!filts[i]) {
665*32002227SRobert Mustacchi 			warnx("filter '%s' did not match any ports",
666*32002227SRobert Mustacchi 			    argv[i]);
667*32002227SRobert Mustacchi 			ret = EXIT_FAILURE;
668*32002227SRobert Mustacchi 		}
669*32002227SRobert Mustacchi 	}
670*32002227SRobert Mustacchi 
671*32002227SRobert Mustacchi 	if (!print && argc == 0) {
672*32002227SRobert Mustacchi 		warnx("no I2C ports found");
673*32002227SRobert Mustacchi 		ret = EXIT_FAILURE;
674*32002227SRobert Mustacchi 	}
675*32002227SRobert Mustacchi 
676*32002227SRobert Mustacchi 	i2c_port_discover_fini(iter);
677*32002227SRobert Mustacchi 	return (ret);
678*32002227SRobert Mustacchi }
679*32002227SRobert Mustacchi 
680*32002227SRobert Mustacchi static i2cadm_cmdtab_t i2cadm_port_cmds[] = {
681*32002227SRobert Mustacchi 	{ "list", i2cadm_port_list, i2cadm_port_list_usage },
682*32002227SRobert Mustacchi 	{ "map", i2cadm_port_map, i2cadm_port_map_usage },
683*32002227SRobert Mustacchi };
684*32002227SRobert Mustacchi 
685*32002227SRobert Mustacchi int
i2cadm_port(int argc,char * argv[])686*32002227SRobert Mustacchi i2cadm_port(int argc, char *argv[])
687*32002227SRobert Mustacchi {
688*32002227SRobert Mustacchi 	return (i2cadm_walk_tab(i2cadm_port_cmds, ARRAY_SIZE(i2cadm_port_cmds),
689*32002227SRobert Mustacchi 	    argc, argv));
690*32002227SRobert Mustacchi }
691*32002227SRobert Mustacchi 
692*32002227SRobert Mustacchi void
i2cadm_port_usage(FILE * f)693*32002227SRobert Mustacchi i2cadm_port_usage(FILE *f)
694*32002227SRobert Mustacchi {
695*32002227SRobert Mustacchi 	i2cadm_walk_usage(i2cadm_port_cmds, ARRAY_SIZE(i2cadm_port_cmds), f);
696*32002227SRobert Mustacchi }
697