OpenSolaris_b135/cmd/sasinfo/sasinfo-list.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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <printAttrs.h>
#include <smhbaapi.h>

#define	TABLEN	2
typedef struct inputArgs {
	int 		wwnCount;
	char 		**wwn_argv;
	uint64_t 	portWWN;
	char		*hbaName;
	int		pflag;
	int		*wwn_flag;
} inputArg_t;

typedef struct tgt_mapping {
	SMHBA_SCSIENTRY	tgtentry;
	uchar_t		inq_vid[8];
	uchar_t		inq_pid[16];
	uchar_t		inq_dtype;
	struct tgt_mapping *next;
}tgt_mapping;

/*
 * Remote port tree node structure.
 */
typedef struct smhba_rp_tree {
	SMHBA_PORTATTRIBUTES	portattr;
	SMHBA_SAS_PORT		sasattr;
	tgt_mapping		*first_entry;
	int			printed;
	struct smhba_rp_tree	*parent;
	struct smhba_rp_tree	*child;
	struct smhba_rp_tree	*sibling;
}rp_tree_t;

/*
 * Report LUN data structure.
 */
struct lun {
	uchar_t	val[8];
};

typedef struct rep_luns_rsp {
	uint32_t    length;
	uint32_t    rsrvd;
	struct lun  lun[1];
} rep_luns_rsp_t;

/*
 * The following flag is used for printing HBA header on-demand.
 */
static int g_printHBA = 0;

/*
 * The following structure is for sorted output of HBA and HBA Port.
 */
typedef struct _sas_elem {
	char	name[256];
	int	index;
}sas_elem_t;

/*
 * The following two functions are for generating hierachy of expander
 * subcommand.
 */
static int
sas_rp_tree_insert(rp_tree_t **rproot, rp_tree_t *rpnode);
static int
sas_rp_tree_print(HBA_HANDLE handle, char *adapterName,
    HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
    rp_tree_t *rpnode, inputArg_t *input, int gident,
    int *printPort);
static int
sas_rp_tree_print_desc(HBA_HANDLE handle, HBA_UINT32 portIndex,
    SMHBA_PORTATTRIBUTES *port, rp_tree_t *desc,
    inputArg_t *input, int lident, int gident);
static int
sas_print_rpnode(inputArg_t *input,
    rp_tree_t *rpnode, int lident, int gident);
static void sas_rp_tree_free(rp_tree_t *rproot);

typedef int (*processPortFunc)(HBA_HANDLE handle, char *adapterName,
    HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
    SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);

static int processHBA(inputArg_t *input,
    processPortFunc processPort);

static int isPortWWNInArgv(inputArg_t *input, PHBA_WWN pWWN);
static int isStringInArgv(inputArg_t *input, const char *adapterName);
static boolean_t compareLUName(char *cmdArg, char *osName);
static discoveredDevice *LUList = NULL;
static targetPortList_t *gTargetPortList = NULL;

/* processes for hanlding local HBA info */
static int handleHBA(SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input,
    int numberOfPorts, const char *adapterName);
static int handleHBAPort(HBA_HANDLE handle, char *adapterName,
    HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
    SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);
static int processHBAPortPhyInfo(HBA_HANDLE handle, HBA_UINT32 portIndex,
    SMHBA_PORTATTRIBUTES *port, int pflag);
static int processHBAPortPhyStat(HBA_HANDLE handle, HBA_UINT32 portIndex,
    int phyIndex, PSMHBA_SAS_PHY phyattrs, int pflag);

/* process for handling expander info */
static int handleExpander(HBA_HANDLE handle, char *adapterName,
    HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
    SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);

/* process for handling target port info */
static int handleTargetPort(HBA_HANDLE handle, char *adapterName,
    HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
    SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);

/* process for handling logical unit info */
static int handleLogicalUnit(HBA_HANDLE handle, char *adapterName,
    HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
    SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);

/* process for target port SCSI processing */
static int
searchTargetPortMappingData(HBA_HANDLE handle, HBA_UINT32 portIndex,
    SMHBA_PORTATTRIBUTES *port, SMHBA_SAS_PORT *sasattr,
    struct targetPortConfig *configData);

/* process for target port config processing */
static int searchTargetPort(HBA_HANDLE handle, HBA_UINT32 portIndex,
    SMHBA_PORTATTRIBUTES *port, SMHBA_PORTATTRIBUTES *targetattr,
    SMHBA_SAS_PORT *sasattr, int pflag);

/* process for logical-unit config processing */
static int
searchDevice(PSMHBA_SCSIENTRY entryP, HBA_HANDLE handle, HBA_WWN hbaPortWWN,
    HBA_WWN domainPortWWN, char *portName, int pflag);

/* get domain port out of hba-port phy attr. */
HBA_STATUS get_domainPort(HBA_HANDLE handle,
    int portindex, PSMHBA_PORTATTRIBUTES port,
    HBA_WWN *pdomainPort);

static int
sas_name_comp(const char *name1, const char *name2);
static void
sas_elem_sort(sas_elem_t *array, int nelem);

/*
 * function for hba subcommand
 *
 * Arguments:
 *	wwnCount - count of the number of WWNs in wwn_argv
 *	    if wwnCount > 0, then we will only print information for
 *		the hba ports listed in wwn_argv
 *	    if wwnCount == 0, then we will print information on all hba ports
 *	wwn_argv - argument array of hba port WWNs
 *	options - any options specified by the caller
 *
 * returns:
 *	0	if successful
 *	>0	otherwise
 */
int
sas_util_list_hba(int hbaCount, char **hba_argv, cmdOptions_t *options)
{
	HBA_STATUS		status;
	int			processHBA_flags = 0;
	inputArg_t		input;
	int 			err_cnt = 0;

	/* process each of the options */
	for (; options->optval; options++) {
		switch (options->optval) {
		case 'v':
			processHBA_flags |= PRINT_VERBOSE;
			break;
		default:
			break;
		}
	}

	if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
		(void *) fprintf(stderr, "%s %s\n",
		    gettext("Failed to load SM-HBA libraries."
		    "Reason:"), getHBAStatus(status));
		err_cnt++;
		return (err_cnt);
	}

	(void *) memset(&input, 0, sizeof (input));
	/* utilize wwnCount and wwn_argv for hbaCount and hba_argv */
	input.wwnCount = hbaCount;
	input.wwn_argv = hba_argv;
	input.pflag = processHBA_flags;

	/*
	 * Process and filter for every local hba,
	 * when the hba is not specificed, print all hba(s).
	 */
	err_cnt += processHBA(&input, NULL);

	(void) HBA_FreeLibrary();

	return (err_cnt);
}

/*
 * function for hba-port subcommand
 *
 * Arguments:
 *	wwnCount - count of the number of WWNs in wwn_argv
 *	    if wwnCount > 0, then we will only print information for
 *		the hba ports listed in wwn_argv
 *	    if wwnCount == 0, then we will print information on all hba ports
 *	wwn_argv - argument array of hba port WWNs
 *	options - any options specified by the caller
 *
 * returns:
 *	0	if successful
 *	>0	otherwise
 */
int
sas_util_list_hbaport(int wwnCount, char **wwn_argv, cmdOptions_t *options)
{
	HBA_STATUS		status;
	int			processHBA_flags = 0;
	inputArg_t		input;
	int 			err_cnt = 0;
	char			hbaName[256] = {'\0'};

	/* process each of the options */
	for (; options->optval; options++) {
		switch (options->optval) {
		case 'a':
			(void *) strlcpy(hbaName,
			    options->optarg, sizeof (hbaName));
			break;
		case 'y':
			processHBA_flags |= PRINT_PHY;
			break;
		case 'l':
			processHBA_flags |= PRINT_PHY_LINKSTAT;
			break;
		case 'v':
			processHBA_flags |= PRINT_VERBOSE;
			break;
		default:
			break;
		}
	}

	if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
		(void *) fprintf(stderr, "%s %s\n",
		    gettext("Failed to load SM-HBA libraries."
		    "Reason:"), getHBAStatus(status));
		err_cnt++;
		return (err_cnt);
	}

	(void *) memset(&input, 0, sizeof (input));
	input.wwnCount = wwnCount;
	input.wwn_argv = wwn_argv;
	input.hbaName = hbaName;
	input.pflag = processHBA_flags;

	/*
	 * Process and filter for every local hba-port,
	 * when the hba-port is not specificed, print all hba-port(s).
	 */
	err_cnt += processHBA(&input, handleHBAPort);

	(void) HBA_FreeLibrary();

	return (err_cnt);
}

/*
 * function for expander subcommand
 *
 * Arguments:
 *	wwnCount - the number of Remote Port SAS Address in wwn_argv
 *	    if wwnCount == 0, then print information on all
 *		expander devices.
 *	    if wwnCount > 0, then print information for the exapnders
 *		given in wwn_argv.
 *	wwn_argv - array of WWNs
 *	options - options specified by the caller
 *
 * returns:
 *	0	if successful
 *	>0	otherwise
 */
int
sas_util_list_expander(int wwnCount, char **wwn_argv, cmdOptions_t *options)
{
	HBA_STATUS		status;
	int			processHBA_flags = 0;
	char			hbaPort[MAXPATHLEN + 1] = {0};
	inputArg_t		input;
	int			err_cnt = 0;

	/* process each of the options */
	for (; options->optval; options++) {
		switch (options->optval) {
		case 'p':
			(void) strlcpy(hbaPort, options->optarg,
			    sizeof (hbaPort));
			break;
		case 't':
			processHBA_flags |= PRINT_TARGET_PORT;
			break;
		case 'v':
			processHBA_flags |= PRINT_VERBOSE;
			break;
		default:
			break;
		}
	}

	if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
		(void *) fprintf(stderr, "%s %s\n",
		    gettext("Failed to load SM-HBA libraries."
		    "Reason:"), getHBAStatus(status));
		err_cnt++;
		return (err_cnt);
	}

	(void *) memset(&input, 0, sizeof (input));
	input.wwnCount = wwnCount;
	input.wwn_argv = wwn_argv;
	input.pflag = processHBA_flags;
	input.hbaName = hbaPort;

	/*
	 * Process and filter for every hba-port,
	 * when the hba-port is not specificed, print all hba-port(s).
	 */
	err_cnt += processHBA(&input, handleExpander);

	(void) HBA_FreeLibrary();

	return (err_cnt);
}

/*
 * function for target-port subcommand
 *
 * Arguments:
 *	wwnCount - the number of Remote Port SAS Address in wwn_argv
 *	    if wwnCount == 0, then print information on all
 *		target ports.
 *	    if wwnCount > 0, then print information for the target ports
 *		given in wwn_argv.
 *	wwn_argv - array of WWNs
 *	options - options specified by the caller
 *
 * returns:
 *	0	if successful
 *	>0	otherwise
 */
