--- /dev/null	Mon Aug  5 17:31:38 2019
+++ new/usr/src/uts/common/io/scsi/adapters/smartpqi/smartpqi_hba.c	Mon Aug  5 17:31:38 2019
@@ -0,0 +1,1279 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Nexenta Systems, Inc.
+ */
+
+/*
+ * This file contains all routines necessary to interface with SCSA trans.
+ */
+#include <smartpqi.h>
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Forward declarations for SCSA trans routines.			|
+ * []------------------------------------------------------------------[]
+ */
+static int pqi_scsi_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
+    scsi_hba_tran_t *hba_tran, struct scsi_device *sd);
+static void pqi_scsi_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
+    scsi_hba_tran_t *hba_tran, struct scsi_device *sd);
+static int pqi_start(struct scsi_address *ap, struct scsi_pkt *pkt);
+static int pqi_scsi_reset(struct scsi_address *ap, int level);
+static int pqi_scsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt);
+static int pqi_scsi_getcap(struct scsi_address *ap, char *cap, int tgtonly);
+static int pqi_scsi_setcap(struct scsi_address *ap, char *cap, int value,
+    int tgtonly);
+static struct scsi_pkt *pqi_init_pkt(struct scsi_address *ap,
+    struct scsi_pkt *pkt, struct buf *bp, int cmdlen, int statuslen, int tgtlen,
+    int flags,  int (*callback)(), caddr_t arg);
+static void pqi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt);
+static void pqi_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt);
+static void pqi_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt);
+static int pqi_reset_notify(struct scsi_address *ap, int flag,
+    void (*callback)(caddr_t), caddr_t arg);
+static int pqi_quiesce(dev_info_t *dip);
+static int pqi_unquiesce(dev_info_t *dip);
+static int pqi_bus_config(dev_info_t *pdip, uint_t flag,
+    ddi_bus_config_op_t op, void *arg, dev_info_t **childp);
+
+/* ---- Support method declaration ---- */
+static int config_one(dev_info_t *pdip, pqi_state_t s,  pqi_device_t,
+    dev_info_t **childp);
+static void abort_all(struct scsi_address *ap, pqi_state_t s);
+static int cmd_ext_alloc(pqi_cmd_t cmd, int kf);
+static void cmd_ext_free(pqi_cmd_t cmd);
+static boolean_t is_physical_dev(pqi_device_t d);
+static boolean_t decode_to_target(void *arg, long *target);
+static void cmd_timeout_scan(void *);
+
+int
+smartpqi_register_hba(pqi_state_t s)
+{
+	scsi_hba_tran_t		*tran;
+	int			flags;
+	char			iport_str[16];
+	int			instance = ddi_get_instance(s->s_dip);
+
+	tran = s->s_tran = scsi_hba_tran_alloc(s->s_dip, SCSI_HBA_CANSLEEP);
+	if (tran == NULL)
+		return (FALSE);
+
+	tran->tran_hba_private		= s;
+	tran->tran_tgt_private		= NULL;
+
+	tran->tran_tgt_init		= pqi_scsi_tgt_init;
+	tran->tran_tgt_free		= pqi_scsi_tgt_free;
+	tran->tran_tgt_probe		= scsi_hba_probe;
+
+	tran->tran_start		= pqi_start;
+	tran->tran_reset		= pqi_scsi_reset;
+	tran->tran_abort		= pqi_scsi_abort;
+	tran->tran_getcap		= pqi_scsi_getcap;
+	tran->tran_setcap		= pqi_scsi_setcap;
+	tran->tran_bus_config		= pqi_bus_config;
+
+	tran->tran_init_pkt		= pqi_init_pkt;
+	tran->tran_destroy_pkt		= pqi_destroy_pkt;
+	tran->tran_dmafree		= pqi_dmafree;
+	tran->tran_sync_pkt		= pqi_sync_pkt;
+
+	tran->tran_reset_notify		= pqi_reset_notify;
+	tran->tran_quiesce		= pqi_quiesce;
+	tran->tran_unquiesce		= pqi_unquiesce;
+	tran->tran_bus_reset		= NULL;
+
+	tran->tran_add_eventcall	= NULL;
+	tran->tran_get_eventcookie	= NULL;
+	tran->tran_post_event		= NULL;
+	tran->tran_remove_eventcall	= NULL;
+	tran->tran_bus_config		= pqi_bus_config;
+	tran->tran_interconnect_type	= INTERCONNECT_SAS;
+
+	/*
+	 * scsi_vhci needs to have "initiator-port" set, but doesn't
+	 * seem to care what it's set to. iSCSI uses the InitiatorName
+	 * whereas mpt_sas uses the WWN port id, but this HBA doesn't
+	 * have such a value. So, for now the instance number will be used.
+	 */
+	(void) snprintf(iport_str, sizeof (iport_str), "0x%x", instance);
+	if (ddi_prop_update_string(DDI_DEV_T_NONE, s->s_dip,
+	    SCSI_ADDR_PROP_INITIATOR_PORT, iport_str) != DDI_PROP_SUCCESS) {
+		cmn_err(CE_WARN, "%s: Failed to create prop (%s) on %d\n",
+		    __func__, SCSI_ADDR_PROP_INITIATOR_PORT, instance);
+	}
+
+	flags = SCSI_HBA_ADDR_COMPLEX | SCSI_HBA_TRAN_SCB;
+	if (scsi_hba_attach_setup(s->s_dip, &s->s_msg_dma_attr, tran,
+	    flags) != DDI_SUCCESS) {
+		dev_err(s->s_dip, CE_NOTE, "scsi_hba_attach_setup failed");
+		scsi_hba_tran_free(s->s_tran);
+		s->s_tran = NULL;
+		return (FALSE);
+	}
+
+	if (s->s_enable_mpxio) {
+		if (mdi_phci_register(MDI_HCI_CLASS_SCSI, s->s_dip, 0) !=
+		    MDI_SUCCESS) {
+			s->s_enable_mpxio = 0;
+		}
+	}
+
+	s->s_cmd_timeout = timeout(cmd_timeout_scan, s,
+	    CMD_TIMEOUT_SCAN_SECS * drv_usectohz(MICROSEC));
+
+	return (TRUE);
+}
+
+void
+smartpqi_unregister_hba(pqi_state_t s)
+{
+	if (s->s_enable_mpxio)
+		(void) mdi_phci_unregister(s->s_dip, 0);
+
+	if (s->s_cmd_timeout != NULL) {
+		(void) untimeout(s->s_cmd_timeout);
+		s->s_cmd_timeout = NULL;
+	}
+
+	if (s->s_tran == NULL)
+		return;
+	scsi_hba_tran_free(s->s_tran);
+	s->s_tran = NULL;
+}
+
+/*ARGSUSED*/
+static int
+pqi_scsi_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
+    scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
+{
+	uint16_t	lun;
+	pqi_device_t	d;
+	pqi_state_t	s	= hba_tran->tran_hba_private;
+	mdi_pathinfo_t	*pip;
+	int		type;
+
+	if ((lun = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
+	    LUN_PROP, 0xffff)) == 0xffff) {
+		return (DDI_NOT_WELL_FORMED);
+	}
+
+	if ((d = pqi_find_target_dev(s, lun)) == NULL)
+		return (DDI_FAILURE);
+
+	scsi_device_hba_private_set(sd, d);
+
+	type = mdi_get_component_type(tgt_dip);
+	if (type == MDI_COMPONENT_CLIENT) {
+		char	wwid_str[64];
+
+		if ((pip = (mdi_pathinfo_t *)sd->sd_private) == NULL)
+			return (DDI_NOT_WELL_FORMED);
+
+		(void) snprintf(wwid_str, sizeof (wwid_str), "%" PRIx64,
+		    d->pd_sas_address);
+		(void) mdi_prop_update_string(pip, SCSI_ADDR_PROP_TARGET_PORT,
+		    wwid_str);
+	}
+
+	return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static void
+pqi_scsi_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
+    scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
+{
+}
+
+/*
+ * Notes:
+ *      - transport the command to the addressed SCSI target/lun device
+ *      - normal operation is to schedule the command to be transported,
+ *        and return TRAN_ACCEPT if this is successful.
+ *      - if NO_INTR, tran_start must poll device for command completion
+ */
+/*ARGSUSED*/
+static int
+pqi_start(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	boolean_t	poll	= ((pkt->pkt_flags & FLAG_NOINTR) != 0);
+	int		rc;
+	pqi_cmd_t	cmd	= PKT2CMD(pkt);
+	pqi_state_t	s	= ap->a_hba_tran->tran_hba_private;
+
+	ASSERT3P(cmd->pc_pkt, ==, pkt);
+	ASSERT3P(cmd->pc_softc, ==, s);
+
+	if (pqi_is_offline(s) || !cmd->pc_device->pd_online)
+		return (TRAN_FATAL_ERROR);
+
+	/*
+	 * Reinitialize some fields because the packet may have been
+	 * resubmitted.
+	 */
+	pkt->pkt_reason = CMD_CMPLT;
+	pkt->pkt_state = 0;
+	pkt->pkt_statistics = 0;
+
+	/* ---- Zero status byte ---- */
+	*(pkt->pkt_scbp) = 0;
+
+	if ((cmd->pc_flags & PQI_FLAG_DMA_VALID) != 0) {
+		ASSERT(cmd->pc_dma_count);
+		pkt->pkt_resid = cmd->pc_dma_count;
+
+		/* ---- Sync consistent packets first (only write data) ---- */
+		if (((cmd->pc_flags & PQI_FLAG_IO_IOPB) != 0) ||
+		    ((cmd->pc_flags & PQI_FLAG_IO_READ) == 0)) {
+			(void) ddi_dma_sync(cmd->pc_dmahdl, 0, 0,
+			    DDI_DMA_SYNC_FORDEV);
+		}
+	}
+
+	cmd->pc_target = ap->a_target;
+
+	mutex_enter(&s->s_mutex);
+	if (HBA_IS_QUIESCED(s) && !poll) {
+		mutex_exit(&s->s_mutex);
+		return (TRAN_BUSY);
+	}
+	mutex_exit(&s->s_mutex);
+
+	pqi_cmd_sm(cmd, PQI_CMD_QUEUED, B_TRUE);
+
+	rc = pqi_transport_command(s, cmd);
+
+	if (poll) {
+		boolean_t	qnotify;
+
+		if (rc == TRAN_ACCEPT) {
+			uint32_t	old_state;
+			int		timeo;
+
+			timeo = pkt->pkt_time ? pkt->pkt_time :
+			    SCSI_POLL_TIMEOUT;
+			timeo *= MILLISEC / 2;
+			old_state = pqi_disable_intr(s);
+			do {
+				drv_usecwait(MILLISEC / 2);
+				pqi_process_io_intr(s, &s->s_queue_groups[0]);
+				if (--timeo == 0) {
+					pkt->pkt_state |= STAT_TIMEOUT;
+					pkt->pkt_reason = CMD_TIMEOUT;
+					break;
+				}
+			} while (pkt->pkt_state == 0);
+			pqi_enable_intr(s, old_state);
+		}
+
+		scsi_hba_pkt_comp(pkt);
+
+		mutex_enter(&s->s_mutex);
+		qnotify = HBA_QUIESCED_PENDING(s);
+		mutex_exit(&s->s_mutex);
+
+		if (qnotify)
+			pqi_quiesced_notify(s);
+	}
+
+	return (rc);
+}
+
+static int
+pqi_scsi_reset(struct scsi_address *ap, int level)
+{
+	pqi_device_t	d;
+	pqi_state_t	s;
+	int		rval = FALSE;
+
+	s = ap->a_hba_tran->tran_hba_private;
+	switch (level) {
+	case RESET_TARGET:
+	case RESET_LUN:
+		if ((d = scsi_device_hba_private_get(ap->a.a_sd)) == NULL)
+			break;
+
+		if (pqi_lun_reset(s, d) == B_TRUE)
+			rval = TRUE;
+		break;
+
+	case RESET_BUS:
+	case RESET_ALL:
+		for (d = list_head(&s->s_devnodes); d != NULL;
+		    d = list_next(&s->s_devnodes, d)) {
+			(void) pqi_lun_reset(s, d);
+		}
+		rval = TRUE;
+		break;
+	}
+	return (rval);
+}
+
+/*
+ * abort handling:
+ *
+ * Notes:
+ *      - if pkt is not NULL, abort just that command
+ *      - if pkt is NULL, abort all outstanding commands for target
+ */
+static int
+pqi_scsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	boolean_t	qnotify	= B_FALSE;
+	pqi_state_t	s	= ADDR2PQI(ap);
+
+	if (pkt != NULL) {
+		/* ---- Abort single command ---- */
+		pqi_cmd_t	cmd = PKT2CMD(pkt);
+
+		mutex_enter(&cmd->pc_device->pd_mutex);
+		pqi_fail_cmd(cmd, CMD_ABORTED, STAT_ABORTED);
+		mutex_exit(&cmd->pc_device->pd_mutex);
+	} else {
+		abort_all(ap, s);
+	}
+	qnotify = HBA_QUIESCED_PENDING(s);
+
+	if (qnotify)
+		pqi_quiesced_notify(s);
+	return (1);
+}
+
+/*
+ * capability handling:
+ * (*tran_getcap).  Get the capability named, and return its value.
+ */
+/*ARGSUSED*/
+static int
+pqi_scsi_getcap(struct scsi_address *ap, char *cap, int tgtonly)
+{
+	pqi_state_t s = ap->a_hba_tran->tran_hba_private;
+
+	if (cap == NULL)
+		return (-1);
+	switch (scsi_hba_lookup_capstr(cap)) {
+	case SCSI_CAP_LUN_RESET:
+		return ((s->s_flags & PQI_HBA_LUN_RESET_CAP) != 0);
+	case SCSI_CAP_ARQ:
+		return ((s->s_flags & PQI_HBA_AUTO_REQUEST_SENSE) != 0);
+	case SCSI_CAP_UNTAGGED_QING:
+		return (1);
+	default:
+		return (-1);
+	}
+}
+
+/*
+ * (*tran_setcap).  Set the capability named to the value given.
+ */
+/*ARGSUSED*/
+static int
+pqi_scsi_setcap(struct scsi_address *ap, char *cap, int value, int tgtonly)
+{
+	pqi_state_t	s	= ADDR2PQI(ap);
+	int		rval	= FALSE;
+
+	if (cap == NULL)
+		return (-1);
+
+	switch (scsi_hba_lookup_capstr(cap)) {
+	case SCSI_CAP_ARQ:
+		if (value)
+			s->s_flags |= PQI_HBA_AUTO_REQUEST_SENSE;
+		else
+			s->s_flags &= ~PQI_HBA_AUTO_REQUEST_SENSE;
+		rval = 1;
+		break;
+
+	case SCSI_CAP_LUN_RESET:
+		if (value)
+			s->s_flags |= PQI_HBA_LUN_RESET_CAP;
+		else
+			s->s_flags &= ~PQI_HBA_LUN_RESET_CAP;
+		break;
+
+	default:
+		break;
+	}
+
+	return (rval);
+}
+
+/*ARGSUSED*/
+int
+pqi_cache_constructor(void *buf, void *un, int flags)
+{
+	pqi_cmd_t		c	= (pqi_cmd_t)buf;
+	pqi_state_t		s	= un;
+	int			(*callback)(caddr_t);
+
+	c->pc_softc = s;
+	callback = (flags == KM_SLEEP) ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT;
+
+	/* ---- Allocate a DMA handle for data transfers ---- */
+	if (ddi_dma_alloc_handle(s->s_dip, &s->s_msg_dma_attr, callback,
+	    NULL, &c->pc_dmahdl) != DDI_SUCCESS) {
+		dev_err(s->s_dip, CE_WARN, "Failed to alloc dma handle");
+		return (-1);
+	}
+	pqi_cmd_sm(c, PQI_CMD_CONSTRUCT, B_TRUE);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+void
+pqi_cache_destructor(void *buf, void *un)
+{
+	pqi_cmd_t	cmd = buf;
+	if (cmd->pc_dmahdl != NULL) {
+		(void) ddi_dma_unbind_handle(cmd->pc_dmahdl);
+		ddi_dma_free_handle(&cmd->pc_dmahdl);
+		cmd->pc_dmahdl = NULL;
+	}
+}
+
+/*
+ * tran_init_pkt(9E) - allocate scsi_pkt(9S) for command
+ *
+ * One of three possibilities:
+ *      - allocate scsi_pkt
+ *      - allocate scsi_pkt and DMA resources
+ *      - allocate DMA resources to an already-allocated pkt
+ */
+/*ARGSUSED*/
+static struct scsi_pkt *
+pqi_init_pkt(struct scsi_address *ap, struct scsi_pkt *pkt,
+    struct buf *bp, int cmdlen, int statuslen, int tgtlen, int flags,
+    int (*callback)(), caddr_t arg)
+{
+	pqi_cmd_t	cmd;
+	pqi_state_t	s;
+	int		kf = (callback == SLEEP_FUNC) ? KM_SLEEP : KM_NOSLEEP;
+	boolean_t	is_new = B_FALSE;
+	int		rc;
+	int		i;
+	pqi_device_t	devp;
+
+	s = ap->a_hba_tran->tran_hba_private;
+
+	if (pkt == NULL) {
+		ddi_dma_handle_t	saved_dmahdl;
+		pqi_cmd_state_t		saved_state;
+
+		if ((devp = scsi_device_hba_private_get(ap->a.a_sd)) == NULL)
+			return (NULL);
+		if ((cmd = kmem_cache_alloc(s->s_cmd_cache, kf)) == NULL)
+			return (NULL);
+
+		is_new = B_TRUE;
+		saved_dmahdl = cmd->pc_dmahdl;
+		saved_state = cmd->pc_cmd_state;
+
+		(void) memset(cmd, 0, sizeof (*cmd));
+
+		cmd->pc_dmahdl = saved_dmahdl;
+		cmd->pc_cmd_state = saved_state;
+
+		cmd->pc_device = devp;
+		cmd->pc_pkt = &cmd->pc_cached_pkt;
+		cmd->pc_softc = s;
+		cmd->pc_tgtlen = tgtlen;
+		cmd->pc_statuslen = statuslen;
+		cmd->pc_cmdlen = cmdlen;
+		cmd->pc_dma_count = 0;
+
+		pkt = cmd->pc_pkt;
+		pkt->pkt_ha_private = cmd;
+		pkt->pkt_address = *ap;
+		pkt->pkt_scbp = (uint8_t *)&cmd->pc_cmd_scb;
+		pkt->pkt_cdbp = cmd->pc_cdb;
+		pkt->pkt_private = (opaque_t)cmd->pc_tgt_priv;
+		if (pkt->pkt_time == 0)
+			pkt->pkt_time = SCSI_POLL_TIMEOUT;
+
+		if (cmdlen > sizeof (cmd->pc_cdb) ||
+		    statuslen > sizeof (cmd->pc_cmd_scb) ||
+		    tgtlen > sizeof (cmd->pc_tgt_priv)) {
+			if (cmd_ext_alloc(cmd, kf) != DDI_SUCCESS) {
+				dev_err(s->s_dip, CE_WARN,
+				    "extent allocation failed");
+				goto out;
+			}
+		}
+	} else {
+		cmd = PKT2CMD(pkt);
+		cmd->pc_flags &= PQI_FLAGS_PERSISTENT;
+	}
+	pqi_cmd_sm(cmd, PQI_CMD_INIT, B_TRUE);
+
+	/* ---- Handle partial DMA transfer ---- */
+	if (cmd->pc_nwin > 0) {
+		if (++cmd->pc_winidx >= cmd->pc_nwin)
+			return (NULL);
+		if (ddi_dma_getwin(cmd->pc_dmahdl, cmd->pc_winidx,
+		    &cmd->pc_dma_offset, &cmd->pc_dma_len, &cmd->pc_dmac,
+		    &cmd->pc_dmaccount) == DDI_FAILURE)
+			return (NULL);
+		goto handle_dma_cookies;
+	}
+
+	/* ---- Setup data buffer ---- */
+	if (bp != NULL && bp->b_bcount > 0 &&
+	    (cmd->pc_flags & PQI_FLAG_DMA_VALID) == 0) {
+		int	dma_flags;
+
+		ASSERT(cmd->pc_dmahdl != NULL);
+
+		if ((bp->b_flags & B_READ) != 0) {
+			cmd->pc_flags |= PQI_FLAG_IO_READ;
+			dma_flags = DDI_DMA_READ;
+		} else {
+			cmd->pc_flags &= ~PQI_FLAG_IO_READ;
+			dma_flags = DDI_DMA_WRITE;
+		}
+		if ((flags & PKT_CONSISTENT) != 0) {
+			cmd->pc_flags |= PQI_FLAG_IO_IOPB;
+			dma_flags |= DDI_DMA_CONSISTENT;
+		}
+		if ((flags & PKT_DMA_PARTIAL) != 0) {
+			dma_flags |= DDI_DMA_PARTIAL;
+		}
+		rc = ddi_dma_buf_bind_handle(cmd->pc_dmahdl, bp,
+		    dma_flags, callback, arg, &cmd->pc_dmac,
+		    &cmd->pc_dmaccount);
+
+		if (rc == DDI_DMA_PARTIAL_MAP) {
+			(void) ddi_dma_numwin(cmd->pc_dmahdl, &cmd->pc_nwin);
+			cmd->pc_winidx = 0;
+			(void) ddi_dma_getwin(cmd->pc_dmahdl, cmd->pc_winidx,
+			    &cmd->pc_dma_offset, &cmd->pc_dma_len,
+			    &cmd->pc_dmac, &cmd->pc_dmaccount);
+		} else if (rc != 0 && rc != DDI_DMA_MAPPED) {
+			switch (rc) {
+			case DDI_DMA_NORESOURCES:
+				bioerror(bp, 0);
+				break;
+			case DDI_DMA_BADATTR:
+			case DDI_DMA_NOMAPPING:
+				bioerror(bp, EFAULT);
+				break;
+			case DDI_DMA_TOOBIG:
+			default:
+				bioerror(bp, EINVAL);
+				break;
+			}
+			goto out;
+		}
+
+handle_dma_cookies:
+		ASSERT(cmd->pc_dmaccount > 0);
+		if (cmd->pc_dmaccount >
+		    (sizeof (cmd->pc_cached_cookies) /
+		    sizeof (ddi_dma_cookie_t))) {
+			dev_err(s->s_dip, CE_WARN,
+			    "invalid cookie count: %d", cmd->pc_dmaccount);
+			goto out;
+		}
+		if (cmd->pc_dmaccount >
+		    (s->s_sg_chain_buf_length / sizeof (pqi_sg_entry_t))) {
+			dev_err(s->s_dip, CE_WARN,
+			    "Cookie(0x%x) verses SG(0x%lx) mismatch",
+			    cmd->pc_dmaccount,
+			    (unsigned long int)s->s_sg_chain_buf_length /
+			    sizeof (pqi_sg_entry_t));
+			goto out;
+		}
+
+		cmd->pc_flags |= PQI_FLAG_DMA_VALID;
+		cmd->pc_dma_count = cmd->pc_dmac.dmac_size;
+		cmd->pc_cached_cookies[0] = cmd->pc_dmac;
+
+		for (i = 1; i < cmd->pc_dmaccount; i++) {
+			ddi_dma_nextcookie(cmd->pc_dmahdl, &cmd->pc_dmac);
+			cmd->pc_cached_cookies[i] = cmd->pc_dmac;
+			cmd->pc_dma_count += cmd->pc_dmac.dmac_size;
+		}
+
+		pkt->pkt_resid = bp->b_bcount - cmd->pc_dma_count;
+	}
+
+	return (pkt);
+out:
+	pqi_cmd_sm(cmd, PQI_CMD_FATAL, B_TRUE);
+	if (is_new == B_TRUE)
+		pqi_destroy_pkt(ap, pkt);
+	return (NULL);
+}
+
+/*
+ * tran_destroy_pkt(9E) - scsi_pkt(9s) deallocation
+ *
+ * Notes:
+ *      - also frees DMA resources if allocated
+ *      - implicit DMA synchonization
+ */
+/*ARGSUSED*/
+static void
+pqi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	pqi_cmd_t	c = PKT2CMD(pkt);
+	pqi_state_t	s = ADDR2PQI(ap);
+
+	if ((c->pc_flags & PQI_FLAG_DMA_VALID) != 0) {
+		c->pc_flags &= ~PQI_FLAG_DMA_VALID;
+		(void) ddi_dma_unbind_handle(c->pc_dmahdl);
+	}
+	cmd_ext_free(c);
+	pqi_cmd_sm(c, PQI_CMD_DESTRUCT, B_TRUE);
+
+	kmem_cache_free(s->s_cmd_cache, c);
+}
+
+/*
+ * tran_dmafree(9E) - deallocate DMA resources allocated for command
+ */
+/*ARGSUSED*/
+static void
+pqi_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	pqi_cmd_t	cmd = PKT2CMD(pkt);
+
+	if (cmd->pc_flags & PQI_FLAG_DMA_VALID) {
+		cmd->pc_flags &= ~PQI_FLAG_DMA_VALID;
+		(void) ddi_dma_unbind_handle(cmd->pc_dmahdl);
+	}
+}
+
+/*
+ * tran_sync_pkt(9E) - explicit DMA synchronization
+ */
+/*ARGSUSED*/
+static void
+pqi_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	pqi_cmd_t	cmd = PKT2CMD(pkt);
+
+	if (cmd->pc_dmahdl != NULL) {
+		(void) ddi_dma_sync(cmd->pc_dmahdl, 0, 0,
+		    (cmd->pc_flags & PQI_FLAG_IO_READ) ? DDI_DMA_SYNC_FORCPU :
+		    DDI_DMA_SYNC_FORDEV);
+	}
+}
+
+static int
+pqi_reset_notify(struct scsi_address *ap, int flag,
+    void (*callback)(caddr_t), caddr_t arg)
+{
+	pqi_state_t	s = ADDR2PQI(ap);
+
+	return (scsi_hba_reset_notify_setup(ap, flag, callback, arg,
+	    &s->s_mutex, &s->s_reset_notify_listf));
+}
+
+/*
+ * Device / Hotplug control
+ */
+/*ARGSUSED*/
+static int
+pqi_quiesce(dev_info_t *dip)
+{
+	pqi_state_t	s;
+	scsi_hba_tran_t	*tran;
+
+	if ((tran = ddi_get_driver_private(dip)) == NULL ||
+	    (s = TRAN2PQI(tran)) == NULL)
+		return (-1);
+
+	mutex_enter(&s->s_mutex);
+	if (!HBA_IS_QUIESCED(s))
+		s->s_flags |= PQI_HBA_QUIESCED;
+
+	if (s->s_cmd_queue_len != 0) {
+		/* ---- Outstanding commands present, wait ---- */
+		s->s_flags |= PQI_HBA_QUIESCED_PENDING;
+		cv_wait(&s->s_quiescedvar, &s->s_mutex);
+		ASSERT0(s->s_cmd_queue_len);
+	}
+	mutex_exit(&s->s_mutex);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+pqi_unquiesce(dev_info_t *dip)
+{
+	pqi_state_t	s;
+	scsi_hba_tran_t	*tran;
+
+	if ((tran = ddi_get_driver_private(dip)) == NULL ||
+	    (s = TRAN2PQI(tran)) == NULL)
+		return (-1);
+
+	mutex_enter(&s->s_mutex);
+	if (!HBA_IS_QUIESCED(s)) {
+		mutex_exit(&s->s_mutex);
+		return (0);
+	}
+	ASSERT0(s->s_cmd_queue_len);
+	s->s_flags &= ~PQI_HBA_QUIESCED;
+	mutex_exit(&s->s_mutex);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+pqi_bus_config(dev_info_t *pdip, uint_t flag,
+    ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
+{
+	scsi_hba_tran_t	*tran;
+	pqi_state_t	s;
+	int		circ	= 0;
+	int		circ1	= 0;
+	int		ret	= NDI_FAILURE;
+	long		target;
+	pqi_device_t	d;
+
+	tran = ddi_get_driver_private(pdip);
+	s = tran->tran_hba_private;
+	if (pqi_is_offline(s))
+		return (NDI_FAILURE);
+
+	ndi_devi_enter(scsi_vhci_dip, &circ1);
+	ndi_devi_enter(pdip, &circ);
+	switch (op) {
+	case BUS_CONFIG_ONE:
+		if (decode_to_target(arg, &target) == B_TRUE) {
+			d = pqi_find_target_dev(s, target);
+			if (d != NULL)
+				ret = config_one(pdip, s, d, childp);
+		} else {
+			dev_err(s->s_dip, CE_WARN, "Couldn't decode %s",
+			    (char *)arg);
+		}
+		flag |= NDI_MDI_FALLBACK;
+		break;
+
+	case BUS_CONFIG_DRIVER:
+	case BUS_CONFIG_ALL:
+		ret = pqi_config_all(pdip, s);
+		break;
+	default:
+		ret = NDI_FAILURE;
+	}
+	if (ret == NDI_SUCCESS)
+		ret = ndi_busop_bus_config(pdip, flag, op, arg, childp, 0);
+	ndi_devi_exit(pdip, circ);
+	ndi_devi_exit(scsi_vhci_dip, circ1);
+
+	return (ret);
+}
+
+pqi_device_t
+pqi_find_target_dev(pqi_state_t s, int target)
+{
+	pqi_device_t d;
+
+	/*
+	 * Should switch to indexed array of devices that can grow
+	 * as needed.
+	 */
+	for (d = list_head(&s->s_devnodes); d != NULL;
+	    d = list_next(&s->s_devnodes, d)) {
+		if (d->pd_target == target && d->pd_online)
+			break;
+	}
+	return (d);
+}
+
+int
+pqi_config_all(dev_info_t *pdip, pqi_state_t s)
+{
+	pqi_device_t d;
+
+	/*
+	 * Make sure we bring the available devices into play first. These
+	 * might be brand new devices just hotplugged into the system or
+	 * they could be devices previously offlined because either they
+	 * were pulled from an enclosure or a cable to the enclosure was
+	 * pulled.
+	 */
+	for (d = list_head(&s->s_devnodes); d != NULL;
+	    d = list_next(&s->s_devnodes, d)) {
+		if (d->pd_online)
+			(void) config_one(pdip, s, d, NULL);
+	}
+
+	/*
+	 * Now deal with devices that we had previously known about, but are
+	 * no longer available.
+	 */
+	for (d = list_head(&s->s_devnodes); d != NULL;
+	    d = list_next(&s->s_devnodes, d)) {
+		if (!d->pd_online)
+			(void) config_one(pdip, s, d, NULL);
+	}
+
+	return (NDI_SUCCESS);
+}
+
+void
+pqi_quiesced_notify(pqi_state_t s)
+{
+	mutex_enter(&s->s_mutex);
+	if (s->s_cmd_queue_len == 0 &&
+	    (s->s_flags & PQI_HBA_QUIESCED_PENDING) != 0) {
+		s->s_flags &= ~PQI_HBA_QUIESCED_PENDING;
+		cv_broadcast(&s->s_quiescedvar);
+	}
+	mutex_exit(&s->s_mutex);
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Support routines used only by the trans_xxx routines		|
+ * []------------------------------------------------------------------[]
+ */
+
+static void
+cmd_timeout_scan(void *v)
+{
+	pqi_state_t	s = v;
+	pqi_device_t	d;
+	pqi_cmd_t	cmd;
+	hrtime_t	now = gethrtime();
+	list_t		to_scan;
+
+	mutex_enter(&s->s_mutex);
+	for (d = list_head(&s->s_devnodes); d != NULL;
+	    d = list_next(&s->s_devnodes, d)) {
+
+		list_create(&to_scan, sizeof (struct pqi_cmd),
+		    offsetof(struct pqi_cmd, pc_list));
+
+		mutex_enter(&d->pd_mutex);
+		list_move_tail(&to_scan, &d->pd_cmd_list);
+
+		while ((cmd = list_remove_head(&to_scan)) != NULL) {
+			if (cmd->pc_expiration < now) {
+				struct scsi_pkt	*pkt	= CMD2PKT(cmd);
+
+				pkt->pkt_reason = CMD_TIMEOUT;
+				pkt->pkt_statistics = STAT_TIMEOUT;
+
+				/*
+				 * Insert the command back onto the list, with
+				 * the lock held, so that the state machine
+				 * can do its processing which removes the
+				 * command from the list and calls pkt_comp.
+				 */
+				list_insert_tail(&d->pd_cmd_list, cmd);
+				pqi_cmd_sm(cmd, PQI_CMD_FATAL, B_FALSE);
+
+			} else {
+				/*
+				 * Once a command's experiation date is in
+				 * the future this command and all remaining
+				 * commands on the chain are in the future as
+				 * well. So, add them back to the device
+				 * command list lock, stock, and barrel. Then
+				 * stop processing for this command.
+				 */
+				list_insert_tail(&d->pd_cmd_list, cmd);
+				list_move_tail(&d->pd_cmd_list, &to_scan);
+				break;
+			}
+		}
+		mutex_exit(&d->pd_mutex);
+	}
+
+	/*
+	 * Certain commands are issued and run serially through the driver.
+	 * These all should complete no matter what since they are commands
+	 * which are actually sent to the HBA. Yet, there have been cases
+	 * where the HBA failed to respond. So, if the time is past the
+	 * expired time mark the IO has having a timeout error and call the
+	 * return function.
+	 */
+	if (s->s_sync_io != NULL &&
+	    s->s_sync_expire < now) {
+		s->s_sync_io->io_status = PQI_DATA_IN_OUT_TIMEOUT;
+		s->s_sync_io->io_cb(s->s_sync_io, s->s_sync_io->io_context);
+	}
+
+	mutex_exit(&s->s_mutex);
+	s->s_cmd_timeout = timeout(cmd_timeout_scan, s,
+	    CMD_TIMEOUT_SCAN_SECS * drv_usectohz(MICROSEC));
+}
+
+static boolean_t
+decode_to_target(void *arg, long *target)
+{
+	char	*ptr = arg;
+	char	*tgt_ptr;
+
+	if (strncmp(NAME_DISK, ptr, sizeof (NAME_DISK) - 1) == 0) {
+		if ((tgt_ptr = strrchr(ptr, '@')) != NULL &&
+		    ddi_strtol(tgt_ptr + 1, NULL, 16, target) == 0)
+			return (B_TRUE);
+		else
+			return (B_FALSE);
+	}
+	if (strncmp(NAME_ENCLOSURE, ptr, sizeof (NAME_ENCLOSURE) - 1) == 0) {
+		if ((tgt_ptr = strrchr(ptr, ',')) != NULL &&
+		    ddi_strtol(tgt_ptr + 1, NULL, 16, target) == 0)
+			return (B_TRUE);
+		else
+			return (B_FALSE);
+	}
+	return (B_FALSE);
+}
+
+/*ARGSUSED*/
+static void
+abort_all(struct scsi_address *ap, pqi_state_t s)
+{
+	pqi_device_t	devp;
+
+	if ((devp = pqi_find_target_dev(s, ap->a_target)) == NULL)
+		return;
+
+	pqi_fail_drive_cmds(devp);
+}
+
+static boolean_t
+create_phys_lun(pqi_state_t s, pqi_device_t d,
+    struct scsi_inquiry *inq, dev_info_t **childp)
+{
+	char		**compatible	= NULL;
+	char		*nodename	= NULL;
+	char		*scsi_binding_set;
+	int		ncompatible	= 0;
+	dev_info_t	*dip;
+	char		*wwn_str;
+	int		rval;
+
+	/* ---- get the 'scsi-binding-set' property ---- */
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, s->s_dip,
+	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "scsi-binding-set",
+	    &scsi_binding_set) != DDI_PROP_SUCCESS) {
+		scsi_binding_set = NULL;
+	}
+
+	/* ---- At this point we have a new device not in our list ---- */
+	scsi_hba_nodename_compatible_get(inq, scsi_binding_set,
+	    inq->inq_dtype, NULL, &nodename, &compatible, &ncompatible);
+	if (scsi_binding_set != NULL)
+		ddi_prop_free(scsi_binding_set);
+	if (nodename == NULL)
+		return (B_FALSE);
+
+	if (ndi_devi_alloc(s->s_dip, nodename, DEVI_SID_NODEID, &dip) !=
+	    NDI_SUCCESS) {
+		dev_err(s->s_dip, CE_WARN, "failed to alloc device instance");
+		goto free_nodename;
+	}
+
+	d->pd_dip = dip;
+	d->pd_pip = NULL;
+
+	if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, TARGET_PROP,
+	    ddi_get_instance(s->s_dip)) != DDI_PROP_SUCCESS) {
+		goto free_devi;
+	}
+
+	if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, LUN_PROP, d->pd_target) !=
+	    DDI_PROP_SUCCESS) {
+		goto free_devi;
+	}
+
+	if (ndi_prop_update_int64(DDI_DEV_T_NONE, dip, LUN64_PROP,
+	    (int64_t)d->pd_target) != DDI_PROP_SUCCESS) {
+		goto free_devi;
+	}
+
+	if (ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, COMPAT_PROP,
+	    compatible, ncompatible) != DDI_PROP_SUCCESS) {
+		goto free_devi;
+	}
+
+	wwn_str = kmem_zalloc(MAX_NAME_PROP_SIZE, KM_SLEEP);
+	(void) snprintf(wwn_str, MAX_NAME_PROP_SIZE, "w%016" PRIx64,
+	    d->pd_wwid);
+	rval = ndi_prop_update_string(DDI_DEV_T_NONE, dip,
+	    SCSI_ADDR_PROP_TARGET_PORT, wwn_str);
+	kmem_free(wwn_str, MAX_NAME_PROP_SIZE);
+	if (rval != DDI_PROP_SUCCESS)
+		goto free_devi;
+
+	if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, NDI_GUID, d->pd_guid) !=
+	    DDI_PROP_SUCCESS) {
+		goto free_devi;
+	}
+
+	if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, "pm-capable", 1) !=
+	    DDI_PROP_SUCCESS) {
+		goto free_devi;
+	}
+
+	if (ndi_devi_online(dip, NDI_ONLINE_ATTACH) != NDI_SUCCESS)
+		goto free_devi;
+
+	if (childp != NULL)
+		*childp = dip;
+
+	scsi_hba_nodename_compatible_free(nodename, compatible);
+	return (B_TRUE);
+
+free_devi:
+	ndi_prop_remove_all(dip);
+	(void) ndi_devi_free(dip);
+	d->pd_dip = NULL;
+free_nodename:
+	scsi_hba_nodename_compatible_free(nodename, compatible);
+	return (B_FALSE);
+}
+
+static boolean_t
+create_virt_lun(pqi_state_t s, pqi_device_t d, struct scsi_inquiry *inq,
+    dev_info_t **childp)
+{
+	char		*nodename;
+	char		**compatible;
+	int		ncompatible;
+	int		rval;
+	mdi_pathinfo_t	*pip		= NULL;
+	char		*guid_ptr;
+	char		wwid_str[17];
+	char		tgt_str[17];
+	int		instance = ddi_get_instance(s->s_dip);
+	dev_info_t	*lun_dip;
+	char		*old_guid;
+
+	if (d->pd_pip_offlined != NULL) {
+		lun_dip = mdi_pi_get_client(d->pd_pip_offlined);
+		ASSERT(lun_dip != NULL);
+
+		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, lun_dip,
+		    (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
+		    MDI_CLIENT_GUID_PROP, &old_guid) == DDI_SUCCESS) {
+			if (strncmp(d->pd_guid, old_guid,
+			    strlen(d->pd_guid)) == 0) {
+				/* ---- Same path came back online ---- */
+				(void) ddi_prop_free(old_guid);
+				if (mdi_pi_online(d->pd_pip_offlined, 0) ==
+				    DDI_SUCCESS) {
+					d->pd_pip = d->pd_pip_offlined;
+					d->pd_pip_offlined = NULL;
+					return (B_TRUE);
+				} else {
+					return (B_FALSE);
+				}
+			} else {
+				/* ---- Different device in slot ---- */
+				(void) ddi_prop_free(old_guid);
+				if (mdi_pi_offline(d->pd_pip_offlined, 0) !=
+				    DDI_SUCCESS) {
+					return (B_FALSE);
+				}
+				if (mdi_pi_free(d->pd_pip_offlined,
+				    MDI_CLIENT_FLAGS_NO_EVENT) != MDI_SUCCESS) {
+					return (B_FALSE);
+				}
+				d->pd_pip_offlined = NULL;
+			}
+		} else {
+			dev_err(s->s_dip, CE_WARN, "Can't get client-guid "
+			    "property for lun %d", d->pd_target);
+			return (B_FALSE);
+		}
+	}
+
+	scsi_hba_nodename_compatible_get(inq, "vhci", inq->inq_dtype, NULL,
+	    &nodename, &compatible, &ncompatible);
+	if (nodename == NULL)
+		return (B_FALSE);
+
+	if (d->pd_guid != NULL) {
+		guid_ptr = d->pd_guid;
+	} else {
+		(void) snprintf(wwid_str, sizeof (wwid_str), "%" PRIx64,
+		    d->pd_wwid);
+		guid_ptr = wwid_str;
+	}
+	(void) snprintf(tgt_str, sizeof (tgt_str), "%x", d->pd_target);
+	rval = mdi_pi_alloc_compatible(s->s_dip, nodename, guid_ptr, tgt_str,
+	    compatible, ncompatible, 0, &pip);
+	if (rval == MDI_SUCCESS) {
+		mdi_pi_set_phci_private(pip, (caddr_t)d);
+
+		if (mdi_prop_update_string(pip, MDI_GUID, guid_ptr) !=
+		    DDI_SUCCESS) {
+			dev_err(s->s_dip, CE_WARN,
+			    "unable to create property (MDI_GUID) for lun %d",
+			    d->pd_target);
+			goto cleanup;
+		}
+
+		if (mdi_prop_update_int(pip, TARGET_PROP, instance) !=
+		    DDI_SUCCESS) {
+			dev_err(s->s_dip, CE_WARN,
+			    "unable to create property (%s) for lun %d\n",
+			    TARGET_PROP, d->pd_target);
+			goto cleanup;
+		}
+
+		if (mdi_prop_update_int(pip, LUN_PROP, d->pd_target) !=
+		    DDI_SUCCESS) {
+			dev_err(s->s_dip, CE_WARN,
+			    "unable to create property (%s) for lun %d",
+			    LUN_PROP, d->pd_target);
+			goto cleanup;
+		}
+
+		if (mdi_prop_update_string_array(pip, COMPAT_PROP,
+		    compatible, ncompatible) != DDI_SUCCESS) {
+			dev_err(s->s_dip, CE_WARN,
+			    "unable to create property (%s) for lun %d",
+			    COMPAT_PROP, d->pd_target);
+			goto cleanup;
+		}
+
+		if (mdi_pi_online(pip, 0) == MDI_NOT_SUPPORTED)
+			goto cleanup;
+
+		d->pd_dip = NULL;
+		d->pd_pip = pip;
+	}
+
+	scsi_hba_nodename_compatible_free(nodename, compatible);
+	if (childp != NULL)
+		*childp = mdi_pi_get_client(pip);
+	return (B_TRUE);
+cleanup:
+	scsi_hba_nodename_compatible_free(nodename, compatible);
+	d->pd_pip = NULL;
+	d->pd_dip = NULL;
+	(void) mdi_prop_remove(pip, NULL);
+	(void) mdi_pi_free(pip, 0);
+	return (B_FALSE);
+}
+
+static int
+config_one(dev_info_t *pdip, pqi_state_t s, pqi_device_t d,
+    dev_info_t **childp)
+{
+	struct scsi_inquiry	inq;
+	boolean_t		rval;
+
+	/* ---- For now ignore logical devices ---- */
+	if (is_physical_dev(d) == B_FALSE)
+		return (NDI_FAILURE);
+
+	/* ---- Inquiry target ---- */
+	if (!d->pd_online ||
+	    pqi_scsi_inquiry(s, d, 0, &inq, sizeof (inq)) == B_FALSE) {
+
+		pqi_fail_drive_cmds(d);
+
+		if (d->pd_dip != NULL) {
+			(void) ndi_devi_offline(d->pd_dip,
+			    NDI_DEVFS_CLEAN | NDI_DEVI_REMOVE | NDI_DEVI_GONE);
+			d->pd_dip = NULL;
+		} else if (d->pd_pip != NULL) {
+			(void) mdi_pi_offline(d->pd_pip, 0);
+			d->pd_pip_offlined = d->pd_pip;
+			d->pd_pip = NULL;
+		}
+		return (NDI_FAILURE);
+	} else if (d->pd_dip != NULL) {
+		if (childp != NULL)
+			*childp = d->pd_dip;
+		return (NDI_SUCCESS);
+	} else if (d->pd_pip != NULL) {
+		if (childp != NULL)
+			*childp = mdi_pi_get_client(d->pd_pip);
+		return (NDI_SUCCESS);
+	}
+
+	d->pd_parent = pdip;
+	if (s->s_enable_mpxio)
+		rval = create_virt_lun(s, d, &inq, childp);
+
+	if (!s->s_enable_mpxio || (rval == B_FALSE))
+		rval = create_phys_lun(s, d, &inq, childp);
+
+	return ((rval == B_TRUE) ? NDI_SUCCESS : NDI_FAILURE);
+}
+
+static void
+cmd_ext_free(pqi_cmd_t cmd)
+{
+	struct scsi_pkt *pkt = CMD2PKT(cmd);
+
+	if ((cmd->pc_flags & PQI_FLAG_CDB_EXT) != 0) {
+		kmem_free(pkt->pkt_cdbp, cmd->pc_cmdlen);
+		cmd->pc_flags &= ~PQI_FLAG_CDB_EXT;
+	}
+	if ((cmd->pc_flags & PQI_FLAG_SCB_EXT) != 0) {
+		kmem_free(pkt->pkt_scbp, cmd->pc_statuslen);
+		cmd->pc_flags &= ~PQI_FLAG_SCB_EXT;
+	}
+	if ((cmd->pc_flags & PQI_FLAG_PRIV_EXT) != 0) {
+		kmem_free(pkt->pkt_private, cmd->pc_tgtlen);
+		cmd->pc_flags &= ~PQI_FLAG_PRIV_EXT;
+	}
+}
+
+static int
+cmd_ext_alloc(pqi_cmd_t cmd, int kf)
+{
+	struct scsi_pkt		*pkt = CMD2PKT(cmd);
+	void			*buf;
+
+	if (cmd->pc_cmdlen > sizeof (cmd->pc_cdb)) {
+		if ((buf = kmem_zalloc(cmd->pc_cmdlen, kf)) == NULL)
+			return (DDI_FAILURE);
+		pkt->pkt_cdbp = buf;
+		cmd->pc_flags |= PQI_FLAG_CDB_EXT;
+	}
+
+	if (cmd->pc_statuslen > sizeof (cmd->pc_cmd_scb)) {
+		if ((buf = kmem_zalloc(cmd->pc_statuslen, kf)) == NULL)
+			goto out;
+		pkt->pkt_scbp = buf;
+		cmd->pc_flags |= PQI_FLAG_SCB_EXT;
+	}
+
+	if (cmd->pc_tgtlen > sizeof (cmd->pc_tgt_priv)) {
+		if ((buf = kmem_zalloc(cmd->pc_tgtlen, kf)) == NULL)
+			goto out;
+		pkt->pkt_private = buf;
+		cmd->pc_flags |= PQI_FLAG_PRIV_EXT;
+	}
+
+	return (DDI_SUCCESS);
+
+out:
+	cmd_ext_free(cmd);
+
+	return (DDI_FAILURE);
+}
+
+static boolean_t
+is_physical_dev(pqi_device_t d)
+{
+	return (d->pd_phys_dev ? B_TRUE : B_FALSE);
+}
