| /* |
| * 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. |
| */ |
| 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); |
| } |
| } |