root/usr/src/uts/intel/io/dktp/hba/ghd/ghd_scsa.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 "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);
}