root/usr/src/lib/cfgadm_plugins/fp/common/cfga_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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include "cfga_fp.h"
#include <sys/fibre-channel/impl/fc_error.h>

/* Structure for walking the tree */
typedef struct {
        apid_t          *apidp;
        char            *xport_logp;
        ldata_list_t    *listp;
        fpcfga_cmd_t    cmd;
        cfga_stat_t     chld_config;
        cfga_type_t     xport_type;
        cfga_stat_t     xport_rstate;
        fpcfga_ret_t    ret;
        int             l_errno;
} fpcfga_list_t;

typedef struct {
        uint_t itype;
        const char *ntype;
        const char *name;
} fpcfga_devtype_t;

#define ERR_INQ_DTYPE   0xff

/* The TYPE field is parseable and should not contain spaces */
#define FP_FC_PORT_TYPE         "fc"
#define FP_FC_PORT_ERROR        "fc-error"
#define FP_FC_FABRIC_PORT_TYPE  "fc-fabric"
#define FP_FC_PUBLIC_PORT_TYPE  "fc-public"
#define FP_FC_PRIVATE_PORT_TYPE "fc-private"
#define FP_FC_PT_TO_PT_PORT_TYPE        "fc-pt_to_pt"

/* Indicates no plag passing */
#define NO_FLAG                 0

/* defines for retry algorithm */
#define OPEN_RETRY_COUNT        5
#define OPEN_RETRY_INTERVAL     10000 /* 1/100 of a sec. */
#define IOCTL_RETRY_COUNT       5
#define IOCTL_RETRY_INTERVAL    5000000 /* 5 sec */

/* define for fcp scsi passthru wait */
#define FCP_SCSI_CMD_TIMEOUT    10

/* define for fcp pseudo node */
#define FCP_PATH        "/devices/pseudo/fcp@0:fcp"

/* Function prototypes */
static fpcfga_ret_t postprocess_list_data(const ldata_list_t *listp,
    fpcfga_cmd_t cmd, cfga_stat_t chld_config, int *np, uint_t flags);
static int stat_fc_dev(di_node_t node, void *arg);
static int stat_FCP_dev(di_node_t node, void *arg);
static fpcfga_ret_t do_stat_fca_xport(fpcfga_list_t *lap, int limited_stat,
        HBA_PORTATTRIBUTES portAttrs);
static int get_xport_state(di_node_t node, void *arg);

static fpcfga_ret_t do_stat_fc_dev(const di_node_t node, const char *nodepath,
    fpcfga_list_t *lap, int limited_stat);
static fpcfga_ret_t do_stat_FCP_dev(const di_node_t node, const char *nodepath,
    fpcfga_list_t *lap, int limited_stat);
static cfga_stat_t xport_devinfo_to_recep_state(uint_t xport_di_state);
static cfga_stat_t dev_devinfo_to_occupant_state(uint_t dev_di_state);
static void get_hw_info(di_node_t node, cfga_list_data_t *clp);
static const char *get_device_type(di_node_t);
static fpcfga_ret_t init_ldata_for_accessible_dev(const char *dyncomp,
        uchar_t inq_type, fpcfga_list_t *lap);
static fpcfga_ret_t init_ldata_for_accessible_FCP_dev(const char *port_wwn,
        int num_luns, struct report_lun_resp *resp_buf,
        fpcfga_list_t *larg, int *l_errnop);
static fpcfga_ret_t is_dyn_ap_on_ldata_list(const char *port_wwn,
        const ldata_list_t *listp, ldata_list_t **matchldpp, int *l_errno);
static fpcfga_ret_t is_FCP_dev_ap_on_ldata_list(const char *port_wwn,
        const int lun_num, ldata_list_t *ldatap, ldata_list_t **matchldpp);

static fpcfga_ret_t init_ldata_for_mpath_dev(di_path_t path, char *port_wwn,
        int *l_errnop, fpcfga_list_t *lap);
static fpcfga_ret_t insert_ldata_to_ldatalist(const char *port_wwn,
        int *lun_nump, ldata_list_t *listp, ldata_list_t **ldatapp);
static fpcfga_ret_t insert_fc_dev_ldata(const char *port_wwn,
        ldata_list_t *listp, ldata_list_t **ldatapp);
static fpcfga_ret_t insert_FCP_dev_ldata(const char *port_wwn, int lun_num,
        ldata_list_t *listp, ldata_list_t **ldatapp);
static int stat_path_info_fc_dev(di_node_t root, fpcfga_list_t  *lap,
        int *l_errnop);
static int stat_path_info_FCP_dev(di_node_t root, fpcfga_list_t *lap,
        int *l_errnop);
static fpcfga_ret_t get_accessible_FCP_dev_ldata(const char *dyncomp,
        fpcfga_list_t *lap, int *l_errnop);
static fpcfga_ret_t get_standard_inq_data(const char *xport_phys,
        const char *dyncomp, uchar_t *lun_num, struct scsi_inquiry **inq_buf,
        int *l_errnop);
static void init_fcp_scsi_cmd(struct fcp_scsi_cmd *fscsi, uchar_t *lun_num,
        la_wwn_t *pwwn, void *scmdbuf, size_t scmdbuf_len, void *respbuf,
        size_t respbuf_len, void *sensebuf, size_t sensebuf_len);
static fpcfga_ret_t issue_fcp_scsi_cmd(const char *xport_phys,
        struct fcp_scsi_cmd *fscsi, int *l_errnop);
static uchar_t get_inq_dtype(char *xport_phys, char *dyncomp, HBA_HANDLE handle,
    HBA_PORTATTRIBUTES *portAttrs, HBA_PORTATTRIBUTES *discPortAttrs);

static fpcfga_devtype_t device_list[] = {
        { DTYPE_DIRECT,         DDI_NT_BLOCK_CHAN,      "disk"},
        { DTYPE_DIRECT,         DDI_NT_BLOCK,           "disk"},
        { DTYPE_DIRECT,         DDI_NT_BLOCK_WWN,       "disk"},
        { DTYPE_DIRECT,         DDI_NT_BLOCK_FABRIC,    "disk"},
        { DTYPE_SEQUENTIAL,     DDI_NT_TAPE,            "tape"},
        { DTYPE_PRINTER,        NULL,                   "printer"},
        { DTYPE_PROCESSOR,      NULL,                   "processor"},
        { DTYPE_WORM,           NULL,                   "WORM"},
        { DTYPE_RODIRECT,       DDI_NT_CD_CHAN,         "CD-ROM"},
        { DTYPE_RODIRECT,       DDI_NT_CD,              "CD-ROM"},
        { DTYPE_SCANNER,        NULL,                   "scanner"},
        { DTYPE_OPTICAL,        NULL,                   "optical"},
        { DTYPE_CHANGER,        NULL,                   "med-changer"},
        { DTYPE_COMM,           NULL,                   "comm-device"},
        { DTYPE_ARRAY_CTRL,     NULL,                   "array-ctrl"},
        { DTYPE_ESI,            NULL,                   "ESI"},
        /*
         * This has to be the LAST entry for DTYPE_UNKNOWN_INDEX.
         * Add entries before this.
         */
        { DTYPE_UNKNOWN,        NULL,                   "unknown"}
};

#define N_DEVICE_TYPES  (sizeof (device_list) / sizeof (device_list[0]))

#define DTYPE_UNKNOWN_INDEX     (N_DEVICE_TYPES - 1)

/*
 * Main routine for list operation.
 * It calls various routines to consturct ldata list and
 * postprocess the list data.
 *
 * Overall algorithm:
 * Get the device list on input hba port and construct ldata list for
 * accesible devices.
 * Stat hba port and devices through walking the device tree.
 * Verify the validity of the list data.
 */
fpcfga_ret_t
do_list(
        apid_t *apidp,
        fpcfga_cmd_t cmd,
        ldata_list_t **llpp,
        int *nelemp,
        char **errstring)
{
        int             n = -1, l_errno = 0, limited_stat;
        walkarg_t       walkarg;
        fpcfga_list_t   larg = {NULL};
        fpcfga_ret_t    ret;
        la_wwn_t        pwwn;
        char            *dyncomp = NULL;
        HBA_HANDLE      handle;
        HBA_PORTATTRIBUTES      portAttrs;
        HBA_PORTATTRIBUTES      discPortAttrs;
        HBA_STATUS              status;
        int                     portIndex, discIndex;
        int                     retry;
        uchar_t                 inq_dtype;

        if (*llpp != NULL || *nelemp != 0) {
                return (FPCFGA_ERR);
        }

        /* Create the hba logid (also base component of logical ap_id) */
        ret = make_xport_logid(apidp->xport_phys, &larg.xport_logp, &l_errno);
        if (ret != FPCFGA_OK) {
                cfga_err(errstring, l_errno, ERR_LIST, 0);
                return (FPCFGA_ERR);
        }

        assert(larg.xport_logp != NULL);

        larg.cmd = cmd;
        larg.apidp = apidp;
        larg.xport_rstate = CFGA_STAT_NONE;

        if ((ret = findMatchingAdapterPort(larg.apidp->xport_phys, &handle,
            &portIndex, &portAttrs, errstring)) != FPCFGA_OK) {
            S_FREE(larg.xport_logp);
            return (ret);
        }

        /*
         * If stating a specific device, we will do limited stat on fca port.
         * otherwise full stat on fca part is required.
         * If stating a specific device we don't know if it exists or is
         * configured yet.  larg.ret is set to apid noexist for do_stat_dev.
         * otherwise larg.ret is set to ok initially.
         */
        if (larg.cmd == FPCFGA_STAT_FC_DEV) {
                limited_stat = 1;               /* for do_stat_fca_xport */
                larg.ret = FPCFGA_APID_NOEXIST; /* for stat_fc_dev      */
        } else {
                limited_stat = 0;               /* for do_stat_fca_xport */
                larg.ret = FPCFGA_OK;           /* for stat_fc_dev      */
        }

        /* For all list commands, the fca port needs to be stat'ed */
        if ((ret = do_stat_fca_xport(&larg, limited_stat,
                portAttrs)) != FPCFGA_OK) {
                cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
                list_free(&larg.listp);
                S_FREE(larg.xport_logp);
                HBA_CloseAdapter(handle);
                HBA_FreeLibrary();
                return (ret);
        }

#ifdef DEBUG
        if (limited_stat) {
                assert(larg.listp == NULL);
        } else {
                assert(larg.listp != NULL);
        }
#endif
        /*
         * If stat'ing a FCA port or ALL, we have the bus stat data at
         * this point.
         * Assume that the bus has no configured children.
         */
        larg.chld_config = CFGA_STAT_UNCONFIGURED;

        switch (larg.cmd) {
        case FPCFGA_STAT_FC_DEV:
                /* la_wwn_t has uchar_t raw_wwn[8] thus no need to free. */
                if (cvt_dyncomp_to_lawwn(apidp->dyncomp, &pwwn) != 0) {
                        cfga_err(errstring, 0, ERR_LIST, 0);
                        list_free(&larg.listp);
                        S_FREE(larg.xport_logp);
                        HBA_CloseAdapter(handle);
                        HBA_FreeLibrary();
                        return (FPCFGA_LIB_ERR);
                }
                /*
                 * if the dyncomp exists on disco ports construct list_data
                 * otherwise return FPCFGA_APID_NOEXIST.
                 */
                retry = 0;
                do {
                    status = getPortAttrsByWWN(handle,
                        *((HBA_WWN *)(&pwwn)), &discPortAttrs);
                    if (status == HBA_STATUS_ERROR_STALE_DATA) {
                        /* get Port Attributes again after refresh. */
                        HBA_RefreshInformation(handle);
                    } else {
                        break; /* either okay or some other error */
                    }
                } while (retry++ < HBA_MAX_RETRIES);

                if (status == HBA_STATUS_OK) {
                        /*
                         * if dyncomp found in disco ports
                         * construct  ldata_list and return.
                         * otherwise continue to stat on dev tree with
                         * larg.ret set to access_ok which informs stat_fc_dev
                         * the existence of device on disco ports.
                         *
                         * if path is null that guatantees the node is not
                         * configured.  if node is detached the path
                         * is incomplete and not usable for further
                         * operations like uscsi_inq so take care of it here.
                         */
                        inq_dtype = get_inq_dtype(apidp->xport_phys,
                            apidp->dyncomp, handle, &portAttrs, &discPortAttrs);

                        if (init_ldata_for_accessible_dev(apidp->dyncomp,
                                inq_dtype, &larg) != FPCFGA_OK) {
                                cfga_err(errstring, larg.l_errno,
                                        ERR_LIST, 0);
                                list_free(&larg.listp);
                                S_FREE(larg.xport_logp);
                                HBA_CloseAdapter(handle);
                                HBA_FreeLibrary();
                                return (FPCFGA_LIB_ERR);
                        }
                        if (apidp->lunlist == NULL) {
                                n = 0;
                                if (postprocess_list_data(
                                        larg.listp, cmd,
                                        larg.chld_config, &n, NO_FLAG) !=
                                        FPCFGA_OK) {
                                        cfga_err(errstring,
                                        larg.l_errno, ERR_LIST, 0);
                                        list_free(&larg.listp);
                                        S_FREE(larg.xport_logp);
                                        HBA_CloseAdapter(handle);
                                        HBA_FreeLibrary();
                                        return (FPCFGA_LIB_ERR);
                                }
                                *nelemp = n;
                                *llpp = larg.listp;
                                S_FREE(larg.xport_logp);
                                HBA_CloseAdapter(handle);
                                HBA_FreeLibrary();
                                return (FPCFGA_OK);
                        }
                        larg.ret = FPCFGA_ACCESS_OK;
                } else if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
                        /*
                         * path indicates if the node exists in dev tree.
                         * if not found in dev tree return apid no exist.
                         * otherwise continue to stat with larg.ret set to
                         * apid_noexist.
                         */
                        if (apidp->lunlist == NULL) {
                                list_free(&larg.listp);
                                S_FREE(larg.xport_logp);
                                HBA_CloseAdapter(handle);
                                HBA_FreeLibrary();
                                return (FPCFGA_APID_NOEXIST);
                        }
                } else { /* any error */
                        /*
                         * path indicates if the node exists in dev tree.
                         * if not found in dev tree return lib error.
                         * otherwise continue to stat with larg.ret set to
                         * apid_noexist.
                         */
                        if (apidp->lunlist == NULL) {
                                cfga_err(errstring, 0, ERR_FC_GET_DEVLIST, 0);
                                list_free(&larg.listp);
                                S_FREE(larg.xport_logp);
                                HBA_CloseAdapter(handle);
                                HBA_FreeLibrary();
                                return (FPCFGA_LIB_ERR);
                        }
                }
                break;
        case FPCFGA_STAT_ALL:
                /*
                 * for each dev in disco ports, create a ldata_list element.
                 * if if no disco ports found, continue to stat on devinfo tree
                 * to see if any node exist on the fca port.
                 */
                for (discIndex = 0;
                        discIndex < portAttrs.NumberofDiscoveredPorts;
                        discIndex++) {
                    if (getDiscPortAttrs(handle, portIndex,
                        discIndex, &discPortAttrs)) {
                        /* Move on to the next target */
                        continue;
                    }
                    memcpy(&pwwn, &discPortAttrs.PortWWN, sizeof (la_wwn_t));
                    cvt_lawwn_to_dyncomp(&pwwn, &dyncomp, &l_errno);
                    if (dyncomp == NULL) {
                        cfga_err(errstring, l_errno, ERR_LIST, 0);
                        list_free(&larg.listp);
                        S_FREE(larg.xport_logp);
                        HBA_CloseAdapter(handle);
                        HBA_FreeLibrary();
                        return (FPCFGA_LIB_ERR);
                    }
                    inq_dtype = get_inq_dtype(apidp->xport_phys, dyncomp,
                        handle, &portAttrs, &discPortAttrs);

                    if ((ret = init_ldata_for_accessible_dev(
                            dyncomp, inq_dtype, &larg)) != FPCFGA_OK) {
                        S_FREE(dyncomp);
                        cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
                                list_free(&larg.listp);
                        S_FREE(larg.xport_logp);
                        HBA_CloseAdapter(handle);
                        HBA_FreeLibrary();
                        return (FPCFGA_LIB_ERR);
                    }
                    S_FREE(dyncomp);
                }
                break;
        default:
                break;
        }

        /* we need to stat at least 1 device for all commands */
        if (apidp->flags == FLAG_DEVINFO_FORCE) {
                walkarg.flags = FLAG_DEVINFO_FORCE;
        } else {
                walkarg.flags = 0;
        }

        walkarg.flags |= FLAG_PATH_INFO_WALK;
        walkarg.walkmode.node_args.flags = DI_WALK_CLDFIRST;
        walkarg.walkmode.node_args.fcn = stat_fc_dev;

        /*
         * Subtree is ALWAYS rooted at the HBA (not at the device) as
         * otherwise deadlock may occur if bus is disconnected.
         *
         * DINFOPROP was sufficient on apidp->xport_phys prior to the support
         * on scsi_vhci child node.  In order to get the link between
         * scsi_vhci node and path info node the snap shot of the
         * the whole device tree is required with DINFOCPYALL | DINFOPATH flag.
         */
        ret = walk_tree(apidp->xport_phys, &larg, DINFOCPYALL | DINFOPATH,
                        &walkarg, FPCFGA_WALK_NODE, &larg.l_errno);

        /*
         * ret from walk_tree is either FPCFGA_OK or FPCFGA_ERR.
         * larg.ret is used to detect other errors. Make sure larg.ret
         * is set to a correct error.
         */
        if (ret != FPCFGA_OK || (ret = larg.ret) != FPCFGA_OK) {
                if (ret != FPCFGA_APID_NOEXIST) {
                        cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
                }
                /* if larg.ret = FPCFGA_APID_NOEXIST; */
                goto out;
        }

        assert(larg.listp != NULL);

        n = 0;
        ret = postprocess_list_data(larg.listp, cmd, larg.chld_config, &n,
                NO_FLAG);
        if (ret != FPCFGA_OK) {
                cfga_err(errstring, 0, ERR_LIST, 0);
                ret = FPCFGA_LIB_ERR;
                goto out;
        }

        *nelemp = n;
        *llpp = larg.listp;
        ret = FPCFGA_OK;
        /* FALLTHROUGH */