int
sas_util_list_targetport(int tpCount, char **tpArgv, cmdOptions_t *options)
{
	HBA_STATUS		status;
	int			processHBA_flags = 0;
	int			tp, tpFound;
	inputArg_t		input;
	targetPortList_t	*tpListWalk;
	int			err_cnt = 0;
	uint64_t		tmpAddr;

	/* process each of the options */
	for (; options->optval; options++) {
		switch (options->optval) {
		case 's':
			processHBA_flags |= PRINT_TARGET_SCSI;
			break;
		case 'v':
			processHBA_flags |= PRINT_VERBOSE;
			break;
		default:
			break;
		}
	}

	if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
		(void *) fprintf(stderr, "%s %s\n",
		    gettext("Failed to load SM-HBA libraries."
		    "Reason:"), getHBAStatus(status));
		err_cnt++;
		return (err_cnt);
	}

	(void *) memset(&input, 0, sizeof (input));
	input.wwnCount = tpCount;
	input.wwn_argv = tpArgv;
	input.pflag = processHBA_flags;

	/*
	 * Process and filter for every hba-port,
	 * when the hba-port is not specificed, print all hba-port(s).
	 */
	err_cnt += processHBA(&input, handleTargetPort);

	if (tpCount == 0) {
		/* list all target port */
		for (tpListWalk = gTargetPortList; tpListWalk != NULL;
		    tpListWalk = tpListWalk->next) {
			err_cnt += printTargetPortInfo(tpListWalk, input.pflag);
		}
	} else {
		/*
		 * When operands provided, we should set the error code
		 * only if there are issues related with the operands.
		 */
		err_cnt = 0;
		/*
		 * list any paths not found first
		 * this gives the user cleaner output
		 */
		for (tp = 0; tp < tpCount; tp++) {
			errno = 0;
			tmpAddr = strtoull(tpArgv[tp], NULL, 16);
			if ((tmpAddr == 0) && (errno != 0)) {
				err_cnt++;
				continue;
			}
			for (tpListWalk = gTargetPortList, tpFound = B_FALSE;
			    tpListWalk != NULL;
			    tpListWalk = tpListWalk->next) {
				if (wwnConversion(tpListWalk->sasattr.
				    LocalSASAddress.wwn) == tmpAddr) {
					tpFound = B_TRUE;
					break;
				}
			}
			if (tpFound == B_FALSE) {
				(void *) fprintf(stderr,
				    "Error: Target Port %s Not Found \n",
				    tpArgv[tp]);
				err_cnt++;
			}
		}
		/* list all paths requested in order requested */
		for (tp = 0; tp < tpCount; tp++) {
			errno = 0;
			tmpAddr = strtoull(tpArgv[tp], NULL, 16);
			if ((tmpAddr == 0) && (errno != 0)) {
				continue;
			}
			for (tpListWalk = gTargetPortList, tpFound = B_FALSE;
			    tpListWalk != NULL;
			    tpListWalk = tpListWalk->next) {
				if (wwnConversion(tpListWalk->sasattr.
				    LocalSASAddress.wwn) == tmpAddr) {
					err_cnt += printTargetPortInfo(
					    tpListWalk,
					    processHBA_flags);
				}
			}
		}
	}
	(void) HBA_FreeLibrary();
	return (err_cnt);
}
/*
 * This function will enumerate all the hba and hba ports,
 * call the callback function to proceed with futher process.
 *
 * Arguments:
 *	input - contains all the input parameters.
 *	processPort - a callback function when handling each port.
 *
 *  Return Value:
 *	    0		sucessfully processed handle
 *	    >0		error has occured
 */
static int
processHBA(inputArg_t *input, processPortFunc processPort)
{
	int			numAdapters = 0;
	int			matchedHBAs = 0;
	int			matchedHBAPorts = 0;
	int			hbaPortExist = 0;
	HBA_STATUS		status;
	HBA_HANDLE		handle;
	HBA_UINT32		numberOfPorts = 0;
	int			portIndex = 0;
	HBA_PORTTYPE		porttype;
	SMHBA_LIBRARYATTRIBUTES libattrs;
	SMHBA_ADAPTERATTRIBUTES	attrs;
	SMHBA_PORTATTRIBUTES	port;
	SMHBA_SAS_PORT		sasattrs;
	int			i, sum, ret = 0;
	int			remote_avail = 0;
	int			local_avail = 0;
	sas_elem_t		*adpt_array = NULL;
	sas_elem_t		*port_array = NULL;

	numAdapters = HBA_GetNumberOfAdapters();
	if (numAdapters == 0) {
		(void *) fprintf(stderr, "%s\n",
		    gettext("Error: No Adapters Found."));
		return (++ret);
	}

	/*
	 * To deal with mismatching HBA/HBA Port/Expander Port, we need an
	 * array of flags for each operands.
	 */
	if (input->wwnCount && (processPort != handleTargetPort) &&
	    (processPort != handleLogicalUnit)) {
		input->wwn_flag = calloc(input->wwnCount, sizeof (int));
		if (input->wwn_flag == NULL) {
			(void *) fprintf(stderr, "%s\n",
			    gettext("No enough memory on heap"));
			return (++ret);
		}
	}

	adpt_array = calloc(numAdapters, sizeof (sas_elem_t));
	if (adpt_array == NULL) {
		(void *) fprintf(stderr, "%s\n",
		    gettext("No enough memory on heap"));
		if (input->wwn_flag) {
			free(input->wwn_flag);
			input->wwn_flag = NULL;
		}
		return (++ret);
	}
	for (i = 0; i < numAdapters; i++) {
		status =
		    SMHBA_GetVendorLibraryAttributes(i, &libattrs);
		/*
		 * If we get SAS incompatible library warning here,
		 * just skip the following steps.
		 */
		if (status != 1) {
			continue;
		}
		status = HBA_GetAdapterName(i, adpt_array[i].name);
		if (status != HBA_STATUS_OK) {
			(void *) fprintf(stderr, "%s %d %s %s\n",
			    gettext("Error: Failed to get the name for"
			    " HBA index"),
			    i, gettext("Reason:"),
			    getHBAStatus(status));
			ret++;
			continue;
		}
		adpt_array[i].index = i;
	}
	/* Sort the HBA Name in place. */
	sas_elem_sort(adpt_array, numAdapters);

	for (i = 0; i < numAdapters; i++) {
		int times = 0;
		if (adpt_array[i].name[0] != '\0') {
			if ((handle = HBA_OpenAdapter(adpt_array[i].name))
			    == 0) {
				(void *) fprintf(stderr, "%s %s.\n",
				    gettext("Error: Failed to open adapter"),
				    adpt_array[i].name);
				ret++;
				continue;
			}
		} else {
			continue;
		}

		/*
		 * We need to support an adapter without hba port.
		 * So get attributes anyway.
		 */
		(void *) memset(&attrs, 0, sizeof (attrs));
		status = SMHBA_GetAdapterAttributes(handle, &attrs);
		while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
		    status == HBA_STATUS_ERROR_BUSY) &&
		    times++ < HBA_MAX_RETRIES) {
			(void) sleep(1);
			status = SMHBA_GetAdapterAttributes(handle,
			    &attrs);
		}
		if (status != HBA_STATUS_OK) {
			(void *) fprintf(stderr, "%s %s %s %s\n",
			    gettext("Error: Failed to get attributes"
			    " for HBA "), adpt_array[i].name,
			    gettext("Reason:"),
			    getHBAStatus(status));

			HBA_CloseAdapter(handle);
			ret++;
			continue;
		}

		status = SMHBA_GetNumberOfPorts(handle, &numberOfPorts);
		if (status != HBA_STATUS_OK) {
			(void *) fprintf(stderr, "%s %s %s %s\n",
			    gettext("Error: Failed to get number of ports "
			    "for HBA"), adpt_array[i].name,
			    gettext("Reason:"),
			    getHBAStatus(status));
			HBA_CloseAdapter(handle);
			ret++;
			continue;
		}

		/*
		 * Deal with each subcommand for hba filter here,
		 * processPort is NULL for hba subcommand.
		 */
		if (processPort == NULL) {
			matchedHBAs += handleHBA(&attrs, input,
			    numberOfPorts, adpt_array[i].name);
			HBA_CloseAdapter(handle);
			continue;
		} else if (processPort == handleHBAPort) {
			if (input->hbaName[0] != '\0') {
				if (strcmp(input->hbaName,
				    adpt_array[i].name) == 0) {
					matchedHBAs++;
				} else {
					continue;
				}
			} else {
				matchedHBAs++;
			}
		} else {
			matchedHBAs++;
		}

		/*
		 * In order to have a sorted output for HBA Port, we should
		 * do the sorting before moving on.
		 */
		if (numberOfPorts) {
			port_array = calloc(numberOfPorts, sizeof (sas_elem_t));
		}
		for (portIndex = 0; portIndex < numberOfPorts; portIndex++) {
			if ((status = SMHBA_GetPortType(handle,
			    portIndex, &porttype)) != HBA_STATUS_OK) {
				(void *) fprintf(stderr, "%s %s %s %s\n",
				    gettext("Failed to get adapter port type "
				    "for HBA"), adpt_array[i].name,
				    gettext("Reason:"),
				    getHBAStatus(status));
				ret++;
				continue;
			}
			if (porttype != HBA_PORTTYPE_SASDEVICE) {
				/* skip any non-sas hba port */
				continue;
			}
			(void *) memset(&port, 0, sizeof (port));
			(void *) memset(&sasattrs, 0, sizeof (sasattrs));
			port.PortSpecificAttribute.SASPort = &sasattrs;
			if ((status = SMHBA_GetAdapterPortAttributes(
			    handle, portIndex, &port)) != HBA_STATUS_OK) {
				/*
				 * Not able to get port attributes.
				 * print out error message and
				 * move on to the next port
				 */
				(void *) fprintf(stderr, "%s %s %s %d %s %s\n",
				    gettext("Error: Failed to get port "
				    "attributes for HBA"), adpt_array[i].name,
				    gettext("port index"), portIndex,
				    gettext("Reason:"),
				    getHBAStatus(status));
				ret++;
				continue;
			}
			(void) strlcpy(port_array[portIndex].name,
			    port.OSDeviceName,
			    sizeof (port_array[portIndex].name));
			port_array[portIndex].index = portIndex;
		}
		/* Sort the HBA Port Name here. */
		if (port_array) {
			sas_elem_sort(port_array, numberOfPorts);
		}
		/*
		 * Sum up the local hba ports available.
		 */
		local_avail += numberOfPorts;

		/*
		 * Clear g_printHBA flag for expander subcommand.
		 */
		g_printHBA = 0;

		/* process each port on the given adapter */
		for (portIndex = 0;
		    portIndex < numberOfPorts;
		    portIndex++) {
			/*
			 * We only handle the port which is valid.
			 */
			if (port_array[portIndex].name[0] == '\0') {
				continue;
			}
			(void *) memset(&port, 0, sizeof (port));
			(void *) memset(&sasattrs, 0, sizeof (sasattrs));
			port.PortSpecificAttribute.SASPort = &sasattrs;

			(void) SMHBA_GetAdapterPortAttributes(handle,
			    port_array[portIndex].index, &port);

			/*
			 * We have different things to do for the three
			 * sub-commands here.
			 */
			if (processPort == handleHBAPort) {
				/*
				 * For hba-port, we will check whether the
				 * specified hba port exist first.
				 * But if no hba port specified, we should
				 * by pass this check(just let hbaPortExist
				 * be 1).
				 */
				if (input->wwnCount > 0) {
					if (isStringInArgv(input,
					    port.OSDeviceName)) {
						hbaPortExist = 1;
						if (g_printHBA == 0) {
							(void *) fprintf(stdout,
							    "%s %s\n",
							    "HBA Name:",
							    adpt_array[i].name);
							g_printHBA = 1;
						}
					}
				} else {
					hbaPortExist = 1;
					if (g_printHBA == 0) {
						(void *) fprintf(stdout,
						    "%s %s\n",
						    "HBA Name:",
						    adpt_array[i].name);
						g_printHBA = 1;
					}
				}
			}

			if (processPort == handleExpander) {
				/*
				 * For expander device, input->hbaName is
				 * the hba port name specified on the
				 * command line(with -p option).
				 */
				if (input->hbaName[0] != '\0') {
					if (strcmp(input->hbaName,
					    port.OSDeviceName) == 0)
						hbaPortExist = 1;
				} else
					hbaPortExist = 1;
			}

			if (processPort == handleTargetPort) {
				/*
				 * For target port, we don't need to check the
				 * hba port address, so let it go here.
				 */
				hbaPortExist = 1;
			}

			if (processPort == handleLogicalUnit) {
				/*
				 * For lu, we don't need to check the hba
				 * port address, so let it go here.
				 */
				hbaPortExist = 1;
			}

			if (hbaPortExist) {
				if (port.PortSpecificAttribute.SASPort->
				    NumberofDiscoveredPorts) {
					remote_avail++;
				}
				ret += (*processPort)(handle,
				    adpt_array[i].name,
				    port_array[portIndex].index, &port,
				    &attrs, input);
				/*
				 * We should reset the hbaPortExist flag
				 * here for next round of check and count
				 * for the machedHBAPorts.
				 */
				hbaPortExist = 0;
				matchedHBAPorts++;
			}
		}
		if (port_array) {
			free(port_array);
			port_array = NULL;
		}
		HBA_CloseAdapter(handle);
	}
	if (adpt_array) {
		free(adpt_array);
		adpt_array = NULL;
	}

	/*
	 * When we are here, we have traversed all the hba and hba ports.
	 */
	if (matchedHBAs == 0) {
		(void *) fprintf(stderr, "%s\n",
		    gettext("Error: Matching HBA not found."));
		if (input->wwn_flag) {
			free(input->wwn_flag);
			input->wwn_flag = NULL;
		}
		return (++ret);
	} else if (processPort == NULL) {
		/*
		 * processPort == NULL signifies hba subcommand.
		 * If enter here, it means we have at least one matching
		 * hba, we need to check if there are mismatching ones.
		 */
		for (i = 0; i < input->wwnCount; i++) {
			if (input->wwn_flag[i] == 0) {
				(void *) fprintf(stderr, "%s %s %s\n",
				    gettext("Error: HBA"),
				    input->wwn_argv[i],
				    gettext("not found."));
				ret++;
			}
		}
	} else {
		if (local_avail > 0 && matchedHBAPorts == 0) {
			(void *) fprintf(stderr, "%s\n",
			    gettext("Error: Matching HBA Port "
			    "not found."));
			if (input->wwn_flag) {
				free(input->wwn_flag);
				input->wwn_flag = NULL;
			}
			return (++ret);
		} else if (local_avail == 0) {
			(void *) fprintf(stderr, "%s\n",
			    gettext("Error: No HBA Port Configured."));
			if (input->wwn_flag) {
				free(input->wwn_flag);
				input->wwn_flag = NULL;
			}
			return (++ret);
		} else if (processPort == handleHBAPort) {
			/*
			 * If enter here, we have at least one HBA port
			 * matched. For hba-port subcommand, we shall check
			 * whether there are operands mismatching.
			 */
			for (i = 0; i < input->wwnCount; i++) {
				if (input->wwn_flag[i] == 0) {
					(void *) fprintf(stderr, "%s %s %s\n",
					    gettext("Error: HBA Port"),
					    input->wwn_argv[i],
					    gettext("not found."));
					ret++;
				}
			}
		}
	}

	/*
	 * For expander subcommand, we need to check if the
	 * specified sas address(ese) exist (none/partial/all).
	 */
	if (processPort == handleExpander) {
		if (input->wwnCount > 0) {
			sum = 0;
			for (i = 0; i < input->wwnCount; i++) {
				sum += input->wwn_flag[i];
			}
			/*
			 * If sum is zero, it means that for all the given
			 * operands matching count is zero. So none of the
			 * specified SAS address exist actually.
			 */
			if (sum == 0) {
				(void *) fprintf(stderr, gettext("Error: "
				    "Matching SAS Address not found.\n"));
				free(input->wwn_flag);
				input->wwn_flag = NULL;
				return (++ret);
			}

			/*
			 * If we get here, it means that some of the specified
			 * sas address exist, we will know through looping the
			 * wwn_flag array.
			 */
			for (i = 0; i < input->wwnCount; i++) {
				if (input->wwn_flag[i] == 0) {
					(void *) fprintf(stderr, "%s %s %s\n",
					    gettext("Error: SAS Address"),
					    input->wwn_argv[i],
					    gettext("not found."));
					ret++;
				}
			}
		}
		/* even if no remote port is found it is not an error. */
	}
	if (input->wwn_flag) {
		free(input->wwn_flag);
		input->wwn_flag = NULL;
	}
	return (ret);
}

