xref: /illumos-gate/usr/src/cmd/luxadm/fchba.c (revision 287247a826fa2ab8d01f6c8f276d405eb08420f8)
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 
28 #include	<hbaapi.h>
29 #include	<stdio.h>
30 #include	<unistd.h>
31 #include	<stdlib.h>
32 #include	<sys/param.h>
33 #include	<sys/types.h>
34 #include	<sys/stat.h>
35 #include	<string.h>
36 #include	<strings.h>
37 #include	<ctype.h>
38 #include	<sys/scsi/generic/sense.h>
39 #include	<sys/scsi/generic/mode.h>
40 #include	<sys/scsi/generic/inquiry.h>
41 #include	<errno.h>
42 #include	<libdevice.h>
43 #include	<config_admin.h>
44 #include	<sys/byteorder.h>
45 #include	<sys/fibre-channel/fcio.h>
46 #include	"common.h"
47 #include	"sun_fc_version.h"
48 
49 #define	DEFAULT_LUN_COUNT	1024
50 #define	LUN_SIZE		8
51 #define	LUN_HEADER_SIZE		8
52 #define	DEFAULT_LUN_LENGTH	DEFAULT_LUN_COUNT   *	\
53 				LUN_SIZE	    +	\
54 				LUN_HEADER_SIZE
55 struct lun_val {
56 	uchar_t val[8];
57 };
58 struct rep_luns_rsp {
59 	uint32_t    length;
60 	uint32_t    rsrvd;
61 	struct lun_val  lun[1];
62 };
63 
64 /* Extracted from the old scsi.h file */
65 struct  capacity_data_struct {
66 	uint_t  last_block_addr;
67 	uint_t  block_size;
68 };
69 
70 
71 /* Structure to handle the inq. page 0x80 serial number */
72 struct page80 {
73 	uchar_t inq_dtype;
74 	uchar_t inq_page_code;
75 	uchar_t reserved;
76 	uchar_t inq_page_len;
77 	uchar_t inq_serial[251];
78 };
79 
80 extern char		*dtype[];
81 extern int		Options;
82 extern const int	OPTION_P;
83 
84 int skip_hba(int i);
85 int find_supported_inq_page(HBA_HANDLE handle, HBA_WWN hwwn, HBA_WWN pwwn,
86     uint64_t lun, int page_num);
87 /*
88  * The routines within this file operate against the T11
89  * HBA API interface.  In some cases, proprietary Sun driver
90  * interface are also called to add additional information
91  * above what the standard library supports.
92  */
93 
94 uint64_t
95 wwnConversion(uchar_t *wwn) {
96 	uint64_t tmp;
97 	(void) memcpy(&tmp, wwn, sizeof (uint64_t));
98 	return (ntohll(tmp));
99 }
100 
101 void printStatus(HBA_STATUS status) {
102 	switch (status) {
103 	case HBA_STATUS_OK:
104 	    printf(MSGSTR(2410, "OK"));
105 	    return;
106 	case HBA_STATUS_ERROR:
107 	    printf(MSGSTR(2411, "ERROR"));
108 	    return;
109 	case HBA_STATUS_ERROR_NOT_SUPPORTED:
110 	    printf(MSGSTR(2412, "NOT SUPPORTED"));
111 	    return;
112 	case HBA_STATUS_ERROR_INVALID_HANDLE:
113 	    printf(MSGSTR(2413, "INVALID HANDLE"));
114 	    return;
115 	case HBA_STATUS_ERROR_ARG:
116 	    printf(MSGSTR(2414, "ERROR ARG"));
117 	    return;
118 	case HBA_STATUS_ERROR_ILLEGAL_WWN:
119 	    printf(MSGSTR(2415, "ILLEGAL WWN"));
120 	    return;
121 	case HBA_STATUS_ERROR_ILLEGAL_INDEX:
122 	    printf(MSGSTR(2416, "ILLEGAL INDEX"));
123 	    return;
124 	case HBA_STATUS_ERROR_MORE_DATA:
125 	    printf(MSGSTR(2417, "MORE DATA"));
126 	    return;
127 	case HBA_STATUS_ERROR_STALE_DATA:
128 	    printf(MSGSTR(2418, "STALE DATA"));
129 	    return;
130 	case HBA_STATUS_SCSI_CHECK_CONDITION:
131 	    printf(MSGSTR(2419, "SCSI CHECK CONDITION"));
132 	    return;
133 	case HBA_STATUS_ERROR_BUSY:
134 	    printf(MSGSTR(2420, "BUSY"));
135 	    return;
136 	case HBA_STATUS_ERROR_TRY_AGAIN:
137 	    printf(MSGSTR(2421, "TRY AGAIN"));
138 	    return;
139 	case HBA_STATUS_ERROR_UNAVAILABLE:
140 	    printf(MSGSTR(2422, "UNAVAILABLE"));
141 	    return;
142 	default:
143 	    printf(MSGSTR(2423, "UNKNOWN ERROR TYPE %d"), status);
144 	    return;
145 	    }
146 }
147 
148 uint32_t
149 getNumberOfAdapters() {
150 	uint32_t count = HBA_GetNumberOfAdapters();
151 	if (count == 0) {
152 		fprintf(stderr, MSGSTR(2405,
153 			"\nERROR: No Fibre Channel Adapters found.\n"));
154 	}
155 	return (count);
156 }
157 
158 #define	MAX_RETRIES	10
159 
160 /*
161  * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
162  * Will handle retries if applicable.
163  */
164 int
165 getAdapterAttrs(HBA_HANDLE handle, char *name, HBA_ADAPTERATTRIBUTES *attrs) {
166 	int count = 0;
167 	HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
168 
169 	/* Loop as long as we have a retryable error */
170 	while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
171 		status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) {
172 		status = HBA_GetAdapterAttributes(handle, attrs);
173 		if (status == HBA_STATUS_OK) {
174 			break;
175 		}
176 		(void) sleep(1);
177 	}
178 	if (status != HBA_STATUS_OK) {
179 		/* We encountered a non-retryable error */
180 		fprintf(stderr, MSGSTR(2501,
181 		"\nERROR: Unable to retrieve adapter port details (%s)"),
182 		name);
183 		printStatus(status);
184 		fprintf(stderr, "\n");
185 	}
186 	return (status);
187 }
188 
189 /*
190  * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
191  * Will handle retries if applicable.
192  */
193 int
194 getAdapterPortAttrs(HBA_HANDLE handle, char *name, int portIndex,
195 	    HBA_PORTATTRIBUTES *attrs) {
196 	int count = 0;
197 	HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
198 
199 	/* Loop as long as we have a retryable error */
200 	while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
201 		status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) {
202 		status = HBA_GetAdapterPortAttributes(handle, portIndex, attrs);
203 		if (status == HBA_STATUS_OK) {
204 			break;
205 		}
206 
207 		/* The odds of this occuring are very slim, but possible. */
208 		if (status == HBA_STATUS_ERROR_STALE_DATA) {
209 			/*
210 			 * If we hit a stale data scenario,
211 			 * we'll just tell the user to try again.
212 			 */
213 			status = HBA_STATUS_ERROR_TRY_AGAIN;
214 			break;
215 		}
216 		sleep(1);
217 	}
218 	if (status != HBA_STATUS_OK) {
219 		/* We encountered a non-retryable error */
220 		fprintf(stderr, MSGSTR(2501,
221 		"\nERROR: Unable to retrieve adapter port details (%s)"),
222 		name);
223 		printStatus(status);
224 		fprintf(stderr, "\n");
225 	}
226 	return (status);
227 }
228 
229 /*
230  * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
231  * Will handle retries if applicable.
232  */
233 int
234 getDiscPortAttrs(HBA_HANDLE handle, char *name, int portIndex, int discIndex,
235 	    HBA_PORTATTRIBUTES *attrs) {
236 	int count = 0;
237 	HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
238 
239 	/* Loop as long as we have a retryable error */
240 	while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
241 		status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) {
242 		status = HBA_GetDiscoveredPortAttributes(handle, portIndex,
243 				discIndex, attrs);
244 		if (status == HBA_STATUS_OK) {
245 			break;
246 		}
247 
248 		/* The odds of this occuring are very slim, but possible. */
249 		if (status == HBA_STATUS_ERROR_STALE_DATA) {
250 			/*
251 			 * If we hit a stale data scenario, we'll just tell the
252 			 * user to try again.
253 			 */
254 			status = HBA_STATUS_ERROR_TRY_AGAIN;
255 			break;
256 		}
257 		sleep(1);
258 	}
259 	if (status != HBA_STATUS_OK) {
260 		/* We encountered a non-retryable error */
261 		fprintf(stderr, MSGSTR(2504,
262 		"\nERROR: Unable to retrieve target port details (%s)"),
263 		name);
264 		printStatus(status);
265 		fprintf(stderr, "\n");
266 	}
267 	return (status);
268 }
269 
270 
271 /*ARGSUSED*/
272 int
273 fchba_display_port(int verbose)
274 {
275 	int retval = 0;
276 	HBA_HANDLE handle;
277 	HBA_ADAPTERATTRIBUTES hbaAttrs;
278 	HBA_PORTATTRIBUTES portAttrs;
279 	HBA_STATUS status;
280 	int count, adapterIndex, portIndex;
281 	char name[256];
282 	char *physical = NULL;
283 	char path[MAXPATHLEN];
284 
285 	if ((retval = loadLibrary())) {
286 	    return (retval);
287 	}
288 
289 	count = getNumberOfAdapters();
290 
291 	for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
292 	    if (skip_hba(adapterIndex)) {
293 		continue;
294 	    }
295 	    status = HBA_GetAdapterName(adapterIndex, (char *)&name);
296 	    if (status != HBA_STATUS_OK) {
297 		/* Just skip it, maybe it was DR'd */
298 		continue;
299 	    }
300 	    handle = HBA_OpenAdapter(name);
301 	    if (handle == 0) {
302 		/* Just skip it, maybe it was DR'd */
303 		continue;
304 	    }
305 
306 	    if (getAdapterAttrs(handle, name, &hbaAttrs)) {
307 		/* This should never happen, we'll just skip the adapter */
308 		HBA_CloseAdapter(handle);
309 		continue;
310 	    }
311 
312 	    for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
313 		    portIndex++) {
314 		if (getAdapterPortAttrs(handle, name, portIndex,
315 			&portAttrs)) {
316 		    continue;
317 		}
318 		physical = get_slash_devices_from_osDevName(
319 				portAttrs.OSDeviceName,
320 				STANDARD_DEVNAME_HANDLING);
321 		if (physical) {
322 			char *tmp = strstr(physical, ":fc");
323 			if (tmp) {
324 				*tmp = '\0';
325 				(void) snprintf(path, MAXPATHLEN, "%s:devctl",
326 					physical);
327 			} else {
328 				(void) snprintf(path, MAXPATHLEN, "%s",
329 					physical);
330 			}
331 			free(physical);
332 			physical = NULL;
333 			(void) printf("%-65s  ", path);
334 		} else {
335 			(void) printf("%-65s  ", portAttrs.OSDeviceName);
336 		}
337 		if (portAttrs.NumberofDiscoveredPorts > 0) {
338 		    printf(MSGSTR(2233, "CONNECTED\n"));
339 		} else {
340 		    printf(MSGSTR(2234, "NOT CONNECTED\n"));
341 		}
342 	    }
343 	}
344 	(void) HBA_FreeLibrary();
345 	return (retval);
346 }
347 
348 /*
349  * Internal routines/structure to deal with a path list
350  * so we can ensure uniqueness
351  */
352 struct path_entry {
353 	char path[MAXPATHLEN];
354 	HBA_UINT8 wwn[8];
355 	uchar_t dtype;
356 	struct path_entry *next;
357 };
358 void add_path(struct path_entry *head, struct path_entry *cur) {
359 	struct path_entry *tmp;
360 	for (tmp = head; tmp->next != NULL; tmp = tmp->next) { }
361 		tmp->next = cur;
362 }
363 struct path_entry *is_duplicate_path(struct path_entry *head, char *path) {
364 	struct path_entry *tmp;
365 	for (tmp = head; tmp != NULL; tmp = tmp->next) {
366 		if (strncmp(tmp->path, path, sizeof (tmp->path)) == 0) {
367 			return (tmp);
368 		}
369 	}
370 	return (NULL);
371 }
372 void free_path_list(struct path_entry *head) {
373 	struct path_entry *tmp;
374 	struct path_entry *tmp2;
375 	for (tmp = head; tmp != NULL; ) {
376 		tmp2 = tmp->next;
377 		free(tmp);
378 		tmp = tmp2;
379 	}
380 }
381 
382 
383 int
384 is_wwn(char *arg) {
385 	int i;
386 	if (strlen(arg) == 16) {
387 		for (i = 0; i < 16; i++) {
388 			if (!isxdigit(arg[i])) {
389 				return (0);
390 			}
391 		}
392 		return (1);
393 	}
394 	return (0);
395 }
396 
397 int
398 is_path(char *arg) {
399 	struct stat buf;
400 	if (stat(arg, &buf)) {
401 		return (0);
402 	}
403 	return (1);
404 }
405 
406 /* We take a wild guess for our first get target mappings call */
407 #define	MAP_GUESS	50
408 
409 HBA_STATUS
410 fetch_mappings(HBA_HANDLE handle, HBA_WWN pwwn, HBA_FCPTARGETMAPPINGV2 **map) {
411 	int loop = 0;
412 	int count = 0;
413 	HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
414 	*map = (HBA_FCPTARGETMAPPINGV2 *) calloc(1,
415 		(sizeof (HBA_FCPSCSIENTRYV2)* (MAP_GUESS-1)) +
416 		sizeof (HBA_FCPTARGETMAPPINGV2));
417 
418 	/* Loop as long as we have a retryable error */
419 	while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
420 		status == HBA_STATUS_ERROR_BUSY ||
421 		status == HBA_STATUS_ERROR_MORE_DATA) && loop++ < MAX_RETRIES) {
422 	    status = HBA_GetFcpTargetMappingV2(handle, pwwn, *map);
423 	    if (status == HBA_STATUS_OK) {
424 		break;
425 	    } else if (status == HBA_STATUS_ERROR_MORE_DATA) {
426 		count = (*map)->NumberOfEntries;
427 		free(*map);
428 		*map = (HBA_FCPTARGETMAPPINGV2 *) calloc(1,
429 		    (sizeof (HBA_FCPSCSIENTRYV2)* (count-1)) +
430 		    sizeof (HBA_FCPTARGETMAPPINGV2));
431 		(*map)->NumberOfEntries = count;
432 		continue;
433 	    }
434 	    sleep(1);
435 	}
436 	if (status != HBA_STATUS_OK) {
437 	    /* We encountered a non-retryable error */
438 	    fprintf(stderr, MSGSTR(2502,
439 		    "\nERROR: Unable to retrieve SCSI device paths "
440 		    "(HBA Port WWN %016llx)"),
441 		    wwnConversion(pwwn.wwn));
442 	    printStatus(status);
443 	    fprintf(stderr, "\n");
444 	}
445 	return (status);
446 }
447 
448 /*
449  * Returns the index of the first match, or -1 if no match
450  */
451 int
452 match_mappings(char *compare, HBA_FCPTARGETMAPPINGV2 *map) {
453 	int		mapIndex;
454 	char	*physical = NULL;
455 	char	*tmp;
456 	int		wwnCompare = 0;
457 	uint64_t	wwn;
458 
459 	if (map == NULL || compare == NULL) {
460 	    return (-1);
461 	}
462 
463 	if (is_wwn(compare)) {
464 	    wwnCompare = 1;
465 	    (void) sscanf(compare, "%016llx", &wwn);
466 	} else {
467 	    /* Convert the paths to phsyical paths */
468 	    physical = get_slash_devices_from_osDevName(compare,
469 			STANDARD_DEVNAME_HANDLING);
470 	}
471 
472 	for (mapIndex = 0; mapIndex < map->NumberOfEntries; mapIndex ++) {
473 	    if (wwnCompare) {
474 		if (wwn == wwnConversion(
475 			map->entry[mapIndex].FcpId.NodeWWN.wwn) ||
476 			wwn == wwnConversion(
477 			map->entry[mapIndex].FcpId.PortWWN.wwn)) {
478 		    return (mapIndex);
479 		}
480 	    } else {
481 		if (physical != NULL) {
482 		    tmp = get_slash_devices_from_osDevName(
483 			map->entry[mapIndex].ScsiId.OSDeviceName,
484 			STANDARD_DEVNAME_HANDLING);
485 		    if ((tmp != NULL) &&
486 			strncmp(physical, tmp, MAXPATHLEN) == 0) {
487 			free(physical);
488 			return (mapIndex);
489 		    }
490 		}
491 	    }
492 	}
493 	if (physical) {
494 	    free(physical);
495 	}
496 	return (-1);
497 }
498 
499 
500 /*
501  * returns non-zero on failure (aka HBA_STATUS_ERROR_*
502  */
503 int
504 loadLibrary() {
505 	int status = HBA_LoadLibrary();
506 	if (status != HBA_STATUS_OK) {
507 		fprintf(stderr, MSGSTR(2505,
508 			"ERROR: Unable to load HBA API library: "));
509 		printStatus(status);
510 		fprintf(stderr, "\n");
511 	}
512 	return (status);
513 }
514 
515 int
516 fchba_non_encl_probe() {
517 	HBA_HANDLE handle;
518 	HBA_ADAPTERATTRIBUTES hbaAttrs;
519 	HBA_PORTATTRIBUTES portAttrs;
520 	HBA_FCPTARGETMAPPINGV2    *map;
521 	HBA_STATUS status;
522 	int count, adapterIndex, portIndex, mapIndex;
523 	char name[256];
524 	struct path_entry *head = NULL;
525 	uint64_t	lun = 0;
526 	L_inquiry	inq;
527 	struct scsi_extended_sense sense;
528 	HBA_UINT8	scsiStatus;
529 	uint32_t	inquirySize = sizeof (inq), senseSize = sizeof (sense);
530 
531 	if (loadLibrary()) {
532 	    return (-1);
533 	}
534 
535 	count = getNumberOfAdapters();
536 
537 	/* Loop over all HBAs */
538 	for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
539 	    if (skip_hba(adapterIndex)) {
540 		continue;
541 	    }
542 	    status = HBA_GetAdapterName(adapterIndex, (char *)&name);
543 	    if (status != HBA_STATUS_OK) {
544 		/* May have been DR'd */
545 		continue;
546 	    }
547 	    handle = HBA_OpenAdapter(name);
548 	    if (handle == 0) {
549 		/* May have been DR'd */
550 		continue;
551 	    }
552 
553 	    if (getAdapterAttrs(handle, name, &hbaAttrs)) {
554 		/* Should not happen, just skip it */
555 		HBA_CloseAdapter(handle);
556 		continue;
557 	    }
558 
559 
560 	    /* Loop over all HBA Ports */
561 	    for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
562 		    portIndex++) {
563 		if (getAdapterPortAttrs(handle, name, portIndex,
564 			&portAttrs)) {
565 		    continue;
566 		}
567 
568 		if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
569 		    continue;
570 		}
571 
572 		/* Loop over all target Mapping entries */
573 		for (mapIndex = 0; mapIndex < map->NumberOfEntries;
574 		    mapIndex ++) {
575 			struct path_entry *tmpPath = NULL;
576 			int doInquiry = 0;
577 			if (!head) {
578 			head = (struct path_entry *)calloc(1,
579 				sizeof (struct path_entry));
580 			tmpPath = head;
581 			strncpy(head->path,
582 			    map->entry[mapIndex].ScsiId.OSDeviceName,
583 			    sizeof (map->entry[mapIndex].ScsiId.OSDeviceName));
584 			(void) memcpy(tmpPath->wwn,
585 			    map->entry[mapIndex].FcpId.NodeWWN.wwn,
586 			    sizeof (HBA_UINT8) * 8);
587 			doInquiry = 1;
588 			} else if (tmpPath = is_duplicate_path(head,
589 				map->entry[mapIndex].ScsiId.OSDeviceName)) {
590 				if (tmpPath->dtype != 0x1f) {
591 					doInquiry = 0;
592 				} else {
593 					doInquiry = 1;
594 				}
595 			} else {
596 			tmpPath = (struct path_entry *)
597 				calloc(1, sizeof (struct path_entry));
598 			strncpy(tmpPath->path,
599 			    map->entry[mapIndex].ScsiId.OSDeviceName,
600 			    sizeof (map->entry[mapIndex].ScsiId.OSDeviceName));
601 			(void) memcpy(tmpPath->wwn,
602 			    map->entry[mapIndex].FcpId.NodeWWN.wwn,
603 			    sizeof (HBA_UINT8) * 8);
604 			add_path(head, tmpPath);
605 			doInquiry = 1;
606 			}
607 
608 			if (doInquiry) {
609 				lun = map->entry[mapIndex].FcpId.FcpLun;
610 				memset(&inq, 0, sizeof (inq));
611 				memset(&sense, 0, sizeof (sense));
612 				status = HBA_ScsiInquiryV2(handle,
613 				    portAttrs.PortWWN,
614 				    map->entry[mapIndex].FcpId.PortWWN,
615 				    lun, 0, 0,
616 				    &inq, &inquirySize,
617 				    &scsiStatus,
618 				    &sense, &senseSize);
619 				if (status != HBA_STATUS_OK) {
620 					inq.inq_dtype = 0x1f;
621 				}
622 				tmpPath->dtype = inq.inq_dtype;
623 			}
624 		}
625 	}
626 	}
627 	if (head) {
628 		struct path_entry *tmp;
629 		printf(MSGSTR(2098, "\nFound Fibre Channel device(s):\n"));
630 		for (tmp = head; tmp != NULL; tmp = tmp->next) {
631 			printf("  ");
632 			printf(MSGSTR(90, "Node WWN:"));
633 			printf("%016llx  ", wwnConversion(tmp->wwn));
634 			fprintf(stdout, MSGSTR(35, "Device Type:"));
635 			(void) fflush(stdout);
636 
637 			if ((tmp->dtype & DTYPE_MASK) < 0x10) {
638 				fprintf(stdout, "%s",
639 				    dtype[tmp->dtype & DTYPE_MASK]);
640 			} else if ((tmp->dtype & DTYPE_MASK) < 0x1f) {
641 				fprintf(stdout, MSGSTR(2406,
642 				    "Reserved"));
643 			} else {
644 				fprintf(stdout, MSGSTR(2407,
645 				    "Unknown"));
646 			}
647 
648 			printf("\n    ");
649 			printf(MSGSTR(31, "Logical Path:%s"), tmp->path);
650 			printf("\n");
651 
652 		/* We probably shouldn't be using a g_fc interface here */
653 			if (Options & OPTION_P) {
654 				char *phys_path =
655 				get_slash_devices_from_osDevName(
656 				    tmp->path,
657 				    STANDARD_DEVNAME_HANDLING);
658 				if (phys_path != NULL) {
659 				fprintf(stdout, "    ");
660 				fprintf(stdout, MSGSTR(5, "Physical Path:"));
661 				fprintf(stdout, "\n     %s\n", phys_path);
662 				free(phys_path);
663 				}
664 			}
665 		}
666 		free_path_list(head);
667 	}
668 	HBA_FreeLibrary();
669 	return (0);
670 }
671 
672 
673 int
674 fchba_inquiry(char **argv)
675 {
676 	int		path_index = 0, found = 0;
677 	uint64_t	wwn;
678 	uint64_t	lun = 0;
679 	HBA_HANDLE handle;
680 	HBA_ADAPTERATTRIBUTES hbaAttrs;
681 	HBA_PORTATTRIBUTES portAttrs;
682 	HBA_FCPTARGETMAPPINGV2    *map;
683 	HBA_STATUS status;
684 	int count, adapterIndex, portIndex, mapIndex;
685 	char name[256];
686 	L_inquiry	inq;
687 	struct page80	serial;
688 	uint32_t	serialSize = sizeof (serial);
689 	struct scsi_extended_sense sense;
690 	HBA_UINT8	scsiStatus;
691 	uint32_t	inquirySize = sizeof (inq), senseSize = sizeof (sense);
692 	boolean_t	goodPath = B_FALSE;
693 	int		matched = 0, wwnCompare = 0;
694 	char		*tmp, *physical = NULL;
695 	int		ret = 0;
696 
697 	if (loadLibrary()) {
698 	    return (-1);
699 	}
700 	for (path_index = 0; argv[path_index] != NULL; path_index++) {
701 	    goodPath = B_FALSE;
702 	    found = 0;
703 
704 	    if (is_wwn(argv[path_index])) {
705 		(void) sscanf(argv[path_index], "%016llx", &wwn);
706 		wwnCompare = 1;
707 	    } else if (!is_path(argv[path_index])) {
708 		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
709 			argv[path_index]);
710 		fprintf(stderr, "\n");
711 		ret = -1;
712 		continue;
713 	    }
714 	    if (!wwnCompare) {
715 		/* Convert the paths to phsyical paths */
716 		physical = get_slash_devices_from_osDevName(argv[path_index],
717 			STANDARD_DEVNAME_HANDLING);
718 		if (!physical) {
719 		    fprintf(stderr, MSGSTR(112,
720 			"Error: Invalid pathname (%s)"),
721 			argv[path_index]);
722 		    fprintf(stderr, "\n");
723 		    ret = -1;
724 		    continue;
725 		}
726 	    }
727 
728 	    count = getNumberOfAdapters();
729 
730 	    /* Loop over all HBAs */
731 	    for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
732 		if (skip_hba(adapterIndex)) {
733 		    continue;
734 		}
735 		status = HBA_GetAdapterName(adapterIndex, (char *)&name);
736 		if (status != HBA_STATUS_OK) {
737 		    /* May have been DR'd */
738 		    continue;
739 		}
740 		handle = HBA_OpenAdapter(name);
741 		if (handle == 0) {
742 		    /* May have been DR'd */
743 		    continue;
744 		}
745 
746 		if (getAdapterAttrs(handle, name, &hbaAttrs)) {
747 		    /* Should never happen */
748 		    HBA_CloseAdapter(handle);
749 		    continue;
750 		}
751 
752 
753 		/* Loop over all HBA Ports */
754 		for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
755 			portIndex++) {
756 		    if (getAdapterPortAttrs(handle, name, portIndex,
757 			    &portAttrs)) {
758 			continue;
759 		    }
760 
761 		    if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
762 			continue;
763 		    }
764 
765 		    for (mapIndex = 0; mapIndex < map->NumberOfEntries;
766 			    mapIndex ++) {
767 			matched = 0;
768 			if (wwnCompare) {
769 			    if (wwn == wwnConversion(
770 				    map->entry[mapIndex].FcpId.NodeWWN.wwn) ||
771 				    wwn == wwnConversion(
772 				    map->entry[mapIndex].FcpId.PortWWN.wwn)) {
773 				lun = map->entry[mapIndex].FcpId.FcpLun;
774 				matched = 1;
775 			    }
776 			} else {
777 			    tmp = get_slash_devices_from_osDevName(
778 				    map->entry[mapIndex].ScsiId.OSDeviceName,
779 				    STANDARD_DEVNAME_HANDLING);
780 			    if ((tmp != NULL) && (strncmp(physical, tmp,
781 				    MAXPATHLEN) == 0)) {
782 				lun = map->entry[mapIndex].FcpId.FcpLun;
783 				matched = 1;
784 				free(tmp);
785 			    }
786 			}
787 
788 			if (matched) {
789 			    memset(&inq, 0, sizeof (inq));
790 			    memset(&sense, 0, sizeof (sense));
791 			    status = HBA_ScsiInquiryV2(handle,
792 				portAttrs.PortWWN,
793 				map->entry[mapIndex].FcpId.PortWWN,
794 				lun, 0, 0,
795 				&inq, &inquirySize,
796 				&scsiStatus,
797 				&sense, &senseSize);
798 			    if (status == HBA_STATUS_OK) {
799 				goodPath = B_TRUE;
800 				/*
801 				 * Call the inquiry cmd on page 0x80 only if
802 				 * the vendor supports page 0x80
803 				 */
804 				memset(&serial, 0, sizeof (serial));
805 				if ((find_supported_inq_page(handle,
806 					    portAttrs.PortWWN,
807 					    map->entry[mapIndex].FcpId.PortWWN,
808 					    lun, 0x80))) {
809 					status = HBA_ScsiInquiryV2(handle,
810 					    portAttrs.PortWWN,
811 					    map->entry[mapIndex].FcpId.PortWWN,
812 					    lun, 1, 0x80,
813 					    &serial, &serialSize,
814 					    &scsiStatus,
815 					    &sense, &senseSize);
816 					if (status != HBA_STATUS_OK) {
817 						strncpy(
818 						    (char *)serial.inq_serial,
819 						    "Unavailable",
820 						    sizeof (serial.inq_serial));
821 					}
822 				} else {
823 					strncpy((char *)serial.inq_serial,
824 					    "Unsupported",
825 					    sizeof (serial.inq_serial));
826 				}
827 				/*
828 				 * we are adding serial number information
829 				 * from 0x80.  If length is less than 39,
830 				 * then we want to increase length to 52 to
831 				 * reflect the fact that we have serial number
832 				 * information
833 				 */
834 				if (inq.inq_len < 39) {
835 					inq.inq_len = 52;
836 				}
837 				print_inq_data(argv[path_index],
838 				    map->entry[mapIndex].ScsiId.OSDeviceName,
839 				    inq, serial.inq_serial,
840 				    sizeof (serial.inq_serial));
841 				if (! wwnCompare) {
842 					found = 1;
843 					break;
844 				}
845 			    } else {
846 				fprintf(stderr, MSGSTR(2430,
847 				"Error: I/O failure communicating with %s  "),
848 				map->entry[mapIndex].ScsiId.OSDeviceName);
849 				printStatus(status);
850 				fprintf(stderr, "\n");
851 			    }
852 			}
853 		    }
854 		    if (found == 1) {
855 			    break;
856 		    }
857 		}
858 		if (found == 1) {
859 			break;
860 		}
861 	    }
862 
863 	    if (physical) {
864 		free(physical);
865 	    }
866 
867 	    if (!goodPath) {
868 		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
869 			argv[path_index]);
870 		fprintf(stderr, "\n");
871 		ret = -1;
872 	    }
873 	}
874 	return (ret);
875 }
876 
877 
878 
879 int
880 fchba_dump_map(char **argv)
881 {
882 	int		path_index = 0;
883 	uint64_t	wwn;
884 	uint64_t	lun = 0;
885 	HBA_HANDLE handle;
886 	HBA_ADAPTERATTRIBUTES hbaAttrs;
887 	HBA_PORTATTRIBUTES portAttrs;
888 	HBA_PORTATTRIBUTES discPortAttrs;
889 	HBA_FCPTARGETMAPPINGV2    *map;
890 	HBA_STATUS status;
891 	int count, adapterIndex, portIndex, mapIndex, discIndex;
892 	char name[256], *physical, *comp_phys;
893 	L_inquiry	inq;
894 	struct scsi_extended_sense sense;
895 	HBA_UINT8	scsiStatus;
896 	int		matched;
897 	int		done;
898 	uint32_t	inquirySize = sizeof (inq), senseSize = sizeof (sense);
899 	boolean_t	goodPath = B_FALSE;
900 	int		ret = 0;
901 	uint32_t	responseSize = DEFAULT_LUN_LENGTH;
902 	uchar_t		raw_luns[DEFAULT_LUN_LENGTH];
903 	struct rep_luns_rsp	*lun_resp;
904 
905 
906 	if (loadLibrary()) {
907 	    return (-1);
908 	}
909 	for (path_index = 0; argv[path_index] != NULL; path_index++) {
910 	    goodPath = B_FALSE;
911 
912 	    if (is_wwn(argv[path_index])) {
913 		(void) sscanf(argv[path_index], "%016llx", &wwn);
914 	    } else if (!is_path(argv[path_index])) {
915 		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
916 			argv[path_index]);
917 		fprintf(stderr, "\n");
918 		ret = -1;
919 		continue;
920 	    }
921 
922 	    count = getNumberOfAdapters();
923 
924 	    done = 0;
925 	    /* Loop over all HBAs */
926 	    for (adapterIndex = 0; adapterIndex < count && !done;
927 		    adapterIndex ++) {
928 		if (skip_hba(adapterIndex)) {
929 		    continue;
930 		}
931 		status = HBA_GetAdapterName(adapterIndex, (char *)&name);
932 		if (status != HBA_STATUS_OK) {
933 		    /* May have been DR'd */
934 		    continue;
935 		}
936 		handle = HBA_OpenAdapter(name);
937 		if (handle == 0) {
938 		    /* May have been DR'd */
939 		    continue;
940 		}
941 
942 		if (getAdapterAttrs(handle, name, &hbaAttrs)) {
943 		    /* Should never happen */
944 		    HBA_CloseAdapter(handle);
945 		    continue;
946 		}
947 
948 
949 		/* Loop over all HBA Ports */
950 		for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts && !done;
951 			portIndex++) {
952 		    if (getAdapterPortAttrs(handle, name, portIndex,
953 			    &portAttrs)) {
954 			continue;
955 		    }
956 
957 
958 		    matched = 0;
959 		    if (is_wwn(argv[path_index])) {
960 			if (wwn == wwnConversion(
961 				portAttrs.NodeWWN.wwn) ||
962 				wwn == wwnConversion(
963 				portAttrs.PortWWN.wwn)) {
964 			    matched = 1;
965 			}
966 		    } else {
967 			if (is_path(argv[path_index]) &&
968 			    ((physical = get_slash_devices_from_osDevName(
969 				argv[path_index],
970 				STANDARD_DEVNAME_HANDLING)) != NULL) &&
971 			    ((comp_phys = get_slash_devices_from_osDevName(
972 				portAttrs.OSDeviceName,
973 				STANDARD_DEVNAME_HANDLING)) != NULL)) {
974 			    char *tmp = strstr(physical, ":devctl");
975 			    if (tmp) {
976 				*tmp = '\0';
977 			    } else {
978 				tmp = strstr(physical, ":fc");
979 				if (tmp) {
980 					*tmp = '\0';
981 				}
982 			    }
983 			    if (strstr(comp_phys, physical)) {
984 				matched = 1;
985 			    }
986 			}
987 			if (physical) {
988 			    free(physical);
989 			    physical = NULL;
990 			}
991 			if (comp_phys) {
992 			    free(comp_phys);
993 			    comp_phys = NULL;
994 			}
995 		    }
996 
997 		    if (!fetch_mappings(handle, portAttrs.PortWWN, &map)) {
998 			mapIndex = match_mappings(argv[path_index], map);
999 			if (mapIndex >= 0) {
1000 			    matched = 1;
1001 			}
1002 		    } else {
1003 			continue;
1004 		    }
1005 
1006 		    if (matched) {
1007 			goodPath = B_TRUE;
1008 			printf(MSGSTR(2095,
1009 				"Pos  Port_ID Hard_Addr Port WWN"
1010 				"         Node WWN         Type\n"));
1011 			for (discIndex = 0;
1012 				discIndex < portAttrs.NumberofDiscoveredPorts;
1013 				discIndex++) {
1014 			    if (getDiscPortAttrs(handle, name, portIndex,
1015 				    discIndex, &discPortAttrs)) {
1016 				/* Move on to the next target */
1017 				continue;
1018 			    }
1019 
1020 			    printf("%-4d %-6x  %-6x   %016llx %016llx",
1021 				    discIndex,
1022 				    discPortAttrs.PortFcId, 0,
1023 				    wwnConversion(discPortAttrs.PortWWN.wwn),
1024 				    wwnConversion(discPortAttrs.NodeWWN.wwn));
1025 
1026 				/*
1027 				 * devices are not all required to respond to
1028 				 * Scsi Inquiry calls sent to LUN 0.  We must
1029 				 * fisrt issue a ReportLUN and then send the
1030 				 * SCSI Inquiry call to the first LUN Returned
1031 				 * from the ReportLUN call
1032 				 */
1033 			    memset(&sense, 0, sizeof (sense));
1034 			    status = HBA_ScsiReportLUNsV2(handle,
1035 				portAttrs.PortWWN,
1036 				discPortAttrs.PortWWN,
1037 				(void *)raw_luns, &responseSize, &scsiStatus,
1038 				(void *)&sense, &senseSize);
1039 			    if (status == HBA_STATUS_OK) {
1040 				    lun_resp =
1041 					(struct rep_luns_rsp *)
1042 					(unsigned long)raw_luns;
1043 				    lun = ntohll(
1044 					wwnConversion(lun_resp->lun[0].val));
1045 			    } else {
1046 				/*
1047 				 * in case we are unable to retrieve report
1048 				 * LUN data, we will blindly try sending the
1049 				 * INQUIRY to lun 0.
1050 				 */
1051 				lun = 0;
1052 			    }
1053 			    memset(&sense, 0, sizeof (sense));
1054 			    status = HBA_ScsiInquiryV2(handle,
1055 				    portAttrs.PortWWN,
1056 				    discPortAttrs.PortWWN,
1057 				    lun, 0, 0,
1058 				    &inq, &inquirySize,
1059 				    &scsiStatus,
1060 				    &sense, &senseSize);
1061 			    if (status != HBA_STATUS_OK) {
1062 				inq.inq_dtype = 0x1f;
1063 			    }
1064 			    print_fabric_dtype_prop(portAttrs.PortWWN.wwn,
1065 				map->entry[mapIndex].FcpId.PortWWN.wwn,
1066 				inq.inq_dtype);
1067 			}
1068 			/* Now dump this HBA's stats */
1069 			printf("%-4d %-6x  %-6x   %016llx %016llx",
1070 			    discIndex,
1071 			    portAttrs.PortFcId, 0,
1072 			    wwnConversion(portAttrs.PortWWN.wwn),
1073 			    wwnConversion(portAttrs.NodeWWN.wwn));
1074 			print_fabric_dtype_prop(portAttrs.PortWWN.wwn,
1075 			    portAttrs.PortWWN.wwn, 0x1f);
1076 			done = 1;
1077 		    }
1078 		}
1079 	    }
1080 	    if (!goodPath) {
1081 		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
1082 			argv[path_index]);
1083 		fprintf(stderr, "\n");
1084 		ret = -1;
1085 	    }
1086 	}
1087 	return (ret);
1088 }
1089 
1090 int
1091 fchba_display_link_status(char **argv)
1092 {
1093 	int		path_index = 0;
1094 	uint64_t	wwn;
1095 	HBA_HANDLE handle;
1096 	HBA_ADAPTERATTRIBUTES hbaAttrs;
1097 	HBA_PORTATTRIBUTES portAttrs;
1098 	HBA_PORTATTRIBUTES discPortAttrs;
1099 	HBA_FCPTARGETMAPPINGV2    *map;
1100 	HBA_STATUS status;
1101 	int count, adapterIndex, portIndex, discIndex;
1102 	char name[256], *physical, *comp_phys;
1103 	int		matched;
1104 	struct fc_rls_acc_params	rls;
1105 	uint32_t	rls_size = sizeof (rls);
1106 	boolean_t	goodPath = B_FALSE;
1107 	int		ret = 0;
1108 
1109 	if (loadLibrary()) {
1110 	    return (-1);
1111 	}
1112 	for (path_index = 0; argv[path_index] != NULL; path_index++) {
1113 	    goodPath = B_FALSE;
1114 
1115 	    if (is_wwn(argv[path_index])) {
1116 		(void) sscanf(argv[path_index], "%016llx", &wwn);
1117 	    } else if (!is_path(argv[path_index])) {
1118 		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
1119 			argv[path_index]);
1120 		fprintf(stderr, "\n");
1121 		ret = -1;
1122 		continue;
1123 	    }
1124 
1125 	    count = getNumberOfAdapters();
1126 
1127 	    /* Loop over all HBAs */
1128 	    for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
1129 		if (skip_hba(adapterIndex)) {
1130 		    continue;
1131 		}
1132 		status = HBA_GetAdapterName(adapterIndex, (char *)&name);
1133 		if (status != HBA_STATUS_OK) {
1134 		    /* May have been DR'd */
1135 		    continue;
1136 		}
1137 		handle = HBA_OpenAdapter(name);
1138 		if (handle == 0) {
1139 		    /* May have been DR'd */
1140 		    continue;
1141 		}
1142 
1143 		if (getAdapterAttrs(handle, name, &hbaAttrs)) {
1144 		    /* Should never happen */
1145 		    HBA_CloseAdapter(handle);
1146 		    continue;
1147 		}
1148 
1149 
1150 		/* Loop over all HBA Ports */
1151 		for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
1152 			portIndex++) {
1153 		    if (getAdapterPortAttrs(handle, name, portIndex,
1154 			    &portAttrs)) {
1155 			continue;
1156 		    }
1157 
1158 		    matched = 0;
1159 		    if (is_wwn(argv[path_index])) {
1160 			if (wwn == wwnConversion(
1161 				portAttrs.NodeWWN.wwn) ||
1162 				wwn == wwnConversion(
1163 				portAttrs.PortWWN.wwn)) {
1164 			    matched = 1;
1165 			}
1166 		    } else {
1167 			if (is_path(argv[path_index]) &&
1168 			    ((physical = get_slash_devices_from_osDevName(
1169 				argv[path_index],
1170 				STANDARD_DEVNAME_HANDLING)) != NULL) &&
1171 			    ((comp_phys = get_slash_devices_from_osDevName(
1172 				portAttrs.OSDeviceName,
1173 				STANDARD_DEVNAME_HANDLING)) != NULL)) {
1174 			    char *tmp = strstr(physical, ":devctl");
1175 			    if (tmp) {
1176 				*tmp = '\0';
1177 			    } else {
1178 				tmp = strstr(physical, ":fc");
1179 				if (tmp) {
1180 					*tmp = '\0';
1181 				}
1182 			    }
1183 			    if (strstr(comp_phys, physical)) {
1184 				matched = 1;
1185 			    }
1186 			}
1187 			if (physical) {
1188 			    free(physical);
1189 			    physical = NULL;
1190 			}
1191 			if (comp_phys) {
1192 			    free(comp_phys);
1193 			    comp_phys = NULL;
1194 			}
1195 		    }
1196 
1197 		    if (!matched) {
1198 			if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
1199 			    continue;
1200 			}
1201 		    }
1202 
1203 		    if (matched || match_mappings(argv[path_index], map) >= 0) {
1204 			goodPath = B_TRUE;
1205 			fprintf(stdout,
1206 				MSGSTR(2007, "\nLink Error Status "
1207 				"information for loop:%s\n"), argv[path_index]);
1208 			fprintf(stdout, MSGSTR(2008, "al_pa   lnk fail "
1209 				"   sync loss   signal loss   sequence err"
1210 				"   invalid word   CRC\n"));
1211 
1212 			for (discIndex = 0;
1213 				discIndex < portAttrs.NumberofDiscoveredPorts;
1214 				discIndex++) {
1215 
1216 
1217 			    if (getDiscPortAttrs(handle, name, portIndex,
1218 				    discIndex, &discPortAttrs)) {
1219 				continue;
1220 			    }
1221 
1222 			    status = HBA_SendRLS(handle, portAttrs.PortWWN,
1223 					discPortAttrs.PortWWN,
1224 					&rls, &rls_size);
1225 			    if (status != HBA_STATUS_OK) {
1226 				memset(&rls, 0xff, sizeof (rls));
1227 			    }
1228 
1229 			    if ((rls.rls_link_fail == 0xffffffff) &&
1230 				(rls.rls_sync_loss == 0xffffffff) &&
1231 				(rls.rls_sig_loss == 0xffffffff) &&
1232 				(rls.rls_prim_seq_err == 0xffffffff) &&
1233 				(rls.rls_invalid_word == 0xffffffff) &&
1234 				(rls.rls_invalid_crc == 0xffffffff)) {
1235 				    fprintf(stdout,
1236 					"%x\t%-12d%-12d%-14d%-15d%-15d%-12d\n",
1237 					    discPortAttrs.PortFcId,
1238 					    rls.rls_link_fail,
1239 					    rls.rls_sync_loss,
1240 					    rls.rls_sig_loss,
1241 					    rls.rls_prim_seq_err,
1242 					    rls.rls_invalid_word,
1243 					    rls.rls_invalid_crc);
1244 			    } else {
1245 				    fprintf(stdout,
1246 					"%x\t%-12u%-12u%-14u%-15u%-15u%-12u\n",
1247 					    discPortAttrs.PortFcId,
1248 					    rls.rls_link_fail,
1249 					    rls.rls_sync_loss,
1250 					    rls.rls_sig_loss,
1251 					    rls.rls_prim_seq_err,
1252 					    rls.rls_invalid_word,
1253 					    rls.rls_invalid_crc);
1254 			    }
1255 
1256 
1257 			}
1258 			/* Now dump this HBA's stats */
1259 			status = HBA_SendRLS(handle, portAttrs.PortWWN,
1260 				portAttrs.PortWWN,
1261 				&rls, &rls_size);
1262 			if (status != HBA_STATUS_OK) {
1263 			    memset(&rls, 0xff, sizeof (rls));
1264 			}
1265 
1266 			if ((rls.rls_link_fail == 0xffffffff) &&
1267 				(rls.rls_sync_loss == 0xffffffff) &&
1268 				(rls.rls_sig_loss == 0xffffffff) &&
1269 				(rls.rls_prim_seq_err == 0xffffffff) &&
1270 				(rls.rls_invalid_word == 0xffffffff) &&
1271 				(rls.rls_invalid_crc == 0xffffffff)) {
1272 			    fprintf(stdout,
1273 				    "%x\t%-12d%-12d%-14d%-15d%-15d%-12d\n",
1274 				    portAttrs.PortFcId,
1275 				    rls.rls_link_fail,
1276 				    rls.rls_sync_loss,
1277 				    rls.rls_sig_loss,
1278 				    rls.rls_prim_seq_err,
1279 				    rls.rls_invalid_word,
1280 				    rls.rls_invalid_crc);
1281 			} else {
1282 			    fprintf(stdout,
1283 				    "%x\t%-12u%-12u%-14u%-15u%-15u%-12u\n",
1284 				    portAttrs.PortFcId,
1285 				    rls.rls_link_fail,
1286 				    rls.rls_sync_loss,
1287 				    rls.rls_sig_loss,
1288 				    rls.rls_prim_seq_err,
1289 				    rls.rls_invalid_word,
1290 				    rls.rls_invalid_crc);
1291 			}
1292 		    }
1293 		}
1294 	    }
1295 	    if (!goodPath) {
1296 		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
1297 			argv[path_index]);
1298 		fprintf(stderr, "\n");
1299 		ret = -1;
1300 	    }
1301 	}
1302 	(void) fprintf(stdout,
1303 		MSGSTR(2009, "NOTE: These LESB counts are not"
1304 		" cleared by a reset, only power cycles.\n"
1305 		"These counts must be compared"
1306 		" to previously read counts.\n"));
1307 	return (ret);
1308 }
1309 
1310 typedef struct _PathInformation {
1311 	char	pathClass[MAXPATHLEN];
1312 	char	pathState[MAXPATHLEN];
1313 	int32_t	pathInfoState;
1314 	int32_t	pathInfoExternalState;
1315 } PathInformation;
1316 
1317 struct lun_tracking {
1318 	HBA_FCPSCSIENTRYV2  map;
1319 	HBA_WWN	hba_pwwn;
1320 	char	hba_path[MAXPATHLEN];
1321 	PathInformation info;
1322 
1323 	/* Points to another lun_tracking instance with the same map->LUID */
1324 	struct lun_tracking	*next_path;
1325 
1326 	/* Points to next lun_tracking with a different map->LUID */
1327 	struct lun_tracking *next_lun;
1328 };
1329 
1330 
1331 static const char VHCI_COMPONENT[] = "scsi_vhci";
1332 static void
1333 scsi_vhci_details(struct lun_tracking *lun)
1334 {
1335 	HBA_FCPSCSIENTRYV2 entry = lun->map;
1336 	int		retval = 0;
1337 	int		pathcnt, i, count, found = 0;
1338 	char		temppath[MAXPATHLEN];
1339 	char		buf[MAXPATHLEN];
1340 	char	*path_state[5];
1341 
1342 	char	*phys_path = get_slash_devices_from_osDevName(
1343 				entry.ScsiId.OSDeviceName,
1344 				STANDARD_DEVNAME_HANDLING);
1345 	char	*devPath = NULL;
1346 	char	*trailingCruft = NULL;
1347 	char	devaddr[MAXPATHLEN];
1348 	sv_iocdata_t	ioc;
1349 	int	prop_buf_size = SV_PROP_MAX_BUF_SIZE;
1350 	char	*path_class_val = NULL;
1351 	char	client_path[MAXPATHLEN];
1352 	char	phci_path[MAXPATHLEN];
1353 
1354 	/* Only proceed if we are an mpxio path */
1355 	if (phys_path == NULL || strstr(phys_path, VHCI_COMPONENT) == NULL) {
1356 	    return;
1357 	}
1358 
1359 	path_state[0] = MSGSTR(2400, "INIT");
1360 	path_state[1] = MSGSTR(2401, "ONLINE");
1361 	path_state[2] = MSGSTR(2402, "STANDBY");
1362 	path_state[3] = MSGSTR(2403, "FAULT");
1363 	path_state[4] = MSGSTR(2404, "OFFLINE");
1364 
1365 	sprintf(devaddr, "%016llx,%x", wwnConversion(
1366 		entry.FcpId.PortWWN.wwn),
1367 		entry.ScsiId.ScsiOSLun);
1368 
1369 	/* First get the controller path */
1370 	sprintf(temppath, "/dev/cfg/c%d", entry.ScsiId.ScsiBusNumber);
1371 	if ((count = readlink(temppath, buf, sizeof (buf)))) {
1372 	    buf[count] = '\0';
1373 	    /* Now skip over the leading "../.." */
1374 	    devPath = strstr(buf, "/devices/");
1375 	    if (devPath == NULL) {
1376 		strcpy(lun->info.pathClass, "Unavailable");
1377 		strcpy(lun->info.pathState, "Unavailable");
1378 		free(phys_path);
1379 		return;
1380 	    }
1381 
1382 	    /* Now chop off the trailing ":xxx" portion if present */
1383 	    trailingCruft = strrchr(buf, ':');
1384 	    if (trailingCruft) {
1385 		trailingCruft[0] = '\0';
1386 	    }
1387 	} else {
1388 	    strcpy(lun->info.pathClass, "Unavailable");
1389 	    strcpy(lun->info.pathState, "Unavailable");
1390 	    free(phys_path);
1391 	    return;
1392 	}
1393 
1394 	ioc.client = client_path;
1395 	ioc.phci = phci_path;
1396 
1397 	retval = get_scsi_vhci_pathinfo(phys_path, &ioc, &pathcnt);
1398 	if (retval != 0) {
1399 	    print_errString(retval, NULL);
1400 	    exit(-1);
1401 	}
1402 
1403 	for (i = 0; i < pathcnt; i++) {
1404 	    nvlist_t *nvl;
1405 	    if (strstr(devPath, ioc.ret_buf[i].device.ret_phci)) {
1406 		/* This could break someday if MPxIO changes devaddr */
1407 		if (strstr(ioc.ret_buf[i].ret_addr, devaddr)) {
1408 		    retval = nvlist_unpack(ioc.ret_buf[i].ret_prop.buf,
1409 			prop_buf_size, &nvl, 0);
1410 		    if (retval != 0) {
1411 			strcpy(lun->info.pathClass,
1412 			    "UNKNOWN PROB");
1413 		    } else {
1414 			strcpy(lun->info.pathState,
1415 			    path_state[ioc.ret_buf[i].ret_state]);
1416 			lun->info.pathInfoState = ioc.ret_buf[i].ret_state;
1417 			lun->info.pathInfoExternalState =
1418 			    ioc.ret_buf[i].ret_ext_state;
1419 			if (nvlist_lookup_string(nvl, "path-class",
1420 				&path_class_val) == 0) {
1421 			    strcpy(lun->info.pathClass, path_class_val);
1422 			} else {
1423 			    strcpy(lun->info.pathClass, "UNKNOWN");
1424 			}
1425 		    }
1426 		    nvlist_free(nvl);
1427 		    found++;
1428 		    break;
1429 		}
1430 	    }
1431 
1432 	}
1433 
1434 	if (!found) {
1435 	    strcpy(lun->info.pathClass, "Unavailable");
1436 	    strcpy(lun->info.pathState, "Unavailable");
1437 	}
1438 	free(phys_path);
1439 
1440 	/* free everything we alloced */
1441 	for (i = 0; i < ioc.buf_elem; i++) {
1442 		free(ioc.ret_buf[i].ret_prop.buf);
1443 		free(ioc.ret_buf[i].ret_prop.ret_buf_size);
1444 	}
1445 	free(ioc.ret_buf);
1446 
1447 }
1448 
1449 /* Utility routine to add new entries to the list (ignores dups) */
1450 static void
1451 add_lun_path(struct lun_tracking *head, HBA_FCPSCSIENTRYV2  *map,
1452 	    HBA_WWN pwwn, char *path)
1453 {
1454 	struct lun_tracking *tmp = NULL, *cmp = NULL;
1455 
1456 	for (tmp = head; tmp != NULL; tmp = tmp->next_lun) {
1457 	    if (memcmp(&tmp->map.LUID, &map->LUID,
1458 		    sizeof (HBA_LUID)) == 0) {
1459 
1460 		/* Ensure this isn't a duplicate */
1461 		for (cmp = tmp; cmp->next_path != NULL;
1462 			    cmp = cmp->next_path) {
1463 		    if (memcmp(&cmp->map, map, sizeof (cmp->map)) == 0) {
1464 			return;
1465 		    }
1466 		}
1467 		if (memcmp(&cmp->map, map, sizeof (cmp->map)) == 0) {
1468 		    return;
1469 		}
1470 
1471 		/* We have a new entry to add */
1472 		cmp->next_path = (struct lun_tracking *)calloc(1,
1473 		    sizeof (struct lun_tracking));
1474 		cmp = cmp->next_path;
1475 		(void) memcpy(&cmp->map, map,
1476 		    sizeof (cmp->map));
1477 		(void) memcpy(&cmp->hba_pwwn, &pwwn,
1478 			sizeof (cmp->hba_pwwn));
1479 		(void) snprintf(cmp->hba_path, MAXPATHLEN,
1480 		    path);
1481 		scsi_vhci_details(cmp);
1482 		return;
1483 	    }
1484 	}
1485 	/* Append a new LUN at the end of the list */
1486 	for (tmp = head; tmp->next_lun != NULL; tmp = tmp->next_lun) {}
1487 	tmp->next_lun = (struct lun_tracking *)calloc(1,
1488 		sizeof (struct lun_tracking));
1489 	tmp = tmp->next_lun;
1490 	(void) memcpy(&tmp->map, map,
1491 		sizeof (tmp->map));
1492 	(void) memcpy(&tmp->hba_pwwn, &pwwn,
1493 		sizeof (tmp->hba_pwwn));
1494 	(void) snprintf(tmp->hba_path, MAXPATHLEN,
1495 		path);
1496 	scsi_vhci_details(tmp);
1497 }
1498 
1499 /*ARGSUSED*/
1500 int
1501 fchba_display_config(char **argv, int option_t_input, int argc)
1502 {
1503 	int		path_index = 0;
1504 	uint64_t	wwn;
1505 	uint64_t	lun = 0;
1506 	HBA_HANDLE handle;
1507 	HBA_ADAPTERATTRIBUTES hbaAttrs;
1508 	HBA_PORTATTRIBUTES portAttrs;
1509 	HBA_FCPTARGETMAPPINGV2    *map;
1510 	HBA_STATUS status;
1511 	int count, adapterIndex, portIndex;
1512 	char name[256];
1513 	L_inquiry	inq;
1514 	struct scsi_extended_sense sense;
1515 	struct page80	serial;
1516 	HBA_UINT8	scsiStatus;
1517 	uint32_t	inquirySize = sizeof (inq), senseSize = sizeof (sense);
1518 	uint32_t	serialSize = sizeof (serial);
1519 	struct mode_page	*pg_hdr;
1520 	uchar_t		*pg_buf;
1521 	float		lunMbytes;
1522 	struct capacity_data_struct cap_data;
1523 	uint32_t	    cap_data_size = sizeof (cap_data);
1524 	struct mode_header_g1	*mode_header_ptr;
1525 	int		offset;
1526 	char *phys_path = NULL;
1527 	int		mpxio = 0;
1528 	int		wwnCompare = 0;
1529 	char	    *physical = NULL;
1530 	struct lun_tracking	*head = NULL;
1531 	boolean_t	goodPath = B_FALSE;
1532 	int		ret = 0;
1533 
1534 
1535 
1536 	if ((status = loadLibrary())) {
1537 	    return (-1);
1538 	}
1539 	for (path_index = 0; argv[path_index] != NULL; path_index++) {
1540 	    goodPath = B_FALSE;
1541 
1542 	    if (is_wwn(argv[path_index])) {
1543 		(void) sscanf(argv[path_index], "%016llx", &wwn);
1544 		wwnCompare = 1;
1545 	    } else if (!is_path(argv[path_index])) {
1546 		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
1547 			argv[path_index]);
1548 		fprintf(stderr, "\n");
1549 		ret = -1;
1550 		continue;
1551 	    }
1552 	    if (!wwnCompare) {
1553 		/* Convert the paths to phsyical paths */
1554 		physical = get_slash_devices_from_osDevName(argv[path_index],
1555 			STANDARD_DEVNAME_HANDLING);
1556 		if (!physical) {
1557 		    fprintf(stderr, MSGSTR(112,
1558 			"Error: Invalid pathname (%s)"),
1559 			argv[path_index]);
1560 		    fprintf(stderr, "\n");
1561 		    ret = -1;
1562 		    continue;
1563 		}
1564 	    }
1565 
1566 	    count = getNumberOfAdapters();
1567 
1568 
1569 		/*
1570 		 * We have to loop twice to ensure we don't miss any
1571 		 * extra paths for other targets in a multi-target device
1572 		 */
1573 
1574 	    /* First check WWN/path comparisons */
1575 	    for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
1576 		if (skip_hba(adapterIndex)) {
1577 		    continue;
1578 		}
1579 		status = HBA_GetAdapterName(adapterIndex, (char *)&name);
1580 		if (status != HBA_STATUS_OK) {
1581 		    /* May have been DR'd */
1582 		    continue;
1583 		}
1584 		handle = HBA_OpenAdapter(name);
1585 		if (handle == 0) {
1586 		    /* May have been DR'd */
1587 		    continue;
1588 		}
1589 		if (getAdapterAttrs(handle, name, &hbaAttrs)) {
1590 		    /* Should never happen */
1591 		    HBA_CloseAdapter(handle);
1592 		    continue;
1593 		}
1594 
1595 		/* Loop over all HBA Ports */
1596 		for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
1597 			portIndex++) {
1598 		    int	    matched = 0;
1599 		    int	    mapIndex;
1600 		    char	    *tmp;
1601 		    if (getAdapterPortAttrs(handle, name, portIndex,
1602 			    &portAttrs)) {
1603 			continue;
1604 		    }
1605 
1606 		    if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
1607 			continue;
1608 		    }
1609 
1610 
1611 
1612 		    for (mapIndex = 0; mapIndex < map->NumberOfEntries;
1613 			    mapIndex ++) {
1614 			matched = 0;
1615 			if (wwnCompare) {
1616 			    if (wwn == wwnConversion(
1617 				    map->entry[mapIndex].FcpId.NodeWWN.wwn) ||
1618 				    wwn == wwnConversion(
1619 				    map->entry[mapIndex].FcpId.PortWWN.wwn)) {
1620 				matched = 1;
1621 			    }
1622 			} else {
1623 			    tmp = get_slash_devices_from_osDevName(
1624 				    map->entry[mapIndex].ScsiId.OSDeviceName,
1625 				    STANDARD_DEVNAME_HANDLING);
1626 			    if ((tmp != NULL) && (strncmp(physical, tmp,
1627 				    MAXPATHLEN) == 0)) {
1628 				matched = 1;
1629 				free(tmp);
1630 			    }
1631 			}
1632 			if (matched && head == NULL) {
1633 			    goodPath = B_TRUE;
1634 			    head  = (struct lun_tracking *)calloc(1,
1635 				    sizeof (struct lun_tracking));
1636 			    (void) memcpy(&head->map, &map->entry[mapIndex],
1637 				    sizeof (head->map));
1638 			    (void) memcpy(&head->hba_pwwn, &portAttrs.PortWWN,
1639 				    sizeof (head->hba_pwwn));
1640 			    (void) snprintf(head->hba_path, MAXPATHLEN,
1641 				portAttrs.OSDeviceName);
1642 			    scsi_vhci_details(head);
1643 			} else if (matched) {
1644 			    goodPath = B_TRUE;
1645 			    add_lun_path(head, &map->entry[mapIndex],
1646 				portAttrs.PortWWN, portAttrs.OSDeviceName);
1647 			}
1648 		    }
1649 		}
1650 	    }
1651 
1652 	    if (physical) {
1653 		free(physical);
1654 	    }
1655 
1656 	    /* Now do it again and look for matching LUIDs (aka GUIDs) */
1657 	    for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
1658 		if (skip_hba(adapterIndex)) {
1659 		    continue;
1660 		}
1661 		status = HBA_GetAdapterName(adapterIndex, (char *)&name);
1662 		if (status != HBA_STATUS_OK) {
1663 		    /* May have been DR'd */
1664 		    continue;
1665 		}
1666 		handle = HBA_OpenAdapter(name);
1667 		if (handle == 0) {
1668 		    /* May have been DR'd */
1669 		    continue;
1670 		}
1671 
1672 		if (getAdapterAttrs(handle, name, &hbaAttrs)) {
1673 		    /* Should never happen */
1674 		    HBA_CloseAdapter(handle);
1675 		    continue;
1676 		}
1677 
1678 
1679 		/* Loop over all HBA Ports */
1680 		for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
1681 			portIndex++) {
1682 		    int	    matched = 0;
1683 		    int	    mapIndex;
1684 		    if (getAdapterPortAttrs(handle, name, portIndex,
1685 			    &portAttrs)) {
1686 			continue;
1687 		    }
1688 
1689 		    if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
1690 			continue;
1691 		    }
1692 
1693 
1694 		    for (mapIndex = 0; mapIndex < map->NumberOfEntries;
1695 			    mapIndex ++) {
1696 			struct lun_tracking *outer;
1697 			matched = 0;
1698 			for (outer = head; outer != NULL;
1699 				    outer = outer->next_lun) {
1700 			    struct lun_tracking *inner;
1701 			    for (inner = outer; inner != NULL;
1702 				    inner = inner->next_path) {
1703 				if (memcmp(&inner->map.LUID,
1704 					&map->entry[mapIndex].LUID,
1705 					sizeof (HBA_LUID)) == 0) {
1706 				    matched = 1;
1707 				    break;
1708 				}
1709 			    }
1710 			    if (matched) {
1711 				break;
1712 			    }
1713 			}
1714 			if (matched && head == NULL) {
1715 			    goodPath = B_TRUE;
1716 			    head  = (struct lun_tracking *)calloc(1,
1717 				    sizeof (struct lun_tracking));
1718 			    (void) memcpy(&head->map, &map->entry[mapIndex],
1719 				    sizeof (head->map));
1720 			    (void) memcpy(&head->hba_pwwn, &portAttrs.PortWWN,
1721 				    sizeof (head->hba_pwwn));
1722 			    (void) snprintf(head->hba_path, MAXPATHLEN,
1723 				portAttrs.OSDeviceName);
1724 			    scsi_vhci_details(head);
1725 			} else if (matched) {
1726 			    goodPath = B_TRUE;
1727 			    add_lun_path(head, &map->entry[mapIndex],
1728 				portAttrs.PortWWN, portAttrs.OSDeviceName);
1729 			}
1730 		    }
1731 		}
1732 	    }
1733 	    if (!goodPath) {
1734 		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
1735 			argv[path_index]);
1736 		fprintf(stderr, "\n");
1737 		ret = -1;
1738 		/* Just bomb out instead of going on */
1739 		return (ret);
1740 	    }
1741 	}
1742 
1743 	/* Now display all the LUNs that we found that matched */
1744 	{
1745 	    struct lun_tracking *first_time;
1746 	    struct lun_tracking *tmp_path;
1747 	    for (first_time = head; first_time != NULL;
1748 		    first_time = first_time->next_lun) {
1749 		struct lun_tracking *path;
1750 		phys_path = get_slash_devices_from_osDevName(
1751 		    first_time->map.ScsiId.OSDeviceName,
1752 		    STANDARD_DEVNAME_HANDLING);
1753 		/* Change behavior if this is an MPxIO device */
1754 		if (phys_path != NULL) {
1755 		    if (strstr(phys_path, VHCI_COMPONENT) != NULL) {
1756 			mpxio = 1;
1757 		    }
1758 		}
1759 
1760 		for (tmp_path = first_time; tmp_path != NULL;
1761 			tmp_path = tmp_path->next_path) {
1762 			if (mpxio && (strncmp(tmp_path->info.pathState,
1763 			    "ONLINE", strlen(tmp_path->info.pathState)))) {
1764 				/* continue to next online path */
1765 				continue;
1766 			}
1767 			status = HBA_OpenAdapterByWWN(&handle,
1768 			    tmp_path->hba_pwwn);
1769 			if (status != HBA_STATUS_OK) {
1770 				fprintf(stderr, MSGSTR(2431,
1771 				    "Error: Failed to get handle for %s  "),
1772 				    tmp_path->hba_path);
1773 				printStatus(status);
1774 				fprintf(stderr, "\n");
1775 				/* continue to next path */
1776 				continue;
1777 			}
1778 
1779 			lun = tmp_path->map.FcpId.FcpLun;
1780 			memset(&inq, 0, sizeof (inq));
1781 			memset(&sense, 0, sizeof (sense));
1782 
1783 			status = HBA_ScsiInquiryV2(handle,
1784 				tmp_path->hba_pwwn,
1785 				tmp_path->map.FcpId.PortWWN,
1786 				lun, 0, 0,
1787 				&inq, &inquirySize,
1788 				&scsiStatus,
1789 				&sense, &senseSize);
1790 
1791 			if (status == HBA_STATUS_OK) {
1792 				break;
1793 			}
1794 			HBA_CloseAdapter(handle);
1795 		}
1796 
1797 		if (tmp_path == NULL) {
1798 			fprintf(stderr, MSGSTR(2430,
1799 			    "Error: I/O failure communicating with %s  "),
1800 			    first_time->map.ScsiId.OSDeviceName);
1801 			printStatus(status);
1802 			fprintf(stderr, "\n");
1803 			continue;
1804 		}
1805 
1806 		switch ((inq.inq_dtype & DTYPE_MASK)) {
1807 		case DTYPE_DIRECT:
1808 		    fprintf(stdout, MSGSTR(121,
1809 			    "DEVICE PROPERTIES for disk: %s\n"),
1810 			    first_time->map.ScsiId.OSDeviceName);
1811 		    break;
1812 		case DTYPE_SEQUENTIAL: /* Tape */
1813 		    fprintf(stdout, MSGSTR(2249,
1814 			    "DEVICE PROPERTIES for tape: %s\n"),
1815 			    first_time->map.ScsiId.OSDeviceName);
1816 		    break;
1817 		default:
1818 		    fprintf(stdout, MSGSTR(2250,
1819 			    "DEVICE PROPERTIES for: %s\n"),
1820 			    first_time->map.ScsiId.OSDeviceName);
1821 		    break;
1822 		}
1823 		fprintf(stdout, "  ");
1824 		fprintf(stdout, MSGSTR(3, "Vendor:"));
1825 		fprintf(stdout, "\t\t");
1826 		print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0);
1827 		fprintf(stdout, MSGSTR(2115, "\n  Product ID:\t\t"));
1828 		print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0);
1829 
1830 		fprintf(stdout, "\n  ");
1831 		fprintf(stdout, MSGSTR(2119, "Revision:"));
1832 		fprintf(stdout, "\t\t");
1833 		print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0);
1834 
1835 		fprintf(stdout, "\n  ");
1836 		fprintf(stdout, MSGSTR(17, "Serial Num:"));
1837 		fprintf(stdout, "\t\t");
1838 		(void) fflush(stdout);
1839 		/*
1840 		 * Call the inquiry cmd on page 0x80 only if the vendor
1841 		 * supports page 0x80.
1842 		 */
1843 		if ((find_supported_inq_page(handle, first_time->hba_pwwn,
1844 		    first_time->map.FcpId.PortWWN, lun, 0x80))) {
1845 			memset(&serial, 0, sizeof (serial));
1846 			status = HBA_ScsiInquiryV2(handle,
1847 			    first_time->hba_pwwn,
1848 			    first_time->map.FcpId.PortWWN,
1849 			    lun, 1, 0x80,
1850 			    &serial, &serialSize,
1851 			    &scsiStatus,
1852 			    &sense, &senseSize);
1853 			if (status == HBA_STATUS_OK) {
1854 				print_chars(serial.inq_serial,
1855 				    sizeof (serial.inq_serial), 0);
1856 			} else {
1857 				fprintf(stdout, MSGSTR(2506, "Unsupported"));
1858 			}
1859 		} else {
1860 			fprintf(stdout, MSGSTR(2506, "Unsupported"));
1861 		}
1862 		HBA_CloseAdapter(handle);
1863 		if ((inq.inq_dtype & DTYPE_MASK) == DTYPE_DIRECT) {
1864 		/* Read capacity wont work on standby paths, so try till OK */
1865 		    for (tmp_path = first_time; tmp_path != NULL;
1866 			tmp_path = tmp_path->next_path) {
1867 			if (mpxio && (strncmp(tmp_path->info.pathState,
1868 			    "ONLINE", strlen(tmp_path->info.pathState)))) {
1869 			    /* continue to next online path */
1870 			    continue;
1871 			}
1872 			status = HBA_OpenAdapterByWWN(&handle,
1873 						tmp_path->hba_pwwn);
1874 			if (status != HBA_STATUS_OK) {
1875 			    /* continue to next path */
1876 			    continue;
1877 			}
1878 
1879 			status = HBA_ScsiReadCapacityV2(handle,
1880 			    tmp_path->hba_pwwn,
1881 			    tmp_path->map.FcpId.PortWWN,
1882 			    tmp_path->map.FcpId.FcpLun,
1883 			    &cap_data, &cap_data_size,
1884 			    &scsiStatus,
1885 			    &sense, &senseSize);
1886 			if (status == HBA_STATUS_OK) {
1887 			    break;
1888 			} else if (status == HBA_STATUS_SCSI_CHECK_CONDITION &&
1889 			    sense.es_key == KEY_UNIT_ATTENTION) {
1890 			/*
1891 			 * retry for check-condition state when unit attention
1892 			 * condition has been established
1893 			 */
1894 			    status =  HBA_ScsiReadCapacityV2(handle,
1895 				tmp_path->hba_pwwn,
1896 				tmp_path->map.FcpId.PortWWN,
1897 				tmp_path->map.FcpId.FcpLun,
1898 				&cap_data, &cap_data_size,
1899 				&scsiStatus,
1900 				&sense, &senseSize);
1901 			    if (status == HBA_STATUS_OK) {
1902 				break;
1903 			    }
1904 			}
1905 			HBA_CloseAdapter(handle);
1906 		    }
1907 		}
1908 		if (handle != HBA_HANDLE_INVALID) {
1909 			HBA_CloseAdapter(handle);
1910 		}
1911 		if (status != HBA_STATUS_OK) {
1912 		    /* Make sure we don't display garbage */
1913 		    cap_data.block_size = 0;
1914 		    cap_data.last_block_addr = 0;
1915 		}
1916 
1917 		if (cap_data.block_size > 0 &&
1918 			cap_data.last_block_addr > 0) {
1919 		    lunMbytes = ntohl(cap_data.last_block_addr) + 1;
1920 		    lunMbytes *= ntohl(cap_data.block_size);
1921 		    lunMbytes /= (float)(1024*1024);
1922 		    fprintf(stdout, "\n  ");
1923 		    fprintf(stdout, MSGSTR(60,
1924 			    "Unformatted capacity:\t%6.3f MBytes"), lunMbytes);
1925 		}
1926 		fprintf(stdout, "\n");
1927 
1928 		/*
1929 		 * get mode page information for FC device.
1930 		 * do not do mode sense if this is a tape device.
1931 		 * mode sense will rewind the tape
1932 		 */
1933 		if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_SEQUENTIAL) {
1934 		    if (get_mode_page(first_time->map.ScsiId.OSDeviceName,
1935 			&pg_buf) == 0) {
1936 			mode_header_ptr = (struct mode_header_g1 *)
1937 				(void *)pg_buf;
1938 			offset = sizeof (struct mode_header_g1) +
1939 			    ntohs(mode_header_ptr->bdesc_length);
1940 			pg_hdr = (struct mode_page *)&pg_buf[offset];
1941 
1942 			while (offset < (ntohs(mode_header_ptr->length) +
1943 			    sizeof (mode_header_ptr->length))) {
1944 			    if (pg_hdr->code == MODEPAGE_CACHING) {
1945 				struct	mode_caching	*pg8_buf;
1946 				pg8_buf = (struct mode_caching *)
1947 				    (void *)pg_hdr;
1948 				if (pg8_buf->wce) {
1949 				    fprintf(stdout, MSGSTR(2122,
1950 					"  Write Cache:\t\t"
1951 					"Enabled\n"));
1952 				}
1953 				if (pg8_buf->rcd == 0) {
1954 				    fprintf(stdout, MSGSTR(2123,
1955 					"  Read Cache:\t\t"
1956 					"Enabled\n"));
1957 				    fprintf(stdout, MSGSTR(2509,
1958 					"    Minimum prefetch:\t0x%x\n"
1959 					"    Maximum prefetch:\t0x%x\n"),
1960 					pg8_buf->min_prefetch,
1961 					pg8_buf->max_prefetch);
1962 				}
1963 				break;
1964 			    }
1965 			    offset += pg_hdr->length +
1966 				sizeof (struct mode_page);
1967 			    pg_hdr = (struct mode_page *)&pg_buf[offset];
1968 			}
1969 		    }
1970 		}
1971 
1972 		fprintf(stdout, "  %s\t\t", MSGSTR(35, "Device Type:"));
1973 		if ((inq.inq_dtype & DTYPE_MASK) < 0x10) {
1974 			fprintf(stdout, "%s\n",
1975 			    dtype[inq.inq_dtype & DTYPE_MASK]);
1976 		} else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) {
1977 			fprintf(stdout, MSGSTR(2432, "Reserved"));
1978 		} else {
1979 			/* dtype of 0x1f is returned */
1980 			fprintf(stdout, MSGSTR(2433, "Unknown"));
1981 		}
1982 
1983 		fprintf(stdout, MSGSTR(2128, "  Path(s):\n"));
1984 		fprintf(stdout, "\n");
1985 		fprintf(stdout, "  %s\n",
1986 		    first_time->map.ScsiId.OSDeviceName);
1987 		if (phys_path != NULL) {
1988 		    fprintf(stdout, "  %s\n", phys_path);
1989 		}
1990 
1991 		/* Now display all paths to this LUN */
1992 		for (path = first_time; path != NULL;
1993 		    path = path->next_path) {
1994 		    /* Display the controller information */
1995 		    fprintf(stdout, MSGSTR(2303, "   Controller      \t%s\n"),
1996 			    path->hba_path);
1997 
1998 		    fprintf(stdout, MSGSTR(2507,
1999 			    "    Device Address\t\t%016llx,%x\n"),
2000 			    wwnConversion(
2001 			    path->map.FcpId.PortWWN.wwn),
2002 			    path->map.ScsiId.ScsiOSLun);
2003 
2004 		    fprintf(stdout, MSGSTR(2508,
2005 			    "    Host controller port WWN\t%016llx\n"),
2006 			    wwnConversion(path->hba_pwwn.wwn));
2007 
2008 		    if (mpxio) {
2009 			fprintf(stdout, MSGSTR(2305,
2010 				"    Class\t\t\t%s\n"), path->info.pathClass);
2011 			fprintf(stdout, MSGSTR(2306,
2012 				"    State\t\t\t%s\n"), path->info.pathState);
2013 		    }
2014 		    if (phys_path != NULL) {
2015 			free(phys_path);
2016 			phys_path = NULL;
2017 		    }
2018 		}
2019 		printf("\n");
2020 	    }
2021 	}
2022 	return (ret);
2023 }
2024 
2025 /*
2026  * handle expert-mode hotplug commands
2027  *
2028  * return 0 iff all is okay
2029  */
2030 int
2031 fchba_hotplug_e(int todo, char **argv, int verbose_flag, int force_flag)
2032 {
2033 char		*path_phys = NULL;
2034 int		exit_code;
2035 devctl_hdl_t	dcp;
2036 
2037 	if (todo != DEV_ONLINE &&
2038 	    todo != DEV_OFFLINE) {
2039 	    fprintf(stderr, "%s\n", strerror(ENOTSUP));
2040 	    return (-1);
2041 	}
2042 
2043 	/* Convert the paths to phsyical paths */
2044 	path_phys = get_slash_devices_from_osDevName(argv[0],
2045 		NOT_IGNORE_DANGLING_LINK);
2046 	if (!path_phys) {
2047 	    fprintf(stderr, MSGSTR(112,
2048 		"Error: Invalid pathname (%s)"),
2049 		argv[0]);
2050 	    fprintf(stderr, "\n");
2051 	    return (-1);
2052 	}
2053 	if (verbose_flag) {
2054 		(void) fprintf(stdout,
2055 				MSGSTR(5516,
2056 				"phys path = \"%s\"\n"),
2057 				path_phys);
2058 	}
2059 	/* acquire rights to hack on device */
2060 	if ((dcp = devctl_device_acquire(path_phys,
2061 		force_flag ? 0 : DC_EXCL)) == NULL) {
2062 
2063 		(void) fprintf(stderr, MSGSTR(5517,
2064 		    "Error: can't acquire \"%s\": %s\n"),
2065 		    path_phys, strerror(errno));
2066 		return (1);
2067 	}
2068 
2069 	switch (todo) {
2070 	case DEV_ONLINE:
2071 		exit_code = devctl_device_online(dcp);
2072 		break;
2073 	case DEV_OFFLINE:
2074 		exit_code = devctl_device_offline(dcp);
2075 		break;
2076 	}
2077 
2078 	if (exit_code != 0) {
2079 		perror(MSGSTR(5518, "devctl"));
2080 	}
2081 
2082 	/* all done now -- release device */
2083 	devctl_release(dcp);
2084 
2085 	if (path_phys) {
2086 	    free(path_phys);
2087 	}
2088 
2089 	return (exit_code);
2090 }
2091 
2092 /*
2093  * Returns non zero if we should use FC-HBA.
2094  * For x86, luxadm uses FC-HBA.
2095  */
2096 int
2097 use_fchba()
2098 {
2099 
2100 #ifdef __x86
2101 	return (1);
2102 #else
2103 	return (0);
2104 #endif
2105 
2106 }
2107 
2108 /*
2109  * Returns non-zero if we should skip the HBA at index "i"
2110  */
2111 int
2112 skip_hba(int i) {
2113 	HBA_LIBRARYATTRIBUTES lib_attrs;
2114 	(void) HBA_GetVendorLibraryAttributes(i, &lib_attrs);
2115 	if (strncmp(lib_attrs.VName, VSL_NAME,
2116 		sizeof (lib_attrs.VName)) == 0) {
2117 	    return (0);
2118 	}
2119 	return (1);
2120 }
2121 
2122 /*
2123  * Function to determine if the given page is supported by vendor.
2124  */
2125 int
2126 find_supported_inq_page(HBA_HANDLE handle, HBA_WWN hwwn, HBA_WWN pwwn,
2127     uint64_t lun, int page_num)
2128 {
2129 	struct	scsi_extended_sense	sense;
2130 	L_inquiry00			inq00;
2131 	uchar_t				*data;
2132 	HBA_STATUS			status = HBA_STATUS_ERROR;
2133 	int				index;
2134 	HBA_UINT8			scsiStatus;
2135 	uint32_t			inqSize = sizeof (inq00);
2136 	uint32_t			senseSize = sizeof (sense);
2137 
2138 	status = HBA_ScsiInquiryV2(handle, hwwn, pwwn, lun, 1, 0x00,
2139 	    &inq00, &inqSize, &scsiStatus, &sense, &senseSize);
2140 
2141 	if (status == HBA_STATUS_OK) {
2142 		data = (uchar_t *)&inq00;
2143 		for (index = 4; (index <= inq00.len+3)&&
2144 		    (data[index] <= page_num); index ++) {
2145 			if (data[index] == page_num) {
2146 				return (1);
2147 			}
2148 		}
2149 	}
2150 	return (0);
2151 }
2152