root/usr/src/uts/common/io/cpqary3/cpqary3_transport.c
/*
 * 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 (C) 2013 Hewlett-Packard Development Company, L.P.
 */

#include <sys/sdt.h>
#include "cpqary3.h"

/*
 * Local Functions Definitions
 */

static int cpqary3_tgt_init(dev_info_t *, dev_info_t *, scsi_hba_tran_t *,
    struct scsi_device *);
static int cpqary3_tgt_probe(struct scsi_device *, int (*)());
static int cpqary3_transport(struct scsi_address *, struct scsi_pkt *);
static int cpqary3_reset(struct scsi_address *, int);
static int cpqary3_abort(struct scsi_address *, struct scsi_pkt *);
static int cpqary3_getcap(struct scsi_address *, char *, int);
static int cpqary3_setcap(struct scsi_address *, char *, int, int);
static int cpqary3_dma_alloc(cpqary3_t *, struct scsi_pkt *,
    struct buf *, int, int (*)());
static int cpqary3_dma_move(struct scsi_pkt *, struct buf *, cpqary3_t *);
static int cpqary3_handle_flag_nointr(cpqary3_cmdpvt_t *, struct scsi_pkt *);
static int cpqary3_poll(cpqary3_t *, uint32_t);
static void cpqary3_dmafree(struct scsi_address *, struct scsi_pkt *);
static void cpqary3_dma_sync(struct scsi_address *, struct scsi_pkt *);
static void cpqary3_destroy_pkt(struct scsi_address *, struct scsi_pkt *);
static struct scsi_pkt *cpqary3_init_pkt(struct scsi_address *,
    struct scsi_pkt *, struct buf *, int, int, int, int, int (*callback)(),
    caddr_t);
static int cpqary3_additional_cmd(struct scsi_pkt *scsi_pktp, cpqary3_t *);
void cpqary3_oscmd_complete(cpqary3_cmdpvt_t *);
static uint8_t cpqary3_is_scsi_read_write(struct scsi_pkt *scsi_pktp);

/*
 * External Variable Declarations
 */

extern ddi_dma_attr_t cpqary3_dma_attr;

/*
 * Function     :       cpqary3_init_hbatran
 * Description  :       This routine initializes the transport vector in the
 *                      SCSA architecture for entry ponts in this driver.
 * Called By    :       cpqary3_attach()
 * Parameters   :       per_controller
 * Calls        :       None
 * Return Values:       None
 */
void
cpqary3_init_hbatran(cpqary3_t *ctlr)
{
        scsi_hba_tran_t *hba_tran;

        ASSERT(ctlr != NULL);

        hba_tran = ctlr->hba_tran;

        /*
         * Memory for the transport vector has been allocated by now.
         * initialize all the entry points in this vector
         */

        hba_tran->tran_hba_private = (void *)ctlr;

        /* Target Driver Instance Initialization */
        hba_tran->tran_tgt_init = cpqary3_tgt_init;
        hba_tran->tran_tgt_probe = cpqary3_tgt_probe;

        /* Resource Allocation */
        hba_tran->tran_init_pkt = cpqary3_init_pkt;
        hba_tran->tran_destroy_pkt = cpqary3_destroy_pkt;
        hba_tran->tran_sync_pkt = cpqary3_dma_sync;
        hba_tran->tran_dmafree = cpqary3_dmafree;

        /* Command Transport */
        hba_tran->tran_start = cpqary3_transport;

        /* Capability Management */
        hba_tran->tran_getcap = cpqary3_getcap;
        hba_tran->tran_setcap = cpqary3_setcap;

        /* Abort and Reset */
        hba_tran->tran_reset = cpqary3_reset;
        hba_tran->tran_abort = cpqary3_abort;
}

/*
 * Function     :       cpqary3_tgt_init ()
 * Description  :       This routine validates the target ID.
 * Called By    :       cpqary3_init_hbatran()
 * Parameters   :       HBA-instance, target instance, transport vector,
 *                      scsi-device structure
 * Calls        :       cpqary3_detect_target_geometry(),
 *                      cpqary3_probe4targets()
 * Return Values:       DDI_SUCCESS : A Target exists at this ID.
 *                      DDI_FAILURE : No such target exists.
 */
/* ARGSUSED */
static int
cpqary3_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
    scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
{
        uint32_t        tid = SD2TGT(sd);
        uint32_t        lun = SD2LUN(sd);
        cpqary3_t       *ctlr;

        ctlr = (cpqary3_t *)hba_tran->tran_hba_private;

        /* HPQacucli Changes */

        extern int8_t   cpqary3_detect_target_geometry(cpqary3_t *);
        if ((CPQARY3_SUCCESS == cpqary3_probe4targets(ctlr)) &&
            (ctlr->num_of_targets > 0)) {
                (void) cpqary3_detect_target_geometry(ctlr);
        }

        /* HPQacucli Changes */
        /*
         * Validate the Target ID
         * Validate Lun --Ver1.10--
         * If not a valid target id, return FAILURE.
         * Derieve the per-controller
         */

        if ((tid >= CPQARY3_MAX_TGT) || (lun != 0)) {
                DTRACE_PROBE2(tgt_init_notsup,
                    cpqary3_t *, ctlr, uint32_t, tid);
                return (DDI_FAILURE);
        }

        /*
         * Check to see if a target structure corrresponding to this
         * target Id exists.(support only for Logical Drives and Controller)
         * if target exists, update target flags, return SUCCESS
         * is target does not exist, return FAILURE
         */

        mutex_enter(&ctlr->sw_mutex);

        if (!(ctlr->cpqary3_tgtp[tid])) {
                mutex_exit(&ctlr->sw_mutex);
                return (DDI_FAILURE);
        }

        ctlr->cpqary3_tgtp[tid]->tgt_dip = tgt_dip;
        ctlr->cpqary3_tgtp[tid]->ctlr_flags = CPQARY3_CAP_DISCON_ENABLED |
            CPQARY3_CAP_SYNC_ENABLED | CPQARY3_CAP_WIDE_XFER_ENABLED |
            CPQARY3_CAP_ARQ_ENABLED;

        mutex_exit(&ctlr->sw_mutex);

        DTRACE_PROBE1(tgt_init_done, uint32_t, tid);

        return (DDI_SUCCESS);
}

/*
 * Function     :       cpqary3_tgt_probe()
 * Description  :       This routine probes into the Target Details.
 * Called By    :       cpqary3_init_hbatran()
 * Parameters   :       scsi-device structure, calling function if any
 * Calls        :       None
 * Return Values:       value returned by scsi_hba_probe()
 */
