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  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  30  * Implementation to interact with libdevinfo to find port nodes,
  31  * and information regarding each node (fru, port, location).
  32  */
  33 
  34 #include <stdio.h>
  35 #include <libdevinfo.h>
  36 #include <picl.h>
  37 #include <picltree.h>
  38 #include <strings.h>
  39 #include <stdlib.h>
  40 #include <config_admin.h>
  41 #include <sys/types.h>
  42 #include <sys/obpdefs.h>
  43 #include <sys/pci.h>
  44 #include <picldefs.h>
  45 #include "piclfrutree.h"
  46 
  47 #include <syslog.h>
  48 
  49 static di_prom_handle_t prom_handle = DI_PROM_HANDLE_NIL;
  50 extern int frutree_debug;
  51 
  52 typedef struct {
  53         di_node_t rnode;
  54         char    bus_addr[PICL_PROPNAMELEN_MAX];
  55         char    path[PICL_PROPNAMELEN_MAX];
  56         void    *arg;
  57         picl_errno_t retval;
  58 } frutree_devinfo_t;
  59 
  60 typedef struct p_info {
  61         frutree_port_type_t     type;
  62         int  geo_addr;
  63         int  instance;
  64         char drv_name[20];
  65         char bus_addr[20];
  66         char devfs_path[MAXPATHLEN];
  67         struct p_info   *next;
  68 }port_info_t;
  69 
  70 typedef struct {
  71         port_info_t     *first;
  72         port_info_t     *last;
  73         int             n_serial;
  74         int             n_parallel;
  75         int             n_network;
  76 } plist_t;
  77 
  78 static void
  79 free_list(plist_t *listptr)
  80 {
  81         port_info_t     *tmp;
  82         port_info_t     *nextptr;
  83         if (listptr == NULL)
  84                 return;
  85 
  86         nextptr = listptr->first;
  87         while (nextptr != NULL) {
  88                 tmp = nextptr;
  89                 nextptr = nextptr->next;
  90                 free(tmp);
  91         }
  92 }
  93 
  94 /* (callback function for qsort) compare the bus_addr */
  95 static int
  96 compare(const void *a, const void *b)
  97 {
  98         port_info_t *pinfo1, *pinfo2;
  99         port_info_t **ptr2pinfo1, **ptr2pinfo2;
 100 
 101         ptr2pinfo1 = (port_info_t **)a;
 102         ptr2pinfo2 = (port_info_t **)b;
 103 
 104         pinfo1 = (port_info_t *)*ptr2pinfo1;
 105         pinfo2 = (port_info_t *)*ptr2pinfo2;
 106         return (strcmp(pinfo1->bus_addr, pinfo2->bus_addr));
 107 }
 108 
 109 /*
 110  * assigns GeoAddr property for ports based on bus-addr
 111  */
 112 static picl_errno_t
 113 assign_geo_addr(plist_t *list, frutree_port_type_t type)
 114 {
 115 
 116         int i = 0;
 117         port_info_t **port_info = NULL;
 118         port_info_t *nextptr = NULL;
 119         int num_ports = 0;
 120 
 121         if (list == NULL) {
 122                 return (PICL_FAILURE);
 123         }
 124 
 125         if (list->first == NULL) {
 126                 return (PICL_SUCCESS);
 127         }
 128 
 129         switch (type) {
 130         case SERIAL_PORT:
 131         if (list->n_serial == 0) {
 132                 return (PICL_SUCCESS);
 133         }
 134         num_ports = list->n_serial;
 135         break;
 136 
 137         case PARALLEL_PORT:
 138         if (list->n_parallel == 0) {
 139                 return (PICL_SUCCESS);
 140         }
 141         num_ports = list->n_parallel;
 142         break;
 143 
 144         case NETWORK_PORT:
 145         if (list->n_network == 0) {
 146                 return (PICL_SUCCESS);
 147         }
 148         num_ports = list->n_network;
 149         break;
 150 
 151         }
 152 
 153         port_info = (port_info_t **)malloc(
 154                 sizeof (port_info_t *) * num_ports);
 155         if (port_info == NULL) {
 156                 return (PICL_NOSPACE);
 157         }
 158 
 159         /* traverse thru list and look for ports of given type */
 160         nextptr = list->first;
 161         while (nextptr != NULL) {
 162                 if (nextptr->type != type) {
 163                         nextptr = nextptr->next;
 164                         continue;
 165                 }
 166                 port_info[i] = nextptr;
 167                 nextptr = nextptr->next;
 168                 i++;
 169         }
 170 
 171         /* sort the nodes to assign geo_address */
 172         (void) qsort((void *)port_info, num_ports,
 173                 sizeof (port_info_t *), compare);
 174         for (i = 0; i < num_ports; i++) {
 175                 if (port_info[i] != NULL) {
 176                         port_info[i]->geo_addr = i + 1;
 177                 }
 178         }
 179         free(port_info);
 180         return (PICL_SUCCESS);
 181 }
 182 
 183 static picl_errno_t
 184 create_port_config_info(plist_t *list, frutree_device_args_t *devp)
 185 {
 186         port_info_t *port_info = NULL;
 187         frutree_cache_t *cachep = NULL;
 188         char port_type[PICL_PROPNAMELEN_MAX];
 189         char label[PICL_PROPNAMELEN_MAX];
 190 
 191         if (list == NULL) {
 192                 return (PICL_FAILURE);
 193         }
 194 
 195         port_info = list->first;
 196         while (port_info != NULL) {
 197 
 198                 cachep = (frutree_cache_t *)malloc(sizeof (frutree_cache_t));
 199                 if (cachep == NULL) {
 200                         return (PICL_NOSPACE);
 201                 }
 202 
 203                 switch (port_info->type) {
 204                 case NETWORK_PORT:
 205                         (void) strncpy(label, SANIBEL_NETWORK_LABEL,
 206                                 sizeof (label));
 207                         (void) strncpy(port_type, SANIBEL_NETWORK_PORT,
 208                                 sizeof (port_type));
 209                         break;
 210                 case PARALLEL_PORT:
 211                         (void) strncpy(label, SANIBEL_PARALLEL_PORT,
 212                                 sizeof (label));
 213                         (void) strncpy(port_type, SANIBEL_PARALLEL_PORT,
 214                                 sizeof (port_type));
 215                         break;
 216                 case SERIAL_PORT:
 217                         (void) strncpy(label, SANIBEL_SERIAL_PORT,
 218                                 sizeof (label));
 219                         (void) strncpy(port_type, SANIBEL_SERIAL_PORT,
 220                                 sizeof (port_type));
 221                         break;
 222                 default:
 223                         port_info = port_info->next;
 224                 }
 225                 cachep->buf[0] = '\0';
 226                 cachep->next = NULL;
 227                 (void) snprintf(cachep->buf,
 228                         sizeof (cachep->buf),
 229                         "\n%s %s%d %s\n"
 230                         "\t%s %s %s %s 0 \"%s %d\"\n"
 231                         "\t%s %s %s %s 0 \"%s\"\n"
 232                         "\t%s %s %s %s 1 %d\n"
 233                         "\t%s %s %s %s 0 \"%s\"\n"
 234                         "\t%s %s %s %s 0 \"%s\"\n"
 235                         "%s\n",
 236                         "NODE", port_info->drv_name, port_info->instance,
 237                                 PICL_CLASS_PORT,
 238                         "PROP", PICL_PROP_LABEL, "string", "r",
 239                                 label, (port_info->geo_addr -1),
 240                         "PROP", PICL_PROP_BUS_ADDR, "string",
 241                                 "r", port_info->bus_addr,
 242                         "PROP", PICL_PROP_GEO_ADDR, "uint",
 243                                 "r", port_info->geo_addr,
 244                         "PROP", PICL_PROP_PORT_TYPE, "string",
 245                                 "r", port_type,
 246                         "PROP", PICL_PROP_DEVFS_PATH, "string",
 247                                 "r", port_info->devfs_path,
 248                         "ENDNODE");
 249 
 250                         /* add to the cache */
 251                         if (devp->first == NULL) {   /* 1st node */
 252                                 devp->first = cachep;
 253                                 devp->last = NULL;
 254                         } else if (devp->last != NULL) { /* last node */
 255                                 devp->last->next = cachep;
 256                                 devp->last = cachep;
 257                         } else {                        /* 2nd node */
 258                                 devp->first->next = cachep;
 259                                 devp->last = cachep;
 260                         }
 261                 port_info = port_info->next; /* advance to next node */
 262         }
 263         return (PICL_SUCCESS);
 264 }
 265 
 266 /*ARGSUSED*/
 267 static int
 268 load_driver(di_node_t node, void *arg)
 269 {
 270         char *drv_name = NULL;
 271         char cmd[MAXPATHLEN];
 272 
 273         if (di_node_state(node) >= DS_ATTACHED) {
 274                 return (DI_WALK_CONTINUE);
 275         }
 276         drv_name = di_driver_name(node);
 277         if (drv_name == NULL) {
 278                 return (DI_WALK_CONTINUE);
 279         }
 280 
 281         (void) snprintf(cmd, sizeof (cmd), "%s %s",
 282                 DEVFSADM_CMD, drv_name);
 283         (void) pclose(popen(cmd, "r"));
 284         return (DI_WALK_CONTINUE);
 285 }
 286 
 287 static picl_errno_t
 288 load_drivers(char *path)
 289 {
 290         di_node_t       rnode;
 291         if (path == NULL) {
 292                 return (PICL_INVALIDARG);
 293         }
 294 
 295         rnode = di_init(path, DINFOSUBTREE|DINFOMINOR);
 296         if (rnode == DI_NODE_NIL) {
 297                 return (PICL_FAILURE);
 298         }
 299 
 300         if (di_walk_node(rnode, DI_WALK_CLDFIRST, NULL, load_driver) != 0) {
 301                 di_fini(rnode);
 302                 return (PICL_FAILURE);
 303         }
 304 
 305         di_fini(rnode);
 306         return (PICL_SUCCESS);
 307 }
 308 
 309 /*
 310  * probe for port nodes
 311  */
 312 static int
 313 probe_tree(di_node_t node, void *arg)
 314 {
 315         char *nodetype = NULL;
 316         char *devfs_path = NULL;
 317         char *bus_addr = NULL;
 318         char *drv_name = NULL;
 319         plist_t *listptr = NULL;
 320         port_info_t *port_info = NULL;
 321         frutree_port_type_t port_type = UNKNOWN_PORT;
 322         di_minor_t minor = DI_MINOR_NIL;
 323 
 324         if (arg == NULL) {
 325                 return (DI_WALK_TERMINATE);
 326         }
 327         listptr = (plist_t *)arg;
 328 
 329         while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
 330                 nodetype = di_minor_nodetype(minor);
 331                 if (nodetype == NULL) {
 332                         continue;
 333                 }
 334 
 335                 if (strcmp(nodetype, DDI_NT_NET) == 0) {
 336                         port_type = NETWORK_PORT;
 337                 } else if (strcmp(nodetype, DDI_NT_PARALLEL) == 0) {
 338                         port_type = PARALLEL_PORT;
 339                 } else if ((strcmp(nodetype, DDI_NT_SERIAL) == 0) ||
 340                         (strcmp(nodetype, DDI_NT_SERIAL_MB) == 0) ||
 341                         (strcmp(nodetype, DDI_NT_SERIAL_DO) == 0) ||
 342                         (strcmp(nodetype, DDI_NT_SERIAL_MB_DO) == 0)) {
 343                         port_type = SERIAL_PORT;
 344                 } else {
 345                         continue;
 346                 }
 347 
 348                 /* found port node */
 349                 devfs_path = di_devfs_path(node);
 350                 if (devfs_path == NULL) {
 351                         continue;
 352                 }
 353 
 354                 bus_addr = di_bus_addr(node);
 355                 drv_name = di_driver_name(node);
 356 
 357                 if ((bus_addr == NULL) || (drv_name == NULL)) {
 358                         di_devfs_path_free(devfs_path);
 359                         continue;
 360                 }
 361 
 362                 port_info = malloc(sizeof (port_info_t));
 363                 if (port_info == NULL) {
 364                         di_devfs_path_free(devfs_path);
 365                         return (PICL_NOSPACE);
 366                 }
 367 
 368                 (void) strncpy(port_info->devfs_path, devfs_path,
 369                         sizeof (port_info->devfs_path));
 370                 (void) strncpy(port_info->bus_addr, bus_addr,
 371                         sizeof (port_info->bus_addr));
 372                 (void) strncpy(port_info->drv_name, drv_name,
 373                         sizeof (port_info->drv_name));
 374                 port_info->type = port_type;
 375                 port_info->instance = di_instance(node);
 376                 port_info->geo_addr = -1;
 377                 port_info->next = NULL;
 378 
 379                 switch (port_type) {
 380                 case NETWORK_PORT:
 381                         listptr->n_network++;
 382                         break;
 383                 case SERIAL_PORT:
 384                         listptr->n_serial++;
 385                         break;
 386                 case PARALLEL_PORT:
 387                         listptr->n_parallel++;
 388                         break;
 389                 }
 390 
 391                 /* add to the list */
 392                 if (listptr->first == NULL) {        /* 1st node */
 393                         listptr->first = port_info;
 394                         listptr->last = NULL;
 395                 } else if (listptr->last != NULL) { /* last node */
 396                         listptr->last->next = port_info;
 397                         listptr->last = port_info;
 398                 } else {                        /* 2nd node */
 399                         listptr->first->next = port_info;
 400                         listptr->last = port_info;
 401                 }
 402                 di_devfs_path_free(devfs_path);
 403                 return (DI_WALK_CONTINUE);
 404         }
 405         return (DI_WALK_CONTINUE);
 406 }
 407 
 408 /* This routine probes libdevinfo for port nodes */
 409 picl_errno_t
 410 probe_libdevinfo(frutree_frunode_t *frup, frutree_device_args_t ** device,
 411         boolean_t load_drv)
 412 {
 413         di_node_t       rnode;
 414         picl_errno_t    rc;
 415         plist_t list;
 416 
 417         if (frup == NULL) {
 418                 return (PICL_FAILURE);
 419         }
 420         FRUTREE_DEBUG1(EVENTS, "loading drivers for %s", frup->name);
 421 
 422         if (load_drv == B_TRUE) {
 423                 if ((rc = load_drivers(frup->fru_path)) != PICL_SUCCESS) {
 424                         return (rc);
 425                 }
 426         }
 427         FRUTREE_DEBUG1(EVENTS, "done with loading drivers for %s", frup->name);
 428 
 429         rnode = di_init(frup->fru_path, DINFOSUBTREE|DINFOMINOR);
 430         if (rnode == DI_NODE_NIL) {
 431                 return (PICL_FAILURE);
 432         }
 433 
 434         list.first = NULL;
 435         list.last = NULL;
 436         list.n_network = 0;
 437         list.n_serial = 0;
 438         list.n_parallel = 0;
 439 
 440         if (di_walk_node(rnode, DI_WALK_CLDFIRST, &list, probe_tree) != 0) {
 441                 di_fini(rnode);
 442                 free_list(&list);
 443                 return (PICL_FAILURE);
 444         }
 445 
 446         if (list.n_serial > 0)
 447         if ((rc = assign_geo_addr(&list, SERIAL_PORT)) != PICL_SUCCESS) {
 448                 di_fini(rnode);
 449                 free_list(&list);
 450                 return (rc);
 451         }
 452 
 453         if (list.n_network > 0)
 454         if ((rc = assign_geo_addr(&list, NETWORK_PORT)) != PICL_SUCCESS) {
 455                 di_fini(rnode);
 456                 free_list(&list);
 457                 return (rc);
 458         }
 459 
 460         if (list.n_parallel > 0)
 461         if ((rc = assign_geo_addr(&list, PARALLEL_PORT)) != PICL_SUCCESS) {
 462                 di_fini(rnode);
 463                 free_list(&list);
 464                 return (rc);
 465         }
 466 
 467         if ((rc = create_port_config_info(&list, *device)) != PICL_SUCCESS) {
 468                 di_fini(rnode);
 469                 free_list(&list);
 470                 return (rc);
 471         }
 472 
 473         di_fini(rnode);
 474         free_list(&list);
 475         FRUTREE_DEBUG1(EVENTS, "done with probing %s", frup->name);
 476         return (PICL_SUCCESS);
 477 }
 478 
 479 static int
 480 get_reg_dev(di_node_t node)
 481 {
 482         int *reg = NULL;
 483         if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, OBP_REG, &reg) < 0) {
 484                 if (di_prom_prop_lookup_ints(prom_handle, node, OBP_REG,
 485                         &reg) < 0) {
 486                         return (-1);
 487                 }
 488                 return (PCI_REG_DEV_G(reg[0]));
 489         }
 490         return (PCI_REG_DEV_G(reg[0]));
 491 }
 492 
 493 static int
 494 walk_tree(di_node_t node, void *arg)
 495 {
 496         char    *path = NULL;
 497         char    *bus_addr = NULL;
 498         char    *char_di_bus_addr = NULL;
 499         int     busaddr = 0;
 500         int     di_busaddr = 0;
 501         char    *node_name = NULL;
 502         frutree_devinfo_t *devinfo;
 503         frutree_frunode_t *frup = NULL;
 504 
 505         devinfo = *(frutree_devinfo_t **)arg;
 506         frup = (frutree_frunode_t *)devinfo->arg;
 507         if (frup == NULL) {
 508                 return (DI_WALK_TERMINATE);
 509         }
 510 
 511         if (devinfo->rnode == node) {        /* skip the root node */
 512                 return (DI_WALK_CONTINUE);
 513         }
 514         bus_addr = devinfo->bus_addr;
 515 
 516         char_di_bus_addr = di_bus_addr(node);
 517         if (char_di_bus_addr == NULL) {
 518                 /*
 519                  * look for reg property
 520                  * This applies to only cPCI devices
 521                  */
 522                 if (strstr(bus_addr, ",") != NULL) {
 523                         /* bus addr is of type 1,0 */
 524                         /* we dont handle this case yet */
 525                         return (DI_WALK_PRUNECHILD);
 526                 }
 527                 di_busaddr = get_reg_dev(node);
 528                 if (di_busaddr == -1) {
 529                         /* reg prop not found */
 530                         return (DI_WALK_PRUNECHILD);
 531                 }
 532 
 533                 /* check if the bus addresses are same */
 534                 errno = 0;
 535                 busaddr = strtol(bus_addr, (char **)NULL, 16);
 536                 if (errno != 0) {
 537                         return (DI_WALK_TERMINATE);
 538                 }
 539                 if (di_busaddr != busaddr) {
 540                         return (DI_WALK_PRUNECHILD);
 541                 }
 542 
 543                 /* build the fru path name */
 544                 /* parent_path/nodename@bus_addr */
 545                 node_name = di_node_name(node);
 546                 if (node_name == NULL) {
 547                         return (DI_WALK_TERMINATE);
 548                 }
 549                 (void) snprintf(devinfo->path, sizeof (devinfo->path),
 550                         "%s/%s@%s", frup->fru_path, node_name, bus_addr);
 551                 return (DI_WALK_TERMINATE);
 552         }
 553 
 554         if (strstr(bus_addr, ",") != NULL) { /* bus addr is of type 1,0 */
 555                 if (strcmp(bus_addr, char_di_bus_addr) != 0) {
 556                         return (DI_WALK_PRUNECHILD);
 557                 }
 558         } else { /* bus addr is of type 0x */
 559 
 560                 /* check if the values are same */
 561                 errno = 0;
 562                 busaddr = strtol(bus_addr, (char **)NULL, 16);
 563                 if (errno != 0) {
 564                         return (DI_WALK_TERMINATE);
 565                 }
 566 
 567                 errno = 0;
 568                 di_busaddr = strtol(char_di_bus_addr, (char **)NULL, 16);
 569                 if (errno != 0) {
 570                         return (DI_WALK_TERMINATE);
 571                 }
 572 
 573                 if (di_busaddr != busaddr) {
 574                         return (DI_WALK_PRUNECHILD);
 575                 }
 576         }
 577 
 578         /* node found */
 579         path = di_devfs_path(node);
 580         (void) strncpy(devinfo->path, path, sizeof (devinfo->path));
 581         di_devfs_path_free(path);
 582         return (DI_WALK_TERMINATE);
 583 }
 584 
 585 picl_errno_t
 586 get_fru_path(char *parent_path, frutree_frunode_t *frup)
 587 {
 588         picl_errno_t rc = 0;
 589         picl_nodehdl_t loch;
 590         di_node_t rnode;
 591         frutree_devinfo_t *devinfo = NULL;
 592         char slot_type[PICL_PROPNAMELEN_MAX];
 593         char probe_path[PICL_PROPNAMELEN_MAX];
 594         char bus_addr[PICL_PROPNAMELEN_MAX];
 595 
 596         if ((rc = ptree_get_propval_by_name(frup->frunodeh, PICL_PROP_PARENT,
 597                 &loch, sizeof (loch))) != PICL_SUCCESS) {
 598                 return (rc);
 599         }
 600 
 601         if ((rc = ptree_get_propval_by_name(loch, PICL_PROP_SLOT_TYPE,
 602                 slot_type, sizeof (slot_type))) != PICL_SUCCESS) {
 603                 return (rc);
 604         }
 605 
 606         if (strcmp(slot_type, SANIBEL_SCSI_SLOT) == 0 ||
 607                 strcmp(slot_type, SANIBEL_IDE_SLOT) == 0) {
 608                 if (ptree_get_propval_by_name(loch, PICL_PROP_PROBE_PATH,
 609                         probe_path, sizeof (probe_path)) != PICL_SUCCESS) {
 610                         return (rc);
 611                 }
 612                 (void) strncpy(frup->fru_path, probe_path,
 613                         sizeof (frup->fru_path));
 614                 return (PICL_SUCCESS);
 615         }
 616 
 617         prom_handle = di_prom_init();
 618         rnode = di_init(parent_path, DINFOSUBTREE|DINFOMINOR);
 619         if (rnode == DI_NODE_NIL) {
 620                 di_prom_fini(prom_handle);
 621                 return (PICL_FAILURE);
 622         }
 623 
 624         devinfo = (frutree_devinfo_t *)malloc(sizeof (frutree_devinfo_t));
 625         if (devinfo == NULL) {
 626                 di_fini(rnode);
 627                 di_prom_fini(prom_handle);
 628                 return (PICL_NOSPACE);
 629         }
 630 
 631         if (ptree_get_propval_by_name(loch, PICL_PROP_BUS_ADDR,
 632                 bus_addr, sizeof (bus_addr)) != PICL_SUCCESS) {
 633                 free(devinfo);
 634                 di_fini(rnode);
 635                 di_prom_fini(prom_handle);
 636                 return (rc);
 637         }
 638 
 639         devinfo->rnode = rnode;
 640         (void) strncpy(devinfo->bus_addr, bus_addr, sizeof (devinfo->bus_addr));
 641         devinfo->path[0] = '\0';
 642         devinfo->arg = frup;
 643 
 644         if (di_walk_node(rnode, DI_WALK_SIBFIRST, &devinfo, walk_tree) != 0) {
 645                 di_fini(rnode);
 646                 di_prom_fini(prom_handle);
 647                 free(devinfo);
 648                 return (PICL_FAILURE);
 649         }
 650         di_fini(rnode);
 651         di_prom_fini(prom_handle);
 652 
 653         if (devinfo->path[0]) {
 654                 (void) strncpy(frup->fru_path, devinfo->path,
 655                         sizeof (frup->fru_path));
 656                 free(devinfo);
 657                 return (PICL_SUCCESS);
 658         } else {
 659                 free(devinfo);
 660                 return (PICL_NODENOTFOUND);
 661         }
 662 }
 663 
 664 static int
 665 find_fru_node(di_node_t node, void *arg)
 666 {
 667         frutree_locnode_t *locp = NULL;
 668         char    *char_di_bus_addr = NULL;
 669         int     busaddr = 0;
 670         int     di_busaddr = 0;
 671         char bus_addr[PICL_PROPNAMELEN_MAX];
 672         frutree_devinfo_t *devinfo = NULL;
 673 
 674         devinfo = *(frutree_devinfo_t **)arg;
 675         locp = *(frutree_locnode_t **)devinfo->arg;
 676 
 677         if (devinfo->rnode == node) {
 678                 return (DI_WALK_CONTINUE);
 679         }
 680 
 681         char_di_bus_addr = di_bus_addr(node);
 682         if (char_di_bus_addr == NULL) {
 683                 return (DI_WALK_PRUNECHILD);
 684         }
 685 
 686         if (ptree_get_propval_by_name(locp->locnodeh, PICL_PROP_BUS_ADDR,
 687                 bus_addr, sizeof (bus_addr)) != PICL_SUCCESS) {
 688                 return (DI_WALK_PRUNECHILD);
 689         }
 690 
 691         if (strstr(bus_addr, ",") != NULL) {
 692                 /* bus addr is of type 1,0 */
 693                 if (strcmp(bus_addr, char_di_bus_addr) == 0) {
 694                         devinfo->retval = PICL_SUCCESS;
 695                         return (DI_WALK_TERMINATE);
 696                 } else {
 697                         return (DI_WALK_PRUNECHILD);
 698                 }
 699         } else { /* bus addr is of type 0x */
 700 
 701                 /* check if the values are same */
 702                 errno = 0;
 703                 busaddr = strtol(bus_addr, (char **)NULL, 16);
 704                 if (errno != 0) {
 705                         return (DI_WALK_PRUNECHILD);
 706                 }
 707 
 708                 errno = 0;
 709                 di_busaddr = strtol(char_di_bus_addr, (char **)NULL, 16);
 710                 if (errno != 0) {
 711                         return (DI_WALK_PRUNECHILD);
 712                 }
 713 
 714                 if (di_busaddr == busaddr) {
 715                         devinfo->retval = PICL_SUCCESS;
 716                         return (DI_WALK_TERMINATE);
 717                 } else {
 718                         return (DI_WALK_PRUNECHILD);
 719                 }
 720         }
 721 }
 722 
 723 /*
 724  * checks if a fru is present under location using pdev-path and busaddr
 725  */
 726 boolean_t
 727 is_fru_present_under_location(frutree_locnode_t *locp)
 728 {
 729         di_node_t               rnode;
 730         frutree_devinfo_t       *devinfo = NULL;
 731         char probe_path[PICL_PROPNAMELEN_MAX];
 732 
 733         if (locp == NULL) {
 734                 return (B_FALSE);
 735         }
 736 
 737         if (ptree_get_propval_by_name(locp->locnodeh, PICL_PROP_PROBE_PATH,
 738                 probe_path, sizeof (probe_path)) != PICL_SUCCESS) {
 739                 if (ptree_get_propval_by_name(locp->locnodeh,
 740                         PICL_PROP_DEVFS_PATH, probe_path,
 741                         sizeof (probe_path)) != PICL_SUCCESS) {
 742                         return (B_FALSE);
 743                 }
 744         }
 745 
 746         rnode = di_init(probe_path, DINFOSUBTREE);
 747         if (rnode == DI_NODE_NIL) {
 748                 di_fini(rnode);
 749                 return (B_FALSE);
 750         }
 751 
 752         devinfo = (frutree_devinfo_t *)malloc(sizeof (frutree_devinfo_t));
 753         if (devinfo == NULL) {
 754                 di_fini(rnode);
 755                 return (B_FALSE);
 756         }
 757         devinfo->rnode = rnode;
 758         devinfo->arg = (frutree_locnode_t **)&locp;
 759         devinfo->retval = PICL_FAILURE;
 760 
 761         if (di_walk_node(rnode, DI_WALK_SIBFIRST, &devinfo,
 762                 find_fru_node) != 0) {
 763                 di_fini(rnode);
 764                 free(devinfo);
 765                 return (B_FALSE);
 766         }
 767         di_fini(rnode);
 768 
 769         if (devinfo->retval == PICL_SUCCESS) {
 770                 free(devinfo);
 771                 return (B_TRUE);
 772         } else {
 773                 free(devinfo);
 774                 return (B_FALSE);
 775         }
 776 }
 777 
 778 /*
 779  * initializes the port driver and instance fields based on libdevinfo
 780  */
 781 picl_errno_t
 782 get_port_info(frutree_portnode_t *portp)
 783 {
 784         picl_errno_t rc;
 785         di_node_t rnode, curr, peer;
 786         char devfs_path[PICL_PROPNAMELEN_MAX];
 787         char bus_addr[PICL_PROPNAMELEN_MAX];
 788         char *di_busaddr = NULL, *di_drv = NULL;
 789         int di_int_busaddr, int_busaddr;
 790 
 791         if ((rc = ptree_get_propval_by_name(portp->portnodeh,
 792                 PICL_PROP_DEVFS_PATH, devfs_path,
 793                 sizeof (devfs_path))) != PICL_SUCCESS) {
 794                 return (rc);
 795         }
 796 
 797         if (ptree_get_propval_by_name(portp->portnodeh, PICL_PROP_BUS_ADDR,
 798                 bus_addr, sizeof (bus_addr)) != PICL_SUCCESS) {
 799                 return (rc);
 800         }
 801 
 802         rnode = di_init(devfs_path, DINFOCPYALL);
 803         if (rnode == DI_NODE_NIL) {
 804                 return (PICL_FAILURE);
 805         }
 806 
 807         peer = di_child_node(rnode);
 808         while (peer != DI_NODE_NIL) {
 809                 curr = peer;
 810                 peer = di_sibling_node(curr);
 811 
 812                 di_busaddr = di_bus_addr(curr);
 813                 if (di_busaddr == NULL) {
 814                         continue;
 815                 }
 816 
 817                 /* compare the bus_addr */
 818                 if (strstr(bus_addr, ",") != NULL) {
 819                         /* bus addr is of type 1,0 */
 820                         if (strcmp(bus_addr, di_busaddr) != 0) {
 821                                 continue;
 822                         }
 823                 } else { /* bus addr is of type 0x */
 824                         errno = 0;
 825                         int_busaddr = strtol(bus_addr, (char **)NULL, 16);
 826                         if (errno != 0) {
 827                                 continue;
 828                         }
 829 
 830                         errno = 0;
 831                         di_int_busaddr = strtol(di_busaddr, (char **)NULL, 16);
 832                         if (errno != 0) {
 833                                 continue;
 834                         }
 835 
 836                         if (di_int_busaddr != int_busaddr) {
 837                                 continue;
 838                         }
 839                 }
 840                 di_drv = di_driver_name(curr);
 841                 if (di_drv == NULL) {
 842                         di_fini(rnode);
 843                         return (PICL_FAILURE);
 844                 }
 845                 /* initialize the driver name and instance number */
 846                 (void) strncpy(portp->driver, di_drv, sizeof (portp->driver));
 847                 portp->instance = di_instance(curr);
 848                 di_fini(rnode);
 849                 return (PICL_SUCCESS);
 850         }
 851         di_fini(rnode);
 852         return (PICL_NODENOTFOUND);
 853 }