/*
 * This function will handle the phy stuff for hba-port subcommand.
 *
 * Arguments:
 *      handle - handle to hba port.
 *      portIndex - the index of hba port currently being processed.
 *      port - pointer to hba port attributes.
 *      pflag - options user specified.
 *
 *  Return Value:
 *	    0		sucessfully processed handle
 *	    >0		error has occured
 */
static int
processHBAPortPhyInfo(HBA_HANDLE handle, HBA_UINT32 portIndex,
    SMHBA_PORTATTRIBUTES *port, int pflag)
{
	int 		phyIndex = 0, err_cnt = 0;
	HBA_UINT32	numphys = 0;
	HBA_STATUS	status = 0;
	SMHBA_SAS_PHY	phyattrs;

	if (port == NULL)
		return (++err_cnt);

	numphys = port->PortSpecificAttribute.SASPort->NumberofPhys;
	if (numphys == 0)
		return (0);

	if ((pflag & PRINT_PHY) || (pflag & PRINT_PHY_LINKSTAT))
		(void *) fprintf(stdout, "%s\n", "    Phy Information:");
	else
		return (0);


	for (phyIndex = 0; phyIndex < numphys; phyIndex++) {
		(void *) memset(&phyattrs, 0, sizeof (phyattrs));
		status = SMHBA_GetSASPhyAttributes(
		    handle, portIndex, phyIndex, &phyattrs);
		if (status != HBA_STATUS_OK) {
			(void *) fprintf(stderr, "%s %d %s %s\n",
			    gettext("Failed to get SAS Phy attributes"
			    "phyIndex"), phyIndex,
			    gettext("Reason:"),
			    getHBAStatus(status));
			err_cnt++;
			continue;
		}
		if (pflag & PRINT_PHY)
			printHBAPortPhyInfo(&phyattrs);
		if (pflag & PRINT_PHY_LINKSTAT)
			err_cnt += processHBAPortPhyStat(handle,
			    portIndex, phyIndex, &phyattrs, pflag);
	}
	return (err_cnt);
}

/*
 * This function will handle the phy stuff for hba-port subcommand.
 *
 * Arguments:
 *      handle - handle to hba port.
 *      portIndex - the index of hba port currently being processed.
 *      port - pointer to hba port attributes.
 *      pflag - options user specified.
 *
 *  Return Value:
 *	    0		sucessfully processed handle
 *	    >0		error has occured
 */
static int
processHBAPortPhyStat(HBA_HANDLE handle, HBA_UINT32 portIndex, int phyIndex,
    PSMHBA_SAS_PHY phyattrs, int pflag)
{
	HBA_STATUS		status = 0;
	SMHBA_PHYSTATISTICS	phystat;
	SMHBA_SASPHYSTATISTICS	sasphystat;

	if ((pflag & PRINT_PHY) == 0) {
		(void *) fprintf(stdout, "%s %d\n",
		    "      Identifier:", phyattrs->PhyIdentifier);
	}

	(void *) memset(&phystat, 0, sizeof (phystat));
	(void *) memset(&sasphystat, 0, sizeof (sasphystat));
	phystat.SASPhyStatistics = &sasphystat;
	status = SMHBA_GetPhyStatistics(handle, portIndex, phyIndex, &phystat);
	if (status != HBA_STATUS_OK) {
		(void *) fprintf(stdout, "%s\n",
		    "        Link Error Statistics:");
		(void *) fprintf(stderr, "%s\n",
		    gettext("            Failed to retrieve Link "
		    "Error Statistics!"));
		return (1);
	}
	printHBAPortPhyStatistics(phystat.SASPhyStatistics);
	return (0);
}

/*
 * Check whether the pWWN exist in the WWNs list which specified by user.
 *
 * Arguments:
 *	input - contains all the input parameters.
 *	pWWN - pointer to the hba port sas address.
 *
 *  Return Value:
 *	    1		true, the pWWN exist in the sas address list specified.
 *	    0		false.
 */
static int
isPortWWNInArgv(inputArg_t *input, PHBA_WWN pWWN)
{
	int 		port_wwn_counter = 0;
	int		portfound = 0;
	uint64_t	hbaWWN;

	/* list only ports given in wwn_argv */
	for (port_wwn_counter = 0;
	    port_wwn_counter < input->wwnCount;
	    port_wwn_counter++) {
		hbaWWN = strtoull(input->wwn_argv[port_wwn_counter], NULL,
		    16);
		if (hbaWWN == 0 && errno != 0)
			continue;
		if (wwnConversion(pWWN->wwn) == hbaWWN) {
			if (input->wwn_flag) {
				input->wwn_flag[port_wwn_counter]++;
			}
			portfound = 1;
		}
	}
	return (portfound);
}

/*
 * Check whether the string value exists in the input list,
 * which specified by user.
 *
 * Arguments:
 *	input - contains all the input parameters.
 *	stringName - could be hba adapter name
 *	                      hba-port name.
 *
 *  Return Value:
 *	    1		true, the HBA exists in the list specified.
 *	    0		false.
 */
static int
isStringInArgv(inputArg_t *input, const char *stringName)
{
	int 		counter = 0;
	int		found = 0;

	/* list only hba(s) given in wwn_argv */
	for (counter = 0;
	    counter < input->wwnCount;
	    counter++) {
		if (strcmp(input->wwn_argv[counter],
		    stringName) == 0) {
			if (input->wwn_flag)
				input->wwn_flag[counter]++;
			found = 1;
		}
	}
	return (found);
}

/*
 * Callback function for hba subcommand.
 *
 * Arguments:
 *      attrs - pointer to adapter attributes currently being processed.
 *	input - contains all the input parameters.
 *	numberOfPorts - number of ports of this HBA.
 *
 *  Return Value:
 *  	matching number
 */
static int handleHBA(SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input,
    int numberOfPorts, const char *adapterName)
{
	int matchingHBA = 1;

	if (input->wwnCount == 0) {
		printHBAInfo(attrs, input->pflag, numberOfPorts, adapterName);
	} else {
		if (isStringInArgv(input, adapterName)) {
			printHBAInfo(attrs,
			    input->pflag, numberOfPorts, adapterName);
		} else {
			matchingHBA = 0;
		}
	}

	return (matchingHBA);
}

