1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 /* 31 * Interfaces for getting device configuration data from kernel 32 * through the devinfo driver. 33 */ 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <strings.h> 39 #include <stropts.h> 40 #include <fcntl.h> 41 #include <poll.h> 42 #include <synch.h> 43 #include <unistd.h> 44 #include <sys/mkdev.h> 45 #include <sys/obpdefs.h> 46 #include <sys/stat.h> 47 #include <sys/types.h> 48 #include <sys/time.h> 49 #include <sys/autoconf.h> 50 #include <stdarg.h> 51 52 #define NDEBUG 1 53 #include <assert.h> 54 55 #include "libdevinfo.h" 56 57 /* 58 * Debug message levels 59 */ 60 typedef enum { 61 DI_QUIET = 0, /* No debug messages - the default */ 62 DI_ERR = 1, 63 DI_INFO, 64 DI_TRACE, 65 DI_TRACE1, 66 DI_TRACE2 67 } di_debug_t; 68 69 int di_debug = DI_QUIET; 70 71 #define DPRINTF(args) { if (di_debug != DI_QUIET) dprint args; } 72 73 void dprint(di_debug_t msglevel, const char *fmt, ...); 74 75 76 #pragma init(_libdevinfo_init) 77 78 void 79 _libdevinfo_init() 80 { 81 char *debug_str = getenv("_LIBDEVINFO_DEBUG"); 82 83 if (debug_str) { 84 errno = 0; 85 di_debug = atoi(debug_str); 86 if (errno || di_debug < DI_QUIET) 87 di_debug = DI_QUIET; 88 } 89 } 90 91 di_node_t 92 di_init(const char *phys_path, uint_t flag) 93 { 94 return (di_init_impl(phys_path, flag, NULL)); 95 } 96 97 /* 98 * We use blocking_open() to guarantee access to the devinfo device, if open() 99 * is failing with EAGAIN. 100 */ 101 static int 102 blocking_open(const char *path, int oflag) 103 { 104 int fd; 105 106 while ((fd = open(path, oflag)) == -1 && errno == EAGAIN) 107 (void) poll(NULL, 0, 1 * MILLISEC); 108 109 return (fd); 110 } 111 112 /* private interface */ 113 di_node_t 114 di_init_driver(const char *drv_name, uint_t flag) 115 { 116 int fd; 117 char driver[MAXPATHLEN]; 118 119 /* 120 * Don't allow drv_name to exceed MAXPATHLEN - 1, or 1023, 121 * which should be sufficient for any sensible programmer. 122 */ 123 if ((drv_name == NULL) || (strlen(drv_name) >= MAXPATHLEN)) { 124 errno = EINVAL; 125 return (DI_NODE_NIL); 126 } 127 (void) strcpy(driver, drv_name); 128 129 /* 130 * open the devinfo driver 131 */ 132 if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo", 133 O_RDONLY)) == -1) { 134 DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", errno)); 135 return (DI_NODE_NIL); 136 } 137 138 if (ioctl(fd, DINFOLODRV, driver) != 0) { 139 DPRINTF((DI_ERR, "failed to load driver %s\n", driver)); 140 (void) close(fd); 141 errno = ENXIO; 142 return (DI_NODE_NIL); 143 } 144 (void) close(fd); 145 146 /* 147 * Driver load succeeded, return a snapshot 148 */ 149 return (di_init("/", flag)); 150 } 151 152 di_node_t 153 di_init_impl(const char *phys_path, uint_t flag, 154 struct di_priv_data *priv) 155 { 156 caddr_t pa; 157 int fd, map_size; 158 struct di_all *dap; 159 struct dinfo_io dinfo_io; 160 161 uint_t pageoffset = sysconf(_SC_PAGESIZE) - 1; 162 uint_t pagemask = ~pageoffset; 163 164 DPRINTF((DI_INFO, "di_init: taking a snapshot\n")); 165 166 /* 167 * Make sure there is no minor name in the path 168 * and the path do not start with /devices.... 169 */ 170 if (strchr(phys_path, ':') || 171 (strncmp(phys_path, "/devices", 8) == 0) || 172 (strlen(phys_path) > MAXPATHLEN)) { 173 errno = EINVAL; 174 return (DI_NODE_NIL); 175 } 176 177 if (strlen(phys_path) == 0) 178 (void) sprintf(dinfo_io.root_path, "/"); 179 else if (*phys_path != '/') 180 (void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path), 181 "/%s", phys_path); 182 else 183 (void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path), 184 "%s", phys_path); 185 186 /* 187 * If private data is requested, copy the format specification 188 */ 189 if (flag & DINFOPRIVDATA & 0xff) { 190 if (priv) 191 bcopy(priv, &dinfo_io.priv, 192 sizeof (struct di_priv_data)); 193 else { 194 errno = EINVAL; 195 return (DI_NODE_NIL); 196 } 197 } 198 199 /* 200 * Attempt to open the devinfo driver. Make a second attempt at the 201 * read-only minor node if we don't have privileges to open the full 202 * version _and_ if we're not requesting operations that the read-only 203 * node can't perform. (Setgid processes would fail an access() test, 204 * of course.) 205 */ 206 if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo", 207 O_RDONLY)) == -1) { 208 if ((flag & DINFOFORCE) == DINFOFORCE || 209 (flag & DINFOPRIVDATA) == DINFOPRIVDATA) { 210 /* 211 * We wanted to perform a privileged operation, but the 212 * privileged node isn't available. Don't modify errno 213 * on our way out (but display it if we're running with 214 * di_debug set). 215 */ 216 DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", 217 errno)); 218 return (DI_NODE_NIL); 219 } 220 221 if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo,ro", 222 O_RDONLY)) == -1) { 223 DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", 224 errno)); 225 return (DI_NODE_NIL); 226 } 227 } 228 229 /* 230 * Verify that there is no major conflict, i.e., we are indeed opening 231 * the devinfo driver. 232 */ 233 if (ioctl(fd, DINFOIDENT, NULL) != DI_MAGIC) { 234 DPRINTF((DI_ERR, 235 "driver ID failed; check for major conflict\n")); 236 (void) close(fd); 237 return (DI_NODE_NIL); 238 } 239 240 /* 241 * create snapshot 242 */ 243 if ((map_size = ioctl(fd, flag, &dinfo_io)) < 0) { 244 DPRINTF((DI_ERR, "devinfo ioctl failed with " 245 "error: %d\n", errno)); 246 (void) close(fd); 247 return (DI_NODE_NIL); 248 } else if (map_size == 0) { 249 DPRINTF((DI_ERR, "%s not found\n", phys_path)); 250 errno = ENXIO; 251 (void) close(fd); 252 return (DI_NODE_NIL); 253 } 254 255 /* 256 * copy snapshot to userland 257 */ 258 map_size = (map_size + pageoffset) & pagemask; 259 if ((pa = valloc(map_size)) == NULL) { 260 DPRINTF((DI_ERR, "valloc failed for snapshot\n")); 261 (void) close(fd); 262 return (DI_NODE_NIL); 263 } 264 265 if (ioctl(fd, DINFOUSRLD, pa) != map_size) { 266 DPRINTF((DI_ERR, "failed to copy snapshot to usrld\n")); 267 (void) close(fd); 268 free(pa); 269 errno = EFAULT; 270 return (DI_NODE_NIL); 271 } 272 273 (void) close(fd); 274 275 dap = DI_ALL(pa); 276 if (dap->top_devinfo == 0) { /* phys_path not found */ 277 DPRINTF((DI_ERR, "%s not found\n", phys_path)); 278 free(pa); 279 errno = EINVAL; 280 return (DI_NODE_NIL); 281 } 282 283 return (DI_NODE(pa + dap->top_devinfo)); 284 } 285 286 void 287 di_fini(di_node_t root) 288 { 289 caddr_t pa; /* starting address of map */ 290 291 DPRINTF((DI_INFO, "di_fini: freeing a snapshot\n")); 292 293 /* 294 * paranoid checking 295 */ 296 if (root == DI_NODE_NIL) { 297 DPRINTF((DI_ERR, "di_fini called with NIL arg\n")); 298 return; 299 } 300 301 /* 302 * The root contains its own offset--self. 303 * Subtracting it from root address, we get the starting addr. 304 * The map_size is stored at the beginning of snapshot. 305 * Once we have starting address and size, we can free(). 306 */ 307 pa = (caddr_t)root - DI_NODE(root)->self; 308 309 free(pa); 310 } 311 312 di_node_t 313 di_parent_node(di_node_t node) 314 { 315 caddr_t pa; /* starting address of map */ 316 317 if (node == DI_NODE_NIL) { 318 errno = EINVAL; 319 return (DI_NODE_NIL); 320 } 321 322 DPRINTF((DI_TRACE, "Get parent of node %s\n", di_node_name(node))); 323 324 pa = (caddr_t)node - DI_NODE(node)->self; 325 326 if (DI_NODE(node)->parent) { 327 return (DI_NODE(pa + DI_NODE(node)->parent)); 328 } 329 330 /* 331 * Deal with error condition: 332 * If parent doesn't exist and node is not the root, 333 * set errno to ENOTSUP. Otherwise, set errno to ENXIO. 334 */ 335 if (strcmp(DI_ALL(pa)->root_path, "/") != 0) 336 errno = ENOTSUP; 337 else 338 errno = ENXIO; 339 340 return (DI_NODE_NIL); 341 } 342 343 di_node_t 344 di_sibling_node(di_node_t node) 345 { 346 caddr_t pa; /* starting address of map */ 347 348 if (node == DI_NODE_NIL) { 349 errno = EINVAL; 350 return (DI_NODE_NIL); 351 } 352 353 DPRINTF((DI_TRACE, "Get sibling of node %s\n", di_node_name(node))); 354 355 pa = (caddr_t)node - DI_NODE(node)->self; 356 357 if (DI_NODE(node)->sibling) { 358 return (DI_NODE(pa + DI_NODE(node)->sibling)); 359 } 360 361 /* 362 * Deal with error condition: 363 * Sibling doesn't exist, figure out if ioctl command 364 * has DINFOSUBTREE set. If it doesn't, set errno to 365 * ENOTSUP. 366 */ 367 if (!(DI_ALL(pa)->command & DINFOSUBTREE)) 368 errno = ENOTSUP; 369 else 370 errno = ENXIO; 371 372 return (DI_NODE_NIL); 373 } 374 375 di_node_t 376 di_child_node(di_node_t node) 377 { 378 caddr_t pa; /* starting address of map */ 379 380 DPRINTF((DI_TRACE, "Get child of node %s\n", di_node_name(node))); 381 382 if (node == DI_NODE_NIL) { 383 errno = EINVAL; 384 return (DI_NODE_NIL); 385 } 386 387 pa = (caddr_t)node - DI_NODE(node)->self; 388 389 if (DI_NODE(node)->child) { 390 return (DI_NODE(pa + DI_NODE(node)->child)); 391 } 392 393 /* 394 * Deal with error condition: 395 * Child doesn't exist, figure out if DINFOSUBTREE is set. 396 * If it isn't, set errno to ENOTSUP. 397 */ 398 if (!(DI_ALL(pa)->command & DINFOSUBTREE)) 399 errno = ENOTSUP; 400 else 401 errno = ENXIO; 402 403 return (DI_NODE_NIL); 404 } 405 406 di_node_t 407 di_drv_first_node(const char *drv_name, di_node_t root) 408 { 409 caddr_t pa; /* starting address of map */ 410 int major, devcnt; 411 struct di_devnm *devnm; 412 413 DPRINTF((DI_INFO, "Get first node of driver %s\n", drv_name)); 414 415 if (root == DI_NODE_NIL) { 416 errno = EINVAL; 417 return (DI_NODE_NIL); 418 } 419 420 /* 421 * get major number of driver 422 */ 423 pa = (caddr_t)root - DI_NODE(root)->self; 424 devcnt = DI_ALL(pa)->devcnt; 425 devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames); 426 427 for (major = 0; major < devcnt; major++) 428 if (devnm[major].name && (strcmp(drv_name, 429 (char *)(pa + devnm[major].name)) == 0)) 430 break; 431 432 if (major >= devcnt) { 433 errno = EINVAL; 434 return (DI_NODE_NIL); 435 } 436 437 if (!(devnm[major].head)) { 438 errno = ENXIO; 439 return (DI_NODE_NIL); 440 } 441 442 return (DI_NODE(pa + devnm[major].head)); 443 } 444 445 di_node_t 446 di_drv_next_node(di_node_t node) 447 { 448 caddr_t pa; /* starting address of map */ 449 450 if (node == DI_NODE_NIL) { 451 errno = EINVAL; 452 return (DI_NODE_NIL); 453 } 454 455 DPRINTF((DI_TRACE, "next node on per driver list:" 456 " current=%s, driver=%s\n", 457 di_node_name(node), di_driver_name(node))); 458 459 if (DI_NODE(node)->next == (di_off_t)-1) { 460 errno = ENOTSUP; 461 return (DI_NODE_NIL); 462 } 463 464 pa = (caddr_t)node - DI_NODE(node)->self; 465 466 if (DI_NODE(node)->next == NULL) { 467 errno = ENXIO; 468 return (DI_NODE_NIL); 469 } 470 471 return (DI_NODE(pa + DI_NODE(node)->next)); 472 } 473 474 /* 475 * Internal library interfaces: 476 * node_list etc. for node walking 477 */ 478 struct node_list { 479 struct node_list *next; 480 di_node_t node; 481 }; 482 483 static void 484 free_node_list(struct node_list **headp) 485 { 486 struct node_list *tmp; 487 488 while (*headp) { 489 tmp = *headp; 490 *headp = (*headp)->next; 491 free(tmp); 492 } 493 } 494 495 static void 496 append_node_list(struct node_list **headp, struct node_list *list) 497 { 498 struct node_list *tmp; 499 500 if (*headp == NULL) { 501 *headp = list; 502 return; 503 } 504 505 if (list == NULL) /* a minor optimization */ 506 return; 507 508 tmp = *headp; 509 while (tmp->next) 510 tmp = tmp->next; 511 512 tmp->next = list; 513 } 514 515 static void 516 prepend_node_list(struct node_list **headp, struct node_list *list) 517 { 518 struct node_list *tmp; 519 520 if (list == NULL) 521 return; 522 523 tmp = *headp; 524 *headp = list; 525 526 if (tmp == NULL) /* a minor optimization */ 527 return; 528 529 while (list->next) 530 list = list->next; 531 532 list->next = tmp; 533 } 534 535 /* 536 * returns 1 if node is a descendant of parent, 0 otherwise 537 */ 538 static int 539 is_descendant(di_node_t node, di_node_t parent) 540 { 541 /* 542 * DI_NODE_NIL is parent of root, so it is 543 * the parent of all nodes. 544 */ 545 if (parent == DI_NODE_NIL) { 546 return (1); 547 } 548 549 do { 550 node = di_parent_node(node); 551 } while ((node != DI_NODE_NIL) && (node != parent)); 552 553 return (node != DI_NODE_NIL); 554 } 555 556 /* 557 * Insert list before the first node which is NOT a descendent of parent. 558 * This is needed to reproduce the exact walking order of link generators. 559 */ 560 static void 561 insert_node_list(struct node_list **headp, struct node_list *list, 562 di_node_t parent) 563 { 564 struct node_list *tmp, *tmp1; 565 566 if (list == NULL) 567 return; 568 569 tmp = *headp; 570 if (tmp == NULL) { /* a minor optimization */ 571 *headp = list; 572 return; 573 } 574 575 if (!is_descendant(tmp->node, parent)) { 576 prepend_node_list(headp, list); 577 return; 578 } 579 580 /* 581 * Find first node which is not a descendant 582 */ 583 while (tmp->next && is_descendant(tmp->next->node, parent)) { 584 tmp = tmp->next; 585 } 586 587 tmp1 = tmp->next; 588 tmp->next = list; 589 append_node_list(headp, tmp1); 590 } 591 592 /* 593 * Get a linked list of handles of all children 594 */ 595 static struct node_list * 596 get_children(di_node_t node) 597 { 598 di_node_t child; 599 struct node_list *result, *tmp; 600 601 DPRINTF((DI_TRACE1, "Get children of node %s\n", di_node_name(node))); 602 603 if ((child = di_child_node(node)) == DI_NODE_NIL) { 604 return (NULL); 605 } 606 607 if ((result = malloc(sizeof (struct node_list))) == NULL) { 608 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 609 return (NULL); 610 } 611 612 result->node = child; 613 tmp = result; 614 615 while ((child = di_sibling_node(tmp->node)) != DI_NODE_NIL) { 616 if ((tmp->next = malloc(sizeof (struct node_list))) == NULL) { 617 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 618 free_node_list(&result); 619 return (NULL); 620 } 621 tmp = tmp->next; 622 tmp->node = child; 623 } 624 625 tmp->next = NULL; 626 627 return (result); 628 } 629 630 /* 631 * Internal library interface: 632 * Delete all siblings of the first node from the node_list, along with 633 * the first node itself. 634 */ 635 static void 636 prune_sib(struct node_list **headp) 637 { 638 di_node_t parent, curr_par, curr_gpar; 639 struct node_list *curr, *prev; 640 641 /* 642 * get handle to parent of first node 643 */ 644 if ((parent = di_parent_node((*headp)->node)) == DI_NODE_NIL) { 645 /* 646 * This must be the root of the snapshot, so can't 647 * have any siblings. 648 * 649 * XXX Put a check here just in case. 650 */ 651 if ((*headp)->next) 652 DPRINTF((DI_ERR, "Unexpected err in di_walk_node.\n")); 653 654 free(*headp); 655 *headp = NULL; 656 return; 657 } 658 659 /* 660 * To be complete, we should also delete the children 661 * of siblings that have already been visited. 662 * This happens for DI_WALK_SIBFIRST when the first node 663 * is NOT the first in the linked list of siblings. 664 * 665 * Hence, we compare parent with BOTH the parent and grandparent 666 * of nodes, and delete node is a match is found. 667 */ 668 prev = *headp; 669 curr = prev->next; 670 while (curr) { 671 if (((curr_par = di_parent_node(curr->node)) != DI_NODE_NIL) && 672 ((curr_par == parent) || ((curr_gpar = 673 di_parent_node(curr_par)) != DI_NODE_NIL) && 674 (curr_gpar == parent))) { 675 /* 676 * match parent/grandparent: delete curr 677 */ 678 prev->next = curr->next; 679 free(curr); 680 curr = prev->next; 681 } else 682 curr = curr->next; 683 } 684 685 /* 686 * delete the first node 687 */ 688 curr = *headp; 689 *headp = curr->next; 690 free(curr); 691 } 692 693 /* 694 * Internal library function: 695 * Update node list based on action (return code from callback) 696 * and flag specifying walking behavior. 697 */ 698 static void 699 update_node_list(int action, uint_t flag, struct node_list **headp) 700 { 701 struct node_list *children, *tmp; 702 di_node_t parent = di_parent_node((*headp)->node); 703 704 switch (action) { 705 case DI_WALK_TERMINATE: 706 /* 707 * free the node list and be done 708 */ 709 children = NULL; 710 free_node_list(headp); 711 break; 712 713 case DI_WALK_PRUNESIB: 714 /* 715 * Get list of children and prune siblings 716 */ 717 children = get_children((*headp)->node); 718 prune_sib(headp); 719 break; 720 721 case DI_WALK_PRUNECHILD: 722 /* 723 * Set children to NULL and pop first node 724 */ 725 children = NULL; 726 tmp = *headp; 727 *headp = tmp->next; 728 free(tmp); 729 break; 730 731 case DI_WALK_CONTINUE: 732 default: 733 /* 734 * Get list of children and pop first node 735 */ 736 children = get_children((*headp)->node); 737 tmp = *headp; 738 *headp = tmp->next; 739 free(tmp); 740 break; 741 } 742 743 /* 744 * insert the list of children 745 */ 746 switch (flag) { 747 case DI_WALK_CLDFIRST: 748 prepend_node_list(headp, children); 749 break; 750 751 case DI_WALK_SIBFIRST: 752 append_node_list(headp, children); 753 break; 754 755 case DI_WALK_LINKGEN: 756 default: 757 insert_node_list(headp, children, parent); 758 break; 759 } 760 } 761 762 /* 763 * Internal library function: 764 * Invoke callback on one node and update the list of nodes to be walked 765 * based on the flag and return code. 766 */ 767 static void 768 walk_one_node(struct node_list **headp, uint_t flag, void *arg, 769 int (*callback)(di_node_t, void *)) 770 { 771 DPRINTF((DI_TRACE, "Walking node %s\n", di_node_name((*headp)->node))); 772 773 update_node_list(callback((*headp)->node, arg), 774 flag & DI_WALK_MASK, headp); 775 } 776 777 int 778 di_walk_node(di_node_t root, uint_t flag, void *arg, 779 int (*node_callback)(di_node_t, void *)) 780 { 781 struct node_list *head; /* node_list for tree walk */ 782 783 if (root == NULL) { 784 errno = EINVAL; 785 return (-1); 786 } 787 788 if ((head = malloc(sizeof (struct node_list))) == NULL) { 789 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 790 return (-1); 791 } 792 793 head->next = NULL; 794 head->node = root; 795 796 DPRINTF((DI_INFO, "Start node walking from node %s\n", 797 di_node_name(root))); 798 799 while (head != NULL) 800 walk_one_node(&head, flag, arg, node_callback); 801 802 return (0); 803 } 804 805 /* 806 * Internal library function: 807 * Invoke callback for each minor on the minor list of first node 808 * on node_list headp, and place children of first node on the list. 809 * 810 * This is similar to walk_one_node, except we only walk in child 811 * first mode. 812 */ 813 static void 814 walk_one_minor_list(struct node_list **headp, const char *desired_type, 815 uint_t flag, void *arg, int (*callback)(di_node_t, di_minor_t, void *)) 816 { 817 int ddm_type; 818 int action = DI_WALK_CONTINUE; 819 char *node_type; 820 di_minor_t minor = DI_MINOR_NIL; 821 di_node_t node = (*headp)->node; 822 823 while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { 824 ddm_type = di_minor_type(minor); 825 826 if ((ddm_type == DDM_ALIAS) && !(flag & DI_CHECK_ALIAS)) 827 continue; 828 829 if ((ddm_type == DDM_INTERNAL_PATH) && 830 !(flag & DI_CHECK_INTERNAL_PATH)) 831 continue; 832 833 node_type = di_minor_nodetype(minor); 834 if ((desired_type != NULL) && ((node_type == NULL) || 835 strncmp(desired_type, node_type, strlen(desired_type)) 836 != 0)) 837 continue; 838 839 if ((action = callback(node, minor, arg)) == 840 DI_WALK_TERMINATE) { 841 break; 842 } 843 } 844 845 update_node_list(action, DI_WALK_LINKGEN, headp); 846 } 847 848 int 849 di_walk_minor(di_node_t root, const char *minor_type, uint_t flag, void *arg, 850 int (*minor_callback)(di_node_t, di_minor_t, void *)) 851 { 852 struct node_list *head; /* node_list for tree walk */ 853 854 #ifdef DEBUG 855 char *path = di_devfs_path(root); 856 DPRINTF((DI_INFO, "walking minor nodes under %s\n", path)); 857 di_devfs_path_free(path); 858 #endif 859 860 if (root == NULL) { 861 errno = EINVAL; 862 return (-1); 863 } 864 865 if ((head = malloc(sizeof (struct node_list))) == NULL) { 866 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 867 return (-1); 868 } 869 870 head->next = NULL; 871 head->node = root; 872 873 DPRINTF((DI_INFO, "Start minor walking from node %s\n", 874 di_node_name(root))); 875 876 while (head != NULL) 877 walk_one_minor_list(&head, minor_type, flag, arg, 878 minor_callback); 879 880 return (0); 881 } 882 883 /* 884 * generic node parameters 885 * Calling these routines always succeeds. 886 */ 887 char * 888 di_node_name(di_node_t node) 889 { 890 return ((caddr_t)node + DI_NODE(node)->node_name - DI_NODE(node)->self); 891 } 892 893 /* returns NULL ptr or a valid ptr to non-NULL string */ 894 char * 895 di_bus_addr(di_node_t node) 896 { 897 caddr_t pa = (caddr_t)node - DI_NODE(node)->self; 898 899 if (DI_NODE(node)->address == 0) 900 return (NULL); 901 902 return ((char *)(pa + DI_NODE(node)->address)); 903 } 904 905 char * 906 di_binding_name(di_node_t node) 907 { 908 caddr_t pa = (caddr_t)node - DI_NODE(node)->self; 909 910 if (DI_NODE(node)->bind_name == 0) 911 return (NULL); 912 913 return ((char *)(pa + DI_NODE(node)->bind_name)); 914 } 915 916 int 917 di_compatible_names(di_node_t node, char **names) 918 { 919 char *c; 920 int len, size, entries = 0; 921 922 if (DI_NODE(node)->compat_names == 0) { 923 *names = NULL; 924 return (0); 925 } 926 927 *names = (caddr_t)node + 928 DI_NODE(node)->compat_names - DI_NODE(node)->self; 929 930 c = *names; 931 len = DI_NODE(node)->compat_length; 932 while (len > 0) { 933 entries++; 934 size = strlen(c) + 1; 935 len -= size; 936 c += size; 937 } 938 939 return (entries); 940 } 941 942 int 943 di_instance(di_node_t node) 944 { 945 return (DI_NODE(node)->instance); 946 } 947 948 /* 949 * XXX: emulate the return value of the old implementation 950 * using info from devi_node_class and devi_node_attributes. 951 */ 952 int 953 di_nodeid(di_node_t node) 954 { 955 if (DI_NODE(node)->node_class == DDI_NC_PROM) 956 return (DI_PROM_NODEID); 957 958 if (DI_NODE(node)->attributes & DDI_PERSISTENT) 959 return (DI_SID_NODEID); 960 961 return (DI_PSEUDO_NODEID); 962 } 963 964 uint_t 965 di_state(di_node_t node) 966 { 967 uint_t result = 0; 968 969 if (di_node_state(node) < DS_ATTACHED) 970 result |= DI_DRIVER_DETACHED; 971 if (DI_NODE(node)->state & DEVI_DEVICE_OFFLINE) 972 result |= DI_DEVICE_OFFLINE; 973 if (DI_NODE(node)->state & DEVI_DEVICE_DOWN) 974 result |= DI_DEVICE_OFFLINE; 975 if (DI_NODE(node)->state & DEVI_BUS_QUIESCED) 976 result |= DI_BUS_QUIESCED; 977 if (DI_NODE(node)->state & DEVI_BUS_DOWN) 978 result |= DI_BUS_DOWN; 979 980 return (result); 981 } 982 983 ddi_node_state_t 984 di_node_state(di_node_t node) 985 { 986 return (DI_NODE(node)->node_state); 987 } 988 989 ddi_devid_t 990 di_devid(di_node_t node) 991 { 992 if (DI_NODE(node)->devid == 0) 993 return (NULL); 994 995 return ((ddi_devid_t)((caddr_t)node + 996 DI_NODE(node)->devid - DI_NODE(node)->self)); 997 } 998 999 int 1000 di_driver_major(di_node_t node) 1001 { 1002 int major; 1003 1004 major = DI_NODE(node)->drv_major; 1005 if (major < 0) 1006 return (-1); 1007 return (major); 1008 } 1009 1010 char * 1011 di_driver_name(di_node_t node) 1012 { 1013 int major; 1014 caddr_t pa; 1015 struct di_devnm *devnm; 1016 1017 major = DI_NODE(node)->drv_major; 1018 if (major < 0) 1019 return (NULL); 1020 1021 pa = (caddr_t)node - DI_NODE(node)->self; 1022 devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames); 1023 1024 if (devnm[major].name) 1025 return (pa + devnm[major].name); 1026 else 1027 return (NULL); 1028 } 1029 1030 uint_t 1031 di_driver_ops(di_node_t node) 1032 { 1033 int major; 1034 caddr_t pa; 1035 struct di_devnm *devnm; 1036 1037 major = DI_NODE(node)->drv_major; 1038 if (major < 0) 1039 return (0); 1040 1041 pa = (caddr_t)node - DI_NODE(node)->self; 1042 devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames); 1043 1044 return (devnm[major].ops); 1045 } 1046 1047 /* 1048 * returns the length of the path, caller must free memory 1049 */ 1050 char * 1051 di_devfs_path(di_node_t node) 1052 { 1053 caddr_t pa; 1054 di_node_t parent; 1055 int depth = 0, len = 0; 1056 char *buf, *name[MAX_TREE_DEPTH], *addr[MAX_TREE_DEPTH]; 1057 1058 if (node == DI_NODE_NIL) { 1059 errno = EINVAL; 1060 return (NULL); 1061 } 1062 1063 /* 1064 * trace back to root, note the node_name & address 1065 */ 1066 while ((parent = di_parent_node(node)) != DI_NODE_NIL) { 1067 name[depth] = di_node_name(node); 1068 len += strlen(name[depth]) + 1; /* 1 for '/' */ 1069 1070 if ((addr[depth] = di_bus_addr(node)) != NULL) 1071 len += strlen(addr[depth]) + 1; /* 1 for '@' */ 1072 1073 node = parent; 1074 depth++; 1075 } 1076 1077 /* 1078 * get the path to the root of snapshot 1079 */ 1080 pa = (caddr_t)node - DI_NODE(node)->self; 1081 name[depth] = DI_ALL(pa)->root_path; 1082 len += strlen(name[depth]) + 1; 1083 1084 /* 1085 * allocate buffer and assemble path 1086 */ 1087 if ((buf = malloc(len)) == NULL) { 1088 return (NULL); 1089 } 1090 1091 (void) strcpy(buf, name[depth]); 1092 len = strlen(buf); 1093 if (buf[len - 1] == '/') 1094 len--; /* delete trailing '/' */ 1095 1096 while (depth) { 1097 depth--; 1098 buf[len] = '/'; 1099 (void) strcpy(buf + len + 1, name[depth]); 1100 len += strlen(name[depth]) + 1; 1101 if (addr[depth] && addr[depth][0] != '\0') { 1102 buf[len] = '@'; 1103 (void) strcpy(buf + len + 1, addr[depth]); 1104 len += strlen(addr[depth]) + 1; 1105 } 1106 } 1107 1108 return (buf); 1109 } 1110 1111 char * 1112 di_devfs_minor_path(di_minor_t minor) 1113 { 1114 di_node_t node; 1115 char *full_path, *name, *path; 1116 int full_path_len; 1117 1118 if (minor == DI_MINOR_NIL) { 1119 errno = EINVAL; 1120 return (NULL); 1121 } 1122 1123 name = di_minor_name(minor); 1124 node = di_minor_devinfo(minor); 1125 path = di_devfs_path(node); 1126 if (path == NULL) 1127 return (NULL); 1128 1129 /* make the full path to the device minor node */ 1130 full_path_len = strlen(path) + strlen(name) + 2; 1131 full_path = (char *)calloc(1, full_path_len); 1132 if (full_path != NULL) 1133 (void) snprintf(full_path, full_path_len, "%s:%s", path, name); 1134 1135 di_devfs_path_free(path); 1136 return (full_path); 1137 } 1138 1139 void 1140 di_devfs_path_free(char *buf) 1141 { 1142 if (buf == NULL) { 1143 DPRINTF((DI_ERR, "di_devfs_path_free NULL arg!\n")); 1144 return; 1145 } 1146 1147 free(buf); 1148 } 1149 1150 /* minor data access */ 1151 di_minor_t 1152 di_minor_next(di_node_t node, di_minor_t minor) 1153 { 1154 caddr_t pa; 1155 1156 /* 1157 * paranoid error checking 1158 */ 1159 if (node == DI_NODE_NIL) { 1160 errno = EINVAL; 1161 return (DI_MINOR_NIL); 1162 } 1163 1164 /* 1165 * minor is not NIL 1166 */ 1167 if (minor != DI_MINOR_NIL) { 1168 if (DI_MINOR(minor)->next != 0) 1169 return ((di_minor_t)((void *)((caddr_t)minor - 1170 DI_MINOR(minor)->self + DI_MINOR(minor)->next))); 1171 else { 1172 errno = ENXIO; 1173 return (DI_MINOR_NIL); 1174 } 1175 } 1176 1177 /* 1178 * minor is NIL-->caller asks for first minor node 1179 */ 1180 if (DI_NODE(node)->minor_data != 0) { 1181 return (DI_MINOR((caddr_t)node - DI_NODE(node)->self + 1182 DI_NODE(node)->minor_data)); 1183 } 1184 1185 /* 1186 * no minor data-->check if snapshot includes minor data 1187 * in order to set the correct errno 1188 */ 1189 pa = (caddr_t)node - DI_NODE(node)->self; 1190 if (DINFOMINOR & DI_ALL(pa)->command) 1191 errno = ENXIO; 1192 else 1193 errno = ENOTSUP; 1194 1195 return (DI_MINOR_NIL); 1196 } 1197 1198 /* private interface for dealing with alias minor link generation */ 1199 di_node_t 1200 di_minor_devinfo(di_minor_t minor) 1201 { 1202 if (minor == DI_MINOR_NIL) { 1203 errno = EINVAL; 1204 return (DI_NODE_NIL); 1205 } 1206 1207 return (DI_NODE((caddr_t)minor - DI_MINOR(minor)->self + 1208 DI_MINOR(minor)->node)); 1209 } 1210 1211 ddi_minor_type 1212 di_minor_type(di_minor_t minor) 1213 { 1214 return (DI_MINOR(minor)->type); 1215 } 1216 1217 char * 1218 di_minor_name(di_minor_t minor) 1219 { 1220 if (DI_MINOR(minor)->name == 0) 1221 return (NULL); 1222 1223 return ((caddr_t)minor - DI_MINOR(minor)->self + DI_MINOR(minor)->name); 1224 } 1225 1226 dev_t 1227 di_minor_devt(di_minor_t minor) 1228 { 1229 return (makedev(DI_MINOR(minor)->dev_major, 1230 DI_MINOR(minor)->dev_minor)); 1231 } 1232 1233 int 1234 di_minor_spectype(di_minor_t minor) 1235 { 1236 return (DI_MINOR(minor)->spec_type); 1237 } 1238 1239 char * 1240 di_minor_nodetype(di_minor_t minor) 1241 { 1242 if (DI_MINOR(minor)->node_type == 0) 1243 return (NULL); 1244 1245 return ((caddr_t)minor - 1246 DI_MINOR(minor)->self + DI_MINOR(minor)->node_type); 1247 } 1248 1249 /* 1250 * Single public interface for accessing software properties 1251 */ 1252 di_prop_t 1253 di_prop_next(di_node_t node, di_prop_t prop) 1254 { 1255 int list = DI_PROP_DRV_LIST; 1256 1257 /* 1258 * paranoid check 1259 */ 1260 if (node == DI_NODE_NIL) { 1261 errno = EINVAL; 1262 return (DI_PROP_NIL); 1263 } 1264 1265 /* 1266 * Find which prop list we are at 1267 */ 1268 if (prop != DI_PROP_NIL) 1269 list = DI_PROP(prop)->prop_list; 1270 1271 do { 1272 switch (list++) { 1273 case DI_PROP_DRV_LIST: 1274 prop = di_prop_drv_next(node, prop); 1275 break; 1276 case DI_PROP_SYS_LIST: 1277 prop = di_prop_sys_next(node, prop); 1278 break; 1279 case DI_PROP_GLB_LIST: 1280 prop = di_prop_global_next(node, prop); 1281 break; 1282 case DI_PROP_HW_LIST: 1283 prop = di_prop_hw_next(node, prop); 1284 break; 1285 default: /* shouldn't happen */ 1286 errno = EFAULT; 1287 return (DI_PROP_NIL); 1288 } 1289 } while ((prop == DI_PROP_NIL) && (list <= DI_PROP_HW_LIST)); 1290 1291 return (prop); 1292 } 1293 1294 dev_t 1295 di_prop_devt(di_prop_t prop) 1296 { 1297 return (makedev(DI_PROP(prop)->dev_major, DI_PROP(prop)->dev_minor)); 1298 } 1299 1300 char * 1301 di_prop_name(di_prop_t prop) 1302 { 1303 if (DI_PROP(prop)->prop_name == 0) 1304 return (NULL); 1305 1306 return ((caddr_t)prop - DI_PROP(prop)->self + DI_PROP(prop)->prop_name); 1307 } 1308 1309 int 1310 di_prop_type(di_prop_t prop) 1311 { 1312 uint_t flags = DI_PROP(prop)->prop_flags; 1313 1314 if (flags & DDI_PROP_UNDEF_IT) 1315 return (DI_PROP_TYPE_UNDEF_IT); 1316 1317 if (DI_PROP(prop)->prop_len == 0) 1318 return (DI_PROP_TYPE_BOOLEAN); 1319 1320 if ((flags & DDI_PROP_TYPE_MASK) == DDI_PROP_TYPE_ANY) 1321 return (DI_PROP_TYPE_UNKNOWN); 1322 1323 if (flags & DDI_PROP_TYPE_INT) 1324 return (DI_PROP_TYPE_INT); 1325 1326 if (flags & DDI_PROP_TYPE_INT64) 1327 return (DI_PROP_TYPE_INT64); 1328 1329 if (flags & DDI_PROP_TYPE_STRING) 1330 return (DI_PROP_TYPE_STRING); 1331 1332 if (flags & DDI_PROP_TYPE_BYTE) 1333 return (DI_PROP_TYPE_BYTE); 1334 1335 /* 1336 * Shouldn't get here. In case we do, return unknown type. 1337 * 1338 * XXX--When DDI_PROP_TYPE_COMPOSITE is implemented, we need 1339 * to add DI_PROP_TYPE_COMPOSITE. 1340 */ 1341 DPRINTF((DI_ERR, "Unimplemented property type: 0x%x\n", flags)); 1342 1343 return (DI_PROP_TYPE_UNKNOWN); 1344 } 1345 1346 /* 1347 * Extract type-specific values of an property 1348 */ 1349 extern int di_prop_decode_common(void *prop_data, int len, 1350 int ddi_type, int prom); 1351 1352 int 1353 di_prop_ints(di_prop_t prop, int **prop_data) 1354 { 1355 if (DI_PROP(prop)->prop_len == 0) 1356 return (0); /* boolean property */ 1357 1358 if ((DI_PROP(prop)->prop_data == 0) || 1359 (DI_PROP(prop)->prop_data == (di_off_t)-1)) { 1360 errno = EFAULT; 1361 *prop_data = NULL; 1362 return (-1); 1363 } 1364 1365 *prop_data = (int *)((void *)((caddr_t)prop - DI_PROP(prop)->self 1366 + DI_PROP(prop)->prop_data)); 1367 1368 return (di_prop_decode_common((void *)prop_data, 1369 DI_PROP(prop)->prop_len, DI_PROP_TYPE_INT, 0)); 1370 } 1371 1372 int 1373 di_prop_int64(di_prop_t prop, int64_t **prop_data) 1374 { 1375 if (DI_PROP(prop)->prop_len == 0) 1376 return (0); /* boolean property */ 1377 1378 if ((DI_PROP(prop)->prop_data == 0) || 1379 (DI_PROP(prop)->prop_data == (di_off_t)-1)) { 1380 errno = EFAULT; 1381 *prop_data = NULL; 1382 return (-1); 1383 } 1384 1385 *prop_data = (int64_t *)((void *)((caddr_t)prop - DI_PROP(prop)->self 1386 + DI_PROP(prop)->prop_data)); 1387 1388 return (di_prop_decode_common((void *)prop_data, 1389 DI_PROP(prop)->prop_len, DI_PROP_TYPE_INT64, 0)); 1390 } 1391 1392 int 1393 di_prop_strings(di_prop_t prop, char **prop_data) 1394 { 1395 if (DI_PROP(prop)->prop_len == 0) 1396 return (0); /* boolean property */ 1397 1398 if ((DI_PROP(prop)->prop_data == 0) || 1399 (DI_PROP(prop)->prop_data == (di_off_t)-1)) { 1400 errno = EFAULT; 1401 *prop_data = NULL; 1402 return (-1); 1403 } 1404 1405 *prop_data = (char *)((caddr_t)prop - DI_PROP(prop)->self 1406 + DI_PROP(prop)->prop_data); 1407 1408 return (di_prop_decode_common((void *)prop_data, 1409 DI_PROP(prop)->prop_len, DI_PROP_TYPE_STRING, 0)); 1410 } 1411 1412 int 1413 di_prop_bytes(di_prop_t prop, uchar_t **prop_data) 1414 { 1415 if (DI_PROP(prop)->prop_len == 0) 1416 return (0); /* boolean property */ 1417 1418 if ((DI_PROP(prop)->prop_data == 0) || 1419 (DI_PROP(prop)->prop_data == (di_off_t)-1)) { 1420 errno = EFAULT; 1421 *prop_data = NULL; 1422 return (-1); 1423 } 1424 1425 *prop_data = (uchar_t *)((caddr_t)prop - DI_PROP(prop)->self 1426 + DI_PROP(prop)->prop_data); 1427 1428 return (di_prop_decode_common((void *)prop_data, 1429 DI_PROP(prop)->prop_len, DI_PROP_TYPE_BYTE, 0)); 1430 } 1431 1432 /* 1433 * returns 1 for match, 0 for no match 1434 */ 1435 static int 1436 match_prop(di_prop_t prop, dev_t match_dev, const char *name, int type) 1437 { 1438 int prop_type; 1439 1440 #ifdef DEBUG 1441 if (di_prop_name(prop) == NULL) { 1442 DPRINTF((DI_ERR, "libdevinfo: property has no name!\n")); 1443 return (0); 1444 } 1445 #endif /* DEBUG */ 1446 1447 if (strcmp(name, di_prop_name(prop)) != 0) 1448 return (0); 1449 1450 if ((match_dev != DDI_DEV_T_ANY) && (di_prop_devt(prop) != match_dev)) 1451 return (0); 1452 1453 /* 1454 * XXX prop_type is different from DDI_*. See PSARC 1997/127. 1455 */ 1456 prop_type = di_prop_type(prop); 1457 if ((prop_type != DI_PROP_TYPE_UNKNOWN) && (prop_type != type) && 1458 (prop_type != DI_PROP_TYPE_BOOLEAN)) 1459 return (0); 1460 1461 return (1); 1462 } 1463 1464 static di_prop_t 1465 di_prop_search(dev_t match_dev, di_node_t node, const char *name, 1466 int type) 1467 { 1468 di_prop_t prop = DI_PROP_NIL; 1469 1470 /* 1471 * The check on match_dev follows ddi_prop_lookup_common(). 1472 * Other checks are libdevinfo specific implementation. 1473 */ 1474 if ((node == DI_NODE_NIL) || (name == NULL) || (strlen(name) == 0) || 1475 (match_dev == DDI_DEV_T_NONE) || !DI_PROP_TYPE_VALID(type)) { 1476 errno = EINVAL; 1477 return (DI_PROP_NIL); 1478 } 1479 1480 while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) { 1481 DPRINTF((DI_TRACE1, "match prop name %s, devt 0x%lx, type %d\n", 1482 di_prop_name(prop), di_prop_devt(prop), 1483 di_prop_type(prop))); 1484 if (match_prop(prop, match_dev, name, type)) 1485 return (prop); 1486 } 1487 1488 return (DI_PROP_NIL); 1489 } 1490 1491 int 1492 di_prop_lookup_ints(dev_t dev, di_node_t node, const char *prop_name, 1493 int **prop_data) 1494 { 1495 di_prop_t prop; 1496 1497 if ((prop = di_prop_search(dev, node, prop_name, 1498 DI_PROP_TYPE_INT)) == DI_PROP_NIL) 1499 return (-1); 1500 1501 return (di_prop_ints(prop, (void *)prop_data)); 1502 } 1503 1504 int 1505 di_prop_lookup_int64(dev_t dev, di_node_t node, const char *prop_name, 1506 int64_t **prop_data) 1507 { 1508 di_prop_t prop; 1509 1510 if ((prop = di_prop_search(dev, node, prop_name, 1511 DI_PROP_TYPE_INT64)) == DI_PROP_NIL) 1512 return (-1); 1513 1514 return (di_prop_int64(prop, (void *)prop_data)); 1515 } 1516 1517 int 1518 di_prop_lookup_strings(dev_t dev, di_node_t node, const char *prop_name, 1519 char **prop_data) 1520 { 1521 di_prop_t prop; 1522 1523 if ((prop = di_prop_search(dev, node, prop_name, 1524 DI_PROP_TYPE_STRING)) == DI_PROP_NIL) 1525 return (-1); 1526 1527 return (di_prop_strings(prop, (void *)prop_data)); 1528 } 1529 1530 int 1531 di_prop_lookup_bytes(dev_t dev, di_node_t node, const char *prop_name, 1532 uchar_t **prop_data) 1533 { 1534 di_prop_t prop; 1535 1536 if ((prop = di_prop_search(dev, node, prop_name, 1537 DI_PROP_TYPE_BYTE)) == DI_PROP_NIL) 1538 return (-1); 1539 1540 return (di_prop_bytes(prop, (void *)prop_data)); 1541 } 1542 1543 /* 1544 * Consolidation private property access functions 1545 */ 1546 enum prop_type { 1547 PROP_TYPE_DRV, 1548 PROP_TYPE_SYS, 1549 PROP_TYPE_GLOB, 1550 PROP_TYPE_HW 1551 }; 1552 1553 static di_prop_t 1554 di_prop_next_common(di_node_t node, di_prop_t prop, int prop_type) 1555 { 1556 caddr_t pa; 1557 di_off_t prop_off = 0; 1558 1559 if (prop != DI_PROP_NIL) { 1560 if (DI_PROP(prop)->next) { 1561 return (DI_PROP((caddr_t)prop - 1562 DI_PROP(prop)->self + DI_PROP(prop)->next)); 1563 } else { 1564 return (DI_PROP_NIL); 1565 } 1566 } 1567 1568 1569 /* 1570 * prop is NIL, caller asks for first property 1571 */ 1572 pa = (caddr_t)node - DI_NODE(node)->self; 1573 switch (prop_type) { 1574 case PROP_TYPE_DRV: 1575 prop_off = DI_NODE(node)->drv_prop; 1576 break; 1577 case PROP_TYPE_SYS: 1578 prop_off = DI_NODE(node)->sys_prop; 1579 break; 1580 case PROP_TYPE_HW: 1581 prop_off = DI_NODE(node)->hw_prop; 1582 break; 1583 case PROP_TYPE_GLOB: 1584 prop_off = DI_NODE(node)->glob_prop; 1585 if (prop_off == -1) { 1586 /* no global property */ 1587 prop_off = 0; 1588 } else if ((prop_off == 0) && (DI_NODE(node)->drv_major >= 0)) { 1589 /* refer to devnames array */ 1590 struct di_devnm *devnm = DI_DEVNM(pa + 1591 DI_ALL(pa)->devnames + (DI_NODE(node)->drv_major * 1592 sizeof (struct di_devnm))); 1593 prop_off = devnm->global_prop; 1594 } 1595 break; 1596 } 1597 1598 if (prop_off) { 1599 return (DI_PROP(pa + prop_off)); 1600 } 1601 1602 /* 1603 * no prop found. Check the reason for not found 1604 */ 1605 if (DINFOPROP & DI_ALL(pa)->command) 1606 errno = ENXIO; 1607 else 1608 errno = ENOTSUP; 1609 1610 return (DI_PROP_NIL); 1611 } 1612 1613 di_prop_t 1614 di_prop_drv_next(di_node_t node, di_prop_t prop) 1615 { 1616 return (di_prop_next_common(node, prop, PROP_TYPE_DRV)); 1617 } 1618 1619 di_prop_t 1620 di_prop_sys_next(di_node_t node, di_prop_t prop) 1621 { 1622 return (di_prop_next_common(node, prop, PROP_TYPE_SYS)); 1623 } 1624 1625 di_prop_t 1626 di_prop_global_next(di_node_t node, di_prop_t prop) 1627 { 1628 return (di_prop_next_common(node, prop, PROP_TYPE_GLOB)); 1629 } 1630 1631 di_prop_t 1632 di_prop_hw_next(di_node_t node, di_prop_t prop) 1633 { 1634 return (di_prop_next_common(node, prop, PROP_TYPE_HW)); 1635 } 1636 1637 int 1638 di_prop_rawdata(di_prop_t prop, uchar_t **prop_data) 1639 { 1640 #ifdef DEBUG 1641 if (prop == DI_PROP_NIL) { 1642 errno = EINVAL; 1643 return (-1); 1644 } 1645 #endif /* DEBUG */ 1646 1647 if (DI_PROP(prop)->prop_len == 0) { 1648 *prop_data = NULL; 1649 return (0); 1650 } 1651 1652 if ((DI_PROP(prop)->prop_data == 0) || 1653 (DI_PROP(prop)->prop_data == (di_off_t)-1)) { 1654 errno = EFAULT; 1655 *prop_data = NULL; 1656 return (-1); 1657 } 1658 1659 /* 1660 * No memory allocation. 1661 */ 1662 *prop_data = (uchar_t *)((caddr_t)prop - DI_PROP(prop)->self + 1663 DI_PROP(prop)->prop_data); 1664 1665 return (DI_PROP(prop)->prop_len); 1666 } 1667 1668 /* 1669 * Consolidation private interfaces for accessing I/O multipathing data 1670 */ 1671 di_path_t 1672 di_path_next_client(di_node_t node, di_path_t path) 1673 { 1674 caddr_t pa; 1675 1676 /* 1677 * path is not NIL 1678 */ 1679 if (path != DI_PATH_NIL) { 1680 if (DI_PATH(path)->path_p_link != 0) 1681 return (DI_PATH((void *)((caddr_t)path - 1682 DI_PATH(path)->self + DI_PATH(path)->path_p_link))); 1683 else { 1684 errno = ENXIO; 1685 return (DI_PATH_NIL); 1686 } 1687 } 1688 1689 /* 1690 * Path is NIL; the caller is asking for the first path info node 1691 */ 1692 if (DI_NODE(node)->multipath_phci != 0) { 1693 DPRINTF((DI_INFO, "phci: returning %p\n", ((caddr_t)node - 1694 DI_NODE(node)->self + DI_NODE(node)->multipath_phci))); 1695 return (DI_PATH((caddr_t)node - DI_NODE(node)->self + 1696 DI_NODE(node)->multipath_phci)); 1697 } 1698 1699 /* 1700 * No pathing data; check if the snapshot includes path data in order 1701 * to set errno properly. 1702 */ 1703 pa = (caddr_t)node - DI_NODE(node)->self; 1704 if (DINFOPATH & (DI_ALL(pa)->command)) 1705 errno = ENXIO; 1706 else 1707 errno = ENOTSUP; 1708 1709 return (DI_PATH_NIL); 1710 } 1711 1712 di_path_t 1713 di_path_next_phci(di_node_t node, di_path_t path) 1714 { 1715 caddr_t pa; 1716 1717 /* 1718 * path is not NIL 1719 */ 1720 if (path != DI_PATH_NIL) { 1721 if (DI_PATH(path)->path_c_link != 0) 1722 return (DI_PATH((caddr_t)path - DI_PATH(path)->self 1723 + DI_PATH(path)->path_c_link)); 1724 else { 1725 errno = ENXIO; 1726 return (DI_PATH_NIL); 1727 } 1728 } 1729 1730 /* 1731 * Path is NIL; the caller is asking for the first path info node 1732 */ 1733 if (DI_NODE(node)->multipath_client != 0) { 1734 DPRINTF((DI_INFO, "client: returning %p\n", ((caddr_t)node - 1735 DI_NODE(node)->self + DI_NODE(node)->multipath_client))); 1736 return (DI_PATH((caddr_t)node - DI_NODE(node)->self + 1737 DI_NODE(node)->multipath_client)); 1738 } 1739 1740 /* 1741 * No pathing data; check if the snapshot includes path data in order 1742 * to set errno properly. 1743 */ 1744 pa = (caddr_t)node - DI_NODE(node)->self; 1745 if (DINFOPATH & (DI_ALL(pa)->command)) 1746 errno = ENXIO; 1747 else 1748 errno = ENOTSUP; 1749 1750 return (DI_PATH_NIL); 1751 } 1752 1753 /* 1754 * XXX Obsolete wrapper to be removed. Won't work under multilevel. 1755 */ 1756 di_path_t 1757 di_path_next(di_node_t node, di_path_t path) 1758 { 1759 if (node == DI_NODE_NIL) { 1760 errno = EINVAL; 1761 return (DI_PATH_NIL); 1762 } 1763 1764 if (DI_NODE(node)->multipath_client) { 1765 return (di_path_next_phci(node, path)); 1766 } else if (DI_NODE(node)->multipath_phci) { 1767 return (di_path_next_client(node, path)); 1768 } else { 1769 /* 1770 * The node had multipathing data but didn't appear to be a 1771 * phci *or* a client; probably a programmer error. 1772 */ 1773 errno = EINVAL; 1774 return (DI_PATH_NIL); 1775 } 1776 } 1777 1778 di_path_state_t 1779 di_path_state(di_path_t path) 1780 { 1781 return ((di_path_state_t)DI_PATH(path)->path_state); 1782 } 1783 1784 char * 1785 di_path_addr(di_path_t path, char *buf) 1786 { 1787 caddr_t pa; /* starting address of map */ 1788 1789 pa = (caddr_t)path - DI_PATH(path)->self; 1790 1791 (void) strncpy(buf, (char *)(pa + DI_PATH(path)->path_addr), 1792 MAXPATHLEN); 1793 return (buf); 1794 } 1795 1796 di_node_t 1797 di_path_client_node(di_path_t path) 1798 { 1799 caddr_t pa; /* starting address of map */ 1800 1801 if (path == DI_PATH_NIL) { 1802 errno = EINVAL; 1803 return (DI_PATH_NIL); 1804 } 1805 1806 DPRINTF((DI_TRACE, "Get client node for path %p\n", path)); 1807 1808 pa = (caddr_t)path - DI_PATH(path)->self; 1809 1810 if (DI_PATH(path)->path_client) { 1811 return (DI_NODE(pa + DI_PATH(path)->path_client)); 1812 } 1813 1814 /* 1815 * Deal with error condition: 1816 * If parent doesn't exist and node is not the root, 1817 * set errno to ENOTSUP. Otherwise, set errno to ENXIO. 1818 */ 1819 if ((DI_PATH(path)->path_snap_state & DI_PATH_SNAP_NOCLIENT) == 0) 1820 errno = ENOTSUP; 1821 else 1822 errno = ENXIO; 1823 1824 return (DI_NODE_NIL); 1825 } 1826 1827 di_node_t 1828 di_path_phci_node(di_path_t path) 1829 { 1830 caddr_t pa; /* starting address of map */ 1831 1832 if (path == DI_PATH_NIL) { 1833 errno = EINVAL; 1834 return (DI_PATH_NIL); 1835 } 1836 1837 DPRINTF((DI_TRACE, "Get phci node for path %p\n", path)); 1838 1839 pa = (caddr_t)path - DI_PATH(path)->self; 1840 1841 if (DI_PATH(path)->path_phci) { 1842 return (DI_NODE(pa + DI_PATH(path)->path_phci)); 1843 } 1844 1845 /* 1846 * Deal with error condition: 1847 * If parent doesn't exist and node is not the root, 1848 * set errno to ENOTSUP. Otherwise, set errno to ENXIO. 1849 */ 1850 if ((DI_PATH(path)->path_snap_state & DI_PATH_SNAP_NOPHCI) == 0) 1851 errno = ENOTSUP; 1852 else 1853 errno = ENXIO; 1854 1855 return (DI_NODE_NIL); 1856 } 1857 1858 di_path_prop_t 1859 di_path_prop_next(di_path_t path, di_path_prop_t prop) 1860 { 1861 caddr_t pa; 1862 1863 if (path == DI_PATH_NIL) { 1864 errno = EINVAL; 1865 return (DI_PROP_NIL); 1866 } 1867 1868 /* 1869 * prop is not NIL 1870 */ 1871 if (prop != DI_PROP_NIL) { 1872 if (DI_PROP(prop)->next != 0) 1873 return (DI_PATHPROP((caddr_t)prop - 1874 DI_PROP(prop)->self + DI_PROP(prop)->next)); 1875 else { 1876 errno = ENXIO; 1877 return (DI_PROP_NIL); 1878 } 1879 } 1880 1881 /* 1882 * prop is NIL-->caller asks for first property 1883 */ 1884 pa = (caddr_t)path - DI_PATH(path)->self; 1885 if (DI_PATH(path)->path_prop != 0) { 1886 return (DI_PATHPROP(pa + DI_PATH(path)->path_prop)); 1887 } 1888 1889 /* 1890 * no property data-->check if snapshot includes props 1891 * in order to set the correct errno 1892 */ 1893 if (DINFOPROP & (DI_ALL(pa)->command)) 1894 errno = ENXIO; 1895 else 1896 errno = ENOTSUP; 1897 1898 return (DI_PROP_NIL); 1899 } 1900 1901 char * 1902 di_path_prop_name(di_path_prop_t prop) 1903 { 1904 caddr_t pa; /* starting address of map */ 1905 pa = (caddr_t)prop - DI_PATHPROP(prop)->self; 1906 return ((char *)(pa + DI_PATHPROP(prop)->prop_name)); 1907 } 1908 1909 int 1910 di_path_prop_len(di_path_prop_t prop) 1911 { 1912 return (DI_PATHPROP(prop)->prop_len); 1913 } 1914 1915 int 1916 di_path_prop_type(di_path_prop_t prop) 1917 { 1918 switch (DI_PATHPROP(prop)->prop_type) { 1919 case DDI_PROP_TYPE_INT: 1920 return (DI_PROP_TYPE_INT); 1921 case DDI_PROP_TYPE_INT64: 1922 return (DI_PROP_TYPE_INT64); 1923 case DDI_PROP_TYPE_BYTE: 1924 return (DI_PROP_TYPE_BYTE); 1925 case DDI_PROP_TYPE_STRING: 1926 return (DI_PROP_TYPE_STRING); 1927 } 1928 return (DI_PROP_TYPE_UNKNOWN); 1929 } 1930 1931 int 1932 di_path_prop_bytes(di_path_prop_t prop, uchar_t **prop_data) 1933 { 1934 if ((DI_PATHPROP(prop)->prop_data == 0) || 1935 (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) { 1936 errno = EFAULT; 1937 *prop_data = NULL; 1938 return (-1); 1939 } 1940 1941 *prop_data = (uchar_t *)((caddr_t)prop - DI_PATHPROP(prop)->self 1942 + DI_PATHPROP(prop)->prop_data); 1943 1944 return (di_prop_decode_common((void *)prop_data, 1945 DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_BYTE, 0)); 1946 } 1947 1948 int 1949 di_path_prop_ints(di_path_prop_t prop, int **prop_data) 1950 { 1951 if (DI_PATHPROP(prop)->prop_len == 0) 1952 return (0); 1953 1954 if ((DI_PATHPROP(prop)->prop_data == 0) || 1955 (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) { 1956 errno = EFAULT; 1957 *prop_data = NULL; 1958 return (-1); 1959 } 1960 1961 *prop_data = (int *)((void *)((caddr_t)prop - DI_PATHPROP(prop)->self 1962 + DI_PATHPROP(prop)->prop_data)); 1963 1964 return (di_prop_decode_common((void *)prop_data, 1965 DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_INT, 0)); 1966 } 1967 1968 int 1969 di_path_prop_int64s(di_path_prop_t prop, int64_t **prop_data) 1970 { 1971 if (DI_PATHPROP(prop)->prop_len == 0) 1972 return (0); 1973 1974 if ((DI_PATHPROP(prop)->prop_data == 0) || 1975 (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) { 1976 errno = EFAULT; 1977 *prop_data = NULL; 1978 return (-1); 1979 } 1980 1981 *prop_data = (int64_t *)((void *)((caddr_t)prop - 1982 DI_PATHPROP(prop)->self + DI_PATHPROP(prop)->prop_data)); 1983 1984 return (di_prop_decode_common((void *)prop_data, 1985 DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_INT64, 0)); 1986 } 1987 1988 int 1989 di_path_prop_strings(di_path_prop_t prop, char **prop_data) 1990 { 1991 if (DI_PATHPROP(prop)->prop_len == 0) 1992 return (0); 1993 1994 if ((DI_PATHPROP(prop)->prop_data == 0) || 1995 (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) { 1996 errno = EFAULT; 1997 *prop_data = NULL; 1998 return (-1); 1999 } 2000 2001 *prop_data = (char *)((caddr_t)prop - DI_PATHPROP(prop)->self 2002 + DI_PATHPROP(prop)->prop_data); 2003 2004 return (di_prop_decode_common((void *)prop_data, 2005 DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_STRING, 0)); 2006 } 2007 2008 static di_path_prop_t 2009 di_path_prop_search(di_path_t path, const char *name, int type) 2010 { 2011 di_path_prop_t prop = DI_PROP_NIL; 2012 2013 /* 2014 * Sanity check arguments 2015 */ 2016 if ((path == DI_PATH_NIL) || (name == NULL) || (strlen(name) == 0) || 2017 !DI_PROP_TYPE_VALID(type)) { 2018 errno = EINVAL; 2019 return (DI_PROP_NIL); 2020 } 2021 2022 while ((prop = di_path_prop_next(path, prop)) != DI_PROP_NIL) { 2023 int prop_type = di_path_prop_type(prop); 2024 2025 DPRINTF((DI_TRACE1, "match path prop name %s, type %d\n", 2026 di_path_prop_name(prop), prop_type)); 2027 2028 if (strcmp(name, di_path_prop_name(prop)) != 0) 2029 continue; 2030 2031 if ((prop_type != DI_PROP_TYPE_UNKNOWN) && (prop_type != type)) 2032 continue; 2033 2034 return (prop); 2035 } 2036 2037 return (DI_PROP_NIL); 2038 } 2039 2040 int 2041 di_path_prop_lookup_bytes(di_path_t path, const char *prop_name, 2042 uchar_t **prop_data) 2043 { 2044 di_path_prop_t prop; 2045 2046 if ((prop = di_path_prop_search(path, prop_name, 2047 DI_PROP_TYPE_BYTE)) == DI_PROP_NIL) 2048 return (-1); 2049 2050 return (di_path_prop_bytes(prop, prop_data)); 2051 } 2052 2053 int 2054 di_path_prop_lookup_ints(di_path_t path, const char *prop_name, 2055 int **prop_data) 2056 { 2057 di_path_prop_t prop; 2058 2059 if ((prop = di_path_prop_search(path, prop_name, 2060 DI_PROP_TYPE_INT)) == DI_PROP_NIL) 2061 return (-1); 2062 2063 return (di_path_prop_ints(prop, prop_data)); 2064 } 2065 2066 int 2067 di_path_prop_lookup_int64s(di_path_t path, const char *prop_name, 2068 int64_t **prop_data) 2069 { 2070 di_path_prop_t prop; 2071 2072 if ((prop = di_path_prop_search(path, prop_name, 2073 DI_PROP_TYPE_INT64)) == DI_PROP_NIL) 2074 return (-1); 2075 2076 return (di_path_prop_int64s(prop, prop_data)); 2077 } 2078 2079 int di_path_prop_lookup_strings(di_path_t path, const char *prop_name, 2080 char **prop_data) 2081 { 2082 di_path_prop_t prop; 2083 2084 if ((prop = di_path_prop_search(path, prop_name, 2085 DI_PROP_TYPE_STRING)) == DI_PROP_NIL) 2086 return (-1); 2087 2088 return (di_path_prop_strings(prop, prop_data)); 2089 } 2090 2091 2092 /* 2093 * Consolidation private interfaces for private data 2094 */ 2095 void * 2096 di_parent_private_data(di_node_t node) 2097 { 2098 caddr_t pa; 2099 2100 if (DI_NODE(node)->parent_data == 0) { 2101 errno = ENXIO; 2102 return (NULL); 2103 } 2104 2105 if (DI_NODE(node)->parent_data == (di_off_t)-1) { 2106 /* 2107 * Private data requested, but not obtained due to a memory 2108 * error (e.g. wrong format specified) 2109 */ 2110 errno = EFAULT; 2111 return (NULL); 2112 } 2113 2114 pa = (caddr_t)node - DI_NODE(node)->self; 2115 if (DI_NODE(node)->parent_data) 2116 return (pa + DI_NODE(node)->parent_data); 2117 2118 if (DI_ALL(pa)->command & DINFOPRIVDATA) 2119 errno = ENXIO; 2120 else 2121 errno = ENOTSUP; 2122 2123 return (NULL); 2124 } 2125 2126 void * 2127 di_driver_private_data(di_node_t node) 2128 { 2129 caddr_t pa; 2130 2131 if (DI_NODE(node)->driver_data == 0) { 2132 errno = ENXIO; 2133 return (NULL); 2134 } 2135 2136 if (DI_NODE(node)->driver_data == (di_off_t)-1) { 2137 /* 2138 * Private data requested, but not obtained due to a memory 2139 * error (e.g. wrong format specified) 2140 */ 2141 errno = EFAULT; 2142 return (NULL); 2143 } 2144 2145 pa = (caddr_t)node - DI_NODE(node)->self; 2146 if (DI_NODE(node)->driver_data) 2147 return (pa + DI_NODE(node)->driver_data); 2148 2149 if (DI_ALL(pa)->command & DINFOPRIVDATA) 2150 errno = ENXIO; 2151 else 2152 errno = ENOTSUP; 2153 2154 return (NULL); 2155 } 2156 2157 /* 2158 * PROM property access 2159 */ 2160 2161 /* 2162 * openprom driver stuff: 2163 * The maximum property length depends on the buffer size. We use 2164 * OPROMMAXPARAM defined in <sys/openpromio.h> 2165 * 2166 * MAXNAMESZ is max property name. obpdefs.h defines it as 32 based on 1275 2167 * MAXVALSZ is maximum value size, which is whatever space left in buf 2168 */ 2169 2170 #define OBP_MAXBUF OPROMMAXPARAM - sizeof (int) 2171 #define OBP_MAXPROPLEN OBP_MAXBUF - OBP_MAXPROPNAME; 2172 2173 struct di_prom_prop { 2174 char *name; 2175 int len; 2176 uchar_t *data; 2177 struct di_prom_prop *next; /* form a linked list */ 2178 }; 2179 2180 struct di_prom_handle { /* handle to prom */ 2181 mutex_t lock; /* synchronize access to openprom fd */ 2182 int fd; /* /dev/openprom file descriptor */ 2183 struct di_prom_prop *list; /* linked list of prop */ 2184 union { 2185 char buf[OPROMMAXPARAM]; 2186 struct openpromio opp; 2187 } oppbuf; 2188 }; 2189 2190 di_prom_handle_t 2191 di_prom_init() 2192 { 2193 struct di_prom_handle *p; 2194 2195 if ((p = malloc(sizeof (struct di_prom_handle))) == NULL) 2196 return (DI_PROM_HANDLE_NIL); 2197 2198 DPRINTF((DI_INFO, "di_prom_init: get prom handle 0x%p\n", p)); 2199 2200 (void) mutex_init(&p->lock, USYNC_THREAD, NULL); 2201 if ((p->fd = open("/dev/openprom", O_RDONLY)) < 0) { 2202 free(p); 2203 return (DI_PROM_HANDLE_NIL); 2204 } 2205 p->list = NULL; 2206 2207 return ((di_prom_handle_t)p); 2208 } 2209 2210 static void 2211 di_prom_prop_free(struct di_prom_prop *list) 2212 { 2213 struct di_prom_prop *tmp = list; 2214 2215 while (tmp != NULL) { 2216 list = tmp->next; 2217 if (tmp->name != NULL) { 2218 free(tmp->name); 2219 } 2220 if (tmp->data != NULL) { 2221 free(tmp->data); 2222 } 2223 free(tmp); 2224 tmp = list; 2225 } 2226 } 2227 2228 void 2229 di_prom_fini(di_prom_handle_t ph) 2230 { 2231 struct di_prom_handle *p = (struct di_prom_handle *)ph; 2232 2233 DPRINTF((DI_INFO, "di_prom_fini: free prom handle 0x%p\n", p)); 2234 2235 (void) close(p->fd); 2236 (void) mutex_destroy(&p->lock); 2237 di_prom_prop_free(p->list); 2238 2239 free(p); 2240 } 2241 2242 /* 2243 * Internal library interface for locating the property 2244 * XXX: ph->lock must be held for the duration of call. 2245 */ 2246 static di_prom_prop_t 2247 di_prom_prop_found(di_prom_handle_t ph, int nodeid, 2248 di_prom_prop_t prom_prop) 2249 { 2250 struct di_prom_handle *p = (struct di_prom_handle *)ph; 2251 struct openpromio *opp = &p->oppbuf.opp; 2252 int *ip = (int *)((void *)opp->oprom_array); 2253 struct di_prom_prop *prop = (struct di_prom_prop *)prom_prop; 2254 2255 DPRINTF((DI_TRACE1, "Looking for nodeid 0x%x\n", nodeid)); 2256 2257 /* 2258 * Set "current" nodeid in the openprom driver 2259 */ 2260 opp->oprom_size = sizeof (int); 2261 *ip = nodeid; 2262 if (ioctl(p->fd, OPROMSETNODEID, opp) < 0) { 2263 DPRINTF((DI_ERR, "*** Nodeid not found 0x%x\n", nodeid)); 2264 return (DI_PROM_PROP_NIL); 2265 } 2266 2267 DPRINTF((DI_TRACE, "Found nodeid 0x%x\n", nodeid)); 2268 2269 bzero(opp, OBP_MAXBUF); 2270 opp->oprom_size = OBP_MAXPROPNAME; 2271 if (prom_prop != DI_PROM_PROP_NIL) 2272 (void) strcpy(opp->oprom_array, prop->name); 2273 2274 if ((ioctl(p->fd, OPROMNXTPROP, opp) < 0) || (opp->oprom_size == 0)) 2275 return (DI_PROM_PROP_NIL); 2276 2277 /* 2278 * Prom property found. Allocate struct for storing prop 2279 * (reuse variable prop) 2280 */ 2281 if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL) 2282 return (DI_PROM_PROP_NIL); 2283 2284 /* 2285 * Get a copy of property name 2286 */ 2287 if ((prop->name = strdup(opp->oprom_array)) == NULL) { 2288 free(prop); 2289 return (DI_PROM_PROP_NIL); 2290 } 2291 2292 /* 2293 * get property value and length 2294 */ 2295 opp->oprom_size = OBP_MAXPROPLEN; 2296 2297 if ((ioctl(p->fd, OPROMGETPROP, opp) < 0) || 2298 (opp->oprom_size == (uint_t)-1)) { 2299 free(prop->name); 2300 free(prop); 2301 return (DI_PROM_PROP_NIL); 2302 } 2303 2304 /* 2305 * make a copy of the property value 2306 */ 2307 prop->len = opp->oprom_size; 2308 2309 if (prop->len == 0) 2310 prop->data = NULL; 2311 else if ((prop->data = malloc(prop->len)) == NULL) { 2312 free(prop->name); 2313 free(prop); 2314 return (DI_PROM_PROP_NIL); 2315 } 2316 2317 bcopy(opp->oprom_array, prop->data, prop->len); 2318 2319 /* 2320 * Prepend prop to list in prom handle 2321 */ 2322 prop->next = p->list; 2323 p->list = prop; 2324 2325 return ((di_prom_prop_t)prop); 2326 } 2327 2328 di_prom_prop_t 2329 di_prom_prop_next(di_prom_handle_t ph, di_node_t node, di_prom_prop_t prom_prop) 2330 { 2331 struct di_prom_handle *p = (struct di_prom_handle *)ph; 2332 2333 DPRINTF((DI_TRACE1, "Search next prop for node 0x%p with ph 0x%p\n", 2334 node, p)); 2335 2336 /* 2337 * paranoid check 2338 */ 2339 if ((ph == DI_PROM_HANDLE_NIL) || (node == DI_NODE_NIL)) { 2340 errno = EINVAL; 2341 return (DI_PROM_PROP_NIL); 2342 } 2343 2344 if (di_nodeid(node) != DI_PROM_NODEID) { 2345 errno = ENXIO; 2346 return (DI_PROM_PROP_NIL); 2347 } 2348 2349 /* 2350 * synchronize access to prom file descriptor 2351 */ 2352 (void) mutex_lock(&p->lock); 2353 2354 /* 2355 * look for next property 2356 */ 2357 prom_prop = di_prom_prop_found(ph, DI_NODE(node)->nodeid, prom_prop); 2358 2359 (void) mutex_unlock(&p->lock); 2360 2361 return (prom_prop); 2362 } 2363 2364 char * 2365 di_prom_prop_name(di_prom_prop_t prom_prop) 2366 { 2367 /* 2368 * paranoid check 2369 */ 2370 if (prom_prop == DI_PROM_PROP_NIL) { 2371 errno = EINVAL; 2372 return (NULL); 2373 } 2374 2375 return (((struct di_prom_prop *)prom_prop)->name); 2376 } 2377 2378 int 2379 di_prom_prop_data(di_prom_prop_t prom_prop, uchar_t **prom_prop_data) 2380 { 2381 /* 2382 * paranoid check 2383 */ 2384 if (prom_prop == DI_PROM_PROP_NIL) { 2385 errno = EINVAL; 2386 return (NULL); 2387 } 2388 2389 *prom_prop_data = ((struct di_prom_prop *)prom_prop)->data; 2390 2391 return (((struct di_prom_prop *)prom_prop)->len); 2392 } 2393 2394 /* 2395 * Internal library interface for locating the property 2396 * Returns length if found, -1 if prop doesn't exist. 2397 */ 2398 static struct di_prom_prop * 2399 di_prom_prop_lookup_common(di_prom_handle_t ph, di_node_t node, 2400 const char *prom_prop_name) 2401 { 2402 struct openpromio *opp; 2403 struct di_prom_prop *prop; 2404 struct di_prom_handle *p = (struct di_prom_handle *)ph; 2405 2406 /* 2407 * paranoid check 2408 */ 2409 if ((ph == DI_PROM_HANDLE_NIL) || (node == DI_NODE_NIL)) { 2410 errno = EINVAL; 2411 return (NULL); 2412 } 2413 2414 if (di_nodeid(node) != DI_PROM_NODEID) { 2415 errno = ENXIO; 2416 return (NULL); 2417 } 2418 2419 opp = &p->oppbuf.opp; 2420 2421 (void) mutex_lock(&p->lock); 2422 2423 opp->oprom_size = sizeof (int); 2424 opp->oprom_node = DI_NODE(node)->nodeid; 2425 if (ioctl(p->fd, OPROMSETNODEID, opp) < 0) { 2426 errno = ENXIO; 2427 DPRINTF((DI_ERR, "*** Nodeid not found 0x%x\n", 2428 DI_NODE(node)->nodeid)); 2429 (void) mutex_unlock(&p->lock); 2430 return (NULL); 2431 } 2432 2433 /* 2434 * get property length 2435 */ 2436 bzero(opp, OBP_MAXBUF); 2437 opp->oprom_size = OBP_MAXPROPLEN; 2438 (void) strcpy(opp->oprom_array, prom_prop_name); 2439 2440 if ((ioctl(p->fd, OPROMGETPROPLEN, opp) < 0) || 2441 (opp->oprom_len == -1)) { 2442 /* no such property */ 2443 (void) mutex_unlock(&p->lock); 2444 return (NULL); 2445 } 2446 2447 /* 2448 * Prom property found. Allocate struct for storing prop 2449 */ 2450 if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL) { 2451 (void) mutex_unlock(&p->lock); 2452 return (NULL); 2453 } 2454 prop->name = NULL; /* we don't need the name */ 2455 prop->len = opp->oprom_len; 2456 2457 if (prop->len == 0) { /* boolean property */ 2458 prop->data = NULL; 2459 prop->next = p->list; 2460 p->list = prop; 2461 (void) mutex_unlock(&p->lock); 2462 return (prop); 2463 } 2464 2465 /* 2466 * retrieve the property value 2467 */ 2468 bzero(opp, OBP_MAXBUF); 2469 opp->oprom_size = OBP_MAXPROPLEN; 2470 (void) strcpy(opp->oprom_array, prom_prop_name); 2471 2472 if ((ioctl(p->fd, OPROMGETPROP, opp) < 0) || 2473 (opp->oprom_size == (uint_t)-1)) { 2474 /* error retrieving property value */ 2475 (void) mutex_unlock(&p->lock); 2476 free(prop); 2477 return (NULL); 2478 } 2479 2480 /* 2481 * make a copy of the property value, stick in ph->list 2482 */ 2483 if ((prop->data = malloc(prop->len)) == NULL) { 2484 (void) mutex_unlock(&p->lock); 2485 free(prop); 2486 return (NULL); 2487 } 2488 2489 bcopy(opp->oprom_array, prop->data, prop->len); 2490 2491 prop->next = p->list; 2492 p->list = prop; 2493 (void) mutex_unlock(&p->lock); 2494 2495 return (prop); 2496 } 2497 2498 int 2499 di_prom_prop_lookup_ints(di_prom_handle_t ph, di_node_t node, 2500 const char *prom_prop_name, int **prom_prop_data) 2501 { 2502 int len; 2503 struct di_prom_prop *prop; 2504 2505 prop = di_prom_prop_lookup_common(ph, node, prom_prop_name); 2506 2507 if (prop == NULL) { 2508 *prom_prop_data = NULL; 2509 return (-1); 2510 } 2511 2512 if (prop->len == 0) { /* boolean property */ 2513 *prom_prop_data = NULL; 2514 return (0); 2515 } 2516 2517 len = di_prop_decode_common((void *)&prop->data, prop->len, 2518 DI_PROP_TYPE_INT, 1); 2519 *prom_prop_data = (int *)((void *)prop->data); 2520 2521 return (len); 2522 } 2523 2524 int 2525 di_prom_prop_lookup_strings(di_prom_handle_t ph, di_node_t node, 2526 const char *prom_prop_name, char **prom_prop_data) 2527 { 2528 int len; 2529 struct di_prom_prop *prop; 2530 2531 prop = di_prom_prop_lookup_common(ph, node, prom_prop_name); 2532 2533 if (prop == NULL) { 2534 *prom_prop_data = NULL; 2535 return (-1); 2536 } 2537 2538 if (prop->len == 0) { /* boolean property */ 2539 *prom_prop_data = NULL; 2540 return (0); 2541 } 2542 2543 /* 2544 * Fix an openprom bug (OBP string not NULL terminated). 2545 * XXX This should really be fixed in promif. 2546 */ 2547 if (((char *)prop->data)[prop->len - 1] != '\0') { 2548 uchar_t *tmp; 2549 prop->len++; 2550 if ((tmp = realloc(prop->data, prop->len)) == NULL) 2551 return (-1); 2552 2553 prop->data = tmp; 2554 ((char *)prop->data)[prop->len - 1] = '\0'; 2555 DPRINTF((DI_INFO, "OBP string not NULL terminated: " 2556 "node=%s, prop=%s, val=%s\n", 2557 di_node_name(node), prom_prop_name, prop->data)); 2558 } 2559 2560 len = di_prop_decode_common((void *)&prop->data, prop->len, 2561 DI_PROP_TYPE_STRING, 1); 2562 *prom_prop_data = (char *)prop->data; 2563 2564 return (len); 2565 } 2566 2567 int 2568 di_prom_prop_lookup_bytes(di_prom_handle_t ph, di_node_t node, 2569 const char *prom_prop_name, uchar_t **prom_prop_data) 2570 { 2571 int len; 2572 struct di_prom_prop *prop; 2573 2574 prop = di_prom_prop_lookup_common(ph, node, prom_prop_name); 2575 2576 if (prop == NULL) { 2577 *prom_prop_data = NULL; 2578 return (-1); 2579 } 2580 2581 if (prop->len == 0) { /* boolean property */ 2582 *prom_prop_data = NULL; 2583 return (0); 2584 } 2585 2586 len = di_prop_decode_common((void *)&prop->data, prop->len, 2587 DI_PROP_TYPE_BYTE, 1); 2588 *prom_prop_data = prop->data; 2589 2590 return (len); 2591 } 2592 2593 di_lnode_t 2594 di_link_to_lnode(di_link_t link, uint_t endpoint) 2595 { 2596 struct di_all *di_all; 2597 2598 if ((link == DI_LINK_NIL) || 2599 ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) { 2600 errno = EINVAL; 2601 return (DI_LNODE_NIL); 2602 } 2603 2604 di_all = DI_ALL((caddr_t)link - DI_LINK(link)->self); 2605 2606 if (endpoint == DI_LINK_SRC) { 2607 return (DI_LNODE((caddr_t)di_all + DI_LINK(link)->src_lnode)); 2608 } else { 2609 return (DI_LNODE((caddr_t)di_all + DI_LINK(link)->tgt_lnode)); 2610 } 2611 /* NOTREACHED */ 2612 } 2613 2614 char * 2615 di_lnode_name(di_lnode_t lnode) 2616 { 2617 return (di_driver_name(di_lnode_devinfo(lnode))); 2618 } 2619 2620 di_node_t 2621 di_lnode_devinfo(di_lnode_t lnode) 2622 { 2623 struct di_all *di_all; 2624 2625 di_all = DI_ALL((caddr_t)lnode - DI_LNODE(lnode)->self); 2626 return (DI_NODE((caddr_t)di_all + DI_LNODE(lnode)->node)); 2627 } 2628 2629 int 2630 di_lnode_devt(di_lnode_t lnode, dev_t *devt) 2631 { 2632 if ((lnode == DI_LNODE_NIL) || (devt == NULL)) { 2633 errno = EINVAL; 2634 return (-1); 2635 } 2636 if ((DI_LNODE(lnode)->dev_major == (major_t)-1) && 2637 (DI_LNODE(lnode)->dev_minor == (minor_t)-1)) 2638 return (-1); 2639 2640 *devt = makedev(DI_LNODE(lnode)->dev_major, DI_LNODE(lnode)->dev_minor); 2641 return (0); 2642 } 2643 2644 int 2645 di_link_spectype(di_link_t link) 2646 { 2647 return (DI_LINK(link)->spec_type); 2648 } 2649 2650 void 2651 di_minor_private_set(di_minor_t minor, void *data) 2652 { 2653 DI_MINOR(minor)->user_private_data = (uintptr_t)data; 2654 } 2655 2656 void * 2657 di_minor_private_get(di_minor_t minor) 2658 { 2659 return ((void *)(uintptr_t)DI_MINOR(minor)->user_private_data); 2660 } 2661 2662 void 2663 di_node_private_set(di_node_t node, void *data) 2664 { 2665 DI_NODE(node)->user_private_data = (uintptr_t)data; 2666 } 2667 2668 void * 2669 di_node_private_get(di_node_t node) 2670 { 2671 return ((void *)(uintptr_t)DI_NODE(node)->user_private_data); 2672 } 2673 2674 void 2675 di_lnode_private_set(di_lnode_t lnode, void *data) 2676 { 2677 DI_LNODE(lnode)->user_private_data = (uintptr_t)data; 2678 } 2679 2680 void * 2681 di_lnode_private_get(di_lnode_t lnode) 2682 { 2683 return ((void *)(uintptr_t)DI_LNODE(lnode)->user_private_data); 2684 } 2685 2686 void 2687 di_link_private_set(di_link_t link, void *data) 2688 { 2689 DI_LINK(link)->user_private_data = (uintptr_t)data; 2690 } 2691 2692 void * 2693 di_link_private_get(di_link_t link) 2694 { 2695 return ((void *)(uintptr_t)DI_LINK(link)->user_private_data); 2696 } 2697 2698 di_lnode_t 2699 di_lnode_next(di_node_t node, di_lnode_t lnode) 2700 { 2701 struct di_all *di_all; 2702 2703 /* 2704 * paranoid error checking 2705 */ 2706 if (node == DI_NODE_NIL) { 2707 errno = EINVAL; 2708 return (DI_LNODE_NIL); 2709 } 2710 2711 di_all = DI_ALL((caddr_t)node - DI_NODE(node)->self); 2712 2713 if (lnode == DI_NODE_NIL) { 2714 if (DI_NODE(node)->lnodes != NULL) 2715 return (DI_LNODE((caddr_t)di_all + 2716 DI_NODE(node)->lnodes)); 2717 } else { 2718 if (DI_LNODE(lnode)->node_next != NULL) 2719 return (DI_LNODE((caddr_t)di_all + 2720 DI_LNODE(lnode)->node_next)); 2721 } 2722 2723 if (DINFOLYR & DI_ALL(di_all)->command) 2724 errno = ENXIO; 2725 else 2726 errno = ENOTSUP; 2727 2728 return (DI_LNODE_NIL); 2729 } 2730 2731 di_link_t 2732 di_link_next_by_node(di_node_t node, di_link_t link, uint_t endpoint) 2733 { 2734 struct di_all *di_all; 2735 2736 /* 2737 * paranoid error checking 2738 */ 2739 if ((node == DI_NODE_NIL) || 2740 ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) { 2741 errno = EINVAL; 2742 return (DI_LINK_NIL); 2743 } 2744 2745 di_all = DI_ALL((caddr_t)node - DI_NODE(node)->self); 2746 2747 if (endpoint == DI_LINK_SRC) { 2748 if (link == DI_LINK_NIL) { 2749 if (DI_NODE(node)->src_links != NULL) 2750 return (DI_LINK((caddr_t)di_all + 2751 DI_NODE(node)->src_links)); 2752 } else { 2753 if (DI_LINK(link)->src_node_next != NULL) 2754 return (DI_LINK((caddr_t)di_all + 2755 DI_LINK(link)->src_node_next)); 2756 } 2757 } else { 2758 if (link == DI_LINK_NIL) { 2759 if (DI_NODE(node)->tgt_links != NULL) 2760 return (DI_LINK((caddr_t)di_all + 2761 DI_NODE(node)->tgt_links)); 2762 } else { 2763 if (DI_LINK(link)->tgt_node_next != NULL) 2764 return (DI_LINK((caddr_t)di_all + 2765 DI_LINK(link)->tgt_node_next)); 2766 } 2767 } 2768 2769 if (DINFOLYR & DI_ALL(di_all)->command) 2770 errno = ENXIO; 2771 else 2772 errno = ENOTSUP; 2773 2774 return (DI_LINK_NIL); 2775 } 2776 2777 di_link_t 2778 di_link_next_by_lnode(di_lnode_t lnode, di_link_t link, uint_t endpoint) 2779 { 2780 struct di_all *di_all; 2781 2782 /* 2783 * paranoid error checking 2784 */ 2785 if ((lnode == DI_LNODE_NIL) || 2786 ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) { 2787 errno = EINVAL; 2788 return (DI_LINK_NIL); 2789 } 2790 2791 di_all = DI_ALL((caddr_t)lnode - DI_LNODE(lnode)->self); 2792 2793 if (endpoint == DI_LINK_SRC) { 2794 if (link == DI_LINK_NIL) { 2795 if (DI_LNODE(lnode)->link_out == NULL) 2796 return (DI_LINK_NIL); 2797 return (DI_LINK((caddr_t)di_all + 2798 DI_LNODE(lnode)->link_out)); 2799 } else { 2800 if (DI_LINK(link)->src_link_next == NULL) 2801 return (DI_LINK_NIL); 2802 return (DI_LINK((caddr_t)di_all + 2803 DI_LINK(link)->src_link_next)); 2804 } 2805 } else { 2806 if (link == DI_LINK_NIL) { 2807 if (DI_LNODE(lnode)->link_in == NULL) 2808 return (DI_LINK_NIL); 2809 return (DI_LINK((caddr_t)di_all + 2810 DI_LNODE(lnode)->link_in)); 2811 } else { 2812 if (DI_LINK(link)->tgt_link_next == NULL) 2813 return (DI_LINK_NIL); 2814 return (DI_LINK((caddr_t)di_all + 2815 DI_LINK(link)->tgt_link_next)); 2816 } 2817 } 2818 /* NOTREACHED */ 2819 } 2820 2821 /* 2822 * Internal library function: 2823 * Invoke callback for each link data on the link list of first node 2824 * on node_list headp, and place children of first node on the list. 2825 * 2826 * This is similar to walk_one_node, except we only walk in child 2827 * first mode. 2828 */ 2829 static void 2830 walk_one_link(struct node_list **headp, uint_t ep, 2831 void *arg, int (*callback)(di_link_t link, void *arg)) 2832 { 2833 int action = DI_WALK_CONTINUE; 2834 di_link_t link = DI_LINK_NIL; 2835 di_node_t node = (*headp)->node; 2836 2837 while ((link = di_link_next_by_node(node, link, ep)) != DI_LINK_NIL) { 2838 action = callback(link, arg); 2839 if (action == DI_WALK_TERMINATE) { 2840 break; 2841 } 2842 } 2843 2844 update_node_list(action, DI_WALK_LINKGEN, headp); 2845 } 2846 2847 int 2848 di_walk_link(di_node_t root, uint_t flag, uint_t endpoint, void *arg, 2849 int (*link_callback)(di_link_t link, void *arg)) 2850 { 2851 struct node_list *head; /* node_list for tree walk */ 2852 2853 #ifdef DEBUG 2854 char *path = di_devfs_path(root); 2855 DPRINTF((DI_INFO, "walking %s link data under %s\n", 2856 (endpoint == DI_LINK_SRC) ? "src" : "tgt", path)); 2857 di_devfs_path_free(path); 2858 #endif 2859 2860 /* 2861 * paranoid error checking 2862 */ 2863 if ((root == DI_NODE_NIL) || (link_callback == NULL) || (flag != 0) || 2864 ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) { 2865 errno = EINVAL; 2866 return (-1); 2867 } 2868 2869 if ((head = malloc(sizeof (struct node_list))) == NULL) { 2870 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 2871 return (-1); 2872 } 2873 2874 head->next = NULL; 2875 head->node = root; 2876 2877 DPRINTF((DI_INFO, "Start link data walking from node %s\n", 2878 di_node_name(root))); 2879 2880 while (head != NULL) 2881 walk_one_link(&head, endpoint, arg, link_callback); 2882 2883 return (0); 2884 } 2885 2886 /* 2887 * Internal library function: 2888 * Invoke callback for each link data on the link list of first node 2889 * on node_list headp, and place children of first node on the list. 2890 * 2891 * This is similar to walk_one_node, except we only walk in child 2892 * first mode. 2893 */ 2894 static void 2895 walk_one_lnode(struct node_list **headp, void *arg, 2896 int (*callback)(di_lnode_t lnode, void *arg)) 2897 { 2898 int action = DI_WALK_CONTINUE; 2899 di_lnode_t lnode = DI_LNODE_NIL; 2900 di_node_t node = (*headp)->node; 2901 2902 while ((lnode = di_lnode_next(node, lnode)) != DI_LNODE_NIL) { 2903 action = callback(lnode, arg); 2904 if (action == DI_WALK_TERMINATE) { 2905 break; 2906 } 2907 } 2908 2909 update_node_list(action, DI_WALK_LINKGEN, headp); 2910 } 2911 2912 int 2913 di_walk_lnode(di_node_t root, uint_t flag, void *arg, 2914 int (*lnode_callback)(di_lnode_t lnode, void *arg)) 2915 { 2916 struct node_list *head; /* node_list for tree walk */ 2917 2918 #ifdef DEBUG 2919 char *path = di_devfs_path(root); 2920 DPRINTF((DI_INFO, "walking lnode data under %s\n", path)); 2921 di_devfs_path_free(path); 2922 #endif 2923 2924 /* 2925 * paranoid error checking 2926 */ 2927 if ((root == DI_NODE_NIL) || (lnode_callback == NULL) || (flag != 0)) { 2928 errno = EINVAL; 2929 return (-1); 2930 } 2931 2932 if ((head = malloc(sizeof (struct node_list))) == NULL) { 2933 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 2934 return (-1); 2935 } 2936 2937 head->next = NULL; 2938 head->node = root; 2939 2940 DPRINTF((DI_INFO, "Start lnode data walking from node %s\n", 2941 di_node_name(root))); 2942 2943 while (head != NULL) 2944 walk_one_lnode(&head, arg, lnode_callback); 2945 2946 return (0); 2947 } 2948 2949 di_node_t 2950 di_lookup_node(di_node_t root, char *path) 2951 { 2952 struct di_all *dap; 2953 di_node_t node; 2954 char copy[MAXPATHLEN]; 2955 char *slash, *pname, *paddr; 2956 2957 /* 2958 * Path must be absolute and musn't have duplicate slashes 2959 */ 2960 if (*path != '/' || strstr(path, "//")) { 2961 DPRINTF((DI_ERR, "Invalid path: %s\n", path)); 2962 return (DI_NODE_NIL); 2963 } 2964 2965 if (root == DI_NODE_NIL) { 2966 DPRINTF((DI_ERR, "root node is DI_NODE_NIL\n")); 2967 return (DI_NODE_NIL); 2968 } 2969 2970 dap = DI_ALL((caddr_t)root - DI_NODE(root)->self); 2971 if (strcmp(dap->root_path, "/") != 0) { 2972 DPRINTF((DI_ERR, "snapshot root not / : %s\n", dap->root_path)); 2973 return (DI_NODE_NIL); 2974 } 2975 2976 if (strlcpy(copy, path, sizeof (copy)) >= sizeof (copy)) { 2977 DPRINTF((DI_ERR, "path too long: %s\n", path)); 2978 return (DI_NODE_NIL); 2979 } 2980 2981 for (slash = copy, node = root; slash; ) { 2982 2983 /* 2984 * Handle path = "/" case as well as trailing '/' 2985 */ 2986 if (*(slash + 1) == '\0') 2987 break; 2988 2989 /* 2990 * More path-components exist. Deal with the next one 2991 */ 2992 pname = slash + 1; 2993 node = di_child_node(node); 2994 2995 if (slash = strchr(pname, '/')) 2996 *slash = '\0'; 2997 if (paddr = strchr(pname, '@')) 2998 *paddr++ = '\0'; 2999 3000 for (; node != DI_NODE_NIL; node = di_sibling_node(node)) { 3001 char *name, *baddr; 3002 3003 name = di_node_name(node); 3004 baddr = di_bus_addr(node); 3005 3006 if (strcmp(pname, name) != 0) 3007 continue; 3008 3009 /* 3010 * Mappings between a "path-address" and bus-addr 3011 * 3012 * paddr baddr 3013 * --------------------- 3014 * NULL NULL 3015 * NULL "" 3016 * "" N/A (invalid paddr) 3017 */ 3018 if (paddr && baddr && strcmp(paddr, baddr) == 0) 3019 break; 3020 if (paddr == NULL && (baddr == NULL || *baddr == '\0')) 3021 break; 3022 } 3023 3024 /* 3025 * No nodes in the sibling list or there was no match 3026 */ 3027 if (node == DI_NODE_NIL) { 3028 DPRINTF((DI_ERR, "%s@%s: no node\n", pname, paddr)); 3029 return (DI_NODE_NIL); 3030 } 3031 } 3032 3033 assert(node != DI_NODE_NIL); 3034 return (node); 3035 } 3036 3037 static char * 3038 msglevel2str(di_debug_t msglevel) 3039 { 3040 switch (msglevel) { 3041 case DI_ERR: 3042 return ("ERROR"); 3043 case DI_INFO: 3044 return ("Info"); 3045 case DI_TRACE: 3046 return ("Trace"); 3047 case DI_TRACE1: 3048 return ("Trace1"); 3049 case DI_TRACE2: 3050 return ("Trace2"); 3051 default: 3052 return ("UNKNOWN"); 3053 } 3054 } 3055 3056 void 3057 dprint(di_debug_t msglevel, const char *fmt, ...) 3058 { 3059 va_list ap; 3060 char *estr; 3061 3062 if (di_debug <= DI_QUIET) 3063 return; 3064 3065 if (di_debug < msglevel) 3066 return; 3067 3068 estr = msglevel2str(msglevel); 3069 3070 assert(estr); 3071 3072 va_start(ap, fmt); 3073 3074 (void) fprintf(stderr, "libdevinfo[%lu]: %s: ", 3075 (ulong_t)getpid(), estr); 3076 (void) vfprintf(stderr, fmt, ap); 3077 3078 va_end(ap); 3079 } 3080 3081 /* end of devinfo.c */ 3082