/* Copyright (C) 1991, 1992 by Always Technology Corporation. This module contains information proprietary to Always Technology Corporation, and is be treated as confidential. */ #include "environ.h" #include "rqm.h" #include "api.h" #include "apiscsi.h" #include "debug.h" #include "33c93.h" #include "in2000.h" #define StatMask 0xc1 // Bits of interest in FIFO status register #define FIFOThresh 16 // Minimum xfer lenth for FIFOed xfers #define FIFOPad 32 // How many bytes must be added to writes to push data out of FIFO #define FIFOFillOffset 64 // Minimum amout of room to leave at top of FIFO #define FIFOSize 2048 // Total size of FIFO #define MaxPreFill (FIFOSize / 2) #define AuxStat HA->IOBase+INAuxOff #define WDSelect HA->IOBase+INWDSelOff #define WDData HA->IOBase+INWDDataOff #define INData HA->IOBase+INDataOff #define lengthof(x) (sizeof(x) / sizeof(x[0])) // Prototypes: int IN2000_ISR(ADAPTER_PTR HA); U32 IN2000_Service(int Func, ADAPTER_PTR HA, U32 Misc); #define SetWDReg(HA,WDReg) outb(HA->Ext->AD.IN2000U.IOMap[INWDSelOff], (WDReg)) #define ReadWDData(HA) inb(HA->Ext->AD.IN2000U.IOMap[INWDDataOff]) #define ReadWDReg(HA,reg) (SetWDReg(HA,reg), ReadWDData(HA)) #define WriteWDData(HA, val) outb(HA->Ext->AD.IN2000U.IOMap[INWDDataOff], (val)) #define WriteWDReg(HA,reg,val) SetWDReg(HA,(reg));WriteWDData(HA, val) extern void WD33c93_ISR(ADAPTER_PTR HA); extern void WD33C93_Init(ADAPTER_PTR HA); extern BOOLEAN GlobalAllowSync; typedef struct { U8 OwnID; U8 CtrlReg; U8 TimeOutReg; U8 SourceReg; } StateBuffer; LOCAL void IN2000ReInit (ADAPTER_PTR HA) { // Reset the chip with a reset command. The reset is complete when // the interrupt register is set WriteWDReg(HA, WDCMDReg, WDResetCmd); while ((ReadWDReg(HA, WDAuxStatReg) & IntPending) == 0) ; ReadWDReg(HA, WDStatusReg); /* Clear the interrupt */ WD33C93_Init(HA); HA->Ext->AD.IN2000U.CurrIntMask = INFIFOMask; outb(HA->IOBase+INIntMaskOff, INFIFOMask); /* Mask off FIFO, allow 33c93 ints. */ } LOCAL void IN2000ResetBus (ADAPTER_PTR HA) { unsigned j; TRACE(2, ("IN2000ResetBus(): \n")); HA->Ext->AD.IN2000U.CurrIntMask = INFIFOMask; outb(HA->Ext->AD.IN2000U.IOMap[INIntMaskOff], INFIFOMask); /* Mask off FIFO, allow 33c93 ints. */ // Issue a SCSI bus reset; SCSI-2 says reset can any length > 25uS, // however, some devices lose their mind if reset is too long, // So, we'll try for 50uS, assumeing an 8-MHz bus: outb(HA->Ext->AD.IN2000U.IOMap[INResetOff], 0); // Assert reset for (j=30; j; j--) // don't make reset too short, figure 10Mhz ISA bus, 4 cycles / IO inb(HA->Ext->AD.IN2000U.IOMap[INFIFOOff]); // but too long breaks some drives inb(HA->Ext->AD.IN2000U.IOMap[INHWRevOff]); // de-assert reset WriteWDReg(HA, WDCMDReg, WDResetCmd); // Reset chip, intr. will re-initialize it } LOCAL U32 IN2000Init (ADAPTER_PTR HA) { int j; /* Mask off ints while we're in init. routine */ outb(HA->IOBase+INIntMaskOff, INFIFOMask | INSBICMask); // Set up the I/O port map: for (j=0; j<= 15; j++) HA->Ext->AD.IN2000U.IOMap[j] = HA->IOBase + j; // Issue a SCSI bus reset; SCSI-2 says reset can any length > 25uS, // however, some devices lose their mind if reset is too long, // So, we'll try for 50uS, assumeing an 8-MHz bus: inb(HA->IOBase + INHWRevOff); // Precautionary deassert reset outb(HA->IOBase + INResetOff, 0); // Reset the board for (j=30; j; j--) // don't make reset too short, figure 10Mhz ISA bus, 4 cycles / IO inb(HA->Ext->AD.IN2000U.IOMap[INFIFOOff]); // but too long breaks some drives inb(HA->IOBase + INHWRevOff); /* de-assert reset */ APINotifyReset(HA); ((StateBuffer *)(HA->Ext->InitialState))->OwnID = 7; ((StateBuffer *)(HA->Ext->InitialState))->CtrlReg = ReadWDReg(HA, WDControlReg); ((StateBuffer *)(HA->Ext->InitialState))->TimeOutReg = ReadWDReg(HA, WDTimeoutReg); ((StateBuffer *)(HA->Ext->InitialState))->SourceReg = ReadWDReg(HA, WDSourceReg); HA->Ext->SBIC.WD33C93.WDSelPort = HA->IOBase; HA->Ext->SBIC.WD33C93.WDDataPort = HA->IOBase+1; HA->Ext->SBIC.WD33C93.MHz = 10; /* The IN-2000 uses a 10Mhz 33c93 */ HA->Ext->SBIC.WD33C93.AsyncValue = 0x30; IN2000ReInit(HA); HA->State.Allow = 1; // Allow request processing return 0; // OK } int Find_IN2000 (ADAPTER_PTR HA, unsigned *Context) { static const unsigned BaseList[]= {0x100, 0x110, 0x200, 0x220}; /* Bits are inverted */ static const unsigned IRQList[]={10, 11, 14, 15}; unsigned Switch; IOHandle INBase; int HWVers; unsigned Terminal = lengthof(BaseList); // For Chicago: // // If HA->IOBase is entered != 0, then we need to find the index of the // matching I/O address. If we find one, limit the terminus of the // primary check below, so we check only one instance. If we don't find // a match, then the primary loop below will fail to start. Not pretty, // but it works. if (HA->IOBaseAddr != 0) { for (*Context = 0; *Context < lengthof(BaseList); (*Context)++) if (HA->IOBaseAddr == BaseList[*Context]) break; Terminal = min(*Context + 1, lengthof(BaseList)); } TRACE(4, ("Find_IN2000(): HA Ptr = %x,Context = %x\n", HA, *Context)); for (; *Context < Terminal; DeregisterIO(HA, INBase), (*Context)++) { INBase = RegisterIO(HA, BaseList[*Context], 15, AddrSpaceIO); Switch = inb(INBase+INSwitchOff); TRACE(5,("Find_IN2000(): Switch value read was: %02x\n", Switch)); if ((Switch & 3) != *Context) /* Do switch settings match? */ continue; /* Check the version number port, see if it appears IN-2000-ish */ HWVers = inb(INBase+INHWRevOff); TRACE(5,("Find_IN2000(): H/W version read as: %02x\n", HWVers)); if ((HWVers < 0x20) || (HWVers > 0x29)) continue; if (HWVers < MinHWVers) { LogMessage(HA, NILL, 0, 0, MSG_BAD_FIRMWARE, HWVers); TRACE(1,("Version of the IN-2000 SPROM at I/O address %x is %02x. Please" " call Always\nfor upgrade instructions. Board is being " "ignored.\n\n", INBase, HWVers)); continue; } if (Switch & 0x04) HA->IRQNumber = IRQList[(Switch >> 3) & 0x3]; else { LogMessage(HA, NILL, 0, 0, MSG_NO_INT_ENABLE, BaseList[*Context]); TRACE(1,("IN-2000 at I/O %xh must have its interrupts enabled." " Board is being ignored.\n\n", INBase)); continue; } HA->IOBase = INBase; HA->IOBaseAddr = BaseList[*Context]; HA->IOAddrLen = 15; HA->SCSI_ID = 7; HA->Service = IN2000_Service; HA->ISR = IN2000_ISR; HA->Name = "IN-2000"; #if defined(WINNT) // Test the DOS 5/Sync. switch (8); On, supports DOS 5 & synchronous HA->Supports.Synchronous = ((Switch & 0x20) == 0); // Support sync. if switch 8 is on #else HA->Supports.Synchronous = GlobalAllowSync; // Support sync. if switch 8 is on #endif HA->Supports.Identify = TRUE; HA->Physical.BusType = BT_ISA; (*Context)++; // Found one, so inc. for next entry return 1; } return 0; } void SetUpXfer (ADAPTER_PTR HA, IO_REQ_PTR Req, unsigned Dir) { unsigned i; HA->Ext->AD.IN2000U.CBuff = (char FAR *)&(((char FAR *)(ReqDataPtr(Req)))[(unsigned)(HA->ReqCurrentIndex)]); HA->Ext->AD.IN2000U.CRemain = HA->ReqCurrentCount; HA->Ext->AD.IN2000U.CurrDir = (Dir == SCSIIn) ? IN2000DataIn : IN2000DataOut; TRACE(5,("SetUpXfer(): %ld (0x%lx) bytes to transfered to/from 0x%08lx\n", HA->Ext->AD.IN2000U.CRemain, HA->Ext->AD.IN2000U.CRemain, HA->Ext->AD.IN2000U.CBuff)); outb(HA->Ext->AD.IN2000U.IOMap[INFIFOResetOff], 1); /* Reset the FIFO */ if (HA->ReqCurrentCount >= FIFOThresh) { TRACE(5,("SetUpXfer(): Setting up for FIFO xfer\n")); if (Dir == SCSIIn) { TRACE(5,("SetUpXfer(): Setting FIFO direction to read\n")); outb(HA->Ext->AD.IN2000U.IOMap[INDirOff], 1); /* set read mode */ HA->Ext->AD.IN2000U.CurrIntMask = (HA->Ext->AD.IN2000U.CRemain >= (FIFOSize-FIFOFillOffset)) ? 0 : INFIFOMask; } i = (ReadWDReg(HA, WDControlReg) & ~DMAModeMask) | DMABus; do { WriteWDReg(HA, WDControlReg, i); } while (i != ReadWDReg(HA, WDControlReg)); if (Dir != SCSIIn) { // Doing DATA OUT /* The IN-2000 FIFO mechinism requires pre-loading on write operations. At least 32 bytes must be pre-loaded, or else data loss may occur. Upon transfering the final bytes to the FIFO, it must be padded by writing 32 bytes of junk, to move the valid data up from the first 32 byte "twilight-zone". This will be done in the FIFO fill routine. */ i = (unsigned)min(HA->ReqCurrentCount, (U32)MaxPreFill); // Don't overfill FIFO TRACE(5,("SetUpXfer(): Preloading %d bytes for write.\n", i)); repoutsw(HA->Ext->AD.IN2000U.IOMap[INDataOff], (U16 FAR *)HA->Ext->AD.IN2000U.CBuff, (i+1)/2); // Pre-fill FIFO HA->Ext->AD.IN2000U.CBuff += i; // Offset buffer ptr by amount written HA->Ext->AD.IN2000U.CRemain -= i; // Decrement remaining count if (HA->Ext->AD.IN2000U.CRemain) // is there more stuff after this? HA->Ext->AD.IN2000U.CurrIntMask = 0; // Then don't mask FIFO ints else { // If not, send pad characters TRACE(5, ("SetupXfer(): Padding FIFO\n")); for (i=(FIFOPad / 2); i; i--) outw(HA->Ext->AD.IN2000U.IOMap[INDataOff], (U16)i); HA->Ext->AD.IN2000U.CurrIntMask = INFIFOMask; // Block FIFO ints } } } else { TRACE(5, ("SetupXfer(): Using byte I/O\n")); i = (ReadWDReg(HA, WDControlReg) & ~DMAModeMask) | DMAPIO; do { WriteWDReg(HA, WDControlReg, i); } while (i != ReadWDReg(HA, WDControlReg)); } TRACE(5,("SetUpXfer(): SetUpXfer complete\n")); } U8 REGPARMS FIFOStat (const IOHandle Port) { #ifdef ASMIO asm { mov dx, word ptr Port in al, dx } InAgain: asm { mov ah, al in al, dx sub ah, al jnz InAgain } return _AX; /* AH is already zeroed for unsigned promotion */ #else int i; U8 Stat1, Stat2; Stat2 = inb(Port) & StatMask; do { Stat1 = Stat2; for (i=3; i; i--) Stat2 &= inb(Port); // Find which bits are stable for 4 reads Stat2 &= StatMask; // Most sig. two bits, and int bit are interesting } while (Stat1 != Stat2); return Stat1; #endif } void EmptyFIFO (ADAPTER_PTR HA) { union { U32 l; unsigned char b[4]; } Count; unsigned InFIFO, i; /* Update the pointers; First get the number of bytes the SCSI chip thinks remain to be transfered. Then compare to the number of bytes the HA structure says remain. The differance is the number of bytes in the FIFO. In the case of read data, we need to read the bytes out of the FIFO. The number of bytes in the FIFO is the number of bytes the structure says we've read, minus what the SCSI chip has sent to the FIFO. The buffer pointer is then incremented, and the remaining count is decremented by that amount. For write data, the number of bytes in the FIFO is the amount the SCSI chip has yet to write, minus what the driver has yet to send to the FIFO. The data in the FIFO is dropped, the buffer pointer is set back, and the remaining count is incremented. */ if ((HA->Ext->AD.IN2000U.CurrDir == IN2000DataIn) && (HA->Ext->AD.IN2000U.CRemain == 0)) { HA->ReqCurrentIndex += HA->ReqCurrentCount; HA->ReqCurrentCount = 0; } else { #if defined(NATIVE32) Count.l = (U32)ReadWDReg(HA, WDCountReg); Count.l = (Count.l * 256) + (U32)ReadWDData(HA); Count.l = (Count.l * 256) + (U32)ReadWDData(HA); #else Count.b[3] = 0; Count.b[2] = ReadWDReg(HA, WDCountReg); Count.b[1] = ReadWDData(HA); Count.b[0] = ReadWDData(HA); #endif TRACE(4,("EmptyFIFO(): Value of Xfer count registers: 0x%08lx\n", Count.l)); if (HA->Ext->AD.IN2000U.CurrDir == IN2000DataIn) { // Get number we have untransfered, minus what the chip has left untransfered // to give the number held in the FIFO: InFIFO = (unsigned)(HA->Ext->AD.IN2000U.CRemain - Count.l); TRACE(4,("EmptyFIFO(): CRemain=0x%08lx, in FIFO to read: %04x\n", HA->Ext->AD.IN2000U.CRemain, InFIFO)); if (InFIFO > 0) { TRACE(5, ("EmptyFIFO(): final read %d bytes\n", InFIFO)); repinsw(HA->Ext->AD.IN2000U.IOMap[INDataOff], (U16 FAR *)HA->Ext->AD.IN2000U.CBuff, (InFIFO+1)/2); } } // When the transfer was set up, the count registers where loaded with // ReqCurrCount(); now the counters reflect the number of bytes untransfered // Increment the index by (StartBytesToXfer - RemainBytesToXfer); and save // away the new remaining count: HA->ReqCurrentIndex += HA->ReqCurrentCount - Count.l; HA->ReqCurrentCount = Count.l; TRACE(4,("EmptyFIFO(): New remaining count: %ld(dec)\n", HA->ReqCurrentCount)); } TRACE(5,("EmptyFIFO(): Reseting xfer mode.\n")); HA->Ext->AD.IN2000U.CurrIntMask |= INFIFOMask; /* Block the FIFO ints */ outb(HA->Ext->AD.IN2000U.IOMap[INFIFOResetOff], 1); /* Reset the FIFO */ i = (ReadWDReg(HA, WDControlReg) & 0x1f); do { /* Clear WD Bus mode */ WriteWDReg(HA, WDControlReg, i); } while (ReadWDReg(HA, WDControlReg) != i); HA->Ext->AD.IN2000U.CurrDir = IN2000NoData; HA->Ext->AD.IN2000U.CRemain = 0; } void FIFO_ISR (ADAPTER_PTR HA) { unsigned S; #if defined(KEEP_STATS) HA->DataInterrupts++; #endif // Stay in here as long as there is no 33C93 interrupt (bit 0), and there is // at least 512 bytes in the FIFO (0xc0), and there is data remaining. // FIFOStat is called to repeatedly read the FIFO status port, since it // may be unstable for a single read. while (!((S = FIFOStat(HA->Ext->AD.IN2000U.IOMap[INFIFOOff])) & 1) && (S & 0xc0) && HA->Ext->AD.IN2000U.CRemain) { TRACE(5, ("IN2000_ISR(): FIFO status port read as %x\n", S)); // Value read from port (bits 1-7) is number of bytes / 16; Bit one is the // WD interrupt pending bit; so the count is effectively already multiplied // by two. Multiply it again by 8 to get the number of bytes in FIFO // S = (S & 0xc0) * 8; S = 512; if (HA->Ext->AD.IN2000U.CurrDir == IN2000DataIn) { // if ((U32)S > HA->Ext->AD.IN2000U.CRemain) { // // TRACE(0, ("FIFO_ISR(): FIFO says %ld, expected remaining is %ld\n", S, HA->Ext->AD.IN2000U.CRemain)); // S = (unsigned)HA->Ext->AD.IN2000U.CRemain; // // } S = (unsigned)min(S, HA->Ext->AD.IN2000U.CRemain); TRACE(4, ("FIFO_ISR(): reading %d bytes to %lx\n", S, HA->Ext->AD.IN2000U.CBuff)); #if !defined(ASMIO) repinsw(HA->Ext->AD.IN2000U.IOMap[INDataOff], HA->Ext->AD.IN2000U.CBuff, S/2); #else /* ASMIO */ #if sizeof(HA) == 4 /* far HA ptr */ asm { mov ax, di les bx, HA mov dx, es:[bx].IOBase les di, es:[bx].AD.IN2000U.CBuff } #else /* near HA ptr */ asm { mov ax, di mov bx, HA mov dx, [bx].IOBase les di, [bx].AD.IN2000U.CBuff } #endif asm { add dx, INDataOff mov cx, S shr cx, 1 cld rep insw mov di, ax } #endif /* ASMIO */ } else { // Leave 16 bytes (FIFOFillOffset) in FIFO for write flush (see below): S = (unsigned)min(min(S - FIFOFillOffset, FIFOSize/2), HA->Ext->AD.IN2000U.CRemain); TRACE(5, ("FIFO_ISR(): Writing next %d chunk from %lx\n", S, HA->Ext->AD.IN2000U.CBuff)); repoutsw(HA->Ext->AD.IN2000U.IOMap[INDataOff], (U16 FAR *)HA->Ext->AD.IN2000U.CBuff, S/2); } HA->Ext->AD.IN2000U.CBuff += S; HA->Ext->AD.IN2000U.CRemain -= S; TRACE(5, ("FIFO_ISR(): New remaining is %ld (0x%lx)\n", HA->Ext->AD.IN2000U.CRemain, HA->Ext->AD.IN2000U.CRemain)); // if (HA->Ext->AD.IN2000U.CRemain) // for(S=16; S && ((inb(HA->Ext->AD.IN2000U.IOMap[INFIFOOff]) & 0xc1) == 0); S--) ; } /* The FIFO logic on the IN-2000 requires writing FIFOPad bytes of garbage into the FIFO to push the end of the valid data out. This flush occurs here: */ if (HA->Ext->AD.IN2000U.CRemain == 0) { /* Don't expect any more FIFO ints */ HA->Ext->AD.IN2000U.CurrIntMask |= INFIFOMask; /* Block the FIFO ints */ if (HA->Ext->AD.IN2000U.CurrDir == IN2000DataOut) { // Pad the FIFO for (S=(FIFOPad / 2); S; S--) outw(HA->Ext->AD.IN2000U.IOMap[INDataOff], (U16)S); } } } int IN2000_ISR (ADAPTER_PTR HA) { unsigned char Stat, Taken = 0; TRACE(5, ("IN2000_ISR(): \n")); outb(HA->Ext->AD.IN2000U.IOMap[INIntMaskOff], INFIFOMask | INSBICMask); HA->Ext->AD.IN2000U.LastPollHadIntPending = FALSE; while (((Stat = inb(HA->Ext->AD.IN2000U.IOMap[INFIFOOff])) & 1) || ((Stat & 0xc0) && (HA->Ext->AD.IN2000U.CRemain != 0))) { Taken = 1; TRACE(5, ("IN2000_ISR(): FIFOStatus is : %x\n", Stat)); if ( !(Stat & 1) ) FIFO_ISR(HA); while(inb(HA->Ext->AD.IN2000U.IOMap[INFIFOOff]) & 0x1) WD33c93_ISR(HA); } outb(HA->Ext->AD.IN2000U.IOMap[INIntMaskOff], HA->Ext->AD.IN2000U.CurrIntMask); return Taken; } void IN2000_Initiate (ADAPTER_PTR HA, IO_REQ_PTR Req, const int StartLevel) { #if defined(COMPOUND_CMD) unsigned char C; #endif TRACE(5, ("IN2000_Initiate(): initiating\n")); critical(HA); // Block interrupts for now HA->ReqCurrentCount = 0; // Next DXFER phase will cause a GetXferSegment() HA->ReqCurrentIndex = 0; HA->Ext->SBIC.WD33C93.State = WD_NO_STATE; // Currently not in any state WriteWDReg(HA, WDDestIDReg, ReqTargetID(Req)); // Set the ID of the target HA->State.Busy = 1; // Mark flag for adapter in use if (HA->DevInfo[ReqTargetID(Req)].Flags.UseSync) { // Do we use sync. xfers on this device? WriteWDReg(HA, WDSyncReg, HA->DevInfo[ReqTargetID(Req)].HASync1); // Then write the Sync. values } else { WriteWDReg(HA, WDSyncReg, HA->Ext->SBIC.WD33C93.AsyncValue); // Alright then, async. values } // enable reselection WriteWDReg(HA, WDSourceReg, EnableRSel); SCSIMakeIdentify(HA, ReqTargetLUN(Req), (BOOLEAN)(ReqAllowDisconnect(Req) && HA->CurrDev->Flags.Allow_Disc)); // Then build Identify with disconnect #if defined(COMPOUND_CMD) // WD Compound commands only know group 0, 1, & 5 CDBs: C = ReqCDB(Req)[0] & 0xe0; if ((HA->Ext->MO_Count > 1) || !(C <= 0x10 || C == 0x50)) { WriteWDReg(HA, WDCMDReg, WDSelATNCmd); // Select with attention TRACE(3, ("IN2000_Initiate(): Using discreet commands\n")); } else { TRACE(3, ("IN2000_Initiate(): Using compound commands\n")); WriteWDReg(HA, WDControlReg, EnableIDI); WriteWDReg(HA, WDTarLUNReg, ReqTargetLUN(Req) | ((BOOLEAN)(ReqAllowDisconnect(Req) && HA->CurrDev->Flags.Allow_Disc)) ? 0x40 : 0); // Set ID of target LUN if ((HA->ReqCurrentCount >= FIFOThresh) && (ReqDataIn(Req) || ReqDataOut(Req))) { TRACE(4, ("IN2000_Initiate(): Early prepare for data xfer; Preparing for %ld byte xfer\n", HA->ReqCurrentCount)); HA->State.DataXfer = 1; HA->Ext->SBIC.WD33C93.State |= WD_BLOCK_XFER; HA->Ext->AD.IN2000U.CurrIntMask = 0; SetUpXfer(HA, HA->CurrReq, ReqDataIn(Req)); outb(HA->Ext->SBIC.WD33C93.WDSelPort, WDCountReg); outb(HA->Ext->SBIC.WD33C93.WDDataPort, (((char FAR *)&ReqCurrCount(HA->CurrReq))[2])); outb(HA->Ext->SBIC.WD33C93.WDDataPort, (((char FAR *)&ReqCurrCount(HA->CurrReq))[1])); outb(HA->Ext->SBIC.WD33C93.WDDataPort, (((char FAR *)&ReqCurrCount(HA->CurrReq))[0])); } else { HA->Ext->AD.IN2000U.CurrIntMask = INFIFOMask; outb(HA->Ext->SBIC.WD33C93.WDSelPort, WDCountReg); outb(HA->Ext->SBIC.WD33C93.WDDataPort, 0); outb(HA->Ext->SBIC.WD33C93.WDDataPort, 0); outb(HA->Ext->SBIC.WD33C93.WDDataPort, 0); } if (StartLevel <= 1) outb(HA->Ext->AD.IN2000U.IOMap[INIntMaskOff], HA->Ext->AD.IN2000U.CurrIntMask); SetWDReg(HA, WDCDBReg); // Send the CDB repoutsb(WDData, ReqCDB(Req), ReqCDBLen(Req)); HA->Ext->SBIC.WD33C93.State |= WD_COMPOUND_CMD; // Flag the use of LEVEL II commands WriteWDReg(HA, WDCMDReg, WDSelATNXCmd); // Start select & xfer w/ attention } #else WriteWDReg(HA, WDCMDReg, WDSelATNCmd); // Select with attention #endif HA->ReqStarting = StartLevel; inb(HA->Ext->AD.IN2000U.IOMap[INLEDOnOff]); // Turn on LED uncritical(HA); // OK, allow ints again TRACE(5, ("IN2000_Initiate(): initiating complete\n")); } U32 IN2000_Service (int Func, ADAPTER_PTR HA, U32 Misc) { int j; switch (Func) { case HA_INITIALIZE: return IN2000Init(HA); break; case HA_START: TRACE(2, ("IN2000_Service(): Got HA_START command\n")); HA->State.Allow = 1; StartNext(HA, 1); break; case HA_STOP: TRACE(2, ("IN2000_Service(): Got HA_STOP command\n")); HA->State.Allow = 0; break; case HA_TICKLE: if (!(HA->State.Busy) && (HA->State.Allow)) { TRACE(5, ("IN2000_Service(): Tickling adapter\n")); StartNext(HA,1); } else { TRACE(5, ("IN2000_Service(): Tickle ignored; Busy = %d, Allow = %d\n", HA->State.Busy, HA->State.Allow)); } break; case HA_TIMER: j = inb(HA->Ext->AD.IN2000U.IOMap[INFIFOOff]) & StatMask; if (HA->Ext->AD.IN2000U.LastPollHadIntPending && j ) { if (IN2000_ISR(HA)) { LogMessage(HA, HA->CurrReq, 0, 0, MSG_NO_INTERRUPTS, __LINE__); TRACE(0, ("IN2000_Service(): Serviced interrupt on timer\n")); } } else HA->Ext->AD.IN2000U.LastPollHadIntPending = j; break; case HA_LED: if ((int)Misc) inb(HA->Ext->AD.IN2000U.IOMap[INLEDOnOff]); else inb(HA->Ext->AD.IN2000U.IOMap[INLEDOffOff]); break; case HA_INITIATE: IN2000_Initiate(HA, HA->CurrReq, (unsigned)Misc); break; case HA_DATA_SETUP: SetUpXfer(HA, HA->CurrReq, (unsigned)Misc); if (HA->ReqCurrentCount < FIFOThresh) return HAServiceResponse_UseByteIO; else return HA->Ext->AD.IN2000U.CRemain; // break; case HA_DATA_CMPLT: if (HA->Ext->AD.IN2000U.CurrDir != IN2000NoData) EmptyFIFO(HA); break; case HA_RESET_BUS: IN2000ResetBus(HA); break; case HA_REVERT_STATE: // Restore the board back to its preveous state. This is used by // Netware / Chicago to switch back to BIOS mode. WriteWDReg(HA, WDOwnIDReg, ((StateBuffer *)(HA->Ext->InitialState))->OwnID); critical(HA); // Reset chip, then wait for reset complete interrupt. This causes the chip // to accept the set ID. WriteWDReg(HA, WDCMDReg, WDResetCmd); while ((ReadWDReg(HA, WDAuxStatReg) & IntPending) == 0) ; ReadWDReg(HA, WDStatusReg); // Clear the interrupt uncritical(HA); WriteWDReg(HA, WDControlReg, ((StateBuffer *)(HA->Ext->InitialState))->CtrlReg); WriteWDReg(HA, WDTimeoutReg, ((StateBuffer *)(HA->Ext->InitialState))->TimeOutReg); WriteWDReg(HA, WDSourceReg, ((StateBuffer *)(HA->Ext->InitialState))->SourceReg); break; case HA_RESTORE_STATE: IN2000ReInit(HA); IN2000ResetBus(HA); break; case HA_POWER_MODE: break; } return 0; }