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