xref: /illumos-gate/usr/src/lib/libi2c/common/libi2c_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  * I2C port discovery and initialization.
18*32002227SRobert Mustacchi  */
19*32002227SRobert Mustacchi 
20*32002227SRobert Mustacchi #include <stdlib.h>
21*32002227SRobert Mustacchi #include <string.h>
22*32002227SRobert Mustacchi #include <unistd.h>
23*32002227SRobert Mustacchi #include <sys/types.h>
24*32002227SRobert Mustacchi #include <sys/stat.h>
25*32002227SRobert Mustacchi #include <fcntl.h>
26*32002227SRobert Mustacchi 
27*32002227SRobert Mustacchi #include "libi2c_impl.h"
28*32002227SRobert Mustacchi 
29*32002227SRobert Mustacchi void
i2c_port_discover_fini(i2c_port_iter_t * iter)30*32002227SRobert Mustacchi i2c_port_discover_fini(i2c_port_iter_t *iter)
31*32002227SRobert Mustacchi {
32*32002227SRobert Mustacchi 	if (iter == NULL) {
33*32002227SRobert Mustacchi 		return;
34*32002227SRobert Mustacchi 	}
35*32002227SRobert Mustacchi 
36*32002227SRobert Mustacchi 	free(iter->pi_ports);
37*32002227SRobert Mustacchi 	di_fini(iter->pi_root);
38*32002227SRobert Mustacchi 	free(iter);
39*32002227SRobert Mustacchi }
40*32002227SRobert Mustacchi 
41*32002227SRobert Mustacchi i2c_iter_t
i2c_port_discover_step(i2c_port_iter_t * iter,const i2c_port_disc_t ** discp)42*32002227SRobert Mustacchi i2c_port_discover_step(i2c_port_iter_t *iter, const i2c_port_disc_t **discp)
43*32002227SRobert Mustacchi {
44*32002227SRobert Mustacchi 	for (;;) {
45*32002227SRobert Mustacchi 		if (iter->pi_curport == iter->pi_nports) {
46*32002227SRobert Mustacchi 			return (I2C_ITER_DONE);
47*32002227SRobert Mustacchi 		}
48*32002227SRobert Mustacchi 
49*32002227SRobert Mustacchi 		iter->pi_disc.pd_devi = iter->pi_ports[iter->pi_curport];
50*32002227SRobert Mustacchi 		iter->pi_curport++;
51*32002227SRobert Mustacchi 
52*32002227SRobert Mustacchi 		if (!i2c_node_to_path(iter->pi_hdl, iter->pi_disc.pd_devi,
53*32002227SRobert Mustacchi 		    iter->pi_disc.pd_path, sizeof (iter->pi_disc.pd_path))) {
54*32002227SRobert Mustacchi 			continue;
55*32002227SRobert Mustacchi 		}
56*32002227SRobert Mustacchi 
57*32002227SRobert Mustacchi 		*discp = &iter->pi_disc;
58*32002227SRobert Mustacchi 		return (I2C_ITER_VALID);
59*32002227SRobert Mustacchi 	}
60*32002227SRobert Mustacchi }
61*32002227SRobert Mustacchi 
62*32002227SRobert Mustacchi /*
63*32002227SRobert Mustacchi  * We have two nodes that are not the same. They are at the same point in the
64*32002227SRobert Mustacchi  * tree though. We expect all devices at a given layer in the tree to have the
65*32002227SRobert Mustacchi  * same type. We start with the node name and if they're the same fall back to
66*32002227SRobert Mustacchi  * the bus address which must be unique at this point.
67*32002227SRobert Mustacchi  */
68*32002227SRobert Mustacchi static int
i2c_port_sort_node(di_node_t l,di_node_t r)69*32002227SRobert Mustacchi i2c_port_sort_node(di_node_t l, di_node_t r)
70*32002227SRobert Mustacchi {
71*32002227SRobert Mustacchi 	const char *nl = di_node_name(l);
72*32002227SRobert Mustacchi 	const char *nr = di_node_name(r);
73*32002227SRobert Mustacchi 	int ret;
74*32002227SRobert Mustacchi 
75*32002227SRobert Mustacchi 	if ((ret = strcmp(nl, nr)) == 0) {
76*32002227SRobert Mustacchi 		nl = di_bus_addr(l);
77*32002227SRobert Mustacchi 		nr = di_bus_addr(r);
78*32002227SRobert Mustacchi 		ret = strcmp(nl, nr);
79*32002227SRobert Mustacchi 	}
80*32002227SRobert Mustacchi 
81*32002227SRobert Mustacchi 	return (ret);
82*32002227SRobert Mustacchi }
83*32002227SRobert Mustacchi 
84*32002227SRobert Mustacchi typedef struct {
85*32002227SRobert Mustacchi 	di_node_t ni_ctrl;
86*32002227SRobert Mustacchi 	di_node_t ni_parent;
87*32002227SRobert Mustacchi 	uint32_t ni_height;
88*32002227SRobert Mustacchi } node_info_t;
89*32002227SRobert Mustacchi 
90*32002227SRobert Mustacchi static void
i2c_port_sort_info(di_node_t dn,node_info_t * info)91*32002227SRobert Mustacchi i2c_port_sort_info(di_node_t dn, node_info_t *info)
92*32002227SRobert Mustacchi {
93*32002227SRobert Mustacchi 	(void) memset(info, 0, sizeof (node_info_t));
94*32002227SRobert Mustacchi 
95*32002227SRobert Mustacchi 	info->ni_parent = di_parent_node(dn);
96*32002227SRobert Mustacchi 	for (;;) {
97*32002227SRobert Mustacchi 		i2c_node_type_t type = i2c_node_type(dn);
98*32002227SRobert Mustacchi 
99*32002227SRobert Mustacchi 		if (type == I2C_NODE_T_CTRL) {
100*32002227SRobert Mustacchi 			info->ni_ctrl = dn;
101*32002227SRobert Mustacchi 			return;
102*32002227SRobert Mustacchi 		}
103*32002227SRobert Mustacchi 
104*32002227SRobert Mustacchi 		info->ni_height++;
105*32002227SRobert Mustacchi 		dn = di_parent_node(dn);
106*32002227SRobert Mustacchi 		if (dn == DI_NODE_NIL) {
107*32002227SRobert Mustacchi 			return;
108*32002227SRobert Mustacchi 		}
109*32002227SRobert Mustacchi 	}
110*32002227SRobert Mustacchi }
111*32002227SRobert Mustacchi 
112*32002227SRobert Mustacchi static int
i2c_port_sort(const void * left,const void * right)113*32002227SRobert Mustacchi i2c_port_sort(const void *left, const void *right)
114*32002227SRobert Mustacchi {
115*32002227SRobert Mustacchi 	di_node_t l = *(di_node_t *)left;
116*32002227SRobert Mustacchi 	di_node_t r = *(di_node_t *)right;
117*32002227SRobert Mustacchi 	node_info_t li, ri;
118*32002227SRobert Mustacchi 
119*32002227SRobert Mustacchi 	if (l == r) {
120*32002227SRobert Mustacchi 		return (0);
121*32002227SRobert Mustacchi 	}
122*32002227SRobert Mustacchi 
123*32002227SRobert Mustacchi 	/*
124*32002227SRobert Mustacchi 	 * We have two nodes that are different points in the tree. We basically
125*32002227SRobert Mustacchi 	 * want to ask:
126*32002227SRobert Mustacchi 	 *
127*32002227SRobert Mustacchi 	 *  - What controller do they point to?
128*32002227SRobert Mustacchi 	 *  - What are the relative heights in the tree?
129*32002227SRobert Mustacchi 	 *
130*32002227SRobert Mustacchi 	 * If they belong to different controllers, we sort based on the
131*32002227SRobert Mustacchi 	 * controller's name. If they belong to the same controller, then we use
132*32002227SRobert Mustacchi 	 * the height in the tree. If they have the same height, we then see if
133*32002227SRobert Mustacchi 	 * they have the same parent. If they don't, we sort on the parent (like
134*32002227SRobert Mustacchi 	 * the controller). If they do, we use their name directly.
135*32002227SRobert Mustacchi 	 */
136*32002227SRobert Mustacchi 	i2c_port_sort_info(l, &li);
137*32002227SRobert Mustacchi 	i2c_port_sort_info(r, &ri);
138*32002227SRobert Mustacchi 
139*32002227SRobert Mustacchi 	if (li.ni_ctrl != ri.ni_ctrl) {
140*32002227SRobert Mustacchi 		return (i2c_port_sort_node(li.ni_ctrl, ri.ni_ctrl));
141*32002227SRobert Mustacchi 	}
142*32002227SRobert Mustacchi 
143*32002227SRobert Mustacchi 	if (li.ni_height != ri.ni_height) {
144*32002227SRobert Mustacchi 		return (li.ni_height < ri.ni_height ? -1 : 1);
145*32002227SRobert Mustacchi 	}
146*32002227SRobert Mustacchi 
147*32002227SRobert Mustacchi 	if (li.ni_parent != ri.ni_parent) {
148*32002227SRobert Mustacchi 		return (i2c_port_sort_node(li.ni_parent, ri.ni_parent));
149*32002227SRobert Mustacchi 	}
150*32002227SRobert Mustacchi 
151*32002227SRobert Mustacchi 	return (i2c_port_sort_node(l, r));
152*32002227SRobert Mustacchi }
153*32002227SRobert Mustacchi bool
i2c_port_discover_init(i2c_hdl_t * hdl,i2c_port_iter_t ** iterp)154*32002227SRobert Mustacchi i2c_port_discover_init(i2c_hdl_t *hdl, i2c_port_iter_t **iterp)
155*32002227SRobert Mustacchi {
156*32002227SRobert Mustacchi 	i2c_port_iter_t *iter;
157*32002227SRobert Mustacchi 
158*32002227SRobert Mustacchi 	if (iterp == NULL) {
159*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
160*32002227SRobert Mustacchi 		    "invalid i2c_port_iter_t output pointer: %p", iterp));
161*32002227SRobert Mustacchi 	}
162*32002227SRobert Mustacchi 
163*32002227SRobert Mustacchi 	iter = calloc(1, sizeof (i2c_port_iter_t));
164*32002227SRobert Mustacchi 	if (iter == NULL) {
165*32002227SRobert Mustacchi 		int e = errno;
166*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
167*32002227SRobert Mustacchi 		    "memory for a new i2c_port_iter_t"));
168*32002227SRobert Mustacchi 	}
169*32002227SRobert Mustacchi 
170*32002227SRobert Mustacchi 	iter->pi_root = di_init("/", DINFOCPYALL);
171*32002227SRobert Mustacchi 	if (iter->pi_root == NULL) {
172*32002227SRobert Mustacchi 		int e = errno;
173*32002227SRobert Mustacchi 		i2c_port_discover_fini(iter);
174*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to "
175*32002227SRobert Mustacchi 		    "initialize devinfo snapshot: %s", strerrordesc_np(e)));
176*32002227SRobert Mustacchi 	}
177*32002227SRobert Mustacchi 	iter->pi_done = false;
178*32002227SRobert Mustacchi 
179*32002227SRobert Mustacchi 	/*
180*32002227SRobert Mustacchi 	 * We want port discovery to have a reasonably stable and meaningful
181*32002227SRobert Mustacchi 	 * order. This is going to be built on top of for devices. We want to
182*32002227SRobert Mustacchi 	 * treat this in a bit of a depth-search sense. We don't want to do the
183*32002227SRobert Mustacchi 	 * children of one controller's port, then switch to a different
184*32002227SRobert Mustacchi 	 * controller, and then come back to say a mux. To facilitate this, note
185*32002227SRobert Mustacchi 	 * all the ports first, sort them, and then come back to it.
186*32002227SRobert Mustacchi 	 */
187*32002227SRobert Mustacchi 	for (di_node_t dn = di_drv_first_node(I2C_NEX_DRV, iter->pi_root);
188*32002227SRobert Mustacchi 	    dn != NULL; dn = di_drv_next_node(dn)) {
189*32002227SRobert Mustacchi 		if (!i2c_node_is_type(dn, I2C_NODE_T_PORT)) {
190*32002227SRobert Mustacchi 			continue;
191*32002227SRobert Mustacchi 		}
192*32002227SRobert Mustacchi 
193*32002227SRobert Mustacchi 		if (iter->pi_nalloc == iter->pi_nports) {
194*32002227SRobert Mustacchi 			di_node_t *new;
195*32002227SRobert Mustacchi 			uint32_t toalloc = iter->pi_nalloc + 16;
196*32002227SRobert Mustacchi 
197*32002227SRobert Mustacchi 			new = recallocarray(iter->pi_ports, iter->pi_nports,
198*32002227SRobert Mustacchi 			    toalloc, sizeof (di_node_t));
199*32002227SRobert Mustacchi 			if (new == NULL) {
200*32002227SRobert Mustacchi 				int e = errno;
201*32002227SRobert Mustacchi 				i2c_port_discover_fini(iter);
202*32002227SRobert Mustacchi 				return (i2c_error(hdl, I2C_ERR_NO_MEM, e,
203*32002227SRobert Mustacchi 				    "failed to allocate memory for a %u "
204*32002227SRobert Mustacchi 				    "element di_node_t array",
205*32002227SRobert Mustacchi 				    toalloc));
206*32002227SRobert Mustacchi 			}
207*32002227SRobert Mustacchi 			iter->pi_ports = new;
208*32002227SRobert Mustacchi 			iter->pi_nalloc = toalloc;
209*32002227SRobert Mustacchi 		}
210*32002227SRobert Mustacchi 
211*32002227SRobert Mustacchi 		iter->pi_ports[iter->pi_nports] = dn;
212*32002227SRobert Mustacchi 		iter->pi_nports++;
213*32002227SRobert Mustacchi 	}
214*32002227SRobert Mustacchi 
215*32002227SRobert Mustacchi 	qsort(iter->pi_ports, iter->pi_nports, sizeof (di_node_t),
216*32002227SRobert Mustacchi 	    i2c_port_sort);
217*32002227SRobert Mustacchi 
218*32002227SRobert Mustacchi 	*iterp = iter;
219*32002227SRobert Mustacchi 	return (i2c_success(hdl));
220*32002227SRobert Mustacchi }
221*32002227SRobert Mustacchi 
222*32002227SRobert Mustacchi bool
i2c_port_discover(i2c_hdl_t * hdl,i2c_port_disc_f func,void * arg)223*32002227SRobert Mustacchi i2c_port_discover(i2c_hdl_t *hdl, i2c_port_disc_f func, void *arg)
224*32002227SRobert Mustacchi {
225*32002227SRobert Mustacchi 	i2c_port_iter_t *iter;
226*32002227SRobert Mustacchi 	const i2c_port_disc_t *disc;
227*32002227SRobert Mustacchi 	i2c_iter_t ret;
228*32002227SRobert Mustacchi 
229*32002227SRobert Mustacchi 	if (func == NULL) {
230*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
231*32002227SRobert Mustacchi 		    "invalid i2c_port_disc_f function pointer: %p", func));
232*32002227SRobert Mustacchi 	}
233*32002227SRobert Mustacchi 
234*32002227SRobert Mustacchi 	if (!i2c_port_discover_init(hdl, &iter)) {
235*32002227SRobert Mustacchi 		return (false);
236*32002227SRobert Mustacchi 	}
237*32002227SRobert Mustacchi 
238*32002227SRobert Mustacchi 	while ((ret = i2c_port_discover_step(iter, &disc)) == I2C_ITER_VALID) {
239*32002227SRobert Mustacchi 		if (!func(hdl, disc, arg))
240*32002227SRobert Mustacchi 			break;
241*32002227SRobert Mustacchi 	}
242*32002227SRobert Mustacchi 
243*32002227SRobert Mustacchi 	i2c_port_discover_fini(iter);
244*32002227SRobert Mustacchi 	if (ret == I2C_ITER_ERROR) {
245*32002227SRobert Mustacchi 		return (false);
246*32002227SRobert Mustacchi 	}
247*32002227SRobert Mustacchi 
248*32002227SRobert Mustacchi 	return (i2c_success(hdl));
249*32002227SRobert Mustacchi }
250*32002227SRobert Mustacchi 
251*32002227SRobert Mustacchi di_node_t
i2c_port_disc_devi(const i2c_port_disc_t * disc)252*32002227SRobert Mustacchi i2c_port_disc_devi(const i2c_port_disc_t *disc)
253*32002227SRobert Mustacchi {
254*32002227SRobert Mustacchi 	return (disc->pd_devi);
255*32002227SRobert Mustacchi }
256*32002227SRobert Mustacchi 
257*32002227SRobert Mustacchi const char *
i2c_port_disc_path(const i2c_port_disc_t * disc)258*32002227SRobert Mustacchi i2c_port_disc_path(const i2c_port_disc_t *disc)
259*32002227SRobert Mustacchi {
260*32002227SRobert Mustacchi 	return (disc->pd_path);
261*32002227SRobert Mustacchi }
262*32002227SRobert Mustacchi 
263*32002227SRobert Mustacchi void
i2c_port_fini(i2c_port_t * port)264*32002227SRobert Mustacchi i2c_port_fini(i2c_port_t *port)
265*32002227SRobert Mustacchi {
266*32002227SRobert Mustacchi 	if (port == NULL) {
267*32002227SRobert Mustacchi 		return;
268*32002227SRobert Mustacchi 	}
269*32002227SRobert Mustacchi 
270*32002227SRobert Mustacchi 	if (port->port_fd >= 0) {
271*32002227SRobert Mustacchi 		(void) close(port->port_fd);
272*32002227SRobert Mustacchi 	}
273*32002227SRobert Mustacchi 
274*32002227SRobert Mustacchi 	di_devfs_path_free(port->port_minor);
275*32002227SRobert Mustacchi 	free(port->port_name);
276*32002227SRobert Mustacchi 	free(port);
277*32002227SRobert Mustacchi }
278*32002227SRobert Mustacchi 
279*32002227SRobert Mustacchi bool
i2c_port_init(i2c_hdl_t * hdl,di_node_t di,i2c_port_t ** portp)280*32002227SRobert Mustacchi i2c_port_init(i2c_hdl_t *hdl, di_node_t di, i2c_port_t **portp)
281*32002227SRobert Mustacchi {
282*32002227SRobert Mustacchi 	di_minor_t minor;
283*32002227SRobert Mustacchi 	di_node_t parent;
284*32002227SRobert Mustacchi 	i2c_node_type_t ptype;
285*32002227SRobert Mustacchi 
286*32002227SRobert Mustacchi 	if (di == NULL) {
287*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
288*32002227SRobert Mustacchi 		    "invalid di_node_t: %p", di));
289*32002227SRobert Mustacchi 	}
290*32002227SRobert Mustacchi 
291*32002227SRobert Mustacchi 	if (portp == NULL) {
292*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
293*32002227SRobert Mustacchi 		    "invalid i2c_port_t output pointer: %p", portp));
294*32002227SRobert Mustacchi 	}
295*32002227SRobert Mustacchi 
296*32002227SRobert Mustacchi 	/*
297*32002227SRobert Mustacchi 	 * We've verified that we were given an i2cnex instance, make sure this
298*32002227SRobert Mustacchi 	 * corresponds to a port.
299*32002227SRobert Mustacchi 	 */
300*32002227SRobert Mustacchi 	if (!i2c_node_is_type(di, I2C_NODE_T_PORT)) {
301*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_DEVI, 0, "devi %s@%s is "
302*32002227SRobert Mustacchi 		    "not an i2c port", di_node_name(di), di_bus_addr(di)));
303*32002227SRobert Mustacchi 	}
304*32002227SRobert Mustacchi 
305*32002227SRobert Mustacchi 	minor = i2c_node_minor(di);
306*32002227SRobert Mustacchi 	if (minor == DI_MINOR_NIL) {
307*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_DEVI, 0, "devi %s@%s is "
308*32002227SRobert Mustacchi 		    "not an i2c port: failed to find port minor",
309*32002227SRobert Mustacchi 		    di_node_name(di), di_bus_addr(di)));
310*32002227SRobert Mustacchi 	}
311*32002227SRobert Mustacchi 
312*32002227SRobert Mustacchi 	i2c_port_t *port = calloc(1, sizeof (i2c_port_t));
313*32002227SRobert Mustacchi 	if (port == NULL) {
314*32002227SRobert Mustacchi 		int e = errno;
315*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
316*32002227SRobert Mustacchi 		    "memory for a new i2c_port_t"));
317*32002227SRobert Mustacchi 	}
318*32002227SRobert Mustacchi 
319*32002227SRobert Mustacchi 	port->port_fd = -1;
320*32002227SRobert Mustacchi 	port->port_hdl = hdl;
321*32002227SRobert Mustacchi 	port->port_inst = di_instance(di);
322*32002227SRobert Mustacchi 	port->port_name = strdup(di_bus_addr(di));
323*32002227SRobert Mustacchi 	if (port->port_name == NULL) {
324*32002227SRobert Mustacchi 		int e = errno;
325*32002227SRobert Mustacchi 		i2c_port_fini(port);
326*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to duplicate "
327*32002227SRobert Mustacchi 		    "port bus address"));
328*32002227SRobert Mustacchi 	}
329*32002227SRobert Mustacchi 
330*32002227SRobert Mustacchi 	parent = di_parent_node(di);
331*32002227SRobert Mustacchi 	ptype = i2c_node_type(parent);
332*32002227SRobert Mustacchi 	if (ptype == I2C_NODE_T_CTRL) {
333*32002227SRobert Mustacchi 		port->port_type = I2C_PORT_TYPE_CTRL;
334*32002227SRobert Mustacchi 	} else if (ptype == I2C_NODE_T_MUX) {
335*32002227SRobert Mustacchi 		port->port_type = I2C_PORT_TYPE_MUX;
336*32002227SRobert Mustacchi 	} else {
337*32002227SRobert Mustacchi 		i2c_port_fini(port);
338*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_DEVI, 0, "devi %s@%s is not "
339*32002227SRobert Mustacchi 		    "an i2c port: found wrong parent", di_node_name(di),
340*32002227SRobert Mustacchi 		    di_bus_addr(di)));
341*32002227SRobert Mustacchi 	}
342*32002227SRobert Mustacchi 
343*32002227SRobert Mustacchi 	if (!i2c_node_to_path(hdl, di, port->port_path,
344*32002227SRobert Mustacchi 	    sizeof (port->port_path))) {
345*32002227SRobert Mustacchi 		i2c_port_fini(port);
346*32002227SRobert Mustacchi 		return (false);
347*32002227SRobert Mustacchi 	}
348*32002227SRobert Mustacchi 
349*32002227SRobert Mustacchi 	port->port_minor = di_devfs_minor_path(minor);
350*32002227SRobert Mustacchi 	if (port->port_minor == NULL) {
351*32002227SRobert Mustacchi 		int e = errno;
352*32002227SRobert Mustacchi 		i2c_port_fini(port);
353*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to "
354*32002227SRobert Mustacchi 		    "obtain ports's devfs path: %s", strerrordesc_np(e)));
355*32002227SRobert Mustacchi 	}
356*32002227SRobert Mustacchi 
357*32002227SRobert Mustacchi 	port->port_fd = openat(hdl->ih_devfd, port->port_minor + 1, O_RDWR);
358*32002227SRobert Mustacchi 	if (port->port_fd < 0) {
359*32002227SRobert Mustacchi 		int e = errno;
360*32002227SRobert Mustacchi 		(void) i2c_error(hdl, I2C_ERR_OPEN_DEV, e, "failed to open "
361*32002227SRobert Mustacchi 		    "device path /devices%s: %s", port->port_minor,
362*32002227SRobert Mustacchi 		    strerrordesc_np(e));
363*32002227SRobert Mustacchi 		i2c_port_fini(port);
364*32002227SRobert Mustacchi 		return (false);
365*32002227SRobert Mustacchi 	}
366*32002227SRobert Mustacchi 
367*32002227SRobert Mustacchi 	if (ioctl(port->port_fd, UI2C_IOCTL_PORT_INFO, &port->port_info) != 0) {
368*32002227SRobert Mustacchi 		int e = errno;
369*32002227SRobert Mustacchi 		i2c_port_fini(port);
370*32002227SRobert Mustacchi 		return (i2c_ioctl_syserror(hdl, e, "port information request"));
371*32002227SRobert Mustacchi 	}
372*32002227SRobert Mustacchi 
373*32002227SRobert Mustacchi 	if (port->port_info.upo_error.i2c_error != I2C_CORE_E_OK) {
374*32002227SRobert Mustacchi 		return (i2c_ioctl_error(hdl, &port->port_info.upo_error,
375*32002227SRobert Mustacchi 		    "port information request"));
376*32002227SRobert Mustacchi 	}
377*32002227SRobert Mustacchi 
378*32002227SRobert Mustacchi 	*portp = port;
379*32002227SRobert Mustacchi 	return (i2c_success(hdl));
380*32002227SRobert Mustacchi }
381*32002227SRobert Mustacchi 
382*32002227SRobert Mustacchi /*
383*32002227SRobert Mustacchi  * Initialize a port based on the passed in name. This name may be a top-level
384*32002227SRobert Mustacchi  * port for the controller or it may be a port on a mux. We end up walking the
385*32002227SRobert Mustacchi  * path, tokenizing and parsing it to try to find something here.
386*32002227SRobert Mustacchi  */
387*32002227SRobert Mustacchi bool
i2c_port_init_by_path(i2c_hdl_t * hdl,const char * path,i2c_port_t ** portp)388*32002227SRobert Mustacchi i2c_port_init_by_path(i2c_hdl_t *hdl, const char *path, i2c_port_t **portp)
389*32002227SRobert Mustacchi {
390*32002227SRobert Mustacchi 	i2c_node_type_t type;
391*32002227SRobert Mustacchi 	di_node_t dn, root;
392*32002227SRobert Mustacchi 
393*32002227SRobert Mustacchi 	if (path == NULL) {
394*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
395*32002227SRobert Mustacchi 		    "invalid i2c port path: %p", path));
396*32002227SRobert Mustacchi 	}
397*32002227SRobert Mustacchi 
398*32002227SRobert Mustacchi 	if (portp == NULL) {
399*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
400*32002227SRobert Mustacchi 		    "invalid i2c_port_t output pointer: %p", portp));
401*32002227SRobert Mustacchi 	}
402*32002227SRobert Mustacchi 
403*32002227SRobert Mustacchi 	root = di_init("/", DINFOCPYALL);
404*32002227SRobert Mustacchi 	if (root == DI_NODE_NIL) {
405*32002227SRobert Mustacchi 		int e = errno;
406*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to "
407*32002227SRobert Mustacchi 		    "initialize devinfo snapshot: %s", strerrordesc_np(e)));
408*32002227SRobert Mustacchi 	}
409*32002227SRobert Mustacchi 
410*32002227SRobert Mustacchi 	if (!i2c_path_parse(hdl, path, root, &dn, &type, I2C_ERR_BAD_PORT)) {
411*32002227SRobert Mustacchi 		di_fini(root);
412*32002227SRobert Mustacchi 		return (false);
413*32002227SRobert Mustacchi 	}
414*32002227SRobert Mustacchi 
415*32002227SRobert Mustacchi 	if (type != I2C_NODE_T_PORT) {
416*32002227SRobert Mustacchi 		di_fini(root);
417*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PORT, 0, "parsed I2C path "
418*32002227SRobert Mustacchi 		    "%s did not end at a port", path));
419*32002227SRobert Mustacchi 	}
420*32002227SRobert Mustacchi 
421*32002227SRobert Mustacchi 	bool ret = i2c_port_init(hdl, dn, portp);
422*32002227SRobert Mustacchi 	di_fini(root);
423*32002227SRobert Mustacchi 	return (ret);
424*32002227SRobert Mustacchi }
425*32002227SRobert Mustacchi 
426*32002227SRobert Mustacchi const char *
i2c_port_name(i2c_port_t * port)427*32002227SRobert Mustacchi i2c_port_name(i2c_port_t *port)
428*32002227SRobert Mustacchi {
429*32002227SRobert Mustacchi 	return (port->port_name);
430*32002227SRobert Mustacchi }
431*32002227SRobert Mustacchi 
432*32002227SRobert Mustacchi const char *
i2c_port_path(i2c_port_t * port)433*32002227SRobert Mustacchi i2c_port_path(i2c_port_t *port)
434*32002227SRobert Mustacchi {
435*32002227SRobert Mustacchi 	return (port->port_path);
436*32002227SRobert Mustacchi }
437*32002227SRobert Mustacchi 
438*32002227SRobert Mustacchi uint32_t
i2c_port_portno(i2c_port_t * port)439*32002227SRobert Mustacchi i2c_port_portno(i2c_port_t *port)
440*32002227SRobert Mustacchi {
441*32002227SRobert Mustacchi 	return (port->port_info.upo_portno);
442*32002227SRobert Mustacchi }
443*32002227SRobert Mustacchi 
444*32002227SRobert Mustacchi i2c_port_type_t
i2c_port_type(i2c_port_t * port)445*32002227SRobert Mustacchi i2c_port_type(i2c_port_t *port)
446*32002227SRobert Mustacchi {
447*32002227SRobert Mustacchi 	return (port->port_type);
448*32002227SRobert Mustacchi }
449*32002227SRobert Mustacchi 
450*32002227SRobert Mustacchi void
i2c_port_map_free(i2c_port_map_t * map)451*32002227SRobert Mustacchi i2c_port_map_free(i2c_port_map_t *map)
452*32002227SRobert Mustacchi {
453*32002227SRobert Mustacchi 	free(map);
454*32002227SRobert Mustacchi }
455*32002227SRobert Mustacchi 
456*32002227SRobert Mustacchi bool
i2c_port_map_snap(i2c_port_t * port,i2c_port_map_t ** mapp)457*32002227SRobert Mustacchi i2c_port_map_snap(i2c_port_t *port, i2c_port_map_t **mapp)
458*32002227SRobert Mustacchi {
459*32002227SRobert Mustacchi 	i2c_port_map_t *map;
460*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = port->port_hdl;
461*32002227SRobert Mustacchi 
462*32002227SRobert Mustacchi 	if (mapp == NULL) {
463*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
464*32002227SRobert Mustacchi 		    "invalid i2c_port_map_t output pointer: %p", mapp));
465*32002227SRobert Mustacchi 	}
466*32002227SRobert Mustacchi 
467*32002227SRobert Mustacchi 	map = calloc(1, sizeof (i2c_port_map_t));
468*32002227SRobert Mustacchi 	if (map == NULL) {
469*32002227SRobert Mustacchi 		int e = errno;
470*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
471*32002227SRobert Mustacchi 		    "memory for a new i2c_port_map_t"));
472*32002227SRobert Mustacchi 	}
473*32002227SRobert Mustacchi 	map->pm_hdl = hdl;
474*32002227SRobert Mustacchi 
475*32002227SRobert Mustacchi 	if (ioctl(port->port_fd, UI2C_IOCTL_PORT_INFO, &map->pm_info) != 0) {
476*32002227SRobert Mustacchi 		int e = errno;
477*32002227SRobert Mustacchi 		i2c_port_fini(port);
478*32002227SRobert Mustacchi 		return (i2c_ioctl_syserror(hdl, e, "port maprmation request"));
479*32002227SRobert Mustacchi 	}
480*32002227SRobert Mustacchi 
481*32002227SRobert Mustacchi 	if (map->pm_info.upo_error.i2c_error != I2C_CORE_E_OK) {
482*32002227SRobert Mustacchi 		return (i2c_ioctl_error(hdl, &map->pm_info.upo_error,
483*32002227SRobert Mustacchi 		    "port information request"));
484*32002227SRobert Mustacchi 	}
485*32002227SRobert Mustacchi 
486*32002227SRobert Mustacchi 	*mapp = map;
487*32002227SRobert Mustacchi 	return (i2c_success(hdl));
488*32002227SRobert Mustacchi }
489*32002227SRobert Mustacchi 
490*32002227SRobert Mustacchi void
i2c_port_map_ndevs(const i2c_port_map_t * map,uint32_t * local,uint32_t * ds)491*32002227SRobert Mustacchi i2c_port_map_ndevs(const i2c_port_map_t *map, uint32_t *local, uint32_t *ds)
492*32002227SRobert Mustacchi {
493*32002227SRobert Mustacchi 	if (local != NULL) {
494*32002227SRobert Mustacchi 		*local = map->pm_info.upo_ndevs;
495*32002227SRobert Mustacchi 	}
496*32002227SRobert Mustacchi 
497*32002227SRobert Mustacchi 	if (ds != NULL) {
498*32002227SRobert Mustacchi 		*ds = map->pm_info.upo_ndevs_ds;
499*32002227SRobert Mustacchi 	}
500*32002227SRobert Mustacchi }
501*32002227SRobert Mustacchi 
502*32002227SRobert Mustacchi bool
i2c_port_map_addr_info(const i2c_port_map_t * map,const i2c_addr_t * addr,uint32_t * devsp,bool * dsp,major_t * majorp)503*32002227SRobert Mustacchi i2c_port_map_addr_info(const i2c_port_map_t *map, const i2c_addr_t *addr,
504*32002227SRobert Mustacchi     uint32_t *devsp, bool *dsp, major_t *majorp)
505*32002227SRobert Mustacchi {
506*32002227SRobert Mustacchi 	if (!i2c_addr_validate(map->pm_hdl, addr)) {
507*32002227SRobert Mustacchi 		return (false);
508*32002227SRobert Mustacchi 	}
509*32002227SRobert Mustacchi 
510*32002227SRobert Mustacchi 	if (addr->ia_type != I2C_ADDR_7BIT) {
511*32002227SRobert Mustacchi 		(void) i2c_error(map->pm_hdl, I2C_ERR_UNSUP_ADDR_TYPE, 0,
512*32002227SRobert Mustacchi 		    "port map information is not available for this address "
513*32002227SRobert Mustacchi 		    "type");
514*32002227SRobert Mustacchi 		return (false);
515*32002227SRobert Mustacchi 	}
516*32002227SRobert Mustacchi 
517*32002227SRobert Mustacchi 	const ui2c_port_addr_info_t *info = &map->pm_info.upo_7b[addr->ia_addr];
518*32002227SRobert Mustacchi 
519*32002227SRobert Mustacchi 	if (devsp != NULL) {
520*32002227SRobert Mustacchi 		*devsp = info->pai_ndevs;
521*32002227SRobert Mustacchi 	}
522*32002227SRobert Mustacchi 
523*32002227SRobert Mustacchi 	if (dsp != NULL) {
524*32002227SRobert Mustacchi 		*dsp = info->pai_downstream;
525*32002227SRobert Mustacchi 	}
526*32002227SRobert Mustacchi 
527*32002227SRobert Mustacchi 	if (majorp != NULL) {
528*32002227SRobert Mustacchi 		*majorp = info->pai_major;
529*32002227SRobert Mustacchi 	}
530*32002227SRobert Mustacchi 
531*32002227SRobert Mustacchi 
532*32002227SRobert Mustacchi 	return (i2c_success(map->pm_hdl));
533*32002227SRobert Mustacchi }
534