/*
* 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");
default:
cmn_err(CE_PANIC, "CPQary3: Unexpected ERROR "
"returned from Call to Bind Buffer "
"to Handle : 0x%X", i);
}
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);
}
}