static int
cpqary3_tgt_probe(struct scsi_device *sd, int (*waitfunc)())
{
#ifdef CPQARY3_DEBUG
        int     status;
#endif

        /*
         * Probe for the presence of the target, using the scsi_hba_probe().
         * It inturn issues the SCSI inquiry command that is serviced by our
         * driver
         */

        /* HPQacucli Changes */
        extern int8_t           cpqary3_detect_target_geometry(cpqary3_t *);
        struct scsi_hba_tran    *hba_tran = sd->sd_address.a_hba_tran;
        cpqary3_t               *ctlr = hba_tran->tran_hba_private;

        if ((CPQARY3_SUCCESS == cpqary3_probe4targets(ctlr)) &&
            (ctlr->num_of_targets > 0)) {
                (void) cpqary3_detect_target_geometry(ctlr);
        }
        /* HPQacucli Changes */

        return (scsi_hba_probe(sd, waitfunc));

#ifdef CPQARY3_DEBUG

        /* Comment the previous line of code */
        status = scsi_hba_probe(sd, waitfunc);
        cmn_err(CE_CONT, "CPQary3 : _tgt_probe : target = %d \n", SD2TGT(sd));
        cmn_err(CE_CONT, "CPQary3 : _tgt_probe : scsi_hba_probe returned %d \n",
            status);
        cmn_err(CE_CONT, "CPQary3 : _tgt_probe : Leaving \n");
        return (status);

#endif
}

/*
 * Function     :       cpqary3_init_pkt
 * Description  :       This routine allocates resources for a SCSI packet.
 * Called By    :       cpqary3_init_pkt()
 * Parameters   :       SCSI address, SCSI packet, buffer, CDB length,
 *                      SCB length, driver private length, flags modifier,
 *                      callback function, arguement for the callback function
 * Calls        :       cpqary3_dma_alloc(), cpqary3_dma_move()
 * Return Values:       allocated SCSI packet / NULL
 */
/* ARGSUSED */
static struct scsi_pkt *
cpqary3_init_pkt(struct scsi_address *sa, struct scsi_pkt *scsi_pktp,
    struct buf *bp, int cmdlen, int statuslen, int tgtlen,
    int flags, int (*callback)(), caddr_t arg)
{
        cpqary3_t       *cpqary3p;
        dev_info_t      *dip;
        cpqary3_pkt_t   *cpqary3_pktp;
        struct scsi_pkt *new_scsi_pktp;

        ASSERT(callback == NULL_FUNC || callback == SLEEP_FUNC);

        cpqary3p = SA2CTLR(sa);
        dip = cpqary3p->dip;

        /*
         * If the SCSI packet is NULL, allocate frresh resources to it.
         * Else, get the next available resources for the same
         */

        if (!scsi_pktp) {
                scsi_pktp = scsi_hba_pkt_alloc(dip, sa, cmdlen, statuslen,
                    tgtlen, sizeof (cpqary3_pkt_t), callback, NULL);
                if (!scsi_pktp)
                        return (NULL);

                cpqary3_pktp = (cpqary3_pkt_t *)scsi_pktp->pkt_ha_private;
                bzero(cpqary3_pktp, sizeof (cpqary3_pkt_t));

                cpqary3_pktp->scsi_cmd_pkt = scsi_pktp;

                /*
                 * Store the CDB length and sense data length in the
                 * pkt private
                 */
                cpqary3_pktp->cdb_len = cmdlen;
                cpqary3_pktp->scb_len = statuslen;
                cpqary3_pktp->cmd_dmahandle = NULL;
                cpqary3_pktp->memp = (cpqary3_cmdpvt_t *)NULL;

                /*
                 * Initialize to NULL all the fields of scsi_pktp, except
                 * pkt_scbp, pkt_cdbp, pkt_ha_private and pkt_private members.
                 */
                scsi_pktp->pkt_address = *sa;
                scsi_pktp->pkt_comp = (void (*) ())NULL;
                scsi_pktp->pkt_flags = 0;
                scsi_pktp->pkt_time = 0;
                scsi_pktp->pkt_resid = 0;
                scsi_pktp->pkt_statistics = 0;
                scsi_pktp->pkt_state = 0;
                scsi_pktp->pkt_reason = 0;

                if (flags & PKT_CONSISTENT)
                        cpqary3_pktp->cmd_flags |=  DDI_DMA_CONSISTENT;

                if (flags & PKT_DMA_PARTIAL)
                        cpqary3_pktp->cmd_flags |= DDI_DMA_PARTIAL;

                new_scsi_pktp = scsi_pktp;
        } else {
                new_scsi_pktp = NULL;
                cpqary3_pktp = (cpqary3_pkt_t *)scsi_pktp->pkt_ha_private;
        }

        cpqary3_pktp->bf = bp;

        /*
         * If any I/O is desired, Allocate/Move DMA resources for the SCSI
         * packet
         * If first time allocation for this SCSI packet, allocate fresh DMA
         * Else, move the already allocated DMA resources
         */
        if (bp && bp->b_bcount != 0) { /* I/O is desired */
                if (!cpqary3_pktp->cmd_dmahandle) { /* First time allocation */
                        if (cpqary3_dma_alloc(cpqary3p, scsi_pktp,
                            bp, flags, callback) == CPQARY3_FAILURE) {
                                if (new_scsi_pktp)
                                        scsi_hba_pkt_free(sa, new_scsi_pktp);
                                return ((struct scsi_pkt *)NULL);
                        }
                } else {
                        ASSERT(new_scsi_pktp == NULL);
                        if (CPQARY3_FAILURE ==
                            cpqary3_dma_move(scsi_pktp, bp, cpqary3p)) {
                                return ((struct scsi_pkt *)NULL);
                        }
                }
        }

        return (scsi_pktp);
}

/*
 * Function     :       cpqary3_dma_alloc()
 * Description  :       This routine services requests for memory (dynamic)
 *                      as and when required by the OS.
 * Called By    :       cpqary3_init_pkt()
 * Parameters   :       per-controller, SCSI packet, buffer, flag modifier,
 *                      callback function
 * Calls        :       None
 * Return Values:       SUCCESS / FAILURE
 */
static int
cpqary3_dma_alloc(cpqary3_t *cpqary3p, struct scsi_pkt *scsi_pktp,
    struct buf *bp, int flags, int (*callback)())
{
        int32_t         (*cb)(caddr_t);
        int32_t         retvalue;
        uint32_t        i = 0;
        uint32_t        dma_flags;
        cpqary3_pkt_t   *cpqary3_pktp;
        ddi_dma_attr_t  tmp_dma_attr;

        cpqary3_pktp = (cpqary3_pkt_t *)scsi_pktp->pkt_ha_private;

        ASSERT(callback == NULL_FUNC || callback == SLEEP_FUNC);
        /*
         * Record the direction of the data transfer, so that it
         * can be used in appropriate synchronization during cpqary3_sync_pkt()
         */
        if (bp->b_flags & B_READ) {
                cpqary3_pktp->cmd_flags &= ~CFLAG_DMASEND;
                dma_flags = DDI_DMA_READ;
        } else {
                cpqary3_pktp->cmd_flags |= CFLAG_DMASEND;
                dma_flags = DDI_DMA_WRITE;
        }

