view usr/src/uts/common/io/scsi/impl/scsi_resource.c @ 10696:cd0f390dd9e2

PSARC 2008/672 thebe SAS/SATA driver PSARC 2008/755 ddi_ssoft_state(9F) and ddi_isoft_state(9F) PSARC 2008/764 Cfgadm SCSI-Plugin MPxIO Support PSARC 2009/125 scsi_device property interfaces 6726110 pmcs driver (driver for thebe) 6726867 SCSAv3
author dh142964 <David.Hollister@Sun.COM>
date Wed, 30 Sep 2009 13:40:27 -0600
parents 051184299af5
children
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/scsi/scsi.h>
#include <sys/vtrace.h>


#define	A_TO_TRAN(ap)	((ap)->a_hba_tran)
#define	P_TO_TRAN(pkt)	((pkt)->pkt_address.a_hba_tran)
#define	P_TO_ADDR(pkt)	(&((pkt)->pkt_address))

/*
 * Callback id
 */
uintptr_t scsi_callback_id = 0;

extern ddi_dma_attr_t scsi_alloc_attr;

struct buf *
scsi_alloc_consistent_buf(struct scsi_address *ap,
    struct buf *in_bp, size_t datalen, uint_t bflags,
    int (*callback)(caddr_t), caddr_t callback_arg)
{
	dev_info_t	*pdip;
	struct		buf *bp;
	int		kmflag;
	size_t		rlen;

	TRACE_0(TR_FAC_SCSI_RES, TR_SCSI_ALLOC_CONSISTENT_BUF_START,
	    "scsi_alloc_consistent_buf_start");

	if (!in_bp) {
		kmflag = (callback == SLEEP_FUNC) ? KM_SLEEP : KM_NOSLEEP;
		if ((bp = getrbuf(kmflag)) == NULL) {
			goto no_resource;
		}
	} else {
		bp = in_bp;

		/* we are establishing a new buffer memory association */
		bp->b_flags &= ~(B_PAGEIO | B_PHYS | B_REMAPPED | B_SHADOW);
		bp->b_proc = NULL;
		bp->b_pages = NULL;
		bp->b_shadow = NULL;
	}

	/* limit bits that can be set by bflags argument */
	ASSERT(!(bflags & ~(B_READ | B_WRITE)));
	bflags &= (B_READ | B_WRITE);
	bp->b_un.b_addr = 0;

	if (datalen) {
		pdip = (A_TO_TRAN(ap))->tran_hba_dip;

		/*
		 * use i_ddi_mem_alloc() for now until we have an interface to
		 * allocate memory for DMA which doesn't require a DMA handle.
		 * ddi_iopb_alloc() is obsolete and we want more flexibility in
		 * controlling the DMA address constraints.
		 */
		while (i_ddi_mem_alloc(pdip, &scsi_alloc_attr, datalen,
		    ((callback == SLEEP_FUNC) ? 1 : 0), 0, NULL,
		    &bp->b_un.b_addr, &rlen, NULL) != DDI_SUCCESS) {
			if (callback == SLEEP_FUNC) {
				delay(drv_usectohz(10000));
			} else {
				if (!in_bp)
					freerbuf(bp);
				goto no_resource;
			}
		}
		bp->b_flags |= bflags;
	}
	bp->b_bcount = datalen;
	bp->b_resid = 0;

	TRACE_0(TR_FAC_SCSI_RES, TR_SCSI_ALLOC_CONSISTENT_BUF_END,
	    "scsi_alloc_consistent_buf_end");
	return (bp);

no_resource:

	if (callback != NULL_FUNC && callback != SLEEP_FUNC) {
		ddi_set_callback(callback, callback_arg,
		    &scsi_callback_id);
	}
	TRACE_0(TR_FAC_SCSI_RES,
	    TR_SCSI_ALLOC_CONSISTENT_BUF_RETURN1_END,
	    "scsi_alloc_consistent_buf_end (return1)");
	return (NULL);
}

