OpenSolaris_b135/cmd/luxadm/fchba.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */



#include	<hbaapi.h>
#include	<stdio.h>
#include	<unistd.h>
#include	<stdlib.h>
#include	<sys/param.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<string.h>
#include	<strings.h>
#include	<ctype.h>
#include	<sys/scsi/generic/sense.h>
#include	<sys/scsi/generic/mode.h>
#include	<sys/scsi/generic/inquiry.h>
#include	<errno.h>
#include	<libdevice.h>
#include	<config_admin.h>
#include	<sys/byteorder.h>
#include	<sys/fibre-channel/fcio.h>
#include	"common.h"
#include	"sun_fc_version.h"

#define	DEFAULT_LUN_COUNT	1024
#define	LUN_SIZE		8
#define	LUN_HEADER_SIZE		8
#define	DEFAULT_LUN_LENGTH	DEFAULT_LUN_COUNT   *	\
				LUN_SIZE	    +	\
				LUN_HEADER_SIZE
struct lun_val {
	uchar_t val[8];
};
struct rep_luns_rsp {
	uint32_t    length;
	uint32_t    rsrvd;
	struct lun_val  lun[1];
};

/* Extracted from the old scsi.h file */
struct  capacity_data_struct {
	uint_t  last_block_addr;
	uint_t  block_size;
};


/* Structure to handle the inq. page 0x80 serial number */
struct page80 {
	uchar_t inq_dtype;
	uchar_t inq_page_code;
	uchar_t reserved;
	uchar_t inq_page_len;
	uchar_t inq_serial[251];
};

extern char		*dtype[];
extern int		Options;
extern const int	OPTION_P;

int skip_hba(int i);
int find_supported_inq_page(HBA_HANDLE handle, HBA_WWN hwwn, HBA_WWN pwwn,
    uint64_t lun, int page_num);
/*
 * The routines within this file operate against the T11
 * HBA API interface.  In some cases, proprietary Sun driver
 * interface are also called to add additional information
 * above what the standard library supports.
 */

uint64_t
wwnConversion(uchar_t *wwn) {
	uint64_t tmp;
	(void) memcpy(&tmp, wwn, sizeof (uint64_t));
	return (ntohll(tmp));
}

void printStatus(HBA_STATUS status) {
	switch (status) {
	case HBA_STATUS_OK:
	    printf(MSGSTR(2410, "OK"));
	    return;
	case HBA_STATUS_ERROR:
	    printf(MSGSTR(2411, "ERROR"));
	    return;
	case HBA_STATUS_ERROR_NOT_SUPPORTED:
	    printf(MSGSTR(2412, "NOT SUPPORTED"));
	    return;
	case HBA_STATUS_ERROR_INVALID_HANDLE:
	    printf(MSGSTR(2413, "INVALID HANDLE"));
	    return;
	case HBA_STATUS_ERROR_ARG:
	    printf(MSGSTR(2414, "ERROR ARG"));
	    return;
	case HBA_STATUS_ERROR_ILLEGAL_WWN:
	    printf(MSGSTR(2415, "ILLEGAL WWN"));
	    return;
	case HBA_STATUS_ERROR_ILLEGAL_INDEX:
	    printf(MSGSTR(2416, "ILLEGAL INDEX"));
	    return;
	case HBA_STATUS_ERROR_MORE_DATA:
	    printf(MSGSTR(2417, "MORE DATA"));
	    return;
	case HBA_STATUS_ERROR_STALE_DATA:
	    printf(MSGSTR(2418, "STALE DATA"));
	    return;
	case HBA_STATUS_SCSI_CHECK_CONDITION:
	    printf(MSGSTR(2419, "SCSI CHECK CONDITION"));
	    return;
	case HBA_STATUS_ERROR_BUSY:
	    printf(MSGSTR(2420, "BUSY"));
	    return;
	case HBA_STATUS_ERROR_TRY_AGAIN:
	    printf(MSGSTR(2421, "TRY AGAIN"));
	    return;
	case HBA_STATUS_ERROR_UNAVAILABLE:
	    printf(MSGSTR(2422, "UNAVAILABLE"));
	    return;
	default:
	    printf(MSGSTR(2423, "UNKNOWN ERROR TYPE %d"), status);
	    return;
	    }
}

uint32_t
getNumberOfAdapters() {
	uint32_t count = HBA_GetNumberOfAdapters();
	if (count == 0) {
		fprintf(stderr, MSGSTR(2405,
			"\nERROR: No Fibre Channel Adapters found.\n"));
	}
	return (count);
}

#define	MAX_RETRIES	10

/*
 * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
 * Will handle retries if applicable.
 */
int
getAdapterAttrs(HBA_HANDLE handle, char *name, HBA_ADAPTERATTRIBUTES *attrs) {
	int count = 0;
	HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */

	/* Loop as long as we have a retryable error */
	while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
		status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) {
		status = HBA_GetAdapterAttributes(handle, attrs);
		if (status == HBA_STATUS_OK) {
			break;
		}
		(void) sleep(1);
	}
	if (status != HBA_STATUS_OK) {
		/* We encountered a non-retryable error */
		fprintf(stderr, MSGSTR(2501,
		"\nERROR: Unable to retrieve adapter port details (%s)"),
		name);
		printStatus(status);
		fprintf(stderr, "\n");
	}
	return (status);
}

/*
 * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
 * Will handle retries if applicable.
 */
int
getAdapterPortAttrs(HBA_HANDLE handle, char *name, int portIndex,
	    HBA_PORTATTRIBUTES *attrs) {
	int count = 0;
	HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */

	/* Loop as long as we have a retryable error */
	while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
		status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) {
		status = HBA_GetAdapterPortAttributes(handle, portIndex, attrs);
		if (status == HBA_STATUS_OK) {
			break;
		}

		/* The odds of this occuring are very slim, but possible. */
		if (status == HBA_STATUS_ERROR_STALE_DATA) {
			/*
			 * If we hit a stale data scenario,
			 * we'll just tell the user to try again.
			 */
			status = HBA_STATUS_ERROR_TRY_AGAIN;
			break;
		}
		sleep(1);
	}
	if (status != HBA_STATUS_OK) {
		/* We encountered a non-retryable error */
		fprintf(stderr, MSGSTR(2501,
		"\nERROR: Unable to retrieve adapter port details (%s)"),
		name);
		printStatus(status);
		fprintf(stderr, "\n");
	}
	return (status);
}

/*
 * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
 * Will handle retries if applicable.
 */
int
getDiscPortAttrs(HBA_HANDLE handle, char *name, int portIndex, int discIndex,
	    HBA_PORTATTRIBUTES *attrs) {
	int count = 0;
	HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */

	/* Loop as long as we have a retryable error */
	while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
		status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) {
		status = HBA_GetDiscoveredPortAttributes(handle, portIndex,
				discIndex, attrs);
		if (status == HBA_STATUS_OK) {
			break;
		}

		/* The odds of this occuring are very slim, but possible. */
		if (status == HBA_STATUS_ERROR_STALE_DATA) {
			/*
			 * If we hit a stale data scenario, we'll just tell the
			 * user to try again.
			 */
			status = HBA_STATUS_ERROR_TRY_AGAIN;
			break;
		}
		sleep(1);
	}
	if (status != HBA_STATUS_OK) {
		/* We encountered a non-retryable error */
		fprintf(stderr, MSGSTR(2504,
		"\nERROR: Unable to retrieve target port details (%s)"),
		name);
		printStatus(status);
		fprintf(stderr, "\n");
	}
	return (status);
}


