root/usr/src/cmd/luxadm/fchba.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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */



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

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

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


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

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

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

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

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

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

#define MAX_RETRIES     10

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

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

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

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

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

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

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

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


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

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

        count = getNumberOfAdapters();

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

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

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

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


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

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

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

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

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

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

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

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

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


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

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

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

        count = getNumberOfAdapters();

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

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


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

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

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

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

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

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

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


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

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

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

            count = getNumberOfAdapters();

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

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


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

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

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

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

            if (physical) {
                free(physical);
            }

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



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


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

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

            count = getNumberOfAdapters();

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

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


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


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

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

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

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

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

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

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

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

            count = getNumberOfAdapters();

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

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


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

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

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

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

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


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

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

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


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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

        }

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

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

}

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

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

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

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

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



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

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

            count = getNumberOfAdapters();


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

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

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

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



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

            if (physical) {
                free(physical);
            }

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

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


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        if (path_phys) {
            free(path_phys);
        }

        return (exit_code);
}

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

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

}

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

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

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

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