out:
        if (ret != FPCFGA_OK) list_free(&larg.listp);
        S_FREE(larg.xport_logp);
        HBA_CloseAdapter(handle);
        HBA_FreeLibrary();
        return (ret);
}

/*
 * Main routine for list operation when show_FCP_dev option is given.
 * It calls various routines to consturct ldata list and
 * postprocess the list data.
 *
 * The difference between do_list() and do_list_FCP_dev() is to
 * process FCP SCSI LUN data list via uscsi report lun operation and
 * stat lun level instead of port WWN based target level.
 * The rest of logic is same.
 *
 * Overall algorithm:
 * Get the device list on input hba port and construct ldata list for
 * accesible devices.
 * For each configured device, USCSI report lun is issued and ldata list
 * with FCP device level(LUN) information is created.
 * Stat hba port and LUN devices through walking the device tree.
 * Verify the validity of the list data.
 */
fpcfga_ret_t
do_list_FCP_dev(
        const char *ap_id,
        uint_t flags,
        fpcfga_cmd_t cmd,
        ldata_list_t **llpp,
        int *nelemp,
        char **errstring)
{
        int             n = -1, l_errno = 0, limited_stat, len;
        walkarg_t       walkarg;
        fpcfga_list_t   larg = {NULL};
        fpcfga_ret_t    ret;
        la_wwn_t        pwwn;
        char            *xport_phys = NULL, *dyn = NULL, *dyncomp = NULL,
                        *lun_dyn = NULL;
        apid_t          apid_con = {NULL};
        HBA_HANDLE      handle;
        HBA_PORTATTRIBUTES      portAttrs;
        HBA_PORTATTRIBUTES      discPortAttrs;
        HBA_STATUS              status;
        int                     portIndex, discIndex;
        int                     retry;
        uint64_t                lun = 0;
        struct scsi_inquiry inq;
        struct scsi_extended_sense sense;
        HBA_UINT8               scsiStatus;
        uint32_t                inquirySize = sizeof (inq),
                                senseSize = sizeof (sense);

        if (*llpp != NULL || *nelemp != 0) {
                return (FPCFGA_ERR);
        }

        if ((xport_phys = pathdup(ap_id, &l_errno)) == NULL) {
                cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
                return (FPCFGA_LIB_ERR);
        }

        /* Extract the base(hba) and dynamic(device) component if any */
        if ((dyn = GET_DYN(xport_phys)) != NULL) {
                len = strlen(DYN_TO_DYNCOMP(dyn)) + 1;
                dyncomp = calloc(1, len);
                if (dyncomp == NULL) {
                        cfga_err(errstring, errno, ERR_OP_FAILED, 0);
                        S_FREE(xport_phys);
                        return (FPCFGA_LIB_ERR);
                }

                (void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
                /* Remove the dynamic component from the base. */
                *dyn = '\0';
                /* if lun dyncomp exists delete it */
                if ((lun_dyn = GET_LUN_DYN(dyncomp)) != NULL) {
                        *lun_dyn = '\0';
                }
        }

        apid_con.xport_phys = xport_phys;
        apid_con.dyncomp = dyncomp;
        apid_con.flags = flags;

        larg.apidp = &apid_con;

        /* Create the hba logid (also base component of logical ap_id) */
        ret = make_xport_logid(larg.apidp->xport_phys, &larg.xport_logp,
                &l_errno);
        if (ret != FPCFGA_OK) {
                cfga_err(errstring, l_errno, ERR_LIST, 0);
                S_FREE(larg.apidp->xport_phys);
                S_FREE(larg.apidp->dyncomp);
                return (FPCFGA_ERR);
        }

        assert(larg.xport_logp != NULL);

        larg.cmd = cmd;
        larg.xport_rstate = CFGA_STAT_NONE;

        if ((ret = findMatchingAdapterPort(larg.apidp->xport_phys, &handle,
            &portIndex, &portAttrs, errstring)) != FPCFGA_OK) {
            S_FREE(larg.xport_logp);
            S_FREE(larg.apidp->dyncomp);
            return (ret);
        }

        /*
         * If stating a specific device, we will do limited stat on fca port.
         * otherwise full stat on fca part is required.
         * If stating a specific device we don't know if it exists or is
         * configured yet.  larg.ret is set to apid noexist for do_stat_dev.
         * otherwise larg.ret is set to ok initially.
         */
        if (larg.cmd == FPCFGA_STAT_FC_DEV) {
                limited_stat = 1;               /* for do_stat_fca_xport */
                larg.ret = FPCFGA_APID_NOEXIST; /* for stat_fc_dev      */
        } else {
                limited_stat = 0;               /* for do_stat_fca_xport */
                larg.ret = FPCFGA_OK;           /* for stat_fc_dev      */
        }

        /* For all list commands, the fca port needs to be stat'ed */
        if ((ret = do_stat_fca_xport(&larg, limited_stat,
                portAttrs)) != FPCFGA_OK) {
                cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
                list_free(&larg.listp);
                S_FREE(larg.xport_logp);
                S_FREE(larg.apidp->xport_phys);
                S_FREE(larg.apidp->dyncomp);
                HBA_CloseAdapter(handle);
                HBA_FreeLibrary();
                return (ret);
        }

        /*
         * If stat'ing a FCA port or ALL, we have the bus stat data at
         * this point.
         * Assume that the bus has no configured children.
         */
        larg.chld_config = CFGA_STAT_UNCONFIGURED;

        switch (larg.cmd) {
        case FPCFGA_STAT_FC_DEV:
                /* la_wwn_t has uchar_t raw_wwn[8] thus no need to free. */
                if (cvt_dyncomp_to_lawwn(larg.apidp->dyncomp, &pwwn) != 0) {
                        cfga_err(errstring, 0, ERR_LIST, 0);
                        list_free(&larg.listp);
                        S_FREE(larg.xport_logp);
                        S_FREE(larg.apidp->xport_phys);
                        S_FREE(larg.apidp->dyncomp);
                        HBA_CloseAdapter(handle);
                        HBA_FreeLibrary();
                        return (FPCFGA_LIB_ERR);
                }
                /*
                 * if the dyncomp exists on disco ports construct list_data
                 * otherwise return FPCFGA_APID_NOEXIST.
                 */
                retry = 0;
                do {
                    status = getPortAttrsByWWN(handle,
                        *((HBA_WWN *)(&pwwn)), &discPortAttrs);
                    if (status == HBA_STATUS_ERROR_STALE_DATA) {
                        /* get Port Attributes again after refresh. */
                        HBA_RefreshInformation(handle);
                    } else {
                        break; /* either okay or some other error */
                    }
                } while (retry++ < HBA_MAX_RETRIES);

                if (status == HBA_STATUS_OK) {
                        /*
                         * if dyncomp exists only in dev list
                         * construct  ldata_list and return.
                         * otherwise continue to stat on dev tree with
                         * larg.ret set to access_ok which informs stat_fc_dev
                         * the existence of device on dev_list.
                         *
                         * if path is null that guatantees the node is not
                         * configured.  if node is detached the path
                         * is incomplete and not usable for further
                         * operations like uscsi_inq so take care of it here.
                         */
                        status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN,
                                    discPortAttrs.PortWWN, lun, 0, 0,
                                    &inq, &inquirySize, &scsiStatus,
                                    &sense, &senseSize);
                        if (status == HBA_STATUS_OK) {
                                inq.inq_dtype = inq.inq_dtype & DTYPE_MASK;
                        } else if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
                                inq.inq_dtype = DTYPE_UNKNOWN;
                        } else {
                            inq.inq_dtype = ERR_INQ_DTYPE;
                        }

                        if (init_ldata_for_accessible_dev(larg.apidp->dyncomp,
                                inq.inq_dtype, &larg) != FPCFGA_OK) {
                                cfga_err(errstring, larg.l_errno,
                                        ERR_LIST, 0);
                                list_free(&larg.listp);
                                S_FREE(larg.xport_logp);
                                S_FREE(larg.apidp->xport_phys);
                                S_FREE(larg.apidp->dyncomp);
                                HBA_CloseAdapter(handle);
                                HBA_FreeLibrary();
                                return (FPCFGA_LIB_ERR);
                        }
                        if ((ret = get_accessible_FCP_dev_ldata(
                                        larg.apidp->dyncomp, &larg, &l_errno))
                                        != FPCFGA_OK) {
                                cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
                                list_free(&larg.listp);
                                S_FREE(larg.xport_logp);
                                S_FREE(larg.apidp->xport_phys);
                                S_FREE(larg.apidp->dyncomp);
                                HBA_CloseAdapter(handle);
                                HBA_FreeLibrary();
                                return (FPCFGA_LIB_ERR);
                        } else {
                                /* continue to stat dev with access okay. */
                                larg.ret = FPCFGA_ACCESS_OK;
                        }
                } else if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
                        /*
                         * path indicates if the node exists in dev tree.
                         * if not found in dev tree return apid no exist.
                         * otherwise continue to stat with larg.ret set to
                         * apid_noexist.
                         */
                        if (larg.apidp->lunlist == NULL) {
                                list_free(&larg.listp);
                                S_FREE(larg.xport_logp);
                                HBA_CloseAdapter(handle);
                                HBA_FreeLibrary();
                                return (FPCFGA_APID_NOEXIST);
                        }
                } else {        /* not found or any error */
                        /*
                         * continue to stat dev with larg.ret set to
                         * apid_noexist.
                         */
                        larg.ret = FPCFGA_APID_NOEXIST;
                }
                break;
        case FPCFGA_STAT_ALL:
                /*
                 * for each dev in disco ports, create a ldata_list element.
                 * if if no disco ports found, continue to stat on devinfo tree
                 * to see if any node exist on the fca port.
                 */
                for (discIndex = 0;
                        discIndex < portAttrs.NumberofDiscoveredPorts;
                        discIndex++) {
                    if (getDiscPortAttrs(handle, portIndex,
                        discIndex, &discPortAttrs)) {
                        /* Move on to the next target */
                        continue;
                    }
                    memcpy(&pwwn, &discPortAttrs.PortWWN, sizeof (la_wwn_t));
                    cvt_lawwn_to_dyncomp(&pwwn, &dyncomp, &l_errno);
                    if (dyncomp == NULL) {
                        cfga_err(errstring, l_errno, ERR_LIST, 0);
                        list_free(&larg.listp);
                        S_FREE(larg.xport_logp);
                        S_FREE(larg.apidp->xport_phys);
                        S_FREE(larg.apidp->dyncomp);
                        HBA_CloseAdapter(handle);
                        HBA_FreeLibrary();
                        return (FPCFGA_LIB_ERR);
                    }
                    status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN,
                            discPortAttrs.PortWWN, lun, 0, 0,
                            &inq, &inquirySize, &scsiStatus,
                            &sense, &senseSize);
                    if (status == HBA_STATUS_OK) {
                            inq.inq_dtype = inq.inq_dtype & DTYPE_MASK;
                    } else if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
                            inq.inq_dtype = DTYPE_UNKNOWN;
                    } else {
                            inq.inq_dtype = ERR_INQ_DTYPE;
                    }
                    if ((ret = init_ldata_for_accessible_dev(
                            dyncomp, inq.inq_dtype, &larg)) != FPCFGA_OK) {
                        S_FREE(dyncomp);
                        cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
                        list_free(&larg.listp);
                        S_FREE(larg.xport_logp);
                        S_FREE(larg.apidp->xport_phys);
                        S_FREE(larg.apidp->dyncomp);
                        HBA_CloseAdapter(handle);
                        HBA_FreeLibrary();
                        return (FPCFGA_LIB_ERR);
                    }
                    if ((ret = get_accessible_FCP_dev_ldata(
                        dyncomp, &larg, &l_errno)) != FPCFGA_OK) {
                        S_FREE(dyncomp);
                        cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
                        list_free(&larg.listp);
                        S_FREE(larg.xport_logp);
                        S_FREE(larg.apidp->xport_phys);
                        S_FREE(larg.apidp->dyncomp);
                        HBA_CloseAdapter(handle);
                        HBA_FreeLibrary();
                        return (ret);
                    }
                    S_FREE(dyncomp);
                }
                break;
        /* default: continue */
        }

        /* we need to stat at least 1 device for all commands */
        if ((larg.apidp->flags & FLAG_DEVINFO_FORCE) == FLAG_DEVINFO_FORCE) {
                walkarg.flags = FLAG_DEVINFO_FORCE;
        } else {
                walkarg.flags = 0;
        }

        walkarg.flags |= FLAG_PATH_INFO_WALK;
        walkarg.walkmode.node_args.flags = DI_WALK_CLDFIRST;
        walkarg.walkmode.node_args.fcn = stat_FCP_dev;

        /*
         * Subtree is ALWAYS rooted at the HBA (not at the device) as
         * otherwise deadlock may occur if bus is disconnected.
         *
         * DINFOPROP was sufficient on apidp->xport_phys prior to the support
         * on scsi_vhci child node.  In order to get the link between
         * scsi_vhci node and path info node the snap shot of the
         * the whole device tree is required with DINFOCPYALL | DINFOPATH flag.
         */
        ret = walk_tree(larg.apidp->xport_phys, &larg, DINFOCPYALL | DINFOPATH,
                        &walkarg, FPCFGA_WALK_NODE, &larg.l_errno);

        /*
         * ret from walk_tree is either FPCFGA_OK or FPCFGA_ERR.
         * larg.ret is used to detect other errors. Make sure larg.ret
         * is set to a correct error.
         */
        if (ret != FPCFGA_OK || (ret = larg.ret) != FPCFGA_OK) {
                if (ret != FPCFGA_APID_NOEXIST) {
                        cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
                }
                /* if larg.ret = FPCFGA_APID_NOEXIST return. */
                list_free(&larg.listp);
                S_FREE(larg.xport_logp);
                S_FREE(larg.apidp->xport_phys);
                S_FREE(larg.apidp->dyncomp);
                HBA_CloseAdapter(handle);
                HBA_FreeLibrary();
                return (ret);
        }

        assert(larg.listp != NULL);

        n = 0;
        ret = postprocess_list_data(larg.listp, cmd, larg.chld_config, &n,
                        flags);
        if (ret != FPCFGA_OK) {
                cfga_err(errstring, 0, ERR_LIST, 0);
                list_free(&larg.listp);
                S_FREE(larg.xport_logp);
                S_FREE(larg.apidp->xport_phys);
                S_FREE(larg.apidp->dyncomp);
                HBA_CloseAdapter(handle);
                HBA_FreeLibrary();
                return (FPCFGA_LIB_ERR);
        }

        *nelemp = n;
        *llpp = larg.listp;
        ret = FPCFGA_OK;
        S_FREE(larg.xport_logp);
        S_FREE(larg.apidp->xport_phys);
        S_FREE(larg.apidp->dyncomp);
        HBA_CloseAdapter(handle);
        HBA_FreeLibrary();
        return (FPCFGA_OK);
}

