/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "ghd.h"
/*
* Local Function Prototypes
*/
static struct scsi_pkt *ghd_pktalloc(ccc_t *cccp, struct scsi_address *ap,
int cmdlen, int statuslen, int tgtlen,
int (*callback)(), caddr_t arg, int ccblen);
/*
* Round up all allocations so that we can guarantee
* long-long alignment. This is the same alignment
* provided by kmem_alloc().
*/
#define ROUNDUP(x) (((x) + 0x07) & ~0x07)
/*
* Private wrapper for gcmd_t
*/
/*
* round up the size so the HBA private area is on a 8 byte boundary
*/
#define GW_PADDED_LENGTH ROUNDUP(sizeof (gcmd_t))
typedef struct gcmd_padded_wrapper {
union {
gcmd_t gw_gcmd;
char gw_pad[GW_PADDED_LENGTH];
} gwrap;
} gwrap_t;
/*ARGSUSED*/
void
ghd_tran_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pktp)
{
gcmd_t *gcmdp = PKTP2GCMDP(pktp);
int status;
if (gcmdp->cmd_dma_handle) {
status = ddi_dma_sync(gcmdp->cmd_dma_handle, 0, 0,
(gcmdp->cmd_dma_flags & DDI_DMA_READ) ?
DDI_DMA_SYNC_FORCPU : DDI_DMA_SYNC_FORDEV);
if (status != DDI_SUCCESS) {
cmn_err(CE_WARN, "ghd_tran_sync_pkt() fail\n");
}
}
}
static struct scsi_pkt *
ghd_pktalloc(ccc_t *cccp,
struct scsi_address *ap,
int cmdlen,
int statuslen,
int tgtlen,
int (*callback)(),
caddr_t arg,
int ccblen)
{
gtgt_t *gtgtp = ADDR2GTGTP(ap);
struct scsi_pkt *pktp;
gcmd_t *gcmdp;
gwrap_t *gwp;
int gwrap_len;
gwrap_len = sizeof (gwrap_t) + ROUNDUP(ccblen);
/* allocate everything from kmem pool */
pktp = scsi_hba_pkt_alloc(cccp->ccc_hba_dip, ap, cmdlen, statuslen,
tgtlen, gwrap_len, callback, arg);
if (pktp == NULL) {
return (NULL);
}
/* get the ptr to the HBA specific buffer */
gwp = (gwrap_t *)(pktp->pkt_ha_private);
/* get the ptr to the GHD specific buffer */
gcmdp = &gwp->gwrap.gw_gcmd;
ASSERT((caddr_t)gwp == (caddr_t)gcmdp);
/*
* save the ptr to HBA private area and initialize the rest
* of the gcmd_t members
*/
GHD_GCMD_INIT(gcmdp, (void *)(gwp + 1), gtgtp);
/*
* save the the scsi_pkt ptr in gcmd_t.
*/
gcmdp->cmd_pktp = pktp;
/*
* callback to the HBA driver so it can initalize its
* buffer and return the ptr to my cmd_t structure which is
* probably embedded in its buffer.
*/
if (!(*cccp->ccc_ccballoc)(gtgtp, gcmdp, cmdlen, statuslen, tgtlen,
ccblen)) {
scsi_hba_pkt_free(ap, pktp);
return (NULL);
}
return (pktp);
}
/*
* packet free
*/
/*ARGSUSED*/
void
ghd_pktfree(ccc_t *cccp,
struct scsi_address *ap,
struct scsi_pkt *pktp)
{
GDBG_PKT(("ghd_pktfree: cccp 0x%p ap 0x%p pktp 0x%p\n",
(void *)cccp, (void *)ap, (void *)pktp));
/* free any extra resources allocated by the HBA */
(*cccp->ccc_ccbfree)(PKTP2GCMDP(pktp));
/* free the scsi_pkt and the GHD and HBA private areas */
scsi_hba_pkt_free(ap, pktp);
}
struct scsi_pkt *
ghd_tran_init_pkt_attr(ccc_t *cccp,
struct scsi_address *ap,
struct scsi_pkt *pktp,
struct buf *bp,
int cmdlen,
int statuslen,
int tgtlen,
int flags,
int (*callback)(),
caddr_t arg,
int ccblen,
ddi_dma_attr_t *sg_attrp)
{
gcmd_t *gcmdp;
int new_pkt;
uint_t xfercount;
ASSERT(callback == NULL_FUNC || callback == SLEEP_FUNC);
/*
* Allocate a pkt
*/
if (pktp == NULL) {
pktp = ghd_pktalloc(cccp, ap, cmdlen, statuslen, tgtlen,
callback, arg, ccblen);
if (pktp == NULL)
return (NULL);
new_pkt = TRUE;
} else {
new_pkt = FALSE;
}
gcmdp = PKTP2GCMDP(pktp);
GDBG_PKT(("ghd_tran_init_pkt_attr: gcmdp 0x%p dma_handle 0x%p\n",
(void *)gcmdp, (void *)gcmdp->cmd_dma_handle));
/*
* free stale DMA window if necessary.
*/
if (cmdlen && gcmdp->cmd_dma_handle) {
/* release the old DMA resources */
ghd_dmafree_attr(gcmdp);
}
/*
* Set up dma info if there's any data and
* if the device supports DMA.
*/
GDBG_PKT(("ghd_tran_init_pkt: gcmdp 0x%p bp 0x%p limp 0x%p\n",
(void *)gcmdp, (void *)bp, (void *)sg_attrp));
if (bp && bp->b_bcount && sg_attrp) {
int dma_flags;
/* check direction for data transfer */
if (bp->b_flags & B_READ)
dma_flags = DDI_DMA_READ;
else
dma_flags = DDI_DMA_WRITE;
/* check dma option flags */
if (flags & PKT_CONSISTENT)
dma_flags |= DDI_DMA_CONSISTENT;
if (flags & PKT_DMA_PARTIAL)
dma_flags |= DDI_DMA_PARTIAL;
if (gcmdp->cmd_dma_handle == NULL) {
if (!ghd_dma_buf_bind_attr(cccp, gcmdp, bp, dma_flags,
callback, arg, sg_attrp)) {
if (new_pkt)
ghd_pktfree(cccp, ap, pktp);
return (NULL);
}
}
/* map the buffer and/or create the scatter/gather list */
if (!ghd_dmaget_attr(cccp, gcmdp,
bp->b_bcount - gcmdp->cmd_totxfer,
sg_attrp->dma_attr_sgllen, &xfercount)) {
if (new_pkt)
ghd_pktfree(cccp, ap, pktp);
return (NULL);
}
pktp->pkt_resid = bp->b_bcount - gcmdp->cmd_totxfer;
} else {
pktp->pkt_resid = 0;
}
return (pktp);
}