/*ARGSUSED*/
int
fchba_display_port(int verbose)
{
	int retval = 0;
	HBA_HANDLE handle;
	HBA_ADAPTERATTRIBUTES hbaAttrs;
	HBA_PORTATTRIBUTES portAttrs;
	HBA_STATUS status;
	int count, adapterIndex, portIndex;
	char name[256];
	char *physical = NULL;
	char path[MAXPATHLEN];

	if ((retval = loadLibrary())) {
	    return (retval);
	}

	count = getNumberOfAdapters();

	for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
	    if (skip_hba(adapterIndex)) {
		continue;
	    }
	    status = HBA_GetAdapterName(adapterIndex, (char *)&name);
	    if (status != HBA_STATUS_OK) {
		/* Just skip it, maybe it was DR'd */
		continue;
	    }
	    handle = HBA_OpenAdapter(name);
	    if (handle == 0) {
		/* Just skip it, maybe it was DR'd */
		continue;
	    }

	    if (getAdapterAttrs(handle, name, &hbaAttrs)) {
		/* This should never happen, we'll just skip the adapter */
		HBA_CloseAdapter(handle);
		continue;
	    }

	    for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
		    portIndex++) {
		if (getAdapterPortAttrs(handle, name, portIndex,
			&portAttrs)) {
		    continue;
		}
		physical = get_slash_devices_from_osDevName(
				portAttrs.OSDeviceName,
				STANDARD_DEVNAME_HANDLING);
		if (physical) {
			char *tmp = strstr(physical, ":fc");
			if (tmp) {
				*tmp = '\0';
				(void) snprintf(path, MAXPATHLEN, "%s:devctl",
					physical);
			} else {
				(void) snprintf(path, MAXPATHLEN, "%s",
					physical);
			}
			free(physical);
			physical = NULL;
			(void) printf("%-65s  ", path);
		} else {
			(void) printf("%-65s  ", portAttrs.OSDeviceName);
		}
		if (portAttrs.NumberofDiscoveredPorts > 0) {
		    printf(MSGSTR(2233, "CONNECTED\n"));
		} else {
		    printf(MSGSTR(2234, "NOT CONNECTED\n"));
		}
	    }
	}
	(void) HBA_FreeLibrary();
	return (retval);
}

/*
 * Internal routines/structure to deal with a path list
 * so we can ensure uniqueness
 */
struct path_entry {
	char path[MAXPATHLEN];
	HBA_UINT8 wwn[8];
	uchar_t dtype;
	struct path_entry *next;
};
void add_path(struct path_entry *head, struct path_entry *cur) {
	struct path_entry *tmp;
	for (tmp = head; tmp->next != NULL; tmp = tmp->next) { }
		tmp->next = cur;
}
struct path_entry *is_duplicate_path(struct path_entry *head, char *path) {
	struct path_entry *tmp;
	for (tmp = head; tmp != NULL; tmp = tmp->next) {
		if (strncmp(tmp->path, path, sizeof (tmp->path)) == 0) {
			return (tmp);
		}
	}
	return (NULL);
}
void free_path_list(struct path_entry *head) {
	struct path_entry *tmp;
	struct path_entry *tmp2;
	for (tmp = head; tmp != NULL; ) {
		tmp2 = tmp->next;
		free(tmp);
		tmp = tmp2;
	}
}


int
is_wwn(char *arg) {
	int i;
	if (strlen(arg) == 16) {
		for (i = 0; i < 16; i++) {
			if (!isxdigit(arg[i])) {
				return (0);
			}
		}
		return (1);
	}
	return (0);
}

int
is_path(char *arg) {
	struct stat buf;
	if (stat(arg, &buf)) {
		return (0);
	}
	return (1);
}

/* We take a wild guess for our first get target mappings call */
#define	MAP_GUESS	50

HBA_STATUS
fetch_mappings(HBA_HANDLE handle, HBA_WWN pwwn, HBA_FCPTARGETMAPPINGV2 **map) {
	int loop = 0;
	int count = 0;
	HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
	*map = (HBA_FCPTARGETMAPPINGV2 *) calloc(1,
		(sizeof (HBA_FCPSCSIENTRYV2)* (MAP_GUESS-1)) +
		sizeof (HBA_FCPTARGETMAPPINGV2));

	/* Loop as long as we have a retryable error */
	while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
		status == HBA_STATUS_ERROR_BUSY ||
		status == HBA_STATUS_ERROR_MORE_DATA) && loop++ < MAX_RETRIES) {
	    status = HBA_GetFcpTargetMappingV2(handle, pwwn, *map);
	    if (status == HBA_STATUS_OK) {
		break;
	    } else if (status == HBA_STATUS_ERROR_MORE_DATA) {
		count = (*map)->NumberOfEntries;
		free(*map);
		*map = (HBA_FCPTARGETMAPPINGV2 *) calloc(1,
		    (sizeof (HBA_FCPSCSIENTRYV2)* (count-1)) +
		    sizeof (HBA_FCPTARGETMAPPINGV2));
		(*map)->NumberOfEntries = count;
		continue;
	    }
	    sleep(1);
	}
	if (status != HBA_STATUS_OK) {
	    /* We encountered a non-retryable error */
	    fprintf(stderr, MSGSTR(2502,
		    "\nERROR: Unable to retrieve SCSI device paths "
		    "(HBA Port WWN %016llx)"),
		    wwnConversion(pwwn.wwn));
	    printStatus(status);
	    fprintf(stderr, "\n");
	}
	return (status);
}

/*
 * Returns the index of the first match, or -1 if no match
 */
int
match_mappings(char *compare, HBA_FCPTARGETMAPPINGV2 *map) {
	int		mapIndex;
	char	*physical = NULL;
	char	*tmp;
	int		wwnCompare = 0;
	uint64_t	wwn;

	if (map == NULL || compare == NULL) {
	    return (-1);
	}

	if (is_wwn(compare)) {
	    wwnCompare = 1;
	    (void) sscanf(compare, "%016llx", &wwn);
	} else {
	    /* Convert the paths to phsyical paths */
	    physical = get_slash_devices_from_osDevName(compare,
			STANDARD_DEVNAME_HANDLING);
	}

	for (mapIndex = 0; mapIndex < map->NumberOfEntries; mapIndex ++) {
	    if (wwnCompare) {
		if (wwn == wwnConversion(
			map->entry[mapIndex].FcpId.NodeWWN.wwn) ||
			wwn == wwnConversion(
			map->entry[mapIndex].FcpId.PortWWN.wwn)) {
		    return (mapIndex);
		}
	    } else {
		if (physical != NULL) {
		    tmp = get_slash_devices_from_osDevName(
			map->entry[mapIndex].ScsiId.OSDeviceName,
			STANDARD_DEVNAME_HANDLING);
		    if ((tmp != NULL) &&
			strncmp(physical, tmp, MAXPATHLEN) == 0) {
			free(physical);
			return (mapIndex);
		    }
		}
	    }
	}
	if (physical) {
	    free(physical);
	}
	return (-1);
}


/*
 * returns non-zero on failure (aka HBA_STATUS_ERROR_*
 */
int
loadLibrary() {
	int status = HBA_LoadLibrary();
	if (status != HBA_STATUS_OK) {
		fprintf(stderr, MSGSTR(2505,
			"ERROR: Unable to load HBA API library: "));
		printStatus(status);
		fprintf(stderr, "\n");
	}
	return (status);
}