void
scsi_free_consistent_buf(struct buf *bp)
{
	TRACE_0(TR_FAC_SCSI_RES, TR_SCSI_FREE_CONSISTENT_BUF_START,
	    "scsi_free_consistent_buf_start");
	if (!bp)
		return;
	if (bp->b_un.b_addr)
		i_ddi_mem_free((caddr_t)bp->b_un.b_addr, NULL);
	freerbuf(bp);
	if (scsi_callback_id != 0) {
		ddi_run_callback(&scsi_callback_id);
	}
	TRACE_0(TR_FAC_SCSI_RES, TR_SCSI_FREE_CONSISTENT_BUF_END,
	    "scsi_free_consistent_buf_end");
}

void
scsi_dmafree_attr(struct scsi_pkt *pktp)
{
	struct scsi_pkt_cache_wrapper *pktw =
	    (struct scsi_pkt_cache_wrapper *)pktp;

	if (pktw->pcw_flags & PCW_BOUND) {
		if (ddi_dma_unbind_handle(pktp->pkt_handle) !=
		    DDI_SUCCESS)
			cmn_err(CE_WARN, "scsi_dmafree_attr: "
			    "unbind handle failed");
		pktw->pcw_flags &= ~PCW_BOUND;
	}
	pktp->pkt_numcookies = 0;
	pktw->pcw_totalwin = 0;
}

struct buf *
scsi_pkt2bp(struct scsi_pkt *pkt)
{
	return (((struct scsi_pkt_cache_wrapper *)pkt)->pcw_bp);
}

int
scsi_dma_buf_bind_attr(struct scsi_pkt_cache_wrapper *pktw,
			struct buf	*bp,
			int		 dma_flags,
			int		(*callback)(),
			caddr_t		 arg)
{
	struct scsi_pkt *pktp = &(pktw->pcw_pkt);
	int	 status;

	/*
	 * First time, need to establish the handle.
	 */

	ASSERT(pktp->pkt_numcookies == 0);
	ASSERT(pktw->pcw_totalwin == 0);

	status = ddi_dma_buf_bind_handle(pktp->pkt_handle, bp, dma_flags,
	    callback, arg, &pktw->pcw_cookie,
	    &pktp->pkt_numcookies);

	switch (status) {
	case DDI_DMA_MAPPED:
		pktw->pcw_totalwin = 1;
		break;

	case DDI_DMA_PARTIAL_MAP:
		/* enable first call to ddi_dma_getwin */
		if (ddi_dma_numwin(pktp->pkt_handle,
		    &pktw->pcw_totalwin) != DDI_SUCCESS) {
			bp->b_error = 0;
			return (0);
		}
		break;

	case DDI_DMA_NORESOURCES:
		bp->b_error = 0;
		return (0);

	case DDI_DMA_TOOBIG:
		bioerror(bp, EINVAL);
		return (0);

	case DDI_DMA_NOMAPPING:
	case DDI_DMA_INUSE:
	default:
		bioerror(bp, EFAULT);
		return (0);
	}

	/* initialize the loop controls for scsi_dmaget_attr() */
	pktw->pcw_curwin = 0;
	pktw->pcw_total_xfer = 0;
	pktp->pkt_dma_flags = dma_flags;
	return (1);
}

