/*********************************************************************** * 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 * ***********************************************************************/ /* $Id: scsiiom.c,v 1.5 1999/01/04 16:07:12 ralf Exp $ */ UCHAR dc390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB ) { USHORT wlval; UCHAR bval, bval1; pSRB->TagNumber = 31; DC390_write8 (Scsi_Dest_ID, pDCB->UnitSCSIID); DC390_write8 (Sync_Period, pDCB->SyncPeriod); DC390_write8 (Sync_Offset, pDCB->SyncOffset); DC390_write8 (CtrlReg1, pDCB->CtrlR1); DC390_write8 (CtrlReg3, pDCB->CtrlR3); DC390_write8 (CtrlReg4, pDCB->CtrlR4); DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); /* Flush FIFO */ DEBUG1(printk (KERN_INFO "DC390: Start SCSI command: %02x (Sync:%02x)\n",\ pSRB->CmdBlock[0], pDCB->SyncMode);) pSRB->ScsiPhase = SCSI_NOP0; //pSRB->MsgOutBuf[0] = MSG_NOP; //pSRB->MsgCnt = 0; bval = pDCB->IdentifyMsg; if( !(pDCB->SyncMode & EN_ATN_STOP) ) /* Don't always try send Extended messages on arbitration */ { if( (pSRB->CmdBlock[0] == INQUIRY) || (pSRB->CmdBlock[0] == REQUEST_SENSE) || (pSRB->SRBFlag & AUTO_REQSENSE) ) { bval &= 0xBF; /* No DisConn */ DC390_write8 (ScsiFifo, bval); bval1 = SEL_W_ATN; pSRB->SRBState = SRB_START_; DEBUG1(printk (KERN_DEBUG "DC390: No DisCn, No TagQ (%02x, %02x)\n", bval, bval1);) if( pDCB->SyncMode & SYNC_ENABLE ) { if( !(pDCB->IdentifyMsg & 7) || /* LUN == 0 || Cmd != INQUIRY */ (pSRB->CmdBlock[0] != INQUIRY) ) { bval1 = SEL_W_ATN_STOP; /* Try to establish SYNC nego */ pSRB->SRBState = SRB_MSGOUT; } } } else /* TagQ ? */ { DC390_write8 (ScsiFifo, bval); if(pDCB->SyncMode & EN_TAG_QUEUEING) { DC390_write8 (ScsiFifo, MSG_SIMPLE_QTAG); DEBUG1(printk (KERN_DEBUG "DC390: %sDisCn, TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, SEL_W_ATN3, pDCB->TagMask);) bval = 0; wlval = 1; while (wlval & pDCB->TagMask) { bval++; wlval <<= 1; }; pDCB->TagMask |= wlval; DC390_write8 (ScsiFifo, bval); pSRB->TagNumber = bval; DEBUG1(printk (KERN_DEBUG "DC390: SRB %p (Cmd %li), Tag %02x queued\n", pSRB, pSRB->pcmd->pid, bval);) bval1 = SEL_W_ATN3; pSRB->SRBState = SRB_START_; } else /* No TagQ */ { bval1 = SEL_W_ATN; DEBUG1(printk (KERN_DEBUG "DC390: %sDisCn, No TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, bval1, pDCB->TagMask);) pSRB->SRBState = SRB_START_; } } } else /* ATN_STOP: Always try to establish Sync nego */ { if( (pSRB->CmdBlock[0] == INQUIRY) || (pSRB->CmdBlock[0] == REQUEST_SENSE) || (pSRB->SRBFlag & AUTO_REQSENSE) ) { bval &= 0xBF; /* No DisConn */ DC390_write8 (ScsiFifo, bval); bval1 = SEL_W_ATN; DEBUG1(printk (KERN_DEBUG "DC390: No DisCn, No TagQ (%02x, %02x)\n", bval, bval1);) pSRB->SRBState = SRB_START_; /* ??? */ if( pDCB->SyncMode & SYNC_ENABLE ) { if( !(pDCB->IdentifyMsg & 7) || /* LUN == 0 || Cmd != INQUIRY */ (pSRB->CmdBlock[0] != INQUIRY) ) { bval1 = SEL_W_ATN_STOP; /* Try to establish Sync nego */ pSRB->SRBState = SRB_MSGOUT; } } } else /* TagQ ? */ { DC390_write8 (ScsiFifo, bval); if(pDCB->SyncMode & EN_TAG_QUEUEING) { pSRB->MsgOutBuf[0] = MSG_SIMPLE_QTAG; DEBUG1(printk (KERN_DEBUG "DC390: %sDisCn, TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, SEL_W_ATN_STOP, pDCB->TagMask);) bval = 0; wlval = 1; while (wlval & pDCB->TagMask) { bval++; wlval <<= 1; }; pDCB->TagMask |= wlval; pSRB->TagNumber = bval; DEBUG1(printk (KERN_DEBUG "DC390: SRB %p (Cmd %li), Tag %02x queued\n", pSRB, pSRB->pcmd->pid, bval);) pSRB->MsgOutBuf[1] = bval; pSRB->MsgCnt = 2; bval1 = SEL_W_ATN_STOP; pSRB->SRBState = SRB_START_; /* ?? */ } else /* No TagQ */ { pSRB->MsgOutBuf[0] = MSG_NOP; pSRB->MsgCnt = 1; pSRB->SRBState = SRB_START_; bval1 = SEL_W_ATN_STOP; DEBUG1(printk (KERN_DEBUG "DC390: %sDisCn, No TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, bval1, pDCB->TagMask);) }; } } if (bval1 != SEL_W_ATN_STOP) { /* Command is written in CommandPhase, if SEL_W_ATN_STOP ... */ if( pSRB->SRBFlag & AUTO_REQSENSE ) { bval = 0; DC390_write8 (ScsiFifo, REQUEST_SENSE); DC390_write8 (ScsiFifo, pDCB->IdentifyMsg << 5); DC390_write8 (ScsiFifo, bval); DC390_write8 (ScsiFifo, bval); DC390_write8 (ScsiFifo, sizeof(pSRB->pcmd->sense_buffer)); DC390_write8 (ScsiFifo, bval); DEBUG1(printk (KERN_DEBUG "DC390: AutoReqSense !\n");) } else /* write cmnd to bus */ { PUCHAR ptr; UCHAR i; ptr = (PUCHAR) pSRB->CmdBlock; for (i=0; iScsiCmdLen; i++) DC390_write8 (ScsiFifo, *(ptr++)); }; } /* Check if we can't win arbitration */ if (DC390_read8 (Scsi_Status) & INTERRUPT) { pSRB->SRBState = SRB_READY; pDCB->TagMask &= ~( 1 << pSRB->TagNumber ); DEBUG0(printk (KERN_WARNING "DC390: Interrupt during StartSCSI!\n");) return 1; } else { pSRB->ScsiPhase = SCSI_NOP1; DEBUG0(if (pACB->pActiveDCB) \ printk (KERN_WARNING "DC390: ActiveDCB != 0\n");) DEBUG0(if (pDCB->pActiveSRB) \ printk (KERN_WARNING "DC390: ActiveSRB != 0\n");) pACB->pActiveDCB = pDCB; pDCB->pActiveSRB = pSRB; //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); DC390_write8 (ScsiCmd, bval1); return 0; } } //#define DMA_INT EN_DMA_INT /*| EN_PAGE_INT*/ #define DMA_INT 0 #if DMA_INT /* This is similar to AM53C974.c ... */ static UCHAR dc390_dma_intr (PACB pACB) { PSRB pSRB; UCHAR dstate; DEBUG0(USHORT pstate;PDEVDECL1;) DEBUG0(PDEVSET1;) DEBUG0(PCI_READ_CONFIG_WORD (PDEV, PCI_STATUS, &pstate);) DEBUG0(if (pstate & (PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY))\ { printk(KERN_WARNING "DC390: PCI state = %04x!\n", pstate); \ PCI_WRITE_CONFIG_WORD (PDEV, PCI_STATUS, (PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY));};) dstate = DC390_read8 (DMA_Status); if (! pACB->pActiveDCB || ! pACB->pActiveDCB->pActiveSRB) return dstate; else pSRB = pACB->pActiveDCB->pActiveSRB; if (dstate & (DMA_XFER_ABORT | DMA_XFER_ERROR | POWER_DOWN | PCI_MS_ABORT)) { printk (KERN_ERR "DC390: DMA error (%02x)!\n", dstate); return dstate; }; if (dstate & DMA_XFER_DONE) { ULONG residual, xferCnt; int ctr = 5000000; if (! (DC390_read8 (DMA_Cmd) & READ_DIRECTION)) { do { DEBUG1(printk (KERN_DEBUG "DC390: read residual bytes ... \n");) dstate = DC390_read8 (DMA_Status); residual = DC390_read8 (CtcReg_Low) | DC390_read8 (CtcReg_Mid) << 8 | DC390_read8 (CtcReg_High) << 16; residual += DC390_read8 (Current_Fifo) & 0x1f; } while (residual && ! (dstate & SCSI_INTERRUPT) && --ctr); if (!ctr) printk (KERN_CRIT "DC390: dma_intr: DMA aborted unfinished: %06x bytes remain!!\n", DC390_read32 (DMA_Wk_ByteCntr)); /* residual = ... */ } else residual = 0; /* ??? */ xferCnt = pSRB->SGToBeXferLen - residual; pSRB->SGBusAddr += xferCnt; pSRB->TotalXferredLen += xferCnt; pSRB->SGToBeXferLen = residual; # ifdef DC390_DEBUG0 printk (KERN_INFO "DC390: DMA: residual = %i, xfer = %i\n", (unsigned int)residual, (unsigned int)xferCnt); # endif DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); } dc390_laststatus &= ~0xff000000; dc390_laststatus |= dstate << 24; return dstate; }; #endif void __inline__ DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs) { PACB pACB; PDCB pDCB; PSRB pSRB; UCHAR sstatus=0; UCHAR phase, i; void (*stateV)( PACB, PSRB, PUCHAR ); UCHAR istate, istatus; #if DMA_INT UCHAR dstatus; #endif DC390_AFLAGS DC390_IFLAGS DC390_DFLAGS pACB = dc390_pACB_start; if (pACB == 0) { printk(KERN_WARNING "DC390: Interrupt on uninitialized adapter!\n"); return; } DC390_LOCK_DRV; for( i=0; i < dc390_adapterCnt; i++ ) { if( pACB->IRQLevel == (UCHAR) irq ) { sstatus = DC390_read8 (Scsi_Status); if( sstatus & INTERRUPT ) break; else pACB = pACB->pNextACB; } else { pACB = pACB->pNextACB; } } DEBUG1(printk (KERN_DEBUG "sstatus=%02x,", sstatus);) if( !pACB ) { DC390_UNLOCK_DRV; return; }; #if DMA_INT DC390_LOCK_IO; DC390_LOCK_ACB; dstatus = dc390_dma_intr (pACB); DC390_UNLOCK_ACB; DC390_UNLOCK_IO; DEBUG1(printk (KERN_DEBUG "dstatus=%02x,", dstatus);) if (! (dstatus & SCSI_INTERRUPT)) { DEBUG0(printk (KERN_WARNING "DC390 Int w/o SCSI actions (only DMA?)\n");) DC390_UNLOCK_DRV; return; }; #else //DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT); //dstatus = DC390_read8 (DMA_Status); //DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT); #endif DC390_LOCK_IO; DC390_LOCK_ACB; DC390_UNLOCK_DRV_NI; /* Allow _other_ CPUs to process IRQ (useful for shared IRQs) */ istate = DC390_read8 (Intern_State); istatus = DC390_read8 (INT_Status); /* This clears Scsi_Status, Intern_State and INT_Status ! */ DEBUG1(printk (KERN_INFO "Istatus(Res,Inv,Dis,Serv,Succ,ReS,SelA,Sel)=%02x,",istatus);) dc390_laststatus &= ~0x00ffffff; dc390_laststatus |= /* dstatus<<24 | */ sstatus<<16 | istate<<8 | istatus; if (sstatus & ILLEGAL_OP_ERR) { printk ("DC390: Illegal Operation detected (%08lx)!\n", dc390_laststatus); dc390_dumpinfo (pACB, pACB->pActiveDCB, pACB->pActiveDCB->pActiveSRB); }; if(istatus & DISCONNECTED) { dc390_Disconnect( pACB ); goto unlock; } if(istatus & RESELECTED) { dc390_Reselect( pACB ); goto unlock; } if( istatus & (SUCCESSFUL_OP|SERVICE_REQUEST) ) { pDCB = pACB->pActiveDCB; if (!pDCB) { printk (KERN_ERR "DC390: Suc. op/ Serv. req: pActiveDCB = 0!\n"); goto unlock; }; pSRB = pDCB->pActiveSRB; if( pDCB->DCBFlag & ABORT_DEV_ ) dc390_EnableMsgOut_Abort (pACB, pSRB); phase = pSRB->ScsiPhase; DEBUG1(printk (KERN_INFO "DC390: [%i]%s(0) (%02x)\n", phase, dc390_p0_str[phase], sstatus);) stateV = (void *) dc390_phase0[phase]; ( *stateV )( pACB, pSRB, &sstatus ); pSRB->ScsiPhase = sstatus & 7; phase = (UCHAR) sstatus & 7; DEBUG1(printk (KERN_INFO "DC390: [%i]%s(1) (%02x)\n", phase, dc390_p1_str[phase], sstatus);) stateV = (void *) dc390_phase1[phase]; ( *stateV )( pACB, pSRB, &sstatus ); goto unlock; } if(istatus & INVALID_CMD) { dc390_InvalidCmd( pACB ); goto unlock; } if(istatus & SCSI_RESET) { dc390_ScsiRstDetect( pACB ); goto unlock; } unlock: DC390_LOCK_DRV_NI; DC390_UNLOCK_ACB; DC390_UNLOCK_IO; DC390_UNLOCK_DRV; /* Restore initial flags */ } void do_DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs) { DEBUG1(printk (KERN_INFO "DC390: Irq (%i) caught: ", irq);) /* Locking is done in DC390_Interrupt */ DC390_Interrupt(irq, dev_id, regs); DEBUG1(printk (".. IRQ returned\n");) } void dc390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) { UCHAR sstatus; PSGL psgl; ULONG ResidCnt, xferCnt; UCHAR dstate = 0; sstatus = *psstatus; if( !(pSRB->SRBState & SRB_XFERPAD) ) { if( sstatus & (PARITY_ERR | ILLEGAL_OP_ERR) ) pSRB->SRBStatus |= PARITY_ERROR; if( sstatus & COUNT_2_ZERO ) { int ctr = 5000000; /* only try for about a tenth of a second */ while( --ctr && !((dstate = DC390_read8 (DMA_Status)) & DMA_XFER_DONE) && pSRB->SGToBeXferLen ); if (!ctr) printk (KERN_CRIT "DC390: Deadlock in DataOut_0: DMA aborted unfinished: %06x bytes remain!!\n", DC390_read32 (DMA_Wk_ByteCntr)); dc390_laststatus &= ~0xff000000; dc390_laststatus |= dstate << 24; pSRB->TotalXferredLen += pSRB->SGToBeXferLen; pSRB->SGIndex++; if( pSRB->SGIndex < pSRB->SGcount ) { pSRB->pSegmentList++; psgl = pSRB->pSegmentList; pSRB->SGBusAddr = virt_to_bus( psgl->address ); pSRB->SGToBeXferLen = (ULONG) psgl->length; } else pSRB->SGToBeXferLen = 0; } else { ResidCnt = (ULONG) DC390_read8 (Current_Fifo) & 0x1f; ResidCnt |= (ULONG) DC390_read8 (CtcReg_High) << 16; ResidCnt |= (ULONG) DC390_read8 (CtcReg_Mid) << 8; ResidCnt += (ULONG) DC390_read8 (CtcReg_Low); xferCnt = pSRB->SGToBeXferLen - ResidCnt; pSRB->SGBusAddr += xferCnt; pSRB->TotalXferredLen += xferCnt; pSRB->SGToBeXferLen = ResidCnt; } } DC390_write8 (DMA_Cmd, WRITE_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */ } void dc390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) { UCHAR sstatus, residual, bval; PSGL psgl; ULONG ResidCnt, xferCnt, i; PUCHAR ptr; sstatus = *psstatus; if( !(pSRB->SRBState & SRB_XFERPAD) ) { if( sstatus & (PARITY_ERR | ILLEGAL_OP_ERR)) pSRB->SRBStatus |= PARITY_ERROR; if( sstatus & COUNT_2_ZERO ) { int ctr = 5000000; /* only try for about a tenth of a second */ int dstate = 0; while( --ctr && !((dstate = DC390_read8 (DMA_Status)) & DMA_XFER_DONE) && pSRB->SGToBeXferLen ); if (!ctr) printk (KERN_CRIT "DC390: Deadlock in DataIn_0: DMA aborted unfinished: %06x bytes remain!!\n", DC390_read32 (DMA_Wk_ByteCntr)); if (!ctr) printk (KERN_CRIT "DC390: DataIn_0: DMA State: %i\n", dstate); dc390_laststatus &= ~0xff000000; dc390_laststatus |= dstate << 24; DEBUG1(ResidCnt = ((ULONG) DC390_read8 (CtcReg_High) << 16) \ + ((ULONG) DC390_read8 (CtcReg_Mid) << 8) \ + ((ULONG) DC390_read8 (CtcReg_Low));) DEBUG1(printk (KERN_DEBUG "Count_2_Zero (ResidCnt=%li,ToBeXfer=%li),", ResidCnt, pSRB->SGToBeXferLen);) DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */ pSRB->TotalXferredLen += pSRB->SGToBeXferLen; pSRB->SGIndex++; if( pSRB->SGIndex < pSRB->SGcount ) { pSRB->pSegmentList++; psgl = pSRB->pSegmentList; pSRB->SGBusAddr = virt_to_bus( psgl->address ); pSRB->SGToBeXferLen = (ULONG) psgl->length; } else pSRB->SGToBeXferLen = 0; } else /* phase changed */ { residual = 0; bval = DC390_read8 (Current_Fifo); while( bval & 0x1f ) { DEBUG1(printk (KERN_DEBUG "Check for residuals,");) if( (bval & 0x1f) == 1 ) { for(i=0; i < 0x100; i++) { bval = DC390_read8 (Current_Fifo); if( !(bval & 0x1f) ) goto din_1; else if( i == 0x0ff ) { residual = 1; /* ;1 residual byte */ goto din_1; } } } else bval = DC390_read8 (Current_Fifo); } din_1: DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_BLAST_CMD); for (i = 0xa000; i; i--) { bval = DC390_read8 (DMA_Status); if (bval & BLAST_COMPLETE) break; } /* It seems a DMA Blast abort isn't that bad ... */ if (!i) printk (KERN_ERR "DC390: DMA Blast aborted unfinished!\n"); //DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */ dc390_laststatus &= ~0xff000000; dc390_laststatus |= bval << 24; DEBUG1(printk (KERN_DEBUG "Blast: Read %li times DMA_Status %02x", 0xa000-i, bval);) ResidCnt = (ULONG) DC390_read8 (CtcReg_High); ResidCnt <<= 8; ResidCnt |= (ULONG) DC390_read8 (CtcReg_Mid); ResidCnt <<= 8; ResidCnt |= (ULONG) DC390_read8 (CtcReg_Low); xferCnt = pSRB->SGToBeXferLen - ResidCnt; pSRB->SGBusAddr += xferCnt; pSRB->TotalXferredLen += xferCnt; pSRB->SGToBeXferLen = ResidCnt; if( residual ) { bval = DC390_read8 (ScsiFifo); /* get one residual byte */ ptr = (PUCHAR) bus_to_virt( pSRB->SGBusAddr ); *ptr = bval; pSRB->SGBusAddr++; xferCnt++; pSRB->TotalXferredLen++; pSRB->SGToBeXferLen--; } DEBUG1(printk (KERN_DEBUG "Xfered: %li, Total: %li, Remaining: %li\n", xferCnt,\ 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) { pSRB->TargetStatus = DC390_read8 (ScsiFifo); //udelay (1); pSRB->EndMessage = DC390_read8 (ScsiFifo); /* get message */ *psstatus = SCSI_NOP0; pSRB->SRBState = SRB_COMPLETED; DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD); } static void dc390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) { if( pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT) ) *psstatus = SCSI_NOP0; //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); } static void __inline__ dc390_reprog (PACB pACB, PDCB pDCB) { DC390_write8 (Sync_Period, pDCB->SyncPeriod); DC390_write8 (Sync_Offset, pDCB->SyncOffset); DC390_write8 (CtrlReg3, pDCB->CtrlR3); DC390_write8 (CtrlReg4, pDCB->CtrlR4); dc390_SetXferRate (pACB, pDCB); }; #ifdef DC390_DEBUG0 static void dc390_printMsg (UCHAR *MsgBuf, UCHAR len) { int i; printk (" %02x", MsgBuf[0]); for (i = 1; i < len; i++) printk (" %02x", MsgBuf[i]); printk ("\n"); }; #endif #define DC390_ENABLE_MSGOUT DC390_write8 (ScsiCmd, SET_ATN_CMD) /* reject_msg */ static void __inline__ dc390_MsgIn_reject (PACB pACB, PSRB pSRB) { pSRB->MsgOutBuf[0] = MSG_REJECT_; pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT; DEBUG0 (printk (KERN_INFO "DC390: Reject message\n");) } /* abort command */ static void __inline__ dc390_EnableMsgOut_Abort ( PACB pACB, PSRB pSRB ) { pSRB->MsgOutBuf[0] = MSG_ABORT; pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT; pSRB->pSRBDCB->DCBFlag &= ~ABORT_DEV_; } static PSRB dc390_MsgIn_QTag (PACB pACB, PDCB pDCB, UCHAR tag) { PSRB lastSRB = pDCB->pGoingLast; PSRB pSRB = pDCB->pGoingSRB; if (pSRB) { for( ;pSRB ; ) { if (pSRB->TagNumber == tag) break; if (pSRB == lastSRB) goto mingx0; pSRB = pSRB->pNextSRB; } if( pDCB->DCBFlag & ABORT_DEV_ ) { pSRB->SRBState = SRB_ABORT_SENT; dc390_EnableMsgOut_Abort( 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; pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT; } return pSRB; } /* set async transfer mode */ static void dc390_MsgIn_set_async (PACB pACB, PSRB pSRB) { PDCB pDCB = pSRB->pSRBDCB; if (!(pSRB->SRBState & DO_SYNC_NEGO)) printk ("DC390: Target %i initiates Non-Sync?\n", pDCB->UnitSCSIID); pSRB->SRBState &= ~DO_SYNC_NEGO; pDCB->SyncMode &= ~(SYNC_ENABLE+SYNC_NEGO_DONE); pDCB->SyncPeriod = 0; pDCB->SyncOffset = 0; //pDCB->NegoPeriod = 50; /* 200ns <=> 5 MHz */ pDCB->CtrlR3 = FAST_CLK; /* fast clock / normal scsi */ pDCB->CtrlR4 &= 0x3f; pDCB->CtrlR4 |= pACB->glitch_cfg; /* glitch eater */ dc390_reprog (pACB, pDCB); } /* set sync transfer mode */ static void dc390_MsgIn_set_sync (PACB pACB, PSRB pSRB) { UCHAR bval; USHORT wval, wval1; PDCB pDCB = pSRB->pSRBDCB; UCHAR oldsyncperiod = pDCB->SyncPeriod; UCHAR oldsyncoffset = pDCB->SyncOffset; if (!(pSRB->SRBState & DO_SYNC_NEGO)) { printk ("DC390: Target %i initiates Sync: %ins %i ... answer ...\n", pDCB->UnitSCSIID, pSRB->MsgInBuf[3]<<2, pSRB->MsgInBuf[4]); /* reject */ //dc390_MsgIn_reject (pACB, pSRB); //return dc390_MsgIn_set_async (pACB, pSRB); /* Reply with corrected SDTR Message */ if (pSRB->MsgInBuf[4] > 15) { printk ("DC390: Lower Sync Offset to 15\n"); pSRB->MsgInBuf[4] = 15; } if (pSRB->MsgInBuf[3] < pDCB->NegoPeriod) { printk ("DC390: Set sync nego period to %ins\n", pDCB->NegoPeriod << 2); pSRB->MsgInBuf[3] = pDCB->NegoPeriod; }; memcpy (pSRB->MsgOutBuf, pSRB->MsgInBuf, 5); pSRB->MsgCnt = 5; DC390_ENABLE_MSGOUT; }; pSRB->SRBState &= ~DO_SYNC_NEGO; 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 -= 3; wval1 = wval / 25; /* compute speed */ if( (wval1 * 25) != wval) wval1++; bval = FAST_CLK+FAST_SCSI; /* fast clock / fast scsi */ pDCB->CtrlR4 &= 0x3f; /* Glitch eater: 12ns less than normal */ if (pACB->glitch_cfg != NS_TO_GLITCH(0)) pDCB->CtrlR4 |= NS_TO_GLITCH(((GLITCH_TO_NS(pACB->glitch_cfg)) - 1)); else pDCB->CtrlR4 |= NS_TO_GLITCH(0); if (wval1 < 4) pDCB->CtrlR4 |= NS_TO_GLITCH(0); /* Ultra */ if (wval1 >= 8) { wval1--; /* Timing computation differs by 1 from FAST_SCSI */ bval = FAST_CLK; /* fast clock / normal scsi */ pDCB->CtrlR4 |= pACB->glitch_cfg; /* glitch eater */ } pDCB->CtrlR3 = bval; pDCB->SyncPeriod = (UCHAR)wval1; if ((oldsyncperiod != wval1 || oldsyncoffset != pDCB->SyncOffset) && pDCB->UnitSCSILUN == 0) { if (! (bval & FAST_SCSI)) wval1++; printk ("DC390: Target %i: Sync transfer %i.%1i MHz, Offset %i\n", pDCB->UnitSCSIID, 40/wval1, ((40%wval1)*10+wval1/2)/wval1, pDCB->SyncOffset & 0x0f); } dc390_reprog (pACB, pDCB); }; /* According to the docs, the AM53C974 reads the message and * generates a Succesful Operation IRQ before asserting ACK for * the last byte (how does it know whether it's the last ?) */ /* The old code handled it in another way, indicating, that on * every message byte an IRQ is generated and every byte has to * be manually ACKed. Hmmm ? (KG, 98/11/28) */ /* The old implementation was correct. Sigh! */ /* Check if the message is complete */ static UCHAR __inline__ dc390_MsgIn_complete (UCHAR *msgbuf, ULONG len) { if (*msgbuf == MSG_EXTENDED) { if (len < 2) return 0; if (len < msgbuf[1] + 2) return 0; } else if (*msgbuf >= 0x20 && *msgbuf <= 0x2f) // two byte messages if (len < 2) return 0; return 1; } /* read and eval received messages */ void dc390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) { PDCB pDCB = pACB->pActiveDCB; /* Read the msg */ pSRB->MsgInBuf[pACB->MsgLen++] = DC390_read8 (ScsiFifo); //pSRB->SRBState = 0; /* Msg complete ? */ if (dc390_MsgIn_complete (pSRB->MsgInBuf, pACB->MsgLen)) { DEBUG0 (printk (KERN_INFO "DC390: MsgIn:"); dc390_printMsg (pSRB->MsgInBuf, pACB->MsgLen);) /* Now eval the msg */ switch (pSRB->MsgInBuf[0]) { case MSG_DISCONNECT: pSRB->SRBState = SRB_DISCONNECT; break; case MSG_SIMPLE_QTAG: case MSG_HEAD_QTAG: case MSG_ORDER_QTAG: pSRB = dc390_MsgIn_QTag (pACB, pDCB, pSRB->MsgInBuf[1]); break; case MSG_REJECT_: DC390_write8 (ScsiCmd, RESET_ATN_CMD); pDCB->NegoPeriod = 50; /* 200ns <=> 5 MHz */ if( pSRB->SRBState & DO_SYNC_NEGO) dc390_MsgIn_set_async (pACB, pSRB); break; case MSG_EXTENDED: /* reject every extended msg but SDTR */ if (pSRB->MsgInBuf[1] != 3 || pSRB->MsgInBuf[2] != EXTENDED_SDTR) dc390_MsgIn_reject (pACB, pSRB); else { if (pSRB->MsgInBuf[3] == 0 || pSRB->MsgInBuf[4] == 0) dc390_MsgIn_set_async (pACB, pSRB); else dc390_MsgIn_set_sync (pACB, pSRB); }; // nothing has to be done case MSG_COMPLETE: break; // SAVE POINTER my be ignored as we have the PSRB associated with the // scsi command. Thanks, Gerard, for pointing it out. case MSG_SAVE_PTR: break; // The device might want to restart transfer with a RESTORE case MSG_RESTORE_PTR: printk ("DC390: RESTORE POINTER message received ... reject\n"); // fall through // reject unknown messages default: dc390_MsgIn_reject (pACB, pSRB); } /* Clear counter and MsgIn state */ pSRB->SRBState &= ~SRB_MSGIN; pACB->MsgLen = 0; }; *psstatus = SCSI_NOP0; DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD); //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); } void dc390_DataIO_Comm( PACB pACB, PSRB pSRB, UCHAR ioDir) { PSGL psgl; ULONG lval; if( pSRB->SGIndex < pSRB->SGcount ) { DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir /* | DMA_INT */); if( !pSRB->SGToBeXferLen ) { psgl = pSRB->pSegmentList; pSRB->SGBusAddr = virt_to_bus( psgl->address ); pSRB->SGToBeXferLen = (ULONG) psgl->length; DEBUG1(printk (KERN_DEBUG " DC390: Next SG segment.");) } lval = pSRB->SGToBeXferLen; DEBUG1(printk (KERN_DEBUG " DC390: Transfer %li bytes (address %08lx)\n", lval, pSRB->SGBusAddr);) DC390_write8 (CtcReg_Low, (UCHAR) lval); lval >>= 8; DC390_write8 (CtcReg_Mid, (UCHAR) lval); lval >>= 8; DC390_write8 (CtcReg_High, (UCHAR) lval); DC390_write32 (DMA_XferCnt, pSRB->SGToBeXferLen); DC390_write32 (DMA_XferAddr, pSRB->SGBusAddr); //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir); /* | DMA_INT; */ pSRB->SRBState = SRB_DATA_XFER; DC390_write8 (ScsiCmd, DMA_COMMAND+INFO_XFER_CMD); DC390_write8 (DMA_Cmd, DMA_START_CMD | ioDir | DMA_INT); //DEBUG1(DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT);) //DEBUG1(printk (KERN_DEBUG "DC390: DMA_Status: %02x\n", DC390_read8 (DMA_Status));) //DEBUG1(DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT);) } else /* xfer pad */ { if( pSRB->SGcount ) { pSRB->AdaptStatus = H_OVER_UNDER_RUN; pSRB->SRBStatus |= OVER_RUN; DEBUG0(printk (KERN_WARNING " DC390: Overrun -");) } DEBUG0(printk (KERN_WARNING " Clear transfer pad \n");) DC390_write8 (CtcReg_Low, 0); DC390_write8 (CtcReg_Mid, 0); DC390_write8 (CtcReg_High, 0); pSRB->SRBState |= SRB_XFERPAD; DC390_write8 (ScsiCmd, DMA_COMMAND+XFER_PAD_BYTE); /* DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir); // | DMA_INT; DC390_write8 (DMA_Cmd, DMA_START_CMD | ioDir | DMA_INT); */ } } static void dc390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) { dc390_DataIO_Comm (pACB, pSRB, WRITE_DIRECTION); } static void dc390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) { dc390_DataIO_Comm (pACB, pSRB, READ_DIRECTION); } void dc390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) { PDCB pDCB; UCHAR i, cnt; PUCHAR ptr; DC390_write8 (ScsiCmd, RESET_ATN_CMD); DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); if( !(pSRB->SRBFlag & AUTO_REQSENSE) ) { cnt = (UCHAR) pSRB->ScsiCmdLen; ptr = (PUCHAR) pSRB->CmdBlock; for(i=0; i < cnt; i++) DC390_write8 (ScsiFifo, *(ptr++)); } else { UCHAR bval = 0; DC390_write8 (ScsiFifo, REQUEST_SENSE); pDCB = pACB->pActiveDCB; DC390_write8 (ScsiFifo, pDCB->IdentifyMsg << 5); DC390_write8 (ScsiFifo, bval); DC390_write8 (ScsiFifo, bval); DC390_write8 (ScsiFifo, sizeof(pSRB->pcmd->sense_buffer)); DC390_write8 (ScsiFifo, bval); } pSRB->SRBState = SRB_COMMAND; DC390_write8 (ScsiCmd, INFO_XFER_CMD); } static void dc390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) { DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); pSRB->SRBState = SRB_STATUS; DC390_write8 (ScsiCmd, INITIATOR_CMD_CMPLTE); //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); } void dc390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) { UCHAR bval, i, cnt; PUCHAR ptr; PDCB pDCB; DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); pDCB = pACB->pActiveDCB; if( !(pSRB->SRBState & SRB_MSGOUT) ) { cnt = pSRB->MsgCnt; if( cnt ) { ptr = (PUCHAR) pSRB->MsgOutBuf; for(i=0; i < cnt; i++) DC390_write8 (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; } DC390_write8 (ScsiFifo, bval); } DC390_write8 (ScsiCmd, INFO_XFER_CMD); } else { mop1: //printk ("DC390: Send SDTR message to %i %i ... \n", pDCB->UnitSCSIID, pDCB->UnitSCSILUN); DC390_write8 (ScsiFifo, MSG_EXTENDED); DC390_write8 (ScsiFifo, 3); /* ;length of extended msg */ DC390_write8 (ScsiFifo, EXTENDED_SDTR); /* ; sync nego */ DC390_write8 (ScsiFifo, pDCB->NegoPeriod); if (pDCB->SyncOffset & 0x0f) DC390_write8 (ScsiFifo, pDCB->SyncOffset); else DC390_write8 (ScsiFifo, SYNC_NEGO_OFFSET); pSRB->SRBState |= DO_SYNC_NEGO; DC390_write8 (ScsiCmd, INFO_XFER_CMD); } } static void dc390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) { DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); if( !(pSRB->SRBState & SRB_MSGIN) ) { pSRB->SRBState &= ~SRB_DISCONNECT; pSRB->SRBState |= SRB_MSGIN; } DC390_write8 (ScsiCmd, INFO_XFER_CMD); //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); } static void dc390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) { } static void dc390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus) { } static void dc390_SetXferRate( PACB pACB, PDCB pDCB ) { UCHAR bval, i, cnt; PDCB ptr; if( !(pDCB->IdentifyMsg & 0x07) ) { if( pACB->scan_devices ) { dc390_CurrSyncOffset = pDCB->SyncOffset; } else { ptr = pACB->pLinkDCB; cnt = pACB->DCBCnt; bval = pDCB->UnitSCSIID; for(i=0; iUnitSCSIID == 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; } void dc390_Disconnect( PACB pACB ) { PDCB pDCB; PSRB pSRB, psrb; UCHAR i, cnt; DEBUG0(printk(KERN_INFO "DISC,");) pDCB = pACB->pActiveDCB; if (!pDCB) { int j = 400; DEBUG0(printk(KERN_WARNING "ACB:%08lx->ActiveDCB:%08lx IOPort:%04x IRQ:%02x !\n",\ (ULONG)pACB,(ULONG)pDCB,pACB->IOPortBase,pACB->IRQLevel);) while (--j) udelay (1000); DC390_read8 (INT_Status); /* Reset Pending INT */ DC390_write8 (ScsiCmd, EN_SEL_RESEL); return; } pSRB = pDCB->pActiveSRB; pACB->pActiveDCB = 0; pSRB->ScsiPhase = SCSI_NOP0; DC390_write8 (ScsiCmd, EN_SEL_RESEL); if( pSRB->SRBState & SRB_UNEXPECT_RESEL ) { pSRB->SRBState = 0; dc390_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; dc390_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; dc390_RewaitSRB( pDCB, pSRB); } else { pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT; goto disc1; } } else if( pSRB->SRBState & SRB_DISCONNECT ) { dc390_DoWaitingSRB( pACB ); } else if( pSRB->SRBState & SRB_COMPLETED ) { disc1: if(pDCB->MaxCommand > 1) { pDCB->TagMask &= (~(1 << pSRB->TagNumber)); /* free tag mask */ } pDCB->pActiveSRB = 0; pSRB->SRBState = SRB_FREE; dc390_SRBdone( pACB, pDCB, pSRB); } } pACB->MsgLen = 0; } void dc390_Reselect( PACB pACB ) { PDCB pDCB; PSRB pSRB; USHORT wval; UCHAR bval; DEBUG0(printk(KERN_INFO "RSEL,");) pDCB = pACB->pActiveDCB; if( pDCB ) { /* Arbitration lost but Reselection won */ DEBUG0(printk ("(ActiveDCB != 0)");) pSRB = pDCB->pActiveSRB; if( !( pACB->scan_devices ) ) { pSRB->SRBState = SRB_READY; dc390_RewaitSRB( pDCB, pSRB); } } bval = DC390_read8 (ScsiFifo); /* get ID */ DEBUG0(printk ("Dev %02x,", bval);) bval ^= 1 << pACB->pScsiHost->this_id; /* Mask AdapterID */ wval = 0; while (bval >>= 1) wval++; wval |= ( (USHORT) DC390_read8 (ScsiFifo) & 7) << 8; /* get LUN */ DEBUG0(printk ("(ID %02x, LUN %02x),", wval & 0xff, (wval & 0xff00) >> 8);) pDCB = pACB->pLinkDCB; while( wval != *((PUSHORT) &pDCB->UnitSCSIID) ) { pDCB = pDCB->pNextDCB; if( pDCB == pACB->pLinkDCB ) { printk (KERN_ERR "DC390: Reselect from non existing device (ID %02x, LUN %02x)\n", wval & 0xff, (wval & 0xff00) >> 8); return; } } pACB->pActiveDCB = pDCB; if( pDCB->SyncMode & EN_TAG_QUEUEING ) { pSRB = pACB->pTmpSRB; /* ?? */ pDCB->pActiveSRB = pSRB; } else { pSRB = pDCB->pActiveSRB; if( !pSRB || !(pSRB->SRBState & SRB_DISCONNECT) ) { pSRB= pACB->pTmpSRB; pSRB->SRBState = SRB_UNEXPECT_RESEL; printk (KERN_ERR "DC390: Reselect without outstanding cmnd (ID %02x, LUN %02x)\n", wval & 0xff, (wval & 0xff00) >> 8); pDCB->pActiveSRB = pSRB; dc390_EnableMsgOut_Abort ( pACB, pSRB ); } else { if( pDCB->DCBFlag & ABORT_DEV_ ) { pSRB->SRBState = SRB_ABORT_SENT; printk (KERN_INFO "DC390: Reselect: Abort (ID %02x, LUN %02x)\n", wval & 0xff, (wval & 0xff00) >> 8); dc390_EnableMsgOut_Abort( pACB, pSRB ); } else pSRB->SRBState = SRB_DATA_XFER; } } DEBUG1(printk (KERN_DEBUG "Resel SRB(%p): TagNum (%02x)\n", pSRB, pSRB->TagNumber);) pSRB->ScsiPhase = SCSI_NOP0; DC390_write8 (Scsi_Dest_ID, pDCB->UnitSCSIID); DC390_write8 (Sync_Period, pDCB->SyncPeriod); DC390_write8 (Sync_Offset, pDCB->SyncOffset); DC390_write8 (CtrlReg1, pDCB->CtrlR1); DC390_write8 (CtrlReg3, pDCB->CtrlR3); DC390_write8 (CtrlReg4, pDCB->CtrlR4); /* ; Glitch eater */ DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD); /* ;to release the /ACK signal */ } static void dc390_remove_dev (PACB pACB, PDCB pDCB) { PDCB pPrevDCB = pACB->pLinkDCB; pACB->DCBmap[pDCB->UnitSCSIID] &= ~(1 << pDCB->UnitSCSILUN); if (pDCB->GoingSRBCnt > 1) { DCBDEBUG(printk (KERN_INFO "DC390: Driver won't free DCB (ID %i, LUN %i): 0x%08x because of SRBCnt %i\n",\ pDCB->UnitSCSIID, pDCB->UnitSCSILUN, (int)pDCB, pDCB->GoingSRBCnt);) return; }; if (pDCB == pACB->pLinkDCB) { if (pDCB->pNextDCB == pDCB) pDCB->pNextDCB = 0; pACB->pLinkDCB = pDCB->pNextDCB; pACB->pLastDCB->pNextDCB = pDCB->pNextDCB; } else { while (pPrevDCB->pNextDCB != pDCB) pPrevDCB = pPrevDCB->pNextDCB; pPrevDCB->pNextDCB = pDCB->pNextDCB; if (pDCB == pACB->pLastDCB) pACB->pLastDCB = pPrevDCB; } DCBDEBUG(printk (KERN_INFO "DC390: Driver about to free DCB (ID %i, LUN %i): 0x%08x\n",\ pDCB->UnitSCSIID, pDCB->UnitSCSILUN, (int)pDCB);) kfree (pDCB); if (pDCB == pACB->pActiveDCB) pACB->pActiveDCB = 0; pACB->DCBCnt--; /* pACB->DeviceCnt--; */ }; static UCHAR __inline__ dc390_tagq_blacklist (char* name) { UCHAR i; for(i=0; iVers & 0x07) >= 2 || (ptr->RDF & 0x0F) == 2 ) { if ( (ptr->Flags & SCSI_INQ_CMDQUEUE) && (pDCB->DevMode & TAG_QUEUEING_) && /* ((pDCB->DevType == TYPE_DISK) || (pDCB->DevType == TYPE_MOD)) &&*/ !dc390_tagq_blacklist (((char*)ptr)+8) ) { pDCB->MaxCommand = pDCB->pDCBACB->TagMaxNum; pDCB->SyncMode |= EN_TAG_QUEUEING /* | EN_ATN_STOP */; pDCB->TagMask = 0; } else { /* Do we really need to check for DevType here ? */ if ( 0 /*(pDCB->DevMode & EN_DISCONNECT_)*/ /* && ((pDCB->DevType == TYPE_DISK) || (pDCB->DevType == TYPE_MOD))*/ ) pDCB->SyncMode |= EN_ATN_STOP; else //pDCB->SyncMode &= ~EN_ATN_STOP; pDCB->SyncMode &= ~0; } } }; static void dc390_add_dev (PACB pACB, PDCB pDCB, PSCSI_INQDATA ptr) { UCHAR bval1 = ptr->DevType & SCSI_DEVTYPE; pDCB->DevType = bval1; /* if (bval1 == TYPE_DISK || bval1 == TYPE_MOD) */ dc390_disc_tagq_set (pDCB, ptr); }; void dc390_SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB ) { PSRB psrb; UCHAR bval, status, i; PSCSICMD pcmd; PSCSI_INQDATA ptr; PSGL ptr2; ULONG swlval; pcmd = pSRB->pcmd; status = pSRB->TargetStatus; DEBUG0(printk (" SRBdone (%02x,%08x), SRB %p, pid %li\n", status, pcmd->result,\ pSRB, pcmd->pid);) if(pSRB->SRBFlag & AUTO_REQSENSE) { /* Last command was a Request Sense */ pSRB->SRBFlag &= ~AUTO_REQSENSE; pSRB->AdaptStatus = 0; pSRB->TargetStatus = SCSI_STAT_CHECKCOND; #ifdef DC390_REMOVABLEDEBUG switch (pcmd->sense_buffer[2] & 0x0f) { case NOT_READY: printk (KERN_INFO "DC390: ReqSense: NOT_READY (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN, status, pACB->scan_devices); break; case UNIT_ATTENTION: printk (KERN_INFO "DC390: ReqSense: UNIT_ATTENTION (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN, status, pACB->scan_devices); break; case ILLEGAL_REQUEST: printk (KERN_INFO "DC390: ReqSense: ILLEGAL_REQUEST (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN, status, pACB->scan_devices); break; case MEDIUM_ERROR: printk (KERN_INFO "DC390: ReqSense: MEDIUM_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN, status, pACB->scan_devices); break; case HARDWARE_ERROR: printk (KERN_INFO "DC390: ReqSense: HARDWARE_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN, status, pACB->scan_devices); break; } #endif //pcmd->result = DRIVER_SENSE << 24 | DID_OK << 16 | status; if(status == SCSI_STAT_CHECKCOND) { pcmd->result = DID_BAD_TARGET << 16; goto ckc_e; } if(pSRB->RetryCnt == 0) { (ULONG)(pSRB->CmdBlock[0]) = pSRB->Segment0[0]; pSRB->TotalXferredLen = pSRB->Segment1[1]; if( (pSRB->TotalXferredLen) && (pSRB->TotalXferredLen >= pcmd->underflow) ) pcmd->result |= (DID_OK << 16); else pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) | SCSI_STAT_CHECKCOND; REMOVABLEDEBUG(printk(KERN_INFO "Cmd=%02x,Result=%08x,XferL=%08x\n",pSRB->CmdBlock[0],\ (UINT) pcmd->result, (UINT) pSRB->TotalXferredLen);) goto ckc_e; } else /* Retry */ { pSRB->RetryCnt--; pSRB->AdaptStatus = 0; pSRB->TargetStatus = 0; *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0]; *((PULONG) &(pSRB->CmdBlock[4])) = pSRB->Segment0[1]; /* Don't retry on TEST_UNIT_READY */ if( pSRB->CmdBlock[0] == TEST_UNIT_READY /* || pSRB->CmdBlock[0] == START_STOP */) { pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) | SCSI_STAT_CHECKCOND; REMOVABLEDEBUG(printk(KERN_INFO "Cmd=%02x, Result=%08x, XferL=%08x\n",pSRB->CmdBlock[0],\ (UINT) pcmd->result, (UINT) pSRB->TotalXferredLen);) goto ckc_e; } pcmd->result |= (DRIVER_SENSE << 24); pSRB->SGcount = (UCHAR) pSRB->Segment1[0]; pSRB->ScsiCmdLen = (UCHAR) (pSRB->Segment1[0] >> 8); pSRB->SGIndex = 0; pSRB->TotalXferredLen = 0; pSRB->SGToBeXferLen = 0; if( pcmd->use_sg ) pSRB->pSegmentList = (PSGL) pcmd->request_buffer; else if( pcmd->request_buffer ) { pSRB->pSegmentList = (PSGL) &pSRB->Segmentx; pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer; pSRB->Segmentx.length = pcmd->request_bufflen; } if( dc390_StartSCSI( pACB, pDCB, pSRB ) ) dc390_RewaitSRB( pDCB, pSRB ); return; } } if( status ) { if( status == SCSI_STAT_CHECKCOND) { REMOVABLEDEBUG(printk (KERN_INFO "DC390: Scsi_Stat_CheckCond (Cmd %02x, Id %02x, LUN %02x)\n",\ pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN);) if( (pSRB->SGIndex < pSRB->SGcount) && (pSRB->SGcount) && (pSRB->SGToBeXferLen) ) { bval = pSRB->SGcount; swlval = 0; ptr2 = pSRB->pSegmentList; for( i=pSRB->SGIndex; i < bval; i++) { swlval += ptr2->length; ptr2++; } REMOVABLEDEBUG(printk(KERN_INFO "XferredLen=%08x,NotXferLen=%08x\n",\ (UINT) pSRB->TotalXferredLen, (UINT) swlval);) } dc390_RequestSense( pACB, pDCB, pSRB ); return; } else if( status == SCSI_STAT_QUEUEFULL ) { bval = (UCHAR) pDCB->GoingSRBCnt; bval--; pDCB->MaxCommand = bval; dc390_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; /* Devices are removed below ... */ } else if (status == SCSI_STAT_BUSY && (pSRB->CmdBlock[0] == TEST_UNIT_READY || pSRB->CmdBlock[0] == INQUIRY) && pACB->scan_devices) { pSRB->AdaptStatus = 0; pSRB->TargetStatus = status; pcmd->result = (ULONG) (pSRB->EndMessage << 8) /* | (ULONG) status */; } else { /* Another error */ pSRB->AdaptStatus = 0; if( pSRB->RetryCnt ) { /* Retry */ pSRB->RetryCnt--; pSRB->TargetStatus = 0; pSRB->SGIndex = 0; pSRB->TotalXferredLen = 0; pSRB->SGToBeXferLen = 0; if( pcmd->use_sg ) pSRB->pSegmentList = (PSGL) pcmd->request_buffer; else if( pcmd->request_buffer ) { pSRB->pSegmentList = (PSGL) &pSRB->Segmentx; pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer; pSRB->Segmentx.length = pcmd->request_bufflen; } if( dc390_StartSCSI( pACB, pDCB, pSRB ) ) dc390_RewaitSRB( pDCB, pSRB ); return; } else { /* Report error */ pcmd->result |= (DID_ERROR << 16) | (ULONG) (pSRB->EndMessage << 8) | (ULONG) status; } } } else { /* Target status == 0 */ status = pSRB->AdaptStatus; if(status & H_OVER_UNDER_RUN) { pSRB->TargetStatus = 0; pcmd->result |= (DID_OK << 16) | (pSRB->EndMessage << 8); } else if( pSRB->SRBStatus & PARITY_ERROR) { pcmd->result |= (DID_PARITY << 16) | (pSRB->EndMessage << 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 ) { #ifdef DC390_DEBUG0 printk (KERN_INFO "DC390: Test_Unit_Ready: result: %08x", pcmd->result); if (pcmd->result & DRIVER_SENSE << 24) printk (" (sense: %02x %02x %02x %02x)\n", pcmd->sense_buffer[0], pcmd->sense_buffer[1], pcmd->sense_buffer[2], pcmd->sense_buffer[3]); else printk ("\n"); #endif if((pcmd->result != (DID_OK << 16) && !(pcmd->result & SCSI_STAT_CHECKCOND) && !(pcmd->result & SCSI_STAT_BUSY)) || ((pcmd->result & DRIVER_SENSE << 24) && (pcmd->sense_buffer[0] & 0x70) == 0x70 && (pcmd->sense_buffer[2] & 0xf) == ILLEGAL_REQUEST) || pcmd->result & DID_ERROR << 16) { /* device not present: remove */ dc390_remove_dev (pACB, pDCB); if( (pcmd->target == pACB->pScsiHost->max_id - 1) && ((pcmd->lun == 0) || (pcmd->lun == pACB->pScsiHost->max_lun - 1)) ) pACB->scan_devices = 0; } else { /* device present: add */ if( (pcmd->target == pACB->pScsiHost->max_id - 1) && (pcmd->lun == pACB->pScsiHost->max_lun - 1) ) pACB->scan_devices = END_SCAN ; /* pACB->DeviceCnt++; */ /* Dev is added on INQUIRY */ } } } if( pSRB->CmdBlock[0] == INQUIRY && (pcmd->result == DID_OK << 16 || pcmd->result & SCSI_STAT_CHECKCOND) ) { ptr = (PSCSI_INQDATA) (pcmd->request_buffer); if( pcmd->use_sg ) ptr = (PSCSI_INQDATA) (((PSGL) ptr)->address); if ((ptr->DevType & SCSI_DEVTYPE) == TYPE_NODEV) { /* device not present: remove */ dc390_remove_dev (pACB, pDCB); } else { /* device found: add */ dc390_add_dev (pACB, pDCB, ptr); if (pACB->scan_devices) pACB->DeviceCnt++; } if( (pcmd->target == pACB->pScsiHost->max_id - 1) && (pcmd->lun == pACB->pScsiHost->max_lun - 1) ) pACB->scan_devices = 0; }; /* dc390_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--; dc390_DoWaitingSRB( pACB ); DC390_UNLOCK_ACB_NI; pcmd->scsi_done( pcmd ); DC390_LOCK_ACB_NI; if( pDCB->QIORBCnt ) dc390_DoNextCmd( pACB, pDCB ); return; } /* Remove all SRBs and tell midlevel code DID_RESET */ void dc390_DoingSRB_Done( PACB pACB ) { PDCB pDCB, pdcb; PSRB psrb, psrb2; UCHAR i; PSCSICMD pcmd; pDCB = pACB->pLinkDCB; pdcb = pDCB; if (! pdcb) return; do { psrb = pdcb->pGoingSRB; for( i=0; iGoingSRBCnt; i++) { psrb2 = psrb->pNextSRB; pcmd = psrb->pcmd; pcmd->result = DID_RESET << 16; /* ReleaseSRB( pDCB, pSRB ); */ psrb->pNextSRB = pACB->pFreeSRB; pACB->pFreeSRB = psrb; DC390_UNLOCK_ACB_NI; pcmd->scsi_done( pcmd ); DC390_LOCK_ACB_NI; psrb = psrb2; } pdcb->GoingSRBCnt = 0;; pdcb->pGoingSRB = NULL; pdcb->TagMask = 0; pdcb = pdcb->pNextDCB; } while( pdcb != pDCB ); } static void dc390_ResetSCSIBus( PACB pACB ) { pACB->ACBFlag |= RESET_DEV; DC390_write8 (ScsiCmd, RST_DEVICE_CMD); udelay (250); DC390_write8 (ScsiCmd, NOP_CMD); DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); DC390_write8 (ScsiCmd, RST_SCSI_BUS_CMD); return; } static void dc390_ScsiRstDetect( PACB pACB ) { printk ("DC390: Rst_Detect: laststat = %08lx\n", dc390_laststatus); //DEBUG0(printk(KERN_INFO "RST_DETECT,");) DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); /* Unlock before ? */ /* delay a second */ { unsigned int msec = 1*1000; while (--msec) udelay(1000); } DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); if( pACB->ACBFlag & RESET_DEV ) pACB->ACBFlag |= RESET_DONE; else { pACB->ACBFlag |= RESET_DETECT; dc390_ResetDevParam( pACB ); /* dc390_DoingSRB_Done( pACB ); ???? */ dc390_RecoverSRB( pACB ); pACB->pActiveDCB = NULL; pACB->ACBFlag = 0; dc390_DoWaitingSRB( pACB ); } return; } static void __inline__ dc390_RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB ) { PSCSICMD pcmd; REMOVABLEDEBUG(printk (KERN_INFO "DC390: RequestSense (Cmd %02x, Id %02x, LUN %02x)\n",\ pSRB->CmdBlock[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN);) pSRB->SRBFlag |= AUTO_REQSENSE; pSRB->Segment0[0] = (ULONG) pSRB->CmdBlock[0]; pSRB->Segment0[1] = (ULONG) pSRB->CmdBlock[4]; pSRB->Segment1[0] = (ULONG) ((pSRB->ScsiCmdLen << 8) + pSRB->SGcount); pSRB->Segment1[1] = pSRB->TotalXferredLen; pSRB->AdaptStatus = 0; pSRB->TargetStatus = 0; /* SCSI_STAT_CHECKCOND; */ pcmd = pSRB->pcmd; pSRB->Segmentx.address = (PUCHAR) &(pcmd->sense_buffer); pSRB->Segmentx.length = sizeof(pcmd->sense_buffer); pSRB->pSegmentList = &pSRB->Segmentx; pSRB->SGcount = 1; pSRB->SGIndex = 0; pSRB->CmdBlock[0] = REQUEST_SENSE; pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5; (USHORT) pSRB->CmdBlock[2] = 0; (USHORT) pSRB->CmdBlock[4] = sizeof(pcmd->sense_buffer); pSRB->ScsiCmdLen = 6; pSRB->TotalXferredLen = 0; pSRB->SGToBeXferLen = 0; if( dc390_StartSCSI( pACB, pDCB, pSRB ) ) dc390_RewaitSRB( pDCB, pSRB ); } static void __inline__ dc390_InvalidCmd( PACB pACB ) { if( pACB->pActiveDCB->pActiveSRB->SRBState & (SRB_START_+SRB_MSGOUT) ) DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); }