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 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 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 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 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 181 i2c_mux_disc_devi(const i2c_mux_disc_t *disc) 182 { 183 return (disc->md_devi); 184 } 185 186 di_minor_t 187 i2c_mux_disc_devctl(const i2c_mux_disc_t *disc) 188 { 189 return (disc->md_minor); 190 } 191 192 const char * 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 * 199 i2c_mux_disc_path(const i2c_mux_disc_t *disc) 200 { 201 return (disc->md_path); 202 } 203 204 uint32_t 205 i2c_mux_disc_nports(const i2c_mux_disc_t *disc) 206 { 207 return (disc->md_info.umi_nports); 208 } 209