/*********************************************************************** * FILE NAME : SCSIIO.C * * BY : C.L. Huang * * Description: Device Driver for Tekram DC-390W/U/F (T) PCI SCSI * * Bus Master Host Adapter * ***********************************************************************/ static void PrepareSG( PACB pACB, PDCB pDCB, PSRB pSRB ) { ULONG retAddr,wlval; USHORT wval,i; PSGL psgl; PSGE psge; retAddr = pACB->jmp_table8; if(pDCB->DCBscntl3 & EN_WIDE_SCSI) retAddr += jmp_table16; wval = (USHORT)(pSRB->SGcount); wval <<= 4; /* 16 bytes per entry, datain=8, dataout=8 */ /* (4 bytes for count, 4 bytes for addr) */ retAddr -= (ULONG)wval; pSRB->ReturnAddr = retAddr; /* return address for SCRIPT */ if(wval) { wval >>= 1; wlval = (ULONG) pSRB->SegmentPad; wlval -= (ULONG)wval; wval >>= 3; psge = (PSGE) wlval; psgl = pSRB->pSegmentList; for(i=0; iSGXPtr = virt_to_phys( psgl->address ); #else psge->SGXPtr = (ULONG) psgl->address; #endif psge->SGXLen = psgl->length; psge++; psgl++; } } } static void DC390W_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB ) { USHORT ioport; UCHAR bval; pSRB->TagNumber = 31; ioport = pACB->IOPortBase; bval = SIGNAL_PROC; outb(bval,ioport+ISTAT); pACB->pActiveDCB = pDCB; pDCB->pActiveSRB = pSRB; return; } #ifndef VERSION_ELF_1_2_13 static void DC390W_Interrupt( int irq, void *dev_id, struct pt_regs *regs) #else static void DC390W_Interrupt( int irq, struct pt_regs *regs) #endif { PACB pACB; PDCB pDCB; ULONG wlval; USHORT ioport = 0; USHORT wval, i; void (*stateV)( PACB ); UCHAR istat = 0; UCHAR bval; pACB = pACB_start; if( pACB == NULL ) return; for( i=0; i < adapterCnt; i++ ) { if( pACB->IRQLevel == (UCHAR) irq ) { ioport = pACB->IOPortBase; istat = inb( ioport+ISTAT ); if( istat & (ABORT_OP+SCSI_INT_PENDING+DMA_INT_PENDING) ) break; else pACB = pACB->pNextACB; } else { pACB = pACB->pNextACB; } } if( pACB == (PACB )-1 ) { printk("DC390W_intr: Spurious interrupt detected!\n"); return; } #ifdef DC390W_DEBUG1 printk("Istate=%2x,",istat); #endif /* if Abort operation occurred, reset abort bit before reading DMA status to prevent further aborted interrupt. */ if(istat & ABORT_OP) { istat &= ~ABORT_OP; outb(istat,ioport+ISTAT); } pDCB = pACB->pActiveDCB; bval = inb(ioport+CTEST2); /* Clear Signal Bit */ /* If Scsi Interrupt, then clear Interrupt Status by reading Scsi interrupt status register 0. */ wlval = 0; if(istat & SCSI_INT_PENDING) { wlval = (ULONG)inw( ioport+SIST0 ); wlval <<= 8; } /* If DMA Interrupt, then read the DMA status register to see what happen */ if(istat & DMA_INT_PENDING) { bval = inb(ioport+DSTAT); wlval |= (ULONG) bval; } #ifdef DC390W_DEBUG1 printk("IDstate=%8x,",(UINT) wlval); #endif if(wlval & ( (SEL_TIMEOUT << 16)+ ((SCSI_GERROR+UNEXPECT_DISC+SCSI_RESET) << 8)+ ILLEGAL_INSTRUC+ABORT_) ) { ExceptionHandler( wlval, pACB, pDCB ); } else if( wlval & SCRIPTS_INT ) { wval = inw( ioport+DSPS ); stateV = (void *) IntVector[wval]; stateV( pACB ); } else if( wlval & ( PARITY_ERROR << 8) ) ParityError( pACB, pDCB ); else if( wlval & ( PHASE_MISMATCH << 8) ) PhaseMismatch( pACB ); return; } static void ExceptionHandler(ULONG wlval, PACB pACB, PDCB pDCB) { PSRB pSRB; UCHAR bval; USHORT ioport; /* disconnect/scsi reset/illegal instruction */ ioport = pACB->IOPortBase; if(wlval & ( (SCSI_RESET+SCSI_GERROR) << 8) ) DC390W_ScsiRstDetect( pACB ); else if(wlval & ABORT_) { #ifdef DC390W_DEBUG0 printk("AboRst,"); #endif if( !InitialTime ) DC390W_ResetSCSIBus2( pACB ); } else if(wlval & (SEL_TIMEOUT << 16) ) { pACB->status = SCSI_STAT_SEL_TIMEOUT; #ifdef DC390W_DEBUG1 printk("Selto,"); #endif DC390W_CmdCompleted( pACB ); } else if(wlval & (UNEXPECT_DISC << 8) ) { bval = inb(ioport+STEST3); bval |= CLR_SCSI_FIFO; outb(bval,ioport+STEST3); bval = CLR_DMA_FIFO; outb(bval,ioport+CTEST3); pSRB = pDCB->pActiveSRB; if( pSRB->SRBState & DO_SYNC_NEGO ) { pDCB->DevMode &= ~SYNC_NEGO_; pACB->status = SCSI_STAT_CHECKCOND; DC390W_CmdCompleted( pACB ); } else if( pSRB->SRBState & DO_WIDE_NEGO ) { pDCB->DevMode &= ~WIDE_NEGO_; pACB->status = SCSI_STAT_CHECKCOND; DC390W_CmdCompleted( pACB ); } else { pACB->status = SCSI_STAT_UNEXP_BUS_F; DC390W_CmdCompleted( pACB ); } #ifdef DC390W_DEBUG0 printk("Uxpbf,"); #endif } else { #ifdef DC390W_DEBUG0 printk("Except,"); #endif DC390W_ResetSCSIBus( pACB ); } } static void ParityError( PACB pACB, PDCB pDCB ) { ULONG ioport; UCHAR bval,msg; ULONG wlval; PSRB pSRB; ioport = pACB->IOPortBase; bval = inb(ioport+SCRATCHA); if(bval & RE_SELECTED_) { #ifdef DC390W_DEBUG0 printk("ParityErr,"); #endif DC390W_ResetSCSIBus( pACB ); return; } else { pSRB = pDCB->pActiveSRB; bval = inb(ioport+STEST3); bval |= CLR_SCSI_FIFO; outb(bval,ioport+STEST3); bval = CLR_DMA_FIFO; outb(bval,ioport+CTEST3); bval = inb(ioport+DCMD); bval &= 0x07; /* get phase bits */ if(bval == 0x07) /* message in phase */ { msg = MSG_PARITY_ERROR; wlval = pACB->jmp_clear_ack; } else { msg = MSG_INITIATOR_ERROR; wlval = pACB->jmp_next; } pSRB->__msgout0[0] = 1; pSRB->MsgOutBuf[0] = msg; outl(wlval,(ioport+DSP)); return; } } static void DC390W_Signal( PACB pACB ) { PDCB pDCB; PSRB pSRB; USHORT ioport; ULONG wlval, flags; UCHAR bval,msgcnt,tagnum; save_flags(flags); cli(); ioport = pACB->IOPortBase; pDCB = pACB->pActiveDCB; pSRB = pDCB->pActiveSRB; #ifdef DC390W_DEBUG0 printk("Signal,Cmd=%2x", pSRB->CmdBlock[0]); #endif wlval = pSRB->PhysSRB; outl(wlval,(ioport+DSA)); wlval = pSRB->ReturnAddr; outl(wlval,(ioport+TEMP)); msgcnt = 1; bval = pDCB->IdentifyMsg; pSRB->MsgOutBuf[0] = bval; if( (pSRB->CmdBlock[0] != INQUIRY) && (pSRB->CmdBlock[0] != REQUEST_SENSE) ) { if(pDCB->MaxCommand > 1) { wlval = 1; tagnum = 0; while( wlval & pDCB->TagMask ) { wlval = wlval << 1; tagnum++; } pDCB->TagMask |= wlval; pSRB->TagNumber = tagnum; pSRB->MsgOutBuf[1] = MSG_SIMPLE_QTAG; pSRB->MsgOutBuf[2] = tagnum; msgcnt = 3; } } else { pSRB->MsgOutBuf[0] &= 0xBF; /* Diable Disconnected */ if(pSRB->CmdBlock[0] == INQUIRY) { if(bval & 0x07) goto type_6_3; } if(pDCB->DevMode & WIDE_NEGO_) { msgcnt = 5; *((PULONG) &(pSRB->MsgOutBuf[1])) = 0x01030201; pSRB->SRBState |= DO_WIDE_NEGO; } else if(pDCB->DevMode & SYNC_NEGO_) { msgcnt = 6; *((PULONG) &(pSRB->MsgOutBuf[1])) = 0x00010301; pSRB->MsgOutBuf[4] = pDCB->NegoPeriod; pSRB->MsgOutBuf[5] = SYNC_NEGO_OFFSET; pSRB->SRBState |= DO_SYNC_NEGO; } } type_6_3: pSRB->__msgout0[0] = (ULONG) msgcnt; wlval = 0; outl(wlval,(ioport+SCRATCHA)); bval = pDCB->DCBscntl0; outb(bval,ioport+SCNTL0); pSRB->__select = *((PULONG) &(pDCB->DCBselect)); #ifdef DC390W_DEBUG0 printk("__sel=%8x,", (UINT)(pSRB->__select)); #endif wlval = pACB->jmp_select; outl(wlval,(ioport+DSP)); restore_flags(flags); return; } static void DC390W_MessageWide( PACB pACB ) { PDCB pDCB; PSRB pSRB; PUCHAR msgoutPtr; USHORT ioport; ULONG wlval; UCHAR bval,msgcnt; #ifdef DC390W_DEBUG0 printk("MsgWide,"); #endif ioport = pACB->IOPortBase; pDCB = pACB->pActiveDCB; pSRB = pDCB->pActiveSRB; msgcnt = 0; pDCB->DCBscntl3 &= ~EN_WIDE_SCSI; msgoutPtr = pSRB->MsgOutBuf; if( pSRB->SRBState & DO_WIDE_NEGO ) { pSRB->SRBState &= ~DO_WIDE_NEGO; if( pACB->msgin123[0] == 3 ) { bval = pACB->msgin123[1]; if(bval == 1) { pDCB->DCBscntl3 |= EN_WIDE_SCSI; goto x5; } if(bval < 1) goto x5; } } /*type_11_1:*/ msgcnt = 1; *msgoutPtr = MSG_REJECT_; msgoutPtr++; x5: bval = pDCB->DCBscntl3; outb(bval,ioport+SCNTL3); AdjustTemp(pACB,pDCB,pSRB); SetXferRate(pACB,pDCB); if( pDCB->DevMode & SYNC_NEGO_ ) { *((PULONG)msgoutPtr) = 0x00010301; *(msgoutPtr + 3) = pDCB->NegoPeriod; *(msgoutPtr + 4) = SYNC_NEGO_OFFSET; msgcnt += 5; pSRB->SRBState |= DO_SYNC_NEGO; } pSRB->__msgout0[0] = (ULONG) msgcnt; wlval = pACB->jmp_clear_ack; if(msgcnt) wlval = pACB->jmp_set_atn; outl(wlval,(ioport+DSP)); return; } static void DC390W_MessageSync( PACB pACB ) { PDCB pDCB; PSRB pSRB; USHORT ioport; ULONG wlval; USHORT wval,wval1; UCHAR bval,bval1; #ifdef DC390W_DEBUG0 printk("MsgSync,"); #endif ioport = pACB->IOPortBase; pDCB = pACB->pActiveDCB; pSRB = pDCB->pActiveSRB; if( !(pSRB->SRBState & DO_SYNC_NEGO) ) goto MessageExtnd; pSRB->SRBState &= ~DO_SYNC_NEGO; if(pACB->msgin123[0] != 1) { MessageExtnd: pSRB->__msgout0[0] = 1; pSRB->MsgOutBuf[0] = MSG_REJECT_; wlval = pACB->jmp_set_atn; outl(wlval,(ioport+DSP)); return; } bval = pACB->msgin123[2]; /* offset */ asyncx: pDCB->DCBsxfer = bval; if(bval == 0) /* if offset or period == 0, async */ { if( pACB->AdaptType == DC390W ) bval = SYNC_CLK_F2+ASYNC_CLK_F2; else bval = SYNC_CLK_F4+ASYNC_CLK_F4; pDCB->DCBscntl3 = bval; } else { bval = pACB->msgin123[1]; if(bval == 0) goto asyncx; pDCB->SyncPeriod = bval; wval = (USHORT)bval; wval <<= 3; bval = pDCB->DCBscntl3; bval &= 0x0f; if(wval < 200) /* < 100 ns ==> Fast-20 */ { bval |= 0x90; /* Fast-20 and div 1 */ bval1 = 25; /* 12.5 ns */ } else if(wval < 400) { bval |= 0x30; /* 1 cycle = 25ns */ bval1 = 50; } else /* Non Fast */ { bval |= 0x50; /* 1 cycle = 50ns */ bval1 = 100; } if( pACB->AdaptType == DC390W ) bval -= 0x20; /* turn down to 40Mhz scsi clock */ /* assume 390W will not receive fast-20 */ wval1 = wval; wval /= bval1; if(wval * bval1 < wval1) wval++; /* XFERP TP2 TP1 TP0 */ wval -= 4; /* 4 0 0 0 */ /* 5 0 0 1 */ wval <<= 5; pDCB->DCBsxfer |= (UCHAR)wval; pDCB->DCBscntl3 = bval; } /*sync_2:*/ SetXferRate( pACB,pDCB ); wlval = pACB->jmp_clear_ack; /*sync_3:*/ bval = pDCB->DCBscntl3; outb(bval,ioport+SCNTL3); bval = pDCB->DCBsxfer; outb(bval,ioport+SXFER); outl(wlval,(ioport+DSP)); return; } static void DC390W_MsgReject( PACB pACB ) { PDCB pDCB; PSRB pSRB; ULONG wlval; USHORT ioport; UCHAR bval; #ifdef DC390W_DEBUG0 printk("Msgrjt,"); #endif pDCB = pACB->pActiveDCB; pSRB = pDCB->pActiveSRB; wlval = pACB->jmp_clear_ack; if(pSRB->SRBState & DO_WIDE_NEGO) { pSRB->SRBState &= ~DO_WIDE_NEGO; pDCB->DCBscntl3 &= ~EN_WIDE_SCSI; AdjustTemp( pACB, pDCB, pSRB ); SetXferRate( pACB, pDCB ); if( pDCB->DevMode & SYNC_NEGO_ ) { *((PULONG) &(pSRB->MsgOutBuf[0])) = 0x00010301; pSRB->MsgOutBuf[3] = pDCB->NegoPeriod; pSRB->MsgOutBuf[4] = SYNC_NEGO_OFFSET; pSRB->__msgout0[0] = 5; pSRB->SRBState |= DO_SYNC_NEGO; wlval = pACB->jmp_set_atn; } } else { if(pSRB->SRBState & DO_SYNC_NEGO) { pSRB->SRBState &= ~DO_SYNC_NEGO; pDCB->DCBsxfer = 0; /* reject sync msg, set aync */ if( pACB->AdaptType == DC390W ) bval = SYNC_CLK_F2+ASYNC_CLK_F2; else bval = SYNC_CLK_F4+ASYNC_CLK_F4; pDCB->DCBscntl3 = bval; SetXferRate(pACB,pDCB); wlval = pACB->jmp_clear_ack; } } ioport = pACB->IOPortBase; bval = pDCB->DCBscntl3; outb(bval,ioport+SCNTL3); bval = pDCB->DCBsxfer; outb(bval,ioport+SXFER); outl(wlval,(ioport+DSP)); return; } static void AdjustTemp( PACB pACB, PDCB pDCB, PSRB pSRB ) { USHORT ioport; ULONG wlval; wlval = pSRB->ReturnAddr; if(wlval <= pACB->jmp_table8) { if(pDCB->DCBscntl3 & EN_WIDE_SCSI) wlval += jmp_table16; } else { if((pDCB->DCBscntl3 & EN_WIDE_SCSI) == 0) wlval -= jmp_table16; } pSRB->ReturnAddr = wlval; ioport = pACB->IOPortBase; outl(wlval,(ioport+TEMP)); return; } static void SetXferRate( PACB pACB, PDCB pDCB ) { UCHAR bval; USHORT cnt, i; PDCB ptr; if( !(pDCB->IdentifyMsg & 0x07) ) { if( pACB->scan_devices ) { CurrDCBscntl3 = pDCB->DCBscntl3; } else { ptr = pACB->pLinkDCB; cnt = pACB->DeviceCnt; bval = pDCB->UnitSCSIID; for(i=0; iUnitSCSIID == bval ) { ptr->DCBsxfer = pDCB->DCBsxfer; ptr->DCBscntl3 = pDCB->DCBscntl3; } ptr = ptr->pNextDCB; } } } return; } static void DC390W_UnknownMsg( PACB pACB ) { PSRB pSRB; ULONG wlval; USHORT ioport; pSRB = pACB->pActiveDCB->pActiveSRB; pSRB->__msgout0[0] = 1; pSRB->MsgOutBuf[0] = MSG_REJECT_; wlval = pACB->jmp_set_atn; ioport = pACB->IOPortBase; outl(wlval,(ioport+DSP)); return; } static void DC390W_MessageExtnd( PACB pACB ) { DC390W_UnknownMsg( pACB ); } static void DC390W_Disconnected( PACB pACB ) { PDCB pDCB; PSRB pSRB; ULONG wlval, flags; USHORT ioport; UCHAR bval; #ifdef DC390W_DEBUG0 printk("Discnet,"); #endif save_flags(flags); cli(); pDCB = pACB->pActiveDCB; if (! pDCB) { #ifdef DC390W_DEBUG0 printk("ACB:%08lx->ActiveDCB:%08lx !,", (ULONG)pACB, (ULONG)pDCB); #endif restore_flags(flags); return; } pSRB = pDCB->pActiveSRB; ioport = pACB->IOPortBase; bval = inb(ioport+SCRATCHA); pSRB->ScratchABuf = bval; pSRB->SRBState |= SRB_DISCONNECT; /* 1.02 */ wlval = pACB->jmp_reselect; outl(wlval,(ioport+DSP)); pACB->pActiveDCB = 0; DoWaitingSRB( pACB ); restore_flags(flags); return; } static void DC390W_Reselected( PACB pACB ) { #ifdef DC390W_DEBUG0 printk("Rsel,"); #endif pACB->msgin123[0] = 0x80; /* set identify byte 80h */ DC390W_Reselected1(pACB); return; } static void DC390W_Reselected1( PACB pACB ) { PDCB pDCB; PSRB pSRB; USHORT ioport, wval; ULONG wlval, flags; UCHAR bval; #ifdef DC390W_DEBUG0 printk("Rsel1,"); #endif ioport = pACB->IOPortBase; pDCB = pACB->pActiveDCB; if(pDCB) { pSRB = pDCB->pActiveSRB; RewaitSRB( pDCB, pSRB ); } wval = (USHORT) (pACB->msgin123[0]); wval = (wval & 7) << 8; /* get LUN */ wval |= (USHORT) (inb(ioport+SSID) & 0x0f); /* get ID */ pDCB = pACB->pLinkDCB; while( *((PUSHORT) &pDCB->UnitSCSIID) != wval ) pDCB = pDCB->pNextDCB; pACB->pActiveDCB = pDCB; bval = pDCB->DCBscntl3; outb(bval,ioport+SCNTL3); bval = pDCB->DCBsxfer; outb(bval,ioport+SXFER); bval = pDCB->DCBscntl0; outb(bval,ioport+SCNTL0); if(pDCB->MaxCommand > 1) { wlval = pACB->jmp_reselecttag; outl(wlval,(ioport+DSP)); } else { pSRB = pDCB->pActiveSRB; if( !pSRB || !(pSRB->SRBState & SRB_DISCONNECT) ) { save_flags(flags); cli(); pSRB = pACB->pFreeSRB; pACB->pFreeSRB = pSRB->pNextSRB; restore_flags(flags); pSRB->SRBState = SRB_UNEXPECT_RESEL; pDCB->pActiveSRB = pSRB; pSRB->MsgOutBuf[0] = MSG_ABORT; pSRB->__msgout0[0] = 1; } pSRB->SRBState &= ~SRB_DISCONNECT; wlval = pSRB->PhysSRB; outl(wlval,(ioport+DSA)); wlval = pSRB->ReturnAddr; outl(wlval,(ioport+TEMP)); bval = pSRB->ScratchABuf; outb(bval,ioport+SCRATCHA); if( pSRB->SRBState & SRB_UNEXPECT_RESEL ) wlval = pACB->jmp_set_atn; else wlval = pACB->jmp_clear_ack; outl(wlval,(ioport+DSP)); } return; } static void DC390W_ReselectedT( PACB pACB ) { PDCB pDCB; PSRB pSRB, psrb1; USHORT ioport; ULONG wlval, flags; UCHAR bval; #ifdef DC390W_DEBUG0 printk("RselT,"); #endif ioport = pACB->IOPortBase; bval = pACB->msgin123[1]; pDCB = pACB->pActiveDCB; pSRB = pDCB->pGoingSRB; psrb1 = pDCB->pGoingLast; if( !pSRB ) goto UXP_RSL; for(;;) { if(pSRB->TagNumber != bval) { if( pSRB != psrb1 ) pSRB = pSRB->pNextSRB; else goto UXP_RSL; } else break; } if( !(pSRB->SRBState & SRB_DISCONNECT) ) { UXP_RSL: save_flags(flags); cli(); pSRB = pACB->pFreeSRB; pACB->pFreeSRB = pSRB->pNextSRB; restore_flags(flags); pSRB->SRBState = SRB_UNEXPECT_RESEL; pDCB->pActiveSRB = pSRB; pSRB->MsgOutBuf[0] = MSG_ABORT_TAG; pSRB->__msgout0[0] = 1; } else { pSRB->SRBState &= ~SRB_DISCONNECT; pDCB->pActiveSRB = pSRB; } wlval = pSRB->PhysSRB; outl(wlval,(ioport+DSA)); wlval = pSRB->ReturnAddr; outl(wlval,(ioport+TEMP)); bval = pSRB->ScratchABuf; outb(bval,ioport+SCRATCHA); if( pSRB->SRBState & SRB_UNEXPECT_RESEL ) wlval = pACB->jmp_set_atn; else wlval = pACB->jmp_clear_ack; outl(wlval,(ioport+DSP)); return; } static void DC390W_RestorePtr( PACB pACB ) { PSRB pSRB; USHORT ioport; ULONG wlval; pSRB = pACB->pActiveDCB->pActiveSRB; wlval = pSRB->ReturnAddr; ioport = pACB->IOPortBase; outl(wlval,(ioport+TEMP)); wlval = inl(ioport+DSP); outl(wlval,(ioport+DSP)); return; } static void PhaseMismatch( PACB pACB ) { USHORT ioport; ULONG wlval,swlval; USHORT wval; UCHAR bval,phase; PDCB pDCB; #ifdef DC390W_DEBUG0 printk("Mismatch,"); #endif ioport = pACB->IOPortBase; bval = inb(ioport+SCRATCHA); if(bval & OVER_RUN_) /* xfer PAD */ { bval = inb(ioport+STEST3); bval |= CLR_SCSI_FIFO; outb(bval,ioport+STEST3); bval = CLR_DMA_FIFO; outb(bval,ioport+CTEST3); wlval = pACB->jmp_next; /* check phase */ outl(wlval,(ioport+DSP)); return; } pDCB = pACB->pActiveDCB; wlval = inl(ioport+DBC); phase = (UCHAR)((wlval & 0x07000000) >> 24); wlval &= 0xffffff; /* bytes not xferred */ if( phase == SCSI_DATA_IN ) { swlval = pACB->jmp_din8; if( pDCB->DCBscntl3 & EN_WIDE_SCSI ) swlval += jmp_din16; DataIOcommon(pACB,swlval,wlval); } else if( phase == SCSI_DATA_OUT ) { wval = (USHORT)inb(ioport+CTEST5); wval <<= 8; bval = inb(ioport+DFIFO); wval |= (USHORT) bval; wval -= ((USHORT)(wlval & 0xffff)); wval &= 0x3ff; wlval += (ULONG)wval; /* # of bytes remains in FIFO */ bval = inb(ioport+SSTAT0); if(bval & SODR_LSB_FULL) wlval++; /* data left in Scsi Output Data Buffer */ if(bval & SODL_LSB_FULL) wlval++; /* data left in Scsi Output Data Latch */ swlval = pACB->jmp_dout8; if(pDCB->DCBscntl3 & EN_WIDE_SCSI) { swlval += jmp_dout16; bval = inb(ioport+SSTAT2); if(bval & SODR_MSB_FULL) wlval++; if(bval & SODL_MSB_FULL) wlval++; } bval = inb(ioport+STEST3); bval |= CLR_SCSI_FIFO; outb(bval,ioport+STEST3); bval = CLR_DMA_FIFO; outb(bval,ioport+CTEST3); DataIOcommon(pACB,swlval,wlval); } else { bval = inb(ioport+STEST3); bval |= CLR_SCSI_FIFO; outb(bval,ioport+STEST3); bval = CLR_DMA_FIFO; outb(bval,ioport+CTEST3); if(phase == SCSI_MSG_OUT) wlval = pACB->jmp_clear_atn; else wlval = pACB->jmp_next; /* check phase */ outl(wlval,(ioport+DSP)); } return; } static void DataIOcommon( PACB pACB, ULONG Swlval, ULONG Cwlval ) { /* Swlval - script address */ /* Cwlval - bytes not xferred */ PDCB pDCB; PSRB pSRB; PSGE Segptr; USHORT ioport; ULONG wlval,swlval,dataXferCnt; UCHAR bval,bvald; ioport = pACB->IOPortBase; wlval = inl((ioport+DSP)); pDCB = pACB->pActiveDCB; pSRB = pDCB->pActiveSRB; wlval -= Swlval; bval = inb(ioport+SBCL); bval &= 0x07; if(bval == SCSI_MSG_IN) { bval = pDCB->DCBscntl3; bval &= ~EN_WIDE_SCSI; outb(bval,ioport+SCNTL3); bval = inb(ioport+SBDL); bvald = pDCB->DCBscntl3; /* enable WIDE SCSI */ outb(bvald,ioport+SCNTL3); if(bval == MSG_DISCONNECT || bval == MSG_SAVE_PTR) { Segptr = (PSGE)((ULONG) &(pSRB->Segment0[0][0]) + wlval); dataXferCnt = Segptr->SGXLen - Cwlval; Segptr->SGXLen = Cwlval; /* modified count */ Segptr->SGXPtr += dataXferCnt; /* modified address */ swlval = pACB->jmp_table8; if(pDCB->DCBscntl3 & EN_WIDE_SCSI) swlval += jmp_table16; wlval <<= 1; swlval += wlval; swlval = swlval - ((MAX_SG_LIST_BUF+1) * 16); pSRB->ReturnAddr = swlval; } } else if( Cwlval ) /* Remaining not xferred -- UNDER_RUN */ { Segptr = (PSGE)((ULONG) &(pSRB->Segment0[0][0]) + wlval); dataXferCnt = Segptr->SGXLen - Cwlval; Segptr->SGXLen = Cwlval; /* modified count */ Segptr->SGXPtr += dataXferCnt; /* modified address */ swlval = pACB->jmp_table8; if(pDCB->DCBscntl3 & EN_WIDE_SCSI) swlval += jmp_table16; wlval <<= 1; swlval += wlval; swlval = swlval - ((MAX_SG_LIST_BUF+1) * 16); pSRB->RemainSegPtr = swlval; } /* pm__1: */ wlval = pSRB->ReturnAddr; outl(wlval,(ioport+TEMP)); wlval = pACB->jmp_next; outl(wlval,(ioport+DSP)); return; } static void DC390W_CmdCompleted( PACB pACB ) { PDCB pDCB; PSRB pSRB; USHORT ioport; ULONG wlval, flags; UCHAR bval; #ifdef DC390W_DEBUG0 printk("Cmplete,"); #endif save_flags(flags); cli(); pDCB = pACB->pActiveDCB; pSRB = pDCB->pActiveSRB; pDCB->pActiveSRB = NULL; ioport = pACB->IOPortBase; bval = inb(ioport+SCRATCHA); pSRB->ScratchABuf = bval; /* save status */ bval = pSRB->TagNumber; if(pDCB->MaxCommand > 1) pDCB->TagMask &= (~(1 << bval)); /* free tag mask */ pACB->pActiveDCB = NULL; /* no active device */ wlval = pACB->jmp_reselect; /* enable reselection */ outl(wlval,(ioport+DSP)); SRBdone( pACB, pDCB, pSRB); restore_flags(flags); return; } static void SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB ) { PSRB psrb; UCHAR bval, bval1, i, j, status; PSCSICMD pcmd; PSCSI_INQDATA ptr; USHORT disable_tag; ULONG flags; PSGE ptr1; PSGL ptr2; ULONG wlval,swlval; pcmd = pSRB->pcmd; status = pACB->status; if(pSRB->SRBFlag & AUTO_REQSENSE) { pSRB->SRBFlag &= ~AUTO_REQSENSE; pSRB->AdaptStatus = 0; pSRB->TargetStatus = SCSI_STAT_CHECKCOND; if(status == SCSI_STAT_CHECKCOND) { pcmd->result = DID_BAD_TARGET << 16; goto ckc_e; } if(pSRB->RetryCnt == 0) { *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0][0]; pSRB->XferredLen = pSRB->Segment0[2][1]; if( (pSRB->XferredLen) && (pSRB->XferredLen >= pcmd->underflow) ) { pcmd->result |= (DID_OK << 16); } else pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) | SCSI_STAT_CHECKCOND; goto ckc_e; } else { pSRB->RetryCnt--; pSRB->TargetStatus = 0; *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0][0]; *((PULONG) &(pSRB->CmdBlock[4])) = pSRB->Segment0[0][1]; *((PULONG) &(pSRB->CmdBlock[8])) = pSRB->Segment0[1][0]; pSRB->__command[0] = pSRB->Segment0[1][1] & 0xff; pSRB->SGcount = (UCHAR) (pSRB->Segment0[1][1] >> 8); *((PULONG) &(pSRB->pSegmentList))= pSRB->Segment0[2][0]; if( pSRB->CmdBlock[0] == TEST_UNIT_READY ) { pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) | SCSI_STAT_CHECKCOND; goto ckc_e; } pcmd->result |= (DRIVER_SENSE << 24); PrepareSG(pACB,pDCB,pSRB); pSRB->XferredLen = 0; DC390W_StartSCSI( pACB, pDCB, pSRB ); return; } } if( status ) { if( status == SCSI_STAT_CHECKCOND) { if( !(pSRB->ScratchABuf & SRB_OK) && (pSRB->SGcount) && (pSRB->RemainSegPtr) ) { wlval = pSRB->RemainSegPtr; swlval = pACB->jmp_table8; if(pDCB->DCBscntl3 & EN_WIDE_SCSI) swlval += jmp_table16; swlval -= wlval; swlval >>= 4; bval = (UCHAR) swlval; wlval = 0; ptr1 = (PSGE) &pSRB->Segment0[MAX_SG_LIST_BUF+1][0]; for( i=0; i< bval; i++) { wlval += ptr1->SGXLen; ptr1--; } bval = pSRB->SGcount; swlval = 0; ptr2 = pSRB->pSegmentList; for( i=0; i< bval; i++) { swlval += ptr2->length; ptr2++; } pSRB->XferredLen = swlval - wlval; pSRB->RemainSegPtr = 0; #ifdef DC390W_DEBUG0 printk("XferredLen=%8x,NotXferLen=%8x,",(UINT) pSRB->XferredLen,(UINT) wlval); #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->result = DID_BAD_TARGET << 16; } else if(status == SCSI_STAT_UNEXP_BUS_F) { pSRB->AdaptStatus = H_UNEXP_BUS_FREE; pSRB->TargetStatus = 0; pcmd->result |= DID_NO_CONNECT << 16; } else if(status == SCSI_STAT_BUS_RST_DETECT ) { pSRB->AdaptStatus = H_ABORT; pSRB->TargetStatus = 0; pcmd->result = DID_RESET << 16; } else { pSRB->AdaptStatus = 0; pSRB->TargetStatus = status; if( pSRB->RetryCnt ) { pSRB->RetryCnt--; pSRB->TargetStatus = 0; PrepareSG(pACB,pDCB,pSRB); pSRB->XferredLen = 0; DC390W_StartSCSI( pACB, pDCB, pSRB ); return; } else { pcmd->result |= (DID_ERROR << 16) | (ULONG) (pACB->msgin123[0] << 8) | (ULONG) status; } } } else { status = pSRB->ScratchABuf; if(status & OVER_RUN_) { pSRB->AdaptStatus = H_OVER_UNDER_RUN; pSRB->TargetStatus = 0; pcmd->result |= (DID_OK << 16) | (pACB->msgin123[0] << 8); } else /* No error */ { pSRB->AdaptStatus = 0; pSRB->TargetStatus = 0; pcmd->result |= (DID_OK << 16); } } ckc_e: if( pACB->scan_devices ) { if( pSRB->CmdBlock[0] == TEST_UNIT_READY ) { if(pcmd->result != (DID_OK << 16)) { if( pcmd->result & SCSI_STAT_CHECKCOND ) { goto RTN_OK; } else { pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun); pPrevDCB->pNextDCB = pACB->pLinkDCB; if( (pcmd->target == pACB->max_id) && ((pcmd->lun == 0) || (pcmd->lun == pACB->max_lun)) ) { pACB->scan_devices = 0; } } } else { RTN_OK: pPrevDCB->pNextDCB = pDCB; pDCB->pNextDCB = pACB->pLinkDCB; if( (pcmd->target == pACB->max_id) && (pcmd->lun == pACB->max_lun) ) pACB->scan_devices = END_SCAN; } } else if( pSRB->CmdBlock[0] == INQUIRY ) { if( (pcmd->target == pACB->max_id) && (pcmd->lun == pACB->max_lun) ) { pACB->scan_devices = 0; } ptr = (PSCSI_INQDATA) (pcmd->request_buffer); if( pcmd->use_sg ) ptr = (PSCSI_INQDATA) (((PSGL) ptr)->address); bval1 = ptr->DevType & SCSI_DEVTYPE; if(bval1 == SCSI_NODEV) { pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun); pPrevDCB->pNextDCB = pACB->pLinkDCB; } else { pACB->DeviceCnt++; pPrevDCB = pDCB; pACB->pDCB_free = (PDCB) ((ULONG) (pACB->pDCB_free) + sizeof( DC390W_DCB )); pDCB->DevType = bval1; if(bval1 == TYPE_DISK || bval1 == TYPE_MOD) { 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; iMaxCommand = pACB->TagMaxNum; pDCB->TagMask = 0; } } } } } } save_flags( flags ); cli(); /* 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 ); restore_flags(flags); /* Notify cmd done */ pcmd->scsi_done( pcmd ); if( pDCB->QIORBCnt ) DoNextCmd( pACB, pDCB ); return; } 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; ipNextSRB; pcmd = psrb->pcmd; pcmd->result = DID_RESET << 16; /* ReleaseSRB( pDCB, pSRB ); */ psrb->pNextSRB = pACB->pFreeSRB; pACB->pFreeSRB = psrb; pcmd->scsi_done( pcmd ); psrb = psrb2; } pdcb->GoingSRBCnt = 0;; pdcb->pGoingSRB = NULL; pdcb->TagMask = 0; pdcb = pdcb->pNextDCB; } while( pdcb != pDCB ); } static void DC390W_ResetSCSIBus( PACB pACB ) { USHORT ioport; UCHAR bval; ULONG flags; save_flags(flags); cli(); pACB->ACBFlag |= RESET_DEV; ioport = pACB->IOPortBase; bval = ABORT_OP; outb(bval,ioport+ISTAT); udelay(25); bval = 0; outb(bval,ioport+ISTAT); bval = ASSERT_RST; outb(bval,ioport+SCNTL1); udelay(25); /* 25 us */ bval = 0; outb(bval,ioport+SCNTL1); restore_flags(flags); return; } static void DC390W_ResetSCSIBus2( PACB pACB ) { USHORT ioport; UCHAR bval; ULONG flags; save_flags(flags); cli(); ioport = pACB->IOPortBase; bval = ASSERT_RST; outb(bval,ioport+SCNTL1); udelay(25); /* 25 us */ bval = 0; outb(bval,ioport+SCNTL1); restore_flags(flags); return; } static void DC390W_ScsiRstDetect( PACB pACB ) { ULONG wlval, flags; USHORT ioport; UCHAR bval; save_flags(flags); sti(); #ifdef DC390W_DEBUG0 printk("Reset_Detect0,"); #endif /* delay 1 sec */ wlval = jiffies + HZ; while( jiffies < wlval ); /* USHORT i; for( i=0; i<1000; i++ ) udelay(1000); */ cli(); ioport = pACB->IOPortBase; bval = inb(ioport+STEST3); bval |= CLR_SCSI_FIFO; outb(bval,ioport+STEST3); bval = CLR_DMA_FIFO; outb(bval,ioport+CTEST3); if( pACB->ACBFlag & RESET_DEV ) pACB->ACBFlag |= RESET_DONE; else { pACB->ACBFlag |= RESET_DETECT; ResetDevParam( pACB ); /* DoingSRB_Done( pACB ); ???? */ RecoverSRB( pACB ); pACB->pActiveDCB = NULL; wlval = pACB->jmp_reselect; outl(wlval,(ioport+DSP)); pACB->ACBFlag = 0; DoWaitingSRB( pACB ); } restore_flags(flags); return; } static void RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB ) { PSCSICMD pcmd; pSRB->SRBFlag |= AUTO_REQSENSE; pSRB->Segment0[0][0] = *((PULONG) &(pSRB->CmdBlock[0])); pSRB->Segment0[0][1] = *((PULONG) &(pSRB->CmdBlock[4])); pSRB->Segment0[1][0] = *((PULONG) &(pSRB->CmdBlock[8])); pSRB->Segment0[1][1] = pSRB->__command[0] | (pSRB->SGcount << 8); pSRB->Segment0[2][0] = *((PULONG) &(pSRB->pSegmentList)); pSRB->Segment0[2][1] = pSRB->XferredLen; pSRB->AdaptStatus = 0; pSRB->TargetStatus = 0; pcmd = pSRB->pcmd; pSRB->Segmentx.address = (PUCHAR) &(pcmd->sense_buffer); pSRB->Segmentx.length = sizeof(pcmd->sense_buffer); pSRB->pSegmentList = &pSRB->Segmentx; pSRB->SGcount = 1; *((PULONG) &(pSRB->CmdBlock[0])) = 0x00000003; pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5; *((PUSHORT) &(pSRB->CmdBlock[4])) = sizeof(pcmd->sense_buffer); pSRB->__command[0] = 6; PrepareSG( pACB, pDCB, pSRB ); pSRB->XferredLen = 0; DC390W_StartSCSI( pACB, pDCB, pSRB ); return; } static void DC390W_MessageOut( PACB pACB ) { DC390W_FatalError( pACB ); } static void DC390W_FatalError( PACB pACB ) { PSRB pSRB; PDCB pDCB; ULONG flags; #ifdef DC390W_DEBUG0 printk("DC390W: Fatal Error!!\n"); #endif pDCB = pACB->pActiveDCB; pSRB = pDCB->pActiveSRB; if( pSRB->SRBState & SRB_UNEXPECT_RESEL ) { save_flags(flags); cli(); pSRB->SRBState &= ~SRB_UNEXPECT_RESEL; pSRB->pNextSRB = pACB->pFreeSRB; pACB->pFreeSRB = pSRB; pACB->pActiveDCB = NULL; pDCB->pActiveSRB = NULL; restore_flags(flags); DoWaitingSRB( pACB ); } else DC390W_ResetSCSIBus(pACB); return; } static void DC390W_Debug( PACB pACB ) { ULONG wlval; USHORT ioport; ioport = pACB->IOPortBase; wlval = inl(ioport+DSP); outl(wlval,(ioport+DSP)); return; }