xref: /illumos-gate/usr/src/cmd/i2cadm/i2cadm_scan.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 scan -- scan a single port for devices. By default all devices are
18*32002227SRobert Mustacchi  * scanned under the port, unless a specific set of devices is specified.
19*32002227SRobert Mustacchi  */
20*32002227SRobert Mustacchi 
21*32002227SRobert Mustacchi #include <err.h>
22*32002227SRobert Mustacchi #include <stdio.h>
23*32002227SRobert Mustacchi #include <stdarg.h>
24*32002227SRobert Mustacchi #include <string.h>
25*32002227SRobert Mustacchi #include <sys/sysmacros.h>
26*32002227SRobert Mustacchi #include <ofmt.h>
27*32002227SRobert Mustacchi 
28*32002227SRobert Mustacchi #include "i2cadm.h"
29*32002227SRobert Mustacchi 
30*32002227SRobert Mustacchi void
i2cadm_scan_usage(FILE * f)31*32002227SRobert Mustacchi i2cadm_scan_usage(FILE *f)
32*32002227SRobert Mustacchi {
33*32002227SRobert Mustacchi 	(void) fprintf(f, "\ti2cadm scan [-d dev] [-o field,[...] [-H] [-p]] "
34*32002227SRobert Mustacchi 	    "<port>\n");
35*32002227SRobert Mustacchi }
36*32002227SRobert Mustacchi 
37*32002227SRobert Mustacchi static void
i2cadm_scan_help(const char * fmt,...)38*32002227SRobert Mustacchi i2cadm_scan_help(const char *fmt, ...)
39*32002227SRobert Mustacchi {
40*32002227SRobert Mustacchi 	if (fmt != NULL) {
41*32002227SRobert Mustacchi 		va_list ap;
42*32002227SRobert Mustacchi 
43*32002227SRobert Mustacchi 		va_start(ap, fmt);
44*32002227SRobert Mustacchi 		vwarnx(fmt, ap);
45*32002227SRobert Mustacchi 		va_end(ap);
46*32002227SRobert Mustacchi 	}
47*32002227SRobert Mustacchi 
48*32002227SRobert Mustacchi 	(void) fprintf(stderr, "Usage:  i2cadm scan [-d dev] [-o field,[...] "
49*32002227SRobert Mustacchi 	    "[-H] [-p]] <port>\n");
50*32002227SRobert Mustacchi 	(void) fprintf(stderr, "\nScan for I2C devices\n\n"
51*32002227SRobert Mustacchi 	    "\t-d dev\t\tonly scan device address dev, can be specified "
52*32002227SRobert Mustacchi 	    "multiple\n\t\t\ttimes\n"
53*32002227SRobert Mustacchi 	    "\t-H\t\tomit the column header (requires -o)\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 when "
57*32002227SRobert Mustacchi 	    "using -o:\n"
58*32002227SRobert Mustacchi 	    "\taddr\t\tthe I2C address\n"
59*32002227SRobert Mustacchi 	    "\tresult\t\tthe address scan result\n"
60*32002227SRobert Mustacchi 	    "\terror\t\tthe error message if an error occurred\n");
61*32002227SRobert Mustacchi }
62*32002227SRobert Mustacchi 
63*32002227SRobert Mustacchi typedef enum {
64*32002227SRobert Mustacchi 	I2CADM_BUS_SCAN_UNKNOWN = 0,
65*32002227SRobert Mustacchi 	I2CADM_BUS_SCAN_FOUND,
66*32002227SRobert Mustacchi 	I2CADM_BUS_SCAN_NO_DEV,
67*32002227SRobert Mustacchi 	I2CADM_BUS_SCAN_RESERVED,
68*32002227SRobert Mustacchi 	I2CADM_BUS_SCAN_TIMEOUT,
69*32002227SRobert Mustacchi 	I2CADM_BUS_SCAN_ERROR,
70*32002227SRobert Mustacchi 	I2CADM_BUS_SCAN_SKIPPED
71*32002227SRobert Mustacchi } i2cadm_scan_result_t;
72*32002227SRobert Mustacchi 
73*32002227SRobert Mustacchi typedef struct {
74*32002227SRobert Mustacchi 	i2c_addr_t scan_addr;
75*32002227SRobert Mustacchi 	i2cadm_scan_result_t scan_res;
76*32002227SRobert Mustacchi 	char *scan_error;
77*32002227SRobert Mustacchi } i2cadm_scan_t;
78*32002227SRobert Mustacchi 
79*32002227SRobert Mustacchi static void
i2cadm_scan_error(i2c_hdl_t * hdl,i2cadm_scan_t * scan)80*32002227SRobert Mustacchi i2cadm_scan_error(i2c_hdl_t *hdl, i2cadm_scan_t *scan)
81*32002227SRobert Mustacchi {
82*32002227SRobert Mustacchi 	i2c_err_t err = i2c_err(hdl);
83*32002227SRobert Mustacchi 	if (err != I2C_ERR_CONTROLLER) {
84*32002227SRobert Mustacchi 		scan->scan_res = I2CADM_BUS_SCAN_ERROR;
85*32002227SRobert Mustacchi 		scan->scan_error = strdup(i2c_errmsg(hdl));
86*32002227SRobert Mustacchi 		if (scan->scan_error == NULL) {
87*32002227SRobert Mustacchi 			scan->scan_error = "libi2c error; but failed to "
88*32002227SRobert Mustacchi 			    "duplicate libi2c error message";
89*32002227SRobert Mustacchi 		}
90*32002227SRobert Mustacchi 		return;
91*32002227SRobert Mustacchi 	}
92*32002227SRobert Mustacchi 
93*32002227SRobert Mustacchi 	switch (i2c_ctrl_err(hdl)) {
94*32002227SRobert Mustacchi 	case I2C_CTRL_E_ADDR_NACK:
95*32002227SRobert Mustacchi 	case I2C_CTRL_E_DATA_NACK:
96*32002227SRobert Mustacchi 	case I2C_CTRL_E_NACK:
97*32002227SRobert Mustacchi 		scan->scan_res = I2CADM_BUS_SCAN_NO_DEV;
98*32002227SRobert Mustacchi 		break;
99*32002227SRobert Mustacchi 	case I2C_CTRL_E_REQ_TO:
100*32002227SRobert Mustacchi 		scan->scan_res = I2CADM_BUS_SCAN_TIMEOUT;
101*32002227SRobert Mustacchi 		break;
102*32002227SRobert Mustacchi 	default:
103*32002227SRobert Mustacchi 		scan->scan_res = I2CADM_BUS_SCAN_ERROR;
104*32002227SRobert Mustacchi 		scan->scan_error = strdup(i2c_errmsg(hdl));
105*32002227SRobert Mustacchi 		if (scan->scan_error == NULL) {
106*32002227SRobert Mustacchi 			scan->scan_error = "i2c controller error; but "
107*32002227SRobert Mustacchi 			    "failed to duplicate libi2c error message";
108*32002227SRobert Mustacchi 		}
109*32002227SRobert Mustacchi 		break;
110*32002227SRobert Mustacchi 	}
111*32002227SRobert Mustacchi }
112*32002227SRobert Mustacchi 
113*32002227SRobert Mustacchi /*
114*32002227SRobert Mustacchi  * One does not simply scan an i2c device. In essence, we're trying to perform
115*32002227SRobert Mustacchi  * some I/O such that it will ack an address, but without causing the device to
116*32002227SRobert Mustacchi  * wreak havoc. Given the plethora of devices that are out there, this may not
117*32002227SRobert Mustacchi  * be possible. Safety cannot be guaranteed by construction.
118*32002227SRobert Mustacchi  *
119*32002227SRobert Mustacchi  * We basically do a one byte read from the device. Most devices will respond to
120*32002227SRobert Mustacchi  * this. The alternative that some other tools have done is to try to perform an
121*32002227SRobert Mustacchi  * SMBus Quick action.
122*32002227SRobert Mustacchi  */
123*32002227SRobert Mustacchi static void
i2cadm_scan_one(i2c_hdl_t * hdl,i2c_port_t * port,i2cadm_scan_t * scan)124*32002227SRobert Mustacchi i2cadm_scan_one(i2c_hdl_t *hdl, i2c_port_t *port, i2cadm_scan_t *scan)
125*32002227SRobert Mustacchi {
126*32002227SRobert Mustacchi 	i2c_io_req_t *req;
127*32002227SRobert Mustacchi 	uint8_t data = 0x77;
128*32002227SRobert Mustacchi 
129*32002227SRobert Mustacchi 	if (!i2c_io_req_init(port, &req)) {
130*32002227SRobert Mustacchi 		i2cadm_fatal("failed to initialize I/O request");
131*32002227SRobert Mustacchi 	}
132*32002227SRobert Mustacchi 
133*32002227SRobert Mustacchi 	if (!i2c_io_req_set_addr(req, &scan->scan_addr)) {
134*32002227SRobert Mustacchi 		i2cadm_fatal("failed to set scan address");
135*32002227SRobert Mustacchi 	}
136*32002227SRobert Mustacchi 
137*32002227SRobert Mustacchi 	if (!i2c_io_req_set_receive_buf(req, &data, sizeof (data))) {
138*32002227SRobert Mustacchi 		i2cadm_fatal("failed to set receive buffer");
139*32002227SRobert Mustacchi 	}
140*32002227SRobert Mustacchi 
141*32002227SRobert Mustacchi 	if (i2c_io_req_exec(req)) {
142*32002227SRobert Mustacchi 		scan->scan_res = I2CADM_BUS_SCAN_FOUND;
143*32002227SRobert Mustacchi 	} else {
144*32002227SRobert Mustacchi 		i2cadm_scan_error(hdl, scan);
145*32002227SRobert Mustacchi 	}
146*32002227SRobert Mustacchi 
147*32002227SRobert Mustacchi 	i2c_io_req_fini(req);
148*32002227SRobert Mustacchi }
149*32002227SRobert Mustacchi 
150*32002227SRobert Mustacchi typedef enum {
151*32002227SRobert Mustacchi 	I2CADM_SCAN_ADDR,
152*32002227SRobert Mustacchi 	I2CADM_SCAN_RESULT,
153*32002227SRobert Mustacchi 	I2CADM_SCAN_ERROR
154*32002227SRobert Mustacchi } i2cadm_scan_otype_t;
155*32002227SRobert Mustacchi 
156*32002227SRobert Mustacchi static boolean_t
i2cadm_scan_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)157*32002227SRobert Mustacchi i2cadm_scan_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
158*32002227SRobert Mustacchi {
159*32002227SRobert Mustacchi 	const i2cadm_scan_t *scan = ofarg->ofmt_cbarg;
160*32002227SRobert Mustacchi 	const char *str;
161*32002227SRobert Mustacchi 	size_t len;
162*32002227SRobert Mustacchi 
163*32002227SRobert Mustacchi 	switch (ofarg->ofmt_id) {
164*32002227SRobert Mustacchi 	case I2CADM_SCAN_ADDR:
165*32002227SRobert Mustacchi 		len = snprintf(buf, buflen, "0x%x", scan->scan_addr.ia_addr);
166*32002227SRobert Mustacchi 		break;
167*32002227SRobert Mustacchi 	case I2CADM_SCAN_RESULT:
168*32002227SRobert Mustacchi 		switch (scan->scan_res) {
169*32002227SRobert Mustacchi 		case I2CADM_BUS_SCAN_FOUND:
170*32002227SRobert Mustacchi 			str = "found";
171*32002227SRobert Mustacchi 			break;
172*32002227SRobert Mustacchi 		case I2CADM_BUS_SCAN_NO_DEV:
173*32002227SRobert Mustacchi 			str = "missing";
174*32002227SRobert Mustacchi 			break;
175*32002227SRobert Mustacchi 		case I2CADM_BUS_SCAN_RESERVED:
176*32002227SRobert Mustacchi 			str = "reserved";
177*32002227SRobert Mustacchi 			break;
178*32002227SRobert Mustacchi 		case I2CADM_BUS_SCAN_TIMEOUT:
179*32002227SRobert Mustacchi 			str = "timeout";
180*32002227SRobert Mustacchi 			break;
181*32002227SRobert Mustacchi 		case I2CADM_BUS_SCAN_ERROR:
182*32002227SRobert Mustacchi 			str = "error";
183*32002227SRobert Mustacchi 			break;
184*32002227SRobert Mustacchi 		case I2CADM_BUS_SCAN_SKIPPED:
185*32002227SRobert Mustacchi 			str = "skipped";
186*32002227SRobert Mustacchi 			break;
187*32002227SRobert Mustacchi 		default:
188*32002227SRobert Mustacchi 			str = "unknown";
189*32002227SRobert Mustacchi 			break;
190*32002227SRobert Mustacchi 		}
191*32002227SRobert Mustacchi 		len = strlcpy(buf, str, buflen);
192*32002227SRobert Mustacchi 		break;
193*32002227SRobert Mustacchi 	case I2CADM_SCAN_ERROR:
194*32002227SRobert Mustacchi 		if (scan->scan_error != NULL) {
195*32002227SRobert Mustacchi 			len = strlcpy(buf, scan->scan_error, buflen);
196*32002227SRobert Mustacchi 		} else {
197*32002227SRobert Mustacchi 			len = strlcpy(buf, "-", buflen);
198*32002227SRobert Mustacchi 		}
199*32002227SRobert Mustacchi 		break;
200*32002227SRobert Mustacchi 	default:
201*32002227SRobert Mustacchi 		return (B_FALSE);
202*32002227SRobert Mustacchi 	}
203*32002227SRobert Mustacchi 
204*32002227SRobert Mustacchi 	return (len < buflen);
205*32002227SRobert Mustacchi }
206*32002227SRobert Mustacchi 
207*32002227SRobert Mustacchi static const ofmt_field_t i2cadm_scan_ofmt[] = {
208*32002227SRobert Mustacchi 	{ "ADDR", 8, I2CADM_SCAN_ADDR, i2cadm_scan_ofmt_cb },
209*32002227SRobert Mustacchi 	{ "RESULT", 16, I2CADM_SCAN_RESULT, i2cadm_scan_ofmt_cb },
210*32002227SRobert Mustacchi 	{ "ERROR", 40, I2CADM_SCAN_ERROR, i2cadm_scan_ofmt_cb },
211*32002227SRobert Mustacchi 	{ NULL, 0, 0, NULL }
212*32002227SRobert Mustacchi };
213*32002227SRobert Mustacchi 
214*32002227SRobert Mustacchi /*
215*32002227SRobert Mustacchi  * We can fit 16 devices on a line.
216*32002227SRobert Mustacchi  */
217*32002227SRobert Mustacchi #define	SCAN_DEVS_PER_LINE	0x10
218*32002227SRobert Mustacchi 
219*32002227SRobert Mustacchi static const char *key = ""
220*32002227SRobert Mustacchi "\t- = No Device      @ = Device Found\n"
221*32002227SRobert Mustacchi "\tR = Reserved       S = Skipped\n"
222*32002227SRobert Mustacchi "\tX = Timed Out    Err = Error\n";
223*32002227SRobert Mustacchi 
224*32002227SRobert Mustacchi static bool
i2cadm_scan_table_cb(void * arg,uint16_t i)225*32002227SRobert Mustacchi i2cadm_scan_table_cb(void *arg, uint16_t i)
226*32002227SRobert Mustacchi {
227*32002227SRobert Mustacchi 	const i2cadm_scan_t *results = arg;
228*32002227SRobert Mustacchi 	const char *msgs[] = { "???", "@", "-", "R", "X", "Err", "S" };
229*32002227SRobert Mustacchi 
230*32002227SRobert Mustacchi 	(void) printf("%3s", msgs[results[i].scan_res]);
231*32002227SRobert Mustacchi 
232*32002227SRobert Mustacchi 	return (results[i].scan_res == I2CADM_BUS_SCAN_ERROR);
233*32002227SRobert Mustacchi }
234*32002227SRobert Mustacchi 
235*32002227SRobert Mustacchi static void
i2cadm_scan_table_post(void * arg,uint16_t max_addr)236*32002227SRobert Mustacchi i2cadm_scan_table_post(void *arg, uint16_t max_addr)
237*32002227SRobert Mustacchi {
238*32002227SRobert Mustacchi 	const i2cadm_scan_t *results = arg;
239*32002227SRobert Mustacchi 	(void) printf("\nErrors\n");
240*32002227SRobert Mustacchi 	for (uint16_t i = 0; i < max_addr; i++) {
241*32002227SRobert Mustacchi 		if (results[i].scan_res != I2CADM_BUS_SCAN_ERROR)
242*32002227SRobert Mustacchi 			continue;
243*32002227SRobert Mustacchi 
244*32002227SRobert Mustacchi 		if (max_addr > UINT8_MAX) {
245*32002227SRobert Mustacchi 			(void) printf("0x%03x: %s\n", i, results[i].scan_error);
246*32002227SRobert Mustacchi 		} else {
247*32002227SRobert Mustacchi 			(void) printf("0x%02x: %s\n", i, results[i].scan_error);
248*32002227SRobert Mustacchi 		}
249*32002227SRobert Mustacchi 	}
250*32002227SRobert Mustacchi }
251*32002227SRobert Mustacchi 
252*32002227SRobert Mustacchi int
i2cadm_scan(int argc,char * argv[])253*32002227SRobert Mustacchi i2cadm_scan(int argc, char *argv[])
254*32002227SRobert Mustacchi {
255*32002227SRobert Mustacchi 	int c;
256*32002227SRobert Mustacchi 	i2c_port_t *port;
257*32002227SRobert Mustacchi 	bool ten_bit = false;
258*32002227SRobert Mustacchi 	uint16_t max_addr = 1 << 7;
259*32002227SRobert Mustacchi 	i2cadm_scan_t *results;
260*32002227SRobert Mustacchi 	uint16_t *dev_addrs = NULL;
261*32002227SRobert Mustacchi 	size_t naddrs = 0, nalloc = 0;
262*32002227SRobert Mustacchi 	boolean_t parse = B_FALSE;
263*32002227SRobert Mustacchi 	uint_t flags = 0;
264*32002227SRobert Mustacchi 	const char *fields = NULL;
265*32002227SRobert Mustacchi 	ofmt_status_t oferr;
266*32002227SRobert Mustacchi 	ofmt_handle_t ofmt;
267*32002227SRobert Mustacchi 
268*32002227SRobert Mustacchi 	/*
269*32002227SRobert Mustacchi 	 * In the future we should consider -T for 10-bit addressing.
270*32002227SRobert Mustacchi 	 */
271*32002227SRobert Mustacchi 	while ((c = getopt(argc, argv, ":d:Ho:p")) != -1) {
272*32002227SRobert Mustacchi 		switch (c) {
273*32002227SRobert Mustacchi 		case 'd': {
274*32002227SRobert Mustacchi 			if (naddrs == nalloc) {
275*32002227SRobert Mustacchi 				nalloc += 8;
276*32002227SRobert Mustacchi 				dev_addrs = recallocarray(dev_addrs, naddrs,
277*32002227SRobert Mustacchi 				    nalloc, sizeof (uint16_t));
278*32002227SRobert Mustacchi 				if (dev_addrs == NULL) {
279*32002227SRobert Mustacchi 					err(EXIT_FAILURE, "failed to allocate "
280*32002227SRobert Mustacchi 					    "memory for %zu I2C addresses",
281*32002227SRobert Mustacchi 					    nalloc);
282*32002227SRobert Mustacchi 				}
283*32002227SRobert Mustacchi 			}
284*32002227SRobert Mustacchi 
285*32002227SRobert Mustacchi 			const char *err;
286*32002227SRobert Mustacchi 			long long l = strtonumx(optarg, 0, max_addr - 1, &err,
287*32002227SRobert Mustacchi 			    0);
288*32002227SRobert Mustacchi 			if (err != NULL) {
289*32002227SRobert Mustacchi 				errx(EXIT_FAILURE, "invalid device address %s: "
290*32002227SRobert Mustacchi 				    "address is %s", optarg, err);
291*32002227SRobert Mustacchi 			}
292*32002227SRobert Mustacchi 			dev_addrs[naddrs] = (uint16_t)l;
293*32002227SRobert Mustacchi 			naddrs++;
294*32002227SRobert Mustacchi 			break;
295*32002227SRobert Mustacchi 		}
296*32002227SRobert Mustacchi 		case 'H':
297*32002227SRobert Mustacchi 			flags |= OFMT_NOHEADER;
298*32002227SRobert Mustacchi 			break;
299*32002227SRobert Mustacchi 		case 'o':
300*32002227SRobert Mustacchi 			fields = optarg;
301*32002227SRobert Mustacchi 			break;
302*32002227SRobert Mustacchi 		case 'p':
303*32002227SRobert Mustacchi 			parse = B_TRUE;
304*32002227SRobert Mustacchi 			flags |= OFMT_PARSABLE;
305*32002227SRobert Mustacchi 			break;
306*32002227SRobert Mustacchi 		case ':':
307*32002227SRobert Mustacchi 			i2cadm_scan_help("option -%c requires an argument",
308*32002227SRobert Mustacchi 			    optopt);
309*32002227SRobert Mustacchi 			exit(EXIT_USAGE);
310*32002227SRobert Mustacchi 		case '?':
311*32002227SRobert Mustacchi 			i2cadm_scan_help("unknown option: -%c", optopt);
312*32002227SRobert Mustacchi 			exit(EXIT_USAGE);
313*32002227SRobert Mustacchi 		}
314*32002227SRobert Mustacchi 	}
315*32002227SRobert Mustacchi 
316*32002227SRobert Mustacchi 	argv += optind;
317*32002227SRobert Mustacchi 	argc -= optind;
318*32002227SRobert Mustacchi 	if (argc == 0) {
319*32002227SRobert Mustacchi 		errx(EXIT_USAGE, "missing required port to scan");
320*32002227SRobert Mustacchi 	} else if (argc > 1) {
321*32002227SRobert Mustacchi 		errx(EXIT_USAGE, "encountered extraneous arguments starting "
322*32002227SRobert Mustacchi 		    "with %s", argv[1]);
323*32002227SRobert Mustacchi 	}
324*32002227SRobert Mustacchi 
325*32002227SRobert Mustacchi 	if (parse && fields == NULL) {
326*32002227SRobert Mustacchi 		errx(EXIT_USAGE, "-p requires fields specified with -o");
327*32002227SRobert Mustacchi 	}
328*32002227SRobert Mustacchi 
329*32002227SRobert Mustacchi 	if (flags != 0 && fields == NULL) {
330*32002227SRobert Mustacchi 		errx(EXIT_USAGE, "-H can only be used with -o");
331*32002227SRobert Mustacchi 	}
332*32002227SRobert Mustacchi 
333*32002227SRobert Mustacchi 	if (fields != NULL) {
334*32002227SRobert Mustacchi 		if (!parse) {
335*32002227SRobert Mustacchi 			flags |= OFMT_WRAP;
336*32002227SRobert Mustacchi 		}
337*32002227SRobert Mustacchi 
338*32002227SRobert Mustacchi 		oferr = ofmt_open(fields, i2cadm_scan_ofmt, flags, 0,
339*32002227SRobert Mustacchi 		    &ofmt);
340*32002227SRobert Mustacchi 		ofmt_check(oferr, parse, ofmt, i2cadm_ofmt_errx, warnx);
341*32002227SRobert Mustacchi 	}
342*32002227SRobert Mustacchi 
343*32002227SRobert Mustacchi 	if (!i2c_port_init_by_path(i2cadm.i2c_hdl, argv[0], &port)) {
344*32002227SRobert Mustacchi 		i2cadm_fatal("failed to parse port path %s", argv[0]);
345*32002227SRobert Mustacchi 	}
346*32002227SRobert Mustacchi 
347*32002227SRobert Mustacchi 	results = calloc(max_addr, sizeof (i2cadm_scan_t));
348*32002227SRobert Mustacchi 	if (results == NULL) {
349*32002227SRobert Mustacchi 		err(EXIT_FAILURE, "failed to allocate scan results tracking "
350*32002227SRobert Mustacchi 		    "structure");
351*32002227SRobert Mustacchi 	}
352*32002227SRobert Mustacchi 
353*32002227SRobert Mustacchi 	/*
354*32002227SRobert Mustacchi 	 * If we have a specific device list, then mark everything skipped and
355*32002227SRobert Mustacchi 	 * come back and mark the specific instances we care about as things to
356*32002227SRobert Mustacchi 	 * check.
357*32002227SRobert Mustacchi 	 */
358*32002227SRobert Mustacchi 	if (dev_addrs != NULL) {
359*32002227SRobert Mustacchi 		for (uint16_t i = 0; i < max_addr; i++) {
360*32002227SRobert Mustacchi 			results[i].scan_res = I2CADM_BUS_SCAN_SKIPPED;
361*32002227SRobert Mustacchi 		}
362*32002227SRobert Mustacchi 
363*32002227SRobert Mustacchi 		for (uint16_t i = 0; i < naddrs; i++) {
364*32002227SRobert Mustacchi 			results[dev_addrs[i]].scan_res =
365*32002227SRobert Mustacchi 			    I2CADM_BUS_SCAN_UNKNOWN;
366*32002227SRobert Mustacchi 		}
367*32002227SRobert Mustacchi 	}
368*32002227SRobert Mustacchi 
369*32002227SRobert Mustacchi 	for (uint16_t i = 0; i < max_addr; i++) {
370*32002227SRobert Mustacchi 		i2cadm_scan_t *scan = &results[i];
371*32002227SRobert Mustacchi 
372*32002227SRobert Mustacchi 		scan->scan_addr.ia_type = ten_bit ? I2C_ADDR_10BIT :
373*32002227SRobert Mustacchi 		    I2C_ADDR_7BIT;
374*32002227SRobert Mustacchi 		scan->scan_addr.ia_addr = i;
375*32002227SRobert Mustacchi 
376*32002227SRobert Mustacchi 		if (scan->scan_res == I2CADM_BUS_SCAN_SKIPPED)
377*32002227SRobert Mustacchi 			continue;
378*32002227SRobert Mustacchi 
379*32002227SRobert Mustacchi 		/*
380*32002227SRobert Mustacchi 		 * Determine if this is a reserved address or not.
381*32002227SRobert Mustacchi 		 */
382*32002227SRobert Mustacchi 		if (i2c_addr_reserved(&scan->scan_addr)) {
383*32002227SRobert Mustacchi 			scan->scan_res = I2CADM_BUS_SCAN_RESERVED;
384*32002227SRobert Mustacchi 			continue;
385*32002227SRobert Mustacchi 		}
386*32002227SRobert Mustacchi 
387*32002227SRobert Mustacchi 		i2cadm_scan_one(i2cadm.i2c_hdl, port, scan);
388*32002227SRobert Mustacchi 	}
389*32002227SRobert Mustacchi 
390*32002227SRobert Mustacchi 	if (fields == NULL) {
391*32002227SRobert Mustacchi 		i2cadm_table_t table = {
392*32002227SRobert Mustacchi 			.table_port = argv[0],
393*32002227SRobert Mustacchi 			.table_key = key,
394*32002227SRobert Mustacchi 			.table_msg = "Device scan on",
395*32002227SRobert Mustacchi 			.table_max = max_addr,
396*32002227SRobert Mustacchi 			.table_cb = i2cadm_scan_table_cb,
397*32002227SRobert Mustacchi 			.table_post = i2cadm_scan_table_post
398*32002227SRobert Mustacchi 		};
399*32002227SRobert Mustacchi 		i2cadm_print_table(&table, results);
400*32002227SRobert Mustacchi 	} else {
401*32002227SRobert Mustacchi 		for (uint16_t i = 0; i < max_addr; i++) {
402*32002227SRobert Mustacchi 			ofmt_print(ofmt, &results[i]);
403*32002227SRobert Mustacchi 		}
404*32002227SRobert Mustacchi 		ofmt_close(ofmt);
405*32002227SRobert Mustacchi 	}
406*32002227SRobert Mustacchi 
407*32002227SRobert Mustacchi 	for (uint16_t i = 0; i < max_addr; i++) {
408*32002227SRobert Mustacchi 		free(results[i].scan_error);
409*32002227SRobert Mustacchi 	}
410*32002227SRobert Mustacchi 	free(results);
411*32002227SRobert Mustacchi 	i2c_port_fini(port);
412*32002227SRobert Mustacchi 	return (EXIT_SUCCESS);
413*32002227SRobert Mustacchi }
414