root/usr/src/cmd/sasinfo/sasinfo-list.c
/*
 * 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);
}