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 * I2C Controller related functions. 18 */ 19 20 #include <stdlib.h> 21 #include <string.h> 22 #include <unistd.h> 23 #include <sys/types.h> 24 #include <sys/stat.h> 25 #include <fcntl.h> 26 27 #include "libi2c_impl.h" 28 29 void 30 i2c_ctrl_discover_fini(i2c_ctrl_iter_t *iter) 31 { 32 if (iter == NULL) 33 return; 34 35 di_fini(iter->ci_root); 36 free(iter); 37 } 38 39 i2c_iter_t 40 i2c_ctrl_discover_step(i2c_ctrl_iter_t *iter, const i2c_ctrl_disc_t **discp) 41 { 42 *discp = NULL; 43 44 if (iter->ci_done) { 45 return (I2C_ITER_DONE); 46 } 47 48 for (;;) { 49 if (iter->ci_cur == DI_NODE_NIL) { 50 iter->ci_cur = di_drv_first_node(I2C_NEX_DRV, 51 iter->ci_root); 52 } else { 53 iter->ci_cur = di_drv_next_node(iter->ci_cur); 54 } 55 56 if (iter->ci_cur == DI_NODE_NIL) { 57 iter->ci_done = true; 58 return (I2C_ITER_DONE); 59 } 60 61 if (!i2c_node_is_type(iter->ci_cur, I2C_NODE_T_CTRL)) { 62 continue; 63 } 64 65 iter->ci_disc.icd_devi = iter->ci_cur; 66 iter->ci_disc.icd_minor = i2c_node_minor(iter->ci_cur); 67 if (iter->ci_disc.icd_minor == DI_MINOR_NIL) { 68 continue; 69 } 70 71 *discp = &iter->ci_disc; 72 return (I2C_ITER_VALID); 73 } 74 75 return (I2C_ITER_DONE); 76 } 77 78 bool 79 i2c_ctrl_discover_init(i2c_hdl_t *hdl, i2c_ctrl_iter_t **iterp) 80 { 81 i2c_ctrl_iter_t *iter; 82 83 if (iterp == NULL) { 84 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 85 "invalid i2c_ctrl_iter_t output pointer: %p", iterp)); 86 } 87 88 iter = calloc(1, sizeof (i2c_ctrl_iter_t)); 89 if (iter == NULL) { 90 int e = errno; 91 return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate " 92 "memory for a new i2c_ctrl_iter_t")); 93 } 94 95 iter->ci_hdl = hdl; 96 iter->ci_root = di_init("/", DINFOCPYALL); 97 if (iter->ci_root == NULL) { 98 int e = errno; 99 i2c_ctrl_discover_fini(iter); 100 return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to " 101 "initialize devinfo snapshot: %s", strerrordesc_np(e))); 102 } 103 iter->ci_done = false; 104 iter->ci_cur = DI_NODE_NIL; 105 106 *iterp = iter; 107 return (i2c_success(hdl)); 108 } 109 110 bool 111 i2c_ctrl_discover(i2c_hdl_t *hdl, i2c_ctrl_disc_f func, void *arg) 112 { 113 i2c_ctrl_iter_t *iter; 114 const i2c_ctrl_disc_t *disc; 115 i2c_iter_t ret; 116 117 if (func == NULL) { 118 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 119 "invalid i2c_ctrl_disc_f function pointer: %p", func)); 120 } 121 122 if (!i2c_ctrl_discover_init(hdl, &iter)) { 123 return (false); 124 } 125 126 while ((ret = i2c_ctrl_discover_step(iter, &disc)) == I2C_ITER_VALID) { 127 if (!func(hdl, disc, arg)) 128 break; 129 } 130 131 i2c_ctrl_discover_fini(iter); 132 if (ret == I2C_ITER_ERROR) { 133 return (false); 134 } 135 136 return (i2c_success(hdl)); 137 } 138 139 di_node_t 140 i2c_ctrl_disc_devi(const i2c_ctrl_disc_t *discp) 141 { 142 return (discp->icd_devi); 143 } 144 145 di_minor_t 146 i2c_ctrl_disc_minor(const i2c_ctrl_disc_t *discp) 147 { 148 return (discp->icd_minor); 149 } 150 151 void 152 i2c_ctrl_fini(i2c_ctrl_t *ctrl) 153 { 154 if (ctrl == NULL) { 155 return; 156 } 157 158 if (ctrl->ctrl_fd >= 0) { 159 (void) close(ctrl->ctrl_fd); 160 } 161 162 di_devfs_path_free(ctrl->ctrl_minor); 163 di_devfs_path_free(ctrl->ctrl_path); 164 free(ctrl->ctrl_name); 165 free(ctrl); 166 } 167 168 bool 169 i2c_ctrl_init(i2c_hdl_t *hdl, di_node_t di, i2c_ctrl_t **ctrlp) 170 { 171 di_minor_t minor; 172 i2c_ctrl_t *ctrl; 173 ui2c_ctrl_nprops_t nprops; 174 175 if (di == DI_NODE_NIL) { 176 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 177 "invalid di_node_t: %p", di)); 178 } 179 180 if (ctrlp == NULL) { 181 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 182 "invalid i2c_ctrl_t output pointer: %p", ctrlp)); 183 } 184 185 if (!i2c_node_is_type(di, I2C_NODE_T_CTRL)) { 186 return (i2c_error(hdl, I2C_ERR_BAD_DEVI, 0, "devi %s@%s isn't " 187 "an i2c controller", di_node_name(di), di_bus_addr(di))); 188 } 189 190 /* 191 * See if we can find a minor node that corresponds to a controller 192 * nexus. 193 */ 194 minor = i2c_node_minor(di); 195 if (minor == DI_MINOR_NIL) { 196 return (i2c_error(hdl, I2C_ERR_BAD_DEVI, 0, "devi %s@%s is " 197 "not an i2c controller: failed to find controller minor", 198 di_node_name(di), di_bus_addr(di))); 199 } 200 201 ctrl = calloc(1, sizeof (i2c_ctrl_t)); 202 if (ctrl == NULL) { 203 int e = errno; 204 return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate " 205 "memory for a new i2c_ctrl_t")); 206 } 207 208 ctrl->ctrl_fd = -1; 209 ctrl->ctrl_hdl = hdl; 210 ctrl->ctrl_inst = di_instance(di); 211 ctrl->ctrl_name = strdup(di_bus_addr(di)); 212 if (ctrl->ctrl_name == NULL) { 213 int e = errno; 214 i2c_ctrl_fini(ctrl); 215 return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to duplicate " 216 "controller bus address")); 217 } 218 219 ctrl->ctrl_path = di_devfs_path(di); 220 if (ctrl->ctrl_path == NULL) { 221 int e = errno; 222 i2c_ctrl_fini(ctrl); 223 return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to " 224 "obtain controller's devfs path: %s", strerrordesc_np(e))); 225 } 226 227 ctrl->ctrl_minor = di_devfs_minor_path(minor); 228 if (ctrl->ctrl_minor == NULL) { 229 int e = errno; 230 i2c_ctrl_fini(ctrl); 231 return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to " 232 "obtain controller's minor path: %s", strerrordesc_np(e))); 233 } 234 235 ctrl->ctrl_fd = openat(hdl->ih_devfd, ctrl->ctrl_minor + 1, O_RDWR); 236 if (ctrl->ctrl_fd < 0) { 237 int e = errno; 238 (void) i2c_error(hdl, I2C_ERR_OPEN_DEV, e, "failed to open " 239 "device path '/devices%s: %s", ctrl->ctrl_minor, 240 strerrordesc_np(e)); 241 i2c_ctrl_fini(ctrl); 242 return (false); 243 } 244 245 if (ioctl(ctrl->ctrl_fd, UI2C_IOCTL_CTRL_NPROPS, &nprops) != 0) { 246 int e = errno; 247 i2c_ctrl_fini(ctrl); 248 return (i2c_ioctl_syserror(hdl, e, "controller nprops " 249 "request")); 250 } 251 ctrl->ctrl_nstd = nprops.ucp_nstd; 252 ctrl->ctrl_npriv = nprops.ucp_npriv; 253 254 *ctrlp = ctrl; 255 return (i2c_success(hdl)); 256 } 257 258 bool 259 i2c_ctrl_init_by_path(i2c_hdl_t *hdl, const char *name, i2c_ctrl_t **ctrlp) 260 { 261 i2c_node_type_t type; 262 di_node_t dn, root; 263 264 if (name == NULL) { 265 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 266 "invalid i2c controller name: %p", name)); 267 } 268 269 if (ctrlp == NULL) { 270 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 271 "invalid i2c_ctrl_t output pointer: %p", ctrlp)); 272 } 273 274 root = di_init("/", DINFOCPYALL); 275 if (root == DI_NODE_NIL) { 276 int e = errno; 277 return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to " 278 "initialize devinfo snapshot: %s", strerrordesc_np(e))); 279 } 280 281 if (!i2c_path_parse(hdl, name, root, &dn, &type, 282 I2C_ERR_BAD_CONTROLLER)) { 283 di_fini(root); 284 return (false); 285 } 286 287 if (type != I2C_NODE_T_CTRL) { 288 di_fini(root); 289 return (i2c_error(hdl, I2C_ERR_BAD_CONTROLLER, 0, "parsed I2C " 290 "path %s did not end at a controller", name)); 291 } 292 293 bool ret = i2c_ctrl_init(hdl, dn, ctrlp); 294 di_fini(root); 295 return (ret); 296 } 297 298 const char * 299 i2c_ctrl_name(i2c_ctrl_t *ctrl) 300 { 301 return (ctrl->ctrl_name); 302 } 303 304 int32_t 305 i2c_ctrl_instance(i2c_ctrl_t *ctrl) 306 { 307 return (ctrl->ctrl_inst); 308 } 309 310 const char * 311 i2c_ctrl_path(i2c_ctrl_t *ctrl) 312 { 313 return (ctrl->ctrl_path); 314 } 315 316 uint32_t 317 i2c_ctrl_nprops(i2c_ctrl_t *ctrl) 318 { 319 /* 320 * Currently we only have standard properties. If we have 321 * controller-specific properties in the future then those should be 322 * added to this. 323 */ 324 return (ctrl->ctrl_nstd); 325 } 326 327 const char * 328 i2c_prop_info_name(i2c_prop_info_t *info) 329 { 330 return (info->pinfo_info.upi_name); 331 } 332 333 i2c_prop_t 334 i2c_prop_info_id(i2c_prop_info_t *info) 335 { 336 return (info->pinfo_info.upi_prop); 337 } 338 339 i2c_prop_type_t 340 i2c_prop_info_type(i2c_prop_info_t *info) 341 { 342 return (info->pinfo_info.upi_type); 343 } 344 345 bool 346 i2c_prop_info_sup(i2c_prop_info_t *info) 347 { 348 return (info->pinfo_sup); 349 } 350 351 i2c_prop_perm_t 352 i2c_prop_info_perm(i2c_prop_info_t *info) 353 { 354 return (info->pinfo_info.upi_perm); 355 } 356 357 bool 358 i2c_prop_info_def_u32(i2c_prop_info_t *info, uint32_t *defp) 359 { 360 i2c_hdl_t *hdl = info->pinfo_hdl; 361 362 if (defp == NULL) { 363 364 } 365 366 if (!info->pinfo_sup) { 367 return (i2c_error(hdl, I2C_ERR_PROP_UNSUP, 0, "default value " 368 "is unavailable because property %s is not supported by " 369 "the controller", info->pinfo_info.upi_name)); 370 } 371 372 switch (info->pinfo_info.upi_type) { 373 case I2C_PROP_TYPE_U32: 374 case I2C_PROP_TYPE_BIT32: 375 if (info->pinfo_info.upi_def_len != sizeof (uint32_t)) { 376 return (i2c_error(hdl, I2C_ERR_PROP_TYPE_MISMATCH, 0, 377 "property %s does not have a default value", 378 info->pinfo_info.upi_name)); 379 } 380 (void) memcpy(defp, info->pinfo_info.upi_def, 381 sizeof (uint32_t)); 382 break; 383 default: 384 return (i2c_error(hdl, I2C_ERR_PROP_TYPE_MISMATCH, 0, 385 "property %s default value does not have a 32-bit " 386 "integer type (found type 0x%x)", info->pinfo_info.upi_name, 387 info->pinfo_info.upi_type)); 388 break; 389 } 390 391 return (i2c_success(hdl)); 392 } 393 394 const i2c_prop_range_t * 395 i2c_prop_info_pos(i2c_prop_info_t *info) 396 { 397 if (!info->pinfo_sup || info->pinfo_info.upi_pos_len < 398 sizeof (i2c_prop_range_t)) { 399 return (NULL); 400 } 401 402 return ((i2c_prop_range_t *)info->pinfo_info.upi_pos); 403 } 404 405 void 406 i2c_prop_info_free(i2c_prop_info_t *info) 407 { 408 free(info); 409 } 410 411 bool 412 i2c_prop_info(i2c_ctrl_t *ctrl, i2c_prop_t prop, i2c_prop_info_t **infop) 413 { 414 i2c_hdl_t *hdl = ctrl->ctrl_hdl; 415 i2c_prop_info_t *info; 416 417 if (infop == NULL) { 418 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 419 "invalid i2c_prop_info_t output pointer: %p", infop)); 420 } 421 422 info = calloc(1, sizeof (i2c_prop_info_t)); 423 if (info == NULL) { 424 int e = errno; 425 return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate " 426 "memory for a new i2c_prop_info_t")); 427 } 428 429 info->pinfo_info.upi_prop = prop; 430 if (ioctl(ctrl->ctrl_fd, UI2C_IOCTL_CTRL_PROP_INFO, 431 &info->pinfo_info) != 0) { 432 int e = errno; 433 return (i2c_ioctl_syserror(hdl, e, "property info request")); 434 } 435 436 i2c_error_t *err = &info->pinfo_info.upi_error; 437 if (err->i2c_error != I2C_CORE_E_OK && 438 err->i2c_error != I2C_PROP_E_UNSUP) { 439 free(info); 440 return (i2c_ioctl_error(hdl, err, "property info request")); 441 } 442 443 info->pinfo_hdl = hdl; 444 info->pinfo_sup = err->i2c_error == I2C_CORE_E_OK; 445 *infop = info; 446 return (i2c_success(hdl)); 447 } 448 449 /* 450 * Find the property that maps to name the max power way. Basically iterate over 451 * all known properties and see if the name matches. 452 */ 453 bool 454 i2c_prop_info_by_name(i2c_ctrl_t *ctrl, const char *name, 455 i2c_prop_info_t **infop) 456 { 457 i2c_hdl_t *hdl = ctrl->ctrl_hdl; 458 i2c_prop_info_t *info; 459 460 if (name == NULL) { 461 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 462 "invalid property name pointer: %p", name)); 463 } 464 465 if (infop == NULL) { 466 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 467 "invalid i2c_prop_info_t output pointer: %p", infop)); 468 } 469 470 for (uint32_t i = 0; i < ctrl->ctrl_nstd; i++) { 471 if (!i2c_prop_info(ctrl, i, &info)) { 472 return (false); 473 } 474 475 if (strcmp(name, i2c_prop_info_name(info)) == 0) { 476 *infop = info; 477 return (i2c_success(hdl)); 478 } 479 480 i2c_prop_info_free(info); 481 } 482 483 return (i2c_error(hdl, I2C_ERR_BAD_PROP, 0, "unkonwn property: %s", 484 name)); 485 } 486 487 bool 488 i2c_prop_get(i2c_ctrl_t *ctrl, i2c_prop_t id, void *buf, size_t *lenp) 489 { 490 i2c_hdl_t *hdl = ctrl->ctrl_hdl; 491 ui2c_prop_t prop; 492 493 if (buf == NULL) { 494 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 495 "invalid property data buffer pointer: %p", buf)); 496 } 497 498 if (lenp == NULL) { 499 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 500 "invalid property size pointer: %p", buf)); 501 } 502 503 (void) memset(&prop, 0, sizeof (ui2c_prop_t)); 504 prop.up_prop = id; 505 506 if (ioctl(ctrl->ctrl_fd, UI2C_IOCTL_CTRL_PROP_GET, &prop) != 0) { 507 int e = errno; 508 return (i2c_ioctl_syserror(hdl, e, "property get request")); 509 } 510 511 if (prop.up_error.i2c_error != I2C_CORE_E_OK) { 512 return (i2c_ioctl_error(hdl, &prop.up_error, 513 "property get request")); 514 } 515 516 size_t orig = *lenp; 517 *lenp = prop.up_size; 518 if (orig < prop.up_size) { 519 return (i2c_error(hdl, I2C_ERR_PROP_BUF_TOO_SMALL, 0, 520 "property requires %u bytes to hold data, but only passed " 521 "a buffer of %zu bytes", prop.up_size, orig)); 522 } 523 524 (void) memcpy(buf, prop.up_value, prop.up_size); 525 return (i2c_success(hdl)); 526 } 527 528 bool 529 i2c_prop_set(i2c_ctrl_t *ctrl, i2c_prop_t id, const void *buf, size_t len) 530 { 531 i2c_hdl_t *hdl = ctrl->ctrl_hdl; 532 ui2c_prop_t prop; 533 534 if (buf == NULL) { 535 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 536 "invalid property data buffer pointer: %p", buf)); 537 } 538 539 if (len == 0) { 540 return (i2c_error(hdl, I2C_ERR_PROP_BUF_TOO_SMALL, 0, 541 "property buffer length must be more than 0")); 542 } 543 544 if (len > I2C_PROP_SIZE_MAX) { 545 return (i2c_error(hdl, I2C_ERR_PROP_BUF_TOO_BIG, 0, 546 "property buffer length must be less than or equal to %u", 547 I2C_PROP_SIZE_MAX)); 548 } 549 550 (void) memset(&prop, 0, sizeof (ui2c_prop_t)); 551 prop.up_prop = id; 552 prop.up_size = len; 553 (void) memcpy(&prop.up_value, buf, len); 554 555 if (ioctl(ctrl->ctrl_fd, UI2C_IOCTL_CTRL_PROP_SET, &prop) != 0) { 556 int e = errno; 557 return (i2c_ioctl_syserror(hdl, e, "property set request")); 558 } 559 560 if (prop.up_error.i2c_error != I2C_CORE_E_OK) { 561 return (i2c_ioctl_error(hdl, &prop.up_error, 562 "property set request")); 563 } 564 565 return (i2c_success(hdl)); 566 } 567