/*
 * This routine returns initialize struct fcp_ioctl.
 */
static void
init_fcp_scsi_cmd(
        struct fcp_scsi_cmd *fscsi,
        uchar_t *lun_num,
        la_wwn_t *pwwn,
        void *scmdbuf,
        size_t scmdbuf_len,
        void *respbuf,
        size_t respbuf_len,
        void *sensebuf,
        size_t sensebuf_len)
{
        memset(fscsi, 0, sizeof (struct fcp_scsi_cmd));
        memset(scmdbuf, 0, scmdbuf_len);
        memcpy(fscsi->scsi_fc_pwwn.raw_wwn, pwwn, sizeof (u_longlong_t));
        fscsi->scsi_fc_rspcode = 0;
        fscsi->scsi_flags = FCP_SCSI_READ;
        fscsi->scsi_timeout = FCP_SCSI_CMD_TIMEOUT;  /* second */
        fscsi->scsi_cdbbufaddr = (caddr_t)scmdbuf;
        fscsi->scsi_cdblen = scmdbuf_len;
        fscsi->scsi_bufaddr = (caddr_t)respbuf;
        fscsi->scsi_buflen = respbuf_len;
        fscsi->scsi_bufresid = 0;
        fscsi->scsi_bufstatus = 0;
        fscsi->scsi_rqbufaddr = (caddr_t)sensebuf;
        fscsi->scsi_rqlen = sensebuf_len;
        fscsi->scsi_rqresid = 0;
        memcpy(&fscsi->scsi_lun, lun_num, sizeof (fscsi->scsi_lun));
}

/*
 * This routine returns issues FCP_TGT_SEND_SCSI
 */
static fpcfga_ret_t
issue_fcp_scsi_cmd(
        const char *xport_phys,
        struct fcp_scsi_cmd *fscsi,
        int *l_errnop)
{
        struct stat     stbuf;
        int fcp_fd, retry, rv;

        if (stat(xport_phys, &stbuf) < 0) {
                *l_errnop = errno;
                return (FPCFGA_LIB_ERR);
        }

        fscsi->scsi_fc_port_num = (uint32_t)minor(stbuf.st_rdev);
        fcp_fd = open(FCP_PATH, O_RDONLY | O_NDELAY);
        retry = 0;
        while (fcp_fd < 0 && retry++ < OPEN_RETRY_COUNT && (
                errno == EBUSY || errno == EAGAIN)) {
                (void) usleep(OPEN_RETRY_INTERVAL);
                fcp_fd = open(FCP_PATH, O_RDONLY|O_NDELAY);
        }
        if (fcp_fd < 0) {
                *l_errnop = errno;
                return (FPCFGA_LIB_ERR);
        }

        rv = ioctl(fcp_fd, FCP_TGT_SEND_SCSI, fscsi);
        retry = 0;
        while ((rv != 0 && retry++ < IOCTL_RETRY_COUNT &&
                        (errno == EBUSY || errno == EAGAIN)) ||
                        (retry++ < IOCTL_RETRY_COUNT &&
                        ((uchar_t)fscsi->scsi_bufstatus & STATUS_MASK)
                        == STATUS_BUSY)) {
                (void) usleep(IOCTL_RETRY_INTERVAL);
                rv = ioctl(fcp_fd, FCP_TGT_SEND_SCSI, fscsi);
        }
        close(fcp_fd);

        if (fscsi->scsi_fc_status == FC_DEVICE_NOT_TGT) {
                return (FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT);
        } else if (rv != 0 || fscsi->scsi_bufstatus != 0) {
                *l_errnop = errno;
                return (FPCFGA_FCP_TGT_SEND_SCSI_FAILED);
        }
        return (FPCFGA_OK);
}

/*
 * This routine returns standard inq data for
 * a target represented by dyncomp.
 *
 * Calls FCP passthru ioctl FCP_TGT_SEND_SCSI to get inquiry data.
 *
 * Caller should free the *inq_buf.
 */
static fpcfga_ret_t
get_standard_inq_data(
        const char *xport_phys,
        const char *dyncomp,
        uchar_t *lun_num,
        struct scsi_inquiry **inq_buf,
        int *l_errnop)
{
        struct fcp_scsi_cmd     fscsi;
        struct scsi_extended_sense sensebuf;
        union scsi_cdb  scsi_inq_req;
        la_wwn_t        pwwn;
        int     alloc_len;
        fpcfga_ret_t ret;


        alloc_len = sizeof (struct scsi_inquiry);
        if ((*inq_buf = (struct scsi_inquiry *)calloc(1, alloc_len)) == NULL) {
                *l_errnop = errno;
                return (FPCFGA_LIB_ERR);
        }

        if (cvt_dyncomp_to_lawwn(dyncomp, &pwwn) != 0) {
                return (FPCFGA_LIB_ERR);
        }

        init_fcp_scsi_cmd(&fscsi, lun_num, &pwwn, &scsi_inq_req,
                sizeof (scsi_inq_req), *inq_buf, alloc_len, &sensebuf,
                sizeof (struct scsi_extended_sense));
        scsi_inq_req.scc_cmd = SCMD_INQUIRY;
        scsi_inq_req.g0_count0 = sizeof (struct scsi_inquiry);

        if ((ret = issue_fcp_scsi_cmd(xport_phys, &fscsi, l_errnop))
                        != FPCFGA_OK) {
                S_FREE(*inq_buf);
                return (ret);
        }

        return (FPCFGA_OK);
}

/*
 * This routine returns report lun data and number of luns found
 * on a target represented by dyncomp.
 *
 * Calls FCP passthru ioctl FCP_TGT_SEND_SCSI to get report lun data.
 *
 * Caller should free the *resp_buf when FPCFGA_OK is returned.
 */
fpcfga_ret_t
get_report_lun_data(
        const char *xport_phys,
        const char *dyncomp,
        int *num_luns,
        report_lun_resp_t **resp_buf,
        struct scsi_extended_sense *sensebuf,
        int *l_errnop)
{
        struct fcp_scsi_cmd     fscsi;
        union scsi_cdb  scsi_rl_req;
        la_wwn_t        pwwn;
        int     alloc_len;
        fpcfga_ret_t    ret;
        uchar_t         lun_data[SAM_LUN_SIZE];

        alloc_len = sizeof (struct report_lun_resp);
        if ((*resp_buf = (report_lun_resp_t *)calloc(1, alloc_len)) == NULL) {
                *l_errnop = errno;
                return (FPCFGA_LIB_ERR);
        }

        if (cvt_dyncomp_to_lawwn(dyncomp, &pwwn) != 0) {
                S_FREE(*resp_buf);
                return (FPCFGA_LIB_ERR);
        }

        /* sending to LUN 0 so initializing lun_data buffer to be 0 */
        memset(lun_data, 0, sizeof (lun_data));
        init_fcp_scsi_cmd(&fscsi, lun_data, &pwwn, &scsi_rl_req,
            sizeof (scsi_rl_req), *resp_buf, alloc_len, sensebuf,
            sizeof (struct scsi_extended_sense));
        scsi_rl_req.scc_cmd = FP_SCMD_REPORT_LUN;
        FORMG5COUNT(&scsi_rl_req, alloc_len);

        if ((ret = issue_fcp_scsi_cmd(xport_phys, &fscsi, l_errnop))
                        != FPCFGA_OK) {
                S_FREE(*resp_buf);
                return (ret);
        }

        if (ntohl((*resp_buf)->num_lun) >
                (sizeof (struct report_lun_resp) - REPORT_LUN_HDR_SIZE)) {
                alloc_len = (*resp_buf)->num_lun + REPORT_LUN_HDR_SIZE;
                S_FREE(*resp_buf);
                if ((*resp_buf = (report_lun_resp_t *)calloc(1, alloc_len))
                    == NULL) {
                        *l_errnop = errno;
                        return (FPCFGA_LIB_ERR);
                }
                (void) memset((char *)*resp_buf, 0, alloc_len);
                FORMG5COUNT(&scsi_rl_req, alloc_len);

                fscsi.scsi_bufaddr = (caddr_t)*resp_buf;
                fscsi.scsi_buflen = alloc_len;

                if ((ret = issue_fcp_scsi_cmd(xport_phys, &fscsi, l_errnop))
                                != FPCFGA_OK) {
                        S_FREE(*resp_buf);
                        return (ret);
                }
        }

        /* num_lun represent number of luns * 8. */
        *num_luns = ntohl((*resp_buf)->num_lun) >> 3;

        return (FPCFGA_OK);
}

/*
 * Routine for consturct ldata list for each FCP SCSI LUN device
 * for a discovered target device.
 * It calls get_report_lun_data to get report lun data and
 * construct ldata list per each lun.
 *
 * It is called only when show_FCP_dev option is given.
 *
 * Overall algorithm:
 * Get the report lun data thru FCP passthru ioctl.
 * Call init_ldata_for_accessible_FCP_dev to process the report LUN data.
 * For each LUN found standard inquiry is issued to get device type.
 */
static fpcfga_ret_t
get_accessible_FCP_dev_ldata(
        const char *dyncomp,
        fpcfga_list_t *lap,
        int *l_errnop)
{
        report_lun_resp_t           *resp_buf;
        struct scsi_extended_sense  sense;
        int                         num_luns;
        fpcfga_ret_t                ret;

        memset(&sense, 0, sizeof (sense));
        if ((ret = get_report_lun_data(lap->apidp->xport_phys, dyncomp,
                &num_luns, &resp_buf, &sense, l_errnop)) != FPCFGA_OK) {
                /*
                 * when report lun data fails then return FPCFGA_OK thus
                 * keep the ldata for the target which is acquired previously.
                 * For remote hba node this will be normal.
                 * For a target error may already be detected through
                 * FCP_TGT_INQ.
                 */
                if ((ret == FPCFGA_FCP_TGT_SEND_SCSI_FAILED) ||
                    (ret == FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT)) {
                        ret = FPCFGA_OK;
                }
                return (ret);
        }

        if (num_luns > 0) {
                ret = init_ldata_for_accessible_FCP_dev(
                        dyncomp, num_luns, resp_buf, lap, l_errnop);
        } else {
                /*
                 * proceed with to stat if no lun found.
                 * This will make the target apid will be kept.
                 */
                ret = FPCFGA_OK;
        }

        S_FREE(resp_buf);
        return (ret);
}

/*
 * Routine for checking validity of ldata list based on input argumemnt.
 * Set the occupant state of hba port if the list is valid.
 */
static fpcfga_ret_t
postprocess_list_data(
        const ldata_list_t *listp,
        fpcfga_cmd_t cmd,
        cfga_stat_t chld_config,
        int *np,
        uint_t flags)
{
        ldata_list_t *tmplp = NULL;
        cfga_list_data_t *xport_ldatap = NULL;
        int i;


        *np = 0;

        if (listp == NULL) {
                return (FPCFGA_ERR);
        }

        tmplp = (ldata_list_t *)listp;
        for (i = 0; tmplp != NULL; tmplp = tmplp->next) {
                i++;
                if (GET_DYN(tmplp->ldata.ap_phys_id) == NULL) {
                        /* A bus stat data */
                        assert(GET_DYN(tmplp->ldata.ap_log_id) == NULL);
                        xport_ldatap = &tmplp->ldata;
#ifdef DEBUG
                } else {
                        assert(GET_DYN(tmplp->ldata.ap_log_id) != NULL);
#endif
                }
        }

        switch (cmd) {
        case FPCFGA_STAT_FC_DEV:
                if ((flags & FLAG_FCP_DEV) == FLAG_FCP_DEV) {
                        if (i < 1 || xport_ldatap != NULL) {
                                return (FPCFGA_LIB_ERR);
                        }
                } else {
                        if (i != 1 || xport_ldatap != NULL) {
                                return (FPCFGA_LIB_ERR);
                        }
                }
                break;
        case FPCFGA_STAT_FCA_PORT:
                if (i != 1 || xport_ldatap == NULL) {
                        return (FPCFGA_LIB_ERR);
                }
                break;
        case FPCFGA_STAT_ALL:
                if (i < 1 || xport_ldatap == NULL) {
                        return (FPCFGA_LIB_ERR);
                }
                break;
        default:
                return (FPCFGA_LIB_ERR);
        }

        *np = i;

        /* Fill in the occupant (child) state. */
        if (xport_ldatap != NULL) {
                xport_ldatap->ap_o_state = chld_config;
        }
        return (FPCFGA_OK);
}

/*
 * Routine for checking each target device found in device tree.
 * When the matching port WWN dev is found from the accessble ldata list
 * the target device is updated with configured ostate.
 *
 * Overall algorithm:
 * Parse the device tree to find configured devices which matches with
 * list argument.  If cmd is stat on a specific target device it
 * matches port WWN and continues to further processing.  If cmd is
 * stat on hba port all the device target under the hba are processed.
 */
static int
stat_fc_dev(di_node_t node, void *arg)
{
        fpcfga_list_t *lap = NULL;
        char *devfsp = NULL, *nodepath = NULL;
        size_t len = 0;
        int limited_stat = 0, match_minor, rv;
        fpcfga_ret_t ret;
        di_prop_t prop = DI_PROP_NIL;
        uchar_t *port_wwn_data;
        char    port_wwn[WWN_SIZE*2+1];
        int     count;

        lap = (fpcfga_list_t *)arg;

        /*
         * Skip partial nodes
         *
         * This checking is from the scsi plug-in and will be deleted for
         * fp plug-in. The node will be processed for fp even if it is
         * in driver detached state. From fp perspective the node is configured
         * as long as the node is not in offline or down state.
         * scsi plug-in considers the known state when it is offlined
         * regradless of driver detached state or when it is not in driver
         * detached state like normal state.
         * If the node is only in driver detached state it is considered as
         * unknown state.
         *
         * if (!known_state(node) && (lap->cmd != FPCFGA_STAT_FC_DEV)) {
         *      return (DI_WALK_CONTINUE);
         *
         */

        devfsp = di_devfs_path(node);
        if (devfsp == NULL) {
                rv = DI_WALK_CONTINUE;
                goto out;
        }

        len = strlen(DEVICES_DIR) + strlen(devfsp) + 1;

        nodepath = calloc(1, len);
        if (nodepath == NULL) {
                lap->l_errno = errno;
                lap->ret = FPCFGA_LIB_ERR;
                rv = DI_WALK_TERMINATE;
                goto out;
        }

        (void) snprintf(nodepath, len, "%s%s", DEVICES_DIR, devfsp);

        /* Skip node if it is HBA */
        match_minor = 0;
        if (!dev_cmp(lap->apidp->xport_phys, nodepath, match_minor)) {
                rv = DI_WALK_CONTINUE;
                goto out;
        }

        /* If stat'ing a specific device, is this node that device */
        if (lap->cmd == FPCFGA_STAT_FC_DEV) {
                /* checks port wwn property to find a match */
                while ((prop = di_prop_next(node, prop))
                                        != DI_PROP_NIL) {
                        if ((strcmp(PORT_WWN_PROP,
                                di_prop_name(prop)) == 0) &&
                                (di_prop_type(prop) ==
                                        DI_PROP_TYPE_BYTE)) {
                                break;
                        }
                }

                if (prop != DI_PROP_NIL) {
                        count = di_prop_bytes(prop, &port_wwn_data);
                        if (count != WWN_SIZE) {
                                lap->ret = FPCFGA_LIB_ERR;
                                rv = DI_WALK_TERMINATE;
                                goto out;
                        }
                        (void) sprintf(port_wwn, "%016llx",
                                (wwnConversion(port_wwn_data)));
                        /*
                         * port wwn doesn't match contine to walk
                         * if match call do_stat_fc_dev.
                         */
                        if (strncmp(port_wwn, lap->apidp->dyncomp,
                                        WWN_SIZE*2)) {
                                rv = DI_WALK_CONTINUE;
                                goto out;
                        }
                } else {
                        rv = DI_WALK_CONTINUE;
                        goto out;
                }
        }

        /*
         * If stat'ing a xport only, we look at device nodes only to get
         * xport configuration status. So a limited stat will suffice.
         */
        if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
                limited_stat = 1;
        } else {
                limited_stat = 0;
        }

        /*
         * Ignore errors if stat'ing a bus or listing all
         */
        ret = do_stat_fc_dev(node, nodepath, lap, limited_stat);
        if (ret != FPCFGA_OK) {
                if (lap->cmd == FPCFGA_STAT_FC_DEV) {
                        lap->ret = ret;
                        rv = DI_WALK_TERMINATE;
                } else {
                        rv = DI_WALK_CONTINUE;
                }
                goto out;
        }

        /* Are we done ? */
        rv = DI_WALK_CONTINUE;
        if (lap->cmd == FPCFGA_STAT_FCA_PORT &&
            lap->chld_config == CFGA_STAT_CONFIGURED) {
                rv = DI_WALK_TERMINATE;
        } else if (lap->cmd == FPCFGA_STAT_FC_DEV) {
                /*
                 * If stat'ing a specific device, we are done at this point.
                 */
                rv = DI_WALK_TERMINATE;
        }

        /*FALLTHRU*/
