/***********************************************************************
 *	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 2.15 1998/12/25 17:33:27 garloff 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; i < pSRB->ScsiCmdLen; 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; 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;
}


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; i < BADDEVCNT; i++)
		if (memcmp(name, dc390_baddevname1[i], 28) == 0)
			return 1;
	return 0;
};


static void dc390_disc_tagq_set(PDCB pDCB, PSCSI_INQDATA ptr)
{
	/* Check for SCSI format (ANSI and Response data format) */
	if ((ptr->Vers & 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; i < pdcb->GoingSRBCnt; 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);
}
