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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 #include "cfga_fp.h" 28 29 /* Function prototypes */ 30 31 static fpcfga_ret_t get_xport_devlink(const char *hba_phys, 32 char **hba_logpp, int *l_errnop); 33 static char ctoi(char c); 34 static fpcfga_ret_t is_apid_configured(const char *xport_phys, 35 const char *dyncomp, struct luninfo_list **lunlistpp, int *l_errnop); 36 static fpcfga_ret_t insert_lun_to_lunlist(struct luninfo_list **lunlistpp, 37 const char *dyncomp, di_node_t devnode, int *l_errnop); 38 static fpcfga_ret_t update_lunlist(struct luninfo_list **lunlistpp, int lun, 39 uint_t state, char *pathp, int *l_errnop); 40 41 42 /* Globals */ 43 44 /* Various conversions routines */ 45 46 void 47 cvt_lawwn_to_dyncomp(const la_wwn_t *pwwn, char **dyncomp, int *l_errnop) 48 { 49 *dyncomp = calloc(1, WWN_SIZE*2 + 1); 50 if (*dyncomp == NULL) { 51 *l_errnop = errno; 52 } 53 54 (void) sprintf(*dyncomp, "%016llx", 55 (wwnConversion((uchar_t *)pwwn->raw_wwn))); 56 } 57 58 59 int 60 cvt_dyncomp_to_lawwn(const char *dyncomp, la_wwn_t *port_wwn) 61 { 62 int i; 63 char c, c1; 64 uchar_t *wwnp; 65 66 wwnp = port_wwn->raw_wwn; 67 for (i = 0; i < WWN_SIZE; i++, wwnp++) { 68 69 c = ctoi(*dyncomp++); 70 c1 = ctoi(*dyncomp++); 71 if (c == -1 || c1 == -1) 72 return (-1); 73 *wwnp = ((c << 4) + c1); 74 } 75 76 return (0); 77 } 78 79 80 static char 81 ctoi(char c) 82 { 83 if ((c >= '0') && (c <= '9')) 84 c -= '0'; 85 else if ((c >= 'A') && (c <= 'F')) 86 c = c - 'A' + 10; 87 else if ((c >= 'a') && (c <= 'f')) 88 c = c - 'a' + 10; 89 else 90 c = -1; 91 return (c); 92 } 93 94 95 /* 96 * Generates the HBA logical ap_id from physical ap_id. 97 */ 98 fpcfga_ret_t 99 make_xport_logid(const char *xport_phys, char **xport_logpp, int *l_errnop) 100 { 101 if (*xport_logpp != NULL) { 102 return (FPCFGA_ERR); 103 } 104 105 /* 106 * A devlink for the XPORT should exist. Without the /dev/cfg link 107 * driver name and instance number based based link needs to be 108 * constructed for the minor node type of DDI_NT_FC_ATTACHMENT_POINT. 109 * sunddi.h defines DDI_NT_FC_ATTACHMENT_POINT for 110 * ddi_ctl:attachment_point:fc 111 */ 112 if (get_xport_devlink(xport_phys, xport_logpp, l_errnop) == FPCFGA_OK) { 113 assert(*xport_logpp != NULL); 114 return (FPCFGA_OK); 115 } else { 116 return (FPCFGA_ERR); 117 } 118 } 119 120 static fpcfga_ret_t 121 get_xport_devlink(const char *xport_phys, char **xport_logpp, int *l_errnop) 122 { 123 int match_minor; 124 size_t len; 125 fpcfga_ret_t ret; 126 127 match_minor = 1; 128 ret = physpath_to_devlink(CFGA_DEV_DIR, (char *)xport_phys, 129 xport_logpp, l_errnop, match_minor); 130 if (ret != FPCFGA_OK) { 131 return (ret); 132 } 133 134 assert(*xport_logpp != NULL); 135 136 /* Remove the "/dev/cfg/" prefix */ 137 len = strlen(CFGA_DEV_DIR SLASH); 138 139 (void) memmove(*xport_logpp, *xport_logpp + len, 140 strlen(*xport_logpp + len) + 1); 141 142 return (FPCFGA_OK); 143 } 144 145 146 /* 147 * Given a xport path and dynamic ap_id, returns the physical 148 * path in pathpp. If the dynamic ap is not configured pathpp set to NULL 149 * and returns FPCFGA_APID_NOCONFIGURE. 150 */ 151 fpcfga_ret_t 152 dyn_apid_to_path( 153 const char *xport_phys, 154 const char *dyncomp, 155 struct luninfo_list **lunlistpp, 156 int *l_errnop) 157 { 158 fpcfga_ret_t ret; 159 160 /* A device MUST have a dynamic component */ 161 if (dyncomp == NULL) { 162 return (FPCFGA_LIB_ERR); 163 } 164 165 ret = is_apid_configured(xport_phys, dyncomp, lunlistpp, l_errnop); 166 167 assert(ret != FPCFGA_OK); 168 169 return (ret); 170 } 171 172 /* 173 * When both the transport and dynamic comp are given this function 174 * checks to see if the dynamic ap is configured on the dev tree. 175 * If it is configured the devfs path will be stored in pathpp. 176 * When the dynamic comp is null this function check to see if the transport 177 * node has any child. 178 * 179 * Retrun value: FPCFGA_OK if the apid is configured. 180 * FPCFGA_APID_NOCONFIGURE if the apid is not configured. 181 * FPCFGA_LIB_ERR for other errors. 182 */ 183 static fpcfga_ret_t 184 is_apid_configured( 185 const char *xport_phys, 186 const char *dyncomp, 187 struct luninfo_list **lunlistpp, 188 int *l_errnop) 189 { 190 char *devfs_phys, *devfs_fp_path, *client_path, *cp, 191 *pathp = NULL; 192 char path_name[MAXPATHLEN]; 193 di_node_t tree_root, root, fpnode, dev_node, client_node; 194 di_path_t path = DI_PATH_NIL; 195 di_prop_t prop = DI_PROP_NIL; 196 uchar_t *port_wwn_data = NULL; 197 char *lun_guid = NULL; 198 char port_wwn[WWN_SIZE*2+1]; 199 int count, *lunnump, devlen, 200 found_fp = 0; 201 uint_t state; 202 uint_t statep; 203 fpcfga_ret_t ret; 204 205 if (*lunlistpp != NULL) { 206 return (FPCFGA_LIB_ERR); 207 } 208 209 ret = FPCFGA_APID_NOCONFIGURE; 210 211 if ((devfs_phys = strdup(xport_phys)) == NULL) { 212 *l_errnop = errno; 213 return (FPCFGA_LIB_ERR); 214 } 215 216 if (strncmp(devfs_phys, DEVICES_DIR SLASH, strlen(DEVICES_DIR) + 217 strlen(SLASH)) == 0) { 218 cp = devfs_phys + strlen(DEVICES_DIR); 219 (void) memmove(devfs_phys, cp, strlen(cp) + 1); 220 } 221 222 if ((cp = strstr(devfs_phys, MINOR_SEP)) != NULL) { 223 *cp = '\0'; /* Terminate string. */ 224 } 225 226 if ((tree_root = di_init("/", DINFOCPYALL | DINFOPATH)) 227 == DI_NODE_NIL) { 228 *l_errnop = errno; 229 S_FREE(devfs_phys); 230 return (FPCFGA_LIB_ERR); 231 } 232 233 fpnode = di_drv_first_node("fp", tree_root); 234 235 while (fpnode) { 236 devfs_fp_path = di_devfs_path(fpnode); 237 if ((devfs_fp_path) && !(strncmp(devfs_fp_path, 238 devfs_phys, strlen(devfs_phys)))) { 239 found_fp = 1; 240 di_devfs_path_free(devfs_fp_path); 241 break; 242 } 243 di_devfs_path_free(devfs_fp_path); 244 fpnode = di_drv_next_node(fpnode); 245 } 246 if (!(found_fp)) { 247 ret = FPCFGA_LIB_ERR; 248 goto out; 249 } else { 250 root = fpnode; 251 } 252 253 /* 254 * when there is no child and path info node the 255 * FPCFGA_APID_NOCONFIGURE is returned 256 * regardless of the dynamic comp. 257 */ 258 dev_node = di_child_node(root); 259 path = di_path_next_client(root, path); 260 if ((dev_node == DI_NODE_NIL) && (path == DI_PATH_NIL)) { 261 *l_errnop = errno; 262 ret = FPCFGA_APID_NOCONFIGURE; 263 goto out; 264 } 265 266 /* 267 * when dyn comp is null the function just checks if there is any 268 * child under fp transport attachment point. 269 */ 270 if (dyncomp == NULL) { 271 ret = FPCFGA_OK; 272 goto out; 273 } 274 275 /* 276 * now checks the children node to find 277 * if dynamic ap is configured. if there are multiple luns 278 * store into lunlist. 279 */ 280 if (dev_node != DI_NODE_NIL) { 281 do { 282 while ((prop = di_prop_next(dev_node, prop)) != 283 DI_PROP_NIL) { 284 /* is property name port-wwn */ 285 if ((!(strcmp(PORT_WWN_PROP, 286 di_prop_name(prop)))) && 287 (di_prop_type(prop) == 288 DI_PROP_TYPE_BYTE)) { 289 break; 290 } 291 } 292 293 if (prop != DI_PROP_NIL) { 294 count = di_prop_bytes(prop, &port_wwn_data); 295 if (count != WWN_SIZE) { 296 ret = FPCFGA_LIB_ERR; 297 goto out; 298 } else { 299 (void) sprintf(port_wwn, 300 "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", 301 port_wwn_data[0], port_wwn_data[1], 302 port_wwn_data[2], port_wwn_data[3], 303 port_wwn_data[4], port_wwn_data[5], 304 port_wwn_data[6], port_wwn_data[7]); 305 if (!(strncmp(port_wwn, dyncomp, 306 WWN_SIZE*2))) { 307 ret = insert_lun_to_lunlist( 308 lunlistpp, dyncomp, 309 dev_node, l_errnop); 310 if (ret != FPCFGA_OK) { 311 goto out; 312 } 313 } 314 } 315 } 316 dev_node = di_sibling_node(dev_node); 317 prop = DI_PROP_NIL; 318 } while (dev_node != DI_NODE_NIL); 319 } 320 321 /* 322 * now checks the path info node to find 323 * if dynamic ap is configured. if there are multiple luns 324 * store into lunlist. 325 */ 326 if (path != DI_PATH_NIL) { 327 /* 328 * now parse the path info node. 329 */ 330 do { 331 count = di_path_prop_lookup_bytes(path, PORT_WWN_PROP, 332 &port_wwn_data); 333 if (count != WWN_SIZE) { 334 ret = FPCFGA_LIB_ERR; 335 goto out; 336 } 337 338 (void) sprintf(port_wwn, 339 "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", 340 port_wwn_data[0], port_wwn_data[1], 341 port_wwn_data[2], port_wwn_data[3], 342 port_wwn_data[4], port_wwn_data[5], 343 port_wwn_data[6], port_wwn_data[7]); 344 345 /* if matches get the path of scsi_vhci child node. */ 346 if (!(strncmp(port_wwn, dyncomp, WWN_SIZE*2))) { 347 client_node = di_path_client_node(path); 348 if (client_node == DI_NODE_NIL) { 349 ret = FPCFGA_LIB_ERR; 350 *l_errnop = errno; 351 goto out; 352 } 353 count = di_path_prop_lookup_ints(path, 354 LUN_PROP, &lunnump); 355 client_path = di_devfs_path(client_node); 356 strcpy(path_name, client_path); 357 di_devfs_path_free(client_path); 358 state = di_state(client_node); 359 statep = di_path_state(path); 360 361 /* 362 * If the node is 363 * state then check the devfs_path to 364 * see if it has a complete path. 365 * For non scsi_vhci node the path 366 * doesn't contain @w(portwwn) part 367 * consistently. For scsi_vhci 368 * this behavior may not be there. 369 * To be safe @g(guid) is attempted 370 * to be added here. 371 */ 372 if ((state & DI_DRIVER_DETACHED) && 373 (strstr(path_name, "@g") == NULL)) { 374 prop = DI_PROP_NIL; 375 while ((prop = di_prop_next(client_node, 376 prop)) != DI_PROP_NIL) { 377 /* is property name lun-wwn */ 378 if ((!(strcmp(LUN_GUID_PROP, 379 di_prop_name(prop)))) && 380 (di_prop_type(prop) == 381 DI_PROP_TYPE_STRING)) { 382 break; 383 } 384 } 385 386 if (prop != DI_PROP_NIL) { 387 count = di_prop_strings( 388 prop, &lun_guid); 389 sprintf(&path_name[ 390 strlen(path_name)], 391 "@g%s", lun_guid); 392 } else { 393 ret = FPCFGA_LIB_ERR; 394 goto out; 395 } 396 } 397 398 devlen = strlen(DEVICES_DIR) + 399 strlen(path_name) + 1; 400 if ((pathp = calloc(1, devlen)) 401 == NULL) { 402 *l_errnop = errno; 403 return (FPCFGA_LIB_ERR); 404 } else { 405 (void) snprintf(pathp, devlen, 406 "%s%s", DEVICES_DIR, path_name); 407 } 408 if ((ret = (update_lunlist(lunlistpp, *lunnump, 409 statep, pathp, l_errnop))) != 410 FPCFGA_OK) { 411 S_FREE(pathp); 412 goto out; 413 } 414 } 415 path = di_path_next_client(root, path); 416 } while (path != DI_PATH_NIL); 417 } 418 419 out: 420 di_fini(tree_root); 421 S_FREE(devfs_phys); 422 return (ret); 423 } 424 425 static fpcfga_ret_t 426 insert_lun_to_lunlist( 427 struct luninfo_list **lunlistpp, 428 const char *dyncomp, 429 di_node_t dev_node, 430 int *l_errnop) 431 { 432 char path_name[MAXPATHLEN]; 433 char *pathp, *dev_phys; 434 di_prop_t prop_lun = DI_PROP_NIL; 435 uint_t state; 436 int count, devlen; 437 int *lunp; 438 439 while ((prop_lun = di_prop_next(dev_node, prop_lun)) != DI_PROP_NIL) { 440 if (!(strcmp(LUN_PROP, di_prop_name(prop_lun))) && 441 (di_prop_type(prop_lun) == DI_PROP_TYPE_INT)) { 442 count = di_prop_ints(prop_lun, &lunp); 443 if (count <= 0) { 444 return (FPCFGA_LIB_ERR); 445 } 446 break; 447 } 448 } 449 450 if (prop_lun == DI_PROP_NIL) { 451 return (FPCFGA_LIB_ERR); 452 } 453 454 /* 455 * stores state info in state. 456 * This information is used to get the 457 * validity of path. 458 * if driver_detached don't try to get 459 * the devfs_path since it is not 460 * complete. ex, /pci@1f,2000/pci@1/ 461 * SUNW,qlc@5/fp@0,0/ssd 462 * which doesn't contain the port wwn 463 * part. The attached node looks like 464 * /pci@1f,2000/pci@1/SUNW,qlc@5/fp@0,0/ 465 * ssd@w2100002037006b14,0 466 */ 467 state = di_state(dev_node); 468 469 dev_phys = di_devfs_path(dev_node); 470 if (dev_phys == NULL) { 471 return (FPCFGA_LIB_ERR); 472 } 473 474 strcpy(path_name, dev_phys); 475 476 di_devfs_path_free(dev_phys); 477 478 if ((state & DI_DRIVER_DETACHED) && 479 (strstr(path_name, "@w") == NULL)) { 480 sprintf(&path_name[strlen(path_name)], "@w%s,%x", dyncomp, 481 *lunp); 482 } 483 484 devlen = strlen(DEVICES_DIR) + strlen(path_name) + 1; 485 486 if ((pathp = calloc(1, devlen)) 487 == NULL) { 488 *l_errnop = errno; 489 return (FPCFGA_LIB_ERR); 490 } else { 491 (void) snprintf(pathp, devlen, "%s%s", DEVICES_DIR, path_name); 492 } 493 494 return (update_lunlist(lunlistpp, *lunp, state, pathp, l_errnop)); 495 } 496 497 static fpcfga_ret_t 498 update_lunlist( 499 struct luninfo_list **lunlistpp, 500 int lun, 501 uint_t state, 502 char *pathp, 503 int *l_errnop) 504 { 505 struct luninfo_list *newlun, *curlun, *prevlun; 506 507 newlun = curlun = prevlun = (struct luninfo_list *)NULL; 508 509 newlun = calloc(1, sizeof (struct luninfo_list)); 510 if (newlun == NULL) { 511 *l_errnop = errno; 512 return (FPCFGA_LIB_ERR); 513 } 514 515 newlun->lunnum = lun; 516 newlun->node_state = state; 517 newlun->path = pathp; 518 newlun->next = (struct luninfo_list *)NULL; 519 520 /* if lunlist is empty add the new lun info and return. */ 521 if (*lunlistpp == NULL) { 522 *lunlistpp = newlun; 523 return (FPCFGA_OK); 524 } 525 526 /* if the first lun in the list is the same as the new lun return. */ 527 if ((*lunlistpp)->lunnum == lun) { 528 S_FREE(newlun); 529 return (FPCFGA_OK); 530 } 531 532 /* 533 * if the first lun in the list is less than the new lun add the 534 * new lun as the first lun and return. 535 */ 536 if ((*lunlistpp)->lunnum < lun) { 537 newlun->next = *lunlistpp; 538 *lunlistpp = newlun; 539 return (FPCFGA_OK); 540 } 541 542 /* 543 * if the first lun in the list is greater than the new lun and 544 * there is a single lun add new lun after the first lun and return. 545 */ 546 if ((*lunlistpp)->next == NULL) { 547 (*lunlistpp)->next = newlun; 548 return (FPCFGA_OK); 549 } 550 551 /* 552 * now there is more than two luns in the list and the first lun 553 * is greter than the input lun. 554 */ 555 curlun = (*lunlistpp)->next; 556 prevlun = *lunlistpp; 557 558 while (curlun != NULL) { 559 if (curlun->lunnum == lun) { 560 S_FREE(newlun); 561 return (FPCFGA_OK); 562 } else if (curlun->lunnum < lun) { 563 newlun->next = curlun; 564 prevlun->next = newlun; 565 return (FPCFGA_OK); 566 } else { 567 prevlun = curlun; 568 curlun = curlun->next; 569 } 570 } 571 572 /* add the new lun at the end of list. */ 573 prevlun->next = newlun; 574 return (FPCFGA_OK); 575 576 } 577 578 579 fpcfga_ret_t 580 make_dyncomp_from_dinode( 581 const di_node_t node, 582 char **dyncompp, 583 int *l_errnop) 584 { 585 di_prop_t prop = DI_PROP_NIL; 586 uchar_t *port_wwn_data; 587 int count; 588 589 *l_errnop = 0; 590 *dyncompp = calloc(1, WWN_SIZE*2 + 1); 591 if (*dyncompp == NULL) { 592 *l_errnop = errno; 593 return (FPCFGA_LIB_ERR); 594 } 595 596 /* now get port-wwn for the input node. */ 597 while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) { 598 if (!(strcmp(PORT_WWN_PROP, di_prop_name(prop))) && 599 (di_prop_type(prop) == DI_PROP_TYPE_BYTE)) { 600 break; 601 } 602 } 603 604 if (prop != DI_PROP_NIL) { 605 count = di_prop_bytes(prop, &port_wwn_data); 606 if (count != WWN_SIZE) { 607 S_FREE(*dyncompp); 608 return (FPCFGA_LIB_ERR); 609 } 610 611 (void) sprintf(*dyncompp, "%016llx", 612 (wwnConversion(port_wwn_data))); 613 } else { 614 *l_errnop = errno; 615 S_FREE(*dyncompp); 616 return (FPCFGA_LIB_ERR); 617 } 618 619 return (FPCFGA_OK); 620 } 621 622 fpcfga_ret_t 623 make_portwwn_luncomp_from_dinode( 624 const di_node_t node, 625 char **dyncompp, 626 int **luncompp, 627 int *l_errnop) 628 { 629 uchar_t *port_wwn_data; 630 int pwwn_ret, lun_ret; 631 632 *l_errnop = 0; 633 634 if ((dyncompp != NULL) && 635 ((pwwn_ret = di_prop_lookup_bytes(DDI_DEV_T_ANY, 636 node, PORT_WWN_PROP, &port_wwn_data)) <= 0)) { 637 *l_errnop = errno; 638 } 639 if ((luncompp != NULL) && 640 ((lun_ret = di_prop_lookup_ints(DDI_DEV_T_ANY, 641 node, LUN_PROP, luncompp)) <= 0)) { 642 *l_errnop = errno; 643 } 644 645 /* 646 * di_prop* returns the number of entries found or 0 if not found 647 * or -1 for othere failure. 648 */ 649 if ((pwwn_ret <= 0) || (lun_ret <= 0)) { 650 return (FPCFGA_LIB_ERR); 651 } 652 653 *dyncompp = calloc(1, WWN_SIZE*2+1); 654 if (*dyncompp == NULL) { 655 *l_errnop = errno; 656 return (FPCFGA_LIB_ERR); 657 } 658 659 (void) sprintf(*dyncompp, "%016llx", (wwnConversion(port_wwn_data))); 660 661 return (FPCFGA_OK); 662 } 663 664 fpcfga_ret_t 665 make_portwwn_luncomp_from_pinode( 666 const di_path_t pinode, 667 char **dyncompp, 668 int **luncompp, 669 int *l_errnop) 670 { 671 uchar_t *port_wwn_data; 672 int pwwn_ret, lun_ret; 673 674 *l_errnop = 0; 675 676 if ((dyncompp != NULL) && 677 ((pwwn_ret = di_path_prop_lookup_bytes(pinode, 678 PORT_WWN_PROP, &port_wwn_data)) <= 0)) { 679 *l_errnop = errno; 680 } 681 if ((luncompp != NULL) && 682 ((lun_ret = di_path_prop_lookup_ints(pinode, 683 LUN_PROP, luncompp)) <= 0)) { 684 *l_errnop = errno; 685 } 686 687 /* 688 * di_prop* returns the number of entries found or 0 if not found 689 * or -1 for othere failure. 690 */ 691 if ((pwwn_ret <= 0) || (lun_ret <= 0)) { 692 return (FPCFGA_LIB_ERR); 693 } 694 695 *dyncompp = calloc(1, WWN_SIZE*2+1); 696 if (*dyncompp == NULL) { 697 *l_errnop = errno; 698 return (FPCFGA_LIB_ERR); 699 } 700 701 (void) sprintf(*dyncompp, "%016llx", (wwnConversion(port_wwn_data))); 702 703 return (FPCFGA_OK); 704 } 705 706 fpcfga_ret_t 707 construct_nodepath_from_dinode( 708 const di_node_t node, 709 char **node_pathp, 710 int *l_errnop) 711 { 712 char *devfs_path, path_name[MAXPATHLEN], *lun_guid, *port_wwn; 713 uchar_t *port_wwn_data; 714 int is_scsi_vhci_dev, di_ret, devlen; 715 uint_t state; 716 717 devfs_path = di_devfs_path(node); 718 strcpy(path_name, devfs_path); 719 di_devfs_path_free(devfs_path); 720 state = di_state(node); 721 722 is_scsi_vhci_dev = (strstr(path_name, SCSI_VHCI_DRVR) != NULL) ? 1 : 0; 723 724 /* 725 * If the node is 726 * state then check the devfs_path to 727 * see if it has a complete path. 728 * For non scsi_vhci node the path 729 * doesn't contain @w(portwwn) part 730 * consistently. For scsi_vhci 731 * this behavior may not be there. 732 * To be safe @g(guid) is attempted 733 * to be added here. 734 */ 735 if (state & DI_DRIVER_DETACHED) { 736 if (is_scsi_vhci_dev && 737 (strstr(path_name, "@g") == NULL)) { 738 di_ret = di_prop_lookup_strings(DDI_DEV_T_ANY, node, 739 LUN_GUID_PROP, &lun_guid); 740 if (di_ret == -1) { 741 *l_errnop = errno; 742 return (FPCFGA_LIB_ERR); 743 } else { 744 sprintf(&path_name[strlen(path_name)], 745 "@g%s", lun_guid); 746 } 747 } else if (!is_scsi_vhci_dev && 748 (strstr(path_name, "@w") == NULL)) { 749 di_ret = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, 750 PORT_WWN_PROP, &port_wwn_data); 751 if (di_ret == -1) { 752 *l_errnop = errno; 753 return (FPCFGA_LIB_ERR); 754 } else { 755 if ((port_wwn = calloc(1, WWN_SIZE*2 + 1)) 756 == NULL) { 757 *l_errnop = errno; 758 return (FPCFGA_LIB_ERR); 759 } 760 761 (void) sprintf(port_wwn, "%016llx", 762 (wwnConversion(port_wwn_data))); 763 (void) sprintf(&path_name[strlen(path_name)], 764 "@w%s", port_wwn); 765 S_FREE(port_wwn); 766 } 767 } 768 } 769 770 devlen = strlen(DEVICES_DIR) + strlen(path_name) + 1; 771 if ((*node_pathp = calloc(1, devlen)) == NULL) { 772 *l_errnop = errno; 773 return (FPCFGA_LIB_ERR); 774 } else { 775 (void) snprintf(*node_pathp, devlen, 776 "%s%s", DEVICES_DIR, path_name); 777 } 778 779 return (FPCFGA_OK); 780 } 781 782 u_longlong_t 783 wwnConversion(uchar_t *wwn) 784 { 785 u_longlong_t tmp; 786 memcpy(&tmp, wwn, sizeof (u_longlong_t)); 787 return (ntohll(tmp)); 788 } 789