        if (flags & PKT_CONSISTENT) {
                cpqary3_pktp->cmd_flags |= CFLAG_CMDIOPB;
                dma_flags |= DDI_DMA_CONSISTENT;
        }

        if (flags & PKT_DMA_PARTIAL) {
                dma_flags |= DDI_DMA_PARTIAL;
        }

        tmp_dma_attr = cpqary3_dma_attr;

        /* SG */
        tmp_dma_attr.dma_attr_sgllen = cpqary3p->sg_cnt;
        /* SG */

        cb = (callback == NULL_FUNC) ? DDI_DMA_DONTWAIT : DDI_DMA_SLEEP;

        /*
         * DMA resources are allocated thru a 2 step protocol :
         * - allocate a DMA handle
         * - bind the buffer to the handle
         * If both the steps succeed, we have succeeded in allocating resources
         */

        if (DDI_SUCCESS != (retvalue = ddi_dma_alloc_handle(cpqary3p->dip,
            &tmp_dma_attr, cb, CPQARY3_DMA_NO_CALLBACK,
            &cpqary3_pktp->cmd_dmahandle))) {
                switch (retvalue) {
                case DDI_DMA_NORESOURCES:
                        /*
                         * No Resources are available to be allocated
                         */
                        bioerror(bp, CPQARY3_BUFFER_ERROR_CLEAR);
                        break;

                case DDI_DMA_BADATTR:
                        /*
                         * The attributes stated in our DMA attribute
                         * structure is such that potential DMA resources can
                         * not be allocated.
                         */
                        cmn_err(CE_CONT, "CPQary3: DmaAlloc: "
                            "AllocHandle Failed BadAttr\n");
                        bioerror(bp, EFAULT);
                        break;

                default:
                        /*
                         * There is no other possible return value
                         */
                        cmn_err(CE_WARN,
                            "CPQary3: dma_alloc: Unexpected Return Value %x "
                            "From call to Allocate DMA Handle \n", retvalue);
                        break;
                }
                return (CPQARY3_FAILURE);
        }

        retvalue = ddi_dma_buf_bind_handle(cpqary3_pktp->cmd_dmahandle, bp,
            dma_flags, cb, CPQARY3_DMA_NO_CALLBACK,
            &cpqary3_pktp->cmd_dmacookies[0], &cpqary3_pktp->cmd_ncookies);

        switch (retvalue) {
        case DDI_DMA_PARTIAL_MAP :
        case DDI_DMA_MAPPED :
                if (DDI_DMA_PARTIAL_MAP == retvalue) {
                        if (ddi_dma_numwin(cpqary3_pktp->cmd_dmahandle,
                            &cpqary3_pktp->cmd_nwin) == DDI_FAILURE) {
                                cmn_err(CE_PANIC, "CPQary3: Retrieval of DMA "
                                    "windows number failed");
                        }

                        if (ddi_dma_getwin(cpqary3_pktp->cmd_dmahandle,
                            cpqary3_pktp->cmd_curwin,
                            &cpqary3_pktp->cmd_dma_offset,
                            &cpqary3_pktp->cmd_dma_len,
                            &cpqary3_pktp->cmd_dmacookies[0],
                            &cpqary3_pktp->cmd_ncookies) == DDI_FAILURE) {
                                cmn_err(CE_PANIC, "CPQary3: Activation of New "
                                    "DMA Window Failed");
                        }
                } else {
                        cpqary3_pktp->cmd_nwin = 1;
                        cpqary3_pktp->cmd_dma_len = 0;
                        cpqary3_pktp->cmd_dma_offset = 0;
                }

                cpqary3_pktp->cmd_dmacount = 0;
                i = 0;
                for (;;) {
                        cpqary3_pktp->cmd_dmacount +=
                            cpqary3_pktp->cmd_dmacookies[i++].dmac_size;
                        /* SG */
                        /* Check Out for Limits */
                        if (i == cpqary3p->sg_cnt ||
                            i == cpqary3_pktp->cmd_ncookies)
                                break;
                        /* SG */

                        ddi_dma_nextcookie(cpqary3_pktp->cmd_dmahandle,
                            &cpqary3_pktp->cmd_dmacookies[i]);
                }

                cpqary3_pktp->cmd_cookie = i;
                cpqary3_pktp->cmd_cookiecnt = i;
                cpqary3_pktp->cmd_flags |= CFLAG_DMAVALID;

                scsi_pktp->pkt_resid =
                    bp->b_bcount - cpqary3_pktp->cmd_dmacount;

                return (CPQARY3_SUCCESS);

        case DDI_DMA_NORESOURCES:
                bioerror(bp, CPQARY3_BUFFER_ERROR_CLEAR);
                break;

        case DDI_DMA_NOMAPPING:
                bioerror(bp, EFAULT);
                break;

        case DDI_DMA_TOOBIG:
                bioerror(bp, EINVAL);
                break;

        case DDI_DMA_INUSE:
                cmn_err(CE_PANIC, "CPQary3: Another I/O transaction "
                    "is using the DMA handle");
                break;

        default:
                cmn_err(CE_PANIC, "CPQary3: Unexpected ERROR "
                    "returned from Call to Bind Buffer "
                    "to Handle : 0x%X", retvalue);
        }

        ddi_dma_free_handle(&cpqary3_pktp->cmd_dmahandle);
        cpqary3_pktp->cmd_dmahandle = NULL;
        cpqary3_pktp->cmd_flags &= ~CFLAG_DMAVALID;

        return (CPQARY3_FAILURE);

}

/*
 * Function     :       cpqary3_dma_move()
 * Description  :       This routine gets the next DMA window.
 * Called By    :       cpqary3_init_pkt()
 * Parameters   :       per-controller, SCSI packet, buffer
 * Calls        :       None
 * Return Values:       SUCCESS / FAILURE
 */