out:
        S_FREE(nodepath);
        if (devfsp != NULL) di_devfs_path_free(devfsp);
        return (rv);
}

/*
 * Routine for checking each FCP SCSI LUN device found in device tree.
 * When the matching port WWN and LUN are found from the accessble ldata list
 * the FCP SCSI LUN is updated with configured ostate.
 *
 * Overall algorithm:
 * Parse the device tree to find configured devices which matches with
 * list argument.  If cmd is stat on a specific target device it
 * matches port WWN and continues to further processing.  If cmd is
 * stat on hba port all the FCP SCSI LUN under the hba are processed.
 */
static int
stat_FCP_dev(di_node_t node, void *arg)
{
        fpcfga_list_t *lap = NULL;
        char *devfsp = NULL, *nodepath = NULL;
        size_t len = 0;
        int limited_stat = 0, match_minor, rv, di_ret;
        fpcfga_ret_t ret;
        uchar_t *port_wwn_data;
        char    port_wwn[WWN_SIZE*2+1];

        lap = (fpcfga_list_t *)arg;

        devfsp = di_devfs_path(node);
        if (devfsp == NULL) {
                rv = DI_WALK_CONTINUE;
                goto out;
        }

        len = strlen(DEVICES_DIR) + strlen(devfsp) + 1;

        nodepath = calloc(1, len);
        if (nodepath == NULL) {
                lap->l_errno = errno;
                lap->ret = FPCFGA_LIB_ERR;
                rv = DI_WALK_TERMINATE;
                goto out;
        }

        (void) snprintf(nodepath, len, "%s%s", DEVICES_DIR, devfsp);

        /* Skip node if it is HBA */
        match_minor = 0;
        if (!dev_cmp(lap->apidp->xport_phys, nodepath, match_minor)) {
                rv = DI_WALK_CONTINUE;
                goto out;
        }

        /* If stat'ing a specific device, is this node that device */
        if (lap->cmd == FPCFGA_STAT_FC_DEV) {
                /* checks port wwn property to find a match */
                di_ret = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
                        PORT_WWN_PROP, &port_wwn_data);
                if (di_ret == -1) {
                        rv = DI_WALK_CONTINUE;
                        goto out;
                } else {
                        (void) sprintf(port_wwn, "%016llx",
                                (wwnConversion(port_wwn_data)));
                        /*
                         * port wwn doesn't match contine to walk
                         * if match call do_stat_FCP_dev.
                         */
                        if (strncmp(port_wwn, lap->apidp->dyncomp,
                                        WWN_SIZE*2)) {
                                rv = DI_WALK_CONTINUE;
                                goto out;
                        }
                }
        }

        /*
         * If stat'ing a xport only, we look at device nodes only to get
         * xport configuration status. So a limited stat will suffice.
         */
        if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
                limited_stat = 1;
        } else {
                limited_stat = 0;
        }

        /*
         * Ignore errors if stat'ing a bus or listing all
         */
        ret = do_stat_FCP_dev(node, nodepath, lap, limited_stat);
        if (ret != FPCFGA_OK) {
                rv = DI_WALK_CONTINUE;
                goto out;
        }

        /* Are we done ? */
        rv = DI_WALK_CONTINUE;
        if (lap->cmd == FPCFGA_STAT_FCA_PORT &&
            lap->chld_config == CFGA_STAT_CONFIGURED) {
                rv = DI_WALK_TERMINATE;
        }

        /*FALLTHRU*/
out:
        S_FREE(nodepath);
        if (devfsp != NULL) di_devfs_path_free(devfsp);
        return (rv);
}

static fpcfga_ret_t
do_stat_fca_xport(fpcfga_list_t *lap, int limited_stat,
        HBA_PORTATTRIBUTES portAttrs)
{
        cfga_list_data_t *clp = NULL;
        ldata_list_t *listp = NULL;
        int l_errno = 0;
        uint_t devinfo_state = 0;
        walkarg_t walkarg;
        fpcfga_ret_t ret;
        cfga_cond_t cond = CFGA_COND_UNKNOWN;

        assert(lap->xport_logp != NULL);

        /* Get xport state */
        if (lap->apidp->flags == FLAG_DEVINFO_FORCE) {
                walkarg.flags = FLAG_DEVINFO_FORCE;
        } else {
                walkarg.flags = 0;
        }
        walkarg.walkmode.node_args.flags = 0;
        walkarg.walkmode.node_args.fcn = get_xport_state;

        ret = walk_tree(lap->apidp->xport_phys, &devinfo_state,
                DINFOCPYALL | DINFOPATH, &walkarg, FPCFGA_WALK_NODE, &l_errno);
        if (ret == FPCFGA_OK) {
                lap->xport_rstate = xport_devinfo_to_recep_state(devinfo_state);
        } else {
                lap->xport_rstate = CFGA_STAT_NONE;
        }

        /*
         * Get topology works okay even if the fp port is connected
         * to a switch and no devices connected to the switch.
         * In this case the list will only shows fp port info without
         * any device listed.
         */
        switch (portAttrs.PortType) {
                case HBA_PORTTYPE_NLPORT:
                        (void) snprintf(lap->xport_type,
                                sizeof (lap->xport_type), "%s",
                                FP_FC_PUBLIC_PORT_TYPE);
                        break;
                case HBA_PORTTYPE_NPORT:
                        (void) snprintf(lap->xport_type,
                                sizeof (lap->xport_type), "%s",
                                FP_FC_FABRIC_PORT_TYPE);
                        break;
                case HBA_PORTTYPE_LPORT:
                        (void) snprintf(lap->xport_type,
                                sizeof (lap->xport_type), "%s",
                                FP_FC_PRIVATE_PORT_TYPE);
                        break;
                case HBA_PORTTYPE_PTP:
                        (void) snprintf(lap->xport_type,
                                sizeof (lap->xport_type), "%s",
                                FP_FC_PT_TO_PT_PORT_TYPE);
                        break;
                /*
                 * HBA_PORTTYPE_UNKNOWN means nothing is connected
                 */
                case HBA_PORTTYPE_UNKNOWN:
                        (void) snprintf(lap->xport_type,
                                sizeof (lap->xport_type), "%s",
                                FP_FC_PORT_TYPE);
                        break;
                /* NOT_PRESENT, OTHER, FPORT, FLPORT */
                default:
                        (void) snprintf(lap->xport_type,
                                sizeof (lap->xport_type), "%s",
                                FP_FC_PORT_TYPE);
                        cond = CFGA_COND_FAILED;
                        break;
        }

        if (limited_stat) {
                /* We only want to know bus(receptacle) connect status */
                return (FPCFGA_OK);
        }

        listp = calloc(1, sizeof (ldata_list_t));
        if (listp == NULL) {
                lap->l_errno = errno;
                return (FPCFGA_LIB_ERR);
        }

        clp = &listp->ldata;

        (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s",
            lap->xport_logp);
        (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s",
            lap->apidp->xport_phys);

        clp->ap_class[0] = '\0';        /* Filled by libcfgadm */
        clp->ap_r_state = lap->xport_rstate;
        clp->ap_o_state = lap->chld_config;
        clp->ap_cond = cond;
        clp->ap_busy = 0;
        clp->ap_status_time = (time_t)-1;
        clp->ap_info[0] = '\0';
        (void) strncpy(clp->ap_type, lap->xport_type, sizeof (clp->ap_type));

        /* Link it in.  lap->listp is NULL originally. */
        listp->next = lap->listp;
        /* lap->listp now gets cfga_list_data for the fca port. */
        lap->listp = listp;

        return (FPCFGA_OK);
}


static int
get_xport_state(di_node_t node, void *arg)
{
        uint_t *di_statep = (uint_t *)arg;

        *di_statep = di_state(node);

        return (DI_WALK_TERMINATE);
}

/*
 * Routine for updating ldata list based on the state of device node.
 * When no matching accessible ldata is found a new ldata is created
 * with proper state information.
 *
 * Overall algorithm:
 * If the device node is online and the matching ldata is found
 * the target device is updated with configued and unknown condition.
 * If the device node is offline or down and the matching ldata is found
 * the target device is updated with configued and unusable condition.
 * If the device node is online but the matching ldata is not found
 * the target device is created with configued and failing condition.
 * If the device node is offline or down and the matching ldata is not found
 * the target device is created with configued and unusable condition.
 */
static fpcfga_ret_t
do_stat_fc_dev(
        const di_node_t node,
        const char *nodepath,
        fpcfga_list_t *lap,
        int limited_stat)
{
        uint_t dctl_state = 0, devinfo_state = 0;
        char *dyncomp = NULL;
        cfga_list_data_t *clp = NULL;
        cfga_busy_t busy;
        ldata_list_t *listp = NULL;
        ldata_list_t *matchldp = NULL;
        int l_errno = 0;
        cfga_stat_t ostate;
        cfga_cond_t cond;
        fpcfga_ret_t ret;

        assert(lap->apidp->xport_phys != NULL);
        assert(lap->xport_logp != NULL);

        cond = CFGA_COND_UNKNOWN;

        devinfo_state = di_state(node);
        ostate = dev_devinfo_to_occupant_state(devinfo_state);

        /*
         * NOTE: The framework cannot currently detect layered driver
         * opens, so the busy indicator is not very reliable. Also,
         * non-root users will not be able to determine busy
         * status (libdevice needs root permissions).
         * This should probably be fixed by adding a DI_BUSY to the di_state()
         * routine in libdevinfo.
         */
        if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE, &dctl_state,
            &l_errno) == FPCFGA_OK) {
                busy = ((dctl_state & DEVICE_BUSY) == DEVICE_BUSY) ? 1 : 0;
        } else {
                busy = 0;
        }

        /* We only want to know device config state */
        if (limited_stat) {
                if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
                        strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
                        lap->chld_config = CFGA_STAT_CONFIGURED;
                } else {
                        if (ostate != CFGA_STAT_UNCONFIGURED) {
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                        }
                }
                return (FPCFGA_OK);
        }

        /*
         * If child device is configured, see if it is accessible also
         * for FPCFGA_STAT_FC_DEV cmd.
         */
        if (lap->cmd == FPCFGA_STAT_FC_DEV) {
                switch (ostate) {
                case CFGA_STAT_CONFIGURED:
                        /*
                         * if configured and not accessble, the device is
                         * till be displayed with failing condition.
                         * return code should be FPCFGA_OK to display it.
                         */
                case CFGA_STAT_NONE:
                        /*
                         * If not unconfigured and not attached
                         * the state is set to CFGA_STAT_NONE currently.
                         * This is okay for the detached node due to
                         * the driver being unloaded.
                         * May need to define another state to
                         * isolate the detached only state.
                         *
                         * handle the same way as configured.
                         */
                        if (lap->ret != FPCFGA_ACCESS_OK) {
                                cond = CFGA_COND_FAILING;
                        }
                        lap->chld_config = CFGA_STAT_CONFIGURED;
                        break;
                case CFGA_STAT_UNCONFIGURED:
                        /*
                         * if unconfigured - offline or down,
                         * set to cond to unusable regardless of accessibility.
                         * This behavior needs to be examined further.
                         * When the device is not accessible the node
                         * may get offline or down. In that case failing
                         * cond may make more sense.
                         * In anycase the ostate will be set to configured
                         * configured.
                         */
                        cond = CFGA_COND_UNUSABLE;
                        /*
                         * For fabric port the fca port is considered as
                         * configured since user configured previously
                         * for any existing node.  Otherwise when the
                         * device was accessible, the hba is considered as
                         * configured.
                         */
                        if (((strcmp(lap->xport_type,
                                FP_FC_PUBLIC_PORT_TYPE) == 0) ||
                                (strcmp(lap->xport_type,
                                FP_FC_FABRIC_PORT_TYPE) == 0)) ||
                                (lap->ret == FPCFGA_ACCESS_OK)) {
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                        } else {
                                lap->ret = FPCFGA_APID_NOEXIST;
                                return (FPCFGA_OK);
                        }
                        break;
                default:
                        break;
                }

                /* if device found in disco ports, ldata already created. */
                if (lap->ret == FPCFGA_ACCESS_OK) {
                        /*
                         * if cond is not changed then don't update
                         * condition to keep the previous condition.
                         */
                        if (cond != CFGA_COND_UNKNOWN) {
                                lap->listp->ldata.ap_cond = cond;
                        }
                        lap->listp->ldata.ap_o_state = CFGA_STAT_CONFIGURED;
                        lap->listp->ldata.ap_busy = busy;
                        lap->ret = FPCFGA_OK;
                        return (FPCFGA_OK);
                }
        }

        /*
         * if cmd is stat all check ldata list
         * to see if the node exist on the dev list.  Otherwise create
         * the list element.
         */
        if (lap->cmd == FPCFGA_STAT_ALL) {
                if (lap->listp != NULL) {
                        if ((ret = make_dyncomp_from_dinode(node,
                                        &dyncomp, &l_errno)) != FPCFGA_OK) {
                                return (ret);
                        }
                        ret = is_dyn_ap_on_ldata_list(dyncomp, lap->listp,
                                        &matchldp, &l_errno);
                        switch (ret) {
                        case FPCFGA_ACCESS_OK:
                                /* node exists so set ostate to configured. */
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                                matchldp->ldata.ap_o_state =
                                        CFGA_STAT_CONFIGURED;
                                matchldp->ldata.ap_busy = busy;
                                clp = &matchldp->ldata;
                                switch (ostate) {
                                case CFGA_STAT_CONFIGURED:
                                /*
                                 * If not unconfigured and not attached
                                 * the state is set to CFGA_STAT_NONE currently.
                                 * This is okay for the detached node due to
                                 * the driver being unloaded.
                                 * May need to define another state to
                                 * isolate the detached only state.
                                 */
                                case CFGA_STAT_NONE:
                                        /* update ap_type and ap_info */
                                        get_hw_info(node, clp);
                                        break;
                                /*
                                 * node is offline or down.
                                 * set cond to unusable.
                                 */
                                case CFGA_STAT_UNCONFIGURED:
                                        /*
                                         * if cond is not unknown
                                         * we already set the cond from
                                         * a different node with the same
                                         * port WWN or initial probing
                                         * was failed so don't update again.
                                         */
                                        if (matchldp->ldata.ap_cond ==
                                                CFGA_COND_UNKNOWN) {
                                                matchldp->ldata.ap_cond =
                                                CFGA_COND_UNUSABLE;
                                        }
                                        break;
                                default:
                                        break;
                                }
                                /* node found in ldata list so just return. */
                                lap->ret = FPCFGA_OK;
                                S_FREE(dyncomp);
                                return (FPCFGA_OK);
                        case FPCFGA_LIB_ERR:
                                lap->l_errno = l_errno;
                                S_FREE(dyncomp);
                                return (ret);
                        case FPCFGA_APID_NOACCESS:
                                switch (ostate) {
                                /* node is attached but not in dev list */
                                case CFGA_STAT_CONFIGURED:
                                case CFGA_STAT_NONE:
                                        lap->chld_config = CFGA_STAT_CONFIGURED;
                                        cond = CFGA_COND_FAILING;
                                        break;
                                /*
                                 * node is offline or down.
                                 * set cond to unusable.
                                 */
                                case CFGA_STAT_UNCONFIGURED:
                                        /*
                                         * For fabric port the fca port is
                                         * considered as configured since user
                                         * configured previously for any
                                         * existing node.
                                         */
                                        cond = CFGA_COND_UNUSABLE;
                                        if ((strcmp(lap->xport_type,
                                                FP_FC_PUBLIC_PORT_TYPE) == 0) ||
                                                (strcmp(lap->xport_type,
                                                FP_FC_FABRIC_PORT_TYPE) == 0)) {
                                                lap->chld_config =
                                                CFGA_STAT_CONFIGURED;
                                        } else {
                                                lap->ret = FPCFGA_OK;
                                                S_FREE(dyncomp);
                                                return (FPCFGA_OK);
                                        }
                                        break;
                                default:
                                /*
                                 * continue to create ldata_list struct for
                                 * this node
                                 */
                                        break;
                                }
                        default:
                                break;
                        }
                } else {
                        /*
                         * dev_list is null so there is no accessible dev.
                         * set the cond and continue to create ldata.
                         */
                        switch (ostate) {
                        case CFGA_STAT_CONFIGURED:
                        case CFGA_STAT_NONE:
                                cond = CFGA_COND_FAILING;
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                                break;
                        /*
                         * node is offline or down.
                         * set cond to unusable.
                         */
                        case CFGA_STAT_UNCONFIGURED:
                                cond = CFGA_COND_UNUSABLE;
                                /*
                                 * For fabric port the fca port is
                                 * considered as configured since user
                                 * configured previously for any
                                 * existing node.
                                 */
                                if ((strcmp(lap->xport_type,
                                        FP_FC_PUBLIC_PORT_TYPE) == 0) ||
                                        (strcmp(lap->xport_type,
                                        FP_FC_FABRIC_PORT_TYPE) == 0)) {
                                        lap->chld_config =
                                        CFGA_STAT_CONFIGURED;
                                } else {
                                        lap->ret = FPCFGA_OK;
                                        S_FREE(dyncomp);
                                        return (FPCFGA_OK);
                                }
                                break;
                        default:
                                break;
                        }
                }
        }

        listp = calloc(1, sizeof (ldata_list_t));
        if (listp == NULL) {
                lap->l_errno = errno;
                S_FREE(dyncomp);
                return (FPCFGA_LIB_ERR);
        }

        clp = &listp->ldata;

        /* Create the dynamic component. */
        if (dyncomp == NULL) {
                ret = make_dyncomp_from_dinode(node, &dyncomp, &l_errno);
                if (ret != FPCFGA_OK) {
                        S_FREE(listp);
                        return (ret);
                }
        }

        /* Create logical and physical ap_id */
        (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
            lap->xport_logp, DYN_SEP, dyncomp);

        (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
            lap->apidp->xport_phys, DYN_SEP, dyncomp);

        S_FREE(dyncomp);

        clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
        clp->ap_r_state = lap->xport_rstate;
        /* set to ostate to configured and set cond with info. */
        clp->ap_o_state = CFGA_STAT_CONFIGURED;
        clp->ap_cond = cond;
        clp->ap_busy = busy;
        clp->ap_status_time = (time_t)-1;

        /* get ap_type and ap_info. */
        get_hw_info(node, clp);

        /* Link it in */
        listp->next = lap->listp;
        lap->listp = listp;

        lap->ret = FPCFGA_OK;
        return (FPCFGA_OK);
}