#if defined(_DMA_USES_PHYSADDR)
int
scsi_dmaget_attr(struct scsi_pkt_cache_wrapper *pktw)
{
	struct scsi_pkt *pktp = &(pktw->pcw_pkt);

	int		status;
	int		num_segs = 0;
	ddi_dma_impl_t	*hp = (ddi_dma_impl_t *)pktp->pkt_handle;
	ddi_dma_cookie_t *cp;

	if (pktw->pcw_curwin != 0) {
		ddi_dma_cookie_t	cookie;

		/*
		 * start the next window, and get its first cookie
		 */
		status = ddi_dma_getwin(pktp->pkt_handle,
		    pktw->pcw_curwin, &pktp->pkt_dma_offset,
		    &pktp->pkt_dma_len, &cookie,
		    &pktp->pkt_numcookies);
		if (status != DDI_SUCCESS)
			return (0);
	}

	/*
	 * start the Scatter/Gather loop
	 */
	cp = hp->dmai_cookie - 1;
	pktp->pkt_dma_len = 0;
	for (;;) {

		/* take care of the loop-bookkeeping */
		pktp->pkt_dma_len += cp->dmac_size;
		num_segs++;
		/*
		 * if this was the last cookie in the current window
		 * set the loop controls start the next window and
		 * exit so the HBA can do this partial transfer
		 */
		if (num_segs >= pktp->pkt_numcookies) {
			pktw->pcw_curwin++;
			break;
		}

		cp++;
	}
	pktw->pcw_total_xfer += pktp->pkt_dma_len;
	pktp->pkt_cookies = hp->dmai_cookie - 1;
	hp->dmai_cookie = cp;

	return (1);
}
#endif

void scsi_free_cache_pkt(struct scsi_address *, struct scsi_pkt *);