/*
 * Callback function for hba-port subcommand.
 *
 * Arguments:
 *      handle - handle to hba port.
 *      portIndex - the index of hba port currently being processed.
 *      port - pointer to hba port attributes.
 *      attrs - pointer to adapter attributes currently being processed.
 *	input - contains all the input parameters.
 *
 *  Return Value:
 *	    0		sucessfully processed handle
 *	    >0		error has occured
 */
/*ARGSUSED*/
static int handleHBAPort(HBA_HANDLE handle, char *adapterName,
    HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
    SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input)
{
	int ret = 0;
	printHBAPortInfo(port, attrs, input->pflag);
	ret = processHBAPortPhyInfo(handle, portIndex, port, input->pflag);
	return (ret);
}

/*
 * Callback function for expander subcommand.
 *
 * Arguments:
 *      handle - handle to hba port.
 *      portIndex - the index of hba port currently being processed.
 *      port - pointer to hba port attributes.
 *      attrs - pointer to adapter attributes currently being processed.
 *	input - contains all the input parameters.
 *
 *  Return Value:
 *	    0		sucessfully processed handle
 *	    >0		error has occured
 */
/*ARGSUSED*/
static int handleExpander(HBA_HANDLE handle, char *adapterName,
    HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
    SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input)
{
	SMHBA_PORTATTRIBUTES	attr;
	SMHBA_SAS_PORT		sasport;
	HBA_STATUS		status;
	int			ret = 0;
	int			i, numberOfRP;
	rp_tree_t		*rpnode;
	rp_tree_t		*rproot = NULL;
	rp_tree_t		*unsolved_head = NULL;
	rp_tree_t		*unsolved_tail = NULL;
	rp_tree_t		*unsolved_sentinel = NULL;
	int			printPort = 0;
	int			numberOfEXP = 0;
	int			unsolved_inserted = 0;
	int			unsolved_left = 0;
	int			disco_port_fail = 0;
	boolean_t		firstPrinted = B_FALSE;

	(void *) memset(&attr, 0, sizeof (attr));
	(void *) memset(&sasport, 0, sizeof (sasport));
	attr.PortSpecificAttribute.SASPort = &sasport;

	/*
	 * Retrive all expander device from this hba port first.
	 */
	if ((numberOfRP = port->PortSpecificAttribute.SASPort->
	    NumberofDiscoveredPorts) == 0) {
		/* no remote port. just return 0. */
		return (ret);
	}

	for (i = 0; i < numberOfRP; i++) {
		rpnode = calloc(1, sizeof (rp_tree_t));
		rpnode->portattr.PortSpecificAttribute.SASPort =
		    &rpnode->sasattr;
		status = SMHBA_GetDiscoveredPortAttributes(handle,
		    portIndex, i, &rpnode->portattr);
		if (status != HBA_STATUS_OK) {
			disco_port_fail++;
			free(rpnode);
			ret++;
			continue;
		}

		if (rpnode->portattr.PortType == HBA_PORTTYPE_SASEXPANDER) {
			numberOfEXP++;
		}
		/*
		 * We will try to insert this expander device and target
		 * ports into the topology tree. If we failed, we can chain
		 * them together and try again when we have all the
		 * discovered port information in hands.
		 */
		if (rproot == NULL && memcmp(port->
		    PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
		    rpnode->sasattr.AttachedSASAddress.wwn,
		    sizeof (HBA_WWN)) == 0) {
			/*
			 * The root node of tree should
			 * be set up first.
			 */
			rproot = rpnode;
		} else {
			/*
			 * If we can not set up the root node of
			 * the tree or we failed to insert
			 * the disocvered port node, queue it up then.
			 */
			if (rproot == NULL ||
			    sas_rp_tree_insert(&rproot, rpnode) != 0) {
				if (unsolved_head == NULL) {
					unsolved_head = rpnode;
					unsolved_tail = rpnode;
				} else {
					rpnode->sibling = unsolved_head;
					unsolved_head = rpnode;
				}
			}
		}
	}

	if (disco_port_fail) {
		(void *) fprintf(stderr, "%s %d %s %s\n",
		    gettext("Error: Failed to get attributes for"),
		    disco_port_fail,
		    gettext("connected ports of HBA port"),
		    port->OSDeviceName);
	}

	/* no expander found.  No need further processing. */
	if (numberOfEXP == 0) {
		while (unsolved_head) {
			unsolved_tail =
			    unsolved_head->sibling;
			free(unsolved_head);
			unsolved_head = unsolved_tail;
		}
		if (rproot) sas_rp_tree_free(rproot);
		return (ret);
	}

	/*
	 * When we're here, we should already have all information,
	 * now we try again to insert them into the topology tree.
	 * unsolved_head is the pointer which point to the head of
	 * unsolved rpnode linked list.
	 * unsolved_tail is the pointer which point to the tail of
	 * unsolved rpnode linked list.
	 * unsolved_sentinel is for insertion failure detection.
	 * When we're trying to insert the rpnodes from unsolved
	 * linked list, it may happen that some of the rpnodes can
	 * not be inserted no matter how many times we loop through
	 * this linked list. So we use unsolved_sentinel to identify
	 * the tail of last round of scanning, and unsolved_inserted
	 * which is a counter will be used to count how many rpnodes
	 * have been inserted from last round, if it is zero, which
	 * means that we can not insert rpnodes into rptree any more,
	 * and we should stop and deallocate the memory they occupied.
	 */
	unsolved_sentinel = unsolved_tail;
	while (unsolved_head) {
		rpnode = unsolved_head;
		unsolved_head = unsolved_head->sibling;
		if (unsolved_head == NULL)
			unsolved_tail = NULL;
		rpnode->sibling = NULL;
		if (sas_rp_tree_insert(&rproot, rpnode) != 0) {
			unsolved_tail->sibling = rpnode;
			unsolved_tail = rpnode;
			if (rpnode == unsolved_sentinel) {
				/*
				 * We just scanned one round for the
				 * unsolved list. Check to see whether we
				 * have nodes inserted, if none, we should
				 * break in case of an indefinite loop.
				 */
				if (unsolved_inserted == 0) {
					/*
					 * Indicate there is unhandled node.
					 * Chain free the whole unsolved
					 * list here.
					 */
					unsolved_left++;
					break;
				} else {
					unsolved_inserted = 0;
					unsolved_sentinel = unsolved_tail;
				}
			}
		} else {
			/*
			 * We just inserted one rpnode, increment the
			 * unsolved_inserted counter. We will utilize this
			 * counter to detect an indefinite insertion loop.
			 */
			unsolved_inserted++;
		}
	}

	/* check if there is left out discovered ports. */
	if (unsolved_left) {
		ret++;
		(void *) fprintf(stderr, "%s %s\n",
		    gettext("Error: Failed to establish expander topology on"),
		    port->OSDeviceName);
		(void *) fprintf(stderr, "%s\n",
		    gettext("       Folowing port(s) are unresolved."));
		while (unsolved_head) {
			unsolved_tail =
			    unsolved_head->sibling;
			(void *) fprintf(stderr, "%s%016llx ",
			    firstPrinted ? "" : "\t",
			    wwnConversion(unsolved_head->sasattr.
			    LocalSASAddress.wwn));
			if (firstPrinted == B_FALSE) firstPrinted = B_TRUE;
			free(unsolved_head);
			unsolved_head = unsolved_tail;
		}
		(void *) fprintf(stderr, "\n");
		/* still print what we have */
		ret += sas_rp_tree_print(handle, adapterName, portIndex,
		    port, rproot, input, 2 * TABLEN, &printPort);
	} else {
		ret += sas_rp_tree_print(handle, adapterName, portIndex,
		    port, rproot, input, 2 * TABLEN, &printPort);
	}

	if (rproot) sas_rp_tree_free(rproot);

	return (ret);
}

/*
 * Callback function for target-port subcommand.
 *
 * Arguments:
 *      handle - handle to hba port.
 *      portIndex - the index of hba port currently being processed.
 *      port - pointer to hba port attributes.
 *      attrs - pointer to adapter attributes currently being processed.
 *	input - contains all the input parameters.
 *
 *  Return Value:
 *	    0		sucessfully processed handle
 *	    >0		error has occured
 */
/*ARGSUSED*/
static int handleTargetPort(HBA_HANDLE handle, char *adapterName,
    HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
    SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input)
{
	HBA_STATUS		status;
	SMHBA_PORTATTRIBUTES	targetattr;
	SMHBA_SAS_PORT		sasattr;
	int			i;
	int			ret = 0;
	int			disco_port_fail = 0;

	targetattr.PortSpecificAttribute.SASPort = &sasattr;

	for (i = 0; i < port->PortSpecificAttribute.SASPort->
	    NumberofDiscoveredPorts; i++) {
		status = SMHBA_GetDiscoveredPortAttributes(handle,
		    portIndex, i, &targetattr);
		if (status != HBA_STATUS_OK) {
			disco_port_fail++;
		} else {
			/* skip expander device */
			if (targetattr.PortType != HBA_PORTTYPE_SASEXPANDER) {
				ret += searchTargetPort(handle, portIndex, port,
				    &targetattr, &sasattr, input->pflag);
			}
		}
	}

	if (disco_port_fail) {
		ret++;
		(void *) fprintf(stderr, "%s %d %s %s\n",
		    gettext("Error: Failed to get attributes for"),
		    disco_port_fail,
		    gettext("connected ports of HBA port"),
		    port->OSDeviceName);
	}
	return (ret);
}

/*
 * ****************************************************************************
 *
 * compareLUName -
 * 	compare names directly and also check if disk namees match with
 *	different slice number or /devices path are speicified and matches.
 *
 * cmdArg	- first string to compare
 * osName	- os name from attributes
 *
 * returns 	B_TRUE if the strings match either directly or via devid
 *		B_FALSE otherwise
 *
 * ****************************************************************************
 */
static boolean_t
compareLUName(char *cmdArg, char *osName)
{

	boolean_t	isSame = B_FALSE;
	char		dev1[MAXPATHLEN], dev2[MAXPATHLEN];
	char		*ch1, *ch2;

	if (strcmp(cmdArg, osName) == 0) {
		isSame = B_TRUE;
	} else {
		/* user input didn't match, try to  match the core of args. */
		(void) strlcpy(dev1, cmdArg, MAXPATHLEN);
		(void) strlcpy(dev2, osName, MAXPATHLEN);
		/* is this /devices path */
		if (((ch1 = strrchr(dev1, ',')) != NULL) &&
		    ((ch2 = strrchr(dev2, ',')) != NULL)) {
			*ch1 = *ch2 = '\0';
			if (strcmp(dev1, dev2) == 0) {
				isSame = B_TRUE;
			}
		/* is this a /dev link */
		} else if ((strncmp(dev1, "/dev/", 5) == 0) &&
		    (strncmp(dev2, "/dev/", 5) == 0)) {
			if ((strstr(dev1, "dsk") != NULL) &&
			    ((strstr(dev2, "dsk") != NULL))) {
				/* if it is disk link */
				if (((ch1 = strrchr(dev1, 's')) != NULL) &&
				    ((ch2 = strrchr(dev2, 's')) != NULL)) {
					*ch1 = *ch2 = '\0';
					if (strcmp(dev1, dev2) == 0) {
						isSame = B_TRUE;
					}
				}
			} else {
				/* other dev links */
				if (strcmp(dev1, dev2) == 0) {
					isSame = B_TRUE;
				}
			}
		}
	} /* compare */

	return (isSame);
}