int
fchba_non_encl_probe() {
	HBA_HANDLE handle;
	HBA_ADAPTERATTRIBUTES hbaAttrs;
	HBA_PORTATTRIBUTES portAttrs;
	HBA_FCPTARGETMAPPINGV2    *map;
	HBA_STATUS status;
	int count, adapterIndex, portIndex, mapIndex;
	char name[256];
	struct path_entry *head = NULL;
	uint64_t	lun = 0;
	L_inquiry	inq;
	struct scsi_extended_sense sense;
	HBA_UINT8	scsiStatus;
	uint32_t	inquirySize = sizeof (inq), senseSize = sizeof (sense);

	if (loadLibrary()) {
	    return (-1);
	}

	count = getNumberOfAdapters();

	/* Loop over all HBAs */
	for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
	    if (skip_hba(adapterIndex)) {
		continue;
	    }
	    status = HBA_GetAdapterName(adapterIndex, (char *)&name);
	    if (status != HBA_STATUS_OK) {
		/* May have been DR'd */
		continue;
	    }
	    handle = HBA_OpenAdapter(name);
	    if (handle == 0) {
		/* May have been DR'd */
		continue;
	    }

	    if (getAdapterAttrs(handle, name, &hbaAttrs)) {
		/* Should not happen, just skip it */
		HBA_CloseAdapter(handle);
		continue;
	    }


	    /* Loop over all HBA Ports */
	    for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
		    portIndex++) {
		if (getAdapterPortAttrs(handle, name, portIndex,
			&portAttrs)) {
		    continue;
		}

		if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
		    continue;
		}

		/* Loop over all target Mapping entries */
		for (mapIndex = 0; mapIndex < map->NumberOfEntries;
		    mapIndex ++) {
			struct path_entry *tmpPath = NULL;
			int doInquiry = 0;
			if (!head) {
			head = (struct path_entry *)calloc(1,
				sizeof (struct path_entry));
			tmpPath = head;
			strncpy(head->path,
			    map->entry[mapIndex].ScsiId.OSDeviceName,
			    sizeof (map->entry[mapIndex].ScsiId.OSDeviceName));
			(void) memcpy(tmpPath->wwn,
			    map->entry[mapIndex].FcpId.NodeWWN.wwn,
			    sizeof (HBA_UINT8) * 8);
			doInquiry = 1;
			} else if (tmpPath = is_duplicate_path(head,
				map->entry[mapIndex].ScsiId.OSDeviceName)) {
				if (tmpPath->dtype != 0x1f) {
					doInquiry = 0;
				} else {
					doInquiry = 1;
				}
			} else {
			tmpPath = (struct path_entry *)
				calloc(1, sizeof (struct path_entry));
			strncpy(tmpPath->path,
			    map->entry[mapIndex].ScsiId.OSDeviceName,
			    sizeof (map->entry[mapIndex].ScsiId.OSDeviceName));
			(void) memcpy(tmpPath->wwn,
			    map->entry[mapIndex].FcpId.NodeWWN.wwn,
			    sizeof (HBA_UINT8) * 8);
			add_path(head, tmpPath);
			doInquiry = 1;
			}

			if (doInquiry) {
				lun = map->entry[mapIndex].FcpId.FcpLun;
				memset(&inq, 0, sizeof (inq));
				memset(&sense, 0, sizeof (sense));
				status = HBA_ScsiInquiryV2(handle,
				    portAttrs.PortWWN,
				    map->entry[mapIndex].FcpId.PortWWN,
				    lun, 0, 0,
				    &inq, &inquirySize,
				    &scsiStatus,
				    &sense, &senseSize);
				if (status != HBA_STATUS_OK) {
					inq.inq_dtype = 0x1f;
				}
				tmpPath->dtype = inq.inq_dtype;
			}
		}
	}
	}
	if (head) {
		struct path_entry *tmp;
		printf(MSGSTR(2098, "\nFound Fibre Channel device(s):\n"));
		for (tmp = head; tmp != NULL; tmp = tmp->next) {
			printf("  ");
			printf(MSGSTR(90, "Node WWN:"));
			printf("%016llx  ", wwnConversion(tmp->wwn));
			fprintf(stdout, MSGSTR(35, "Device Type:"));
			(void) fflush(stdout);

			if ((tmp->dtype & DTYPE_MASK) < 0x10) {
				fprintf(stdout, "%s",
				    dtype[tmp->dtype & DTYPE_MASK]);
			} else if ((tmp->dtype & DTYPE_MASK) < 0x1f) {
				fprintf(stdout, MSGSTR(2406,
				    "Reserved"));
			} else {
				fprintf(stdout, MSGSTR(2407,
				    "Unknown"));
			}

			printf("\n    ");
			printf(MSGSTR(31, "Logical Path:%s"), tmp->path);
			printf("\n");

		/* We probably shouldn't be using a g_fc interface here */
			if (Options & OPTION_P) {
				char *phys_path =
				get_slash_devices_from_osDevName(
				    tmp->path,
				    STANDARD_DEVNAME_HANDLING);
				if (phys_path != NULL) {
				fprintf(stdout, "    ");
				fprintf(stdout, MSGSTR(5, "Physical Path:"));
				fprintf(stdout, "\n     %s\n", phys_path);
				free(phys_path);
				}
			}
		}
		free_path_list(head);
	}
	HBA_FreeLibrary();
	return (0);
}


int
fchba_inquiry(char **argv)
{
	int		path_index = 0, found = 0;
	uint64_t	wwn;
	uint64_t	lun = 0;
	HBA_HANDLE handle;
	HBA_ADAPTERATTRIBUTES hbaAttrs;
	HBA_PORTATTRIBUTES portAttrs;
	HBA_FCPTARGETMAPPINGV2    *map;
	HBA_STATUS status;
	int count, adapterIndex, portIndex, mapIndex;
	char name[256];
	L_inquiry	inq;
	struct page80	serial;
	uint32_t	serialSize = sizeof (serial);
	struct scsi_extended_sense sense;
	HBA_UINT8	scsiStatus;
	uint32_t	inquirySize = sizeof (inq), senseSize = sizeof (sense);
	boolean_t	goodPath = B_FALSE;
	int		matched = 0, wwnCompare = 0;
	char		*tmp, *physical = NULL;
	int		ret = 0;

	if (loadLibrary()) {
	    return (-1);
	}
	for (path_index = 0; argv[path_index] != NULL; path_index++) {
	    goodPath = B_FALSE;
	    found = 0;

	    if (is_wwn(argv[path_index])) {
		(void) sscanf(argv[path_index], "%016llx", &wwn);
		wwnCompare = 1;
	    } else if (!is_path(argv[path_index])) {
		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
			argv[path_index]);
		fprintf(stderr, "\n");
		ret = -1;
		continue;
	    }
	    if (!wwnCompare) {
		/* Convert the paths to phsyical paths */
		physical = get_slash_devices_from_osDevName(argv[path_index],
			STANDARD_DEVNAME_HANDLING);
		if (!physical) {
		    fprintf(stderr, MSGSTR(112,
			"Error: Invalid pathname (%s)"),
			argv[path_index]);
		    fprintf(stderr, "\n");
		    ret = -1;
		    continue;
		}
	    }

	    count = getNumberOfAdapters();

	    /* Loop over all HBAs */
	    for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
		if (skip_hba(adapterIndex)) {
		    continue;
		}
		status = HBA_GetAdapterName(adapterIndex, (char *)&name);
		if (status != HBA_STATUS_OK) {
		    /* May have been DR'd */
		    continue;
		}
		handle = HBA_OpenAdapter(name);
		if (handle == 0) {
		    /* May have been DR'd */
		    continue;
		}

		if (getAdapterAttrs(handle, name, &hbaAttrs)) {
		    /* Should never happen */
		    HBA_CloseAdapter(handle);
		    continue;
		}


		/* Loop over all HBA Ports */
		for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
			portIndex++) {
		    if (getAdapterPortAttrs(handle, name, portIndex,
			    &portAttrs)) {
			continue;
		    }

		    if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
			continue;
		    }

		    for (mapIndex = 0; mapIndex < map->NumberOfEntries;
			    mapIndex ++) {
			matched = 0;
			if (wwnCompare) {
			    if (wwn == wwnConversion(
				    map->entry[mapIndex].FcpId.NodeWWN.wwn) ||
				    wwn == wwnConversion(
				    map->entry[mapIndex].FcpId.PortWWN.wwn)) {
				lun = map->entry[mapIndex].FcpId.FcpLun;
				matched = 1;
			    }
			} else {
			    tmp = get_slash_devices_from_osDevName(
				    map->entry[mapIndex].ScsiId.OSDeviceName,
				    STANDARD_DEVNAME_HANDLING);
			    if ((tmp != NULL) && (strncmp(physical, tmp,
				    MAXPATHLEN) == 0)) {
				lun = map->entry[mapIndex].FcpId.FcpLun;
				matched = 1;
				free(tmp);
			    }
			}

			if (matched) {
			    memset(&inq, 0, sizeof (inq));
			    memset(&sense, 0, sizeof (sense));
			    status = HBA_ScsiInquiryV2(handle,
				portAttrs.PortWWN,
				map->entry[mapIndex].FcpId.PortWWN,
				lun, 0, 0,
				&inq, &inquirySize,
				&scsiStatus,
				&sense, &senseSize);
			    if (status == HBA_STATUS_OK) {
				goodPath = B_TRUE;
				/*
				 * Call the inquiry cmd on page 0x80 only if
				 * the vendor supports page 0x80
				 */
				memset(&serial, 0, sizeof (serial));
				if ((find_supported_inq_page(handle,
					    portAttrs.PortWWN,
					    map->entry[mapIndex].FcpId.PortWWN,
					    lun, 0x80))) {
					status = HBA_ScsiInquiryV2(handle,
					    portAttrs.PortWWN,
					    map->entry[mapIndex].FcpId.PortWWN,
					    lun, 1, 0x80,
					    &serial, &serialSize,
					    &scsiStatus,
					    &sense, &senseSize);
					if (status != HBA_STATUS_OK) {
						strncpy(
						    (char *)serial.inq_serial,
						    "Unavailable",
						    sizeof (serial.inq_serial));
					}
				} else {
					strncpy((char *)serial.inq_serial,
					    "Unsupported",
					    sizeof (serial.inq_serial));
				}
				/*
				 * we are adding serial number information
				 * from 0x80.  If length is less than 39,
				 * then we want to increase length to 52 to
				 * reflect the fact that we have serial number
				 * information
				 */
				if (inq.inq_len < 39) {
					inq.inq_len = 52;
				}
				print_inq_data(argv[path_index],
				    map->entry[mapIndex].ScsiId.OSDeviceName,
				    inq, serial.inq_serial,
				    sizeof (serial.inq_serial));
				if (! wwnCompare) {
					found = 1;
					break;
				}
			    } else {
				fprintf(stderr, MSGSTR(2430,
				"Error: I/O failure communicating with %s  "),
				map->entry[mapIndex].ScsiId.OSDeviceName);
				printStatus(status);
				fprintf(stderr, "\n");
			    }
			}
		    }
		    if (found == 1) {
			    break;
		    }
		}
		if (found == 1) {
			break;
		}
	    }

	    if (physical) {
		free(physical);
	    }

	    if (!goodPath) {
		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
			argv[path_index]);
		fprintf(stderr, "\n");
		ret = -1;
	    }
	}
	return (ret);
}