static int
cpqary3_dma_move(struct scsi_pkt *scsi_pktp, struct buf *bp,
    cpqary3_t *cpqary3p)
{
        uint32_t                i = 0;
        cpqary3_pkt_t   *cpqary3_pktp;

        cpqary3_pktp = PKT2PVTPKT(scsi_pktp);

        /*
         * If there are no more cookies remaining in this window,
         * must move to the next window first.
         */
        if (cpqary3_pktp->cmd_cookie == cpqary3_pktp->cmd_ncookies) {
                /* For small pkts, leave things where they are */
                if ((cpqary3_pktp->cmd_curwin == cpqary3_pktp->cmd_nwin) &&
                    (cpqary3_pktp->cmd_nwin == 1))
                        return (CPQARY3_SUCCESS);

                /* Shall not be able to move if last window */
                if (++cpqary3_pktp->cmd_curwin >= cpqary3_pktp->cmd_nwin)
                        return (CPQARY3_FAILURE);

                if (ddi_dma_getwin(cpqary3_pktp->cmd_dmahandle,
                    cpqary3_pktp->cmd_curwin, &cpqary3_pktp->cmd_dma_offset,
                    &cpqary3_pktp->cmd_dma_len,
                    &cpqary3_pktp->cmd_dmacookies[0],
                    &cpqary3_pktp->cmd_ncookies) == DDI_FAILURE)
                        return (CPQARY3_FAILURE);

                cpqary3_pktp->cmd_cookie = 0;
        } else {
                /* Still more cookies in this window - get the next one */
                ddi_dma_nextcookie(cpqary3_pktp->cmd_dmahandle,
                    &cpqary3_pktp->cmd_dmacookies[0]);
        }

        /* Get remaining cookies in this window, up to our maximum */
        for (;;) {
                cpqary3_pktp->cmd_dmacount +=
                    cpqary3_pktp->cmd_dmacookies[i++].dmac_size;
                cpqary3_pktp->cmd_cookie++;
                /* SG */
                /* no. of DATA SEGMENTS */
                if (i == cpqary3p->sg_cnt ||
                    cpqary3_pktp->cmd_cookie == cpqary3_pktp->cmd_ncookies)
                        break;
                /* SG */

                ddi_dma_nextcookie(cpqary3_pktp->cmd_dmahandle,
                    &cpqary3_pktp->cmd_dmacookies[i]);
        }

        cpqary3_pktp->cmd_cookiecnt = i;
        scsi_pktp->pkt_resid = bp->b_bcount - cpqary3_pktp->cmd_dmacount;

        return (CPQARY3_SUCCESS);

}

/*
 * Function     :       cpqary3_transport()
 * Description  :       This routine services requests from the OS that are
 *                      directed towards the targets.(any SCSI command)
 * Called By    :       kernel
 * Parameters   :       SCSI address, SCSI packet, buffer
 * Calls        :       cpqary3_build_iop, cpqary3_add2submitted
 * Return Values:       TRAN_ACCEPT     : The driver accepts the command.
 *                      TRAN_BUSY       : Required resources not available
 *                                      at the moment.
 *                      TRAN_FATAL_ERROR: A target no longer exists.
 */
static int
cpqary3_transport(struct scsi_address *sa, struct scsi_pkt *scsi_pktp)
{
        cpqary3_t               *ctlr;
        cpqary3_pkt_t           *cpqary3_pktp;
        cpqary3_tgt_t           *tgtp;
        cpqary3_cmdpvt_t        *memp;

        ASSERT(sa != NULL);
        ctlr = SA2CTLR(sa);
        cpqary3_pktp = PKT2PVTPKT(scsi_pktp);
        tgtp = ctlr->cpqary3_tgtp[SA2TGT(sa)];

        if (!tgtp)
                return (TRAN_FATAL_ERROR);

        if (tgtp->type == CPQARY3_TARGET_NONE)
                return (TRAN_FATAL_ERROR);

        if (cpqary3_additional_cmd(scsi_pktp, ctlr))
                return (TRAN_ACCEPT);

        /*
         * Attempt to occupy a free command memory block
         * If not successful, return TRAN_BUSY
         * Else, build the Command
         * Submit it to the controller
         * If NO_INTR flag is set, wait for the completion of the command and
         * when the command completes, update packet values appropriately and
         * return TRAN_ACCEPT.
         * Make an entry in the submitted Q
         * return TRAN_ACCEPT
         */

        if (NULL == (memp = cpqary3_cmdlist_occupy(ctlr)))
                return (TRAN_BUSY);

        cpqary3_pktp->memp = memp;
        memp->pvt_pkt = cpqary3_pktp;

        if ((cpqary3_pktp->cmd_flags & DDI_DMA_CONSISTENT) &&
            cpqary3_pktp->cmd_dmahandle) {
                (void) ddi_dma_sync(cpqary3_pktp->cmd_dmahandle, 0, 0,
                    DDI_DMA_SYNC_FORDEV);
        }
        /* SG */
        ASSERT(cpqary3_pktp->cmd_cookiecnt <= ctlr->sg_cnt);
        /* SG */

        /* PERF */
        memp->complete = cpqary3_oscmd_complete;
        /* PERF */

        switch (cpqary3_build_cmdlist(memp, SA2TGT(sa))) {
        case CPQARY3_SUCCESS :
                if (scsi_pktp->pkt_flags & FLAG_NOINTR) {
                        return (cpqary3_handle_flag_nointr(memp, scsi_pktp));
                }
                cpqary3_pktp->cmd_start_time = ddi_get_lbolt();
                mutex_enter(&ctlr->hw_mutex);
                /* CONTROLLER_LOCKUP */
                if (EIO == cpqary3_submit(ctlr, memp->cmdlist_phyaddr)) {
                        mutex_exit(&ctlr->hw_mutex);
                        cpqary3_cmdlist_release(memp, CPQARY3_HOLD_SW_MUTEX);
                        return (TRAN_FATAL_ERROR);
                }
                /* CONTROLLER_LOCKUP */
                mutex_exit(&ctlr->hw_mutex);
                break;
        case CPQARY3_FAILURE :
                cpqary3_cmdlist_release(memp, CPQARY3_HOLD_SW_MUTEX);
                return (TRAN_FATAL_ERROR);
        default: /* Never occurs */
                cmn_err(CE_NOTE, "CPQary3 : Transport : Unexpected Error");
                return (TRAN_FATAL_ERROR);
        }

        return (TRAN_ACCEPT);
}

/*
 * Function     :       cpqary3_dmafree
 * Description  :       This routine de-allocates previously allocated
 *                      DMA resources.
 * Called By    :       kernel
 * Parameters   :       SCSI address, SCSI packet
 * Calls        :       None
 * Return Values:       None
 */
/* ARGSUSED */
static void
cpqary3_dmafree(struct scsi_address *sa, struct scsi_pkt *scsi_pktp)
{
        cpqary3_pkt_t   *cpqary3_pktp;

        cpqary3_pktp = PKT2PVTPKT(scsi_pktp);

        /*
         * If any DMA was succesfully attempted earlier, free all allocated
         * resources
         */

        if (cpqary3_pktp->cmd_flags & CFLAG_DMAVALID) {
                if (!cpqary3_pktp->cmd_dmahandle) {
                        DTRACE_PROBE(dmafree_null);
                        return;
                }
                cpqary3_pktp->cmd_flags &= ~CFLAG_DMAVALID;
                (void) ddi_dma_unbind_handle(cpqary3_pktp->cmd_dmahandle);
                ddi_dma_free_handle(&cpqary3_pktp->cmd_dmahandle);
                cpqary3_pktp->cmd_dmahandle = NULL;
        }
}