/*
 * Wrapper routine for handling path info.
 *
 * When show_FCP_dev option is given stat_path_info_FCP_dev() is called.
 * Otherwise stat_path_info_fc_dev() is called.
 */
int
stat_path_info_node(
        di_node_t       root,
        void            *arg,
        int             *l_errnop)
{
        fpcfga_list_t *lap = NULL;

        lap = (fpcfga_list_t *)arg;
        if ((lap->apidp->flags & (FLAG_FCP_DEV)) == FLAG_FCP_DEV) {
                return (stat_path_info_FCP_dev(root, lap, l_errnop));
        } else {
                return (stat_path_info_fc_dev(root, lap, l_errnop));
        }
}

/*
 * Routine for updating ldata list based on the state of path info node.
 * When no matching accessible ldata is found a new ldata is created
 * with proper state information.
 *
 * Overall algorithm:
 * If the path info node is not offline and the matching ldata is found
 * the target device is updated with configued and unknown condition.
 * If the path info node is offline or failed and the matching ldata is found
 * the target device is updated with configued and unusable condition.
 * If the path info node is online but the matching ldata is not found
 * the target device is created with configued and failing condition.
 * If the path info is offline or failed and the matching ldata is not found
 * the target device is created with configued and unusable condition.
 */
static int
stat_path_info_fc_dev(
        di_node_t       root,
        fpcfga_list_t   *lap,
        int             *l_errnop)
{
        ldata_list_t *matchldp = NULL;
        di_path_t path = DI_PATH_NIL;
        uchar_t         *port_wwn_data;
        char            port_wwn[WWN_SIZE*2+1];
        int             count;
        fpcfga_ret_t    ret;
        di_path_state_t pstate;

        if (root == DI_NODE_NIL) {
                return (FPCFGA_LIB_ERR);
        }

        /*
         * if stat on a specific dev and walk_node found it okay
         * then just return ok.
         */
        if ((lap->cmd == FPCFGA_STAT_FC_DEV) && (lap->ret == FPCFGA_OK)) {
                return (FPCFGA_OK);
        }

        /*
         * if stat on a fca xport and chld_config is set
         * then just return ok.
         */
        if ((lap->cmd == FPCFGA_STAT_FCA_PORT) &&
                                (lap->chld_config == CFGA_STAT_CONFIGURED)) {
                return (FPCFGA_OK);
        }

        /*
         * when there is no path_info node return FPCFGA_OK.
         * That way the result from walk_node shall be maintained.
         */
        if ((path = di_path_next_client(root, path)) == DI_PATH_NIL) {
                /*
                 * if the dev was in dev list but not found
                 * return OK to indicate is not configured.
                 */
                if (lap->ret == FPCFGA_ACCESS_OK) {
                        lap->ret = FPCFGA_OK;
                }
                return (FPCFGA_OK);
        }

        /* if stat on fca port return. */
        if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
                if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
                        strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
                        lap->chld_config = CFGA_STAT_CONFIGURED;
                        return (FPCFGA_OK);
                } else {
                        if ((pstate = di_path_state(path)) !=
                                DI_PATH_STATE_OFFLINE) {
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                                return (FPCFGA_OK);
                        }
                }
        }
        /*
         * now parse the path info node.
         */
        do {
                count = di_path_prop_lookup_bytes(path, PORT_WWN_PROP,
                        &port_wwn_data);
                if (count != WWN_SIZE) {
                        ret = FPCFGA_LIB_ERR;
                        break;
                }

                (void) sprintf(port_wwn, "%016llx",
                        (wwnConversion(port_wwn_data)));
                switch (lap->cmd) {
                case FPCFGA_STAT_FC_DEV:
                        /* if no match contine to the next path info node. */
                        if (strncmp(port_wwn, lap->apidp->dyncomp,
                                        WWN_SIZE*2)) {
                                break;
                        }
                        /* if device in dev_list, ldata already created. */
                        if (lap->ret == FPCFGA_ACCESS_OK) {
                                lap->listp->ldata.ap_o_state =
                                        CFGA_STAT_CONFIGURED;
                                if (((pstate = di_path_state(path)) ==
                                        DI_PATH_STATE_OFFLINE) ||
                                        (pstate == DI_PATH_STATE_FAULT)) {
                                        lap->listp->ldata.ap_cond =
                                                        CFGA_COND_UNUSABLE;
                                }
                                lap->ret = FPCFGA_OK;
                                return (FPCFGA_OK);
                        } else {
                                if ((strcmp(lap->xport_type,
                                        FP_FC_PUBLIC_PORT_TYPE) == 0) ||
                                        (strcmp(lap->xport_type,
                                        FP_FC_FABRIC_PORT_TYPE) == 0)) {
                                        lap->chld_config = CFGA_STAT_CONFIGURED;
                                        return (init_ldata_for_mpath_dev(
                                                path, port_wwn, l_errnop, lap));
                                } else {
                                        if ((di_path_state(path)) !=
                                                DI_PATH_STATE_OFFLINE) {
                                            return (init_ldata_for_mpath_dev(
                                                path, port_wwn, l_errnop, lap));
                                        } else {
                                            lap->ret = FPCFGA_APID_NOEXIST;
                                            return (FPCFGA_OK);
                                        }
                                }
                        }
                case FPCFGA_STAT_ALL:
                        /* check if there is list data. */
                        if (lap->listp != NULL) {
                                ret = is_dyn_ap_on_ldata_list(port_wwn,
                                        lap->listp, &matchldp, l_errnop);
                                if (ret == FPCFGA_ACCESS_OK) {
                                        lap->chld_config = CFGA_STAT_CONFIGURED;
                                        matchldp->ldata.ap_o_state =
                                                        CFGA_STAT_CONFIGURED;
                                        /*
                                         * Update the condition as unusable
                                         * if the pathinfo state is failed
                                         * or offline.
                                         */
                                        if (((pstate = di_path_state(path)) ==
                                                DI_PATH_STATE_OFFLINE) ||
                                                (pstate ==
                                                        DI_PATH_STATE_FAULT)) {
                                                matchldp->ldata.ap_cond =
                                                        CFGA_COND_UNUSABLE;
                                        }
                                        break;
                                } else if (ret == FPCFGA_LIB_ERR) {
                                        lap->l_errno = *l_errnop;
                                        return (ret);
                                }
                        }
                        /*
                         * now create ldata for this particular path info node.
                         * if port top is private loop and pathinfo is in
                         * in offline state don't include to ldata list.
                         */
                        if (((strcmp(lap->xport_type,
                                FP_FC_PUBLIC_PORT_TYPE) == 0) ||
                                (strcmp(lap->xport_type,
                                        FP_FC_FABRIC_PORT_TYPE) == 0)) ||
                                (di_path_state(path) !=
                                        DI_PATH_STATE_OFFLINE)) {
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                                ret = init_ldata_for_mpath_dev(
                                        path, port_wwn, l_errnop, lap);
                                if (ret != FPCFGA_OK) {
                                        return (ret);
                                }
                        }
                        break;
                case FPCFGA_STAT_FCA_PORT:
                        if (di_path_state(path) != DI_PATH_STATE_OFFLINE) {
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                                return (FPCFGA_OK);
                        }
                }
                path = di_path_next_client(root, path);
        } while (path != DI_PATH_NIL);

        return (FPCFGA_OK);

}

/*
 * Routine for updating ldata list based on the state of path info node.
 * When no matching accessible ldata is found a new ldata is created
 * with proper state information.
 *
 * The difference from stat_path_info_fc_dev() is
 * to handle FCP SCSI LUN information. Otherwise overall algorithm is
 * same.
 *
 * Overall algorithm:
 * If the path info node is not offline and the matching ldata is found
 * the target device is updated with configued and unknown condition.
 * If the path info node is offline or failed and the matching ldata is found
 * the target device is updated with configued and unusable condition.
 * If the path info node is online but the matching ldata is not found
 * the target device is created with configued and failing condition.
 * If the path info is offline or failed and the matching ldata is not found
 * the target device is created with configued and unusable condition.
 */