/*
 * Process logical-unit(lu) subcommand.
 *
 * Arguments:
 *      luCount - number of OS device name(s) specified by user.
 *      luArgv - array of OS device name(s) specified by user.
 *      options - all the options specified by user.
 *
 *  Return Value:
 *	    0		sucessfully processed handle
 *	    >0		error has occured
 */
int
sas_util_list_logicalunit(int luCount, char **luArgv, cmdOptions_t *options)
{
	HBA_STATUS		status;
	int			processHBA_flags = 0;
	int			lu;
	boolean_t		pathFound;
	boolean_t		verbose;
	inputArg_t		input;
	discoveredDevice	*LUListWalk = NULL;
	int			err_cnt = 0;

	for (; options->optval; options++) {
		if (options->optval == 'v') {
			processHBA_flags |= PRINT_VERBOSE;
		}
	}

	/* HBA_LoadLibrary() */
	if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
		(void *) fprintf(stderr, "%s %s\n",
		    gettext("Failed to load SM-HBA libraries."
		    "Reason:"), getHBAStatus(status));
		err_cnt++;
		return (err_cnt);
	}

	(void *) memset(&input, 0, sizeof (input));
	input.pflag = processHBA_flags;
	input.wwnCount = luCount;
	input.wwn_argv = luArgv;

	err_cnt += processHBA(&input, handleLogicalUnit);
	verbose = (input.pflag & PRINT_VERBOSE) ? B_TRUE : B_FALSE;

	if (luCount == 0) {
		/* list all paths */
		for (LUListWalk = LUList; LUListWalk != NULL;
		    LUListWalk = LUListWalk->next) {
			err_cnt += printOSDeviceNameInfo(LUListWalk, verbose);
		}
	} else {
		/*
		 * When operands provided, we should set the error code
		 * only if there are issues related with the operands.
		 */
		err_cnt = 0;
		/*
		 * list any paths not found first
		 * this gives the user cleaner output
		 */
		for (lu = 0; lu < luCount; lu++) {
			for (LUListWalk = LUList, pathFound = B_FALSE;
			    LUListWalk != NULL;
			    LUListWalk = LUListWalk->next) {
				if (compareLUName(luArgv[lu],
				    LUListWalk->OSDeviceName)) {
					pathFound = B_TRUE;
					break;
				}
			}
			if (pathFound == B_FALSE) {
				(void *) fprintf(stderr,
				    "Error: Logical Unit %s Not Found \n",
				    luArgv[lu]);
				err_cnt++;
			}
		}
		/* list all paths requested in order requested */
		for (lu = 0; lu < luCount; lu++) {
			for (LUListWalk = LUList; LUListWalk != NULL;
			    LUListWalk = LUListWalk->next) {
				if (compareLUName(luArgv[lu],
				    LUListWalk->OSDeviceName)) {
					err_cnt += printOSDeviceNameInfo(
					    LUListWalk,
					    verbose);
				}
			}
		}
	}
	(void) HBA_FreeLibrary();
	return (err_cnt);
}

/*
 * Callback function for logical-unit(lu) subcommand.
 *
 * Arguments:
 *      handle - handle to hba port.
 *      portIndex - the index of hba port currently being processed.
 *      port - pointer to hba port attributes.
 *      attrs - pointer to adapter attributes currently being processed.
 *	input - contains all the input parameters.
 *
 *  Return Value:
 *	    0		sucessfully processed handle
 *	    >0		error has occured
 */
/*ARGSUSED*/
static int handleLogicalUnit(HBA_HANDLE handle, char *adapterName,
    HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
    SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input)
{
	HBA_STATUS		status;
	SMHBA_TARGETMAPPING	*map;
	HBA_WWN			hbaPortWWN, domainPortWWN;
	char			*portName = NULL;
	int			numentries;
	int			count = 0;
	int			ret = 0;

	hbaPortWWN = port->PortSpecificAttribute.SASPort->LocalSASAddress;
	portName = port->OSDeviceName;

	status = get_domainPort(handle, portIndex, port, &domainPortWWN);
	switch (status) {
		case HBA_STATUS_OK:
			break;
		case HBA_STATUS_ERROR_NOT_SUPPORTED:
			/* don't increase error flag for no phy configuration */
			return (ret);
		case HBA_STATUS_ERROR:
		default:
			return (++ret);
	}

	if ((map = calloc(1, sizeof (*map))) == NULL) {
		(void *) fprintf(stderr, "%s\n",
		    gettext("No enough memory on heap."));
		return (++ret);
	}
	map->NumberOfEntries = 1;

	/*
	 * First, we need to get the target mapping data from this hba
	 * port.
	 */
	status = SMHBA_GetTargetMapping(handle,
	    hbaPortWWN, domainPortWWN, map);

	if (status == HBA_STATUS_ERROR_MORE_DATA) {
		numentries = map->NumberOfEntries;
		free(map);
		map = calloc(1, sizeof (HBA_UINT32) +
		    (numentries * sizeof (SMHBA_SCSIENTRY)));
		if (map == NULL) {
			(void *) fprintf(stderr, "%s\n",
			    gettext("No enough memory on heap."));
			return (++ret);
		}
		map->NumberOfEntries = numentries;
		status = SMHBA_GetTargetMapping(handle,
		    hbaPortWWN, domainPortWWN, map);
	}

	if (status != HBA_STATUS_OK) {
		(void *) fprintf(stderr, "%s %016llx %s %s\n",
		    gettext("Error: Failed to get SCSI mapping data for "
		    "the HBA port"), wwnConversion(hbaPortWWN.wwn),
		    gettext("Reason:"),
		    getHBAStatus(status));
		free(map);
		return (++ret);
	}

	/*
	 * By iterating each entry of the targetmapping data, we will
	 * construct a global list of logical unit.
	 */
	for (count = 0; count < map->NumberOfEntries; count++) {
		ret += searchDevice(
		    &(map->entry[count]), handle, hbaPortWWN, domainPortWWN,
		    portName, input->pflag);
	}
	free(map);
	return (ret);
}

/*
 * Search the matching targetmapping data for given target port and SAM LUN
 *	and return target mapping data if found.
 *
 * Arguments:
 *      handle - handle to hba port.
 *      portIndex - hba port index
 *      port - hba port attributes.
 *      targetportWWN - target port SAS address.
 *      domainportWWN - domain port SAS address.
 *      domainportttr - target port SAS attributes.
 *      samLUN - samLUN from report LUNs data.
 *      data - matching target mapping data.
 *
 *  Return Value:
 *	    0		sucessfully processed handle
 *	    >0		error has occured
 */
static int
searchTargetPortMappingData(HBA_HANDLE handle, HBA_UINT32 portIndex,
    SMHBA_PORTATTRIBUTES *port, SMHBA_SAS_PORT *sasattr,
    struct targetPortConfig *configData)
{
	int			ret = 0;
	HBA_STATUS		status;
	SMHBA_TARGETMAPPING	*map = NULL;
	HBA_WWN			hbaPortWWN, domainPortWWN;
	int			numentries, count;
	targetPortMappingData_t	*TPMapData;
	struct scsi_inquiry	inq;
	struct scsi_extended_sense  sense;
	HBA_UINT32		responseSize, senseSize = 0;
	uchar_t			rawLUNs[DEFAULT_LUN_LENGTH], *lun_string;
	HBA_UINT8		scsiStatus;
	rep_luns_rsp_t		*lun_resp;
	int			lunNum, numberOfLun, lunCount;
	uint32_t		lunlength, tmp_lunlength;
	uint64_t		sasLUN;
	SMHBA_SCSILUN		smhbaLUN;

	hbaPortWWN = port->PortSpecificAttribute.SASPort->
	    LocalSASAddress;

	status = get_domainPort(handle, portIndex, port, &domainPortWWN);
	if (status == HBA_STATUS_OK) {
		if ((map = calloc(1, sizeof (*map))) == NULL) {
			(void *) fprintf(stderr, "%s\n",
			gettext("No enough memory on heap."));
			return (++ret);
		}
		map->NumberOfEntries = 1;

		status = SMHBA_GetTargetMapping(handle, hbaPortWWN,
		    domainPortWWN, map);

		if (status == HBA_STATUS_ERROR_MORE_DATA) {
			numentries = map->NumberOfEntries;
			free(map);
			map = calloc(1, sizeof (HBA_UINT32) +
			    (numentries * sizeof (SMHBA_SCSIENTRY)));
			if (map == NULL) {
				(void *) fprintf(stderr, "%s\n",
				    gettext("No enough memory on heap."));
				return (++ret);
			}
			map->NumberOfEntries = numentries;
			status = SMHBA_GetTargetMapping(handle,
			    hbaPortWWN, domainPortWWN, map);
		}

		if (status != HBA_STATUS_OK) {
			/* continue to build mapping data based SCSI info */
			ret++;
			free(map);
			map = NULL;
		}
	}

	/*
	 * Get report lun data.
	 */
	responseSize = DEFAULT_LUN_LENGTH;
	senseSize = sizeof (struct scsi_extended_sense);
	(void) memset(&sense, 0, sizeof (sense));
	status = SMHBA_ScsiReportLUNs(
	    handle,
	    hbaPortWWN,
	    sasattr->LocalSASAddress,
	    domainPortWWN,
	    (void *)rawLUNs,
	    &responseSize,
	    &scsiStatus,
	    (void *) &sense, &senseSize);