/*
 * Function     :       cpqary3_dma_sync
 * Description  :       This routine synchronizes the CPU's / HBA's view of
 *                      the data associated with the pkt, typically by calling
 *                      ddi_dma_sync().
 * Called By    :       kernel
 * Parameters   :       SCSI address, SCSI packet
 * Calls        :       None
 * Return Values:       None
 */
/* ARGSUSED */
static void
cpqary3_dma_sync(struct scsi_address *sa, struct scsi_pkt *scsi_pktp)
{
        cpqary3_pkt_t   *cpqary3_pktp = PKT2PVTPKT(scsi_pktp);

        /*
         * Check whether DMA was attempted successfully earlier
         * If yes and
         * if the command flags is write, then synchronise the device else
         * synchronise the CPU
         */

        if (cpqary3_pktp->cmd_flags & CFLAG_DMAVALID) {
                (void) ddi_dma_sync(cpqary3_pktp->cmd_dmahandle,
                    cpqary3_pktp->cmd_dma_offset, cpqary3_pktp->cmd_dma_len,
                    (cpqary3_pktp->cmd_flags & CFLAG_DMASEND) ?
                    DDI_DMA_SYNC_FORDEV : DDI_DMA_SYNC_FORCPU);
        }
}

/*
 * Function     :       cpqary3_destroy_pkt
 * Description  :       This routine de-allocates previously allocated
 *                      resources for the SCSI packet.
 * Called By    :       kernel
 * Parameters   :       SCSI address, SCSI packet
 * Calls        :       None
 * Return Values:       None
 */
static void
cpqary3_destroy_pkt(struct scsi_address *sa, struct scsi_pkt *scsi_pktp)
{
        cpqary3_pkt_t   *cpqary3_pktp;

        cpqary3_pktp = PKT2PVTPKT(scsi_pktp);

        /*
         * Deallocate DMA Resources, if allocated.
         * Free the SCSI Packet.
         */

        if (cpqary3_pktp->cmd_flags & CFLAG_DMAVALID) {
                if (!cpqary3_pktp->cmd_dmahandle) {
                        DTRACE_PROBE(dmafree_null);
                } else {
                        cpqary3_pktp->cmd_flags &= ~CFLAG_DMAVALID;

                        (void) ddi_dma_unbind_handle(
                            cpqary3_pktp->cmd_dmahandle);
                        ddi_dma_free_handle(&cpqary3_pktp->cmd_dmahandle);

                        cpqary3_pktp->cmd_dmahandle = NULL;
                }
        }

        scsi_hba_pkt_free(sa, scsi_pktp);
}

/*
 * Function     :       cpqary3_reset
 * Description  :       This routine resets a SCSI bus/target.
 * Called By    :       kernel
 * Parameters   :       SCSI address, reset level required
 * Calls        :       None
 * Return Values:       SUCCESS
 */
/* ARGSUSED */
static int
cpqary3_reset(struct scsi_address *sa, int level)
{
        /*
         * Fix for Crash seen during RAID 0 Drive removal -
         * just return CPQARY3_SUCCESS on reset request
         */
        return (CPQARY3_SUCCESS);
}

/*
 * Function     :       cpqary3_abort()
 * Description  :       This routine aborts a particular command or all commands
 *                      directed towards a target.
 * Called By    :       kernel
 * Parameters   :       SCSI address, SCSI packet
 * Calls        :       None
 * Return Values:       SUCCESS / FAILURE
 *                      [ abort of concernd command(s) was a success or
 *                      a failure. ]
 */
static int
cpqary3_abort(struct scsi_address *sa, struct scsi_pkt *scsi_pktp)
{
        uint32_t        tid;
        cpqary3_t       *ctlr;

        ASSERT(sa != NULL);
        tid  = SA2TGT(sa);
        ctlr = SA2CTLR(sa);

        /*
         * If SCSI packet exists, abort that particular command.
         * Else, abort all existing commands to the target
         * In either of the cases, we shall have to wait after the abort
         * functions are called to return the status.
         */

        if (!scsi_pktp) {
                return (cpqary3_send_abortcmd(ctlr, tid,
                    (CommandList_t *)NULL));
        } else {
                return (cpqary3_send_abortcmd(ctlr, tid, SP2CMD(scsi_pktp)));
        }
}

/*
 * Function     :       cpqary3_getcap
 * Description  :       This routine is called to get the current value of a
 *                      capability.(SCSI transport capability)
 * Called By    :       kernel
 * Parameters   :       SCSI address, capability identifier, target(s) affected
 * Calls        :       None
 * Return Values:       current value of capability / -1 (if unsupported)
 */
static int
cpqary3_getcap(struct scsi_address *sa, char *capstr, int tgtonly)
{
        int             index;
        cpqary3_t       *ctlr = SA2CTLR(sa);
        cpqary3_tgt_t   *tgtp = ctlr->cpqary3_tgtp[SA2TGT(sa)];

        /*
         * If requested Capability is not supported, return -1.
         */
        if (DDI_FAILURE == (index = scsi_hba_lookup_capstr(capstr)))
                return (CAP_NOT_DEFINED);

        /*
         * Getting capability for a particulat target is supported
         * the generic form of tran_getcap() is unsupported(for all targets)
         * If directed towards a particular target, return current capability.
         */
        if (tgtonly == 0) {     /* all targets */
                DTRACE_PROBE1(getcap_alltgt, int, index);
                return (CAP_NOT_DEFINED);
        }

        DTRACE_PROBE1(getcap_index, int, index);

        switch (index) {
        case SCSI_CAP_DMA_MAX:
                return ((int)cpqary3_dma_attr.dma_attr_maxxfer);
        case SCSI_CAP_DISCONNECT:
                return (tgtp->ctlr_flags & CPQARY3_CAP_DISCON_ENABLED);
        case SCSI_CAP_SYNCHRONOUS:
                return (tgtp->ctlr_flags & CPQARY3_CAP_SYNC_ENABLED);
        case SCSI_CAP_WIDE_XFER:
                return (tgtp->ctlr_flags & CPQARY3_CAP_WIDE_XFER_ENABLED);
        case SCSI_CAP_ARQ:
                return ((tgtp->ctlr_flags & CPQARY3_CAP_ARQ_ENABLED) ? 1 : 0);
        case SCSI_CAP_INITIATOR_ID:
                return (CTLR_SCSI_ID);
        case SCSI_CAP_UNTAGGED_QING:
                return (1);
        case SCSI_CAP_TAGGED_QING:
                return (1);
        case SCSI_CAP_SECTOR_SIZE:
                return (cpqary3_dma_attr.dma_attr_granular);
        case SCSI_CAP_TOTAL_SECTORS:
                return (CAP_NOT_DEFINED);
        case SCSI_CAP_GEOMETRY:
                return (cpqary3_target_geometry(sa));
        case SCSI_CAP_RESET_NOTIFICATION:
                return (0);
        default:
                return (CAP_NOT_DEFINED);
        }
}

