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