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