/*
 * Function     :       cpqary3_setcap
 * Description  :       This routine is called to set the current value of a
 *                      capability.(SCSI transport capability)
 * Called By    :       kernel
 * Parameters   :       SCSI address, capability identifier,
 *                      new capability value, target(s) affected
 * Calls        :       None
 * Return Values:       SUCCESS / FAILURE / -1 (if capability is unsupported)
 */
/* ARGSUSED */
static int
cpqary3_setcap(struct scsi_address *sa, char *capstr, int value, int tgtonly)
{
        int     index;
        int     retstatus = CAP_NOT_DEFINED;

        /*
         * If requested Capability is not supported, return -1.
         */
        if ((index = scsi_hba_lookup_capstr(capstr)) == DDI_FAILURE)
                return (retstatus);

        /*
         * Setting capability for a particulat target is supported
         * the generic form of tran_setcap() is unsupported(for all targets)
         * If directed towards a particular target, set & return current
         * capability.
         */
        if (!tgtonly) {
                DTRACE_PROBE1(setcap_alltgt, int, index);
                return (retstatus);
        }

        DTRACE_PROBE1(setcap_index, int, index);

        switch (index) {
        case SCSI_CAP_DMA_MAX:
                return (CAP_CHG_NOT_ALLOWED);
        case SCSI_CAP_DISCONNECT:
                return (CAP_CHG_NOT_ALLOWED);
        case SCSI_CAP_SYNCHRONOUS:
                return (CAP_CHG_NOT_ALLOWED);
        case SCSI_CAP_WIDE_XFER:
                return (CAP_CHG_NOT_ALLOWED);
        case SCSI_CAP_ARQ:
                return (1);
        case SCSI_CAP_INITIATOR_ID:
                return (CAP_CHG_NOT_ALLOWED);
        case SCSI_CAP_UNTAGGED_QING:
                return (1);
        case SCSI_CAP_TAGGED_QING:
                return (1);
        case SCSI_CAP_SECTOR_SIZE:
                return (CAP_CHG_NOT_ALLOWED);
        case SCSI_CAP_TOTAL_SECTORS:
                return (CAP_CHG_NOT_ALLOWED);
        case SCSI_CAP_GEOMETRY:
                return (CAP_CHG_NOT_ALLOWED);
        case SCSI_CAP_RESET_NOTIFICATION:
                return (CAP_CHG_NOT_ALLOWED);
        default:
                return (CAP_NOT_DEFINED);
        }
}

/*
 * Function     :       cpqary3_handle_flag_nointr
 * Description  :       This routine is called to handle submission and
 *                      subsequently poll for the completion of a command,
 *                      when its FLAG_NOINTR bit is set.
 * Called By    :       cpqary3_transport()
 * Parameters   :       command private structure, SCSI packet
 * Calls        :       cpqary3_intr_onoff, cpqary3_retrieve,
 *                      cpqary3_submit, cpqary3_poll
 * Return Values:       TRAN_ACCEPT
 */
static int
cpqary3_handle_flag_nointr(cpqary3_cmdpvt_t *memp, struct scsi_pkt *scsi_pktp)
{
        uint32_t                tag;
        uint32_t                simple_tag;
        uint32_t                i;
        cpqary3_t               *ctlr;
        cpqary3_cmdpvt_t        *cpqary3_cmdpvtp;
        uint32_t                CmdsOutMax;
        uint32_t                no_cmds;

        RETURN_FAILURE_IF_NULL(memp);
        tag = memp->tag.tag_value;
        ctlr = memp->ctlr;
        ctlr->poll_flag = CPQARY3_FALSE;

        /*
         * Before sumitting this command, ensure all commands pending
         * with the controller are completed.
         */

        cpqary3_intr_onoff(ctlr, CPQARY3_INTR_DISABLE);
        if (ctlr->host_support & 0x4)
                cpqary3_lockup_intr_onoff(ctlr, CPQARY3_LOCKUP_INTR_DISABLE);

        no_cmds = (uint32_t)((ctlr->ctlr_maxcmds / 3) * NO_OF_CMDLIST_IN_A_BLK);
        mutex_enter(&ctlr->sw_mutex);

        for (;;) {
                ctlr->poll_flag = CPQARY3_FALSE;
                for (i = 0; i < no_cmds; i++) {
                        cpqary3_cmdpvtp = &ctlr->cmdmemlistp->pool[i];
                        ASSERT(cpqary3_cmdpvtp != NULL);

                        if ((tag != cpqary3_cmdpvtp->tag.tag_value) &&
                            (cpqary3_cmdpvtp->occupied == CPQARY3_OCCUPIED)) {
                                if (ctlr->noe_support == 1) {
                                        if ((cpqary3_cmdpvtp->cmdlist_memaddr->
                                            Header.Tag.drvinfo_n_err ==
                                            CPQARY3_NOECMD_SUCCESS) ||
                                            (cpqary3_cmdpvtp->cmdpvt_flag ==
                                            CPQARY3_TIMEOUT))  {
                                                continue;
                                        }
                                } else {
                                        if (cpqary3_cmdpvtp->cmdpvt_flag ==
                                            CPQARY3_TIMEOUT)  {
                                                continue;
                                        }
                                }
                                ctlr->poll_flag = CPQARY3_TRUE;
                        }
                        /* NOE */

                        if (ctlr->poll_flag == CPQARY3_TRUE) {
                                break;
                        }
                }

                if (ctlr->poll_flag == CPQARY3_TRUE) {
                        if (!(ctlr->bddef->bd_flags & SA_BD_SAS)) {
                                while ((simple_tag =
                                    ddi_get32(ctlr->opq_handle,
                                    (uint32_t *)ctlr->opq)) != 0xFFFFFFFF) {
                                        CmdsOutMax = ctlr->ctlr_maxcmds;
                                        if ((simple_tag >>
                                            CPQARY3_GET_MEM_TAG) >=
                                            ((CmdsOutMax / 3) * 3)) {
                                                cmn_err(CE_WARN,
                                                    "CPQary3 : HBA returned "
                                                    "Spurious Tag");
                                                return (CPQARY3_FAILURE);
                                        }

                                        cpqary3_cmdpvtp =
                                            &ctlr->cmdmemlistp->pool[
                                            simple_tag >> CPQARY3_GET_MEM_TAG];
                                        cpqary3_cmdpvtp->cmdlist_memaddr->
                                            Header.Tag.drvinfo_n_err =
                                            (simple_tag & 0xF) >> 1;
                                        cpqary3_cmdpvtp->complete(
                                            cpqary3_cmdpvtp);
                                }
                        } else {
                                mutex_exit(&ctlr->sw_mutex);
                                if (CPQARY3_SUCCESS != cpqary3_retrieve(ctlr)) {
                                        drv_usecwait(1000);
                                }
                                mutex_enter(&ctlr->sw_mutex); /* Changes */
                        }
                } else {
                        break;
                }
        }

        mutex_enter(&ctlr->hw_mutex);
        if (EIO == cpqary3_submit(ctlr, memp->cmdlist_phyaddr)) {
                mutex_exit(&ctlr->hw_mutex);
                mutex_exit(&ctlr->sw_mutex);
                cpqary3_cmdlist_release(memp, CPQARY3_HOLD_SW_MUTEX);
                return (TRAN_FATAL_ERROR);
        }

        if (CPQARY3_FAILURE == cpqary3_poll(ctlr, tag)) {
                scsi_pktp->pkt_reason = CMD_TIMEOUT;
                scsi_pktp->pkt_statistics = STAT_TIMEOUT;
                scsi_pktp->pkt_state = 0;
                mutex_exit(&ctlr->hw_mutex);
                mutex_exit(&ctlr->sw_mutex);
                cpqary3_cmdlist_release(memp, CPQARY3_HOLD_SW_MUTEX);
                cpqary3_intr_onoff(ctlr, CPQARY3_INTR_ENABLE);
                if (ctlr->host_support & 0x4)
                        cpqary3_lockup_intr_onoff(ctlr,
                            CPQARY3_LOCKUP_INTR_ENABLE);
                return (TRAN_ACCEPT);
        } else {
                mutex_exit(&ctlr->hw_mutex);
                mutex_exit(&ctlr->sw_mutex);
                cpqary3_intr_onoff(ctlr, CPQARY3_INTR_ENABLE);
                if (ctlr->host_support & 0x4) {
                        cpqary3_lockup_intr_onoff(ctlr,
                            CPQARY3_LOCKUP_INTR_ENABLE);
                }
                return (TRAN_ACCEPT);
        }
}