static int
stat_path_info_FCP_dev(
        di_node_t       root,
        fpcfga_list_t   *lap,
        int             *l_errnop)
{
        ldata_list_t    *matchldp = NULL, *listp = NULL;
        cfga_list_data_t        *clp;
        di_path_t       path = DI_PATH_NIL;
        di_node_t       client_node = DI_NODE_NIL;
        char            *port_wwn = NULL, *nodepath = NULL;
        int             *lun_nump;
        fpcfga_ret_t    ldata_ret;
        di_path_state_t pstate;
        cfga_busy_t     busy;
        uint_t          dctl_state = 0;

        if (root == DI_NODE_NIL) {
                return (FPCFGA_LIB_ERR);
        }

        /*
         * if stat on a fca xport and chld_config is set
         * then just return ok.
         */
        if ((lap->cmd == FPCFGA_STAT_FCA_PORT) &&
                                (lap->chld_config == CFGA_STAT_CONFIGURED)) {
                return (FPCFGA_OK);
        }
        /*
         * when there is no path_info node return FPCFGA_OK.
         * That way the result from walk_node shall be maintained.
         */
        if ((path = di_path_next_client(root, path)) == DI_PATH_NIL) {
                /*
                 * if the dev was in dev list but not found
                 * return ok.
                 */
                if (lap->ret == FPCFGA_ACCESS_OK) {
                        lap->ret = FPCFGA_OK;
                }
                return (FPCFGA_OK);
        }
        /*
         * If stat on fca port and port topology is fabric return here.
         * If not fabric return only when path state is not offfline.
         * The other cases are handbled below.
         */
        if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
                if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
                        strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
                        lap->chld_config = CFGA_STAT_CONFIGURED;
                        return (FPCFGA_OK);
                } else {
                        if ((pstate = di_path_state(path)) !=
                                DI_PATH_STATE_OFFLINE) {
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                                return (FPCFGA_OK);
                        }
                }
        }
        /*
         * now parse the path info node.
         */
        do {
                switch (lap->cmd) {
                case FPCFGA_STAT_FC_DEV:
                        if ((make_portwwn_luncomp_from_pinode(path, &port_wwn,
                                &lun_nump, l_errnop)) != FPCFGA_OK) {
                                return (FPCFGA_LIB_ERR);
                        }

                        if ((ldata_ret = is_FCP_dev_ap_on_ldata_list(port_wwn,
                                *lun_nump, lap->listp, &matchldp))
                                == FPCFGA_LIB_ERR) {
                                S_FREE(port_wwn);
                                return (ldata_ret);
                        }

                        if (ldata_ret == FPCFGA_ACCESS_OK) {
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                                matchldp->ldata.ap_o_state =
                                                CFGA_STAT_CONFIGURED;
                                /*
                                 * Update the condition as unusable
                                 * if the pathinfo state is failed
                                 * or offline.
                                 */
                                if (((pstate = di_path_state(path)) ==
                                        DI_PATH_STATE_OFFLINE) ||
                                        (pstate == DI_PATH_STATE_FAULT)) {
                                        matchldp->ldata.ap_cond =
                                                        CFGA_COND_UNUSABLE;
                                }
                                lap->ret = FPCFGA_OK;
                                break;
                        }

                        if (strncmp(port_wwn, lap->apidp->dyncomp, WWN_SIZE*2)
                                        != 0) {
                                break;
                        }
                        /*
                         * now create ldata for this particular path info node.
                         * if port top is private loop and pathinfo is in
                         * in offline state don't include to ldata list.
                         */
                        if (((strcmp(lap->xport_type,
                                FP_FC_PUBLIC_PORT_TYPE) == 0) ||
                                (strcmp(lap->xport_type,
                                        FP_FC_FABRIC_PORT_TYPE) == 0)) ||
                                (di_path_state(path) !=
                                        DI_PATH_STATE_OFFLINE)) {
                            lap->chld_config = CFGA_STAT_CONFIGURED;
                                /* create ldata for this pi node. */
                            client_node = di_path_client_node(path);
                            if (client_node == DI_NODE_NIL) {
                                *l_errnop = errno;
                                S_FREE(port_wwn);
                                return (FPCFGA_LIB_ERR);
                            }
                            if ((construct_nodepath_from_dinode(
                                client_node, &nodepath, l_errnop))
                                        != FPCFGA_OK) {
                                S_FREE(port_wwn);
                                return (FPCFGA_LIB_ERR);
                            }

                            listp = calloc(1, sizeof (ldata_list_t));
                            if (listp == NULL) {
                                S_FREE(port_wwn);
                                S_FREE(nodepath);
                                lap->l_errno = errno;
                                return (FPCFGA_LIB_ERR);
                            }

                            clp = &listp->ldata;

                            /* Create logical and physical ap_id */
                            (void) snprintf(clp->ap_log_id,
                                sizeof (clp->ap_log_id), "%s%s%s%s%d",
                                lap->xport_logp, DYN_SEP, port_wwn,
                                LUN_COMP_SEP, *lun_nump);
                            (void) snprintf(clp->ap_phys_id,
                                sizeof (clp->ap_phys_id), "%s%s%s%s%d",
                                lap->apidp->xport_phys, DYN_SEP, port_wwn,
                                LUN_COMP_SEP, *lun_nump);
                                /*
                                 * We reached here since FCP dev is not found
                                 * in ldata list but path info node exists.
                                 *
                                 * Update the condition as failing
                                 * if the pathinfo state was normal.
                                 * Update the condition as unusable
                                 * if the pathinfo state is failed
                                 * or offline.
                                 */
                            clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
                            clp->ap_o_state = CFGA_STAT_CONFIGURED;
                            if (((pstate = di_path_state(path))
                                        == DI_PATH_STATE_OFFLINE) ||
                                (pstate == DI_PATH_STATE_FAULT)) {
                                clp->ap_cond = CFGA_COND_UNUSABLE;
                            } else {
                                clp->ap_cond = CFGA_COND_FAILING;
                            }
                            clp->ap_r_state = lap->xport_rstate;
                            clp->ap_info[0] = '\0';
                                /* update ap_type and ap_info */
                            get_hw_info(client_node, clp);
                            if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE,
                                &dctl_state, l_errnop) == FPCFGA_OK) {
                                busy = ((dctl_state & DEVICE_BUSY)
                                        == DEVICE_BUSY) ? 1 : 0;
                            } else {
                                busy = 0;
                            }
                            clp->ap_busy = busy;
                            clp->ap_status_time = (time_t)-1;

                            (void) insert_ldata_to_ldatalist(port_wwn,
                                lun_nump, listp, &(lap->listp));
                        }
                        break;
                case FPCFGA_STAT_ALL:
                        if ((make_portwwn_luncomp_from_pinode(path, &port_wwn,
                                &lun_nump, l_errnop)) != FPCFGA_OK) {
                                return (FPCFGA_LIB_ERR);
                        }

                        if ((ldata_ret = is_FCP_dev_ap_on_ldata_list(port_wwn,
                                *lun_nump, lap->listp, &matchldp))
                                == FPCFGA_LIB_ERR) {
                                S_FREE(port_wwn);
                                return (ldata_ret);
                        }

                        if (ldata_ret == FPCFGA_ACCESS_OK) {
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                                matchldp->ldata.ap_o_state =
                                                CFGA_STAT_CONFIGURED;
                                /*
                                 * Update the condition as unusable
                                 * if the pathinfo state is failed
                                 * or offline.
                                 */
                                if (((pstate = di_path_state(path)) ==
                                        DI_PATH_STATE_OFFLINE) ||
                                        (pstate == DI_PATH_STATE_FAULT)) {
                                        matchldp->ldata.ap_cond =
                                                        CFGA_COND_UNUSABLE;
                                }
                                break;
                        }
                        /*
                         * now create ldata for this particular path info node.
                         * if port top is private loop and pathinfo is in
                         * in offline state don't include to ldata list.
                         */
                        if (((strcmp(lap->xport_type,
                                FP_FC_PUBLIC_PORT_TYPE) == 0) ||
                                (strcmp(lap->xport_type,
                                        FP_FC_FABRIC_PORT_TYPE) == 0)) ||
                                (di_path_state(path) !=
                                        DI_PATH_STATE_OFFLINE)) {
                            lap->chld_config = CFGA_STAT_CONFIGURED;
                                /* create ldata for this pi node. */
                            client_node = di_path_client_node(path);
                            if (client_node == DI_NODE_NIL) {
                                *l_errnop = errno;
                                S_FREE(port_wwn);
                                return (FPCFGA_LIB_ERR);
                            }
                            if ((construct_nodepath_from_dinode(
                                client_node, &nodepath, l_errnop))
                                        != FPCFGA_OK) {
                                S_FREE(port_wwn);
                                return (FPCFGA_LIB_ERR);
                            }

                            listp = calloc(1, sizeof (ldata_list_t));
                            if (listp == NULL) {
                                S_FREE(port_wwn);
                                S_FREE(nodepath);
                                lap->l_errno = errno;
                                return (FPCFGA_LIB_ERR);
                            }

                            clp = &listp->ldata;

                            /* Create logical and physical ap_id */
                            (void) snprintf(clp->ap_log_id,
                                sizeof (clp->ap_log_id), "%s%s%s%s%d",
                                lap->xport_logp, DYN_SEP, port_wwn,
                                LUN_COMP_SEP, *lun_nump);
                            (void) snprintf(clp->ap_phys_id,
                                sizeof (clp->ap_phys_id), "%s%s%s%s%d",
                                lap->apidp->xport_phys, DYN_SEP, port_wwn,
                                LUN_COMP_SEP, *lun_nump);
                                /*
                                 * We reached here since FCP dev is not found
                                 * in ldata list but path info node exists.
                                 *
                                 * Update the condition as failing
                                 * if the pathinfo state was normal.
                                 * Update the condition as unusable
                                 * if the pathinfo state is failed
                                 * or offline.
                                 */
                            clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
                            clp->ap_o_state = CFGA_STAT_CONFIGURED;
                            if (((pstate = di_path_state(path))
                                        == DI_PATH_STATE_OFFLINE) ||
                                (pstate == DI_PATH_STATE_FAULT)) {
                                clp->ap_cond = CFGA_COND_UNUSABLE;
                            } else {
                                clp->ap_cond = CFGA_COND_FAILING;
                            }
                            clp->ap_r_state = lap->xport_rstate;
                            clp->ap_info[0] = '\0';
                                /* update ap_type and ap_info */
                            get_hw_info(client_node, clp);
                            if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE,
                                &dctl_state, l_errnop) == FPCFGA_OK) {
                                busy = ((dctl_state & DEVICE_BUSY)
                                        == DEVICE_BUSY) ? 1 : 0;
                            } else {
                                busy = 0;
                            }
                            clp->ap_busy = busy;
                            clp->ap_status_time = (time_t)-1;

                            (void) insert_ldata_to_ldatalist(port_wwn,
                                lun_nump, listp, &(lap->listp));
                        }
                        break;
                case FPCFGA_STAT_FCA_PORT:
                        if (di_path_state(path) != DI_PATH_STATE_OFFLINE) {
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                                lap->ret = FPCFGA_OK;
                                return (FPCFGA_OK);
                        }
                }
                path = di_path_next_client(root, path);
        } while (path != DI_PATH_NIL);

        lap->ret = FPCFGA_OK;
        S_FREE(port_wwn);
        S_FREE(nodepath);
        return (FPCFGA_OK);

}

/*
 * Routine for updating ldata list based on the state of device node.
 * When no matching accessible ldata is found a new ldata is created
 * with proper state information.
 *
 * The difference from do_stat_fc_dev() is
 * to handle FCP SCSI LUN information. Otherwise overall algorithm is
 * same.
 *
 * Overall algorithm:
 * If the device node is online and the matching ldata is found
 * the target device is updated with configued and unknown condition.
 * If the device node is offline or down and the matching ldata is found
 * the target device is updated with configued and unusable condition.
 * If the device node is online but the matching ldata is not found
 * the target device is created with configued and failing condition.
 * If the device node is offline or down and the matching ldata is not found
 * the target device is created with configued and unusable condition.
 */
static fpcfga_ret_t
do_stat_FCP_dev(
        const di_node_t node,
        const char *nodepath,
        fpcfga_list_t *lap,
        int limited_stat)
{
        uint_t dctl_state = 0, devinfo_state = 0;
        char *port_wwn = NULL;
        cfga_list_data_t *clp = NULL;
        cfga_busy_t busy;
        ldata_list_t *listp = NULL;
        ldata_list_t *matchldp = NULL;
        int l_errno = 0, *lun_nump;
        cfga_stat_t ostate;
        cfga_cond_t cond;
        fpcfga_ret_t ldata_ret;

        assert(lap->apidp->xport_phys != NULL);
        assert(lap->xport_logp != NULL);

        cond = CFGA_COND_UNKNOWN;

        devinfo_state = di_state(node);
        ostate = dev_devinfo_to_occupant_state(devinfo_state);

        /*
         * NOTE: The devctl framework cannot currently detect layered driver
         * opens, so the busy indicator is not very reliable. Also,
         * non-root users will not be able to determine busy
         * status (libdevice needs root permissions).
         * This should probably be fixed by adding a DI_BUSY to the di_state()
         * routine in libdevinfo.
         */
        if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE, &dctl_state,
            &l_errno) == FPCFGA_OK) {
                busy = ((dctl_state & DEVICE_BUSY) == DEVICE_BUSY) ? 1 : 0;
        } else {
                busy = 0;
        }

        /* We only want to know device config state */
        if (limited_stat) {
                if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
                        strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
                        lap->chld_config = CFGA_STAT_CONFIGURED;
                } else {
                        if (ostate != CFGA_STAT_UNCONFIGURED) {
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                        }
                }
                return (FPCFGA_OK);
        }

        /*
         * If child device is configured, see if it is accessible also
         * for FPCFGA_STAT_FC_DEV cmd.
         */
        if ((make_portwwn_luncomp_from_dinode(node, &port_wwn, &lun_nump,
                        &l_errno)) != FPCFGA_OK) {
                lap->l_errno = l_errno;
                return (FPCFGA_LIB_ERR);
        }

        if ((ldata_ret = is_FCP_dev_ap_on_ldata_list(port_wwn, *lun_nump,
                        lap->listp, &matchldp)) == FPCFGA_LIB_ERR) {
                lap->l_errno = l_errno;
                S_FREE(port_wwn);
                return (ldata_ret);
        }

        if (lap->cmd == FPCFGA_STAT_FC_DEV) {
                switch (ostate) {
                case CFGA_STAT_CONFIGURED:
                        /*
                         * if configured and not accessble, the device is
                         * till be displayed with failing condition.
                         * return code should be FPCFGA_OK to display it.
                         */
                case CFGA_STAT_NONE:
                        /*
                         * If not unconfigured and not attached
                         * the state is set to CFGA_STAT_NONE currently.
                         * This is okay for the detached node due to
                         * the driver being unloaded.
                         * May need to define another state to
                         * isolate the detached only state.
                         *
                         * handle the same way as configured.
                         */
                        if (ldata_ret != FPCFGA_ACCESS_OK) {
                                cond = CFGA_COND_FAILING;
                        }
                        lap->chld_config = CFGA_STAT_CONFIGURED;
                        break;
                case CFGA_STAT_UNCONFIGURED:
                        /*
                         * if unconfigured - offline or down,
                         * set to cond to unusable regardless of accessibility.
                         * This behavior needs to be examined further.
                         * When the device is not accessible the node
                         * may get offline or down. In that case failing
                         * cond may make more sense.
                         * In anycase the ostate will be set to configured
                         * configured.
                         */
                        cond = CFGA_COND_UNUSABLE;
                        /*
                         * For fabric port the fca port is considered as
                         * configured since user configured previously
                         * for any existing node.  Otherwise when the
                         * device was accessible, the hba is considered as
                         * configured.
                         */
                        if (((strcmp(lap->xport_type,
                                FP_FC_PUBLIC_PORT_TYPE) == 0) ||
                                (strcmp(lap->xport_type,
                                FP_FC_FABRIC_PORT_TYPE) == 0)) ||
                                (lap->ret == FPCFGA_ACCESS_OK)) {
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                        } else {
                                /*
                                 * if lap->ret is okay there is at least
                                 * one matching ldata exist.  Need to keep
                                 * okay ret to display the matching ones.
                                 */
                                if (lap->ret != FPCFGA_OK) {
                                        lap->ret = FPCFGA_APID_NOEXIST;
                                }
                                S_FREE(port_wwn);
                                return (FPCFGA_OK);
                        }
                        break;
                default:
                        break;
                }

                /* if device found in dev_list, ldata already created. */
                if (ldata_ret == FPCFGA_ACCESS_OK) {
                        /*
                         * if cond is not changed then don't update
                         * condition to keep any condtion
                         * from initial discovery. If the initial
                         * cond was failed the same condition will be kept.
                         */
                        if (cond != CFGA_COND_UNKNOWN) {
                                matchldp->ldata.ap_cond = cond;
                        }
                        matchldp->ldata.ap_o_state = CFGA_STAT_CONFIGURED;
                        matchldp->ldata.ap_busy = busy;
                        /* update ap_info via inquiry */
                        clp = &matchldp->ldata;
                        /* update ap_type and ap_info */
                        get_hw_info(node, clp);
                        lap->ret = FPCFGA_OK;
                        S_FREE(port_wwn);
                        return (FPCFGA_OK);
                }
        }

        /*
         * if cmd is stat all check ldata list
         * to see if the node exist on the dev list.  Otherwise create
         * the list element.
         */
        if (lap->cmd == FPCFGA_STAT_ALL) {
                switch (ldata_ret) {
                case FPCFGA_ACCESS_OK:
                        /* node exists so set ostate to configured. */
                        lap->chld_config = CFGA_STAT_CONFIGURED;
                        matchldp->ldata.ap_o_state =
                                CFGA_STAT_CONFIGURED;
                        matchldp->ldata.ap_busy = busy;
                        clp = &matchldp->ldata;
                        switch (ostate) {
                        case CFGA_STAT_CONFIGURED:
                        /*
                         * If not unconfigured and not attached
                         * the state is set to CFGA_STAT_NONE currently.
                         * This is okay for the detached node due to
                         * the driver being unloaded.
                         * May need to define another state to
                         * isolate the detached only state.
                         */
                        case CFGA_STAT_NONE:
                                /* update ap_type and ap_info */
                                get_hw_info(node, clp);
                                break;
                        /*
                         * node is offline or down.
                         * set cond to unusable.
                         */
                        case CFGA_STAT_UNCONFIGURED:
                                /*
                                 * if cond is not unknown
                                 * initial probing was failed
                                 * so don't update again.
                                 */
                                if (matchldp->ldata.ap_cond ==
                                        CFGA_COND_UNKNOWN) {
                                        matchldp->ldata.ap_cond =
                                        CFGA_COND_UNUSABLE;
                                }
                                break;
                        default:
                                break;
                        }
                        /* node found in ldata list so just return. */
                        lap->ret = FPCFGA_OK;
                        S_FREE(port_wwn);
                        return (FPCFGA_OK);
                case FPCFGA_APID_NOACCESS:
                        switch (ostate) {
                        /* node is attached but not in dev list */
                        case CFGA_STAT_CONFIGURED:
                        case CFGA_STAT_NONE:
                                lap->chld_config = CFGA_STAT_CONFIGURED;
                                cond = CFGA_COND_FAILING;
                                break;
                        /*
                         * node is offline or down.
                         * set cond to unusable.
                         */
                        case CFGA_STAT_UNCONFIGURED:
                                /*
                                 * For fabric port the fca port is
                                 * considered as configured since user
                                 * configured previously for any
                                 * existing node.
                                 */
                                cond = CFGA_COND_UNUSABLE;
                                if ((strcmp(lap->xport_type,
                                        FP_FC_PUBLIC_PORT_TYPE) == 0) ||
                                        (strcmp(lap->xport_type,
                                        FP_FC_FABRIC_PORT_TYPE) == 0)) {
                                        lap->chld_config =
                                        CFGA_STAT_CONFIGURED;
                                } else {
                                        lap->ret = FPCFGA_OK;
                                        S_FREE(port_wwn);
                                        return (FPCFGA_OK);
                                }
                                break;
                        default:
                        /*
                         * continue to create ldata_list struct for
                         * this node
                         */
                                break;
                        }
                default:
                        break;
                }
        }

        listp = calloc(1, sizeof (ldata_list_t));
        if (listp == NULL) {
                lap->l_errno = errno;
                S_FREE(port_wwn);
                return (FPCFGA_LIB_ERR);
        }

        clp = &listp->ldata;

        /* Create logical and physical ap_id */
        (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id),
                "%s%s%s%s%d", lap->xport_logp, DYN_SEP, port_wwn,
                LUN_COMP_SEP, *lun_nump);
        (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id),
                "%s%s%s%s%d", lap->apidp->xport_phys, DYN_SEP, port_wwn,
                LUN_COMP_SEP, *lun_nump);
        clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
        clp->ap_r_state = lap->xport_rstate;
        clp->ap_o_state = CFGA_STAT_CONFIGURED;
        clp->ap_cond = cond;
        clp->ap_busy = busy;
        clp->ap_status_time = (time_t)-1;
        clp->ap_info[0] = '\0';

        get_hw_info(node, clp);

        (void) insert_ldata_to_ldatalist(port_wwn, lun_nump, listp,
                &(lap->listp));

        lap->ret = FPCFGA_OK;
        S_FREE(port_wwn);
        return (FPCFGA_OK);
}

