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 2004 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 #include "cfga_scsi.h" 30 31 /* Structure for walking the tree */ 32 typedef struct { 33 apid_t *apidp; 34 char *hba_logp; 35 ldata_list_t *listp; 36 scfga_cmd_t cmd; 37 cfga_stat_t chld_config; 38 cfga_stat_t hba_rstate; 39 scfga_ret_t ret; 40 int l_errno; 41 } scfga_list_t; 42 43 typedef struct { 44 uint_t itype; 45 const char *ntype; 46 const char *name; 47 } scfga_devtype_t; 48 49 /* The TYPE field is parseable and should not contain spaces */ 50 #define SCFGA_BUS_TYPE "scsi-bus" 51 52 /* Function prototypes */ 53 static scfga_ret_t postprocess_list_data(const ldata_list_t *listp, 54 scfga_cmd_t cmd, cfga_stat_t chld_config, int *np); 55 static int stat_dev(di_node_t node, void *arg); 56 static scfga_ret_t do_stat_bus(scfga_list_t *lap, int limited_bus_stat); 57 static int get_bus_state(di_node_t node, void *arg); 58 59 static scfga_ret_t do_stat_dev(const di_node_t node, const char *nodepath, 60 scfga_list_t *lap, int limited_dev_stat); 61 static cfga_stat_t bus_devinfo_to_recep_state(uint_t bus_di_state); 62 static cfga_stat_t dev_devinfo_to_occupant_state(uint_t dev_di_state); 63 static char *get_device_type(di_node_t); 64 static void get_hw_info(di_node_t node, cfga_list_data_t *clp); 65 66 67 static scfga_devtype_t device_list[] = { 68 { DTYPE_DIRECT, DDI_NT_BLOCK_CHAN, "disk"}, 69 { DTYPE_DIRECT, DDI_NT_BLOCK, "disk"}, 70 { DTYPE_DIRECT, DDI_NT_BLOCK_WWN, "disk"}, 71 { DTYPE_DIRECT, DDI_NT_BLOCK_FABRIC, "disk"}, 72 { DTYPE_SEQUENTIAL, DDI_NT_TAPE, "tape"}, 73 { DTYPE_PRINTER, NULL, "printer"}, 74 { DTYPE_PROCESSOR, NULL, "processor"}, 75 { DTYPE_WORM, NULL, "WORM"}, 76 { DTYPE_RODIRECT, DDI_NT_CD_CHAN, "CD-ROM"}, 77 { DTYPE_RODIRECT, DDI_NT_CD, "CD-ROM"}, 78 { DTYPE_SCANNER, NULL, "scanner"}, 79 { DTYPE_OPTICAL, NULL, "optical"}, 80 { DTYPE_CHANGER, NULL, "med-changer"}, 81 { DTYPE_COMM, NULL, "comm-device"}, 82 { DTYPE_ARRAY_CTRL, NULL, "array-ctrl"}, 83 { DTYPE_ESI, NULL, "ESI"} 84 }; 85 86 #define N_DEVICE_TYPES (sizeof (device_list) / sizeof (device_list[0])) 87 88 scfga_ret_t 89 do_list( 90 apid_t *apidp, 91 scfga_cmd_t cmd, 92 ldata_list_t **llpp, 93 int *nelemp, 94 char **errstring) 95 { 96 int n = -1, l_errno = 0, limited_bus_stat; 97 walkarg_t u; 98 scfga_list_t larg = {NULL}; 99 scfga_ret_t ret; 100 int init_flag; 101 102 assert(apidp->hba_phys != NULL && apidp->path != NULL); 103 104 if (*llpp != NULL || *nelemp != 0) { 105 return (SCFGA_ERR); 106 } 107 108 /* Create the HBA logid (also base component of logical ap_id) */ 109 ret = make_hba_logid(apidp->hba_phys, &larg.hba_logp, &l_errno); 110 if (ret != SCFGA_OK) { 111 cfga_err(errstring, l_errno, ERR_LIST, 0); 112 return (SCFGA_ERR); 113 } 114 115 assert(larg.hba_logp != NULL); 116 117 larg.cmd = cmd; 118 larg.apidp = apidp; 119 larg.hba_rstate = CFGA_STAT_NONE; 120 121 122 /* 123 * For all list commands, the bus and 1 or more devices 124 * needs to be stat'ed 125 */ 126 127 /* 128 * By default we use DINFOCACHE to get a "full" snapshot 129 * This much faster than DINFOFORCE which actually 130 * attaches devices. DINFOFORCE used only if caller 131 * explicitly requests it via a private option. 132 */ 133 init_flag = (apidp->flags & FLAG_USE_DIFORCE) ? DINFOFORCE : DINFOCACHE; 134 limited_bus_stat = 0; 135 136 switch (larg.cmd) { 137 case SCFGA_STAT_DEV: 138 limited_bus_stat = 1; /* We need only bus state */ 139 /*FALLTHRU*/ 140 case SCFGA_STAT_ALL: 141 break; 142 case SCFGA_STAT_BUS: 143 /* limited_bus_stat = 0 and no DINFOCACHE/DINFOFORCE */ 144 init_flag = 0; 145 break; 146 default: 147 cfga_err(errstring, EINVAL, ERR_LIST, 0); 148 goto out; 149 } 150 151 /* 152 * DINFOCACHE implies DINFOCPYALL. DINFOCPYALL shouldn't 153 * be ORed with DINFOCACHE, else libdevinfo will return 154 * error 155 */ 156 if (init_flag != DINFOCACHE) 157 init_flag |= DINFOCPYALL; 158 159 if ((ret = do_stat_bus(&larg, limited_bus_stat)) != SCFGA_OK) { 160 cfga_err(errstring, larg.l_errno, ERR_LIST, 0); 161 goto out; 162 } 163 164 #ifdef DEBUG 165 if (limited_bus_stat) { 166 assert(larg.listp == NULL); 167 } else { 168 assert(larg.listp != NULL); 169 } 170 #endif 171 172 /* Assume that the bus has no configured children */ 173 larg.chld_config = CFGA_STAT_UNCONFIGURED; 174 175 /* 176 * If stat'ing a specific device, we don't know if it exists yet. 177 * If stat'ing a bus or a bus and child devices, we have at least the 178 * bus stat data at this point. 179 */ 180 if (larg.cmd == SCFGA_STAT_DEV) { 181 larg.ret = SCFGA_APID_NOEXIST; 182 } else { 183 larg.ret = SCFGA_OK; 184 } 185 186 /* we need to stat at least 1 device for all commands */ 187 u.node_args.flags = DI_WALK_CLDFIRST; 188 u.node_args.fcn = stat_dev; 189 190 /* 191 * Subtree is ALWAYS rooted at the HBA (not at the device) as 192 * otherwise deadlock may occur if bus is disconnected. 193 */ 194 ret = walk_tree(apidp->hba_phys, &larg, init_flag, &u, 195 SCFGA_WALK_NODE, &larg.l_errno); 196 197 if (ret != SCFGA_OK || (ret = larg.ret) != SCFGA_OK) { 198 if (ret != SCFGA_APID_NOEXIST) { 199 cfga_err(errstring, larg.l_errno, ERR_LIST, 0); 200 } 201 goto out; 202 } 203 204 assert(larg.listp != NULL); 205 206 n = 0; 207 ret = postprocess_list_data(larg.listp, cmd, larg.chld_config, &n); 208 if (ret != SCFGA_OK) { 209 cfga_err(errstring, 0, ERR_LIST, 0); 210 ret = SCFGA_LIB_ERR; 211 goto out; 212 } 213 214 *nelemp = n; 215 *llpp = larg.listp; 216 ret = SCFGA_OK; 217 /* FALLTHROUGH */ 218 out: 219 if (ret != SCFGA_OK) list_free(&larg.listp); 220 S_FREE(larg.hba_logp); 221 return (ret); 222 } 223 224 static scfga_ret_t 225 postprocess_list_data( 226 const ldata_list_t *listp, 227 scfga_cmd_t cmd, 228 cfga_stat_t chld_config, 229 int *np) 230 { 231 ldata_list_t *tmplp = NULL; 232 cfga_list_data_t *hba_ldatap = NULL; 233 int i; 234 235 236 *np = 0; 237 238 if (listp == NULL) { 239 return (SCFGA_ERR); 240 } 241 242 hba_ldatap = NULL; 243 tmplp = (ldata_list_t *)listp; 244 for (i = 0; tmplp != NULL; tmplp = tmplp->next) { 245 i++; 246 if (GET_DYN(tmplp->ldata.ap_phys_id) == NULL) { 247 /* A bus stat data */ 248 assert(GET_DYN(tmplp->ldata.ap_log_id) == NULL); 249 hba_ldatap = &tmplp->ldata; 250 #ifdef DEBUG 251 } else { 252 assert(GET_DYN(tmplp->ldata.ap_log_id) != NULL); 253 #endif 254 } 255 } 256 257 switch (cmd) { 258 case SCFGA_STAT_DEV: 259 if (i != 1 || hba_ldatap != NULL) { 260 return (SCFGA_LIB_ERR); 261 } 262 break; 263 case SCFGA_STAT_BUS: 264 if (i != 1 || hba_ldatap == NULL) { 265 return (SCFGA_LIB_ERR); 266 } 267 break; 268 case SCFGA_STAT_ALL: 269 if (i < 1 || hba_ldatap == NULL) { 270 return (SCFGA_LIB_ERR); 271 } 272 break; 273 default: 274 return (SCFGA_LIB_ERR); 275 } 276 277 *np = i; 278 279 /* Fill in the occupant (child) state. */ 280 if (hba_ldatap != NULL) { 281 hba_ldatap->ap_o_state = chld_config; 282 } 283 return (SCFGA_OK); 284 } 285 286 static int 287 stat_dev(di_node_t node, void *arg) 288 { 289 scfga_list_t *lap = NULL; 290 char *devfsp = NULL, *nodepath = NULL; 291 size_t len = 0; 292 int limited_dev_stat = 0, match_minor, rv; 293 scfga_ret_t ret; 294 295 lap = (scfga_list_t *)arg; 296 297 /* Skip stub nodes */ 298 if (IS_STUB_NODE(node)) { 299 return (DI_WALK_CONTINUE); 300 } 301 302 /* Skip partial nodes */ 303 if (!known_state(node)) { 304 return (DI_WALK_CONTINUE); 305 } 306 307 devfsp = di_devfs_path(node); 308 if (devfsp == NULL) { 309 rv = DI_WALK_CONTINUE; 310 goto out; 311 } 312 313 len = strlen(DEVICES_DIR) + strlen(devfsp) + 1; 314 315 nodepath = calloc(1, len); 316 if (nodepath == NULL) { 317 lap->l_errno = errno; 318 lap->ret = SCFGA_LIB_ERR; 319 rv = DI_WALK_TERMINATE; 320 goto out; 321 } 322 323 (void) snprintf(nodepath, len, "%s%s", DEVICES_DIR, devfsp); 324 325 /* Skip node if it is HBA */ 326 match_minor = 0; 327 if (!dev_cmp(lap->apidp->hba_phys, nodepath, match_minor)) { 328 rv = DI_WALK_CONTINUE; 329 goto out; 330 } 331 332 /* If stat'ing a specific device, is this that device */ 333 if (lap->cmd == SCFGA_STAT_DEV) { 334 assert(lap->apidp->path != NULL); 335 if (dev_cmp(lap->apidp->path, nodepath, match_minor)) { 336 rv = DI_WALK_CONTINUE; 337 goto out; 338 } 339 } 340 341 /* 342 * If stat'ing a bus only, we look at device nodes only to get 343 * bus configuration status. So a limited stat will suffice. 344 */ 345 if (lap->cmd == SCFGA_STAT_BUS) { 346 limited_dev_stat = 1; 347 } else { 348 limited_dev_stat = 0; 349 } 350 351 /* 352 * Ignore errors if stat'ing a bus or listing all 353 */ 354 ret = do_stat_dev(node, nodepath, lap, limited_dev_stat); 355 if (ret != SCFGA_OK) { 356 if (lap->cmd == SCFGA_STAT_DEV) { 357 lap->ret = ret; 358 rv = DI_WALK_TERMINATE; 359 } else { 360 rv = DI_WALK_CONTINUE; 361 } 362 goto out; 363 } 364 365 /* Are we done ? */ 366 rv = DI_WALK_CONTINUE; 367 if (lap->cmd == SCFGA_STAT_BUS && 368 lap->chld_config == CFGA_STAT_CONFIGURED) { 369 rv = DI_WALK_TERMINATE; 370 } else if (lap->cmd == SCFGA_STAT_DEV) { 371 /* 372 * If stat'ing a specific device, we are done at this point. 373 */ 374 lap->ret = SCFGA_OK; 375 rv = DI_WALK_TERMINATE; 376 } 377 378 /*FALLTHRU*/ 379 out: 380 S_FREE(nodepath); 381 if (devfsp != NULL) di_devfs_path_free(devfsp); 382 return (rv); 383 } 384 385 386 static scfga_ret_t 387 do_stat_bus(scfga_list_t *lap, int limited_bus_stat) 388 { 389 cfga_list_data_t *clp = NULL; 390 ldata_list_t *listp = NULL; 391 int l_errno = 0; 392 uint_t devinfo_state = 0; 393 walkarg_t u; 394 scfga_ret_t ret; 395 396 assert(lap->hba_logp != NULL); 397 398 /* Get bus state */ 399 u.node_args.flags = 0; 400 u.node_args.fcn = get_bus_state; 401 402 ret = walk_tree(lap->apidp->hba_phys, &devinfo_state, DINFOPROP, &u, 403 SCFGA_WALK_NODE, &l_errno); 404 if (ret == SCFGA_OK) { 405 lap->hba_rstate = bus_devinfo_to_recep_state(devinfo_state); 406 } else { 407 lap->hba_rstate = CFGA_STAT_NONE; 408 } 409 410 if (limited_bus_stat) { 411 /* We only want to know bus(receptacle) connect status */ 412 return (SCFGA_OK); 413 } 414 415 listp = calloc(1, sizeof (ldata_list_t)); 416 if (listp == NULL) { 417 lap->l_errno = errno; 418 return (SCFGA_LIB_ERR); 419 } 420 421 clp = &listp->ldata; 422 423 (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s", 424 lap->hba_logp); 425 (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s", 426 lap->apidp->hba_phys); 427 428 clp->ap_class[0] = '\0'; /* Filled by libcfgadm */ 429 clp->ap_r_state = lap->hba_rstate; 430 clp->ap_o_state = CFGA_STAT_NONE; /* filled in later by the plug-in */ 431 clp->ap_cond = CFGA_COND_UNKNOWN; 432 clp->ap_busy = 0; 433 clp->ap_status_time = (time_t)-1; 434 clp->ap_info[0] = '\0'; 435 436 (void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s", 437 SCFGA_BUS_TYPE); 438 439 /* Link it in */ 440 listp->next = lap->listp; 441 lap->listp = listp; 442 443 return (SCFGA_OK); 444 } 445 446 static int 447 get_bus_state(di_node_t node, void *arg) 448 { 449 uint_t *di_statep = (uint_t *)arg; 450 451 *di_statep = di_state(node); 452 453 return (DI_WALK_TERMINATE); 454 } 455 456 static scfga_ret_t 457 do_stat_dev( 458 const di_node_t node, 459 const char *nodepath, 460 scfga_list_t *lap, 461 int limited_dev_stat) 462 { 463 uint_t devinfo_state = 0; 464 char *dyncomp = NULL; 465 cfga_list_data_t *clp = NULL; 466 ldata_list_t *listp = NULL; 467 cfga_stat_t ostate; 468 scfga_ret_t ret; 469 470 assert(lap->apidp->hba_phys != NULL); 471 assert(lap->hba_logp != NULL); 472 473 devinfo_state = di_state(node); 474 ostate = dev_devinfo_to_occupant_state(devinfo_state); 475 476 /* If child device is configured, record it */ 477 if (ostate == CFGA_STAT_CONFIGURED) { 478 lap->chld_config = CFGA_STAT_CONFIGURED; 479 } 480 481 if (limited_dev_stat) { 482 /* We only want to know device config state */ 483 return (SCFGA_OK); 484 } 485 486 listp = calloc(1, sizeof (ldata_list_t)); 487 if (listp == NULL) { 488 lap->l_errno = errno; 489 return (SCFGA_LIB_ERR); 490 } 491 492 clp = &listp->ldata; 493 494 /* Create the dynamic component */ 495 ret = make_dyncomp(node, nodepath, &dyncomp, &lap->l_errno); 496 if (ret != SCFGA_OK) { 497 S_FREE(listp); 498 return (ret); 499 } 500 501 assert(dyncomp != NULL); 502 503 /* Create logical and physical ap_id */ 504 (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s", 505 lap->hba_logp, DYN_SEP, dyncomp); 506 507 (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s", 508 lap->apidp->hba_phys, DYN_SEP, dyncomp); 509 510 S_FREE(dyncomp); 511 512 clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */ 513 clp->ap_r_state = lap->hba_rstate; 514 clp->ap_o_state = ostate; 515 clp->ap_cond = CFGA_COND_UNKNOWN; 516 clp->ap_busy = 0; /* no way to determine state change */ 517 clp->ap_status_time = (time_t)-1; 518 519 get_hw_info(node, clp); 520 521 /* Link it in */ 522 listp->next = lap->listp; 523 lap->listp = listp; 524 525 return (SCFGA_OK); 526 } 527 528 /* fill in device type, vid, pid from properties */ 529 static void 530 get_hw_info(di_node_t node, cfga_list_data_t *clp) 531 { 532 char *cp = NULL; 533 char *inq_vid, *inq_pid; 534 535 /* 536 * Fill in type information 537 */ 538 cp = (char *)get_device_type(node); 539 if (cp == NULL) { 540 cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE); 541 } 542 (void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s", S_STR(cp)); 543 544 /* 545 * Fill in vendor and product ID. 546 */ 547 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node, 548 "inquiry-product-id", &inq_pid) == 1) && 549 (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 550 "inquiry-vendor-id", &inq_vid) == 1)) { 551 (void) snprintf(clp->ap_info, sizeof (clp->ap_info), 552 "%s %s", inq_vid, inq_pid); 553 } 554 } 555 556 /* 557 * Get dtype from "inquiry-device-type" property. If not present, 558 * derive it from minor node type 559 */ 560 static char * 561 get_device_type(di_node_t node) 562 { 563 char *name = NULL; 564 int *inq_dtype; 565 int i; 566 567 /* first, derive type based on inquiry property */ 568 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type", 569 &inq_dtype) == 1) { 570 int itype = (*inq_dtype) & DTYPE_MASK; 571 572 for (i = 0; i < N_DEVICE_TYPES; i++) { 573 if (device_list[i].itype == DTYPE_UNKNOWN) 574 continue; 575 if (itype == device_list[i].itype) { 576 name = (char *)device_list[i].name; 577 break; 578 } 579 } 580 } 581 582 /* if property fails, use minor nodetype */ 583 if (name == NULL) { 584 char *nodetype; 585 di_minor_t minor = di_minor_next(node, DI_MINOR_NIL); 586 587 if ((minor != DI_MINOR_NIL) && 588 ((nodetype = di_minor_nodetype(minor)) != NULL)) { 589 for (i = 0; i < N_DEVICE_TYPES; i++) { 590 if (device_list[i].ntype && 591 (strcmp(nodetype, device_list[i].ntype) 592 == 0)) { 593 name = (char *)device_list[i].name; 594 break; 595 } 596 } 597 } 598 } 599 600 if (name == NULL) /* default to unknown */ 601 name = "unknown"; 602 return (name); 603 } 604 605 /* Transform linked list into an array */ 606 scfga_ret_t 607 list_ext_postprocess( 608 ldata_list_t **llpp, 609 int nelem, 610 cfga_list_data_t **ap_id_list, 611 int *nlistp, 612 char **errstring) 613 { 614 cfga_list_data_t *ldatap = NULL; 615 ldata_list_t *tmplp = NULL; 616 int i = -1; 617 618 *ap_id_list = NULL; 619 *nlistp = 0; 620 621 if (*llpp == NULL || nelem < 0) { 622 return (SCFGA_LIB_ERR); 623 } 624 625 if (nelem == 0) { 626 return (SCFGA_APID_NOEXIST); 627 } 628 629 ldatap = calloc(nelem, sizeof (cfga_list_data_t)); 630 if (ldatap == NULL) { 631 cfga_err(errstring, errno, ERR_LIST, 0); 632 return (SCFGA_LIB_ERR); 633 } 634 635 /* Extract the list_data structures from the linked list */ 636 tmplp = *llpp; 637 for (i = 0; i < nelem && tmplp != NULL; i++) { 638 ldatap[i] = tmplp->ldata; 639 tmplp = tmplp->next; 640 } 641 642 if (i < nelem || tmplp != NULL) { 643 S_FREE(ldatap); 644 return (SCFGA_LIB_ERR); 645 } 646 647 *nlistp = nelem; 648 *ap_id_list = ldatap; 649 650 return (SCFGA_OK); 651 } 652 653 /* 654 * Convert bus state to receptacle state 655 */ 656 static cfga_stat_t 657 bus_devinfo_to_recep_state(uint_t bus_di_state) 658 { 659 cfga_stat_t rs; 660 661 switch (bus_di_state) { 662 case DI_BUS_QUIESCED: 663 case DI_BUS_DOWN: 664 rs = CFGA_STAT_DISCONNECTED; 665 break; 666 /* 667 * NOTE: An explicit flag for active should probably be added to 668 * libdevinfo. 669 */ 670 default: 671 rs = CFGA_STAT_CONNECTED; 672 break; 673 } 674 675 return (rs); 676 } 677 678 /* 679 * Convert device state to occupant state 680 */ 681 static cfga_stat_t 682 dev_devinfo_to_occupant_state(uint_t dev_di_state) 683 { 684 /* Driver attached ? */ 685 if ((dev_di_state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) { 686 return (CFGA_STAT_CONFIGURED); 687 } 688 689 if ((dev_di_state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE || 690 (dev_di_state & DI_DEVICE_DOWN) == DI_DEVICE_DOWN) { 691 return (CFGA_STAT_UNCONFIGURED); 692 } else { 693 return (CFGA_STAT_NONE); 694 } 695 } 696