/*
 * Function     :       cpqary3_poll
 * Description  :       This routine polls for the completion of a command.
 * Called By    :       cpqary3_handle_flag_nointr
 * Parameters   :       per controller, tag of the command to be polled
 * Calls        :       cpqary3_poll_retrieve
 * Return Values:       TRAN_ACCEPT
 */
static int
cpqary3_poll(cpqary3_t *ctlr, uint32_t tag)
{
        uint32_t                ii = 0;

        RETURN_FAILURE_IF_NULL(ctlr);

        /*
         * POLL for the completion of the said command
         * Since, we had ensured that controller is empty, we need not
         * check for the complete Retrieved Q.
         * However, we just check the Retrieved Q and complete all
         * commands in it, inclusive of the polled command.
         * if the polled command is completed, send back a success.
         */

        for (;;) {      /* this function is called with both the locks held */
                if (CPQARY3_SUCCESS != cpqary3_poll_retrieve(ctlr, tag)) {
                        ii++;
                        if (ii > 120000)
                                return (CPQARY3_FAILURE);
                        drv_usecwait(500);
                        continue;
                }
                break;
        }

        return (CPQARY3_SUCCESS);
}

static int
cpqary3_additional_cmd(struct scsi_pkt *scsi_pktp, cpqary3_t *ctlr)
{
        struct scsi_arq_status *arqstat;
        /* LINTED: alignment */
        arqstat = (struct scsi_arq_status *)(scsi_pktp->pkt_scbp);

        switch (scsi_pktp->pkt_cdbp[0]) {
        case 0x35: /* Synchronize Cache */

                cpqary3_flush_cache(ctlr);

                scsi_pktp->pkt_reason = CMD_CMPLT;
                scsi_pktp->pkt_statistics = 0;
                scsi_pktp->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
                    STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS;

                if (scsi_pktp->pkt_comp) {
                        (*scsi_pktp->pkt_comp)(scsi_pktp);
                }

                return (1);

        case 0x04: /* Format Unit */
                cmn_err(CE_NOTE, "The FORMAT UNIT is not supported by this "
                    "device If this option is selected from the format utility "
                    "do not continue further.  Please refer to cpqary3 driver "
                    "man pages for details.");

                return (0);
        case SCSI_LOG_SENSE:
        case SCSI_MODE_SELECT:
        case SCSI_PERSISTENT_RESERVE_IN:
                arqstat->sts_status.sts_chk = 1; /* CHECK CONDITION */
                arqstat->sts_rqpkt_reason = CMD_CMPLT;
                arqstat->sts_rqpkt_resid = 0;
                arqstat->sts_rqpkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
                    STATE_SENT_CMD | STATE_XFERRED_DATA;
                arqstat->sts_rqpkt_statistics = 0;
                arqstat->sts_sensedata.es_valid = 1;
                arqstat->sts_sensedata.es_class = CLASS_EXTENDED_SENSE;
                arqstat->sts_sensedata.es_key = KEY_ILLEGAL_REQUEST;
                scsi_pktp->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
                    STATE_SENT_CMD | STATE_XFERRED_DATA;

                if (scsi_pktp->pkt_comp) {
                        (*scsi_pktp->pkt_comp)(scsi_pktp);
                }
                return (1);
        }

        return (0);
}

/* PERF */
/*
 * Function     :       cpqary3_oscmd_complete
 * Description  :       This routine processes the
 *                      completed OS commands and
 *                      initiates any callback that is needed.
 * Called By    :       cpqary3_transport
 * Parameters   :       per-command
 * Calls        :       cpqary3_ioctl_send_bmiccmd,
 *                      cpqary3_ioctl_send_scsicmd,
 *                      cpqary3_send_abortcmd, cpqary3_flush_cache,
 *                      cpqary3_probe4LVs,
 *                      cpqary3_probe4Tapes, cpqary3_synccmd_complete,
 *                      cpqary3_detect_target_geometry,
 *                      cpqary3_detect_target_geometry
 * Return Values:       None
 */
