#include <sys/sdt.h>
#include "cpqary3.h"
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);
extern ddi_dma_attr_t cpqary3_dma_attr;
void
cpqary3_init_hbatran(cpqary3_t *ctlr)
{
scsi_hba_tran_t *hba_tran;
ASSERT(ctlr != NULL);
hba_tran = ctlr->hba_tran;
hba_tran->tran_hba_private = (void *)ctlr;
hba_tran->tran_tgt_init = cpqary3_tgt_init;
hba_tran->tran_tgt_probe = cpqary3_tgt_probe;
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;
hba_tran->tran_start = cpqary3_transport;
hba_tran->tran_getcap = cpqary3_getcap;
hba_tran->tran_setcap = cpqary3_setcap;
hba_tran->tran_reset = cpqary3_reset;
hba_tran->tran_abort = cpqary3_abort;
}
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;
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);
}
if ((tid >= CPQARY3_MAX_TGT) || (lun != 0)) {
DTRACE_PROBE2(tgt_init_notsup,
cpqary3_t *, ctlr, uint32_t, tid);
return (DDI_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);
}
static int
cpqary3_tgt_probe(struct scsi_device *sd, int (*waitfunc)())
{
#ifdef CPQARY3_DEBUG
int status;
#endif
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);
}
return (scsi_hba_probe(sd, waitfunc));
#ifdef CPQARY3_DEBUG
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
}
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 (!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;
cpqary3_pktp->cdb_len = cmdlen;
cpqary3_pktp->scb_len = statuslen;
cpqary3_pktp->cmd_dmahandle = NULL;
cpqary3_pktp->memp = (cpqary3_cmdpvt_t *)NULL;
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 (bp && bp->b_bcount != 0) {
if (!cpqary3_pktp->cmd_dmahandle) {
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);
}
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);
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;
tmp_dma_attr.dma_attr_sgllen = cpqary3p->sg_cnt;
cb = (callback == NULL_FUNC) ? DDI_DMA_DONTWAIT : DDI_DMA_SLEEP;
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:
bioerror(bp, CPQARY3_BUFFER_ERROR_CLEAR);
break;
case DDI_DMA_BADATTR:
cmn_err(CE_CONT, "CPQary3: DmaAlloc: "
"AllocHandle Failed BadAttr\n");
bioerror(bp, EFAULT);
break;
default:
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;
if (i == cpqary3p->sg_cnt ||
i == cpqary3_pktp->cmd_ncookies)
break;
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);
}
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 (cpqary3_pktp->cmd_cookie == cpqary3_pktp->cmd_ncookies) {
if ((cpqary3_pktp->cmd_curwin == cpqary3_pktp->cmd_nwin) &&
(cpqary3_pktp->cmd_nwin == 1))
return (CPQARY3_SUCCESS);
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 {
ddi_dma_nextcookie(cpqary3_pktp->cmd_dmahandle,
&cpqary3_pktp->cmd_dmacookies[0]);
}
for (;;) {
cpqary3_pktp->cmd_dmacount +=
cpqary3_pktp->cmd_dmacookies[i++].dmac_size;
cpqary3_pktp->cmd_cookie++;
if (i == cpqary3p->sg_cnt ||
cpqary3_pktp->cmd_cookie == cpqary3_pktp->cmd_ncookies)
break;
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);
}
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);
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);
}
ASSERT(cpqary3_pktp->cmd_cookiecnt <= ctlr->sg_cnt);
memp->complete = cpqary3_oscmd_complete;
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);
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);
}
mutex_exit(&ctlr->hw_mutex);
break;
case CPQARY3_FAILURE :
cpqary3_cmdlist_release(memp, CPQARY3_HOLD_SW_MUTEX);
return (TRAN_FATAL_ERROR);
default:
cmn_err(CE_NOTE, "CPQary3 : Transport : Unexpected Error");
return (TRAN_FATAL_ERROR);
}
return (TRAN_ACCEPT);
}
static void
cpqary3_dmafree(struct scsi_address *sa, struct scsi_pkt *scsi_pktp)
{
cpqary3_pkt_t *cpqary3_pktp;
cpqary3_pktp = PKT2PVTPKT(scsi_pktp);
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;
}
}
static void
cpqary3_dma_sync(struct scsi_address *sa, struct scsi_pkt *scsi_pktp)
{
cpqary3_pkt_t *cpqary3_pktp = PKT2PVTPKT(scsi_pktp);
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);
}
}
static void
cpqary3_destroy_pkt(struct scsi_address *sa, struct scsi_pkt *scsi_pktp)
{
cpqary3_pkt_t *cpqary3_pktp;
cpqary3_pktp = PKT2PVTPKT(scsi_pktp);
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);
}
static int
cpqary3_reset(struct scsi_address *sa, int level)
{
return (CPQARY3_SUCCESS);
}
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_pktp) {
return (cpqary3_send_abortcmd(ctlr, tid,
(CommandList_t *)NULL));
} else {
return (cpqary3_send_abortcmd(ctlr, tid, SP2CMD(scsi_pktp)));
}
}
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 (DDI_FAILURE == (index = scsi_hba_lookup_capstr(capstr)))
return (CAP_NOT_DEFINED);
if (tgtonly == 0) {
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);
}
}
static int
cpqary3_setcap(struct scsi_address *sa, char *capstr, int value, int tgtonly)
{
int index;
int retstatus = CAP_NOT_DEFINED;
if ((index = scsi_hba_lookup_capstr(capstr)) == DDI_FAILURE)
return (retstatus);
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);
}
}
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;
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;
}
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);
}
} 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);
}
}
static int
cpqary3_poll(cpqary3_t *ctlr, uint32_t tag)
{
uint32_t ii = 0;
RETURN_FAILURE_IF_NULL(ctlr);
for (;;) {
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;
arqstat = (struct scsi_arq_status *)(scsi_pktp->pkt_scbp);
switch (scsi_pktp->pkt_cdbp[0]) {
case 0x35:
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:
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;
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);
}
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:
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;
}
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:
scsi_pktp->pkt_reason = CMD_TRAN_ERR;
break;
}
if ((errorinfop->ScsiStatus == SCSI_CHECK_CONDITION) ||
(errorinfop->ScsiStatus == SCSI_COMMAND_TERMINATED)) {
if (errorinfop->SenseLen) {
struct scsi_arq_status *arq_statusp;
arq_statusp =
(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)
{
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);
}
}