	/*
	 * if HBA_STATUS_ERROR_NOT_A_TARGET is return, we can assume this is
	 * a remote HBA and move on
	 */
	if (status != HBA_STATUS_OK) {
		configData->reportLUNsFailed = B_TRUE;
		if (map != NULL) {
			/*
			 * Let's search mapping data and indicate that Report
			 * LUNs failed.
			 */
			for (count = 0; count < map->NumberOfEntries; count++) {
				if (memcmp(map->entry[count].PortLun.
				    PortWWN.wwn, sasattr->LocalSASAddress.wwn,
				    sizeof (HBA_WWN)) == 0) {
					/* allocate mapping data for each LUN */
					TPMapData = calloc(1,
					    sizeof (targetPortMappingData_t));
					if (TPMapData == NULL) {
						(void *) fprintf(stderr, "%s\n",
						    gettext("No enough "
						    "memory."));
						free(map);
						return (++ret);
					}
					TPMapData->mappingExist = B_TRUE;
					TPMapData->osLUN =
					    map->entry[count].ScsiId.ScsiOSLun;
					(void) strlcpy(TPMapData->osDeviceName,
					    map->entry[count].ScsiId.
					    OSDeviceName,
					    sizeof (TPMapData->osDeviceName));
					TPMapData->inq_vid[0] = '\0';
					TPMapData->inq_pid[0] = '\0';
					TPMapData->inq_dtype = DTYPE_UNKNOWN;
					if (configData->map == NULL) {
						configData->map = TPMapData;
					} else {
						TPMapData->next =
						    configData->map->next;
						configData->map = TPMapData;
					}
				}
			}
		}
		(void) free(map);
		return (++ret);
	}
	lun_resp = (rep_luns_rsp_t *)((void *)rawLUNs);
	(void) memcpy(&tmp_lunlength, &(lun_resp->length),
	    sizeof (tmp_lunlength));
	lunlength = ntohl(tmp_lunlength);
	(void) memcpy(&numberOfLun, &lunlength, sizeof (numberOfLun));
	for (lunCount = 0; lunCount < (numberOfLun / 8); lunCount++) {
		/* allocate mapping data for each LUN */
		TPMapData = calloc(1,
		    sizeof (targetPortMappingData_t));
		if (TPMapData == NULL) {
			(void *) fprintf(stderr, "%s\n",
			    gettext("No enough memory."));
			free(map);
			return (++ret);
		}

		(void) memcpy(&TPMapData->reportLUN, lun_resp->
		    lun[lunCount].val, sizeof (SMHBA_SCSILUN));

		/*
		 * now issue standard inquiry to get Vendor
		 * and product information
		 */
		responseSize = sizeof (struct scsi_inquiry);
		senseSize = sizeof (struct scsi_extended_sense);
		(void) memset(&inq, 0, sizeof (struct scsi_inquiry));
		(void) memset(&sense, 0, sizeof (sense));
		sasLUN = ntohll(wwnConversion(lun_resp->lun[lunCount].val));
		(void) memcpy(&smhbaLUN, &sasLUN, sizeof (SMHBA_SCSILUN));
		status = SMHBA_ScsiInquiry(
		    handle,
		    hbaPortWWN,
		    sasattr->LocalSASAddress,
		    domainPortWWN,
		    smhbaLUN,
		    0,
		    0,
		    (void *) &inq, &responseSize,
		    &scsiStatus,
		    (void *) &sense, &senseSize);
		if (status != HBA_STATUS_OK) {
			TPMapData->inq_vid[0] = '\0';
			TPMapData->inq_pid[0] = '\0';
			TPMapData->inq_dtype = DTYPE_UNKNOWN;
			/* indicate that inquiry for this lun is failed */
			TPMapData->inquiryFailed = B_TRUE;
		} else {
			(void *) memcpy(TPMapData->inq_vid, inq.inq_vid,
			    sizeof (TPMapData->inq_vid));
			(void *) memcpy(TPMapData->inq_pid, inq.inq_pid,
			    sizeof (TPMapData->inq_pid));
			TPMapData->inq_dtype = inq.inq_dtype;
		}

		if (map != NULL) {
			for (count = 0; count < map->NumberOfEntries; count++) {
				if ((memcmp(map->entry[count].PortLun.
				    PortWWN.wwn, sasattr->LocalSASAddress.wwn,
				    sizeof (HBA_WWN)) == 0) &&
				    (memcmp(&(map->entry[count].PortLun.
				    TargetLun), &smhbaLUN,
				    sizeof (SMHBA_SCSILUN))
				    == 0)) {
					TPMapData->mappingExist = B_TRUE;
					TPMapData->osLUN =
					    map->entry[count].ScsiId.ScsiOSLun;
					(void) strlcpy(TPMapData->osDeviceName,
					    map->entry[count].ScsiId.
					    OSDeviceName,
					    sizeof (TPMapData->osDeviceName));
					break;
				}
			}
			if (count == map->NumberOfEntries) {
				TPMapData->osDeviceName[0] = '\0';
				lun_string = lun_resp->lun[lunCount].val;
				lunNum = ((lun_string[0] & 0x3F) << 8) |
				    lun_string[1];
				TPMapData->osLUN = lunNum;
			}
		} else {
		/* Not able to get any target mapping information */
			TPMapData->osDeviceName[0] = '\0';
			lun_string = lun_resp->lun[lunCount].val;
			lunNum = ((lun_string[0] & 0x3F) << 8) |
			    lun_string[1];
			TPMapData->osLUN = lunNum;
		}

		if (configData->map == NULL) {
			configData->map = TPMapData;
		} else {
			TPMapData->next = configData->map->next;
			configData->map = TPMapData;
		}
	}
	free(map);
	return (ret);
}

/*
 * Search the discovered LUs and construct the global LU list.
 *
 * Arguments:
 *      handle - handle to hba port.
 *      portIndex - hba port index
 *      port - hba port attributes.
 *      targetattr - target port attributes.
 *      sasattr - target port SAS attributes.
 *      pflag - options the user specified.
 *
 *  Return Value:
 *	    0		sucessfully processed handle
 *	    >0		error has occured
 */
static int
searchTargetPort(HBA_HANDLE handle, HBA_UINT32 portIndex,
    SMHBA_PORTATTRIBUTES *port, SMHBA_PORTATTRIBUTES *targetattr,
    SMHBA_SAS_PORT *sasattr, int pflag)
{
	int			ret = 0;
	HBA_WWN			expander;
	HBA_WWN			domainPortWWN;
	targetPortList_t 	*discoveredTP, *newTP;
	targetPortConfig_t	*TPConfig, *newConfig, *prevConfig;
	boolean_t		foundTP = B_FALSE;
	boolean_t		foundConfig = B_FALSE;
	int			status;
	SMHBA_PORTATTRIBUTES	tgtattr;
	SMHBA_SAS_PORT		tgtsasport;
	int			expanderValid = 0;

	status = get_domainPort(handle, portIndex, port, &domainPortWWN);
	switch (status) {
		case HBA_STATUS_OK:
			break;
		case HBA_STATUS_ERROR_NOT_SUPPORTED:
			/* don't increase error flag for no phy configuration */
			return (ret);
		case HBA_STATUS_ERROR:
		default:
			return (++ret);
	}

	/*
	 * First, we will iterate the already constructed target port
	 * list to see whether there is a target port already exist with
	 * matching target port SAS address.
	 */
	for (discoveredTP = gTargetPortList; discoveredTP != NULL;
	    discoveredTP = discoveredTP->next) {
		if (memcmp((void *)sasattr->LocalSASAddress.wwn,
		    (void *)discoveredTP->sasattr.LocalSASAddress.wwn,
		    sizeof (HBA_WWN)) == 0) {
			/*
			 * if the target port exist and
			 * verbose is not set, just return
			 */
			if (((pflag & PRINT_VERBOSE) == 0) &&
			    ((pflag & PRINT_TARGET_SCSI) == 0)) {
				return (ret);
			}
			foundTP = B_TRUE;
			break;
		}
	}

	if (foundTP == B_TRUE) {
		/*
		 * If there is a target port already exist, we should
		 * add more information on the target port to construct the
		 * whole topology.
		 * Here we will check whether the current hba port name
		 * has already been added.
		 */
		/* first get the expander SAS address compare */
		if (memcmp((void *)port->PortSpecificAttribute.SASPort->
		    LocalSASAddress.wwn, (void *)sasattr->
		    AttachedSASAddress.wwn, sizeof (HBA_WWN)) == 0) {
			/* NO expander */
			(void) memset((void *)expander.wwn, 0,
			    sizeof (HBA_WWN));
			expanderValid = 1;
		} else {
			if (wwnConversion(sasattr->AttachedSASAddress.wwn)
			    != 0) {
				/* expander exist.  We should verify it.  */
				(void) memcpy((void *)expander.wwn,
				    (void *)sasattr->AttachedSASAddress.wwn,
				    sizeof (HBA_WWN));

				(void *) memset(&tgtattr, 0, sizeof (tgtattr));
				(void *) memset(&tgtsasport, 0,
				    sizeof (tgtsasport));
				tgtattr.PortSpecificAttribute.SASPort
				    = &tgtsasport;
				status = SMHBA_GetPortAttributesByWWN(handle,
				    sasattr->AttachedSASAddress, domainPortWWN,
				    &tgtattr);
				if (status == HBA_STATUS_OK && tgtattr.PortType
				    == HBA_PORTTYPE_SASEXPANDER) {
					expanderValid = 1;
				}
			}
		}

		for (TPConfig = discoveredTP->configEntry,
		    foundConfig = B_FALSE; TPConfig != NULL;
		    TPConfig = TPConfig->next) {
			if ((strcmp(TPConfig->hbaPortName,
			    port->OSDeviceName) == 0) &&
			    (memcmp((void *)expander.wwn, (void *)TPConfig->
			    expanderSASAddr.wwn,
			    sizeof (HBA_WWN)) == 0)) {
				foundConfig = B_TRUE;
				break;
			}
		}

		/*
		 * If we get here, it means that it is a new hba port/exapnder
		 * sas address for this discovered target port.
		 */
		if (foundConfig == B_FALSE) {
			newConfig = (targetPortConfig_t *)calloc(1,
			    sizeof (targetPortConfig_t));
			if (newConfig == NULL) {
				(void *) fprintf(stderr,
				    "%s\n", strerror(errno));
				return (++ret);
			}

			(void) strlcpy(newConfig->hbaPortName, port->
			    OSDeviceName, sizeof (newConfig->hbaPortName));
			(void) memcpy((void *)newConfig->expanderSASAddr.wwn,
			    (void *)expander.wwn, sizeof (HBA_WWN));
			newConfig->expanderValid = expanderValid;
			if (discoveredTP->configEntry == NULL) {
				discoveredTP->configEntry = newConfig;
			} else {
				TPConfig = discoveredTP->configEntry;
				prevConfig = TPConfig;
				while (TPConfig != NULL &&
				    sas_name_comp(newConfig->hbaPortName,
				    TPConfig->hbaPortName) > 0) {
					prevConfig = TPConfig;
					TPConfig = TPConfig->next;
				}
				if (TPConfig == prevConfig) {
					/* Should be inserted in the head. */
					newConfig->next = TPConfig;
					discoveredTP->configEntry = newConfig;
				} else {
					newConfig->next = TPConfig;
					prevConfig->next = newConfig;
				}
			}
			/* if scsi option is not set return */
			if ((pflag & PRINT_TARGET_SCSI) == 0) {
				return (0);
			} else {
				return (searchTargetPortMappingData(
				    handle, portIndex, port,
				    sasattr, newConfig));
			}
		}
	} else {
		/*
		 * Here we got a new target port which has not ever exist
		 * in our global target port list. So add it to the list.
		 * list.
		 */
		newTP = (targetPortList_t *)calloc(1,
		    sizeof (targetPortList_t));

		if (newTP == NULL) {
			(void *) fprintf(stderr, "%s\n", strerror(errno));
			return (++ret);
		}

		(void) memcpy((void *)&newTP->targetattr, (void *)targetattr,
		    sizeof (SMHBA_PORTATTRIBUTES));
		(void) memcpy((void *)&newTP->sasattr, (void *)sasattr,
		    sizeof (SMHBA_SAS_PORT));

		newConfig = (targetPortConfig_t *)calloc(1,
		    sizeof (targetPortConfig_t));

		if (newConfig == NULL) {
			(void *) fprintf(stderr, "%s\n", strerror(errno));
			free(newTP);
			return (++ret);
		}

		(void) strlcpy(newConfig->hbaPortName, port->OSDeviceName,
		    sizeof (newConfig->hbaPortName));
		if (memcmp((void *)port->PortSpecificAttribute.SASPort->
		    LocalSASAddress.wwn, (void *)sasattr->
		    AttachedSASAddress.wwn, sizeof (HBA_WWN)) == 0) {
			/* NO expander */
			(void) memset((void *)newConfig->expanderSASAddr.wwn,
			    0, sizeof (HBA_WWN));
		} else {
			/* expander exist.  We should verify it. */
			(void) memcpy((void *)newConfig->expanderSASAddr.wwn,
			    (void *)sasattr->AttachedSASAddress.wwn,
			    sizeof (HBA_WWN));

			(void *) memset(&tgtattr, 0, sizeof (tgtattr));
			(void *) memset(&tgtsasport, 0, sizeof (tgtsasport));
			tgtattr.PortSpecificAttribute.SASPort = &tgtsasport;
			status = SMHBA_GetPortAttributesByWWN(handle,
			    sasattr->AttachedSASAddress, domainPortWWN,
			    &tgtattr);
			if (status == HBA_STATUS_OK && tgtattr.PortType ==
			    HBA_PORTTYPE_SASEXPANDER) {
				expanderValid = 1;
			}
			newConfig->expanderValid = expanderValid;
		}

		newTP->configEntry = newConfig;

		newTP->next = gTargetPortList; /* insert at head */
		gTargetPortList = newTP; /* set new head */

		/* if scsi option is not set return */
		if ((pflag & PRINT_TARGET_SCSI) == 0) {
			return (0);
		} else {
			return (searchTargetPortMappingData(
			    handle, portIndex, port, sasattr, newConfig));
		}
	}
	return (ret);
}

