OpenSolaris_b135/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 = NULL;
		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 == NULL) {
			(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);
}