struct scsi_pkt *
scsi_init_cache_pkt(struct scsi_address *ap, struct scsi_pkt *in_pktp,
    struct buf *bp, int cmdlen, int statuslen, int pplen,
    int flags, int (*callback)(caddr_t), caddr_t callback_arg)
{
	struct scsi_pkt_cache_wrapper *pktw;
	scsi_hba_tran_t *tranp = ap->a_hba_tran;
	int		(*func)(caddr_t);

	func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC;

	if (in_pktp == NULL) {
		int kf;

		if (callback == SLEEP_FUNC)
			kf = KM_SLEEP;
		else
			kf = KM_NOSLEEP;
		/*
		 * By using kmem_cache_alloc(), the layout of the
		 * scsi_pkt, scsi_pkt_cache_wrapper, hba private data,
		 * cdb, tgt driver private data, and status block is
		 * as below.
		 *
		 * This is a piece of contiguous memory starting from
		 * the first structure field scsi_pkt in the struct
		 * scsi_pkt_cache_wrapper, followed by the hba private
		 * data, pkt_cdbp, the tgt driver private data and
		 * pkt_scbp.
		 *
		 * |----------------------------|--------------------->
		 * |	struct scsi_pkt		|	struct
		 * |	......			|scsi_pkt_cache_wrapper
		 * |	pcw_flags		|
		 * |----------------------------|<---------------------
		 * |	hba private data	|tranp->tran_hba_len
		 * |----------------------------|
		 * |	pkt_cdbp		|DEFAULT_CDBLEN
		 * |----------------------------|
		 * |	tgt private data	|DEFAULT_PRIVLEN
		 * |----------------------------|
		 * |	pkt_scbp		|DEFAULT_SCBLEN
		 * |----------------------------|
		 *
		 * If the actual data length of the cdb, or the tgt
		 * driver private data, or the status block is bigger
		 * than the default data length, kmem_alloc() will be
		 * called to get extra space.
		 */
		pktw = kmem_cache_alloc(tranp->tran_pkt_cache_ptr,
		    kf);
		if (pktw == NULL)
			goto fail1;

		pktw->pcw_flags = 0;
		in_pktp = &(pktw->pcw_pkt);
		in_pktp->pkt_address = *ap;

		/*
		 * target drivers should initialize pkt_comp and
		 * pkt_time, but sometimes they don't so initialize
		 * them here to be safe.
		 */
		in_pktp->pkt_flags = 0;
		in_pktp->pkt_time = 0;
		in_pktp->pkt_resid = 0;
		in_pktp->pkt_state = 0;
		in_pktp->pkt_statistics = 0;
		in_pktp->pkt_reason = 0;
		in_pktp->pkt_dma_offset = 0;
		in_pktp->pkt_dma_len = 0;
		in_pktp->pkt_dma_flags = 0;
		in_pktp->pkt_path_instance = 0;
		ASSERT(in_pktp->pkt_numcookies == 0);
		pktw->pcw_curwin = 0;
		pktw->pcw_totalwin = 0;
		pktw->pcw_total_xfer = 0;

		in_pktp->pkt_cdblen = cmdlen;
		if ((tranp->tran_hba_flags & SCSI_HBA_TRAN_CDB) &&
		    (cmdlen > DEFAULT_CDBLEN)) {
			pktw->pcw_flags |= PCW_NEED_EXT_CDB;
			in_pktp->pkt_cdbp = kmem_alloc(cmdlen, kf);
			if (in_pktp->pkt_cdbp == NULL)
				goto fail2;
		}
		in_pktp->pkt_tgtlen = pplen;
		if (pplen > DEFAULT_PRIVLEN) {
			pktw->pcw_flags |= PCW_NEED_EXT_TGT;
			in_pktp->pkt_private = kmem_alloc(pplen, kf);
			if (in_pktp->pkt_private == NULL)
				goto fail3;
		}
		in_pktp->pkt_scblen = statuslen;
		if ((tranp->tran_hba_flags & SCSI_HBA_TRAN_SCB) &&
		    (statuslen > DEFAULT_SCBLEN)) {
			pktw->pcw_flags |= PCW_NEED_EXT_SCB;
			in_pktp->pkt_scbp = kmem_alloc(statuslen, kf);
			if (in_pktp->pkt_scbp == NULL)
				goto fail4;
		}
		if ((*tranp->tran_setup_pkt) (in_pktp,
		    func, NULL) == -1) {
				goto fail5;
		}
		if (cmdlen)
			bzero((void *)in_pktp->pkt_cdbp, cmdlen);
		if (pplen)
			bzero((void *)in_pktp->pkt_private, pplen);
		if (statuslen)
			bzero((void *)in_pktp->pkt_scbp, statuslen);
	} else
		pktw = (struct scsi_pkt_cache_wrapper *)in_pktp;

	if (bp && bp->b_bcount) {

		int dma_flags = 0;

		/*
		 * we need to transfer data, so we alloc dma resources
		 * for this packet
		 */
		/*CONSTCOND*/
		ASSERT(SLEEP_FUNC == DDI_DMA_SLEEP);
		/*CONSTCOND*/
		ASSERT(NULL_FUNC == DDI_DMA_DONTWAIT);

#if defined(_DMA_USES_PHYSADDR)
		/*
		 * with an IOMMU we map everything, so we don't
		 * need to bother with this
		 */
		if (tranp->tran_dma_attr.dma_attr_granular !=
		    pktw->pcw_granular) {

			ddi_dma_free_handle(&in_pktp->pkt_handle);
			if (ddi_dma_alloc_handle(tranp->tran_hba_dip,
			    &tranp->tran_dma_attr,
			    func, NULL,
			    &in_pktp->pkt_handle) != DDI_SUCCESS) {

				in_pktp->pkt_handle = NULL;
				return (NULL);
			}
			pktw->pcw_granular =
			    tranp->tran_dma_attr.dma_attr_granular;
		}
#endif

		if (in_pktp->pkt_numcookies == 0) {
			pktw->pcw_bp = bp;
			/*
			 * set dma flags; the "read" case must be first
			 * since B_WRITE isn't always be set for writes.
			 */
			if (bp->b_flags & B_READ) {
				dma_flags |= DDI_DMA_READ;
			} else {
				dma_flags |= DDI_DMA_WRITE;
			}
			if (flags & PKT_CONSISTENT)
				dma_flags |= DDI_DMA_CONSISTENT;
			if (flags & PKT_DMA_PARTIAL)
				dma_flags |= DDI_DMA_PARTIAL;

#if defined(__sparc)
			/*
			 * workaround for byte hole issue on psycho and
			 * schizo pre 2.1
			 */
			if ((bp->b_flags & B_READ) && ((bp->b_flags &
			    (B_PAGEIO|B_REMAPPED)) != B_PAGEIO) &&
			    (((uintptr_t)bp->b_un.b_addr & 0x7) ||
			    ((uintptr_t)bp->b_bcount & 0x7))) {
				dma_flags |= DDI_DMA_CONSISTENT;
			}
#endif
			if (!scsi_dma_buf_bind_attr(pktw, bp,
			    dma_flags, callback, callback_arg)) {
				return (NULL);
			} else {
				pktw->pcw_flags |= PCW_BOUND;
			}
		}

#if defined(_DMA_USES_PHYSADDR)
		if (!scsi_dmaget_attr(pktw)) {
			scsi_dmafree_attr(in_pktp);
			goto fail5;
		}
#else
		in_pktp->pkt_cookies = &pktw->pcw_cookie;
		in_pktp->pkt_dma_len = pktw->pcw_cookie.dmac_size;
		pktw->pcw_total_xfer += in_pktp->pkt_dma_len;
#endif
		ASSERT(in_pktp->pkt_numcookies <=
		    tranp->tran_dma_attr.dma_attr_sgllen);
		ASSERT(pktw->pcw_total_xfer <= bp->b_bcount);
		in_pktp->pkt_resid = bp->b_bcount -
		    pktw->pcw_total_xfer;

		ASSERT((in_pktp->pkt_resid % pktw->pcw_granular) ==
		    0);
	} else {
		/* !bp or no b_bcount */
		in_pktp->pkt_resid = 0;
	}
	return (in_pktp);

fail5:
	if (pktw->pcw_flags & PCW_NEED_EXT_SCB) {
		kmem_free(in_pktp->pkt_scbp, statuslen);
		in_pktp->pkt_scbp = (opaque_t)((char *)in_pktp +
		    tranp->tran_hba_len + DEFAULT_PRIVLEN +
		    sizeof (struct scsi_pkt_cache_wrapper));
		if ((A_TO_TRAN(ap))->tran_hba_flags & SCSI_HBA_TRAN_CDB)
			in_pktp->pkt_scbp = (opaque_t)((in_pktp->pkt_scbp) +
			    DEFAULT_CDBLEN);
		in_pktp->pkt_scblen = 0;
	}
fail4:
	if (pktw->pcw_flags & PCW_NEED_EXT_TGT) {
		kmem_free(in_pktp->pkt_private, pplen);
		in_pktp->pkt_tgtlen = 0;
		in_pktp->pkt_private = NULL;
	}
fail3:
	if (pktw->pcw_flags & PCW_NEED_EXT_CDB) {
		kmem_free(in_pktp->pkt_cdbp, cmdlen);
		in_pktp->pkt_cdbp = (opaque_t)((char *)in_pktp +
		    tranp->tran_hba_len +
		    sizeof (struct scsi_pkt_cache_wrapper));
		in_pktp->pkt_cdblen = 0;
	}
	pktw->pcw_flags &=
	    ~(PCW_NEED_EXT_CDB|PCW_NEED_EXT_TGT|PCW_NEED_EXT_SCB);
fail2:
	kmem_cache_free(tranp->tran_pkt_cache_ptr, pktw);
fail1:
	if (callback != NULL_FUNC && callback != SLEEP_FUNC) {
		ddi_set_callback(callback, callback_arg,
		    &scsi_callback_id);
	}

	return (NULL);
}