/*
 * Search the discovered LUs and construct the global LU list.
 *
 * Arguments:
 *      entryP - one of the target mapping data.
 *      handle - handle to hba port.
 *      hbaPortWWN - hba port sas address.
 *      domainPortWWN - domain port WWN for this sas domain.
 *      portName - HBA port OS Device Name.
 *      pflag - options the user specified.
 *
 *  Return Value:
 *	    0		sucessfully processed handle
 *	    >0		error has occured
 */
static int
searchDevice(PSMHBA_SCSIENTRY entryP,
    HBA_HANDLE handle, HBA_WWN hbaPortWWN, HBA_WWN domainPortWWN,
    char *portName, int pflag)
{
	HBA_STATUS		status;
	int			ret = 0;
	discoveredDevice 	*discoveredLU, *newDevice;
	portList		*portElem, *newPort, *prevElem;
	tgtPortWWNList 		*newTgtWWN, *TgtWWNList;
	boolean_t		foundDevice = B_FALSE;
	boolean_t		foundPort = B_FALSE;
	struct scsi_inquiry	inq;
	HBA_UINT32		responseSize, senseSize = 0;
	HBA_UINT8		inq_status;
	SMHBA_SCSILUN		smhbaLUN;
	struct scsi_extended_sense sense;

	/* if OSDeviceName is not set, we don't need to search */
	if (entryP->ScsiId.OSDeviceName[0] == '\0') {
		return (ret);
	}

	/*
	 * First, we will iterate the already constructed discovered LU
	 * list to see whether there is a LU already exist with the same OS
	 * device name as current target mapping data entry.
	 */
	for (discoveredLU = LUList; discoveredLU != NULL;
	    discoveredLU = discoveredLU->next) {
		if (strcmp(entryP->ScsiId.OSDeviceName,
		    discoveredLU->OSDeviceName) == 0) {
			/*
			 * if there is existing OS Device Name and
			 * verbose is not set, just return
			 */
			if ((pflag & PRINT_VERBOSE) == 0) {
				return (ret);
			}
			foundDevice = B_TRUE;
			break;
		}
	}

	if (foundDevice == B_TRUE) {
		/*
		 * If there is a discovered LU already exist, we should
		 * add more information on this LU to construct the whole
		 * topology.
		 * Here we will check whether the current hba port has
		 * already been added.
		 */
		for (portElem = discoveredLU->HBAPortList,
		    foundPort = B_FALSE;  portElem != NULL;
		    portElem = portElem->next) {
			if (strcmp(portElem->portName,
			    portName) == 0) {
				foundPort = B_TRUE;
				break;
			}
		}

		/*
		 * If we get here, it means that it is a new hba port name
		 * for this discovered LU.
		 */
		if (foundPort == B_FALSE) {
			newPort = (portList *)calloc(1, sizeof (portList));
			if (newPort == NULL) {
				(void *) fprintf(stderr,
				    "%s\n", strerror(errno));
				return (++ret);
			}
			(void) strlcpy(newPort->portName, portName,
			    sizeof (newPort->portName));

			portElem = discoveredLU->HBAPortList;
			prevElem = portElem;
			while (portElem != NULL &&
			    sas_name_comp(newPort->portName, portElem->portName)
			    > 0) {
				prevElem = portElem;
				portElem = portElem->next;
			}
			if (portElem == prevElem) {
				/* Insert in the head of list. */
				newPort->next = portElem;
				discoveredLU->HBAPortList = newPort;
			} else {
				newPort->next = portElem;
				prevElem->next = newPort;
			}
			/* add Target Port */
			newPort->tgtPortWWN = (tgtPortWWNList *)calloc(1,
			    sizeof (tgtPortWWNList));
			if (newPort->tgtPortWWN == NULL) {
				(void *) fprintf(stderr,
				    "%s\n", strerror(errno));
				return (++ret);
			}
			(void *) memcpy((void *)&(newPort->tgtPortWWN->portWWN),
			    (void *)&(entryP->PortLun.PortWWN),
			    sizeof (HBA_WWN));
			/* Set LUN data */
			newPort->tgtPortWWN->scsiOSLun =
			    entryP->ScsiId.ScsiOSLun;
		} else {
			/*
			 * Otherwise, we just need to add the target port
			 * sas address information.
			 */
			for (TgtWWNList = portElem->tgtPortWWN;
			    TgtWWNList != NULL;
			    TgtWWNList = TgtWWNList->next) {
				if (memcmp(&TgtWWNList->portWWN,
				    &entryP->PortLun.PortWWN,
				    sizeof (HBA_WWN)) == 0)
					return (0);
			}
			/* add it to existing */
			newTgtWWN = (tgtPortWWNList *)calloc(1,
			    sizeof (tgtPortWWNList));
			if (newTgtWWN == NULL) {
				(void *) fprintf(stderr,
				    "%s\n", strerror(errno));
				return (++ret);
			}
			/* insert at head */
			newTgtWWN->next = portElem->tgtPortWWN;
			portElem->tgtPortWWN = newTgtWWN;
			(void *) memcpy((void *)&(newTgtWWN->portWWN),
			    (void *)&(entryP->PortLun.PortWWN),
			    sizeof (HBA_WWN));
			/* Set LUN data */
			newTgtWWN->scsiOSLun =
			    entryP->ScsiId.ScsiOSLun;
		}
	} else {
		/*
		 * Here we got a new discovered LU which has not ever exist
		 * in our global LU list. So add it into our global LU
		 * list.
		 */
		newDevice = (discoveredDevice *)calloc(1,
		    sizeof (discoveredDevice));

		if (newDevice == NULL) {
			(void *) fprintf(stderr, "%s\n", strerror(errno));
			return (++ret);
		}
		newDevice->next = LUList; /* insert at head */
		LUList = newDevice; /* set new head */

		/* copy device name */
		(void *) strlcpy(newDevice->OSDeviceName,
		    entryP->ScsiId.OSDeviceName,
		    sizeof (newDevice->OSDeviceName));

		/* if verbose is not set return */
		if ((pflag & PRINT_VERBOSE) == 0) {
			return (0);
		}

		/* copy WWN data */
		newDevice->HBAPortList = (portList *)calloc(1,
		    sizeof (portList));
		if (newDevice->HBAPortList == NULL) {
			(void *) fprintf(stderr, "%s\n", strerror(errno));
			return (++ret);
		}
		(void) strlcpy(newDevice->HBAPortList->portName,
		    portName, sizeof (newDevice->HBAPortList->portName));

		newDevice->HBAPortList->tgtPortWWN =
		    (tgtPortWWNList *)calloc(1, sizeof (tgtPortWWNList));
		if (newDevice->HBAPortList->tgtPortWWN == NULL) {
			(void *) fprintf(stderr, "%s\n", strerror(errno));
			return (++ret);
		}

		(void *) memcpy((void *)&(newDevice->HBAPortList->\
		    tgtPortWWN->portWWN),
		    (void *)&(entryP->PortLun.PortWWN),
		    sizeof (HBA_WWN));
		newDevice->HBAPortList->tgtPortWWN->scsiOSLun =
		    entryP->ScsiId.ScsiOSLun;

		responseSize = sizeof (struct scsi_inquiry);
		senseSize = sizeof (struct scsi_extended_sense);
		(void *) memset(&inq, 0, sizeof (struct scsi_inquiry));
		(void *) memset(&sense, 0, sizeof (sense));
		(void *) memcpy(&smhbaLUN, &entryP->PortLun.TargetLun,
		    sizeof (smhbaLUN));

		/*
		 * Retrieve the VPD data for the newly found discovered LU.
		 */
		status = SMHBA_ScsiInquiry(
		    handle,
		    hbaPortWWN,
		    entryP->PortLun.PortWWN,
		    domainPortWWN,
		    smhbaLUN,
		    0,
		    0,
		    (void *) &inq, &responseSize,
		    &inq_status,
		    (void *) &sense, &senseSize);

		if (status != HBA_STATUS_OK) {
			/* init VID/PID/dType as '\0' */
			newDevice->VID[0] = '\0';
			newDevice->PID[0] = '\0';
			newDevice->dType = DTYPE_UNKNOWN;
			/* initialize inq status */
			newDevice->inquiryFailed = B_TRUE;
			ret++;
		} else {
			(void *) memcpy(newDevice->VID, inq.inq_vid,
			    sizeof (newDevice->VID));
			(void *) memcpy(newDevice->PID, inq.inq_pid,
			    sizeof (newDevice->PID));
			newDevice->dType = inq.inq_dtype;
			/* initialize inq status */
			newDevice->inquiryFailed = B_FALSE;
		}
	}
	return (ret);
}

/*
 * Function we use to insert a newly discovered port.
 * Return:
 * 	0 - success
 * 	>0 - failed
 */
static int
sas_rp_tree_insert(rp_tree_t **rproot,
    rp_tree_t *rpnode)
{
	HBA_UINT8 *wwn1, *wwn2, *wwn3;
	rp_tree_t *node_ptr;
	int ret = 0;

	if (rproot == NULL) {
		(void *) fprintf(stderr, "%s\n",
		    gettext("Error: NULL rproot"));
		return (1);
	}

	if (rpnode == NULL) {
		(void *) fprintf(stderr, "%s\n",
		    gettext("Error: NULL rpnode"));
		return (1);
	}

	if (*rproot == NULL) {
		*rproot = rpnode;
		return (0);
	}

	wwn1 = (*rproot)->sasattr.LocalSASAddress.wwn;
	wwn2 = (*rproot)->sasattr.AttachedSASAddress.wwn;
	wwn3 = rpnode->sasattr.AttachedSASAddress.wwn;

	/*
	 * If the attched sas address is equal to the local sas address,
	 * then this should be a child node of current root node.
	 */
	if (memcmp(wwn1, wwn3, sizeof (HBA_WWN)) == 0) {
		(void) sas_rp_tree_insert(&(*rproot)->child, rpnode);
		rpnode->parent = *rproot;
	} else if (memcmp(wwn2, wwn3, sizeof (HBA_WWN)) == 0) {
		/*
		 * If the attached sas address is equal to the attached sas
		 * address of current root node, then this should be a
		 * sibling node.
		 * Insert the SAS/SATA Device at the head of sibling list.
		 */
		if (rpnode->portattr.PortType != HBA_PORTTYPE_SASEXPANDER) {
			rpnode->sibling = *rproot;
			*rproot = rpnode;
		} else {
			/*
			 * Insert the SAS Expander at the tail of sibling
			 * list.
			 */
			node_ptr = *rproot;
			while (node_ptr->sibling != NULL)
				node_ptr = node_ptr->sibling;
			node_ptr->sibling = rpnode;
		}
		rpnode->parent = (*rproot)->parent;
	} else {
		/*
		 * If enter here, we should first try to insert the discovered
		 * port node into the child sub-tree, then try to insert to the
		 * sibling sub-trees. If we failed to insert the discovered
		 * port node, return 1. The caller will queue this node
		 * up and retry insertion later.
		 */
		if ((*rproot)->child) {
			ret = sas_rp_tree_insert(&(*rproot)->child, rpnode);
		}
		if ((*rproot)->child == NULL || ret != 0) {
			if ((*rproot)->sibling) {
				ret = sas_rp_tree_insert(&(*rproot)->sibling,
				    rpnode);
			} else
				ret = 1;
		}
		return (ret);
	}
	return (0);
}

