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 /* implementation specific to scsi nodes probing */
  30 
  31 #include <stdio.h>
  32 #include <unistd.h>
  33 #include <syslog.h>
  34 #include <stdlib.h>
  35 #include <sys/param.h>
  36 #include <config_admin.h>
  37 #include <string.h>
  38 #include <strings.h>
  39 #include <picl.h>
  40 #include <picltree.h>
  41 #include <libintl.h>
  42 #include <libdevinfo.h>
  43 #include <sys/types.h>
  44 #include <picldefs.h>
  45 #include "piclfrutree.h"
  46 
  47 #define SCSI_SLOT       "scsi-bus"
  48 #define SCSI_LOC_FORMAT "t%dd0"
  49 #define TARGET          "target"
  50 #define CLASS           "class"
  51 #define BUF_SIZE        256
  52 
  53 #define SCSI_INITIATOR_ID       7
  54 #define DRV_TYPE_DSK    1
  55 #define DRV_TYPE_TAPE   2
  56 #define NUM_DSK_TARGS   15
  57 /*
  58  * No support for wide tapes for now.
  59  * If required wide support, set this to 8
  60  * See st.conf.
  61  */
  62 #define NUM_TAPE_TARGS  7
  63 
  64 #define DIRLINK_DSK     "dsk"
  65 #define DIRLINK_RMT     "rmt"
  66 #define DRV_SCSI_DSK    "sd"
  67 #define DRV_SCSI_TAPE   "st"
  68 #define NULL_ENTRY      0
  69 
  70 /* currently supported directory strings for SCSI FRUs in cfgadm APs */
  71 static char *scsi_dirlink_names[] = { DIRLINK_DSK, DIRLINK_RMT, NULL_ENTRY};
  72 /* currently supported SCSI FRU drivers */
  73 static struct scsi_drv_info {
  74         char *drv_name;
  75         uint8_t num_targets;
  76         uint8_t drv_type;
  77 } scsi_drv[] = {
  78                 DRV_SCSI_DSK, NUM_DSK_TARGS, DRV_TYPE_DSK,
  79                 DRV_SCSI_TAPE, NUM_TAPE_TARGS, DRV_TYPE_TAPE,
  80                 NULL_ENTRY,     NULL_ENTRY,     NULL_ENTRY
  81                 };
  82 
  83 /* the following defs are based on defines in scsi cfgadm plugin */
  84 #define CDROM           "CD-ROM"
  85 #define RMM             "tape"
  86 #define DISK            "disk"
  87 
  88 extern boolean_t is_location_present_in_subtree(frutree_frunode_t *,
  89         const char *, const char *);
  90 extern picl_errno_t create_children(frutree_frunode_t *, char *, char *,
  91         int, char *, boolean_t);
  92 extern char *strtok_r(char *s1, const char *s2, char **lasts);
  93 extern boolean_t frutree_connects_initiated;
  94 extern int frutree_debug;
  95 
  96 typedef struct node {
  97         struct node *next;
  98         cfga_list_data_t *data;
  99 } node_t;
 100 
 101 typedef struct linked_list {
 102         node_t *first;
 103         int num_nodes;
 104 } plist_t;
 105 
 106 typedef struct scsi_info {
 107         frutree_frunode_t *frup;
 108         cfga_list_data_t *cfgalist;
 109         plist_t *list;
 110         int num_list;
 111         boolean_t compare_cfgadm;
 112         int geo_addr;
 113 } scsi_info_t;
 114 
 115 static plist_t *scsi_list = NULL;
 116 static cfga_list_data_t *cfglist = NULL;
 117 static int nlist = 0;
 118 
 119 static void
 120 free_list(plist_t *list)
 121 {
 122         node_t  *tmp = NULL, *tmp1 = NULL;
 123 
 124         if (list == NULL)
 125                 return;
 126         tmp = list->first;
 127         while (tmp != NULL) {
 128                 free(tmp->data);
 129                 tmp1 = tmp->next;
 130                 free(tmp);
 131                 tmp = tmp1;
 132         }
 133 }
 134 
 135 /*
 136  * This routine gets the list of scsi controllers present
 137  */
 138 static cfga_err_t
 139 populate_controllers_list(plist_t *cntrl_list, cfga_list_data_t *list, int num)
 140 {
 141         int i;
 142         node_t *nodeptr = NULL;
 143         cfga_list_data_t *temp = NULL;
 144 
 145         if (cntrl_list == NULL || list == NULL) {
 146                 return (CFGA_ATTR_INVAL);
 147         }
 148 
 149         cntrl_list->first = NULL;
 150         cntrl_list->num_nodes = 0;
 151 
 152         if (num == 0) {
 153                 return (CFGA_OK);
 154         }
 155 
 156         for (i = 0; i < num; i++) {
 157                 if (strcmp(list[i].ap_type, SCSI_SLOT) != 0) {
 158                         continue;
 159                 }
 160 
 161                 /* scsi controller */
 162                 temp = (cfga_list_data_t *)malloc(sizeof (cfga_list_data_t));
 163                 if (temp == NULL) {
 164                         return (CFGA_ERROR);
 165                 }
 166                 (void) memcpy(temp, &list[i], sizeof (cfga_list_data_t));
 167 
 168                 nodeptr = (node_t *)malloc(sizeof (node_t));
 169                 if (nodeptr == NULL) {
 170                         free(temp);
 171                         return (CFGA_ERROR);
 172                 }
 173                 nodeptr->data = temp;
 174                 nodeptr->next = NULL;
 175 
 176                 /* append to the list */
 177                 if (cntrl_list->first == NULL) {
 178                         cntrl_list->first = nodeptr;
 179                         cntrl_list->num_nodes++;
 180                 } else {
 181                         nodeptr->next = cntrl_list->first;
 182                         cntrl_list->first = nodeptr;
 183                         cntrl_list->num_nodes++;
 184                 }
 185         }
 186         return (CFGA_OK);
 187 }
 188 
 189 picl_errno_t
 190 scsi_info_init()
 191 {
 192         cfga_err_t      ap_list_err;
 193 
 194         ap_list_err = config_list_ext(0, NULL, &cfglist, &nlist, NULL,
 195                 NULL, NULL, CFGA_FLAG_LIST_ALL);
 196 
 197         if (ap_list_err != CFGA_OK) {
 198                 if (ap_list_err == CFGA_NOTSUPP) {
 199                         return (PICL_SUCCESS);
 200                 } else {
 201                         return (PICL_FAILURE);
 202                 }
 203         }
 204 
 205         scsi_list = (plist_t *)malloc(sizeof (plist_t));
 206         if (scsi_list == NULL) {
 207                 free(cfglist);
 208                 return (PICL_NOSPACE);
 209         }
 210 
 211         ap_list_err = populate_controllers_list(scsi_list, cfglist, nlist);
 212         if (ap_list_err != CFGA_OK) {
 213                 free(cfglist);
 214                 free(scsi_list);
 215                 return (PICL_FAILURE);
 216         }
 217         return (PICL_SUCCESS);
 218 }
 219 
 220 void
 221 scsi_info_fini()
 222 {
 223         free(cfglist);
 224         free_list(scsi_list);
 225         free(scsi_list);
 226 }
 227 
 228 /*
 229  * This routine searches the controllers list to find the mapping based
 230  * on given devfs_path.
 231  * caller should allocate memory for ap_id
 232  */
 233 static picl_errno_t
 234 find_scsi_controller(char *devfs_path, plist_t *list, char *ap_id)
 235 {
 236         node_t  *tmp = NULL;
 237         char *lasts = NULL;
 238         char *token = NULL;
 239         char path[MAXPATHLEN];
 240 
 241         if (devfs_path == NULL || ap_id == NULL) {
 242                 return (PICL_INVALIDARG);
 243         }
 244         (void) snprintf((char *)path, sizeof (path), "/devices%s", devfs_path);
 245 
 246         tmp = list->first;
 247         while (tmp != NULL) {
 248                 lasts = tmp->data->ap_phys_id;
 249                 token = (char *)strtok_r(lasts, (const char *)":",
 250                         (char **)&lasts);
 251                 if (token == NULL) {
 252                         tmp = tmp->next;
 253                         continue;
 254                 }
 255 
 256                 if (strcmp(path, token) == 0) { /* match found */
 257                         (void) strncpy(ap_id, tmp->data->ap_log_id,
 258                                 sizeof (ap_id));
 259                         return (PICL_SUCCESS);
 260                 }
 261                 tmp = tmp->next;
 262         }
 263         return (PICL_NODENOTFOUND);
 264 }
 265 
 266 /*
 267  * This routine dynamically determines the cfgadm attachment point
 268  * for a given devfspath and target id.
 269  * memory for name should be allocated by the caller.
 270  */
 271 picl_errno_t
 272 get_scsislot_name(char *devfs_path, char *bus_addr, char *name)
 273 {
 274         picl_errno_t    rc;
 275         int target_id = 0;
 276         int numlist;
 277         plist_t                 list;
 278         cfga_err_t              ap_list_err;
 279         cfga_list_data_t        *cfgalist = NULL;
 280         char controller[MAXPATHLEN];
 281 
 282         ap_list_err = config_list_ext(0, NULL, &cfgalist,
 283                 &numlist, NULL, NULL, NULL, CFGA_FLAG_LIST_ALL);
 284         if (ap_list_err != CFGA_OK) {
 285                 return (PICL_NODENOTFOUND);
 286         }
 287 
 288         ap_list_err = populate_controllers_list(&list, cfgalist,
 289                 numlist);
 290         if (ap_list_err != CFGA_OK) {
 291                 free_list(&list);
 292                 free(cfgalist);
 293                 return (PICL_NODENOTFOUND);
 294         }
 295 
 296         if (list.num_nodes <= 0) {
 297                 free(cfgalist);
 298                 return (PICL_NODENOTFOUND);
 299         }
 300 
 301         if ((rc = find_scsi_controller(devfs_path, &list,
 302                 controller)) != PICL_SUCCESS) {
 303                 free(cfgalist);
 304                 free_list(&list);
 305                 return (rc);
 306         }
 307         target_id = strtol(bus_addr, (char **)NULL, 16);
 308         (void) sprintf(name, "%s::dsk/%st%dd0", controller,
 309                 controller, target_id);
 310         free(cfgalist);
 311         free_list(&list);
 312         return (PICL_SUCCESS);
 313 }
 314 
 315 /*
 316  * Arg scsi_loc can be any of the following forms appearing in cfgadm output
 317  *      c0::dsk/c0t0d0
 318  *      c1::sd56
 319  *      c2::rmt/0
 320  *      c3::st41
 321  *      dsk/c1t1d0
 322  *      rmt/1
 323  *      /devices/pci@1f,0/pci@1,1/scsi@2:scsi::dsk/c0t0d0
 324  *
 325  *      On return, bus_addr contains the target id of the device.
 326  *      Please note that currently the target id is computed. It is better
 327  *      to eventually change this to getting from libdevinfo.
 328  *      Also, please note that SCSI_INITIATOR_ID should not
 329  *      be hardcoded, but should be dynamically retrieved from an OBP property.
 330  */
 331 static void
 332 get_bus_addr(char *scsi_loc, char **bus_addr)
 333 {
 334         char *ap, *token, *p, *ap_idp;
 335         int len = 0, i = 0;
 336         char parse_link = 0;
 337         char addr[BUF_SIZE], ap_id[BUF_SIZE];
 338         char fileinfo[BUF_SIZE], ap_id_link[BUF_SIZE];
 339 
 340         (void) strncpy(ap_id, scsi_loc, sizeof (ap_id));
 341         ap = strrchr(ap_id, ':');
 342         if (!ap)
 343                 ap = ap_idp = ap_id;
 344         else
 345                 ap_idp = ++ap;
 346 
 347         while (scsi_dirlink_names[i] && !len) {
 348                 len = strspn(ap, scsi_dirlink_names[i++]);
 349                 /*
 350                  * strspn may return positive len even when there is no
 351                  * complete string matches!!! hence the following check is
 352                  * necessary. So ensure the string match.
 353                  */
 354                 if (len && strstr(ap, scsi_dirlink_names[i-1]))
 355                         break;
 356                 len = 0;
 357         }
 358         if (len)
 359                 parse_link = 1;
 360         else {
 361                 i = 0;
 362                 while (scsi_drv[i].drv_name && !len) {
 363                         len = strspn(ap, scsi_drv[i++].drv_name);
 364                         if (len && strstr(ap, scsi_drv[i-1].drv_name))
 365                                 break;
 366                         len = 0;
 367                 }
 368         }
 369         ap += len;
 370         if (strlen(ap) && parse_link) {
 371 
 372                 /* slice 0 must be present in the system */
 373                 if (strstr(ap, "/c")) {
 374                         if (strstr(ap, "s0") == NULL)
 375                                 (void) strcat(ap, "s0");
 376                 }
 377                 /* get the devlink and read the target id from minor node */
 378                 (void) snprintf(ap_id_link, sizeof (ap_id_link), "/dev/%s",
 379                         ap_idp);
 380                 (void) bzero(fileinfo, sizeof (fileinfo));
 381                 if (readlink(ap_id_link, fileinfo, sizeof (fileinfo)) < 0)
 382                         return;
 383                 if (!fileinfo[0])
 384                         return;
 385                 ap = strrchr(fileinfo, '@');
 386                 ap++;
 387         }
 388         token = (char *)strtok_r(ap, ",", &p);
 389         (void) strncpy(addr, token, sizeof (addr));
 390         if (!parse_link) {
 391                 int drv_inst = atoi(token);
 392                 int tmp_targ_id = drv_inst % scsi_drv[i-1].num_targets;
 393                 int targ_id = scsi_drv[i-1].drv_type == DRV_TYPE_DSK ?
 394                         (tmp_targ_id < SCSI_INITIATOR_ID ?
 395                         tmp_targ_id : tmp_targ_id+1):
 396                         DRV_TYPE_TAPE ? tmp_targ_id : drv_inst;
 397                 (void) snprintf(addr, sizeof (addr), "%d", targ_id);
 398         }
 399         if (strlen(addr)) {
 400                 *bus_addr = (char *)malloc(strlen(addr)+1);
 401                 if ((*bus_addr) == NULL)
 402                         return;
 403                 (void) strcpy((char *)*bus_addr, addr);
 404         }
 405 }
 406 
 407 /*
 408  * This routine determines all the scsi nodes under a FRU and
 409  * creates a subtree of all the scsi nodes with basic properties.
 410  */
 411 static picl_errno_t
 412 dyn_probe_for_scsi_frus(frutree_frunode_t *frup, cfga_list_data_t *cfgalist,
 413         plist_t *list, int numlist)
 414 {
 415         picl_errno_t rc;
 416         int i, geo_addr = 0;
 417         node_t *curr = NULL;
 418         char *bus_addr = NULL;
 419         char path[MAXPATHLEN];
 420         char controller_name[MAXPATHLEN];
 421 
 422         /* for each controller in the list, find if disk/fru is present */
 423         curr = list->first;
 424         while (curr != NULL) {
 425                 /* compare the path */
 426                 (void) snprintf((char *)path, sizeof (path),  "/devices%s",
 427                         frup->fru_path);
 428                 if (strstr(curr->data->ap_phys_id, path) == NULL) {
 429                         curr = curr->next;
 430                         continue;
 431 
 432                 }
 433                 (void) snprintf(controller_name, sizeof (controller_name),
 434                         "%s::", curr->data->ap_log_id);
 435 
 436                 for (i = 0; i < numlist; i++) {
 437                         if (strcmp(cfgalist[i].ap_type, SCSI_SLOT) == 0) {
 438                                 continue;
 439                         }
 440                         if (strstr(cfgalist[i].ap_log_id,
 441                                 controller_name) == NULL) {
 442                                 continue;
 443                         }
 444                         /* check if device is under fru */
 445                         if (strstr(cfgalist[i].ap_phys_id, path) == NULL) {
 446                                 continue;
 447                         }
 448 
 449                         /* we found a scsi fru */
 450                         geo_addr++;
 451                         /* check if the device is present in subtree */
 452                         if (is_location_present_in_subtree(frup,
 453                                 cfgalist[i].ap_log_id, path) == B_TRUE) {
 454                                 continue;
 455                         }
 456                         get_bus_addr(cfgalist[i].ap_log_id, &bus_addr);
 457                         if (bus_addr == NULL) {
 458                                 continue;
 459                         }
 460                         rc = create_children(frup, cfgalist[i].ap_log_id,
 461                                 bus_addr, geo_addr, SANIBEL_SCSI_SLOT, B_TRUE);
 462                         free(bus_addr);
 463                         if (rc != PICL_SUCCESS) {
 464                                 FRUTREE_DEBUG3(FRUTREE_INIT, "SUNW_frutree:"
 465                                 "Error in creating node %s under %s(error=%d)",
 466                                         cfgalist[i].ap_log_id, frup->name, rc);
 467                         }
 468                 }
 469                 curr = curr->next;
 470         }
 471         return (PICL_SUCCESS);
 472 }
 473 
 474 /*
 475  * data used here is cached information (cfglist, nlist)
 476  */
 477 static picl_errno_t
 478 cache_probe_for_scsi_frus(frutree_frunode_t *frup)
 479 {
 480         int i, geo_addr = 0;
 481         picl_errno_t rc;
 482         node_t *curr = NULL;
 483         char path[MAXPATHLEN];
 484         char controller_name[MAXPATHLEN];
 485         char *bus_addr = NULL;
 486 
 487         /* for each controller in the list, find if disk/fru is present */
 488         if (scsi_list == NULL) {
 489                 return (PICL_SUCCESS);
 490         }
 491         curr = scsi_list->first;
 492         while (curr != NULL) {
 493                 /* compare the path */
 494                 (void) snprintf((char *)path, sizeof (path), "/devices%s",
 495                         frup->fru_path);
 496                 if (strstr(curr->data->ap_phys_id, path) == NULL) {
 497                         curr = curr->next;
 498                         continue;
 499                 }
 500                 (void) snprintf(controller_name, sizeof (controller_name),
 501                         "%s::", curr->data->ap_log_id);
 502 
 503                 for (i = 0; i < nlist; i++) {
 504                         if (strcmp(cfglist[i].ap_type, SCSI_SLOT) == 0) {
 505                                 continue;
 506                         }
 507                         if (strstr(cfglist[i].ap_log_id,
 508                                 controller_name) == NULL) {
 509                                 continue;
 510                         }
 511                         /* check if the device is under fru */
 512                         if (strstr(cfglist[i].ap_phys_id, path) == NULL) {
 513                                 continue;
 514                         }
 515 
 516                         /* we found a scsi fru */
 517                         geo_addr++;
 518                         /* check if the device is present in subtree */
 519                         if (is_location_present_in_subtree(frup,
 520                                 cfglist[i].ap_log_id, path) == B_TRUE) {
 521                                 continue;
 522                         }
 523                         get_bus_addr(cfglist[i].ap_log_id, &bus_addr);
 524                         if (bus_addr == NULL) {
 525                                 continue;
 526                         }
 527                         rc = create_children(frup, cfglist[i].ap_log_id,
 528                                 bus_addr, geo_addr, SANIBEL_SCSI_SLOT, B_TRUE);
 529                         free(bus_addr);
 530                         if (rc != PICL_SUCCESS) {
 531                                 FRUTREE_DEBUG3(FRUTREE_INIT, "SUNW_frutree:"
 532                                 "Error in creating node %s under %s(error=%d)",
 533                                         cfglist[i].ap_log_id, frup->name, rc);
 534                         }
 535                 }
 536                 curr = curr->next;
 537         }
 538         return (PICL_SUCCESS);
 539 }
 540 
 541 /*
 542  * This routine checks if the node (scsi device) is present in cfgadm data
 543  * Algorithm:
 544  * 1. traverse thru list of controllers and find
 545  *    the controller of interest
 546  * 2. go thru list of devices under controller and compare if the target is same
 547  * 3. if yes
 548  *      - device is already represented
 549  * 4. if No
 550  *      - The node must be repreented in PICL tree.
 551  */
 552 static boolean_t
 553 is_node_present(scsi_info_t *scsi_info, char *devfs_path, int target)
 554 {
 555         node_t  *curr = NULL;
 556         char    path[MAXPATHLEN];
 557         char    controller[MAXPATHLEN];
 558         char    *bus_addr = NULL;
 559         char    *lasts = NULL, *token = NULL;
 560         int     i = 0;
 561 
 562         if (scsi_info == NULL) {
 563                 return (B_FALSE);
 564         }
 565 
 566         if (scsi_info->list == NULL) {
 567                 return (B_FALSE);
 568         }
 569 
 570         (void) snprintf(path, sizeof (path), "/devices%s", devfs_path);
 571 
 572         curr = scsi_info->list->first;
 573         while (curr != NULL) {
 574 
 575                 lasts = curr->data->ap_phys_id;
 576                 token = (char *)strtok_r(lasts, (const char *)":",
 577                         (char **)&lasts);
 578                 if (token == NULL) {
 579                         curr = curr->next;
 580                         continue;
 581                 }
 582 
 583                 if (strstr(path, token) == NULL) {
 584                         /* this controller is not of interest */
 585                         curr = curr->next;
 586                         continue;
 587                 }
 588 
 589                 (void) snprintf(controller, sizeof (controller), "%s::",
 590                         curr->data->ap_log_id);
 591                 for (i = 0; i < scsi_info->num_list; i++) {
 592                         if (strcmp(scsi_info->cfgalist[i].ap_type,
 593                                 SCSI_SLOT) == 0) {
 594                                 continue;
 595                         }
 596 
 597                         if (strstr(scsi_info->cfgalist[i].ap_log_id,
 598                                 controller) == NULL) {
 599                                 continue;
 600                         }
 601 
 602                         get_bus_addr(scsi_info->cfgalist[i].ap_phys_id,
 603                                 &bus_addr);
 604                         /*
 605                          * compare  with target value
 606                          */
 607                         if (bus_addr == NULL) {
 608                                 return (B_TRUE);
 609                         }
 610                         if (strtoul(bus_addr, NULL, 16) == target) {
 611                                 /*
 612                                  * this device is already represented
 613                                  * in fru tree
 614                                  */
 615                                 free(bus_addr);
 616                                 return (B_TRUE);
 617                         }
 618                         free(bus_addr);
 619                 }
 620                 curr = curr->next;
 621         }
 622         return (B_FALSE);
 623 }
 624 
 625 static di_prop_t
 626 get_prop_by_name(di_node_t node, char *name)
 627 {
 628         di_prop_t prop = DI_PROP_NIL;
 629         char *prop_name = NULL;
 630 
 631         prop = di_prop_next(node, DI_PROP_NIL);
 632         while (prop != DI_PROP_NIL) {
 633                 prop_name = di_prop_name(prop);
 634                 if (prop_name != NULL) {
 635                         if (strcmp(prop_name, name) == 0) {
 636                                 return (prop);
 637                         }
 638                 }
 639                 prop = di_prop_next(node, prop);
 640         }
 641         return (DI_PROP_NIL);
 642 }
 643 
 644 static int
 645 get_geoaddr(picl_nodehdl_t nodeh, void *c_args)
 646 {
 647         picl_errno_t rc;
 648         uint8_t *geo_addr = NULL;
 649         char slot_type[PICL_PROPNAMELEN_MAX];
 650 
 651         if (c_args == NULL)
 652                 return (PICL_INVALIDARG);
 653         geo_addr = (uint8_t *)c_args;
 654 
 655         if ((rc = ptree_get_propval_by_name(nodeh, PICL_PROP_SLOT_TYPE,
 656                 slot_type, sizeof (slot_type))) != PICL_SUCCESS) {
 657                 return (rc);
 658         }
 659 
 660         if (strcmp(slot_type, SANIBEL_SCSI_SLOT) == 0 ||
 661                 strcmp(slot_type, SANIBEL_IDE_SLOT) == 0) {
 662                 *geo_addr = *geo_addr + 1;
 663         }
 664         return (PICL_WALK_CONTINUE);
 665 }
 666 
 667 static int
 668 frutree_get_geoaddr(frutree_frunode_t *frup)
 669 {
 670         int geo_addr = 1;
 671         if (ptree_walk_tree_by_class(frup->frunodeh, PICL_CLASS_LOCATION,
 672                 &geo_addr, get_geoaddr) != PICL_SUCCESS) {
 673                 return (geo_addr);
 674         }
 675         return (geo_addr);
 676 }
 677 
 678 static int
 679 probe_disks(di_node_t node, void *arg)
 680 {
 681         di_prop_t prop;
 682         picl_errno_t rc;
 683         int *target_val = NULL;
 684         char *nodetype = NULL;
 685         char *devfs_path = NULL;
 686         char *bus_addr = NULL;
 687         char *drv_name = NULL;
 688         scsi_info_t *data = NULL;
 689         di_minor_t minor = DI_MINOR_NIL;
 690         char *class = NULL;
 691         char node_name[BUF_SIZE];
 692         char slot_type[PICL_PROPNAMELEN_MAX];
 693 
 694         if (arg == NULL)
 695                 return (DI_WALK_TERMINATE);
 696 
 697         data = *(scsi_info_t **)arg;
 698         if (data == NULL) {
 699                 return (DI_WALK_TERMINATE);
 700         }
 701 
 702         /* initialize the geo_addr value */
 703         if (data->geo_addr == 0) {
 704                 if (data->compare_cfgadm == B_FALSE) {
 705                         data->geo_addr = 1;
 706                 } else {
 707                         data->geo_addr = frutree_get_geoaddr(data->frup);
 708                 }
 709         }
 710 
 711         while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
 712                 nodetype = di_minor_nodetype(minor);
 713                 if (nodetype == NULL) {
 714                         continue;
 715                 }
 716 
 717                 if (strcmp(nodetype, DDI_NT_BLOCK_CHAN) == 0 ||
 718                         strcmp(nodetype, DDI_NT_BLOCK_WWN) == 0) {
 719                         (void) snprintf(node_name, sizeof (node_name),
 720                                 "%s%d", DISK, data->geo_addr);
 721                 } else if (strcmp(nodetype, DDI_NT_TAPE) == 0) {
 722                         (void) snprintf(node_name, sizeof (node_name),
 723                                 "%s%d", RMM, data->geo_addr);
 724                 } else if (strcmp(nodetype, DDI_NT_CD) == 0 ||
 725                         strcmp(nodetype, DDI_NT_CD_CHAN) == 0) {
 726                         (void) snprintf(node_name, sizeof (node_name),
 727                                 "%s%d", CDROM, data->geo_addr);
 728                 } else {
 729                         continue;
 730                 }
 731 
 732                 devfs_path = di_devfs_path(node);
 733                 drv_name = di_driver_name(node);
 734                 bus_addr = di_bus_addr(node);
 735                 if (devfs_path == NULL) {
 736                         continue;
 737                 }
 738                 if (drv_name == NULL || bus_addr == NULL) {
 739                         di_devfs_path_free(devfs_path);
 740                         continue;
 741                 }
 742                 prop = get_prop_by_name(node, TARGET);
 743                 if (prop != DI_PROP_NIL) {
 744                         di_prop_ints(prop, &target_val);
 745                         if (data->compare_cfgadm) {
 746                                 /* check if node is present in cfgadm data */
 747                                 if (is_node_present(data, devfs_path,
 748                                         *target_val) == B_TRUE) {
 749                                         di_devfs_path_free(devfs_path);
 750                                         return (DI_WALK_CONTINUE);
 751                                 }
 752                         }
 753 
 754                         di_devfs_path_free(devfs_path);
 755                         prop = get_prop_by_name(node, CLASS);
 756                         if (prop != DI_PROP_NIL) {
 757                                 di_prop_strings(prop, &class);
 758                         }
 759 
 760                         /* determine the slot type based on class code */
 761                         if (class != NULL) {
 762                                 if (strcmp(class, DEVICE_CLASS_SCSI) == 0) {
 763                                         (void) strncpy(slot_type,
 764                                                 SANIBEL_SCSI_SLOT,
 765                                                 sizeof (slot_type));
 766                                 } else if (strcmp(class,
 767                                         DEVICE_CLASS_IDE) == 0) {
 768                                         (void) strncpy(slot_type,
 769                                                 SANIBEL_IDE_SLOT,
 770                                                 sizeof (slot_type));
 771                                 } else {
 772                                         (void) strncpy(slot_type,
 773                                                 SANIBEL_UNKNOWN_SLOT,
 774                                                 sizeof (slot_type));
 775                                 }
 776 
 777                         } else {
 778                                 (void) strncpy(slot_type, SANIBEL_UNKNOWN_SLOT,
 779                                         sizeof (slot_type));
 780                         }
 781 
 782                         if ((rc = create_children(data->frup, node_name,
 783                                 bus_addr, data->geo_addr, slot_type,
 784                                 B_FALSE)) != PICL_SUCCESS) {
 785                                 return (rc);
 786                         }
 787                         /* increment the geo_addr */
 788                         data->geo_addr++;
 789                 } else {
 790                         di_devfs_path_free(devfs_path);
 791                         continue;
 792                 }
 793                 return (DI_WALK_CONTINUE);
 794         }
 795         return (DI_WALK_CONTINUE);
 796 }
 797 
 798 static picl_errno_t
 799 probe_scsi_in_libdevinfo(frutree_frunode_t *frup, cfga_list_data_t *cfgalist,
 800         plist_t *list, int num_list, boolean_t compare_cfgadm)
 801 {
 802         di_node_t       rnode;
 803         scsi_info_t     *scsi_data = NULL;
 804 
 805         if (frup == NULL) {
 806                 return (PICL_FAILURE);
 807         }
 808 
 809         rnode = di_init(frup->fru_path, DINFOCPYALL);
 810         if (rnode == DI_NODE_NIL) {
 811                 return (PICL_FAILURE);
 812         }
 813 
 814         scsi_data = (scsi_info_t *)malloc(sizeof (scsi_info_t));
 815         if (scsi_data == NULL) {
 816                 di_fini(rnode);
 817                 return (PICL_NOSPACE);
 818         }
 819 
 820         scsi_data->frup = frup;
 821         scsi_data->cfgalist = cfgalist;
 822         scsi_data->list = list;
 823         scsi_data->num_list = num_list;
 824         scsi_data->compare_cfgadm = compare_cfgadm;
 825         scsi_data->geo_addr = 0;
 826         if (di_walk_node(rnode, DI_WALK_CLDFIRST, &scsi_data,
 827                 probe_disks) != 0) {
 828                 free(scsi_data);
 829                 di_fini(rnode);
 830                 return (PICL_FAILURE);
 831         }
 832 
 833         free(scsi_data);
 834         di_fini(rnode);
 835         return (PICL_SUCCESS);
 836 }
 837 
 838 picl_errno_t
 839 probe_for_scsi_frus(frutree_frunode_t *frup)
 840 {
 841         int numlist;
 842         picl_errno_t rc;
 843         plist_t list;
 844         cfga_err_t ap_list_err;
 845         cfga_list_data_t *cfgalist = NULL;
 846 
 847         if (frutree_connects_initiated == B_TRUE) { /* probing after hotswap */
 848                 ap_list_err = config_list_ext(0, NULL, &cfgalist,
 849                         &numlist, NULL, NULL, NULL, CFGA_FLAG_LIST_ALL);
 850 
 851                 if (ap_list_err != CFGA_OK) {
 852                         rc = probe_scsi_in_libdevinfo(frup, NULL, NULL,
 853                                 0, B_FALSE);
 854                         return (rc);
 855                 }
 856 
 857                 /* get list of all controllers in the system */
 858                 ap_list_err = populate_controllers_list(&list, cfgalist,
 859                         numlist);
 860                 if (ap_list_err != CFGA_OK) {
 861                         free_list(&list);
 862                         free(cfgalist);
 863                         rc = probe_scsi_in_libdevinfo(frup, NULL, NULL,
 864                                 0, B_FALSE);
 865                         return (rc);
 866                 }
 867 
 868                 /* no controllers found */
 869                 if (list.num_nodes <= 0) {
 870                         free_list(&list);
 871                         free(cfgalist);
 872                         rc = probe_scsi_in_libdevinfo(frup, NULL, NULL,
 873                                 0, B_FALSE);
 874                         return (rc);
 875                 }
 876                 /*
 877                  * we have to fetch cfgadm, look for scsi controllers
 878                  * dynamically
 879                  */
 880                 (void) dyn_probe_for_scsi_frus(frup, cfgalist, &list, numlist);
 881                 rc = probe_scsi_in_libdevinfo(frup, cfgalist, &list,
 882                         numlist, B_TRUE);
 883                 free_list(&list);
 884                 free(cfgalist);
 885                 return (rc);
 886         } else {
 887                 /* during initialization */
 888                 /* use the cached cfgadm data */
 889                 rc = cache_probe_for_scsi_frus(frup);
 890                 if (scsi_list && scsi_list->num_nodes > 0) {
 891                         rc = probe_scsi_in_libdevinfo(frup, cfglist,
 892                                 scsi_list, nlist, B_TRUE);
 893                 } else {
 894                         rc = probe_scsi_in_libdevinfo(frup, NULL,
 895                                 NULL, 0, B_FALSE);
 896                 }
 897                 return (rc);
 898         }
 899 }