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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include "cfga_scsi.h" 27 28 /* Structure for walking the tree */ 29 typedef struct { 30 apid_t *apidp; 31 char *hba_logp; 32 ldata_list_t *listp; 33 scfga_cmd_t cmd; 34 cfga_stat_t chld_config; 35 cfga_stat_t hba_rstate; 36 scfga_ret_t ret; 37 int l_errno; 38 } scfga_list_t; 39 40 typedef struct { 41 uint_t itype; 42 const char *ntype; 43 const char *name; 44 const char *pathname; 45 } scfga_devtype_t; 46 47 /* The TYPE field is parseable and should not contain spaces */ 48 #define SCFGA_BUS_TYPE "scsi-bus" 49 50 /* Function prototypes */ 51 static scfga_ret_t postprocess_list_data(const ldata_list_t *listp, 52 scfga_cmd_t cmd, cfga_stat_t chld_config, int *np); 53 static int stat_dev(di_node_t node, void *arg); 54 static scfga_ret_t do_stat_bus(scfga_list_t *lap, int limited_bus_stat); 55 static int get_bus_state(di_node_t node, void *arg); 56 57 static scfga_ret_t do_stat_dev(const di_node_t node, const char *nodepath, 58 scfga_list_t *lap, int limited_dev_stat); 59 static cfga_stat_t bus_devinfo_to_recep_state(uint_t bus_di_state); 60 static cfga_stat_t dev_devinfo_to_occupant_state(uint_t dev_di_state); 61 static char *get_device_type(di_node_t, dyncomp_t); 62 static void get_hw_info(di_node_t node, cfga_list_data_t *clp, dyncomp_t type); 63 static scfga_ret_t create_pathinfo_ldata(di_path_t pi_node, scfga_list_t *lap, 64 int *l_errnop); 65 66 67 static scfga_devtype_t device_list[] = { 68 { DTYPE_DIRECT, DDI_NT_BLOCK_CHAN, "disk", "disk-path"}, 69 { DTYPE_DIRECT, DDI_NT_BLOCK, "disk", "disk-path"}, 70 { DTYPE_DIRECT, DDI_NT_BLOCK_WWN, "disk", "disk-path"}, 71 { DTYPE_DIRECT, DDI_NT_BLOCK_FABRIC, "disk", "disk-path"}, 72 { DTYPE_DIRECT, DDI_NT_BLOCK_SAS, "disk", "disk-path"}, 73 { DTYPE_SEQUENTIAL, DDI_NT_TAPE, "tape", "tape-path"}, 74 { DTYPE_PRINTER, NULL, "printer", "printer-path"}, 75 { DTYPE_PROCESSOR, NULL, "processor", "PRCS-path"}, 76 { DTYPE_WORM, NULL, "WORM", "WORM-path"}, 77 { DTYPE_RODIRECT, DDI_NT_CD_CHAN, "CD-ROM", "CD-ROM-path"}, 78 { DTYPE_RODIRECT, DDI_NT_CD, "CD-ROM", "CD-ROM-path"}, 79 { DTYPE_SCANNER, NULL, "scanner", "scanner-path"}, 80 { DTYPE_OPTICAL, NULL, "optical", "optical-path"}, 81 { DTYPE_CHANGER, NULL, "med-changer", "MEDCHGR-path"}, 82 { DTYPE_COMM, NULL, "comm-device", "COMDEV-path"}, 83 { DTYPE_ARRAY_CTRL, NULL, "array-ctrl", "ARRCTRL-path"}, 84 { DTYPE_ESI, NULL, "ESI", "ESI-path"} 85 }; 86 87 #define N_DEVICE_TYPES (sizeof (device_list) / sizeof (device_list[0])) 88 89 scfga_ret_t 90 do_list( 91 apid_t *apidp, 92 scfga_cmd_t cmd, 93 ldata_list_t **llpp, 94 int *nelemp, 95 char **errstring) 96 { 97 int n = -1, l_errno = 0, limited_bus_stat; 98 walkarg_t u; 99 scfga_list_t larg = {NULL}; 100 scfga_ret_t ret; 101 int init_flag; 102 103 assert(apidp->hba_phys != NULL && apidp->path != NULL); 104 105 if (*llpp != NULL || *nelemp != 0) { 106 return (SCFGA_ERR); 107 } 108 109 /* Create the HBA logid (also base component of logical ap_id) */ 110 ret = make_hba_logid(apidp->hba_phys, &larg.hba_logp, &l_errno); 111 if (ret != SCFGA_OK) { 112 cfga_err(errstring, l_errno, ERR_LIST, 0); 113 return (SCFGA_ERR); 114 } 115 116 assert(larg.hba_logp != NULL); 117 118 larg.cmd = cmd; 119 larg.apidp = apidp; 120 larg.hba_rstate = CFGA_STAT_NONE; 121 122 123 /* 124 * For all list commands, the bus and 1 or more devices 125 * needs to be stat'ed 126 */ 127 128 /* 129 * By default we use DINFOCACHE to get a "full" snapshot 130 * This much faster than DINFOFORCE which actually 131 * attaches devices. DINFOFORCE used only if caller 132 * explicitly requests it via a private option. 133 */ 134 init_flag = (apidp->flags & FLAG_USE_DIFORCE) ? DINFOFORCE : DINFOCACHE; 135 limited_bus_stat = 0; 136 137 switch (larg.cmd) { 138 case SCFGA_STAT_DEV: 139 limited_bus_stat = 1; /* We need only bus state */ 140 /*FALLTHRU*/ 141 case SCFGA_STAT_ALL: 142 break; 143 case SCFGA_STAT_BUS: 144 /* limited_bus_stat = 0 and no DINFOCACHE/DINFOFORCE */ 145 init_flag = 0; 146 break; 147 default: 148 cfga_err(errstring, EINVAL, ERR_LIST, 0); 149 goto out; 150 } 151 152 /* 153 * DINFOCACHE implies DINFOCPYALL. DINFOCPYALL shouldn't 154 * be ORed with DINFOCACHE, else libdevinfo will return 155 * error 156 */ 157 if (init_flag != DINFOCACHE) 158 init_flag |= DINFOCPYALL; 159 160 if ((ret = do_stat_bus(&larg, limited_bus_stat)) != SCFGA_OK) { 161 cfga_err(errstring, larg.l_errno, ERR_LIST, 0); 162 goto out; 163 } 164 165 #ifdef DEBUG 166 if (limited_bus_stat) { 167 assert(larg.listp == NULL); 168 } else { 169 assert(larg.listp != NULL); 170 } 171 #endif 172 173 /* Assume that the bus has no configured children */ 174 larg.chld_config = CFGA_STAT_UNCONFIGURED; 175 176 /* 177 * If stat'ing a specific device, we don't know if it exists yet. 178 * If stat'ing a bus or a bus and child devices, we have at least the 179 * bus stat data at this point. 180 */ 181 if (larg.cmd == SCFGA_STAT_DEV) { 182 larg.ret = SCFGA_APID_NOEXIST; 183 } else { 184 larg.ret = SCFGA_OK; 185 } 186 187 /* we need to stat at least 1 device for all commands */ 188 if (apidp->dyntype == PATH_APID) { 189 /* 190 * When cmd is SCFGA_STAT_DEV and the ap id is pathinfo 191 * related. 192 */ 193 ret = walk_tree(apidp->hba_phys, &larg, init_flag, NULL, 194 SCFGA_WALK_PATH, &larg.l_errno); 195 } else { 196 /* we need to stat at least 1 device for all commands */ 197 u.node_args.flags = DI_WALK_CLDFIRST; 198 u.node_args.fcn = stat_dev; 199 200 /* 201 * Subtree is ALWAYS rooted at the HBA (not at the device) as 202 * otherwise deadlock may occur if bus is disconnected. 203 */ 204 ret = walk_tree(apidp->hba_phys, &larg, init_flag, &u, 205 SCFGA_WALK_NODE, &larg.l_errno); 206 207 /* 208 * Check path info on the following conditions. 209 * 210 * - chld_config is still set to CFGA_STAT_UNCONFIGURED for 211 * SCFGA_STAT_BUS cmd after walking any child node. 212 * - walking node succeeded for SCFGA_STAT_ALL cmd(Continue on 213 * stating path info node). 214 * - apid is pathinfo associated and larg.ret is still set to 215 * SCFGA_APID_NOEXIST for SCFGA_STAT_DEV cmd. 216 */ 217 if (((cmd == SCFGA_STAT_BUS) && 218 (larg.chld_config == CFGA_STAT_UNCONFIGURED)) || 219 ((cmd == SCFGA_STAT_ALL) && (ret == SCFGA_OK))) { 220 ret = walk_tree(apidp->hba_phys, &larg, init_flag, NULL, 221 SCFGA_WALK_PATH, &larg.l_errno); 222 } 223 } 224 225 if (ret != SCFGA_OK || (ret = larg.ret) != SCFGA_OK) { 226 if (ret != SCFGA_APID_NOEXIST) { 227 cfga_err(errstring, larg.l_errno, ERR_LIST, 0); 228 } 229 goto out; 230 } 231 232 assert(larg.listp != NULL); 233 234 n = 0; 235 ret = postprocess_list_data(larg.listp, cmd, larg.chld_config, &n); 236 if (ret != SCFGA_OK) { 237 cfga_err(errstring, 0, ERR_LIST, 0); 238 ret = SCFGA_LIB_ERR; 239 goto out; 240 } 241 242 *nelemp = n; 243 *llpp = larg.listp; 244 ret = SCFGA_OK; 245 /* FALLTHROUGH */ 246 out: 247 if (ret != SCFGA_OK) list_free(&larg.listp); 248 S_FREE(larg.hba_logp); 249 return (ret); 250 } 251 252 static scfga_ret_t 253 postprocess_list_data( 254 const ldata_list_t *listp, 255 scfga_cmd_t cmd, 256 cfga_stat_t chld_config, 257 int *np) 258 { 259 ldata_list_t *tmplp = NULL; 260 cfga_list_data_t *hba_ldatap = NULL; 261 int i; 262 263 264 *np = 0; 265 266 if (listp == NULL) { 267 return (SCFGA_ERR); 268 } 269 270 hba_ldatap = NULL; 271 tmplp = (ldata_list_t *)listp; 272 for (i = 0; tmplp != NULL; tmplp = tmplp->next) { 273 i++; 274 if (GET_DYN(tmplp->ldata.ap_phys_id) == NULL) { 275 /* A bus stat data */ 276 assert(GET_DYN(tmplp->ldata.ap_log_id) == NULL); 277 hba_ldatap = &tmplp->ldata; 278 #ifdef DEBUG 279 } else { 280 assert(GET_DYN(tmplp->ldata.ap_log_id) != NULL); 281 #endif 282 } 283 } 284 285 switch (cmd) { 286 case SCFGA_STAT_DEV: 287 if (i != 1 || hba_ldatap != NULL) { 288 return (SCFGA_LIB_ERR); 289 } 290 break; 291 case SCFGA_STAT_BUS: 292 if (i != 1 || hba_ldatap == NULL) { 293 return (SCFGA_LIB_ERR); 294 } 295 break; 296 case SCFGA_STAT_ALL: 297 if (i < 1 || hba_ldatap == NULL) { 298 return (SCFGA_LIB_ERR); 299 } 300 break; 301 default: 302 return (SCFGA_LIB_ERR); 303 } 304 305 *np = i; 306 307 /* Fill in the occupant (child) state. */ 308 if (hba_ldatap != NULL) { 309 hba_ldatap->ap_o_state = chld_config; 310 } 311 return (SCFGA_OK); 312 } 313 314 static int 315 stat_dev(di_node_t node, void *arg) 316 { 317 scfga_list_t *lap = NULL; 318 char *devfsp = NULL, *nodepath = NULL; 319 size_t len = 0; 320 int limited_dev_stat = 0, match_minor, rv; 321 scfga_ret_t ret; 322 323 lap = (scfga_list_t *)arg; 324 325 /* Skip stub nodes */ 326 if (IS_STUB_NODE(node)) { 327 return (DI_WALK_CONTINUE); 328 } 329 330 /* Skip partial nodes */ 331 if (!known_state(node)) { 332 return (DI_WALK_CONTINUE); 333 } 334 335 devfsp = di_devfs_path(node); 336 if (devfsp == NULL) { 337 rv = DI_WALK_CONTINUE; 338 goto out; 339 } 340 341 len = strlen(DEVICES_DIR) + strlen(devfsp) + 1; 342 343 nodepath = calloc(1, len); 344 if (nodepath == NULL) { 345 lap->l_errno = errno; 346 lap->ret = SCFGA_LIB_ERR; 347 rv = DI_WALK_TERMINATE; 348 goto out; 349 } 350 351 (void) snprintf(nodepath, len, "%s%s", DEVICES_DIR, devfsp); 352 353 /* Skip node if it is HBA */ 354 match_minor = 0; 355 if (!dev_cmp(lap->apidp->hba_phys, nodepath, match_minor)) { 356 rv = DI_WALK_CONTINUE; 357 goto out; 358 } 359 360 /* If stat'ing a specific device, is this that device */ 361 if (lap->cmd == SCFGA_STAT_DEV) { 362 assert(lap->apidp->path != NULL); 363 if (dev_cmp(lap->apidp->path, nodepath, match_minor)) { 364 rv = DI_WALK_CONTINUE; 365 goto out; 366 } 367 } 368 369 /* 370 * If stat'ing a bus only, we look at device nodes only to get 371 * bus configuration status. So a limited stat will suffice. 372 */ 373 if (lap->cmd == SCFGA_STAT_BUS) { 374 limited_dev_stat = 1; 375 } else { 376 limited_dev_stat = 0; 377 } 378 379 /* 380 * Ignore errors if stat'ing a bus or listing all 381 */ 382 ret = do_stat_dev(node, nodepath, lap, limited_dev_stat); 383 if (ret != SCFGA_OK) { 384 if (lap->cmd == SCFGA_STAT_DEV) { 385 lap->ret = ret; 386 rv = DI_WALK_TERMINATE; 387 } else { 388 rv = DI_WALK_CONTINUE; 389 } 390 goto out; 391 } 392 393 /* Are we done ? */ 394 rv = DI_WALK_CONTINUE; 395 if (lap->cmd == SCFGA_STAT_BUS && 396 lap->chld_config == CFGA_STAT_CONFIGURED) { 397 rv = DI_WALK_TERMINATE; 398 } else if (lap->cmd == SCFGA_STAT_DEV) { 399 /* 400 * If stat'ing a specific device, we are done at this point. 401 */ 402 lap->ret = SCFGA_OK; 403 rv = DI_WALK_TERMINATE; 404 } 405 406 /*FALLTHRU*/ 407 out: 408 S_FREE(nodepath); 409 if (devfsp != NULL) di_devfs_path_free(devfsp); 410 return (rv); 411 } 412 413 /* 414 * Create list date entry and add to ldata list. 415 */ 416 static scfga_ret_t 417 create_pathinfo_ldata(di_path_t pi_node, scfga_list_t *lap, int *l_errnop) 418 { 419 ldata_list_t *listp = NULL; 420 cfga_list_data_t *clp; 421 di_node_t client_node = DI_NODE_NIL; 422 di_minor_t minor; 423 scfga_ret_t ret; 424 di_path_state_t pi_state; 425 char *dyncomp = NULL, *client_path = NULL; 426 char pathbuf[MAXPATHLEN], *client_devlink = NULL; 427 int match_minor; 428 429 listp = calloc(1, sizeof (ldata_list_t)); 430 if (listp == NULL) { 431 lap->l_errno = errno; 432 return (SCFGA_LIB_ERR); 433 } 434 clp = &listp->ldata; 435 ret = make_path_dyncomp(pi_node, &dyncomp, &lap->l_errno); 436 if (ret != SCFGA_OK) { 437 S_FREE(listp); 438 return (ret); 439 } 440 441 client_node = di_path_client_node(pi_node); 442 if (client_node == DI_NODE_NIL) { 443 *l_errnop = errno; 444 S_FREE(dyncomp); 445 return (SCFGA_LIB_ERR); 446 } 447 448 /* Create logical and physical ap_id */ 449 (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s", 450 lap->hba_logp, DYN_SEP, dyncomp); 451 452 (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s", 453 lap->apidp->hba_phys, DYN_SEP, dyncomp); 454 455 S_FREE(dyncomp); 456 457 /* ap class filled in by libcfgadm */ 458 clp->ap_class[0] = '\0'; 459 clp->ap_r_state = lap->hba_rstate; 460 /* path info exist so set to configured. */ 461 clp->ap_o_state = CFGA_STAT_CONFIGURED; 462 463 /* now fill up ap_info field with client dev link and instance #. */ 464 client_path = di_devfs_path(client_node); 465 if (client_path) { 466 /* get first minor node. */ 467 minor = di_minor_next(client_node, DI_MINOR_NIL); 468 if (minor == DI_MINOR_NIL) { 469 match_minor = 0; 470 (void) snprintf(pathbuf, MAXPATHLEN, "%s:%s", 471 DEVICES_DIR, client_path); 472 } else { 473 match_minor = 1; 474 (void) snprintf(pathbuf, MAXPATHLEN, "%s%s:%s", 475 DEVICES_DIR, client_path, di_minor_name(minor)); 476 } 477 (void) physpath_to_devlink(pathbuf, &client_devlink, l_errnop, 478 match_minor); 479 di_devfs_path_free(client_path); 480 } 481 482 if (client_devlink) { 483 (void) snprintf(clp->ap_info, CFGA_INFO_LEN, 484 "%s: %s", "Client Device", client_devlink); 485 S_FREE(client_devlink); 486 } 487 488 get_hw_info(client_node, clp, PATH_APID); 489 490 if ((pi_state = di_path_state(pi_node)) == DI_PATH_STATE_OFFLINE) { 491 clp->ap_o_state = CFGA_STAT_UNCONFIGURED; 492 } 493 494 if (pi_state == DI_PATH_STATE_FAULT) { 495 clp->ap_cond = CFGA_COND_FAILED; 496 } else { 497 clp->ap_cond = CFGA_COND_UNKNOWN; 498 } 499 500 /* no way to determine state change */ 501 clp->ap_busy = 0; 502 clp->ap_status_time = (time_t)-1; 503 504 /* Link it in */ 505 listp->next = lap->listp; 506 lap->listp = listp; 507 508 return (SCFGA_OK); 509 } 510 511 /* 512 * Routine to stat pathinfo nodes. 513 * 514 * No pathinfo founds returns a success. 515 * When cmd is SCFGA_STAT_DEV, finds a matching pathinfo node and 516 * and create ldata if found. 517 * When cmd is SCFGA_STAT_ALL, create ldata for each pathinfo node. 518 * When cmd is SCFGA_STAT_BUS, checks if any pathinfo exist. 519 * 520 * Return: 521 * 0 for success 522 * -1 for failure. 523 */ 524 int 525 stat_path_info( 526 di_node_t root, 527 void *arg, 528 int *l_errnop) 529 { 530 scfga_list_t *lap = (scfga_list_t *)arg; 531 di_path_t pi_node; 532 533 if (root == DI_NODE_NIL) { 534 return (-1); 535 } 536 537 /* 538 * when there is no path_info node return SCFGA_OK. 539 */ 540 if (di_path_next_client(root, DI_PATH_NIL) == DI_PATH_NIL) { 541 return (0); 542 } 543 544 if (lap->cmd == SCFGA_STAT_BUS) { 545 lap->chld_config = CFGA_STAT_CONFIGURED; 546 return (0); 547 } else if (lap->cmd == SCFGA_STAT_DEV) { 548 assert(lap->apidp->dyntype == PATH_APID); 549 for (pi_node = di_path_next_client(root, DI_PATH_NIL); pi_node; 550 pi_node = di_path_next_client(root, pi_node)) { 551 /* 552 * NOTE: apidt_create() validated pathinfo apid so 553 * the apid should have a valid format. 554 */ 555 556 /* check the length first. */ 557 if (strlen(di_path_bus_addr(pi_node)) != 558 strlen(lap->apidp->dyncomp)) { 559 continue; 560 } 561 562 /* check for full match. */ 563 if (strcmp(di_path_bus_addr(pi_node), 564 lap->apidp->dyncomp)) { 565 continue; 566 } 567 568 /* found match, record information */ 569 if (create_pathinfo_ldata(pi_node, lap, 570 l_errnop) == SCFGA_OK) { 571 lap->ret = SCFGA_OK; 572 return (0); 573 } else { 574 return (-1); 575 } 576 } 577 } else { /* cmd = STAT_ALL */ 578 /* set child config to configured */ 579 lap->chld_config = CFGA_STAT_CONFIGURED; 580 for (pi_node = di_path_next_client(root, DI_PATH_NIL); pi_node; 581 pi_node = di_path_next_client(root, pi_node)) { 582 /* continue on even if there is an error on one path. */ 583 (void) create_pathinfo_ldata(pi_node, lap, l_errnop); 584 } 585 } 586 587 lap->ret = SCFGA_OK; 588 return (0); 589 590 } 591 592 struct bus_state { 593 int b_state; 594 int b_retired; 595 char iconnect_type[16]; 596 }; 597 598 static scfga_ret_t 599 do_stat_bus(scfga_list_t *lap, int limited_bus_stat) 600 { 601 cfga_list_data_t *clp = NULL; 602 ldata_list_t *listp = NULL; 603 int l_errno = 0; 604 struct bus_state bstate = {0}; 605 walkarg_t u; 606 scfga_ret_t ret; 607 int i; 608 char itypelower[MAXNAMELEN]; 609 610 assert(lap->hba_logp != NULL); 611 612 /* Get bus state */ 613 u.node_args.flags = 0; 614 u.node_args.fcn = get_bus_state; 615 616 ret = walk_tree(lap->apidp->hba_phys, &bstate, DINFOPROP, &u, 617 SCFGA_WALK_NODE, &l_errno); 618 if (ret == SCFGA_OK) { 619 lap->hba_rstate = bus_devinfo_to_recep_state(bstate.b_state); 620 } else { 621 lap->hba_rstate = CFGA_STAT_NONE; 622 } 623 624 if (limited_bus_stat) { 625 /* We only want to know bus(receptacle) connect status */ 626 return (SCFGA_OK); 627 } 628 629 listp = calloc(1, sizeof (ldata_list_t)); 630 if (listp == NULL) { 631 lap->l_errno = errno; 632 return (SCFGA_LIB_ERR); 633 } 634 635 clp = &listp->ldata; 636 637 (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s", 638 lap->hba_logp); 639 (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s", 640 lap->apidp->hba_phys); 641 642 clp->ap_class[0] = '\0'; /* Filled by libcfgadm */ 643 clp->ap_r_state = lap->hba_rstate; 644 clp->ap_o_state = CFGA_STAT_NONE; /* filled in later by the plug-in */ 645 clp->ap_cond = 646 (bstate.b_retired) ? CFGA_COND_FAILED : CFGA_COND_UNKNOWN; 647 clp->ap_busy = 0; 648 clp->ap_status_time = (time_t)-1; 649 clp->ap_info[0] = '\0'; 650 651 if (bstate.iconnect_type) { 652 /* 653 * For SPI type, keep the existing SCFGA_BUS_TYPE. 654 * For other types, the ap type will be scsi-'interconnct-type'. 655 */ 656 if (strcmp(bstate.iconnect_type, "SPI") == 0) { 657 (void) snprintf(clp->ap_type, sizeof (clp->ap_type), 658 "%s", SCFGA_BUS_TYPE); 659 } else { 660 for (i = 0; i < strlen(bstate.iconnect_type); i++) { 661 itypelower[i] = 662 tolower(bstate.iconnect_type[i]); 663 } 664 itypelower[i] = '\0'; 665 (void) snprintf(clp->ap_type, sizeof (clp->ap_type), 666 "%s-%s", "scsi", itypelower); 667 } 668 } 669 670 /* Link it in */ 671 listp->next = lap->listp; 672 lap->listp = listp; 673 674 return (SCFGA_OK); 675 } 676 677 static int 678 get_bus_state(di_node_t node, void *arg) 679 { 680 struct bus_state *bsp = (struct bus_state *)arg; 681 char *itype = NULL; 682 683 bsp->b_state = di_state(node); 684 bsp->b_retired = di_retired(node); 685 (void) di_prop_lookup_strings(DDI_DEV_T_ANY, 686 node, "initiator-interconnect-type", &itype); 687 if (itype != NULL) { 688 (void) strlcpy(bsp->iconnect_type, itype, 16); 689 } else { 690 bsp->iconnect_type[0] = '\0'; 691 } 692 693 return (DI_WALK_TERMINATE); 694 } 695 696 static scfga_ret_t 697 do_stat_dev( 698 const di_node_t node, 699 const char *nodepath, 700 scfga_list_t *lap, 701 int limited_dev_stat) 702 { 703 uint_t devinfo_state = 0; 704 char *dyncomp = NULL; 705 cfga_list_data_t *clp = NULL; 706 ldata_list_t *listp = NULL; 707 cfga_stat_t ostate; 708 scfga_ret_t ret; 709 710 assert(lap->apidp->hba_phys != NULL); 711 assert(lap->hba_logp != NULL); 712 713 devinfo_state = di_state(node); 714 ostate = dev_devinfo_to_occupant_state(devinfo_state); 715 716 /* If child device is configured, record it */ 717 if (ostate == CFGA_STAT_CONFIGURED) { 718 lap->chld_config = CFGA_STAT_CONFIGURED; 719 } 720 721 if (limited_dev_stat) { 722 /* We only want to know device config state */ 723 return (SCFGA_OK); 724 } 725 726 listp = calloc(1, sizeof (ldata_list_t)); 727 if (listp == NULL) { 728 lap->l_errno = errno; 729 return (SCFGA_LIB_ERR); 730 } 731 732 clp = &listp->ldata; 733 734 /* Create the dynamic component */ 735 ret = make_dyncomp(node, nodepath, &dyncomp, &lap->l_errno); 736 if (ret != SCFGA_OK) { 737 S_FREE(listp); 738 return (ret); 739 } 740 741 assert(dyncomp != NULL); 742 743 /* Create logical and physical ap_id */ 744 (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s", 745 lap->hba_logp, DYN_SEP, dyncomp); 746 747 (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s", 748 lap->apidp->hba_phys, DYN_SEP, dyncomp); 749 750 S_FREE(dyncomp); 751 752 clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */ 753 clp->ap_r_state = lap->hba_rstate; 754 clp->ap_o_state = ostate; 755 clp->ap_cond = di_retired(node) ? CFGA_COND_FAILED : CFGA_COND_UNKNOWN; 756 clp->ap_busy = 0; /* no way to determine state change */ 757 clp->ap_status_time = (time_t)-1; 758 759 get_hw_info(node, clp, DEV_APID); 760 761 /* Link it in */ 762 listp->next = lap->listp; 763 lap->listp = listp; 764 765 return (SCFGA_OK); 766 } 767 768 /* fill in device type, vid, pid from properties */ 769 static void 770 get_hw_info(di_node_t node, cfga_list_data_t *clp, dyncomp_t type) 771 { 772 char *cp = NULL; 773 char *inq_vid, *inq_pid; 774 char client_inst[MAXNAMELEN]; 775 776 /* 777 * Fill in type information 778 */ 779 cp = (char *)get_device_type(node, type); 780 if (cp == NULL) { 781 cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE); 782 } 783 (void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s", S_STR(cp)); 784 785 if (type == DEV_APID) { 786 /* 787 * Fill in vendor and product ID. 788 */ 789 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node, 790 "inquiry-product-id", &inq_pid) == 1) && 791 (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 792 "inquiry-vendor-id", &inq_vid) == 1)) { 793 (void) snprintf(clp->ap_info, sizeof (clp->ap_info), 794 "%s %s", inq_vid, inq_pid); 795 } 796 } else { 797 if ((di_driver_name(node) != NULL) && 798 (di_instance(node) != -1)) { 799 if (clp->ap_info == NULL) { 800 (void) snprintf(client_inst, MAXNAMELEN - 1, 801 "%s%d", di_driver_name(node), 802 di_instance(node)); 803 (void) snprintf(clp->ap_info, MAXNAMELEN - 1, 804 "Client Device: %s", client_inst); 805 } else { 806 (void) snprintf(client_inst, MAXNAMELEN - 1, 807 "(%s%d)", di_driver_name(node), 808 di_instance(node)); 809 (void) strlcat(clp->ap_info, client_inst, 810 CFGA_INFO_LEN); 811 } 812 } 813 814 } 815 } 816 817 /* 818 * Get dtype from "inquiry-device-type" property. If not present, 819 * derive it from minor node type 820 */ 821 static char * 822 get_device_type(di_node_t node, dyncomp_t type) 823 { 824 char *name = NULL; 825 int *inq_dtype; 826 int i; 827 828 if (di_prop_find(DDI_DEV_T_ANY, node, "smp-device") != DI_PROP_NIL) { 829 return ("smp"); 830 } 831 832 /* first, derive type based on inquiry property */ 833 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type", 834 &inq_dtype) == 1) { 835 int itype = (*inq_dtype) & DTYPE_MASK; 836 837 for (i = 0; i < N_DEVICE_TYPES; i++) { 838 if (device_list[i].itype == DTYPE_UNKNOWN) 839 continue; 840 if (itype == device_list[i].itype) { 841 name = (type == DEV_APID) ? 842 (char *)device_list[i].name : 843 (char *)device_list[i].pathname; 844 break; 845 } 846 } 847 } 848 849 /* if property fails, use minor nodetype */ 850 if (name == NULL) { 851 char *nodetype; 852 di_minor_t minor = di_minor_next(node, DI_MINOR_NIL); 853 854 if ((minor != DI_MINOR_NIL) && 855 ((nodetype = di_minor_nodetype(minor)) != NULL)) { 856 for (i = 0; i < N_DEVICE_TYPES; i++) { 857 if (device_list[i].ntype && 858 (strcmp(nodetype, device_list[i].ntype) 859 == 0)) { 860 name = (type == DEV_APID) ? 861 (char *)device_list[i].name : 862 (char *)device_list[i].pathname; 863 break; 864 } 865 } 866 } 867 } 868 869 if (name == NULL) /* default to unknown */ 870 name = "unknown"; 871 return (name); 872 } 873 874 /* Transform linked list into an array */ 875 scfga_ret_t 876 list_ext_postprocess( 877 ldata_list_t **llpp, 878 int nelem, 879 cfga_list_data_t **ap_id_list, 880 int *nlistp, 881 char **errstring) 882 { 883 cfga_list_data_t *ldatap = NULL; 884 ldata_list_t *tmplp = NULL; 885 int i = -1; 886 887 *ap_id_list = NULL; 888 *nlistp = 0; 889 890 if (*llpp == NULL || nelem < 0) { 891 return (SCFGA_LIB_ERR); 892 } 893 894 if (nelem == 0) { 895 return (SCFGA_APID_NOEXIST); 896 } 897 898 ldatap = calloc(nelem, sizeof (cfga_list_data_t)); 899 if (ldatap == NULL) { 900 cfga_err(errstring, errno, ERR_LIST, 0); 901 return (SCFGA_LIB_ERR); 902 } 903 904 /* Extract the list_data structures from the linked list */ 905 tmplp = *llpp; 906 for (i = 0; i < nelem && tmplp != NULL; i++) { 907 ldatap[i] = tmplp->ldata; 908 tmplp = tmplp->next; 909 } 910 911 if (i < nelem || tmplp != NULL) { 912 S_FREE(ldatap); 913 return (SCFGA_LIB_ERR); 914 } 915 916 *nlistp = nelem; 917 *ap_id_list = ldatap; 918 919 return (SCFGA_OK); 920 } 921 922 /* 923 * Convert bus state to receptacle state 924 */ 925 static cfga_stat_t 926 bus_devinfo_to_recep_state(uint_t bus_di_state) 927 { 928 if (bus_di_state & (DI_BUS_QUIESCED | DI_BUS_DOWN)) 929 return (CFGA_STAT_DISCONNECTED); 930 931 return (CFGA_STAT_CONNECTED); 932 } 933 934 /* 935 * Convert device state to occupant state 936 */ 937 static cfga_stat_t 938 dev_devinfo_to_occupant_state(uint_t dev_di_state) 939 { 940 if (dev_di_state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN)) 941 return (CFGA_STAT_UNCONFIGURED); 942 943 if (!(dev_di_state & DI_DRIVER_DETACHED)) 944 return (CFGA_STAT_CONFIGURED); 945 946 return (CFGA_STAT_NONE); 947 } 948