/*
 * Searches the ldata_list to find if the the input port_wwn exist.
 *
 * Input:  port_wwn, ldata_list.
 * Return value: FPCFGA_APID_NOACCESS if not found on ldata_list.
 *               FPCFGA_ACCESS_OK if found on ldata_list.
 */
static fpcfga_ret_t
is_dyn_ap_on_ldata_list(const char *port_wwn, const ldata_list_t *listp,
                        ldata_list_t **matchldpp, int *l_errnop)
{
        char            *dyn = NULL, *dyncomp = NULL;
        int             len;
        ldata_list_t    *tmplp;
        fpcfga_ret_t    ret;


        ret = FPCFGA_APID_NOACCESS;

        tmplp = (ldata_list_t *)listp;
        while (tmplp != NULL) {
                if ((dyn = GET_DYN(tmplp->ldata.ap_phys_id)) != NULL) {
                        len = strlen(DYN_TO_DYNCOMP(dyn)) + 1;
                        dyncomp = calloc(1, len);
                        if (dyncomp == NULL) {
                                *l_errnop = errno;
                                ret = FPCFGA_LIB_ERR;
                                break;
                        }
                        (void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
                        if (!(strncmp(port_wwn, dyncomp, WWN_SIZE*2))) {
                                *matchldpp = tmplp;
                                S_FREE(dyncomp);
                                ret = FPCFGA_ACCESS_OK;
                                break;
                        }
                        S_FREE(dyncomp);
                }
                tmplp = tmplp->next;
        }

        return (ret);
}

/*
 * Searches the ldata_list to find if the the input port_wwn and lun exist.
 *
 * Input:  port_wwn, ldata_list.
 * Return value: FPCFGA_APID_NOACCESS if not found on ldata_list.
 *               FPCFGA_ACCESS_OK if found on ldata_list.
 */
static fpcfga_ret_t
is_FCP_dev_ap_on_ldata_list(const char *port_wwn, const int lun_num,
                        ldata_list_t *ldatap,
                        ldata_list_t **matchldpp)
{
        ldata_list_t *curlp = NULL;
        char *dyn = NULL, *dyncomp = NULL;
        char *lun_dyn = NULL, *lunp = NULL;
        int ldata_lun;
        fpcfga_ret_t ret;

        /*
         * if there is no list data just return the FCP dev list.
         * Normally this should not occur since list data should
         * be created through discoveredPort list.
         */
        ret = FPCFGA_APID_NOACCESS;
        if (ldatap == NULL) {
                return (ret);
        }

        dyn = GET_DYN(ldatap->ldata.ap_phys_id);
        if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
        if ((dyncomp != NULL) &&
                        (strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
                lun_dyn = GET_LUN_DYN(dyncomp);
                if (lun_dyn != NULL) {
                        lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
                        if ((ldata_lun = atoi(lunp)) == lun_num) {
                                *matchldpp = ldatap;
                                return (FPCFGA_ACCESS_OK);
                        } else if (ldata_lun > lun_num) {
                                return (ret);
                        }
                        /* else continue */
                } else {
                        /* we have match without lun comp. */
                        *matchldpp = ldatap;
                        return (FPCFGA_ACCESS_OK);
                }
        }

        curlp = ldatap->next;

        dyn = dyncomp = NULL;
        lun_dyn = lunp = NULL;
        while (curlp != NULL) {
                dyn = GET_DYN(curlp->ldata.ap_phys_id);
                if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
                if ((dyncomp != NULL) &&
                                (strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
                        lun_dyn = GET_LUN_DYN(dyncomp);
                        if (lun_dyn != NULL) {
                                lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
                                if ((ldata_lun = atoi(lunp)) == lun_num) {
                                        *matchldpp = curlp;
                                        return (FPCFGA_ACCESS_OK);
                                } else if (ldata_lun > lun_num) {
                                        return (ret);
                                }
                                /* else continue */
                        } else {
                                /* we have match without lun comp. */
                                *matchldpp = curlp;
                                return (FPCFGA_ACCESS_OK);
                        }
                }
                dyn = dyncomp = NULL;
                lun_dyn = lunp = NULL;
                curlp = curlp->next;
        }

        return (ret);

}

/*
 * This routine is called when a pathinfo without matching pwwn in dev_list
 * is found.
 */
static fpcfga_ret_t
init_ldata_for_mpath_dev(di_path_t path, char *pwwn, int *l_errnop,
        fpcfga_list_t *lap)
{
        ldata_list_t *listp = NULL;
        cfga_list_data_t *clp = NULL;
        size_t          devlen;
        char            *devpath;
        di_node_t       client_node = DI_NODE_NIL;
        uint_t          dctl_state = 0;
        cfga_busy_t     busy;
        char            *client_path;
        di_path_state_t pstate;

        /* get the client node path */
        if (path == DI_PATH_NIL) {
                return (FPCFGA_LIB_ERR);
        }
        client_node = di_path_client_node(path);
        if (client_node == DI_NODE_NIL) {
                return (FPCFGA_LIB_ERR);
        }
        if ((client_path = di_devfs_path(client_node)) == NULL) {
                return (FPCFGA_LIB_ERR);
        }
        devlen = strlen(DEVICES_DIR) + strlen(client_path) + 1;
        devpath = calloc(1, devlen);
        if (devpath == NULL) {
                di_devfs_path_free(client_path);
                *l_errnop = errno;
                return (FPCFGA_LIB_ERR);
        }
        (void) snprintf(devpath, devlen, "%s%s", DEVICES_DIR, client_path);

        /* now need to create ldata for this dev */
        listp = calloc(1, sizeof (ldata_list_t));
        if (listp == NULL) {
                di_devfs_path_free(client_path);
                S_FREE(devpath);
                *l_errnop = errno;
                return (FPCFGA_LIB_ERR);
        }

        clp = &listp->ldata;

        /* Create logical and physical ap_id */
        (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
                        lap->xport_logp, DYN_SEP, pwwn);
        (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
                        lap->apidp->xport_phys, DYN_SEP, pwwn);

        /* Filled in by libcfgadm */
        clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
        clp->ap_r_state = lap->xport_rstate;
        /* set to ostate to configured. */
        clp->ap_o_state = CFGA_STAT_CONFIGURED;
        /*
         * This routine is called when a port WWN is not found in dev list
         * but path info node exists.
         *
         * Update the condition as failing if the pathinfo state was normal.
         * Update the condition as unusable if the pathinfo state is failed
         * or offline.
         */
        if (((pstate = di_path_state(path)) == DI_PATH_STATE_OFFLINE) ||
                        (pstate == DI_PATH_STATE_FAULT)) {
                clp->ap_cond = CFGA_COND_UNUSABLE;
        } else {
                clp->ap_cond = CFGA_COND_FAILING;
        }
        clp->ap_status_time = (time_t)-1;
        /* update ap_type and ap_info */
        get_hw_info(client_node, clp);

        if (devctl_cmd(devpath, FPCFGA_DEV_GETSTATE,
                &dctl_state, l_errnop) == FPCFGA_OK) {
                busy = ((dctl_state & DEVICE_BUSY) == DEVICE_BUSY) ? 1 : 0;
        } else {
                busy = 0;
        }
        clp->ap_busy = busy;
        /* Link it in */
        listp->next = lap->listp;
        lap->listp = listp;

        di_devfs_path_free(client_path);
        S_FREE(devpath);

        /* now return with ok status with ldata. */
        lap->ret = FPCFGA_OK;
        return (FPCFGA_OK);
}

/*
 * Initialize the cfga_list_data struct for an accessible device
 * from g_get_dev_list().
 *
 * Input:  fca port ldata.
 * Output: device cfga_list_data.
 *
 */
static fpcfga_ret_t
init_ldata_for_accessible_dev(const char *dyncomp, uchar_t inq_type,
                                                        fpcfga_list_t *lap)
{
        ldata_list_t *listp = NULL;
        cfga_list_data_t *clp = NULL;
        int i;

        listp = calloc(1, sizeof (ldata_list_t));
        if (listp == NULL) {
                lap->l_errno = errno;
                return (FPCFGA_LIB_ERR);
        }

        clp = &listp->ldata;

        assert(dyncomp != NULL);

        /* Create logical and physical ap_id */
        (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
                lap->xport_logp, DYN_SEP, dyncomp);

        (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
                lap->apidp->xport_phys, DYN_SEP, dyncomp);

        clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
        clp->ap_r_state = lap->xport_rstate;
        clp->ap_o_state = CFGA_STAT_UNCONFIGURED;
        clp->ap_cond = CFGA_COND_UNKNOWN;
        clp->ap_busy = 0;
        clp->ap_status_time = (time_t)-1;
        clp->ap_info[0] = '\0';
        for (i = 0; i < N_DEVICE_TYPES; i++) {
                if (inq_type == device_list[i].itype) {
                        (void) snprintf(clp->ap_type, sizeof (clp->ap_type),
                        "%s", (char *)device_list[i].name);
                break;
                }
        }
        if (i == N_DEVICE_TYPES) {
                if (inq_type == ERR_INQ_DTYPE) {
                        clp->ap_cond = CFGA_COND_FAILED;
                        snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
                            (char *)GET_MSG_STR(ERR_UNAVAILABLE));
                } else {
                        (void) snprintf(clp->ap_type, sizeof (clp->ap_type),
                                "%s", "unknown");
                }
        }

        /* Link it in */
        (void) insert_ldata_to_ldatalist(dyncomp, NULL, listp, &(lap->listp));

        return (FPCFGA_OK);
}

/*
 * Initialize the cfga_list_data struct for an accessible FCP SCSI LUN device
 * from the report lun data.
 *
 * Input:  fca port ldata. report lun info
 * Output: device cfga_list_data.
 *
 */
static fpcfga_ret_t
init_ldata_for_accessible_FCP_dev(
        const char *port_wwn,
        int num_luns,
        struct report_lun_resp *resp_buf,
        fpcfga_list_t   *lap,
        int *l_errnop)
{
        ldata_list_t *listp = NULL, *listp_start = NULL, *listp_end = NULL,
                *prevlp = NULL, *curlp = NULL, *matchp_start = NULL,
                *matchp_end = NULL;
        cfga_list_data_t *clp = NULL;
        char *dyn = NULL, *dyncomp = NULL;
        uchar_t *lun_string;
        uint16_t lun_num;
        int i, j, str_ret;
        fpcfga_ret_t ret;
        char dtype[CFGA_TYPE_LEN];
        struct scsi_inquiry *inq_buf;
        uchar_t peri_qual;
        cfga_cond_t cond = CFGA_COND_UNKNOWN;
        uchar_t lun_num_raw[SAM_LUN_SIZE];

        /* when number of lun is 0 it is not an error. so just return ok. */
        if (num_luns == 0) {
                return (FPCFGA_OK);
        }

        for (i = 0; i < num_luns; i++) {
            lun_string = (uchar_t *)&(resp_buf->lun_string[i]);
            memcpy(lun_num_raw, lun_string, sizeof (lun_num_raw));
            if ((ret = get_standard_inq_data(lap->apidp->xport_phys, port_wwn,
                lun_num_raw, &inq_buf, l_errnop))
                != FPCFGA_OK) {
                if (ret == FPCFGA_FCP_TGT_SEND_SCSI_FAILED) {
                        (void) strlcpy(dtype,
                        (char *)GET_MSG_STR(ERR_UNAVAILABLE), CFGA_TYPE_LEN);
                        cond = CFGA_COND_FAILED;
                } else {
                        S_FREE(inq_buf);
                        return (FPCFGA_LIB_ERR);
                }
            } else {
                peri_qual = inq_buf->inq_dtype & FP_PERI_QUAL_MASK;
                /*
                 * peripheral qualifier is not 0 so the device node should not
                 * included in the ldata list. There should not be a device
                 * node for the lun either.
                 */
                if (peri_qual != DPQ_POSSIBLE) {
                        S_FREE(inq_buf);
                        continue;
                }
                *dtype = '\0';
                for (j = 0; j < N_DEVICE_TYPES; j++) {
                    if ((inq_buf->inq_dtype & DTYPE_MASK)
                                == device_list[j].itype) {
                        (void) strlcpy(dtype, (char *)device_list[j].name,
                                        CFGA_TYPE_LEN);
                        break;
                    }
                }
                if (*dtype == '\0') {
                        (void) strlcpy(dtype,
                                (char *)device_list[DTYPE_UNKNOWN_INDEX].name,
                                CFGA_TYPE_LEN);
                }
            }
                /*
                 * Followed FCP driver for getting lun number from report
                 * lun data.
                 * According to SAM-2 there are multiple address method for
                 * FCP SCIS LUN.  Logincal unit addressing, peripheral device
                 * addressing, flat space addressing, and extended logical
                 * unit addressing.
                 *
                 * as of 11/2001 FCP supports logical unit addressing and
                 * peripheral device addressing even thoough 3 defined.
                 * SSFCP_LUN_ADDRESSING 0x80
                 * SSFCP_PD_ADDRESSING 0x00
                 * SSFCP_VOLUME_ADDRESSING 0x40
                 *
                 * the menthod below is used by FCP when (lun_string[0] & 0xC0)
                 * is either SSFCP_LUN_ADDRESSING or SSFCP_PD_ADDRESSING mode.
                 */
            lun_num = ((lun_string[0] & 0x3F) << 8) | lun_string[1];
            listp = calloc(1, sizeof (ldata_list_t));
            if (listp == NULL) {
                *l_errnop = errno;
                list_free(&listp_start);
                return (FPCFGA_LIB_ERR);
            }

            clp = &listp->ldata;
                /* Create logical and physical ap_id */
            (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id),
                "%s%s%s%s%d", lap->xport_logp, DYN_SEP, port_wwn,
                LUN_COMP_SEP, lun_num);
            (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id),
                "%s%s%s%s%d", lap->apidp->xport_phys, DYN_SEP, port_wwn,
                LUN_COMP_SEP, lun_num);
            (void) strncpy(clp->ap_type, dtype, strlen(dtype));
            clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
            clp->ap_r_state = lap->xport_rstate;
            clp->ap_o_state = CFGA_STAT_UNCONFIGURED;
            clp->ap_cond = cond;
            clp->ap_busy = 0;
            clp->ap_status_time = (time_t)-1;
            clp->ap_info[0] = '\0';
            if (listp_start == NULL) {
                listp_start = listp;
            } else {
                if ((ret = insert_FCP_dev_ldata(
                        port_wwn, lun_num, listp,
                        &listp_start)) != FPCFGA_OK) {
                        list_free(&listp_start);
                        return (ret);
                }
            }
            listp = NULL;
            S_FREE(inq_buf);
        }

        /*
         * list data can be null when device peripheral qualifier is not 0
         * for any luns.  Return ok to continue.
         */
        if (listp_start == NULL) {
                return (FPCFGA_OK);
        }
        /*
         * get the end of list for later uses.
         */
        curlp = listp_start->next;
        prevlp = listp_start;
        while (curlp) {
                prevlp = curlp;
                curlp = curlp->next;
        }
        listp_end = prevlp;

        /*
         * if there is no list data just return the FCP dev list.
         * Normally this should not occur since list data should
         * be created through g_get_dev_list().
         */
        if (lap->listp == NULL) {
                lap->listp = listp_start;
                for (listp = listp_start; listp != NULL; listp = listp->next) {
                        listp->ldata.ap_cond = CFGA_COND_FAILING;
                }
                return (FPCFGA_OK);
        }

        dyn = GET_DYN(lap->listp->ldata.ap_phys_id);
        if ((dyn != NULL) && ((dyncomp = DYN_TO_DYNCOMP(dyn)) != NULL)) {
                if ((str_ret = strncmp(dyncomp, port_wwn, WWN_SIZE*2)) == 0) {
                        matchp_start = matchp_end = lap->listp;
                        while (matchp_end->next != NULL) {
                                dyn = GET_DYN(
                                        matchp_end->next->ldata.ap_phys_id);
                                if ((dyn != NULL) &&
                                ((dyncomp = DYN_TO_DYNCOMP(dyn)) != NULL)) {
                                        if ((str_ret = strncmp(dyncomp,
                                                port_wwn, WWN_SIZE*2)) == 0) {
                                                matchp_end = matchp_end->next;
                                        } else {
                                                break;
                                        }
                                } else {
                                        break;
                                }
                        }
                        /* fillup inqdtype */
                        for (listp = listp_start; listp != NULL;
                                        listp = listp->next) {
                                listp->ldata.ap_cond =
                                        lap->listp->ldata.ap_cond;
                        }
                        /* link the new elem of lap->listp. */
                        listp_end->next = matchp_end->next;
                        /* free the one matching wwn. */
                        matchp_end->next = NULL;
                        list_free(&matchp_start);
                        /* link lap->listp to listp_start. */
                        lap->listp = listp_start;
                        return (FPCFGA_OK);
                } else if (str_ret > 0) {
                        for (listp = listp_start; listp != NULL;
                                        listp = listp->next) {
                                listp->ldata.ap_cond = CFGA_COND_FAILING;
                        }
                        listp_end->next = lap->listp->next;
                        lap->listp = listp_start;
                        return (FPCFGA_OK);
                }
        }

        prevlp = lap->listp;
        curlp = lap->listp->next;

        dyn = dyncomp = NULL;
        while (curlp != NULL) {
                dyn = GET_DYN(curlp->ldata.ap_phys_id);
                if ((dyn != NULL) &&
                        ((dyncomp = DYN_TO_DYNCOMP(dyn)) != NULL)) {
                        if ((str_ret = strncmp(dyncomp, port_wwn,
                                        WWN_SIZE*2)) == 0) {
                                matchp_start = matchp_end = curlp;
                                while (matchp_end->next != NULL) {
                                        dyn = GET_DYN(
                                        matchp_end->next->ldata.ap_phys_id);
                                        if ((dyn != NULL) &&
                                                ((dyncomp = DYN_TO_DYNCOMP(dyn))
                                                != NULL)) {
                                                if ((str_ret = strncmp(dyncomp,
                                                        port_wwn, WWN_SIZE*2))
                                                        == 0) {
                                                        matchp_end =
                                                        matchp_end->next;
                                                } else {
                                                        break;
                                                }
                                        } else {
                                                break;
                                        }
                                }
                                for (listp = listp_start; listp != NULL;
                                                listp = listp->next) {
                                    listp->ldata.ap_cond = curlp->ldata.ap_cond;
                                }
                                /* link the next elem to listp_end. */
                                listp_end->next = matchp_end->next;
                                /* link prevlp to listp_start to drop curlp. */
                                prevlp->next = listp_start;
                                /* free matching pwwn elem. */
                                matchp_end->next = NULL;
                                list_free(&matchp_start);
                                return (FPCFGA_OK);
                        } else if (str_ret > 0) {
                                for (listp = listp_start; listp != NULL;
                                                listp = listp->next) {
                                        /*
                                         * Dev not found from accessible
                                         * fc dev list but the node should
                                         * exist. Set to failing cond now
                                         * and check the node state later.
                                         */
                                    listp->ldata.ap_cond = CFGA_COND_FAILING;
                                }
                                /* keep the cur elem by linking to list_end. */
                                listp_end->next = curlp;
                                prevlp->next = listp_start;
                                return (FPCFGA_OK);
                        }
                }
                dyn = dyncomp = NULL;
                prevlp = curlp;
                curlp = curlp->next;
        }

        prevlp->next = listp_start;
        for (listp = listp_start; listp != NULL; listp = listp->next) {
                listp->ldata.ap_cond = CFGA_COND_FAILING;
        }

        return (FPCFGA_OK);

}