void
scsi_free_cache_pkt(struct scsi_address *ap, struct scsi_pkt *pktp)
{
	struct scsi_pkt_cache_wrapper *pktw;

	(*A_TO_TRAN(ap)->tran_teardown_pkt)(pktp);
	pktw = (struct scsi_pkt_cache_wrapper *)pktp;
	if (pktw->pcw_flags & PCW_BOUND)
		scsi_dmafree_attr(pktp);

	/*
	 * if we allocated memory for anything that wouldn't fit, free
	 * the memory and restore the pointers
	 */
	if (pktw->pcw_flags & PCW_NEED_EXT_SCB) {
		kmem_free(pktp->pkt_scbp, pktp->pkt_scblen);
		pktp->pkt_scbp = (opaque_t)((char *)pktp +
		    (A_TO_TRAN(ap))->tran_hba_len +
		    DEFAULT_PRIVLEN + sizeof (struct scsi_pkt_cache_wrapper));
		if ((A_TO_TRAN(ap))->tran_hba_flags & SCSI_HBA_TRAN_CDB)
			pktp->pkt_scbp = (opaque_t)((pktp->pkt_scbp) +
			    DEFAULT_CDBLEN);
		pktp->pkt_scblen = 0;
	}
	if (pktw->pcw_flags & PCW_NEED_EXT_TGT) {
		kmem_free(pktp->pkt_private, pktp->pkt_tgtlen);
		pktp->pkt_tgtlen = 0;
		pktp->pkt_private = NULL;
	}
	if (pktw->pcw_flags & PCW_NEED_EXT_CDB) {
		kmem_free(pktp->pkt_cdbp, pktp->pkt_cdblen);
		pktp->pkt_cdbp = (opaque_t)((char *)pktp +
		    (A_TO_TRAN(ap))->tran_hba_len +
		    sizeof (struct scsi_pkt_cache_wrapper));
		pktp->pkt_cdblen = 0;
	}
	pktw->pcw_flags &=
	    ~(PCW_NEED_EXT_CDB|PCW_NEED_EXT_TGT|PCW_NEED_EXT_SCB);
	kmem_cache_free(A_TO_TRAN(ap)->tran_pkt_cache_ptr, pktw);

	if (scsi_callback_id != 0) {
		ddi_run_callback(&scsi_callback_id);
	}

}