int
fchba_dump_map(char **argv)
{
	int		path_index = 0;
	uint64_t	wwn;
	uint64_t	lun = 0;
	HBA_HANDLE handle;
	HBA_ADAPTERATTRIBUTES hbaAttrs;
	HBA_PORTATTRIBUTES portAttrs;
	HBA_PORTATTRIBUTES discPortAttrs;
	HBA_FCPTARGETMAPPINGV2    *map;
	HBA_STATUS status;
	int count, adapterIndex, portIndex, mapIndex, discIndex;
	char name[256], *physical, *comp_phys;
	L_inquiry	inq;
	struct scsi_extended_sense sense;
	HBA_UINT8	scsiStatus;
	int		matched;
	int		done;
	uint32_t	inquirySize = sizeof (inq), senseSize = sizeof (sense);
	boolean_t	goodPath = B_FALSE;
	int		ret = 0;
	uint32_t	responseSize = DEFAULT_LUN_LENGTH;
	uchar_t		raw_luns[DEFAULT_LUN_LENGTH];
	struct rep_luns_rsp	*lun_resp;


	if (loadLibrary()) {
	    return (-1);
	}
	for (path_index = 0; argv[path_index] != NULL; path_index++) {
	    goodPath = B_FALSE;

	    if (is_wwn(argv[path_index])) {
		(void) sscanf(argv[path_index], "%016llx", &wwn);
	    } else if (!is_path(argv[path_index])) {
		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
			argv[path_index]);
		fprintf(stderr, "\n");
		ret = -1;
		continue;
	    }

	    count = getNumberOfAdapters();

	    done = 0;
	    /* Loop over all HBAs */
	    for (adapterIndex = 0; adapterIndex < count && !done;
		    adapterIndex ++) {
		if (skip_hba(adapterIndex)) {
		    continue;
		}
		status = HBA_GetAdapterName(adapterIndex, (char *)&name);
		if (status != HBA_STATUS_OK) {
		    /* May have been DR'd */
		    continue;
		}
		handle = HBA_OpenAdapter(name);
		if (handle == 0) {
		    /* May have been DR'd */
		    continue;
		}

		if (getAdapterAttrs(handle, name, &hbaAttrs)) {
		    /* Should never happen */
		    HBA_CloseAdapter(handle);
		    continue;
		}


		/* Loop over all HBA Ports */
		for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts && !done;
			portIndex++) {
		    if (getAdapterPortAttrs(handle, name, portIndex,
			    &portAttrs)) {
			continue;
		    }


		    matched = 0;
		    if (is_wwn(argv[path_index])) {
			if (wwn == wwnConversion(
				portAttrs.NodeWWN.wwn) ||
				wwn == wwnConversion(
				portAttrs.PortWWN.wwn)) {
			    matched = 1;
			}
		    } else {
			if (is_path(argv[path_index]) &&
			    ((physical = get_slash_devices_from_osDevName(
				argv[path_index],
				STANDARD_DEVNAME_HANDLING)) != NULL) &&
			    ((comp_phys = get_slash_devices_from_osDevName(
				portAttrs.OSDeviceName,
				STANDARD_DEVNAME_HANDLING)) != NULL)) {
			    char *tmp = strstr(physical, ":devctl");
			    if (tmp) {
				*tmp = '\0';
			    } else {
				tmp = strstr(physical, ":fc");
				if (tmp) {
					*tmp = '\0';
				}
			    }
			    if (strstr(comp_phys, physical)) {
				matched = 1;
			    }
			}
			if (physical) {
			    free(physical);
			    physical = NULL;
			}
			if (comp_phys) {
			    free(comp_phys);
			    comp_phys = NULL;
			}
		    }

		    if (!fetch_mappings(handle, portAttrs.PortWWN, &map)) {
			mapIndex = match_mappings(argv[path_index], map);
			if (mapIndex >= 0) {
			    matched = 1;
			}
		    } else {
			continue;
		    }

		    if (matched) {
			goodPath = B_TRUE;
			printf(MSGSTR(2095,
				"Pos  Port_ID Hard_Addr Port WWN"
				"         Node WWN         Type\n"));
			for (discIndex = 0;
				discIndex < portAttrs.NumberofDiscoveredPorts;
				discIndex++) {
			    if (getDiscPortAttrs(handle, name, portIndex,
				    discIndex, &discPortAttrs)) {
				/* Move on to the next target */
				continue;
			    }

			    printf("%-4d %-6x  %-6x   %016llx %016llx",
				    discIndex,
				    discPortAttrs.PortFcId, 0,
				    wwnConversion(discPortAttrs.PortWWN.wwn),
				    wwnConversion(discPortAttrs.NodeWWN.wwn));

				/*
				 * devices are not all required to respond to
				 * Scsi Inquiry calls sent to LUN 0.  We must
				 * fisrt issue a ReportLUN and then send the
				 * SCSI Inquiry call to the first LUN Returned
				 * from the ReportLUN call
				 */
			    memset(&sense, 0, sizeof (sense));
			    status = HBA_ScsiReportLUNsV2(handle,
				portAttrs.PortWWN,
				discPortAttrs.PortWWN,
				(void *)raw_luns, &responseSize, &scsiStatus,
				(void *)&sense, &senseSize);
			    if (status == HBA_STATUS_OK) {
				    lun_resp =
					(struct rep_luns_rsp *)
					(unsigned long)raw_luns;
				    lun = ntohll(
					wwnConversion(lun_resp->lun[0].val));
			    } else {
				/*
				 * in case we are unable to retrieve report
				 * LUN data, we will blindly try sending the
				 * INQUIRY to lun 0.
				 */
				lun = 0;
			    }
			    memset(&sense, 0, sizeof (sense));
			    status = HBA_ScsiInquiryV2(handle,
				    portAttrs.PortWWN,
				    discPortAttrs.PortWWN,
				    lun, 0, 0,
				    &inq, &inquirySize,
				    &scsiStatus,
				    &sense, &senseSize);
			    if (status != HBA_STATUS_OK) {
				inq.inq_dtype = 0x1f;
			    }
			    print_fabric_dtype_prop(portAttrs.PortWWN.wwn,
				map->entry[mapIndex].FcpId.PortWWN.wwn,
				inq.inq_dtype);
			}
			/* Now dump this HBA's stats */
			printf("%-4d %-6x  %-6x   %016llx %016llx",
			    discIndex,
			    portAttrs.PortFcId, 0,
			    wwnConversion(portAttrs.PortWWN.wwn),
			    wwnConversion(portAttrs.NodeWWN.wwn));
			print_fabric_dtype_prop(portAttrs.PortWWN.wwn,
			    portAttrs.PortWWN.wwn, 0x1f);
			done = 1;
		    }
		}
	    }
	    if (!goodPath) {
		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
			argv[path_index]);
		fprintf(stderr, "\n");
		ret = -1;
	    }
	}
	return (ret);
}

