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 * Device addition, removal, and discovery 18 */ 19 20 #include <stdlib.h> 21 #include <strings.h> 22 #include <unistd.h> 23 #include <sys/sysmacros.h> 24 #include <sys/debug.h> 25 #include <fcntl.h> 26 27 #include "libi2c_impl.h" 28 29 void 30 i2c_device_add_req_fini(i2c_dev_add_req_t *req) 31 { 32 nvlist_free(req->add_nvl); 33 free(req); 34 } 35 36 bool 37 i2c_device_add_req_init(i2c_port_t *port, i2c_dev_add_req_t **reqp) 38 { 39 i2c_hdl_t *hdl = port->port_hdl; 40 i2c_dev_add_req_t *req; 41 42 if (reqp == NULL) { 43 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 44 "invalid i2c_dev_add_req_t output pointer: %p", reqp)); 45 } 46 47 req = calloc(1, sizeof (i2c_dev_add_req_t)); 48 if (req == NULL) { 49 int e = errno; 50 return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate " 51 "memory for a new i2c_dev_add_req_t")); 52 } 53 req->add_port = port; 54 req->add_need = I2C_DEV_ADD_REQ_FIELD_NAME | I2C_DEV_ADD_REQ_FIELD_ADDR; 55 56 int ret = nvlist_alloc(&req->add_nvl, NV_UNIQUE_NAME, 0); 57 if (!i2c_nvlist_error(hdl, ret, "create a nvlist")) { 58 free(req); 59 return (false); 60 } 61 62 *reqp = req; 63 return (i2c_success(hdl)); 64 } 65 66 bool 67 i2c_device_add_req_set_addr(i2c_dev_add_req_t *req, const i2c_addr_t *addr) 68 { 69 int ret; 70 i2c_hdl_t *hdl = req->add_port->port_hdl; 71 72 if (addr == NULL) { 73 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 74 "invalid i2c_addr_t pointer: %p", addr)); 75 } 76 77 if (!i2c_addr_validate(hdl, addr)) { 78 return (false); 79 } 80 81 ret = nvlist_add_uint16(req->add_nvl, UI2C_IOCTL_NVL_TYPE, 82 addr->ia_type); 83 if (!i2c_nvlist_error(hdl, ret, "insert address type")) { 84 return (false); 85 } 86 87 ret = nvlist_add_uint16(req->add_nvl, UI2C_IOCTL_NVL_ADDR, 88 addr->ia_addr); 89 if (!i2c_nvlist_error(hdl, ret, "insert address type")) { 90 return (false); 91 } 92 93 req->add_need &= ~I2C_DEV_ADD_REQ_FIELD_ADDR; 94 return (i2c_success(hdl)); 95 } 96 97 bool 98 i2c_device_add_req_set_name(i2c_dev_add_req_t *req, const char *name) 99 { 100 i2c_hdl_t *hdl = req->add_port->port_hdl; 101 102 if (!i2c_name_validate(hdl, name, "name")) { 103 return (false); 104 } 105 106 int ret = nvlist_add_string(req->add_nvl, UI2C_IOCTL_NVL_NAME, name); 107 if (!i2c_nvlist_error(hdl, ret, "insert name string")) { 108 return (false); 109 } 110 111 req->add_need &= ~I2C_DEV_ADD_REQ_FIELD_NAME; 112 return (i2c_success(hdl)); 113 } 114 115 bool 116 i2c_device_add_req_set_compatible(i2c_dev_add_req_t *req, char *const *compat, 117 size_t ncompat) 118 { 119 i2c_hdl_t *hdl = req->add_port->port_hdl; 120 121 /* 122 * Treat this as a request to clear the optional compatible information. 123 */ 124 if (compat == NULL && ncompat == 0) { 125 int ret = nvlist_remove(req->add_nvl, UI2C_IOCTL_NVL_COMPAT, 126 DATA_TYPE_STRING_ARRAY); 127 if (ret == 0 || ret == ENOENT) { 128 return (i2c_success(hdl)); 129 } 130 return (i2c_error(hdl, I2C_ERR_INTERNAL, ret, "unexpected " 131 "internal error while trying to clear compatible[]")); 132 } 133 134 if (compat == NULL) { 135 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 136 "invalid compatible pointer: %p", compat)); 137 } else if (ncompat == 0) { 138 return (i2c_error(hdl, I2C_ERR_COMPAT_LEN_RANGE, 0, "number " 139 "of compatible entries cannot be zero when given a " 140 "non-NULL pointer (%p)", compat)); 141 } else if (ncompat > UI2C_IOCTL_NVL_NCOMPAT_MAX) { 142 return (i2c_error(hdl, I2C_ERR_COMPAT_LEN_RANGE, 0, "device " 143 "compatible array is too long (%zu), valid range is [1, " 144 "%u]", ncompat, UI2C_IOCTL_NVL_NCOMPAT_MAX)); 145 } 146 147 for (size_t i = 0; i < ncompat; i++) { 148 char desc[64]; 149 150 (void) snprintf(desc, sizeof (desc), "compatible[%u]", i); 151 if (!i2c_name_validate(hdl, compat[i], desc)) { 152 return (false); 153 } 154 } 155 156 int ret = nvlist_add_string_array(req->add_nvl, UI2C_IOCTL_NVL_COMPAT, 157 compat, ncompat); 158 if (!i2c_nvlist_error(hdl, ret, "insert compatible string[]")) { 159 return (false); 160 } 161 return (i2c_success(hdl)); 162 } 163 164 bool 165 i2c_device_add_req_exec(i2c_dev_add_req_t *req) 166 { 167 i2c_hdl_t *hdl = req->add_port->port_hdl; 168 size_t pack_size; 169 char *pack_buf = NULL; 170 int nvl_ret; 171 bool ret = false; 172 ui2c_dev_add_t dev; 173 174 if (req->add_need != 0) { 175 char buf[128]; 176 bool comma = false; 177 178 buf[0] = '\0'; 179 if ((req->add_need & I2C_DEV_ADD_REQ_FIELD_ADDR) != 0) { 180 (void) strlcat(buf, "device address", sizeof (buf)); 181 comma = true; 182 } 183 184 if ((req->add_need & I2C_DEV_ADD_REQ_FIELD_NAME) != 0) { 185 if (comma) { 186 (void) strlcat(buf, ",", sizeof (buf)); 187 } 188 (void) strlcat(buf, "name", sizeof (buf)); 189 comma = true; 190 } 191 192 return (i2c_error(hdl, I2C_ERR_ADD_DEV_REQ_MISSING_FIELDS, 0, 193 "cannot execute add device request due to missing fields: " 194 "%s", buf)); 195 } 196 197 nvl_ret = nvlist_size(req->add_nvl, &pack_size, NV_ENCODE_NATIVE); 198 if (!i2c_nvlist_error(hdl, nvl_ret, "determine packed nvlist size")) { 199 goto out; 200 } 201 202 pack_buf = malloc(pack_size); 203 if (pack_buf == NULL) { 204 ret = i2c_error(hdl, I2C_ERR_NO_MEM, errno, "failed to " 205 "allocate %zu bytes for packed request nvlist", pack_size); 206 goto out; 207 } 208 209 nvl_ret = nvlist_pack(req->add_nvl, &pack_buf, &pack_size, 210 NV_ENCODE_NATIVE, 0); 211 if (!i2c_nvlist_error(hdl, nvl_ret, "pack request nvlist")) { 212 goto out; 213 } 214 215 (void) memset(&dev, 0, sizeof (ui2c_dev_add_t)); 216 dev.uda_nvl = (uintptr_t)pack_buf; 217 dev.uda_nvl_len = pack_size; 218 219 if (ioctl(req->add_port->port_fd, UI2C_IOCTL_DEVICE_ADD, &dev) != 0) { 220 int e = errno; 221 ret = i2c_ioctl_syserror(hdl, e, "add device request"); 222 goto out; 223 } 224 225 if (dev.uda_error.i2c_error != I2C_CORE_E_OK) { 226 ret = i2c_ioctl_error(hdl, &dev.uda_error, 227 "add device request"); 228 goto out; 229 } 230 231 ret = i2c_success(hdl); 232 out: 233 free(pack_buf); 234 return (ret); 235 } 236 237 bool 238 i2c_device_rem(i2c_port_t *port, const i2c_addr_t *addr) 239 { 240 ui2c_dev_rem_t rem; 241 i2c_hdl_t *hdl = port->port_hdl; 242 243 if (addr == NULL) { 244 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 245 "invalid i2c_addr_t pointer: %p", addr)); 246 } 247 248 if (!i2c_addr_validate(hdl, addr)) { 249 return (false); 250 } 251 252 (void) memset(&rem, 0, sizeof (ui2c_dev_rem_t)); 253 rem.udr_addr = *addr; 254 255 if (ioctl(port->port_fd, UI2C_IOCTL_DEVICE_REMOVE, &rem) != 0) { 256 int e = errno; 257 return (i2c_ioctl_syserror(hdl, e, "remove device request")); 258 } 259 260 if (rem.udr_error.i2c_error != I2C_CORE_E_OK) { 261 return (i2c_ioctl_error(hdl, &rem.udr_error, 262 "remove device request")); 263 } 264 265 return (i2c_success(hdl)); 266 } 267 268 void 269 i2c_device_discover_fini(i2c_dev_iter_t *iter) 270 { 271 if (iter == NULL) 272 return; 273 274 i2c_port_discover_fini(iter->di_iter); 275 free(iter); 276 } 277 278 /* 279 * Fill information about the device's under this single port per the notes in 280 * i2c_device_discover_step(). 281 */ 282 static bool 283 i2c_device_discover_port(i2c_hdl_t *hdl, dev_port_info_t *dpi) 284 { 285 for (di_minor_t m = di_minor_next(dpi->dpi_port, DI_MINOR_NIL); 286 m != DI_MINOR_NIL; m = di_minor_next(dpi->dpi_port, m)) { 287 i2c_addr_t addr; 288 289 if (strcmp(di_minor_nodetype(m), DDI_NT_I2C_DEV) != 0) 290 continue; 291 292 if (!i2c_kernel_address_parse(hdl, di_minor_name(m), &addr)) { 293 return (false); 294 } 295 296 if (addr.ia_type == I2C_ADDR_7BIT) { 297 dpi->dpi_7b[addr.ia_addr].dmi_minor = m; 298 } else { 299 dpi->dpi_10b[addr.ia_addr].dmi_minor = m; 300 } 301 } 302 303 /* 304 * Now go through all of our children and try to map them to something 305 * we know. 306 */ 307 for (di_node_t di = di_child_node(dpi->dpi_port); di != DI_NODE_NIL; 308 di = di_sibling_node(di)) { 309 i2c_addr_t addr; 310 311 if (i2c_node_type(di) != I2C_NODE_T_DEV) { 312 continue; 313 } 314 315 /* 316 * For the purposes of iteration we order devices by the first 317 * address that they have in their regs[] array. We assume that 318 * this will be the primary one. We will skip cases where we 319 * have a minor but not a devinfo in this list. 320 */ 321 if (!i2c_reg_to_addr(hdl, di, &addr, 0)) { 322 return (false); 323 } 324 325 if (addr.ia_type == I2C_ADDR_7BIT) { 326 dpi->dpi_7b[addr.ia_addr].dmi_node = di; 327 } else { 328 dpi->dpi_10b[addr.ia_addr].dmi_node = di; 329 } 330 } 331 332 return (true); 333 } 334 335 static bool 336 i2c_device_discover_one(i2c_dev_iter_t *iter, dev_map_info_t *map) 337 { 338 iter->di_disc.idd_map = map; 339 iter->di_disc.idd_port = &iter->di_info; 340 if (!i2c_node_to_path(iter->di_hdl, map->dmi_node, 341 iter->di_disc.idd_path, sizeof (iter->di_disc.idd_path))) { 342 return (false); 343 } 344 345 return (true); 346 } 347 348 /* 349 * Device discovery starts by walking the last of I2C ports. After that we 350 * proceed to try to walk all of the immediate children. The port has a list of 351 * all of the minors that are I2C devices. So we first gather that up and marry 352 * it up to the actual dev_info nodes in the snapshot. The minor node will be 353 * created while we're creating the child node and we should only see one if we 354 * see the other. By walking the minor node list, this gives us a way to ignore 355 * dev info nodes that end up under the port that aren't actually in-band 356 * devices (e.g. a non-in-band mux). 357 */ 358 i2c_iter_t 359 i2c_device_discover_step(i2c_dev_iter_t *iter, const i2c_dev_disc_t **discp) 360 { 361 for (;;) { 362 /* 363 * First check if we're already done or if we're taking a lap 364 * because we've processed all ports. 365 */ 366 if (iter->di_done) { 367 return (I2C_ITER_DONE); 368 } 369 370 if (iter->di_curport == NULL) { 371 i2c_iter_t iret = i2c_port_discover_step(iter->di_iter, 372 &iter->di_curport); 373 if (iret == I2C_ITER_DONE) { 374 iter->di_done = true; 375 return (I2C_ITER_DONE); 376 } else if (iret != I2C_ITER_VALID) { 377 return (iret); 378 } 379 380 memset(&iter->di_info, 0, sizeof (dev_port_info_t)); 381 iter->di_info.dpi_port = 382 i2c_port_disc_devi(iter->di_curport); 383 } 384 385 /* 386 * See if we have minor info for this port yet. If not, we go 387 * and build it. 388 */ 389 dev_port_info_t *pi = &iter->di_info; 390 if (!pi->dpi_scanned) { 391 pi->dpi_scanned = true; 392 if (!i2c_device_discover_port(iter->di_hdl, pi)) { 393 return (I2C_ITER_ERROR); 394 } 395 } 396 397 /* 398 * Is this port done, if so move onto the next. 399 */ 400 if (pi->dpi_7bit_done && pi->dpi_10bit_done) { 401 iter->di_curport = NULL; 402 continue; 403 } 404 405 if (!pi->dpi_7bit_done) { 406 while (pi->dpi_curidx < ARRAY_SIZE(pi->dpi_7b)) { 407 dev_map_info_t *map = 408 &pi->dpi_7b[pi->dpi_curidx]; 409 pi->dpi_curidx++; 410 if (map->dmi_minor == DI_MINOR_NIL || 411 map->dmi_node == DI_NODE_NIL) { 412 continue; 413 } 414 415 if (i2c_device_discover_one(iter, map)) { 416 *discp = &iter->di_disc; 417 return (I2C_ITER_VALID); 418 } else { 419 return (I2C_ITER_ERROR); 420 } 421 } 422 pi->dpi_7bit_done = true; 423 pi->dpi_curidx = 0; 424 } 425 426 427 if (!pi->dpi_10bit_done) { 428 while (pi->dpi_curidx < ARRAY_SIZE(pi->dpi_10b)) { 429 dev_map_info_t *map = 430 &pi->dpi_10b[pi->dpi_curidx]; 431 pi->dpi_curidx++; 432 if (map->dmi_minor == DI_MINOR_NIL || 433 map->dmi_node == DI_NODE_NIL) { 434 continue; 435 } 436 437 if (i2c_device_discover_one(iter, map)) { 438 *discp = &iter->di_disc; 439 return (I2C_ITER_VALID); 440 } else { 441 return (I2C_ITER_ERROR); 442 } 443 } 444 pi->dpi_10bit_done = true; 445 pi->dpi_curidx = 0; 446 } 447 } 448 449 return (I2C_ITER_ERROR); 450 } 451 452 bool 453 i2c_device_discover_init(i2c_hdl_t *hdl, i2c_dev_iter_t **iterp) 454 { 455 i2c_dev_iter_t *iter; 456 457 if (iterp == NULL) { 458 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 459 "invalid i2c_dev_iter_t output pointer: %p", iterp)); 460 } 461 462 iter = calloc(1, sizeof (i2c_dev_iter_t)); 463 if (iter == NULL) { 464 int e = errno; 465 return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate " 466 "memory for a new i2c_dev_iter_t")); 467 } 468 469 iter->di_hdl = hdl; 470 iter->di_done = false; 471 if (!i2c_port_discover_init(hdl, &iter->di_iter)) { 472 free(iter); 473 return (false); 474 } 475 476 *iterp = iter; 477 return (i2c_success(hdl)); 478 } 479 480 bool 481 i2c_device_discover(i2c_hdl_t *hdl, i2c_dev_disc_f func, void *arg) 482 { 483 i2c_dev_iter_t *iter; 484 const i2c_dev_disc_t *disc; 485 i2c_iter_t ret; 486 487 if (func == NULL) { 488 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 489 "invalid i2c_dev_disc_f function pointer: %p", func)); 490 } 491 492 if (!i2c_device_discover_init(hdl, &iter)) { 493 return (false); 494 } 495 496 while ((ret = i2c_device_discover_step(iter, &disc)) == 497 I2C_ITER_VALID) { 498 if (!func(hdl, disc, arg)) 499 break; 500 } 501 502 i2c_device_discover_fini(iter); 503 if (ret == I2C_ITER_ERROR) { 504 return (false); 505 } 506 507 return (i2c_success(hdl)); 508 } 509 510 const char * 511 i2c_device_disc_name(const i2c_dev_disc_t *disc) 512 { 513 return (di_node_name(disc->idd_map->dmi_node)); 514 } 515 516 di_node_t 517 i2c_device_disc_devi(const i2c_dev_disc_t *disc) 518 { 519 return (disc->idd_map->dmi_node); 520 } 521 522 di_minor_t 523 i2c_device_disc_devctl(const i2c_dev_disc_t *disc) 524 { 525 return (disc->idd_map->dmi_minor); 526 } 527 528 const char * 529 i2c_device_disc_path(const i2c_dev_disc_t *disc) 530 { 531 return (disc->idd_path); 532 } 533 534 void 535 i2c_device_info_free(i2c_dev_info_t *info) 536 { 537 free(info->dinfo_name); 538 free(info->dinfo_driver); 539 free(info->dinfo_addrs); 540 di_devfs_path_free(info->dinfo_minor); 541 free(info); 542 } 543 544 const char * 545 i2c_device_info_path(const i2c_dev_info_t *info) 546 { 547 return (info->dinfo_path); 548 } 549 550 const char * 551 i2c_device_info_name(const i2c_dev_info_t *info) 552 { 553 return (info->dinfo_name); 554 } 555 556 const char * 557 i2c_device_info_driver(const i2c_dev_info_t *info) 558 { 559 return (info->dinfo_driver); 560 } 561 562 int 563 i2c_device_info_instance(const i2c_dev_info_t *info) 564 { 565 return (info->dinfo_inst); 566 } 567 568 uint32_t 569 i2c_device_info_naddrs(const i2c_dev_info_t *info) 570 { 571 return (info->dinfo_naddrs); 572 } 573 574 const i2c_addr_t * 575 i2c_device_info_addr_primary(const i2c_dev_info_t *info) 576 { 577 return (&info->dinfo_info.udi_primary); 578 } 579 580 const i2c_addr_t * 581 i2c_device_info_addr(const i2c_dev_info_t *info, uint32_t n) 582 { 583 if (n >= info->dinfo_naddrs) { 584 return (NULL); 585 } 586 587 return (&info->dinfo_addrs[n]); 588 } 589 590 i2c_addr_source_t 591 i2c_device_info_addr_source(const i2c_dev_info_t *info, uint32_t n) 592 { 593 if (n >= info->dinfo_naddrs) { 594 return (0); 595 } 596 597 return (info->dinfo_info.udi_7b[info->dinfo_addrs[n].ia_addr]); 598 } 599 600 bool 601 i2c_device_info_snap(i2c_hdl_t *hdl, di_node_t dn, i2c_dev_info_t **infop) 602 { 603 di_minor_t minor; 604 i2c_dev_info_t *info; 605 606 if (dn == DI_NODE_NIL) { 607 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 608 "invalid di_node_t: %p", dn)); 609 } 610 611 if (infop == NULL) { 612 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 613 "invalid i2c_dev_info_t output pointer: %p", infop)); 614 } 615 616 if (!i2c_node_is_type(dn, I2C_NODE_T_DEV)) { 617 return (i2c_error(hdl, I2C_ERR_BAD_DEVI, 0, "devi %s@%s is " 618 "not an i2c device", di_node_name(dn), di_bus_addr(dn))); 619 } 620 621 minor = i2c_node_minor(dn); 622 if (minor == DI_MINOR_NIL) { 623 return (i2c_error(hdl, I2C_ERR_BAD_DEVI, 0, "devi %s@%s is " 624 "not an i2c device: failed to find device minor", 625 di_node_name(dn), di_bus_addr(dn))); 626 } 627 628 info = calloc(1, sizeof (i2c_dev_info_t)); 629 info->dinfo_name = strdup(di_node_name(dn)); 630 if (info->dinfo_name == NULL) { 631 int e = errno; 632 i2c_device_info_free(info); 633 return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to duplicate " 634 "device node name")); 635 } 636 637 if (!i2c_node_to_path(hdl, dn, info->dinfo_path, 638 sizeof (info->dinfo_path))) { 639 i2c_device_info_free(info); 640 return (false); 641 } 642 643 if (di_driver_name(dn) != NULL) { 644 info->dinfo_driver = strdup(di_driver_name(dn)); 645 if (info->dinfo_driver == NULL) { 646 int e = errno; 647 i2c_device_info_free(info); 648 return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to " 649 "duplicate device driver name")); 650 } 651 } else { 652 info->dinfo_driver = NULL; 653 } 654 info->dinfo_inst = di_instance(dn); 655 info->dinfo_minor = di_devfs_minor_path(minor); 656 if (info->dinfo_minor == NULL) { 657 int e = errno; 658 i2c_device_info_free(info); 659 return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to " 660 "obtain devices's devfs path: %s", strerrordesc_np(e))); 661 } 662 663 int fd = openat(hdl->ih_devfd, info->dinfo_minor + 1, O_RDONLY); 664 if (fd < 0) { 665 int e = errno; 666 (void) i2c_error(hdl, I2C_ERR_OPEN_DEV, e, "failed to open " 667 "device path /devices%s: %s", info->dinfo_minor, 668 strerrordesc_np(e)); 669 i2c_device_info_free(info); 670 return (false); 671 } 672 673 if (ioctl(fd, UI2C_IOCTL_DEV_INFO, &info->dinfo_info) != 0) { 674 int e = errno; 675 i2c_device_info_free(info); 676 return (i2c_ioctl_syserror(hdl, e, "device information " 677 "request")); 678 } 679 680 (void) close(fd); 681 if (info->dinfo_info.udi_error.i2c_error != I2C_CORE_E_OK) { 682 i2c_device_info_free(info); 683 return (i2c_ioctl_error(hdl, &info->dinfo_info.udi_error, 684 "device information request")); 685 } 686 687 for (uint32_t i = 0; i < ARRAY_SIZE(info->dinfo_info.udi_7b); i++) { 688 if (info->dinfo_info.udi_7b[i] != 0) { 689 info->dinfo_naddrs++; 690 } 691 } 692 693 VERIFY3U(info->dinfo_naddrs, >, 0); 694 info->dinfo_addrs = calloc(info->dinfo_naddrs, sizeof (i2c_addr_t)); 695 if (info->dinfo_addrs == NULL) { 696 int e = errno; 697 (void) i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate " 698 "memory for %u I2C addresses", info->dinfo_naddrs); 699 i2c_device_info_free(info); 700 return (false); 701 } 702 703 for (uint32_t i = 0, idx = 0; i < ARRAY_SIZE(info->dinfo_info.udi_7b); 704 i++) { 705 if (info->dinfo_info.udi_7b[i] != 0) { 706 info->dinfo_addrs[idx].ia_type = I2C_ADDR_7BIT; 707 info->dinfo_addrs[idx].ia_addr = i; 708 idx++; 709 } 710 } 711 712 *infop = info; 713 return (i2c_success(hdl)); 714 } 715 716 /* 717 * Get information about a device specified by path. In addition, return its 718 * port. If nodev_ok is set to true, then our caller is fine with returning 719 * success, but without the device information. This would happen if the path 720 * ended at a port. 721 */ 722 bool 723 i2c_port_dev_init_by_path(i2c_hdl_t *hdl, const char *path, bool nodev_ok, 724 i2c_port_t **portp, i2c_dev_info_t **infop) 725 { 726 i2c_node_type_t type; 727 di_node_t dn, root, port_dn, dev_dn; 728 if (path == NULL) { 729 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 730 "invalid i2c path: %p", path)); 731 } 732 733 if (portp == NULL) { 734 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 735 "invalid i2c_port_t output pointer: %p", infop)); 736 } 737 *portp = NULL; 738 739 if (infop == NULL) { 740 return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered " 741 "invalid i2c_dev_info_t output pointer: %p", infop)); 742 } 743 *infop = NULL; 744 745 root = di_init("/", DINFOCPYALL); 746 if (root == DI_NODE_NIL) { 747 int e = errno; 748 return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to " 749 "initialize devinfo snapshot: %s", strerrordesc_np(e))); 750 } 751 752 if (!i2c_path_parse(hdl, path, root, &dn, &type, I2C_ERR_BAD_DEVICE)) { 753 di_fini(root); 754 return (false); 755 } 756 757 switch (type) { 758 case I2C_NODE_T_DEV: 759 dev_dn = dn; 760 port_dn = di_parent_node(dev_dn); 761 break; 762 case I2C_NODE_T_PORT: 763 if (!nodev_ok) { 764 return (i2c_error(hdl, I2C_ERR_BAD_DEVICE, 0, "parsed " 765 "I2C path %s did not end at a device", path)); 766 } 767 dev_dn = DI_NODE_NIL; 768 port_dn = dn; 769 break; 770 default: 771 di_fini(root); 772 return (i2c_error(hdl, I2C_ERR_BAD_DEVICE, 0, "parsed I2C " 773 "path %s did not end at a device (or port)", path)); 774 } 775 776 if (!i2c_port_init(hdl, port_dn, portp)) { 777 di_fini(root); 778 return (false); 779 } 780 781 if (dev_dn != DI_NODE_NIL) { 782 if (!i2c_device_info_snap(hdl, dev_dn, infop)) { 783 di_fini(root); 784 i2c_port_fini(*portp); 785 return (false); 786 } 787 } 788 789 return (true); 790 } 791