struct scsi_pkt *
scsi_init_pkt(struct scsi_address *ap, struct scsi_pkt *in_pktp,
    struct buf *bp, int cmdlen, int statuslen, int pplen,
    int flags, int (*callback)(caddr_t), caddr_t callback_arg)
{
	struct scsi_pkt *pktp;
	scsi_hba_tran_t *tranp = ap->a_hba_tran;
	int		(*func)(caddr_t);

	TRACE_5(TR_FAC_SCSI_RES, TR_SCSI_INIT_PKT_START,
"scsi_init_pkt_start: addr %p in_pktp %p cmdlen %d statuslen %d pplen %d",
	    ap, in_pktp, cmdlen, statuslen, pplen);

#if defined(__i386) || defined(__amd64)
	if (flags & PKT_CONSISTENT_OLD) {
		flags &= ~PKT_CONSISTENT_OLD;
		flags |= PKT_CONSISTENT;
	}
#endif

	func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC;

	pktp = (*tranp->tran_init_pkt) (ap, in_pktp, bp, cmdlen,
	    statuslen, pplen, flags, func, NULL);
	if (pktp == NULL) {
		if (callback != NULL_FUNC && callback != SLEEP_FUNC) {
			ddi_set_callback(callback, callback_arg,
			    &scsi_callback_id);
		}
	}

	TRACE_1(TR_FAC_SCSI_RES, TR_SCSI_INIT_PKT_END,
	    "scsi_init_pkt_end: pktp %p", pktp);
	return (pktp);
}

void
scsi_destroy_pkt(struct scsi_pkt *pkt)
{
	struct scsi_address	*ap = P_TO_ADDR(pkt);

	TRACE_1(TR_FAC_SCSI_RES, TR_SCSI_DESTROY_PKT_START,
	    "scsi_destroy_pkt_start: pkt %p", pkt);

	(*A_TO_TRAN(ap)->tran_destroy_pkt)(ap, pkt);

	if (scsi_callback_id != 0) {
		ddi_run_callback(&scsi_callback_id);
	}

	TRACE_0(TR_FAC_SCSI_RES, TR_SCSI_DESTROY_PKT_END,
	    "scsi_destroy_pkt_end");
}


/*
 *	Generic Resource Allocation Routines
 */