int
fchba_display_link_status(char **argv)
{
	int		path_index = 0;
	uint64_t	wwn;
	HBA_HANDLE handle;
	HBA_ADAPTERATTRIBUTES hbaAttrs;
	HBA_PORTATTRIBUTES portAttrs;
	HBA_PORTATTRIBUTES discPortAttrs;
	HBA_FCPTARGETMAPPINGV2    *map;
	HBA_STATUS status;
	int count, adapterIndex, portIndex, discIndex;
	char name[256], *physical, *comp_phys;
	int		matched;
	struct fc_rls_acc_params	rls;
	uint32_t	rls_size = sizeof (rls);
	boolean_t	goodPath = B_FALSE;
	int		ret = 0;

	if (loadLibrary()) {
	    return (-1);
	}
	for (path_index = 0; argv[path_index] != NULL; path_index++) {
	    goodPath = B_FALSE;

	    if (is_wwn(argv[path_index])) {
		(void) sscanf(argv[path_index], "%016llx", &wwn);
	    } else if (!is_path(argv[path_index])) {
		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
			argv[path_index]);
		fprintf(stderr, "\n");
		ret = -1;
		continue;
	    }

	    count = getNumberOfAdapters();

	    /* Loop over all HBAs */
	    for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
		if (skip_hba(adapterIndex)) {
		    continue;
		}
		status = HBA_GetAdapterName(adapterIndex, (char *)&name);
		if (status != HBA_STATUS_OK) {
		    /* May have been DR'd */
		    continue;
		}
		handle = HBA_OpenAdapter(name);
		if (handle == 0) {
		    /* May have been DR'd */
		    continue;
		}

		if (getAdapterAttrs(handle, name, &hbaAttrs)) {
		    /* Should never happen */
		    HBA_CloseAdapter(handle);
		    continue;
		}


		/* Loop over all HBA Ports */
		for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
			portIndex++) {
		    if (getAdapterPortAttrs(handle, name, portIndex,
			    &portAttrs)) {
			continue;
		    }

		    matched = 0;
		    if (is_wwn(argv[path_index])) {
			if (wwn == wwnConversion(
				portAttrs.NodeWWN.wwn) ||
				wwn == wwnConversion(
				portAttrs.PortWWN.wwn)) {
			    matched = 1;
			}
		    } else {
			if (is_path(argv[path_index]) &&
			    ((physical = get_slash_devices_from_osDevName(
				argv[path_index],
				STANDARD_DEVNAME_HANDLING)) != NULL) &&
			    ((comp_phys = get_slash_devices_from_osDevName(
				portAttrs.OSDeviceName,
				STANDARD_DEVNAME_HANDLING)) != NULL)) {
			    char *tmp = strstr(physical, ":devctl");
			    if (tmp) {
				*tmp = '\0';
			    } else {
				tmp = strstr(physical, ":fc");
				if (tmp) {
					*tmp = '\0';
				}
			    }
			    if (strstr(comp_phys, physical)) {
				matched = 1;
			    }
			}
			if (physical) {
			    free(physical);
			    physical = NULL;
			}
			if (comp_phys) {
			    free(comp_phys);
			    comp_phys = NULL;
			}
		    }

		    if (!matched) {
			if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
			    continue;
			}
		    }

		    if (matched || match_mappings(argv[path_index], map) >= 0) {
			goodPath = B_TRUE;
			fprintf(stdout,
				MSGSTR(2007, "\nLink Error Status "
				"information for loop:%s\n"), argv[path_index]);
			fprintf(stdout, MSGSTR(2008, "al_pa   lnk fail "
				"   sync loss   signal loss   sequence err"
				"   invalid word   CRC\n"));

			for (discIndex = 0;
				discIndex < portAttrs.NumberofDiscoveredPorts;
				discIndex++) {


			    if (getDiscPortAttrs(handle, name, portIndex,
				    discIndex, &discPortAttrs)) {
				continue;
			    }

			    status = HBA_SendRLS(handle, portAttrs.PortWWN,
					discPortAttrs.PortWWN,
					&rls, &rls_size);
			    if (status != HBA_STATUS_OK) {
				memset(&rls, 0xff, sizeof (rls));
			    }

			    if ((rls.rls_link_fail == 0xffffffff) &&
				(rls.rls_sync_loss == 0xffffffff) &&
				(rls.rls_sig_loss == 0xffffffff) &&
				(rls.rls_prim_seq_err == 0xffffffff) &&
				(rls.rls_invalid_word == 0xffffffff) &&
				(rls.rls_invalid_crc == 0xffffffff)) {
				    fprintf(stdout,
					"%x\t%-12d%-12d%-14d%-15d%-15d%-12d\n",
					    discPortAttrs.PortFcId,
					    rls.rls_link_fail,
					    rls.rls_sync_loss,
					    rls.rls_sig_loss,
					    rls.rls_prim_seq_err,
					    rls.rls_invalid_word,
					    rls.rls_invalid_crc);
			    } else {
				    fprintf(stdout,
					"%x\t%-12u%-12u%-14u%-15u%-15u%-12u\n",
					    discPortAttrs.PortFcId,
					    rls.rls_link_fail,
					    rls.rls_sync_loss,
					    rls.rls_sig_loss,
					    rls.rls_prim_seq_err,
					    rls.rls_invalid_word,
					    rls.rls_invalid_crc);
			    }


			}
			/* Now dump this HBA's stats */
			status = HBA_SendRLS(handle, portAttrs.PortWWN,
				portAttrs.PortWWN,
				&rls, &rls_size);
			if (status != HBA_STATUS_OK) {
			    memset(&rls, 0xff, sizeof (rls));
			}

			if ((rls.rls_link_fail == 0xffffffff) &&
				(rls.rls_sync_loss == 0xffffffff) &&
				(rls.rls_sig_loss == 0xffffffff) &&
				(rls.rls_prim_seq_err == 0xffffffff) &&
				(rls.rls_invalid_word == 0xffffffff) &&
				(rls.rls_invalid_crc == 0xffffffff)) {
			    fprintf(stdout,
				    "%x\t%-12d%-12d%-14d%-15d%-15d%-12d\n",
				    portAttrs.PortFcId,
				    rls.rls_link_fail,
				    rls.rls_sync_loss,
				    rls.rls_sig_loss,
				    rls.rls_prim_seq_err,
				    rls.rls_invalid_word,
				    rls.rls_invalid_crc);
			} else {
			    fprintf(stdout,
				    "%x\t%-12u%-12u%-14u%-15u%-15u%-12u\n",
				    portAttrs.PortFcId,
				    rls.rls_link_fail,
				    rls.rls_sync_loss,
				    rls.rls_sig_loss,
				    rls.rls_prim_seq_err,
				    rls.rls_invalid_word,
				    rls.rls_invalid_crc);
			}
		    }
		}
	    }
	    if (!goodPath) {
		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
			argv[path_index]);
		fprintf(stderr, "\n");
		ret = -1;
	    }
	}
	(void) fprintf(stdout,
		MSGSTR(2009, "NOTE: These LESB counts are not"
		" cleared by a reset, only power cycles.\n"
		"These counts must be compared"
		" to previously read counts.\n"));
	return (ret);
}

typedef struct _PathInformation {
	char	pathClass[MAXPATHLEN];
	char	pathState[MAXPATHLEN];
	int32_t	pathInfoState;
	int32_t	pathInfoExternalState;
} PathInformation;

struct lun_tracking {
	HBA_FCPSCSIENTRYV2  map;
	HBA_WWN	hba_pwwn;
	char	hba_path[MAXPATHLEN];
	PathInformation info;

	/* Points to another lun_tracking instance with the same map->LUID */
	struct lun_tracking	*next_path;

	/* Points to next lun_tracking with a different map->LUID */
	struct lun_tracking *next_lun;
};


