xref: /illumos-gate/usr/src/lib/libi2c/common/libi2c_mux.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  * Mux discovery support
18*32002227SRobert Mustacchi  */
19*32002227SRobert Mustacchi 
20*32002227SRobert Mustacchi #include <string.h>
21*32002227SRobert Mustacchi #include <stdlib.h>
22*32002227SRobert Mustacchi #include <fcntl.h>
23*32002227SRobert Mustacchi #include <unistd.h>
24*32002227SRobert Mustacchi 
25*32002227SRobert Mustacchi #include "libi2c_impl.h"
26*32002227SRobert Mustacchi 
27*32002227SRobert Mustacchi void
i2c_mux_discover_fini(i2c_mux_iter_t * iter)28*32002227SRobert Mustacchi i2c_mux_discover_fini(i2c_mux_iter_t *iter)
29*32002227SRobert Mustacchi {
30*32002227SRobert Mustacchi 	if (iter == NULL)
31*32002227SRobert Mustacchi 		return;
32*32002227SRobert Mustacchi 	di_fini(iter->mi_root);
33*32002227SRobert Mustacchi 	free(iter);
34*32002227SRobert Mustacchi }
35*32002227SRobert Mustacchi 
36*32002227SRobert Mustacchi i2c_iter_t
i2c_mux_discover_step(i2c_mux_iter_t * iter,const i2c_mux_disc_t ** discp)37*32002227SRobert Mustacchi i2c_mux_discover_step(i2c_mux_iter_t *iter, const i2c_mux_disc_t **discp)
38*32002227SRobert Mustacchi {
39*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = iter->mi_hdl;
40*32002227SRobert Mustacchi 	*discp = NULL;
41*32002227SRobert Mustacchi 
42*32002227SRobert Mustacchi 	if (iter->mi_done) {
43*32002227SRobert Mustacchi 		return (I2C_ITER_DONE);
44*32002227SRobert Mustacchi 	}
45*32002227SRobert Mustacchi 
46*32002227SRobert Mustacchi 	for (;;) {
47*32002227SRobert Mustacchi 		if (iter->mi_cur == DI_NODE_NIL) {
48*32002227SRobert Mustacchi 			iter->mi_cur = di_drv_first_node(I2C_NEX_DRV,
49*32002227SRobert Mustacchi 			    iter->mi_root);
50*32002227SRobert Mustacchi 		} else {
51*32002227SRobert Mustacchi 			iter->mi_cur = di_drv_next_node(iter->mi_cur);
52*32002227SRobert Mustacchi 		}
53*32002227SRobert Mustacchi 
54*32002227SRobert Mustacchi 		if (iter->mi_cur == DI_NODE_NIL) {
55*32002227SRobert Mustacchi 			iter->mi_done = true;
56*32002227SRobert Mustacchi 			return (I2C_ITER_DONE);
57*32002227SRobert Mustacchi 		}
58*32002227SRobert Mustacchi 
59*32002227SRobert Mustacchi 		if (!i2c_node_is_type(iter->mi_cur, I2C_NODE_T_MUX)) {
60*32002227SRobert Mustacchi 			continue;
61*32002227SRobert Mustacchi 		}
62*32002227SRobert Mustacchi 
63*32002227SRobert Mustacchi 		di_minor_t m = i2c_node_minor(iter->mi_cur);
64*32002227SRobert Mustacchi 		if (m == DI_MINOR_NIL) {
65*32002227SRobert Mustacchi 			continue;
66*32002227SRobert Mustacchi 		}
67*32002227SRobert Mustacchi 
68*32002227SRobert Mustacchi 		iter->mi_disc.md_devi = iter->mi_cur;
69*32002227SRobert Mustacchi 		iter->mi_disc.md_minor = m;
70*32002227SRobert Mustacchi 		if (!i2c_node_to_path(hdl, iter->mi_cur, iter->mi_disc.md_path,
71*32002227SRobert Mustacchi 		    sizeof (iter->mi_disc.md_path))) {
72*32002227SRobert Mustacchi 			return (I2C_ITER_ERROR);
73*32002227SRobert Mustacchi 		}
74*32002227SRobert Mustacchi 
75*32002227SRobert Mustacchi 		char *mpath = di_devfs_minor_path(m);
76*32002227SRobert Mustacchi 		if (mpath == NULL) {
77*32002227SRobert Mustacchi 			int e = errno;
78*32002227SRobert Mustacchi 			di_devfs_path_free(mpath);
79*32002227SRobert Mustacchi 			(void) i2c_error(hdl, I2C_ERR_LIBDEVINFO, e,
80*32002227SRobert Mustacchi 			    "failed to get minor path for %s@%s:%s",
81*32002227SRobert Mustacchi 			    di_node_name(iter->mi_cur),
82*32002227SRobert Mustacchi 			    di_bus_addr(iter->mi_cur), di_minor_name(m));
83*32002227SRobert Mustacchi 			return (I2C_ITER_ERROR);
84*32002227SRobert Mustacchi 		}
85*32002227SRobert Mustacchi 
86*32002227SRobert Mustacchi 		int fd = openat(hdl->ih_devfd, mpath + 1, O_RDONLY);
87*32002227SRobert Mustacchi 		if (fd < 0) {
88*32002227SRobert Mustacchi 			int e = errno;
89*32002227SRobert Mustacchi 			di_devfs_path_free(mpath);
90*32002227SRobert Mustacchi 			(void) i2c_error(hdl, I2C_ERR_OPEN_DEV, e, "failed to "
91*32002227SRobert Mustacchi 			    "open device path '/devices%s: %s", mpath,
92*32002227SRobert Mustacchi 			    strerrordesc_np(e));
93*32002227SRobert Mustacchi 			return (I2C_ITER_ERROR);
94*32002227SRobert Mustacchi 		}
95*32002227SRobert Mustacchi 		di_devfs_path_free(mpath);
96*32002227SRobert Mustacchi 
97*32002227SRobert Mustacchi 		if (ioctl(fd, UI2C_IOCTL_MUX_INFO, &iter->mi_disc.md_info) !=
98*32002227SRobert Mustacchi 		    0) {
99*32002227SRobert Mustacchi 			int e = errno;
100*32002227SRobert Mustacchi 			(void) close(fd);
101*32002227SRobert Mustacchi 			(void) i2c_ioctl_syserror(hdl, e, "mux information "
102*32002227SRobert Mustacchi 			    "request");
103*32002227SRobert Mustacchi 			return (I2C_ITER_ERROR);
104*32002227SRobert Mustacchi 		}
105*32002227SRobert Mustacchi 		(void) close(fd);
106*32002227SRobert Mustacchi 		if (iter->mi_disc.md_info.umi_error.i2c_error !=
107*32002227SRobert Mustacchi 		    I2C_CORE_E_OK) {
108*32002227SRobert Mustacchi 			(void) i2c_ioctl_error(hdl,
109*32002227SRobert Mustacchi 			    &iter->mi_disc.md_info.umi_error, "mux information "
110*32002227SRobert Mustacchi 			    "request");
111*32002227SRobert Mustacchi 			return (I2C_ITER_ERROR);
112*32002227SRobert Mustacchi 		}
113*32002227SRobert Mustacchi 
114*32002227SRobert Mustacchi 		*discp = &iter->mi_disc;
115*32002227SRobert Mustacchi 		return (I2C_ITER_VALID);
116*32002227SRobert Mustacchi 	}
117*32002227SRobert Mustacchi }
118*32002227SRobert Mustacchi 
119*32002227SRobert Mustacchi bool
i2c_mux_discover_init(i2c_hdl_t * hdl,i2c_mux_iter_t ** iterp)120*32002227SRobert Mustacchi i2c_mux_discover_init(i2c_hdl_t *hdl, i2c_mux_iter_t **iterp)
121*32002227SRobert Mustacchi {
122*32002227SRobert Mustacchi 	i2c_mux_iter_t *iter;
123*32002227SRobert Mustacchi 
124*32002227SRobert Mustacchi 	if (iterp == NULL) {
125*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
126*32002227SRobert Mustacchi 		    "invalid i2c_mux_iter_t output pointer: %p", iterp));
127*32002227SRobert Mustacchi 	}
128*32002227SRobert Mustacchi 
129*32002227SRobert Mustacchi 	iter = calloc(1, sizeof (i2c_mux_iter_t));
130*32002227SRobert Mustacchi 	if (iter == NULL) {
131*32002227SRobert Mustacchi 		int e = errno;
132*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
133*32002227SRobert Mustacchi 		    "memory for a new i2c_mux_iter_t"));
134*32002227SRobert Mustacchi 	}
135*32002227SRobert Mustacchi 
136*32002227SRobert Mustacchi 	iter->mi_hdl = hdl;
137*32002227SRobert Mustacchi 	iter->mi_root = di_init("/", DINFOCPYALL);
138*32002227SRobert Mustacchi 	if (iter->mi_root == NULL) {
139*32002227SRobert Mustacchi 		int e = errno;
140*32002227SRobert Mustacchi 		i2c_mux_discover_fini(iter);
141*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to "
142*32002227SRobert Mustacchi 		    "initialize devinfo snapshot: %s", strerrordesc_np(e)));
143*32002227SRobert Mustacchi 	}
144*32002227SRobert Mustacchi 	iter->mi_done = false;
145*32002227SRobert Mustacchi 	iter->mi_cur = DI_NODE_NIL;
146*32002227SRobert Mustacchi 
147*32002227SRobert Mustacchi 	*iterp = iter;
148*32002227SRobert Mustacchi 	return (i2c_success(hdl));
149*32002227SRobert Mustacchi }
150*32002227SRobert Mustacchi 
151*32002227SRobert Mustacchi bool
i2c_mux_discover(i2c_hdl_t * hdl,i2c_mux_disc_f func,void * arg)152*32002227SRobert Mustacchi i2c_mux_discover(i2c_hdl_t *hdl, i2c_mux_disc_f func, void *arg)
153*32002227SRobert Mustacchi {
154*32002227SRobert Mustacchi 	i2c_mux_iter_t *iter;
155*32002227SRobert Mustacchi 	const i2c_mux_disc_t *disc;
156*32002227SRobert Mustacchi 	i2c_iter_t ret;
157*32002227SRobert Mustacchi 
158*32002227SRobert Mustacchi 	if (func == NULL) {
159*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
160*32002227SRobert Mustacchi 		    "invalid i2c_mux_disc_f function pointer: %p", func));
161*32002227SRobert Mustacchi 	}
162*32002227SRobert Mustacchi 
163*32002227SRobert Mustacchi 	if (!i2c_mux_discover_init(hdl, &iter)) {
164*32002227SRobert Mustacchi 		return (false);
165*32002227SRobert Mustacchi 	}
166*32002227SRobert Mustacchi 
167*32002227SRobert Mustacchi 	while ((ret = i2c_mux_discover_step(iter, &disc)) == I2C_ITER_VALID) {
168*32002227SRobert Mustacchi 		if (!func(hdl, disc, arg))
169*32002227SRobert Mustacchi 			break;
170*32002227SRobert Mustacchi 	}
171*32002227SRobert Mustacchi 
172*32002227SRobert Mustacchi 	i2c_mux_discover_fini(iter);
173*32002227SRobert Mustacchi 	if (ret == I2C_ITER_ERROR) {
174*32002227SRobert Mustacchi 		return (false);
175*32002227SRobert Mustacchi 	}
176*32002227SRobert Mustacchi 
177*32002227SRobert Mustacchi 	return (i2c_success(hdl));
178*32002227SRobert Mustacchi }
179*32002227SRobert Mustacchi 
180*32002227SRobert Mustacchi di_node_t
i2c_mux_disc_devi(const i2c_mux_disc_t * disc)181*32002227SRobert Mustacchi i2c_mux_disc_devi(const i2c_mux_disc_t *disc)
182*32002227SRobert Mustacchi {
183*32002227SRobert Mustacchi 	return (disc->md_devi);
184*32002227SRobert Mustacchi }
185*32002227SRobert Mustacchi 
186*32002227SRobert Mustacchi di_minor_t
i2c_mux_disc_devctl(const i2c_mux_disc_t * disc)187*32002227SRobert Mustacchi i2c_mux_disc_devctl(const i2c_mux_disc_t *disc)
188*32002227SRobert Mustacchi {
189*32002227SRobert Mustacchi 	return (disc->md_minor);
190*32002227SRobert Mustacchi }
191*32002227SRobert Mustacchi 
192*32002227SRobert Mustacchi const char *
i2c_mux_disc_name(const i2c_mux_disc_t * disc)193*32002227SRobert Mustacchi i2c_mux_disc_name(const i2c_mux_disc_t *disc)
194*32002227SRobert Mustacchi {
195*32002227SRobert Mustacchi 	return (di_bus_addr(disc->md_devi));
196*32002227SRobert Mustacchi }
197*32002227SRobert Mustacchi 
198*32002227SRobert Mustacchi const char *
i2c_mux_disc_path(const i2c_mux_disc_t * disc)199*32002227SRobert Mustacchi i2c_mux_disc_path(const i2c_mux_disc_t *disc)
200*32002227SRobert Mustacchi {
201*32002227SRobert Mustacchi 	return (disc->md_path);
202*32002227SRobert Mustacchi }
203*32002227SRobert Mustacchi 
204*32002227SRobert Mustacchi uint32_t
i2c_mux_disc_nports(const i2c_mux_disc_t * disc)205*32002227SRobert Mustacchi i2c_mux_disc_nports(const i2c_mux_disc_t *disc)
206*32002227SRobert Mustacchi {
207*32002227SRobert Mustacchi 	return (disc->md_info.umi_nports);
208*32002227SRobert Mustacchi }
209