struct scsi_pkt *
scsi_resalloc(struct scsi_address *ap, int cmdlen, int statuslen,
    opaque_t dmatoken, int (*callback)())
{
	register struct	scsi_pkt *pkt;
	register scsi_hba_tran_t *tranp = ap->a_hba_tran;
	register int			(*func)(caddr_t);

	func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC;

	pkt = (*tranp->tran_init_pkt) (ap, NULL, (struct buf *)dmatoken,
	    cmdlen, statuslen, 0, 0, func, NULL);
	if (pkt == NULL) {
		if (callback != NULL_FUNC && callback != SLEEP_FUNC) {
			ddi_set_callback(callback, NULL, &scsi_callback_id);
		}
	}

	return (pkt);
}

struct scsi_pkt *
scsi_pktalloc(struct scsi_address *ap, int cmdlen, int statuslen,
    int (*callback)())
{
	struct scsi_pkt		*pkt;
	struct scsi_hba_tran	*tran = ap->a_hba_tran;
	register int			(*func)(caddr_t);

	func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC;

	pkt = (*tran->tran_init_pkt) (ap, NULL, NULL, cmdlen,
	    statuslen, 0, 0, func, NULL);
	if (pkt == NULL) {
		if (callback != NULL_FUNC && callback != SLEEP_FUNC) {
			ddi_set_callback(callback, NULL, &scsi_callback_id);
		}
	}

	return (pkt);
}

struct scsi_pkt *
scsi_dmaget(struct scsi_pkt *pkt, opaque_t dmatoken, int (*callback)())
{
	struct scsi_pkt		*new_pkt;
	register int		(*func)(caddr_t);

	func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC;

	new_pkt = (*P_TO_TRAN(pkt)->tran_init_pkt) (&pkt->pkt_address,
	    pkt, (struct buf *)dmatoken,
	    0, 0, 0, 0, func, NULL);
	ASSERT(new_pkt == pkt || new_pkt == NULL);
	if (new_pkt == NULL) {
		if (callback != NULL_FUNC && callback != SLEEP_FUNC) {
			ddi_set_callback(callback, NULL, &scsi_callback_id);
		}
	}

	return (new_pkt);
}


/*
 *	Generic Resource Deallocation Routines
 */

void
scsi_dmafree(struct scsi_pkt *pkt)
{
	register struct scsi_address	*ap = P_TO_ADDR(pkt);

	(*A_TO_TRAN(ap)->tran_dmafree)(ap, pkt);

	if (scsi_callback_id != 0) {
		ddi_run_callback(&scsi_callback_id);
	}
}

/*ARGSUSED*/
void
scsi_cache_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt)
{
	ASSERT(pkt->pkt_numcookies == 0 ||
	    ((struct scsi_pkt_cache_wrapper *)pkt)->pcw_flags & PCW_BOUND);
	ASSERT(pkt->pkt_handle != NULL);
	scsi_dmafree_attr(pkt);

	if (scsi_callback_id != 0) {
		ddi_run_callback(&scsi_callback_id);
	}
}

void
scsi_sync_pkt(struct scsi_pkt *pkt)
{
	register struct scsi_address	*ap = P_TO_ADDR(pkt);

	if (pkt->pkt_state & STATE_XFERRED_DATA)
		(*A_TO_TRAN(ap)->tran_sync_pkt)(ap, pkt);
}

/*ARGSUSED*/
void
scsi_sync_cache_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
{
	if (pkt->pkt_handle &&
	    (pkt->pkt_dma_flags & (DDI_DMA_WRITE | DDI_DMA_READ))) {
		(void) ddi_dma_sync(pkt->pkt_handle,
		    pkt->pkt_dma_offset, pkt->pkt_dma_len,
		    (pkt->pkt_dma_flags & DDI_DMA_WRITE) ?
		    DDI_DMA_SYNC_FORDEV : DDI_DMA_SYNC_FORCPU);
	}
}

void
scsi_resfree(struct scsi_pkt *pkt)
{
	register struct scsi_address	*ap = P_TO_ADDR(pkt);
	(*A_TO_TRAN(ap)->tran_destroy_pkt)(ap, pkt);

	if (scsi_callback_id != 0) {
		ddi_run_callback(&scsi_callback_id);
	}
}