static const char VHCI_COMPONENT[] = "scsi_vhci";
static void
scsi_vhci_details(struct lun_tracking *lun)
{
	HBA_FCPSCSIENTRYV2 entry = lun->map;
	int		retval = 0;
	int		pathcnt, i, count, found = 0;
	char		temppath[MAXPATHLEN];
	char		buf[MAXPATHLEN];
	char	*path_state[5];

	char	*phys_path = get_slash_devices_from_osDevName(
				entry.ScsiId.OSDeviceName,
				STANDARD_DEVNAME_HANDLING);
	char	*devPath = NULL;
	char	*trailingCruft = NULL;
	char	devaddr[MAXPATHLEN];
	sv_iocdata_t	ioc;
	int	prop_buf_size = SV_PROP_MAX_BUF_SIZE;
	char	*path_class_val = NULL;
	char	client_path[MAXPATHLEN];
	char	phci_path[MAXPATHLEN];

	/* Only proceed if we are an mpxio path */
	if (phys_path == NULL || strstr(phys_path, VHCI_COMPONENT) == NULL) {
	    return;
	}

	path_state[0] = MSGSTR(2400, "INIT");
	path_state[1] = MSGSTR(2401, "ONLINE");
	path_state[2] = MSGSTR(2402, "STANDBY");
	path_state[3] = MSGSTR(2403, "FAULT");
	path_state[4] = MSGSTR(2404, "OFFLINE");

	sprintf(devaddr, "%016llx,%x", wwnConversion(
		entry.FcpId.PortWWN.wwn),
		entry.ScsiId.ScsiOSLun);

	/* First get the controller path */
	sprintf(temppath, "/dev/cfg/c%d", entry.ScsiId.ScsiBusNumber);
	if ((count = readlink(temppath, buf, sizeof (buf)))) {
	    buf[count] = '\0';
	    /* Now skip over the leading "../.." */
	    devPath = strstr(buf, "/devices/");
	    if (devPath == NULL) {
		strcpy(lun->info.pathClass, "Unavailable");
		strcpy(lun->info.pathState, "Unavailable");
		free(phys_path);
		return;
	    }

	    /* Now chop off the trailing ":xxx" portion if present */
	    trailingCruft = strrchr(buf, ':');
	    if (trailingCruft) {
		trailingCruft[0] = '\0';
	    }
	} else {
	    strcpy(lun->info.pathClass, "Unavailable");
	    strcpy(lun->info.pathState, "Unavailable");
	    free(phys_path);
	    return;
	}

	ioc.client = client_path;
	ioc.phci = phci_path;

	retval = get_scsi_vhci_pathinfo(phys_path, &ioc, &pathcnt);
	if (retval != 0) {
	    print_errString(retval, NULL);
	    exit(-1);
	}

	for (i = 0; i < pathcnt; i++) {
	    nvlist_t *nvl;
	    if (strstr(devPath, ioc.ret_buf[i].device.ret_phci)) {
		/* This could break someday if MPxIO changes devaddr */
		if (strstr(ioc.ret_buf[i].ret_addr, devaddr)) {
		    retval = nvlist_unpack(ioc.ret_buf[i].ret_prop.buf,
			prop_buf_size, &nvl, 0);
		    if (retval != 0) {
			strcpy(lun->info.pathClass,
			    "UNKNOWN PROB");
		    } else {
			strcpy(lun->info.pathState,
			    path_state[ioc.ret_buf[i].ret_state]);
			lun->info.pathInfoState = ioc.ret_buf[i].ret_state;
			lun->info.pathInfoExternalState =
			    ioc.ret_buf[i].ret_ext_state;
			if (nvlist_lookup_string(nvl, "path-class",
				&path_class_val) == 0) {
			    strcpy(lun->info.pathClass, path_class_val);
			} else {
			    strcpy(lun->info.pathClass, "UNKNOWN");
			}
		    }
		    nvlist_free(nvl);
		    found++;
		    break;
		}
	    }

	}

	if (!found) {
	    strcpy(lun->info.pathClass, "Unavailable");
	    strcpy(lun->info.pathState, "Unavailable");
	}
	free(phys_path);

	/* free everything we alloced */
	for (i = 0; i < ioc.buf_elem; i++) {
		free(ioc.ret_buf[i].ret_prop.buf);
		free(ioc.ret_buf[i].ret_prop.ret_buf_size);
	}
	free(ioc.ret_buf);

}

/* Utility routine to add new entries to the list (ignores dups) */
static void
add_lun_path(struct lun_tracking *head, HBA_FCPSCSIENTRYV2  *map,
	    HBA_WWN pwwn, char *path)
{
	struct lun_tracking *tmp = NULL, *cmp = NULL;

	for (tmp = head; tmp != NULL; tmp = tmp->next_lun) {
	    if (memcmp(&tmp->map.LUID, &map->LUID,
		    sizeof (HBA_LUID)) == 0) {

		/* Ensure this isn't a duplicate */
		for (cmp = tmp; cmp->next_path != NULL;
			    cmp = cmp->next_path) {
		    if (memcmp(&cmp->map, map, sizeof (cmp->map)) == 0) {
			return;
		    }
		}
		if (memcmp(&cmp->map, map, sizeof (cmp->map)) == 0) {
		    return;
		}

		/* We have a new entry to add */
		cmp->next_path = (struct lun_tracking *)calloc(1,
		    sizeof (struct lun_tracking));
		cmp = cmp->next_path;
		(void) memcpy(&cmp->map, map,
		    sizeof (cmp->map));
		(void) memcpy(&cmp->hba_pwwn, &pwwn,
			sizeof (cmp->hba_pwwn));
		(void) snprintf(cmp->hba_path, MAXPATHLEN,
		    path);
		scsi_vhci_details(cmp);
		return;
	    }
	}
	/* Append a new LUN at the end of the list */
	for (tmp = head; tmp->next_lun != NULL; tmp = tmp->next_lun) {}
	tmp->next_lun = (struct lun_tracking *)calloc(1,
		sizeof (struct lun_tracking));
	tmp = tmp->next_lun;
	(void) memcpy(&tmp->map, map,
		sizeof (tmp->map));
	(void) memcpy(&tmp->hba_pwwn, &pwwn,
		sizeof (tmp->hba_pwwn));
	(void) snprintf(tmp->hba_path, MAXPATHLEN,
		path);
	scsi_vhci_details(tmp);
}

