freebsd-dev/sys/pci/scsiiom.c
1997-09-21 22:02:25 +00:00

1512 lines
31 KiB
C

/***********************************************************************
* FILE NAME : SCSIIOM.C *
* BY : C.L. Huang (ching@tekram.com.tw) *
* Description: Device Driver for Tekram DC-390 (T) PCI SCSI *
* Bus Master Host Adapter *
***********************************************************************/
static USHORT
DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB )
{
USHORT ioport, rc;
UCHAR bval, bval1, i, cnt;
PUCHAR ptr;
ULONG wlval;
pSRB->TagNumber = 31;
ioport = pACB->IOPortBase;
bval = pDCB->UnitSCSIID;
OutB(bval,ioport+Scsi_Dest_ID);
bval = pDCB->SyncPeriod;
OutB(bval,ioport+Sync_Period);
bval = pDCB->SyncOffset;
OutB(bval,ioport+Sync_Offset);
bval = pDCB->CtrlR1;
OutB(bval,ioport+CtrlReg1);
bval = pDCB->CtrlR3;
OutB(bval,ioport+CtrlReg3);
bval = pDCB->CtrlR4;
OutB(bval,ioport+CtrlReg4);
bval = CLEAR_FIFO_CMD; /* Flush FIFO */
OutB(bval,ioport+ScsiCmd);
pSRB->ScsiPhase = SCSI_NOP0;
bval = pDCB->IdentifyMsg;
if( !(pDCB->SyncMode & EN_ATN_STOP) )
{
if( (pSRB->CmdBlock[0] == INQUIRY) ||
(pSRB->CmdBlock[0] == REQUEST_SENSE) ||
(pSRB->SRBFlag & AUTO_REQSENSE) )
{
bval &= 0xBF; /* NO disconnection */
OutB(bval,ioport+ScsiFifo);
bval1 = SELECT_W_ATN;
pSRB->SRBState = SRB_START_;
if( pDCB->SyncMode & SYNC_ENABLE )
{
if( !(pDCB->IdentifyMsg & 7) ||
(pSRB->CmdBlock[0] != INQUIRY) )
{
bval1 = SEL_W_ATN_STOP;
pSRB->SRBState = SRB_MSGOUT;
}
}
}
else
{
if(pDCB->SyncMode & EN_TAG_QUEUING)
{
OutB(bval,ioport+ScsiFifo);
bval = MSG_SIMPLE_QTAG;
OutB(bval,ioport+ScsiFifo);
wlval = 1;
bval = 0;
while( wlval & pDCB->TagMask )
{
wlval = wlval << 1;
bval++;
}
OutB(bval,ioport+ScsiFifo);
pDCB->TagMask |= wlval;
pSRB->TagNumber = bval;
bval1 = SEL_W_ATN2;
pSRB->SRBState = SRB_START_;
}
else
{
OutB(bval,ioport+ScsiFifo);
bval1 = SELECT_W_ATN;
pSRB->SRBState = SRB_START_;
}
}
if( pSRB->SRBFlag & AUTO_REQSENSE )
{
bval = REQUEST_SENSE;
OutB(bval,ioport+ScsiFifo);
bval = pDCB->IdentifyMsg << 5;
OutB(bval,ioport+ScsiFifo);
bval = 0;
OutB(bval,ioport+ScsiFifo);
OutB(bval,ioport+ScsiFifo);
bval = sizeof(struct scsi_sense_data);
OutB(bval,ioport+ScsiFifo);
bval = 0;
OutB(bval,ioport+ScsiFifo);
}
else
{
cnt = pSRB->ScsiCmdLen;
ptr = (PUCHAR) pSRB->CmdBlock;
for(i=0; i<cnt; i++)
{
bval = *ptr++;
OutB(bval,ioport+ScsiFifo);
}
}
}
else /* ATN_STOP */
{
if( (pSRB->CmdBlock[0] == INQUIRY) ||
(pSRB->CmdBlock[0] == REQUEST_SENSE) ||
(pSRB->SRBFlag & AUTO_REQSENSE) )
{
bval &= 0xBF;
OutB(bval,ioport+ScsiFifo);
bval1 = SELECT_W_ATN;
pSRB->SRBState = SRB_START_;
if( pDCB->SyncMode & SYNC_ENABLE )
{
if( !(pDCB->IdentifyMsg & 7) ||
(pSRB->CmdBlock[0] != INQUIRY) )
{
bval1 = SEL_W_ATN_STOP;
pSRB->SRBState = SRB_MSGOUT;
}
}
}
else
{
if(pDCB->SyncMode & EN_TAG_QUEUING)
{
OutB(bval,ioport+ScsiFifo);
pSRB->MsgOutBuf[0] = MSG_SIMPLE_QTAG;
wlval = 1;
bval = 0;
while( wlval & pDCB->TagMask )
{
wlval = wlval << 1;
bval++;
}
pDCB->TagMask |= wlval;
pSRB->TagNumber = bval;
pSRB->MsgOutBuf[1] = bval;
pSRB->MsgCnt = 2;
bval1 = SEL_W_ATN_STOP;
pSRB->SRBState = SRB_START_;
}
else
{
OutB(bval,ioport+ScsiFifo);
pSRB->MsgOutBuf[0] = MSG_NOP;
pSRB->MsgCnt = 1;
pSRB->SRBState = SRB_START_;
bval1 = SEL_W_ATN_STOP;
}
}
}
bval = inb( ioport+Scsi_Status );
if( bval & INTERRUPT )
{
pSRB->SRBState = SRB_READY;
pDCB->TagMask &= ~( 1 << pSRB->TagNumber );
rc = 1;
}
else
{
pSRB->ScsiPhase = SCSI_NOP1;
pACB->pActiveDCB = pDCB;
pDCB->pActiveSRB = pSRB;
rc = 0;
OutB(bval1,ioport+ScsiCmd);
}
return( rc );
}
#ifdef REL_2_1_0
static int DC390_Interrupt( PACB pACB )
#endif
#ifdef REL_2_1_5
static void DC390_Interrupt( PACB pACB )
#endif
{
PDCB pDCB;
PSRB pSRB;
USHORT ioport = 0;
USHORT phase;
void (*stateV)( PACB, PSRB, PUCHAR );
UCHAR istate = 0;
UCHAR sstatus=0, istatus;
if( pACB == NULL )
#ifdef REL_2_1_0
return 0;
#endif
#ifdef REL_2_1_5
return;
#endif
ioport = pACB->IOPortBase;
sstatus = inb( ioport+Scsi_Status );
if( !(sstatus & INTERRUPT) )
#ifdef REL_2_1_0
return 0;
#endif
#ifdef REL_2_1_5
return;
#endif
#ifdef DC390_DEBUG1
printf("sstatus=%2x,",sstatus);
#endif
istate = inb( ioport+Intern_State );
istatus = inb( ioport+INT_Status );
#ifdef DC390_DEBUG1
printf("Istatus=%2x,",istatus);
#endif
if(istatus & DISCONNECTED)
{
DC390_Disconnect( pACB );
#ifdef REL_2_1_0
return 1;
#endif
#ifdef REL_2_1_5
return;
#endif
}
if(istatus & RESELECTED)
{
DC390_Reselect( pACB );
#ifdef REL_2_1_0
return 1;
#endif
#ifdef REL_2_1_5
return;
#endif
}
if(istatus & INVALID_CMD)
{
DC390_InvalidCmd( pACB );
#ifdef REL_2_1_0
return 1;
#endif
#ifdef REL_2_1_5
return;
#endif
}
if(istatus & SCSI_RESET_)
{
DC390_ScsiRstDetect( pACB );
#ifdef REL_2_1_0
return 1;
#endif
#ifdef REL_2_1_5
return;
#endif
}
if( istatus & (SUCCESSFUL_OP+SERVICE_REQUEST) )
{
pDCB = pACB->pActiveDCB;
pSRB = pDCB->pActiveSRB;
if( pDCB )
{
if( pDCB->DCBFlag & ABORT_DEV_ )
EnableMsgOut( pACB, pSRB );
}
phase = (USHORT) pSRB->ScsiPhase;
stateV = (void *) DC390_phase0[phase];
stateV( pACB, pSRB, &sstatus );
pSRB->ScsiPhase = sstatus & 7;
phase = (USHORT) sstatus & 7;
stateV = (void *) DC390_phase1[phase];
stateV( pACB, pSRB, &sstatus );
}
#ifdef REL_2_1_0
return 1;
#endif
}
static void
DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
UCHAR sstatus, bval;
USHORT ioport;
PSEG psgl;
ULONG ResidCnt, xferCnt;
ioport = pACB->IOPortBase;
sstatus = *psstatus;
if( !(pSRB->SRBState & SRB_XFERPAD) )
{
if( sstatus & PARITY_ERR )
pSRB->SRBStatus |= PARITY_ERROR;
if( sstatus & COUNT_2_ZERO )
{
bval = inb(ioport+DMA_Status);
while( !(bval & DMA_XFER_DONE) )
bval = inb(ioport+DMA_Status);
pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
pSRB->SGIndex++;
if( pSRB->SGIndex < pSRB->SGcount )
{
pSRB->pSegmentList++;
psgl = pSRB->pSegmentList;
pSRB->SGPhysAddr = psgl->SGXPtr;
pSRB->SGToBeXferLen = psgl->SGXLen;
}
else
pSRB->SGToBeXferLen = 0;
}
else
{
bval = inb( ioport+Current_Fifo );
bval &= 0x1f;
ResidCnt = (ULONG) inb(ioport+CtcReg_High);
ResidCnt = ResidCnt << 8;
ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid);
ResidCnt = ResidCnt << 8;
ResidCnt |= (ULONG) inb(ioport+CtcReg_Low);
ResidCnt += (ULONG) bval;
xferCnt = pSRB->SGToBeXferLen - ResidCnt;
pSRB->SGPhysAddr += xferCnt;
pSRB->TotalXferredLen += xferCnt;
pSRB->SGToBeXferLen = ResidCnt;
}
}
bval = WRITE_DIRECTION+DMA_IDLE_CMD;
OutB( bval, ioport+DMA_Cmd);
}
static void
DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
UCHAR sstatus, bval;
USHORT i, ioport, residual;
PSEG psgl;
ULONG ResidCnt, xferCnt;
PUCHAR ptr;
ioport = pACB->IOPortBase;
sstatus = *psstatus;
if( !(pSRB->SRBState & SRB_XFERPAD) )
{
if( sstatus & PARITY_ERR )
pSRB->SRBStatus |= PARITY_ERROR;
if( sstatus & COUNT_2_ZERO )
{
bval = inb(ioport+DMA_Status);
while( !(bval & DMA_XFER_DONE) )
bval = inb(ioport+DMA_Status);
bval = READ_DIRECTION+DMA_IDLE_CMD;
OutB( bval, ioport+DMA_Cmd);
pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
pSRB->SGIndex++;
if( pSRB->SGIndex < pSRB->SGcount )
{
pSRB->pSegmentList++;
psgl = pSRB->pSegmentList;
pSRB->SGPhysAddr = psgl->SGXPtr;
pSRB->SGToBeXferLen = psgl->SGXLen;
}
else
pSRB->SGToBeXferLen = 0;
}
else /* phase changed */
{
residual = 0;
bval = inb(ioport+Current_Fifo);
while( bval & 0x1f )
{
if( (bval & 0x1f) == 1 )
{
for(i=0; i< 0x100; i++)
{
bval = inb(ioport+Current_Fifo);
if( !(bval & 0x1f) )
goto din_1;
else if( i == 0x0ff )
{
residual = 1; /* ;1 residual byte */
goto din_1;
}
}
}
else
bval = inb(ioport+Current_Fifo);
}
din_1:
bval = READ_DIRECTION+DMA_BLAST_CMD;
OutB(bval, ioport+DMA_Cmd);
for(i=0; i<0x8000; i++)
{
bval = inb(ioport+DMA_Status);
if(bval & BLAST_COMPLETE)
break;
}
bval = READ_DIRECTION+DMA_IDLE_CMD;
OutB(bval, ioport+DMA_Cmd);
ResidCnt = (ULONG) inb(ioport+CtcReg_High);
ResidCnt = ResidCnt << 8;
ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid);
ResidCnt = ResidCnt << 8;
ResidCnt |= (ULONG) inb(ioport+CtcReg_Low);
xferCnt = pSRB->SGToBeXferLen - ResidCnt;
pSRB->SGPhysAddr += xferCnt;
pSRB->TotalXferredLen += xferCnt;
pSRB->SGToBeXferLen = ResidCnt;
if( residual )
{
bval = inb(ioport+ScsiFifo); /* get residual byte */
ptr = phystovirt( pSRB, xferCnt);
*ptr = bval;
pSRB->SGPhysAddr++;
pSRB->TotalXferredLen++;
pSRB->SGToBeXferLen--;
}
}
}
}
static void
DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
}
static void
DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
UCHAR bval;
USHORT ioport;
ioport = pACB->IOPortBase;
bval = inb(ioport+ScsiFifo);
pSRB->TargetStatus = bval;
bval++;
bval = inb(ioport+ScsiFifo); /* get message */
pSRB->EndMessage = bval;
*psstatus = SCSI_NOP0;
pSRB->SRBState = SRB_COMPLETED;
bval = MSG_ACCEPTED_CMD;
OutB(bval, ioport+ScsiCmd);
}
static void
DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
if( pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT) )
*psstatus = SCSI_NOP0;
}
static void
DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
UCHAR bval;
USHORT ioport, wval, wval1;
PDCB pDCB;
PSRB psrb;
ioport = pACB->IOPortBase;
pDCB = pACB->pActiveDCB;
bval = inb( ioport+ScsiFifo );
if( !(pSRB->SRBState & SRB_MSGIN_MULTI) )
{
if(bval == MSG_DISCONNECT)
{
pSRB->SRBState = SRB_DISCONNECT;
}
else if( bval == MSG_SAVE_PTR )
goto min6;
else if( (bval == MSG_EXTENDED) || ((bval >= MSG_SIMPLE_QTAG) &&
(bval <= MSG_ORDER_QTAG)) )
{
pSRB->SRBState |= SRB_MSGIN_MULTI;
pSRB->MsgInBuf[0] = bval;
pSRB->MsgCnt = 1;
pSRB->pMsgPtr = &pSRB->MsgInBuf[1];
}
else if(bval == MSG_REJECT_)
{
bval = RESET_ATN_CMD;
OutB(bval, ioport+ScsiCmd);
if( pSRB->SRBState & DO_SYNC_NEGO)
goto set_async;
}
else if( bval == MSG_RESTORE_PTR)
goto min6;
else
goto min6;
}
else
{ /* minx: */
*pSRB->pMsgPtr = bval;
pSRB->MsgCnt++;
pSRB->pMsgPtr++;
if( (pSRB->MsgInBuf[0] >= MSG_SIMPLE_QTAG) &&
(pSRB->MsgInBuf[0] <= MSG_ORDER_QTAG) )
{
if( pSRB->MsgCnt == 2)
{
pSRB->SRBState = 0;
bval = pSRB->MsgInBuf[1];
pSRB = pDCB->pGoingSRB;
psrb = pDCB->pGoingLast;
if( pSRB )
{
for( ;; )
{
if(pSRB->TagNumber != bval)
{
if( pSRB == psrb )
goto mingx0;
pSRB = pSRB->pNextSRB;
}
else
break;
}
if( pDCB->DCBFlag & ABORT_DEV_ )
{
pSRB->SRBState = SRB_ABORT_SENT;
EnableMsgOut( pACB, pSRB );
}
if( !(pSRB->SRBState & SRB_DISCONNECT) )
goto mingx0;
pDCB->pActiveSRB = pSRB;
pSRB->SRBState = SRB_DATA_XFER;
}
else
{
mingx0:
pSRB = pACB->pTmpSRB;
pSRB->SRBState = SRB_UNEXPECT_RESEL;
pDCB->pActiveSRB = pSRB;
pSRB->MsgOutBuf[0] = MSG_ABORT_TAG;
EnableMsgOut2( pACB, pSRB );
}
}
}
else if( (pSRB->MsgInBuf[0] == MSG_EXTENDED) && (pSRB->MsgCnt == 5) )
{
pSRB->SRBState &= ~(SRB_MSGIN_MULTI+DO_SYNC_NEGO);
if( (pSRB->MsgInBuf[1] != 3) || (pSRB->MsgInBuf[2] != 1) )
{ /* reject_msg: */
pSRB->MsgCnt = 1;
pSRB->MsgInBuf[0] = MSG_REJECT_;
bval = SET_ATN_CMD;
OutB(bval, ioport+ScsiCmd);
}
else if( !(pSRB->MsgInBuf[3]) || !(pSRB->MsgInBuf[4]) )
{
set_async:
pDCB = pSRB->pSRBDCB;
pDCB->SyncMode &= ~(SYNC_ENABLE+SYNC_NEGO_DONE);
pDCB->SyncPeriod = 0;
pDCB->SyncOffset = 0;
pDCB->CtrlR3 = FAST_CLK; /* ;non_fast */
pDCB->CtrlR4 &= 0x3f;
pDCB->CtrlR4 |= EATER_25NS; /* ; 25ns glitch eater */
goto re_prog;
}
else
{ /* set_sync: */
pDCB = pSRB->pSRBDCB;
pDCB->SyncMode |= SYNC_ENABLE+SYNC_NEGO_DONE;
pDCB->SyncOffset &= 0x0f0;
pDCB->SyncOffset |= pSRB->MsgInBuf[4];
pDCB->NegoPeriod = pSRB->MsgInBuf[3];
wval = (USHORT) pSRB->MsgInBuf[3];
wval = wval << 2;
wval--;
wval1 = wval / 25;
if( (wval1 * 25) != wval)
wval1++;
bval = FAST_CLK+FAST_SCSI;
pDCB->CtrlR4 &= 0x3f;
if(wval1 >= 8)
{
wval1--;
bval = FAST_CLK; /* ;fast clock/normal scsi */
pDCB->CtrlR4 |= EATER_25NS; /* ;25 ns glitch eater */
}
pDCB->CtrlR3 = bval;
pDCB->SyncPeriod = (UCHAR)wval1;
re_prog:
bval = pDCB->SyncPeriod;
OutB(bval, ioport+Sync_Period);
bval = pDCB->SyncOffset;
OutB(bval, ioport+Sync_Offset);
bval = pDCB->CtrlR3;
OutB(bval, ioport+CtrlReg3);
bval = pDCB->CtrlR4;
OutB(bval, ioport+CtrlReg4);
SetXferRate( pACB, pDCB);
}
}
}
min6:
*psstatus = SCSI_NOP0;
bval = MSG_ACCEPTED_CMD;
OutB(bval, ioport+ScsiCmd);
}
static void
DataIO_Comm( PACB pACB, PSRB pSRB, UCHAR ioDir)
{
PSEG psgl;
UCHAR bval;
USHORT ioport;
ULONG lval;
ioport = pACB->IOPortBase;
if( pSRB->SGIndex < pSRB->SGcount )
{
bval = DMA_IDLE_CMD | ioDir; /* ;+EN_DMA_INT */
OutB( bval, ioport+DMA_Cmd);
if( !pSRB->SGToBeXferLen )
{
psgl = pSRB->pSegmentList;
pSRB->SGPhysAddr = psgl->SGXPtr;
pSRB->SGToBeXferLen = psgl->SGXLen;
}
lval = pSRB->SGToBeXferLen;
bval = (UCHAR) lval;
OutB(bval,ioport+CtcReg_Low);
lval = lval >> 8;
bval = (UCHAR) lval;
OutB(bval,ioport+CtcReg_Mid);
lval = lval >> 8;
bval = (UCHAR) lval;
OutB(bval,ioport+CtcReg_High);
lval = pSRB->SGToBeXferLen;
OutL(lval, ioport+DMA_XferCnt);
lval = pSRB->SGPhysAddr;
OutL( lval, ioport+DMA_XferAddr);
pSRB->SRBState = SRB_DATA_XFER;
bval = DMA_COMMAND+INFO_XFER_CMD;
OutB(bval, ioport+ScsiCmd);
bval = DMA_IDLE_CMD | ioDir; /* ;+EN_DMA_INT */
OutB(bval, ioport+DMA_Cmd);
bval = DMA_START_CMD | ioDir; /* ;+EN_DMA_INT */
OutB(bval, ioport+DMA_Cmd);
}
else /* xfer pad */
{
if( pSRB->SGcount )
{
pSRB->AdaptStatus = H_OVER_UNDER_RUN;
pSRB->SRBStatus |= OVER_RUN;
}
bval = 0;
OutB(bval,ioport+CtcReg_Low);
OutB(bval,ioport+CtcReg_Mid);
OutB(bval,ioport+CtcReg_High);
pSRB->SRBState |= SRB_XFERPAD;
bval = DMA_COMMAND+XFER_PAD_BYTE;
OutB(bval, ioport+ScsiCmd);
/*
bval = DMA_IDLE_CMD | ioDir; ;+EN_DMA_INT
OutB(bval, ioport+DMA_Cmd);
bval = DMA_START_CMD | ioDir; ;+EN_DMA_INT
OutB(bval, ioport+DMA_Cmd);
*/
}
}
static void
DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
UCHAR ioDir;
ioDir = WRITE_DIRECTION;
DataIO_Comm( pACB, pSRB, ioDir);
}
static void
DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
UCHAR ioDir;
ioDir = READ_DIRECTION;
DataIO_Comm( pACB, pSRB, ioDir);
}
static void
DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
PDCB pDCB;
UCHAR bval;
PUCHAR ptr;
USHORT ioport, i, cnt;
ioport = pACB->IOPortBase;
bval = RESET_ATN_CMD;
OutB(bval, ioport+ScsiCmd);
bval = CLEAR_FIFO_CMD;
OutB(bval, ioport+ScsiCmd);
if( !(pSRB->SRBFlag & AUTO_REQSENSE) )
{
cnt = (USHORT) pSRB->ScsiCmdLen;
ptr = (PUCHAR) pSRB->CmdBlock;
for(i=0; i < cnt; i++)
{
OutB(*ptr, ioport+ScsiFifo);
ptr++;
}
}
else
{
bval = REQUEST_SENSE;
OutB(bval, ioport+ScsiFifo);
pDCB = pACB->pActiveDCB;
bval = pDCB->IdentifyMsg << 5;
OutB(bval, ioport+ScsiFifo);
bval = 0;
OutB(bval, ioport+ScsiFifo);
OutB(bval, ioport+ScsiFifo);
bval = sizeof(struct scsi_sense_data);
OutB(bval, ioport+ScsiFifo);
bval = 0;
OutB(bval, ioport+ScsiFifo);
}
pSRB->SRBState = SRB_COMMAND;
bval = INFO_XFER_CMD;
OutB(bval, ioport+ScsiCmd);
}
static void
DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
UCHAR bval;
USHORT ioport;
ioport = pACB->IOPortBase;
bval = CLEAR_FIFO_CMD;
OutB(bval, ioport+ScsiCmd);
pSRB->SRBState = SRB_STATUS;
bval = INITIATOR_CMD_CMPLTE;
OutB(bval, ioport+ScsiCmd);
}
static void
DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
UCHAR bval;
USHORT ioport, i, cnt;
PUCHAR ptr;
PDCB pDCB;
ioport = pACB->IOPortBase;
bval = CLEAR_FIFO_CMD;
OutB(bval, ioport+ScsiCmd);
pDCB = pACB->pActiveDCB;
if( !(pSRB->SRBState & SRB_MSGOUT) )
{
cnt = pSRB->MsgCnt;
if( cnt )
{
ptr = (PUCHAR) pSRB->MsgOutBuf;
for(i=0; i < cnt; i++)
{
OutB(*ptr, ioport+ScsiFifo);
ptr++;
}
pSRB->MsgCnt = 0;
if( (pDCB->DCBFlag & ABORT_DEV_) &&
(pSRB->MsgOutBuf[0] == MSG_ABORT) )
pSRB->SRBState = SRB_ABORT_SENT;
}
else
{
bval = MSG_ABORT; /* ??? MSG_NOP */
if( (pSRB->CmdBlock[0] == INQUIRY ) ||
(pSRB->CmdBlock[0] == REQUEST_SENSE) ||
(pSRB->SRBFlag & AUTO_REQSENSE) )
{
if( pDCB->SyncMode & SYNC_ENABLE )
goto mop1;
}
OutB(bval, ioport+ScsiFifo);
}
bval = INFO_XFER_CMD;
OutB( bval, ioport+ScsiCmd);
}
else
{
mop1:
bval = MSG_EXTENDED;
OutB(bval, ioport+ScsiFifo);
bval = 3; /* ;length of extended msg */
OutB(bval, ioport+ScsiFifo);
bval = 1; /* ; sync nego */
OutB(bval, ioport+ScsiFifo);
bval = pDCB->NegoPeriod;
OutB(bval, ioport+ScsiFifo);
bval = SYNC_NEGO_OFFSET;
OutB(bval, ioport+ScsiFifo);
pSRB->SRBState |= DO_SYNC_NEGO;
bval = INFO_XFER_CMD;
OutB(bval, ioport+ScsiCmd);
}
}
static void
DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
UCHAR bval;
USHORT ioport;
ioport = pACB->IOPortBase;
bval = CLEAR_FIFO_CMD;
OutB(bval, ioport+ScsiCmd);
if( !(pSRB->SRBState & SRB_MSGIN) )
{
pSRB->SRBState &= SRB_DISCONNECT;
pSRB->SRBState |= SRB_MSGIN;
}
bval = INFO_XFER_CMD;
OutB(bval, ioport+ScsiCmd);
}
static void
DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
}
static void
DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus)
{
}
static void
SetXferRate( PACB pACB, PDCB pDCB )
{
UCHAR bval;
USHORT cnt, i;
PDCB ptr;
if( !(pDCB->IdentifyMsg & 0x07) )
{
if( pACB->scan_devices )
{
CurrSyncOffset = pDCB->SyncOffset;
}
else
{
ptr = pACB->pLinkDCB;
cnt = pACB->DeviceCnt;
bval = pDCB->UnitSCSIID;
for(i=0; i<cnt; i++)
{
if( ptr->UnitSCSIID == bval )
{
ptr->SyncPeriod = pDCB->SyncPeriod;
ptr->SyncOffset = pDCB->SyncOffset;
ptr->CtrlR3 = pDCB->CtrlR3;
ptr->CtrlR4 = pDCB->CtrlR4;
ptr->SyncMode = pDCB->SyncMode;
}
ptr = ptr->pNextDCB;
}
}
}
return;
}
static void
DC390_Disconnect( PACB pACB )
{
PDCB pDCB;
PSRB pSRB, psrb;
int flags;
USHORT ioport, i, cnt;
UCHAR bval;
#ifdef DC390_DEBUG0
printf("DISC,");
#endif
flags = splbio();
ioport = pACB->IOPortBase;
pDCB = pACB->pActiveDCB;
pSRB = pDCB->pActiveSRB;
pACB->pActiveDCB = 0;
pSRB->ScsiPhase = SCSI_NOP0;
bval = EN_SEL_RESEL;
OutB(bval, ioport+ScsiCmd);
if( pSRB->SRBState & SRB_UNEXPECT_RESEL )
{
pSRB->SRBState = 0;
DoWaitingSRB( pACB );
}
else if( pSRB->SRBState & SRB_ABORT_SENT )
{
pDCB->TagMask = 0;
pDCB->DCBFlag = 0;
cnt = pDCB->GoingSRBCnt;
pDCB->GoingSRBCnt = 0;
pSRB = pDCB->pGoingSRB;
for( i=0; i < cnt; i++)
{
psrb = pSRB->pNextSRB;
pSRB->pNextSRB = pACB->pFreeSRB;
pACB->pFreeSRB = pSRB;
pSRB = psrb;
}
pDCB->pGoingSRB = 0;
DoWaitingSRB( pACB );
}
else
{
if( (pSRB->SRBState & (SRB_START_+SRB_MSGOUT)) ||
!(pSRB->SRBState & (SRB_DISCONNECT+SRB_COMPLETED)) )
{ /* Selection time out */
if( !(pACB->scan_devices) )
{
pSRB->SRBState = SRB_READY;
RewaitSRB( pDCB, pSRB);
}
else
{
pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT;
goto disc1;
}
}
else if( pSRB->SRBState & SRB_DISCONNECT )
{
DoWaitingSRB( pACB );
}
else if( pSRB->SRBState & SRB_COMPLETED )
{
disc1:
if(pDCB->MaxCommand > 1)
{
bval = pSRB->TagNumber;
pDCB->TagMask &= (~(1 << bval)); /* free tag mask */
}
pDCB->pActiveSRB = 0;
pSRB->SRBState = SRB_FREE;
SRBdone( pACB, pDCB, pSRB);
}
}
splx(flags);
return;
}
static void
DC390_Reselect( PACB pACB )
{
PDCB pDCB;
PSRB pSRB;
USHORT ioport, wval;
UCHAR bval, bval1;
#ifdef DC390_DEBUG0
printf("RSEL,");
#endif
ioport = pACB->IOPortBase;
pDCB = pACB->pActiveDCB;
if( pDCB )
{ /* Arbitration lost but Reselection win */
pSRB = pDCB->pActiveSRB;
if( !( pACB->scan_devices ) )
{
pSRB->SRBState = SRB_READY;
RewaitSRB( pDCB, pSRB);
}
}
bval = inb(ioport+ScsiFifo); /* get ID */
bval = bval ^ pACB->HostID_Bit;
wval = 0;
bval1 = 1;
for(;;)
{
if( !(bval & bval1) )
{
bval1 = bval1 << 1;
wval++;
}
else
break;
}
wval |= ( (USHORT) inb(ioport+ScsiFifo) & 7) << 8; /* get LUN */
pDCB = pACB->pLinkDCB;
while( wval != *((PUSHORT) &pDCB->UnitSCSIID) )
pDCB = pDCB->pNextDCB;
pACB->pActiveDCB = pDCB;
if( pDCB->SyncMode & EN_TAG_QUEUING )
{
pSRB = pACB->pTmpSRB;
pDCB->pActiveSRB = pSRB;
}
else
{
pSRB = pDCB->pActiveSRB;
if( !pSRB || !(pSRB->SRBState & SRB_DISCONNECT) )
{
pSRB= pACB->pTmpSRB;
pSRB->SRBState = SRB_UNEXPECT_RESEL;
pDCB->pActiveSRB = pSRB;
EnableMsgOut( pACB, pSRB );
}
else
{
if( pDCB->DCBFlag & ABORT_DEV_ )
{
pSRB->SRBState = SRB_ABORT_SENT;
EnableMsgOut( pACB, pSRB );
}
else
pSRB->SRBState = SRB_DATA_XFER;
}
}
pSRB->ScsiPhase = SCSI_NOP0;
bval = pDCB->UnitSCSIID;
OutB( bval, ioport+Scsi_Dest_ID);
bval = pDCB->SyncPeriod;
OutB(bval, ioport+Sync_Period);
bval = pDCB->SyncOffset;
OutB( bval, ioport+Sync_Offset);
bval = pDCB->CtrlR1;
OutB(bval, ioport+CtrlReg1);
bval = pDCB->CtrlR3;
OutB(bval, ioport+CtrlReg3);
bval = pDCB->CtrlR4; /* ; Glitch eater */
OutB(bval, ioport+CtrlReg4);
bval = MSG_ACCEPTED_CMD; /* ;to rls the /ACK signal */
OutB(bval, ioport+ScsiCmd);
}
static void
SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB )
{
PSRB psrb;
UCHAR bval, bval1, i, j, status;
PSCSICMD pcmd;
PSCLINK plink;
PSCSI_INQDATA ptr;
USHORT disable_tag;
int flags;
PSEG ptr2;
ULONG swlval;
pcmd = pSRB->pcmd;
plink = pcmd->sc_link;
status = pSRB->TargetStatus;
if(pSRB->SRBFlag & AUTO_REQSENSE)
{
pSRB->SRBFlag &= ~AUTO_REQSENSE;
pSRB->AdaptStatus = 0;
pSRB->TargetStatus = SCSI_STAT_CHECKCOND;
if(status == SCSI_STAT_CHECKCOND)
{
pcmd->error = XS_TIMEOUT;
goto ckc_e;
}
if(pSRB->RetryCnt == 0)
{
*((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
pSRB->TotalXferredLen = pSRB->Segment1[1];
if( pSRB->TotalXferredLen )
{
pcmd->resid = pcmd->datalen - pSRB->TotalXferredLen;
pcmd->error = XS_SENSE;
pcmd->flags |= SCSI_RESID_VALID;
}
else
{
pcmd->error = XS_SENSE;
pcmd->status = SCSI_STAT_CHECKCOND;
}
goto ckc_e;
}
else
{
pSRB->RetryCnt--;
pSRB->AdaptStatus = 0;
pSRB->TargetStatus = 0;
*((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
*((PULONG) &(pSRB->CmdBlock[4])) = pSRB->Segment0[1];
if( pSRB->CmdBlock[0] == TEST_UNIT_READY )
{
pcmd->error = XS_SENSE;
pcmd->status = SCSI_STAT_CHECKCOND;
goto ckc_e;
}
pcmd->error = XS_SENSE;
pSRB->SGcount = (UCHAR) pSRB->Segment1[0];
pSRB->ScsiCmdLen = (UCHAR) (pSRB->Segment1[0] >> 8);
pSRB->pSegmentList = (PSEG) &pSRB->SGsegment[0];
pSRB->SGIndex = 0;
pSRB->TotalXferredLen = 0;
pSRB->SGToBeXferLen = 0;
if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
RewaitSRB( pDCB, pSRB );
return;
}
}
if( status )
{
if( status == SCSI_STAT_CHECKCOND)
{
if( (pSRB->SGIndex < pSRB->SGcount) && (pSRB->SGcount) && (pSRB->SGToBeXferLen) )
{
bval = pSRB->SGcount;
swlval = pSRB->SGToBeXferLen;
ptr2 = pSRB->pSegmentList;
ptr2++;
for( i=pSRB->SGIndex+1; i < bval; i++)
{
swlval += ptr2->SGXLen;
ptr2++;
}
#ifdef DC390_DEBUG0
printf("XferredLen=%8x,NotXferLen=%8x,",
(UINT) pSRB->TotalXferredLen, (UINT) swlval);
#endif
}
RequestSense( pACB, pDCB, pSRB );
return;
}
else if( status == SCSI_STAT_QUEUEFULL )
{
bval = (UCHAR) pDCB->GoingSRBCnt;
bval--;
pDCB->MaxCommand = bval;
RewaitSRB( pDCB, pSRB );
pSRB->AdaptStatus = 0;
pSRB->TargetStatus = 0;
return;
}
else if(status == SCSI_STAT_SEL_TIMEOUT)
{
pSRB->AdaptStatus = H_SEL_TIMEOUT;
pSRB->TargetStatus = 0;
pcmd->error = XS_TIMEOUT;
}
else if (status == SCSI_STAT_BUSY)
{
#ifdef DC390_DEBUG0
printf("DC390: target busy at %s %d\n", __FILE__, __LINE__);
#endif
pcmd->error = XS_BUSY;
}
else if (status == SCSI_STAT_RESCONFLICT)
{
#ifdef DC390_DEBUG0
printf("DC390: target reserved at %s %d\n", __FILE__, __LINE__);
#endif
pcmd->error = XS_BUSY; /*XXX*/
}
else
{
pSRB->AdaptStatus = 0;
if( pSRB->RetryCnt )
{
pSRB->RetryCnt--;
pSRB->TargetStatus = 0;
pSRB->SGIndex = 0;
pSRB->TotalXferredLen = 0;
pSRB->SGToBeXferLen = 0;
pSRB->pSegmentList = (PSEG) &pSRB->SGsegment[0];
if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
RewaitSRB( pDCB, pSRB );
return;
}
else
{
#ifdef DC390_DEBUG0
printf("DC390: driver stuffup at %s %d\n", __FILE__, __LINE__);
#endif
pcmd->error = XS_DRIVER_STUFFUP;
}
}
}
else
{
status = pSRB->AdaptStatus;
if(status & H_OVER_UNDER_RUN)
{
pSRB->TargetStatus = 0;
pcmd->error = XS_LENGTH;
}
else if( pSRB->SRBStatus & PARITY_ERROR)
{
#ifdef DC390_DEBUG0
printf("DC390: driver stuffup %s %d\n", __FILE__, __LINE__);
#endif
pcmd->error = XS_DRIVER_STUFFUP;
}
else /* No error */
{
pSRB->AdaptStatus = 0;
pSRB->TargetStatus = 0;
pcmd->error = XS_NOERROR;
}
}
ckc_e:
if( pACB->scan_devices )
{
if( pSRB->CmdBlock[0] == TEST_UNIT_READY )
{
if(pcmd->error == XS_TIMEOUT )
{
pACB->DCBmap[plink->target] &= ~(1 << plink->lun);
pPrevDCB->pNextDCB = pACB->pLinkDCB;
}
else
{
pPrevDCB->pNextDCB = pDCB;
pDCB->pNextDCB = pACB->pLinkDCB;
}
}
else if( pSRB->CmdBlock[0] == INQUIRY )
{
if( (plink->target == pACB->max_id) && (plink->lun == pACB->max_lun) )
pACB->scan_devices = 0;
if(pcmd->error == XS_TIMEOUT )
goto NO_DEV;
ptr = (PSCSI_INQDATA) (pcmd->data);
bval1 = ptr->DevType & SCSI_DEVTYPE;
if(bval1 == SCSI_NODEV)
{
NO_DEV:
pACB->DCBmap[plink->target] &= ~(1 << plink->lun);
pPrevDCB->pNextDCB = pACB->pLinkDCB;
}
else
{
pACB->DeviceCnt++;
pPrevDCB = pDCB;
pACB->pDCB_free = (PDCB) ((ULONG) (pACB->pDCB_free) + sizeof( DC390_DCB ));
pDCB->DevType = bval1;
if(bval1 == SCSI_DASD || bval1 == SCSI_OPTICAL)
{
if( (((ptr->Vers & 0x07) >= 2) || ((ptr->RDF & 0x0F) == 2)) &&
(ptr->Flags & SCSI_INQ_CMDQUEUE) &&
(pDCB->DevMode & TAG_QUEUING_) &&
(pDCB->DevMode & EN_DISCONNECT_) )
{
disable_tag = 0;
for(i=0; i<BADDEVCNT; i++)
{
for(j=0; j<28; j++)
{
if( ((PUCHAR)ptr)[8+j] != baddevname1[i][j])
break;
}
if(j == 28)
{
disable_tag = 1;
break;
}
}
if( !disable_tag )
{
pDCB->MaxCommand = pACB->TagMaxNum;
pDCB->SyncMode |= EN_TAG_QUEUING;
pDCB->TagMask = 0;
}
else
{
pDCB->SyncMode |= EN_ATN_STOP;
}
}
}
}
}
}
flags = splbio();
/* ReleaseSRB( pDCB, pSRB ); */
if(pSRB == pDCB->pGoingSRB )
{
pDCB->pGoingSRB = pSRB->pNextSRB;
}
else
{
psrb = pDCB->pGoingSRB;
while( psrb->pNextSRB != pSRB )
psrb = psrb->pNextSRB;
psrb->pNextSRB = pSRB->pNextSRB;
if( pSRB == pDCB->pGoingLast )
pDCB->pGoingLast = psrb;
}
pSRB->pNextSRB = pACB->pFreeSRB;
pACB->pFreeSRB = pSRB;
pDCB->GoingSRBCnt--;
DoWaitingSRB( pACB );
splx(flags);
pcmd->flags |= ITSDONE;
/* Notify cmd done */
scsi_done( pcmd );
}
static void
DoingSRB_Done( PACB pACB )
{
PDCB pDCB, pdcb;
PSRB psrb, psrb2;
USHORT cnt, i;
PSCSICMD pcmd;
pDCB = pACB->pLinkDCB;
pdcb = pDCB;
do
{
cnt = pdcb->GoingSRBCnt;
psrb = pdcb->pGoingSRB;
for( i=0; i<cnt; i++)
{
psrb2 = psrb->pNextSRB;
pcmd = psrb->pcmd;
pcmd->error = XS_TIMEOUT;
/* ReleaseSRB( pDCB, pSRB ); */
psrb->pNextSRB = pACB->pFreeSRB;
pACB->pFreeSRB = psrb;
scsi_done( pcmd );
psrb = psrb2;
}
pdcb->GoingSRBCnt = 0;;
pdcb->pGoingSRB = NULL;
pdcb->TagMask = 0;
pdcb = pdcb->pNextDCB;
}
while( pdcb != pDCB );
}
static void
DC390_ResetSCSIBus( PACB pACB )
{
USHORT ioport;
UCHAR bval;
int flags;
flags = splbio();
pACB->ACBFlag |= RESET_DEV;
ioport = pACB->IOPortBase;
bval = DMA_IDLE_CMD;
OutB(bval,ioport+DMA_Cmd);
bval = RST_SCSI_BUS_CMD;
OutB(bval,ioport+ScsiCmd);
splx(flags);
return;
}
static void
DC390_ScsiRstDetect( PACB pACB )
{
int flags;
ULONG wlval;
USHORT ioport;
UCHAR bval;
#ifdef DC390_DEBUG0
printf("RST_DETEC");
#endif
wlval = 1000;
while( --wlval ) /* delay 1 sec */
{
DELAY(1000);
}
flags = splbio();
ioport = pACB->IOPortBase;
bval = DMA_IDLE_CMD;
OutB(bval,ioport+DMA_Cmd);
bval = CLEAR_FIFO_CMD;
OutB(bval,ioport+ScsiCmd);
if( pACB->ACBFlag & RESET_DEV )
pACB->ACBFlag |= RESET_DONE;
else
{
pACB->ACBFlag |= RESET_DETECT;
ResetDevParam( pACB );
/* DoingSRB_Done( pACB ); ???? */
RecoverSRB( pACB );
pACB->pActiveDCB = NULL;
pACB->ACBFlag = 0;
DoWaitingSRB( pACB );
}
splx(flags);
return;
}
static void
RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB )
{
PSCSICMD pcmd;
pSRB->SRBFlag |= AUTO_REQSENSE;
pSRB->Segment0[0] = *((PULONG) &(pSRB->CmdBlock[0]));
pSRB->Segment0[1] = *((PULONG) &(pSRB->CmdBlock[4]));
pSRB->Segment1[0] = (ULONG) ((pSRB->ScsiCmdLen << 8) + pSRB->SGcount);
pSRB->Segment1[1] = pSRB->TotalXferredLen;
pSRB->AdaptStatus = 0;
pSRB->TargetStatus = 0;
pcmd = pSRB->pcmd;
pSRB->Segmentx.SGXPtr = (ULONG) vtophys(&pcmd->sense);
pSRB->Segmentx.SGXLen = (ULONG) sizeof(struct scsi_sense_data);
pSRB->pSegmentList = &pSRB->Segmentx;
pSRB->SGcount = 1;
pSRB->SGIndex = 0;
*((PULONG) &(pSRB->CmdBlock[0])) = 0x00000003;
pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5;
*((PUSHORT) &(pSRB->CmdBlock[4])) = sizeof(struct scsi_sense_data);
pSRB->ScsiCmdLen = 6;
pSRB->TotalXferredLen = 0;
pSRB->SGToBeXferLen = 0;
if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
RewaitSRB( pDCB, pSRB );
}
static void
EnableMsgOut2( PACB pACB, PSRB pSRB )
{
USHORT ioport;
UCHAR bval;
ioport = pACB->IOPortBase;
pSRB->MsgCnt = 1;
bval = SET_ATN_CMD;
OutB(bval, ioport+ScsiCmd);
}
static void
EnableMsgOut( PACB pACB, PSRB pSRB )
{
pSRB->MsgOutBuf[0] = MSG_ABORT;
EnableMsgOut2( pACB, pSRB );
}
static void
DC390_InvalidCmd( PACB pACB )
{
UCHAR bval;
USHORT ioport;
PSRB pSRB;
pSRB = pACB->pActiveDCB->pActiveSRB;
if( pSRB->SRBState & (SRB_START_+SRB_MSGOUT) )
{
ioport = pACB->IOPortBase;
bval = CLEAR_FIFO_CMD;
OutB(bval,(ioport+ScsiCmd));
}
}