void
cpqary3_oscmd_complete(cpqary3_cmdpvt_t *cpqary3_cmdpvtp)
{
        cpqary3_t       *cpqary3p;
        ErrorInfo_t     *errorinfop;
        CommandList_t   *cmdlistp;
        struct scsi_pkt *scsi_pktp;

        ASSERT(cpqary3_cmdpvtp != NULL);

        if (CPQARY3_TIMEOUT == cpqary3_cmdpvtp->cmdpvt_flag) {
                cpqary3_cmdlist_release(cpqary3_cmdpvtp,
                    CPQARY3_NO_MUTEX);
                return;
        }

        cpqary3p = cpqary3_cmdpvtp->ctlr;
        cmdlistp = cpqary3_cmdpvtp->cmdlist_memaddr;
        errorinfop = cpqary3_cmdpvtp->errorinfop;

        if (cmdlistp->Header.Tag.drvinfo_n_err == CPQARY3_OSCMD_SUCCESS) {
                scsi_pktp = cpqary3_cmdpvtp->pvt_pkt->scsi_cmd_pkt;
                scsi_pktp->pkt_reason = CMD_CMPLT;
                scsi_pktp->pkt_statistics = 0;
                scsi_pktp->pkt_state = STATE_GOT_BUS |
                    STATE_GOT_TARGET | STATE_SENT_CMD |
                    STATE_XFERRED_DATA | STATE_GOT_STATUS;

                if (cpqary3_cmdpvtp->pvt_pkt->scsi_cmd_pkt->pkt_flags &
                    FLAG_NOINTR) {
                        cpqary3_cmdlist_release(cpqary3_cmdpvtp,
                            CPQARY3_NO_MUTEX);
                } else {
                        cpqary3_cmdlist_release(cpqary3_cmdpvtp,
                            CPQARY3_NO_MUTEX);

                        if (scsi_pktp->pkt_comp) {
                                mutex_exit(&cpqary3p->sw_mutex);
                                (*scsi_pktp->pkt_comp)(scsi_pktp);
                                mutex_enter(&cpqary3p->sw_mutex);
                        }
                }
                return;
        } else {
                scsi_pktp = cpqary3_cmdpvtp->pvt_pkt->scsi_cmd_pkt;
        }

        switch (errorinfop->CommandStatus) {
        case CISS_CMD_DATA_OVERRUN :
                scsi_pktp->pkt_reason = CMD_DATA_OVR;
                scsi_pktp->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
                    STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS;
                break;

        case CISS_CMD_INVALID :
                DTRACE_PROBE1(invalid_cmd, struct scsi_pkt *, scsi_pktp);
                scsi_pktp->pkt_reason = CMD_BADMSG;
                scsi_pktp->pkt_state = STATE_GOT_BUS |STATE_GOT_TARGET |
                    STATE_SENT_CMD | STATE_GOT_STATUS;
                break;

        case CISS_CMD_PROTOCOL_ERR :
                scsi_pktp->pkt_reason = CMD_BADMSG;
                scsi_pktp->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
                    STATE_SENT_CMD | STATE_GOT_STATUS;
                break;

        case CISS_CMD_HARDWARE_ERR:
        case CISS_CMD_CONNECTION_LOST:
                scsi_pktp->pkt_reason = CMD_INCOMPLETE;
                scsi_pktp->pkt_state = 0;
                break;

        case CISS_CMD_ABORTED:
        case CISS_CMD_UNSOLICITED_ABORT:
                scsi_pktp->pkt_reason = CMD_ABORTED;
                scsi_pktp->pkt_statistics = STAT_ABORTED;
                scsi_pktp->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
                    STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS;
                break;

        case CISS_CMD_ABORT_FAILED:
                break;

        case CISS_CMD_TIMEOUT:
                scsi_pktp->pkt_reason = CMD_TIMEOUT;
                scsi_pktp->pkt_statistics = STAT_TIMEOUT;
                scsi_pktp->pkt_state = 0;
                break;

        case CISS_CMD_DATA_UNDERRUN:    /* Significant ONLY for Read & Write */
                if (cpqary3_is_scsi_read_write(scsi_pktp)) {
                        scsi_pktp->pkt_reason = CMD_CMPLT;
                        scsi_pktp->pkt_statistics = 0;
                        scsi_pktp->pkt_state =
                            STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD |
                            STATE_XFERRED_DATA | STATE_GOT_STATUS;
                        break;
                }
                /* FALLTHROUGH */
        case CISS_CMD_SUCCESS:
        case CISS_CMD_TARGET_STATUS:
                scsi_pktp->pkt_reason = CMD_CMPLT;
                scsi_pktp->pkt_statistics = 0;
                scsi_pktp->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
                    STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS;
                break;

        default:        /* Should never Occur !!! */
                scsi_pktp->pkt_reason = CMD_TRAN_ERR;
                break;
        }


        /*
         * if ever a command completes with a CHECK CONDITION or a
         * COMMAND_TERMINATED SCSI status, Update the sense data.
         * NOTE : The CISS_CMD_INVALID command status would always result in a
         * CHECK CONDITION and hence reach this part of the code.
         */

        if ((errorinfop->ScsiStatus == SCSI_CHECK_CONDITION) ||
            (errorinfop->ScsiStatus == SCSI_COMMAND_TERMINATED)) {
                if (errorinfop->SenseLen) {
                        struct scsi_arq_status  *arq_statusp;
                        arq_statusp =
                            /* LINTED: alignment */
                            (struct scsi_arq_status *)scsi_pktp->pkt_scbp;

                        if ((errorinfop->ScsiStatus == SCSI_CHECK_CONDITION)) {
                                arq_statusp->sts_status.sts_chk = (uint8_t)1;
                        } else {
                                arq_statusp->sts_status.sts_chk = (uint8_t)1;
                                arq_statusp->sts_status.sts_scsi2 = (uint8_t)1;
                        }
                        bzero((void *)&(arq_statusp->sts_rqpkt_status),
                            sizeof (struct scsi_status));
                        arq_statusp->sts_rqpkt_reason = CMD_CMPLT;
                        arq_statusp->sts_rqpkt_resid = 0;
                        arq_statusp->sts_rqpkt_state = scsi_pktp->pkt_state;
                        arq_statusp->sts_rqpkt_statistics =
                            scsi_pktp->pkt_statistics;
                        bcopy((caddr_t)&errorinfop->SenseInfo[0],
                            (caddr_t)(&arq_statusp->sts_sensedata),
                            CPQARY3_MIN(errorinfop->SenseLen,
                            cpqary3_cmdpvtp->pvt_pkt->scb_len));
                        scsi_pktp->pkt_state |= STATE_ARQ_DONE;
                }
        }

        if (cpqary3_cmdpvtp->pvt_pkt->scsi_cmd_pkt->pkt_flags & FLAG_NOINTR) {
                cpqary3_cmdlist_release(cpqary3_cmdpvtp, CPQARY3_NO_MUTEX);
        } else {
                cpqary3_cmdlist_release(cpqary3_cmdpvtp, CPQARY3_NO_MUTEX);

                if (scsi_pktp->pkt_comp) {
                        mutex_exit(&cpqary3p->sw_mutex);
                        (*scsi_pktp->pkt_comp)(scsi_pktp);
                        mutex_enter(&cpqary3p->sw_mutex);
                }
        }
}

static uint8_t
cpqary3_is_scsi_read_write(struct scsi_pkt *scsi_pktp)
{
        /*
         * In the scsi packet structure, the first byte is the SCSI Command
         * OpCode.  We check to see if it is any one of the SCSI Read or Write
         * opcodes.
         */

        switch (scsi_pktp->pkt_cdbp[0]) {
        case SCSI_READ_6:
        case SCSI_READ_10:
        case SCSI_READ_12:
        case SCSI_WRITE_6:
        case SCSI_WRITE_10:
        case SCSI_WRITE_12:
                return (1);

        default:
                return (0);
        }
}