/*
 * Function which will print out the whole disocvered port topology.
 * Here we use the Preorder Traversal algorithm.
 * The indentation rules are:
 * 	1 * TABLEN - for attributes
 * 	2 * TABLEN - for next tier target port/expander
 */
static int
sas_rp_tree_print(HBA_HANDLE handle, char *adapterName,
    HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
    rp_tree_t *rpnode, inputArg_t *input,
    int gident, int *printPort)
{
	int ret = 0, lident;

	if (rpnode == NULL)
		return (ret);
	lident = gident;

	/*
	 * We assume that all the nodes are disocvered ports(sas device or
	 * expander).
	 */
	if (input->wwnCount > 0) {
		/* Adjust local indentation if a discovered port specified. */
		lident = 2 * TABLEN;
		/*
		 * Check whether current node match one of the specified
		 * SAS addresses.
		 */
		if ((rpnode->portattr.PortType != HBA_PORTTYPE_SASEXPANDER) ||
		    !isPortWWNInArgv(input,
		    &rpnode->sasattr.LocalSASAddress)) {
			/*
			 * Step down to child tree first.
			 */
			ret += sas_rp_tree_print(handle, adapterName,
			    portIndex, port, rpnode->child, input,
			    gident + 2 * TABLEN, printPort);
			/*
			 * Then check the sibling tree.
			 */
			ret += sas_rp_tree_print(handle, adapterName,
			    portIndex, port, rpnode->sibling, input,
			    gident, printPort);
			return (ret);
		}
	}

	if ((rpnode->portattr.PortType == HBA_PORTTYPE_SASEXPANDER) ||
	    (input->pflag & PRINT_TARGET_PORT)) {
		/*
		 * We should print the header(HBA Name + HBA Port Name)
		 * on-demand. It means that, if we have expander device
		 * address specified on the command line, we should print
		 * the header once we find a matching one. Or we will
		 * print the header from the beginning of the output.
		 */
		if (g_printHBA == 0) {
			(void *) fprintf(stdout, "%s %s\n",
			    "HBA Name:", adapterName);
			g_printHBA = 1;
		}

		if (*printPort == 0) {
			(void *) fprintf(stdout, "%s%s %s\n",
			    getIndentSpaces(TABLEN),
			    "HBA Port Name:", port->OSDeviceName);
			*printPort = 1;
		}
		ret += sas_print_rpnode(input, rpnode, lident, gident);
	}

	/*
	 * If operands provided with "-t" option specified, we will print
	 * the immediate child nodes information under the expander.
	 */
	if (input->pflag & PRINT_TARGET_PORT) {
		/* no operand. ignore the option. */
		if (input->wwnCount > 0) {
			if (rpnode->portattr.PortType ==
			    HBA_PORTTYPE_SASEXPANDER) {
				ret += sas_rp_tree_print_desc(handle,
				    portIndex, port, rpnode->child,
				    input,
				    lident + 2 * TABLEN,
				    gident + 2 * TABLEN);
			}
		}
	}

	/*
	 * Here we use DFS(Depth First Search) algorithm to traverse the
	 * whole tree.
	 */
	ret += sas_rp_tree_print(handle, adapterName,
	    portIndex, port, rpnode->child, input,
	    gident + 2 * TABLEN, printPort);
	ret += sas_rp_tree_print(handle, adapterName,
	    portIndex, port, rpnode->sibling, input,
	    gident, printPort);
	return (ret);
}

/*
 * Function which will destroy the whole discovered port tree.
 * Here we use the Postorder Traversal algorithm.
 */
static void sas_rp_tree_free(rp_tree_t *rproot)
{
	tgt_mapping *cur, *next;

	if (rproot == NULL)
		return;

	/*
	 * Free child tree first.
	 */
	if (rproot->child) {
		sas_rp_tree_free(rproot->child);
	}

	/*
	 * Free sibling trees then.
	 */
	if (rproot->sibling) {
		sas_rp_tree_free(rproot->sibling);
	}

	/*
	 * Free root node at last.
	 */
	cur = rproot->first_entry;
	while (cur != NULL) {
		next = cur->next;
		free(cur);
		cur = next;
	}
	free(rproot);
}

/*
 * Function used to print out all the descendant nodes.
 * handle - handle to HBA.
 * port - port attributes of current HBA port.
 * desc - the root node of a subtree which will be processed.
 * input - input argument.
 * lident - local indentation for shifting indentation.
 * gident - global indentation, can also be used to obtain Tier number.
 */
/*ARGSUSED*/
static int
sas_rp_tree_print_desc(HBA_HANDLE handle, HBA_UINT32 portIndex,
    SMHBA_PORTATTRIBUTES *port, rp_tree_t *desc,
    inputArg_t *input, int lident, int gident)
{
	int ret = 0;
	rp_tree_t   *rp_node;

	if (desc == NULL)
		return (ret);
	/*
	 * Walk through the subtree of desc by Pre-Order Traversal Algo.
	 */
	for (rp_node = desc; rp_node !=	NULL; rp_node = rp_node->sibling) {
		ret += sas_print_rpnode(input, rp_node, lident, gident);
	}

	return (ret);
}

/*
 * Function used to print the information of specified SAS address.
 * handle - handle to a HBA.
 * port - port attributes of a HBA port.
 * rpnode - discovered port which will be processed.
 * lident - local indentation used for shifting indentation.
 * gident - global indentation used for calculating "Tier" number.
 */
static int
sas_print_rpnode(inputArg_t *input,
    rp_tree_t *rpnode, int lident, int gident)
{
	int ret = 0;

	if (rpnode->portattr.PortType == HBA_PORTTYPE_SASEXPANDER) {
		(void *) fprintf(stdout, "%s%s(Tier %d): %016llx\n",
		    getIndentSpaces(lident),
		    "Expander SAS Address",
		    gident / (2 * TABLEN),
		    wwnConversion(rpnode->sasattr.LocalSASAddress.wwn));
	} else {
		(void *) fprintf(stdout, "%s%s %016llx\n",
		    getIndentSpaces(lident),
		    "Target Port SAS Address:",
		    wwnConversion(rpnode->sasattr.LocalSASAddress.wwn));
	}
	if (input->pflag & PRINT_VERBOSE) {
		if (rpnode->portattr.PortType != HBA_PORTTYPE_SASEXPANDER) {
			(void *) fprintf(stdout, "%s%s %s\n",
			    getIndentSpaces(TABLEN + lident),
			    "Type:",
			    getStateString(rpnode->portattr.PortType,
			    porttype_string));
		} else {
			(void *) fprintf(stdout, "%s%s %s\n",
			    getIndentSpaces(TABLEN + lident),
			    "OS Device Name:",
			    rpnode->portattr.OSDeviceName);
			(void *) fprintf(stdout, "%s%s %s\n",
			    getIndentSpaces(TABLEN + lident),
			    "State: ",
			    getStateString(rpnode->portattr.PortState,
			    portstate_string));
		}
	}
	rpnode->printed = 1;
	return (ret);
}

/*
 * Function used to get the correct domainPortWWN as needed by some of the
 * SMHBA APIs.
 * handle - handle to a HBA.
 * portIndex - index to locate the port.
 * port - pointer to the structure holding port attributes.
 * pdomainPort - pointer to the buffer holding domainPortWWN.
 */
HBA_STATUS
get_domainPort(HBA_HANDLE handle,
    int portIndex, PSMHBA_PORTATTRIBUTES port,
    HBA_WWN *pdomainPort)
{
	HBA_STATUS status;
	PSMHBA_SAS_PORT sasport;
	SMHBA_SAS_PHY phyattr;

	sasport = port->PortSpecificAttribute.SASPort;
	(void *) memset(pdomainPort, 0, sizeof (HBA_WWN));
	/*
	 * Since iport can exist without any phys,
	 * sasinfo hba-port -v has indicated numberOfPhys;
	 * if there is no phys within the hba, just return OK.
	 */
	if (sasport->NumberofPhys > 0) {
		status = SMHBA_GetSASPhyAttributes(handle, portIndex,
		    0, &phyattr);
		if (status != HBA_STATUS_OK)
			return (status);
		(void *) memcpy(pdomainPort, &phyattr.domainPortWWN,
		    sizeof (HBA_WWN));
	} else {
		/* return not supported for no phy configured */
		return (HBA_STATUS_ERROR_NOT_SUPPORTED);
	}
	return (HBA_STATUS_OK);
}

/*
 * Comparison function for comparing names possibly ending with digits.
 * Return:
 * 	<0 - name1 is less than name2.
 * 	0 - name1 is equal with name2.
 * 	>0 - name1 is more than name2.
 */
static int
sas_name_comp(const char *name1, const char *name2)
{
	int i = 0;

	if (name1 == name2)
		return (0);

	while ((name1[i] == name2[i]) && (name1[i] != '\0'))
		i++;

	/* If neither of name1[i] and name2[i] is '\0'. */
	if (isdigit(name1[i]) && isdigit(name2[i]))
		return (atoi(&name1[i]) - atoi(&name2[i]));

	/* One of name1[i] and name2[i] is not digit. */
	return (name1[i] - name2[i]);
}
/*
 * Comparison function for sorting HBA/HBA Port.
 * arg1 - first argument of type sas_elem_t.
 * arg2 - second argument of type sas_elem_t.
 * Return:
 * 	<0 - arg1 is less than arg2.
 * 	0 - arg1 is equal with arg2.
 * 	>0 - arg1 is more than arg2.
 */
static int
sas_elem_compare(const void *arg1, const void *arg2)
{
	sas_elem_t *p1, *p2;
	p1 = (sas_elem_t *)arg1;
	p2 = (sas_elem_t *)arg2;
	return (sas_name_comp(p1->name, p2->name));
}

/*
 * Sorting function for HBA/HBA Port output.
 * array - elements array of type sas_elem_t.
 * nelem - number of elements in array of type sas_elem_t.
 */
static void
sas_elem_sort(sas_elem_t *array, int nelem)
{
	qsort((void *)array, nelem, sizeof (sas_elem_t), sas_elem_compare);
}