/*ARGSUSED*/
int
fchba_display_config(char **argv, int option_t_input, int argc)
{
	int		path_index = 0;
	uint64_t	wwn;
	uint64_t	lun = 0;
	HBA_HANDLE handle;
	HBA_ADAPTERATTRIBUTES hbaAttrs;
	HBA_PORTATTRIBUTES portAttrs;
	HBA_FCPTARGETMAPPINGV2    *map;
	HBA_STATUS status;
	int count, adapterIndex, portIndex;
	char name[256];
	L_inquiry	inq;
	struct scsi_extended_sense sense;
	struct page80	serial;
	HBA_UINT8	scsiStatus;
	uint32_t	inquirySize = sizeof (inq), senseSize = sizeof (sense);
	uint32_t	serialSize = sizeof (serial);
	struct mode_page	*pg_hdr;
	uchar_t		*pg_buf;
	float		lunMbytes;
	struct capacity_data_struct cap_data;
	uint32_t	    cap_data_size = sizeof (cap_data);
	struct mode_header_g1	*mode_header_ptr;
	int		offset;
	char *phys_path = NULL;
	int		mpxio = 0;
	int		wwnCompare = 0;
	char	    *physical = NULL;
	struct lun_tracking	*head = NULL;
	boolean_t	goodPath = B_FALSE;
	int		ret = 0;



	if ((status = loadLibrary())) {
	    return (-1);
	}
	for (path_index = 0; argv[path_index] != NULL; path_index++) {
	    goodPath = B_FALSE;

	    if (is_wwn(argv[path_index])) {
		(void) sscanf(argv[path_index], "%016llx", &wwn);
		wwnCompare = 1;
	    } else if (!is_path(argv[path_index])) {
		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
			argv[path_index]);
		fprintf(stderr, "\n");
		ret = -1;
		continue;
	    }
	    if (!wwnCompare) {
		/* Convert the paths to phsyical paths */
		physical = get_slash_devices_from_osDevName(argv[path_index],
			STANDARD_DEVNAME_HANDLING);
		if (!physical) {
		    fprintf(stderr, MSGSTR(112,
			"Error: Invalid pathname (%s)"),
			argv[path_index]);
		    fprintf(stderr, "\n");
		    ret = -1;
		    continue;
		}
	    }

	    count = getNumberOfAdapters();


		/*
		 * We have to loop twice to ensure we don't miss any
		 * extra paths for other targets in a multi-target device
		 */

	    /* First check WWN/path comparisons */
	    for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
		if (skip_hba(adapterIndex)) {
		    continue;
		}
		status = HBA_GetAdapterName(adapterIndex, (char *)&name);
		if (status != HBA_STATUS_OK) {
		    /* May have been DR'd */
		    continue;
		}
		handle = HBA_OpenAdapter(name);
		if (handle == 0) {
		    /* May have been DR'd */
		    continue;
		}
		if (getAdapterAttrs(handle, name, &hbaAttrs)) {
		    /* Should never happen */
		    HBA_CloseAdapter(handle);
		    continue;
		}

		/* Loop over all HBA Ports */
		for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
			portIndex++) {
		    int	    matched = 0;
		    int	    mapIndex;
		    char	    *tmp;
		    if (getAdapterPortAttrs(handle, name, portIndex,
			    &portAttrs)) {
			continue;
		    }

		    if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
			continue;
		    }



		    for (mapIndex = 0; mapIndex < map->NumberOfEntries;
			    mapIndex ++) {
			matched = 0;
			if (wwnCompare) {
			    if (wwn == wwnConversion(
				    map->entry[mapIndex].FcpId.NodeWWN.wwn) ||
				    wwn == wwnConversion(
				    map->entry[mapIndex].FcpId.PortWWN.wwn)) {
				matched = 1;
			    }
			} else {
			    tmp = get_slash_devices_from_osDevName(
				    map->entry[mapIndex].ScsiId.OSDeviceName,
				    STANDARD_DEVNAME_HANDLING);
			    if ((tmp != NULL) && (strncmp(physical, tmp,
				    MAXPATHLEN) == 0)) {
				matched = 1;
				free(tmp);
			    }
			}
			if (matched && head == NULL) {
			    goodPath = B_TRUE;
			    head  = (struct lun_tracking *)calloc(1,
				    sizeof (struct lun_tracking));
			    (void) memcpy(&head->map, &map->entry[mapIndex],
				    sizeof (head->map));
			    (void) memcpy(&head->hba_pwwn, &portAttrs.PortWWN,
				    sizeof (head->hba_pwwn));
			    (void) snprintf(head->hba_path, MAXPATHLEN,
				portAttrs.OSDeviceName);
			    scsi_vhci_details(head);
			} else if (matched) {
			    goodPath = B_TRUE;
			    add_lun_path(head, &map->entry[mapIndex],
				portAttrs.PortWWN, portAttrs.OSDeviceName);
			}
		    }
		}
	    }

	    if (physical) {
		free(physical);
	    }

	    /* Now do it again and look for matching LUIDs (aka GUIDs) */
	    for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
		if (skip_hba(adapterIndex)) {
		    continue;
		}
		status = HBA_GetAdapterName(adapterIndex, (char *)&name);
		if (status != HBA_STATUS_OK) {
		    /* May have been DR'd */
		    continue;
		}
		handle = HBA_OpenAdapter(name);
		if (handle == 0) {
		    /* May have been DR'd */
		    continue;
		}

		if (getAdapterAttrs(handle, name, &hbaAttrs)) {
		    /* Should never happen */
		    HBA_CloseAdapter(handle);
		    continue;
		}


		/* Loop over all HBA Ports */
		for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
			portIndex++) {
		    int	    matched = 0;
		    int	    mapIndex;
		    if (getAdapterPortAttrs(handle, name, portIndex,
			    &portAttrs)) {
			continue;
		    }

		    if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
			continue;
		    }


		    for (mapIndex = 0; mapIndex < map->NumberOfEntries;
			    mapIndex ++) {
			struct lun_tracking *outer;
			matched = 0;
			for (outer = head; outer != NULL;
				    outer = outer->next_lun) {
			    struct lun_tracking *inner;
			    for (inner = outer; inner != NULL;
				    inner = inner->next_path) {
				if (memcmp(&inner->map.LUID,
					&map->entry[mapIndex].LUID,
					sizeof (HBA_LUID)) == 0) {
				    matched = 1;
				    break;
				}
			    }
			    if (matched) {
				break;
			    }
			}
			if (matched && head == NULL) {
			    goodPath = B_TRUE;
			    head  = (struct lun_tracking *)calloc(1,
				    sizeof (struct lun_tracking));
			    (void) memcpy(&head->map, &map->entry[mapIndex],
				    sizeof (head->map));
			    (void) memcpy(&head->hba_pwwn, &portAttrs.PortWWN,
				    sizeof (head->hba_pwwn));
			    (void) snprintf(head->hba_path, MAXPATHLEN,
				portAttrs.OSDeviceName);
			    scsi_vhci_details(head);
			} else if (matched) {
			    goodPath = B_TRUE;
			    add_lun_path(head, &map->entry[mapIndex],
				portAttrs.PortWWN, portAttrs.OSDeviceName);
			}
		    }
		}
	    }
	    if (!goodPath) {
		fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
			argv[path_index]);
		fprintf(stderr, "\n");
		ret = -1;
		/* Just bomb out instead of going on */
		return (ret);
	    }
	}

	/* Now display all the LUNs that we found that matched */
	{
	    struct lun_tracking *first_time;
	    struct lun_tracking *tmp_path;
	    for (first_time = head; first_time != NULL;
		    first_time = first_time->next_lun) {
		struct lun_tracking *path;
		phys_path = get_slash_devices_from_osDevName(
		    first_time->map.ScsiId.OSDeviceName,
		    STANDARD_DEVNAME_HANDLING);
		/* Change behavior if this is an MPxIO device */
		if (phys_path != NULL) {
		    if (strstr(phys_path, VHCI_COMPONENT) != NULL) {
			mpxio = 1;
		    }
		}

		for (tmp_path = first_time; tmp_path != NULL;
			tmp_path = tmp_path->next_path) {
			if (mpxio && (strncmp(tmp_path->info.pathState,
			    "ONLINE", strlen(tmp_path->info.pathState)))) {
				/* continue to next online path */
				continue;
			}
			status = HBA_OpenAdapterByWWN(&handle,
			    tmp_path->hba_pwwn);
			if (status != HBA_STATUS_OK) {
				fprintf(stderr, MSGSTR(2431,
				    "Error: Failed to get handle for %s  "),
				    tmp_path->hba_path);
				printStatus(status);
				fprintf(stderr, "\n");
				/* continue to next path */
				continue;
			}

			lun = tmp_path->map.FcpId.FcpLun;
			memset(&inq, 0, sizeof (inq));
			memset(&sense, 0, sizeof (sense));

			status = HBA_ScsiInquiryV2(handle,
				tmp_path->hba_pwwn,
				tmp_path->map.FcpId.PortWWN,
				lun, 0, 0,
				&inq, &inquirySize,
				&scsiStatus,
				&sense, &senseSize);

			if (status == HBA_STATUS_OK) {
				break;
			}
			HBA_CloseAdapter(handle);
		}

		if (tmp_path == NULL) {
			fprintf(stderr, MSGSTR(2430,
			    "Error: I/O failure communicating with %s  "),
			    first_time->map.ScsiId.OSDeviceName);
			printStatus(status);
			fprintf(stderr, "\n");
			continue;
		}

		switch ((inq.inq_dtype & DTYPE_MASK)) {
		case DTYPE_DIRECT:
		    fprintf(stdout, MSGSTR(121,
			    "DEVICE PROPERTIES for disk: %s\n"),
			    first_time->map.ScsiId.OSDeviceName);
		    break;
		case DTYPE_SEQUENTIAL: /* Tape */
		    fprintf(stdout, MSGSTR(2249,
			    "DEVICE PROPERTIES for tape: %s\n"),
			    first_time->map.ScsiId.OSDeviceName);
		    break;
		default:
		    fprintf(stdout, MSGSTR(2250,
			    "DEVICE PROPERTIES for: %s\n"),
			    first_time->map.ScsiId.OSDeviceName);
		    break;
		}
		fprintf(stdout, "  ");
		fprintf(stdout, MSGSTR(3, "Vendor:"));
		fprintf(stdout, "\t\t");
		print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0);
		fprintf(stdout, MSGSTR(2115, "\n  Product ID:\t\t"));
		print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0);

		fprintf(stdout, "\n  ");
		fprintf(stdout, MSGSTR(2119, "Revision:"));
		fprintf(stdout, "\t\t");
		print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0);

		fprintf(stdout, "\n  ");
		fprintf(stdout, MSGSTR(17, "Serial Num:"));
		fprintf(stdout, "\t\t");
		(void) fflush(stdout);
		/*
		 * Call the inquiry cmd on page 0x80 only if the vendor
		 * supports page 0x80.
		 */
		if ((find_supported_inq_page(handle, first_time->hba_pwwn,
		    first_time->map.FcpId.PortWWN, lun, 0x80))) {
			memset(&serial, 0, sizeof (serial));
			status = HBA_ScsiInquiryV2(handle,
			    first_time->hba_pwwn,
			    first_time->map.FcpId.PortWWN,
			    lun, 1, 0x80,
			    &serial, &serialSize,
			    &scsiStatus,
			    &sense, &senseSize);
			if (status == HBA_STATUS_OK) {
				print_chars(serial.inq_serial,
				    sizeof (serial.inq_serial), 0);
			} else {
				fprintf(stdout, MSGSTR(2506, "Unsupported"));
			}
		} else {
			fprintf(stdout, MSGSTR(2506, "Unsupported"));
		}
		HBA_CloseAdapter(handle);
		if ((inq.inq_dtype & DTYPE_MASK) == DTYPE_DIRECT) {
		/* Read capacity wont work on standby paths, so try till OK */
		    for (tmp_path = first_time; tmp_path != NULL;
			tmp_path = tmp_path->next_path) {
			if (mpxio && (strncmp(tmp_path->info.pathState,
			    "ONLINE", strlen(tmp_path->info.pathState)))) {
			    /* continue to next online path */
			    continue;
			}
			status = HBA_OpenAdapterByWWN(&handle,
						tmp_path->hba_pwwn);
			if (status != HBA_STATUS_OK) {
			    /* continue to next path */
			    continue;
			}

			status = HBA_ScsiReadCapacityV2(handle,
			    tmp_path->hba_pwwn,
			    tmp_path->map.FcpId.PortWWN,
			    tmp_path->map.FcpId.FcpLun,
			    &cap_data, &cap_data_size,
			    &scsiStatus,
			    &sense, &senseSize);
			if (status == HBA_STATUS_OK) {
			    break;
			} else if (status == HBA_STATUS_SCSI_CHECK_CONDITION &&
			    sense.es_key == KEY_UNIT_ATTENTION) {
			/*
			 * retry for check-condition state when unit attention
			 * condition has been established
			 */
			    status =  HBA_ScsiReadCapacityV2(handle,
				tmp_path->hba_pwwn,
				tmp_path->map.FcpId.PortWWN,
				tmp_path->map.FcpId.FcpLun,
				&cap_data, &cap_data_size,
				&scsiStatus,
				&sense, &senseSize);
			    if (status == HBA_STATUS_OK) {
				break;
			    }
			}
			HBA_CloseAdapter(handle);
		    }
		}
		if (handle != HBA_HANDLE_INVALID) {
			HBA_CloseAdapter(handle);
		}
		if (status != HBA_STATUS_OK) {
		    /* Make sure we don't display garbage */
		    cap_data.block_size = 0;
		    cap_data.last_block_addr = 0;
		}

		if (cap_data.block_size > 0 &&
			cap_data.last_block_addr > 0) {
		    lunMbytes = ntohl(cap_data.last_block_addr) + 1;
		    lunMbytes *= ntohl(cap_data.block_size);
		    lunMbytes /= (float)(1024*1024);
		    fprintf(stdout, "\n  ");
		    fprintf(stdout, MSGSTR(60,
			    "Unformatted capacity:\t%6.3f MBytes"), lunMbytes);
		}
		fprintf(stdout, "\n");

		/*
		 * get mode page information for FC device.
		 * do not do mode sense if this is a tape device.
		 * mode sense will rewind the tape
		 */
		if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_SEQUENTIAL) {
		    if (get_mode_page(first_time->map.ScsiId.OSDeviceName,
			&pg_buf) == 0) {
			mode_header_ptr = (struct mode_header_g1 *)
				(void *)pg_buf;
			offset = sizeof (struct mode_header_g1) +
			    ntohs(mode_header_ptr->bdesc_length);
			pg_hdr = (struct mode_page *)&pg_buf[offset];

			while (offset < (ntohs(mode_header_ptr->length) +
			    sizeof (mode_header_ptr->length))) {
			    if (pg_hdr->code == MODEPAGE_CACHING) {
				struct	mode_caching	*pg8_buf;
				pg8_buf = (struct mode_caching *)
				    (void *)pg_hdr;
				if (pg8_buf->wce) {
				    fprintf(stdout, MSGSTR(2122,
					"  Write Cache:\t\t"
					"Enabled\n"));
				}
				if (pg8_buf->rcd == 0) {
				    fprintf(stdout, MSGSTR(2123,
					"  Read Cache:\t\t"
					"Enabled\n"));
				    fprintf(stdout, MSGSTR(2509,
					"    Minimum prefetch:\t0x%x\n"
					"    Maximum prefetch:\t0x%x\n"),
					pg8_buf->min_prefetch,
					pg8_buf->max_prefetch);
				}
				break;
			    }
			    offset += pg_hdr->length +
				sizeof (struct mode_page);
			    pg_hdr = (struct mode_page *)&pg_buf[offset];
			}
		    }
		}

		fprintf(stdout, "  %s\t\t", MSGSTR(35, "Device Type:"));
		if ((inq.inq_dtype & DTYPE_MASK) < 0x10) {
			fprintf(stdout, "%s\n",
			    dtype[inq.inq_dtype & DTYPE_MASK]);
		} else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) {
			fprintf(stdout, MSGSTR(2432, "Reserved"));
		} else {
			/* dtype of 0x1f is returned */
			fprintf(stdout, MSGSTR(2433, "Unknown"));
		}

		fprintf(stdout, MSGSTR(2128, "  Path(s):\n"));
		fprintf(stdout, "\n");
		fprintf(stdout, "  %s\n",
		    first_time->map.ScsiId.OSDeviceName);
		if (phys_path != NULL) {
		    fprintf(stdout, "  %s\n", phys_path);
		}

		/* Now display all paths to this LUN */
		for (path = first_time; path != NULL;
		    path = path->next_path) {
		    /* Display the controller information */
		    fprintf(stdout, MSGSTR(2303, "   Controller      \t%s\n"),
			    path->hba_path);

		    fprintf(stdout, MSGSTR(2507,
			    "    Device Address\t\t%016llx,%x\n"),
			    wwnConversion(
			    path->map.FcpId.PortWWN.wwn),
			    path->map.ScsiId.ScsiOSLun);

		    fprintf(stdout, MSGSTR(2508,
			    "    Host controller port WWN\t%016llx\n"),
			    wwnConversion(path->hba_pwwn.wwn));

		    if (mpxio) {
			fprintf(stdout, MSGSTR(2305,
				"    Class\t\t\t%s\n"), path->info.pathClass);
			fprintf(stdout, MSGSTR(2306,
				"    State\t\t\t%s\n"), path->info.pathState);
		    }
		    if (phys_path != NULL) {
			free(phys_path);
			phys_path = NULL;
		    }
		}
		printf("\n");
	    }
	}
	return (ret);
}