/* fill in device type, vid, pid from properties */
static void
get_hw_info(di_node_t node, cfga_list_data_t *clp)
{
        char *cp = NULL;
        char *inq_vid, *inq_pid;
        int i;

        /*
         * if the type is not previously assigned with valid SCSI device type
         * check devinfo to find the type.
         * once device is configured it should have a valid device type.
         * device node is configured but no valid device type is found
         * the type will be set to unavailable.
         */
        if (clp->ap_type != NULL) {
                /*
                 * if the type is not one of defined SCSI device type
                 * check devinfo to find the type.
                 *
                 * Note: unknown type is not a valid device type.
                 *      It is added in to the device list table to provide
                 *      constant string of "unknown".
                 */
            for (i = 0; i < (N_DEVICE_TYPES -1); i++) {
                if (strncmp((char *)clp->ap_type, (char *)device_list[i].name,
                        sizeof (clp->ap_type)) == 0) {
                        break;
                }
            }
            if (i == (N_DEVICE_TYPES - 1)) {
                cp = (char *)get_device_type(node);
                if (cp == NULL) {
                        cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE);
                }
                (void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
                        S_STR(cp));
            }
        } else {
                cp = (char *)get_device_type(node);
                if (cp == NULL) {
                        cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE);
                }
                (void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
                        S_STR(cp));
        }

        /*
         * Fill in vendor and product ID.
         */
        if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
            "inquiry-product-id", &inq_pid) == 1) &&
            (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
            "inquiry-vendor-id", &inq_vid) == 1)) {
                (void) snprintf(clp->ap_info, sizeof (clp->ap_info),
                    "%s %s", inq_vid, inq_pid);
        }
}

/*
 * Get dtype from "inquiry-device-type" property. If not present,
 * derive it from minor node type
 */
static const char *
get_device_type(di_node_t node)
{
        char *name = NULL;
        int *inq_dtype;
        int i;

        if (node == DI_NODE_NIL) {
                return (NULL);
        }

        /* first, derive type based on inquiry property */
        if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
            &inq_dtype) != -1) {
                int itype = (*inq_dtype) & DTYPE_MASK;

                for (i = 0; i < N_DEVICE_TYPES; i++) {
                        if (itype == device_list[i].itype) {
                                name = (char *)device_list[i].name;
                                break;
                        }
                }
                /*
                 * when found to be unknown type, set name to null to check
                 * device minor node type.
                 */
                if (i == (N_DEVICE_TYPES - 1)) {
                        name = NULL;
                }
        }

        /* if property fails, use minor nodetype */
        if (name == NULL) {
                char *nodetype;
                di_minor_t minor = di_minor_next(node, DI_MINOR_NIL);

                if ((minor != DI_MINOR_NIL) &&
                    ((nodetype = di_minor_nodetype(minor)) != NULL)) {
                        for (i = 0; i < N_DEVICE_TYPES; i++) {
                                if (device_list[i].ntype &&
                                    (strcmp(nodetype, device_list[i].ntype)
                                    == 0)) {
                                        name = (char *)device_list[i].name;
                                        break;
                                }
                        }
                }
        }

        return (name);
}

/* Transform list data to stat data */
fpcfga_ret_t
list_ext_postprocess(
        ldata_list_t            **llpp,
        int                     nelem,
        cfga_list_data_t        **ap_id_list,
        int                     *nlistp,
        char                    **errstring)
{
        cfga_list_data_t *ldatap = NULL;
        ldata_list_t *tmplp = NULL;
        int i = -1;

        *ap_id_list = NULL;
        *nlistp = 0;

        if (*llpp == NULL || nelem < 0) {
                return (FPCFGA_LIB_ERR);
        }

        if (nelem == 0) {
                return (FPCFGA_APID_NOEXIST);
        }

        ldatap = calloc(nelem, sizeof (cfga_list_data_t));
        if (ldatap == NULL) {
                cfga_err(errstring, errno, ERR_LIST, 0);
                return (FPCFGA_LIB_ERR);
        }

        /* Extract the list_data structures from the linked list */
        tmplp = *llpp;
        for (i = 0; i < nelem && tmplp != NULL; i++) {
                ldatap[i] = tmplp->ldata;
                tmplp = tmplp->next;
        }

        if (i < nelem || tmplp != NULL) {
                S_FREE(ldatap);
                return (FPCFGA_LIB_ERR);
        }

        *nlistp = nelem;
        *ap_id_list = ldatap;

        return (FPCFGA_OK);
}

/*
 * Convert bus state to receptacle state
 */
static cfga_stat_t
xport_devinfo_to_recep_state(uint_t xport_di_state)
{
        cfga_stat_t rs;

        switch (xport_di_state) {
        case DI_BUS_QUIESCED:
        case DI_BUS_DOWN:
                rs = CFGA_STAT_DISCONNECTED;
                break;
        /*
         * NOTE: An explicit flag for active should probably be added to
         * libdevinfo.
         */
        default:
                rs = CFGA_STAT_CONNECTED;
                break;
        }

        return (rs);
}

/*
 * Convert device state to occupant state
 * if driver is attached the node is configured.
 * if offline or down the node is unconfigured.
 * if only driver detached it is none state which is treated the same
 * way as configured state.
 */
static cfga_stat_t
dev_devinfo_to_occupant_state(uint_t dev_di_state)
{
        /* Driver attached ? */
        if ((dev_di_state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
                return (CFGA_STAT_CONFIGURED);
        }

        if ((dev_di_state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE ||
            (dev_di_state & DI_DEVICE_DOWN) == DI_DEVICE_DOWN) {
                return (CFGA_STAT_UNCONFIGURED);
        } else {
                return (CFGA_STAT_NONE);
        }
}

/*
 * Wrapper routine for inserting ldata to make an sorted ldata list.
 *
 * When show_FCP_dev option is given insert_FCP_dev_ldata() is called.
 * Otherwise insert_fc_dev_ldata() is called.
 */
static fpcfga_ret_t
insert_ldata_to_ldatalist(
        const char *port_wwn,
        int *lun_nump,
        ldata_list_t *listp,
        ldata_list_t **ldatapp)
{

        if (lun_nump == NULL) {
                return (insert_fc_dev_ldata(port_wwn, listp, ldatapp));
        } else {
                return
                (insert_FCP_dev_ldata(port_wwn, *lun_nump, listp, ldatapp));
        }
}

/*
 * Insert an input ldata to ldata list to make sorted ldata list.
 */
static fpcfga_ret_t
insert_fc_dev_ldata(
        const char *port_wwn,
        ldata_list_t *listp,
        ldata_list_t **ldatapp)
{
        ldata_list_t *prevlp = NULL, *curlp = NULL;
        char *dyn = NULL, *dyncomp = NULL;

        if (*ldatapp == NULL) {
                *ldatapp = listp;
                return (FPCFGA_OK);
        }

        dyn = GET_DYN((*ldatapp)->ldata.ap_phys_id);
        if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
        if ((dyncomp != NULL) &&
                (strncmp(dyncomp, port_wwn, WWN_SIZE*2) >= 0)) {
                        listp->next = *ldatapp;
                        *ldatapp = listp;
                        return (FPCFGA_OK);
        }
        /* else continue */

        prevlp = *ldatapp;
        curlp = (*ldatapp)->next;

        dyn = dyncomp = NULL;
        while (curlp != NULL) {
                dyn = GET_DYN(curlp->ldata.ap_phys_id);
                if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
                if ((dyncomp != NULL) &&
                                (strncmp(dyncomp, port_wwn, WWN_SIZE*2) >= 0)) {
                        listp->next = prevlp->next;
                        prevlp->next = listp;
                        return (FPCFGA_OK);
                }
                dyn = dyncomp = NULL;
                prevlp = curlp;
                curlp = curlp->next;
        }

        /* add the ldata to the end of the list. */
        prevlp->next = listp;
        return (FPCFGA_OK);
}

/*
 * Insert an input ldata to ldata list to make sorted ldata list.
 */
static fpcfga_ret_t
insert_FCP_dev_ldata(
        const char *port_wwn,
        int lun_num,
        ldata_list_t *listp,
        ldata_list_t **ldatapp)
{
        ldata_list_t *prevlp = NULL, *curlp = NULL;
        char *dyn = NULL, *dyncomp = NULL;
        char *lun_dyn = NULL, *lunp = NULL;

        if (*ldatapp == NULL) {
                *ldatapp = listp;
                return (FPCFGA_OK);
        }

        dyn = GET_DYN((*ldatapp)->ldata.ap_phys_id);
        if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
        if ((dyncomp != NULL) &&
                (strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
                lun_dyn = GET_LUN_DYN(dyncomp);
                if (lun_dyn != NULL) {
                        lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
                        if ((atoi(lunp)) >= lun_num) {
                                listp->next = *ldatapp;
                                *ldatapp = listp;
                                return (FPCFGA_OK);
                        }
                }
        } else if ((dyncomp != NULL) &&
                        (strncmp(dyncomp, port_wwn, WWN_SIZE*2) > 0)) {
                listp->next = *ldatapp;
                *ldatapp = listp;
                return (FPCFGA_OK);
        }

        prevlp = *ldatapp;
        curlp = (*ldatapp)->next;

        dyn = dyncomp = NULL;
        lun_dyn = lunp = NULL;
        while (curlp != NULL) {
                dyn = GET_DYN(curlp->ldata.ap_phys_id);
                if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);

                if ((dyncomp != NULL) &&
                                (strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
                        lun_dyn = GET_LUN_DYN(dyncomp);
                        if (lun_dyn != NULL) {
                                lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
                                if ((atoi(lunp)) >= lun_num) {
                                        listp->next = prevlp->next;
                                        prevlp->next = listp;
                                        return (FPCFGA_OK);
                                }
                        }
                        /* else continue */
                } else if ((dyncomp != NULL) &&
                                (strncmp(dyncomp, port_wwn, WWN_SIZE*2) > 0)) {
                        listp->next = prevlp->next;
                        prevlp->next = listp;
                        return (FPCFGA_OK);
                }
                /* else continue */

                dyn = dyncomp = NULL;
                lun_dyn = lunp = NULL;
                prevlp = curlp;
                curlp = curlp->next;
        }

        /* add the ldata to the end of the list. */
        prevlp->next = listp;
        return (FPCFGA_OK);
}

/*
 * This function will return the dtype for the given device
 * It will first issue a report lun to lun 0 and then it will issue a SCSI
 * Inquiry to the first lun returned by report luns.
 *
 * If everything is successful, the dtype will be returned with the peri
 * qualifier masked out.
 *
 * If either the report lun or the scsi inquiry fails, we will first check
 * the return status.  If the return status is SCSI_DEVICE_NOT_TGT, then
 * we will assume this is a remote HBA and return an UNKNOWN DTYPE
 * for all other failures, we will return a dtype of ERR_INQ_DTYPE
 */
static uchar_t
get_inq_dtype(char *xport_phys, char *dyncomp, HBA_HANDLE handle,
    HBA_PORTATTRIBUTES *portAttrs, HBA_PORTATTRIBUTES *discPortAttrs) {
        HBA_STATUS                  status;
        report_lun_resp_t           *resp_buf;
        int                         num_luns = 0, ret, l_errno;
        uchar_t                     *lun_string;
        uint64_t                    lun = 0;
        struct scsi_inquiry         inq;
        struct scsi_extended_sense  sense;
        HBA_UINT8                   scsiStatus;
        uint32_t                    inquirySize = sizeof (inq);
        uint32_t                    senseSize = sizeof (sense);

        memset(&inq, 0, sizeof (inq));
        memset(&sense, 0, sizeof (sense));
        if ((ret = get_report_lun_data(xport_phys, dyncomp,
                            &num_luns, &resp_buf, &sense, &l_errno))
            != FPCFGA_OK) {
                /*
                 * Checking the sense key data as well as the additional
                 * sense key.  The SES Node is not required to repond
                 * to Report LUN.  In the case of Minnow, the SES node
                 * returns with KEY_ILLEGAL_REQUEST and the additional
                 * sense key of 0x20.  In this case we will blindly
                 * send the SCSI Inquiry call to lun 0
                 *
                 * if we get any other error we will set the inq_type
                 * appropriately
                 */
                if ((sense.es_key == KEY_ILLEGAL_REQUEST) &&
                    (sense.es_add_code == 0x20)) {
                        lun = 0;
                } else {
                        if (ret == FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT) {
                                inq.inq_dtype = DTYPE_UNKNOWN;
                        } else {
                                inq.inq_dtype = ERR_INQ_DTYPE;
                        }
                        return (inq.inq_dtype);
                }
        } else {
                /* send the inquiry to the first lun */
                lun_string = (uchar_t *)&(resp_buf->lun_string[0]);
                memcpy(&lun, lun_string, sizeof (lun));
                S_FREE(resp_buf);
        }

        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 = inq.inq_dtype & DTYPE_MASK;
        } else if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
                inq.inq_dtype = DTYPE_UNKNOWN;
        } else {
                inq.inq_dtype = ERR_INQ_DTYPE;
        }
        return (inq.inq_dtype);
}