/*
 * handle expert-mode hotplug commands
 *
 * return 0 iff all is okay
 */
int
fchba_hotplug_e(int todo, char **argv, int verbose_flag, int force_flag)
{
char		*path_phys = NULL;
int		exit_code;
devctl_hdl_t	dcp;

	if (todo != DEV_ONLINE &&
	    todo != DEV_OFFLINE) {
	    fprintf(stderr, "%s\n", strerror(ENOTSUP));
	    return (-1);
	}

	/* Convert the paths to phsyical paths */
	path_phys = get_slash_devices_from_osDevName(argv[0],
		NOT_IGNORE_DANGLING_LINK);
	if (!path_phys) {
	    fprintf(stderr, MSGSTR(112,
		"Error: Invalid pathname (%s)"),
		argv[0]);
	    fprintf(stderr, "\n");
	    return (-1);
	}
	if (verbose_flag) {
		(void) fprintf(stdout,
				MSGSTR(5516,
				"phys path = \"%s\"\n"),
				path_phys);
	}
	/* acquire rights to hack on device */
	if ((dcp = devctl_device_acquire(path_phys,
		force_flag ? 0 : DC_EXCL)) == NULL) {

		(void) fprintf(stderr, MSGSTR(5517,
		    "Error: can't acquire \"%s\": %s\n"),
		    path_phys, strerror(errno));
		return (1);
	}

	switch (todo) {
	case DEV_ONLINE:
		exit_code = devctl_device_online(dcp);
		break;
	case DEV_OFFLINE:
		exit_code = devctl_device_offline(dcp);
		break;
	}

	if (exit_code != 0) {
		perror(MSGSTR(5518, "devctl"));
	}

	/* all done now -- release device */
	devctl_release(dcp);

	if (path_phys) {
	    free(path_phys);
	}

	return (exit_code);
}

/*
 * Returns non zero if we should use FC-HBA.
 * For x86, luxadm uses FC-HBA.
 */
int
use_fchba()
{

#ifdef __x86
	return (1);
#else
	return (0);
#endif

}

/*
 * Returns non-zero if we should skip the HBA at index "i"
 */
int
skip_hba(int i) {
	HBA_LIBRARYATTRIBUTES lib_attrs;
	(void) HBA_GetVendorLibraryAttributes(i, &lib_attrs);
	if (strncmp(lib_attrs.VName, VSL_NAME,
		sizeof (lib_attrs.VName)) == 0) {
	    return (0);
	}
	return (1);
}

/*
 * Function to determine if the given page is supported by vendor.
 */
int
find_supported_inq_page(HBA_HANDLE handle, HBA_WWN hwwn, HBA_WWN pwwn,
    uint64_t lun, int page_num)
{
	struct	scsi_extended_sense	sense;
	L_inquiry00			inq00;
	uchar_t				*data;
	HBA_STATUS			status = HBA_STATUS_ERROR;
	int				index;
	HBA_UINT8			scsiStatus;
	uint32_t			inqSize = sizeof (inq00);
	uint32_t			senseSize = sizeof (sense);

	status = HBA_ScsiInquiryV2(handle, hwwn, pwwn, lun, 1, 0x00,
	    &inq00, &inqSize, &scsiStatus, &sense, &senseSize);

	if (status == HBA_STATUS_OK) {
		data = (uchar_t *)&inq00;
		for (index = 4; (index <= inq00.len+3)&&
		    (data[index] <= page_num); index ++) {
			if (data[index] == page_num) {
				return (1);
			}
		}
	}
	return (0);
}