From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/ndis/elnkii/card.c | 2780 ++++++++++++++++++++++++ private/ntos/ndis/elnkii/elnkhrd.h | 780 +++++++ private/ntos/ndis/elnkii/elnkii.c | 4010 +++++++++++++++++++++++++++++++++++ private/ntos/ndis/elnkii/elnkii.rc | 39 + private/ntos/ndis/elnkii/elnksft.h | 1295 +++++++++++ private/ntos/ndis/elnkii/interrup.c | 2426 +++++++++++++++++++++ private/ntos/ndis/elnkii/keywords.h | 56 + private/ntos/ndis/elnkii/makefile | 6 + private/ntos/ndis/elnkii/pend.c | 440 ++++ private/ntos/ndis/elnkii/sources | 45 + 10 files changed, 11877 insertions(+) create mode 100644 private/ntos/ndis/elnkii/card.c create mode 100644 private/ntos/ndis/elnkii/elnkhrd.h create mode 100644 private/ntos/ndis/elnkii/elnkii.c create mode 100644 private/ntos/ndis/elnkii/elnkii.rc create mode 100644 private/ntos/ndis/elnkii/elnksft.h create mode 100644 private/ntos/ndis/elnkii/interrup.c create mode 100644 private/ntos/ndis/elnkii/keywords.h create mode 100644 private/ntos/ndis/elnkii/makefile create mode 100644 private/ntos/ndis/elnkii/pend.c create mode 100644 private/ntos/ndis/elnkii/sources (limited to 'private/ntos/ndis/elnkii') diff --git a/private/ntos/ndis/elnkii/card.c b/private/ntos/ndis/elnkii/card.c new file mode 100644 index 000000000..093e8fb95 --- /dev/null +++ b/private/ntos/ndis/elnkii/card.c @@ -0,0 +1,2780 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + card.c + +Abstract: + + Card-specific functions for the NDIS 3.0 Etherlink II driver. + +Author: + + Adam Barr (adamba) 30-Jul-1990 + +Environment: + + Kernel mode, FSD + +Revision History: + + Adam Barr (adamba) 28-Aug-1990 + - moved the SyncXXX() functions to sync.c + + +--*/ + +#include +#include +#include "elnkhrd.h" +#include "elnksft.h" + + +// +// The amount of data to transfer in one programmed I/O burst +// (should be 8 or 16). +// + +#define DMA_BURST_SIZE 16 + +#if DBG +UCHAR PrevBurstSize = 0; +UCHAR PrevPrevBurstSize = 0; +#endif + + +// +// Array to hold multicast address list. +// + +CHAR Addresses[DEFAULT_MULTICASTLISTMAX][ETH_LENGTH_OF_ADDRESS] = {0}; + + + +#pragma NDIS_INIT_FUNCTION(CardGetMemBaseAddr) + +PUCHAR +CardGetMemBaseAddr( + IN PELNKII_ADAPTER AdaptP, + OUT PBOOLEAN CardPresent, + OUT PBOOLEAN IoBaseCorrect + ) + +/*++ + +Routine Description: + + Checks that the I/O base address is correct and returns + the memory base address. For cards that are not set up + for memory mapped mode, it will only check the I/O base + address, and return NULL if it is not correct. + +Arguments: + + AdaptP - pointer to the adapter block. + + CardPresent - Returns FALSE if the card does not appear + to be present in the machine. + + IoBaseCorrect - Returns TRUE if the jumper matches the + configured I/O base address. + +Return Value: + + The memory base address for memory mapped systems. + +--*/ + +{ + static PVOID IoBases[] = { (PVOID)0x2e0, (PVOID)0x2a0, + (PVOID)0x280, (PVOID)0x250, + (PVOID)0x350, (PVOID)0x330, + (PVOID)0x310, (PVOID)0x300 }; + static PVOID MemBases[] = { (PVOID)0xc8000, (PVOID)0xcc000, + (PVOID)0xd8000, (PVOID)0xdc000 }; + UCHAR BaseConfig, Tmp, MemConfig; + + + // + // Read in the Base Configuration Register. + // + + NdisRawReadPortUchar(AdaptP->MappedGaBaseAddr+GA_IO_BASE, &Tmp); + + // + // Make sure that only one bit in Tmp is on. + // + + if ((Tmp != 0) && ((Tmp & (Tmp-1)) == 0)) { + + *CardPresent = TRUE; + + } else { + + *CardPresent = FALSE; + return NULL; + + } + + + // + // Make sure the correct bit is on for AdaptP->IoBaseAddr. + // + + BaseConfig = 0; + + while (!(Tmp & 1)) { + + Tmp >>= 1; + + ++BaseConfig; + + if (BaseConfig == 8) { + + return NULL; + + } + + } + + if (IoBases[BaseConfig] != AdaptP->IoBaseAddr) { + + // + // Probably the jumper is wrong. + // + + *IoBaseCorrect = FALSE; + return NULL; + + } else { + + *IoBaseCorrect = TRUE; + + } + + + // + // For non-memory-mapped cards, there is nothing else to check. + // + + if (!AdaptP->MemMapped) { + + return NULL; + + } + + + // + // Now read in the PROM configuration register. + // + + NdisRawReadPortUchar(AdaptP->MappedGaBaseAddr+GA_MEM_BASE, &Tmp); + + + // + // See which bit is on, minus 4. + // + + MemConfig = 0; + + while (!(Tmp & 0x10)) { + + Tmp >>= 1; + + ++MemConfig; + + if (MemConfig == 4) { + + return NULL; + + } + + } + + + // + // Based on the bit, look up MemBaseAddr in the table. + // + + AdaptP->MemMapped = TRUE; + + return MemBases[MemConfig]; +} + + +#pragma NDIS_INIT_FUNCTION(CardReadEthernetAddress) + +VOID +CardReadEthernetAddress( + IN PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + Reads in the Ethernet address from the Etherlink II PROM. + +Arguments: + + AdaptP - pointer to the adapter block. + +Return Value: + + The address is stored in AdaptP->PermanentAddress, and StationAddress if it + is currently zero. + +--*/ + +{ + UINT i; + + // + // Window the PROM into the NIC ports. + // + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONTROL, CTRL_PROM_SEL | CTRL_BNC); + + + // + // Read in the station address. + // + + for (i=0; iMappedIoBaseAddr+i, &AdaptP->PermanentAddress[i]); + + } + + IF_LOUD( DbgPrint(" [ %x-%x-%x-%x-%x-%x ]\n", + AdaptP->PermanentAddress[0], + AdaptP->PermanentAddress[1], + AdaptP->PermanentAddress[2], + AdaptP->PermanentAddress[3], + AdaptP->PermanentAddress[4], + AdaptP->PermanentAddress[5]);) + + // + // Window the NIC registers into the NIC ports. + // + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONTROL, CTRL_GA_SEL | CTRL_BNC); + + if ((AdaptP->StationAddress[0] == 0x00) && + (AdaptP->StationAddress[1] == 0x00) && + (AdaptP->StationAddress[2] == 0x00) && + (AdaptP->StationAddress[3] == 0x00) && + (AdaptP->StationAddress[4] == 0x00) && + (AdaptP->StationAddress[5] == 0x00)) { + + AdaptP->StationAddress[0] = AdaptP->PermanentAddress[0]; + AdaptP->StationAddress[1] = AdaptP->PermanentAddress[1]; + AdaptP->StationAddress[2] = AdaptP->PermanentAddress[2]; + AdaptP->StationAddress[3] = AdaptP->PermanentAddress[3]; + AdaptP->StationAddress[4] = AdaptP->PermanentAddress[4]; + AdaptP->StationAddress[5] = AdaptP->PermanentAddress[5]; + + } + +} + + +BOOLEAN +CardSetup( + IN PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + Sets up the card, using the sequence given in the Etherlink II + technical reference. + +Arguments: + + AdaptP - pointer to the adapter block, which must be initialized. + +Return Value: + + TRUE if successful. + +--*/ + +{ + UINT i; + UINT Filter; + UCHAR IntConfig; + UCHAR Tmp; + + + // + // First set up the Gate Array. + // + + // + // Toggle the reset bit. + // + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONTROL, CTRL_RESET | CTRL_BNC); + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONTROL, 0x00); + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONTROL, CTRL_BNC); + + + // + // Set up the bits in the Control Register that don't change. + // + + AdaptP->GaControlBits = AdaptP->ExternalTransceiver ? CTRL_DIX : CTRL_BNC; + + if (DMA_BURST_SIZE == 16) { + + AdaptP->GaControlBits |= CTRL_DB_SEL; + + } + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONTROL, AdaptP->GaControlBits); + + + + + // + // Set Page Start and Page Stop to match the NIC registers. + // + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_PAGE_START, AdaptP->NicPageStart); + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_PAGE_STOP, AdaptP->NicPageStop); + + + + + + // + // Select which interrupt to use. + // + + IntConfig = 0x04; // set bit in position 2 + IntConfig <<= AdaptP->InterruptNumber; // move it to 4 through 7 + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_INT_DMA_CONFIG, IntConfig); + + + + + // + // Choose between 8- and 16-byte programmed I/O bursts. + // + + if (DMA_BURST_SIZE == 8) { + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_DRQ_TIMER, DQTR_8_BYTE); + + } else { + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_DRQ_TIMER, DQTR_16_BYTE); + + } + + + + + // + // Initialize these to a correct value for an 8K card. + // + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_DMA_ADDR_MSB, 0x20); + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_DMA_ADDR_LSB, 0x00); + + + + + // + // Set up the Configuration register. + // + + if (AdaptP->MemMapped) { + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONFIG, + GACFR_TC_MASK | GACFR_RAM_SEL | GACFR_MEM_BANK1); + + } else { + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONFIG, GACFR_TC_MASK); + + } + + + + // + // Now set up NIC registers. + // + + // + // Write to and read from CR to make sure it is there. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, CR_STOP | CR_NO_DMA | CR_PAGE0); + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, &Tmp); + + if (Tmp != (CR_STOP | CR_NO_DMA | CR_PAGE0)) { + + return FALSE; + + } + + + + + + // + // Set up the registers in the correct sequence. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_DATA_CONFIG, + DCR_BYTE_WIDE | DCR_NORMAL | DCR_FIFO_8_BYTE); + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_RMT_COUNT_MSB, 0); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_RMT_COUNT_LSB, 0); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_RCV_CONFIG, AdaptP->NicReceiveConfig); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_CONFIG, TCR_LOOPBACK); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_BOUNDARY, AdaptP->NicPageStart); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_PAGE_START, AdaptP->NicPageStart); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_PAGE_STOP, AdaptP->NicPageStop); + + AdaptP->Current = AdaptP->NicPageStart + (UCHAR)1; + AdaptP->NicNextPacket = AdaptP->NicPageStart + (UCHAR)1; + AdaptP->BufferOverflow = FALSE; + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, 0xff); // clear all + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_MASK, AdaptP->NicInterruptMask); + + + // + // Move to page 1 to write the station address and + // multicast registers. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_STOP | CR_NO_DMA | CR_PAGE1); + + for (i=0; iMappedIoBaseAddr+(NIC_PHYS_ADDR+i), + AdaptP->StationAddress[i]); + + } + + Filter = ETH_QUERY_FILTER_CLASSES(AdaptP->FilterDB); + + for (i=0; i<8; i++) { + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+(NIC_MC_ADDR+i), + (UCHAR)((Filter & NDIS_PACKET_TYPE_ALL_MULTICAST) + ? 0xff : AdaptP->NicMulticastRegs[i])); + + } + + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_CURRENT, AdaptP->Current); + + + // + // move back to page 0 and start the card... + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_STOP | CR_NO_DMA | CR_PAGE0); + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_NO_DMA | CR_PAGE0); + + + // + // ... but it is still in loopback mode. + // + + return TRUE; +} + +VOID +CardStop( + IN PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + Stops the card. + +Arguments: + + AdaptP - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + UINT i; + UCHAR Tmp; + + // + // Turn on the STOP bit in the Command register. + // + + NdisSynchronizeWithInterrupt( + &(AdaptP)->NdisInterrupt, + (PVOID)SyncCardStop, + (PVOID)AdaptP + ); + + + // + // Clear the Remote Byte Count register so that ISR_RESET + // will come on. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_RMT_COUNT_MSB, 0); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_RMT_COUNT_LSB, 0); + + + // + // Wait for ISR_RESET, but only for 1.6 milliseconds (as + // described in the March 1991 8390 addendum), since that + // is the maximum time for a software reset to occur. + // + // + + for (i=0; i<4; i++) { + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, &Tmp); + if (Tmp & ISR_RESET) { + + break; + + } + + + NdisStallExecution(500); + + } + + + + if (i == 4) { + + IF_LOUD( DbgPrint("RESET\n");) + IF_LOG( ElnkiiLog('R');) + + } + + + // + // Put the card in loopback mode, then start it. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_CONFIG, TCR_LOOPBACK); + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, CR_START | CR_NO_DMA); + + // + // At this point the card is still in loopback mode. + // + +} + +VOID DelayComplete( + IN PVOID SystemSpecific1, + IN PVOID TimerExpired, + IN PVOID SystemSpecific2, + IN PVOID SystemSpecific3 + ) +{ + UNREFERENCED_PARAMETER(SystemSpecific1); + UNREFERENCED_PARAMETER(SystemSpecific2); + UNREFERENCED_PARAMETER(SystemSpecific3); + + *((BOOLEAN *)TimerExpired)=TRUE; +} + + +#pragma NDIS_INIT_FUNCTION(CardTest) + +BOOLEAN +CardTest( + IN PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + Tests the card. Follows the tests described in section 12 of + the 8390 Data Sheet. + +Arguments: + + AdaptP - pointer to the adapter block, which must be initialized + and set up. + +Return Value: + + TRUE if everything is OK. + +--*/ + +{ +#define TEST_LEN 60 +#define MAGIC_NUM 0x92 + + UINT FirstTest, SecondTest, i; + UCHAR TSRResult, RSRResult; + UCHAR CrcBuf[4]; + BOOLEAN FinalResult = TRUE; + UCHAR TestSrcBuf[256], TestDestBuf[256]; + PUCHAR CurTestLoc; + UCHAR Tmp; + + static NDIS_TIMER Timer = {0}; + BOOLEAN TimerExpired=FALSE; + BOOLEAN dummy; //for return from NdisCancelTimer + + // + // These arrays are indexed by FirstTest. + // + + static UCHAR TCRValues[3] = { TCR_NIC_LBK, TCR_SNI_LBK, TCR_COAX_LBK }; + static UCHAR TSRCDHWanted[3] = { TSR_NO_CDH, TSR_NO_CDH, 0x00 }; + static UCHAR TSRCRSWanted[3] = { TSR_NO_CARRIER, 0x00, 0x00 }; + static UCHAR FIFOWanted[4] = { LSB(TEST_LEN+4), MSB(TEST_LEN+4), + MSB(TEST_LEN+4), MAGIC_NUM }; + + + // + // These arrays are indexed by SecondTest. + // + + static BOOLEAN GoodCrc[3] = { TRUE, FALSE, FALSE }; + static BOOLEAN GoodAddress[3] = { TRUE, TRUE, FALSE }; + static UCHAR RSRWanted[3] = { RSR_PACKET_OK, RSR_CRC_ERROR, RSR_PACKET_OK }; + + static UCHAR TestPacket[TEST_LEN] = {0}; // a dummy packet. + static UCHAR NullAddress[ETH_LENGTH_OF_ADDRESS] = { 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + + + // + // First construct TestPacket. + // + + ELNKII_MOVE_MEM(TestPacket, AdaptP->StationAddress, ETH_LENGTH_OF_ADDRESS); + ELNKII_MOVE_MEM(TestPacket+ETH_LENGTH_OF_ADDRESS, AdaptP->StationAddress, ETH_LENGTH_OF_ADDRESS); + TestPacket[2*ETH_LENGTH_OF_ADDRESS] = 0x00; + TestPacket[2*ETH_LENGTH_OF_ADDRESS+1] = 0x00; + TestPacket[TEST_LEN-1] = MAGIC_NUM; + + + // + // Set up the DCR for loopback operation. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_DATA_CONFIG, + DCR_BYTE_WIDE | DCR_LOOPBACK | DCR_FIFO_8_BYTE); + + + // + // Set the RCR to reject all packets. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_RCV_CONFIG, 0x00); + + + // + // First round of tests -- different loopback modes + // + + for (FirstTest = 0; FirstTest < 2; ++FirstTest) { + + // + // Stop the card. + // + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_STOP | CR_NO_DMA | CR_PAGE0); + + + // + // Set up the TCR for the appropriate loopback mode. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_CONFIG, 0x00); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_CONFIG, TCRValues[FirstTest]); + + + // + // Restart the card. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_NO_DMA | CR_PAGE0); + + + // + // Now copy down TestPacket and start the transmission. + // + + CardCopyDownBuffer(AdaptP, TestPacket, 0, 0, TEST_LEN); + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_START, AdaptP->NicXmitStart); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_COUNT_MSB, MSB(TEST_LEN)); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_COUNT_LSB, LSB(TEST_LEN)); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_XMIT | CR_NO_DMA); + + + // + // Wait for the transmission to complete, for about a second. + // + + { + UINT i; + i=0; + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, &Tmp); + while (!(Tmp & (ISR_XMIT | ISR_XMIT_ERR))) { + + if (++i > 100) { + IF_TEST( DbgPrint("F%d: TEST reset timed out\n", FirstTest);) + FinalResult = FALSE; + goto FinishTest; + } + + NdisStallExecution(11000); + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, &Tmp); + } + } + + // + // WAIT FOR CHIP TO STABILIZE + // Write to and read from CR to make sure it is there. + // Bug#4267 - WFW + // + + { + UINT i; + for (i = 0; i < 2000; ++i) { + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, CR_STOP | CR_NO_DMA | CR_PAGE0); + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, &Tmp); + if (Tmp != (CR_STOP | CR_NO_DMA | CR_PAGE0)) { + NdisStallExecution(1000); + } else { + break; + } + } + } + + + // + // Acknowledge the interrupt. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, + ISR_XMIT | ISR_XMIT_ERR); + + + // + // Check that the CRS and CDH bits are set correctly. + // + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_STATUS, &TSRResult); + + if ((TSRResult & TSR_NO_CARRIER) != TSRCRSWanted[FirstTest]) { + + IF_TEST(DbgPrint("F%d: Incorrect CRS value: %x\n", FirstTest, TSRResult);) + + FinalResult = FALSE; + + goto FinishTest; + + } + + if ((TSRResult & TSR_NO_CDH) != TSRCDHWanted[FirstTest]) { + + // + // the spec says CDH won't go on for TCR_COAX_LBK, but it does + // + + if (TCRValues[FirstTest] != TCR_COAX_LBK) { + + IF_TEST( DbgPrint("F%d: Incorrect CDH value: %x\n", FirstTest,TSRResult);) + + FinalResult = FALSE; + + goto FinishTest; + + } + + } + + + // + // For the Loopback to Coax test the RSR and FIFO + // can't be trusted, so skip them. + // + + if (TCRValues[FirstTest] == TCR_COAX_LBK) { + + continue; + + } + + + // + // Check that the CRC error happened (it should). + // + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_RCV_STATUS, &RSRResult); + + if (!(RSRResult & RSR_CRC_ERROR)) { + + IF_TEST( DbgPrint("F%d: No CRC error: %x\n", FirstTest, RSRResult);) + + FinalResult = FALSE; + + goto FinishTest; + + } + + + // + // Check that the right values are in the FIFO. + // + + for (i=0; i<4; i++) { + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_FIFO, &Tmp); + if (Tmp != FIFOWanted[i]) { + + IF_TEST( DbgPrint("F%d: Bad FIFO value: %d\n", FirstTest, i);) + + FinalResult = FALSE; + + goto FinishTest; + + } + + } + + + // + // Flush the rest of the FIFO. + // + + for (i=0; i<4; i++) { + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_FIFO, &Tmp); + + } + + } + + + // + // Stop the card. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_STOP | CR_NO_DMA | CR_PAGE0); + + + // + // Set the TCR for internal loopback. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_CONFIG, 0x00); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_CONFIG, + TCR_INHIBIT_CRC | TCR_NIC_LBK); + + // + // Restart the card. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_NO_DMA | CR_PAGE0); + + + + // + // Second round of tests -- CRC and Address recognition logic + // + + for (SecondTest = 0; SecondTest < 3; ++SecondTest) { + + // + // See if the destination address should be valid. + // + + if (GoodAddress[SecondTest]) { + + ELNKII_MOVE_MEM(TestPacket, AdaptP->StationAddress, ETH_LENGTH_OF_ADDRESS); + + } else { + + ELNKII_MOVE_MEM(TestPacket, NullAddress, ETH_LENGTH_OF_ADDRESS); + + } + + + // + // Copy down TestPacket. + // + + CardCopyDownBuffer(AdaptP, TestPacket, 0, 0, TEST_LEN); + + + // + // Copy down a good or bad CRC, as needed. + // + + CardGetPacketCrc(TestPacket, TEST_LEN, CrcBuf); + + if (!GoodCrc[SecondTest]) { + + CrcBuf[0] = (UCHAR)(CrcBuf[0] ^ 0xff); // intentionally make it bad + + } + + CardCopyDownBuffer(AdaptP, CrcBuf, 0, TEST_LEN, 4); + + + // + // Start the transmission. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_START, AdaptP->NicXmitStart); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_COUNT_MSB, MSB(TEST_LEN+4)); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_COUNT_LSB, LSB(TEST_LEN+4)); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_XMIT | CR_NO_DMA); + + + // + // Wait for the transmission to complete, for about a second. + // + + TimerExpired=FALSE; + + NdisInitializeTimer(&Timer, DelayComplete, &TimerExpired); + NdisSetTimer(&Timer, 1000); + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, &Tmp); + while (!(Tmp & (ISR_XMIT | ISR_XMIT_ERR))) { + + if (TimerExpired) { + + IF_TEST( DbgPrint("F%d: TEST reset timed out\n", FirstTest);) + + FinalResult = FALSE; + + goto FinishTest; + + } + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, &Tmp); + + } + + // + //MUST Cancel the unexpired timer + // + + NdisCancelTimer(&Timer,&dummy); + + // + // Acknowledge the interrupt. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, + ISR_XMIT | ISR_XMIT_ERR); + + + // + // Check that RSR is as expected. + // + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_RCV_STATUS, &RSRResult); + + if ((UCHAR)(RSRResult & (RSR_PACKET_OK | RSR_CRC_ERROR)) != + RSRWanted[SecondTest]) { + + IF_TEST( DbgPrint("S%d: Bad RSR: wanted %x got %x\n", SecondTest, + RSRWanted[SecondTest], RSRResult);) + + FinalResult = FALSE; + + goto FinishTest; + + } + + } + + + + // + // Third round of tests - copying data to and from the card. + // + + // + // First put data in the buffer. + // + + for (i=0; i<256; i++) { + + TestSrcBuf[i] = (UCHAR)(256-i); + + } + + // + // Loop through all the card memory in 256-byte pieces. + // + + for (CurTestLoc = 0; CurTestLoc < (PUCHAR)0x2000; CurTestLoc += 256) { + + // + // Copy the data down (have to play around with buffer + // numbers and offsets to put it in the right place). + // + + CardCopyDownBuffer(AdaptP, TestSrcBuf, + (XMIT_BUF)((ULONG)CurTestLoc / TX_BUF_SIZE), + (ULONG)CurTestLoc % TX_BUF_SIZE, 256); + + // + // Clear the destination buffer and read it back. + // + + for (i=0; i<256; i++) { + + TestDestBuf[i] = 77; + + } + + CardCopyUp(AdaptP, TestDestBuf, + AdaptP->XmitStart + (ULONG)CurTestLoc, 256); + + // + // Make sure that the data matches. + // + + for (i=0; i<256; i++) { + + if (TestSrcBuf[i] != TestDestBuf[i]) { + + IF_TEST( DbgPrint("T: Bad data at %lx\n", (ULONG)(CurTestLoc+i));) + + FinalResult = FALSE; + + goto FinishTest; + + } + + } + + } + + + + // + // FinishTest: jump here to exit the tests cleanly. + // + +FinishTest: + + // + // Stop the card. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_STOP | CR_NO_DMA | CR_PAGE0); + + + // + // Restore DCR, RCR, and TCR. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_DATA_CONFIG, + DCR_BYTE_WIDE | DCR_NORMAL | DCR_FIFO_8_BYTE); + + // + // (clear these two to be safe) + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_RMT_COUNT_MSB, 0); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_RMT_COUNT_LSB, 0); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_RCV_CONFIG, AdaptP->NicReceiveConfig); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_CONFIG, TCR_LOOPBACK); + + // + // The reconfiguring of the config registers can cause the xmit to complete + // if the test was a failure. Therefore, we pause here to allow the xmit + // to complete so that we can ACK it below - leaving the card in a valid state. + // + + NdisStallExecution(50000); + + // + // Acknowledge any interrupts that are floating around. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, 0xff); + + + // + // Start the card, but stay in loopback mode. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_NO_DMA | CR_PAGE0); + + + + return FinalResult; +} + + +BOOLEAN +CardReset( + IN PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + Resets the card. + +Arguments: + + AdaptP - pointer to the adapter block + +Return Value: + + TRUE if everything is OK. + +--*/ + +{ + CardStop(AdaptP); + + // + // CardSetup() does a software reset. + // + + if (!CardSetup(AdaptP)) { + + NdisWriteErrorLogEntry( + AdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_HARDWARE_FAILURE, + 2, + cardReset, + ELNKII_ERRMSG_CARD_SETUP + ); + + return FALSE; + + } + + CardStart(AdaptP); + + return TRUE; +} + + + +BOOLEAN +CardCopyDownPacket( + IN PELNKII_ADAPTER AdaptP, + IN PNDIS_PACKET Packet, + IN XMIT_BUF XmitBufferNum, + OUT UINT * Length + ) + +/*++ + +Routine Description: + + Copies the packet Packet down starting at the beginning of + transmit buffer XmitBufferNum, fills in Length to be the + length of the packet. It uses memory mapping or programmed + I/O as appropriate. + +Arguments: + + AdaptP - pointer to the adapter block + Packet - the packet to copy down + XmitBufferNum - the transmit buffer number + +Return Value: + + Length - the length of the data in the packet in bytes. + TRUE if the transfer completed with no problems. + +--*/ + +{ + PUCHAR CurAddress, BufAddress; + UINT CurLength, Len; + PNDIS_BUFFER CurBuffer; + UINT TmpLen, BurstSize; + UCHAR GaStatus; + + if (AdaptP->MemMapped) { + + // + // Memory mapped, just copy each buffer over. + // + + NdisQueryPacket(Packet, NULL, NULL, &CurBuffer, NULL); + + CurAddress = AdaptP->XmitStart + XmitBufferNum*TX_BUF_SIZE; + + CurLength = 0; + + while (CurBuffer) { + + NdisQueryBuffer(CurBuffer, (PVOID *)&BufAddress, &Len); + + ELNKII_MOVE_MEM_TO_SHARED_RAM(CurAddress, BufAddress, Len); + + CurAddress += Len; + + CurLength += Len; + + NdisGetNextBuffer(CurBuffer, &CurBuffer); + + } + + *Length = CurLength; + + } else { + + // + // Programmed I/O, have to transfer the data. + // + + NdisQueryPacket(Packet, NULL, NULL, &CurBuffer, NULL); + + CurAddress = (PUCHAR)0x2000 + XmitBufferNum*TX_BUF_SIZE; + + CurLength = 0; + + NdisAcquireSpinLock(&AdaptP->Lock); + + while (CurBuffer) { + + NdisQueryBuffer(CurBuffer, (PVOID *)&BufAddress, &Len); + + if (Len == 0) { + + NdisGetNextBuffer(CurBuffer, &CurBuffer); + + continue; + + } + + // + // Set up the Gate Array for programmed I/O transfer. + // + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_DMA_ADDR_MSB, + MSB((ULONG)CurAddress)); + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_DMA_ADDR_LSB, + LSB((ULONG)CurAddress)); + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONTROL, + (UCHAR)((CTRL_START | CTRL_DIR_DOWN) | AdaptP->GaControlBits)); + + + // + // First transfer multiples of DMA_BURST_SIZE. + // + + TmpLen = Len; + BurstSize = DMA_BURST_SIZE; + + while (TmpLen >= BurstSize) { + + if ((ULONG)BufAddress & 0x01) { + + NdisRawWritePortBufferUchar( + AdaptP->MappedGaBaseAddr+GA_REG_FILE_MSB, + (PUCHAR)BufAddress, + BurstSize + ); + + } else { + + NdisRawWritePortBufferUshort( + AdaptP->MappedGaBaseAddr+GA_REG_FILE_MSB, + (PUSHORT)BufAddress, + BurstSize/2 + ); + + } + + + TmpLen -= BurstSize; + + BufAddress += BurstSize; + + // + // Wait for the Gate Array FIFO to be ready. + // + + do { + + NdisRawReadPortUchar(AdaptP->MappedGaBaseAddr+GA_STATUS, &GaStatus); + + if (GaStatus & (STREG_UNDERFLOW | STREG_OVERFLOW)) { +#if DBG + DbgPrint("DATA PORT READY ERROR IN ELNKII - CCDP\n"); + DbgPrint("\tStatus = 0x%x, BurstSize = 0x%x, PrevBurstSize = 0x%x\n", + GaStatus, BurstSize, PrevBurstSize); +#endif + + NdisWriteErrorLogEntry( + AdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_DRIVER_FAILURE, + 2, + cardCopyDownPacket, + ELNKII_ERRMSG_DATA_PORT_READY + ); + + NdisReleaseSpinLock(&AdaptP->Lock); + + return FALSE; + + } + + } while (!(GaStatus & STREG_DP_READY)); + +#if DBG + PrevBurstSize = (UCHAR)BurstSize; +#endif + + } + + // + // Now copy the last bit as UCHARs. + // + + NdisRawWritePortBufferUchar( + AdaptP->MappedGaBaseAddr+GA_REG_FILE_MSB, + BufAddress, TmpLen); + + do { + + NdisRawReadPortUchar(AdaptP->MappedGaBaseAddr+GA_STATUS, &GaStatus); + + if (GaStatus & (STREG_UNDERFLOW | STREG_OVERFLOW)) { +#if DBG + DbgPrint("DATA PORT READY ERROR IN ELNKII - CCDPII\n"); + DbgPrint("\tStatus = 0x%x, BurstSize = 0x%x, PrevBurstSize = 0x%x\n", + GaStatus, BurstSize, PrevBurstSize); +#endif + + NdisWriteErrorLogEntry( + AdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_DRIVER_FAILURE, + 2, + cardCopyDownPacket, + ELNKII_ERRMSG_DATA_PORT_READY + ); + + NdisReleaseSpinLock(&AdaptP->Lock); + + return FALSE; + } + + } while (!(GaStatus & STREG_DP_READY)); + + +#if DBG + PrevBurstSize = (UCHAR)TmpLen; +#endif + + // + // Done, turn off the start bit... + // + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONTROL, + (UCHAR)(CTRL_STOP | AdaptP->GaControlBits)); + + // + // ... and wait for DMA_IN_PROGRESS to go off, + // indicating end of flush. + // + + do { + + NdisRawReadPortUchar(AdaptP->MappedGaBaseAddr+GA_STATUS, &GaStatus); + + } while (GaStatus & STREG_IN_PROG); + + + CurAddress += Len; + + CurLength += Len; + + NdisGetNextBuffer(CurBuffer, &CurBuffer); + + } + + NdisReleaseSpinLock(&AdaptP->Lock); + + *Length = CurLength; + + } + + return TRUE; +} + +BOOLEAN +CardCopyDownBuffer( + IN PELNKII_ADAPTER AdaptP, + IN PUCHAR SourceBuffer, + IN XMIT_BUF XmitBufferNum, + IN UINT Offset, + IN UINT Length + ) + +/*++ + +Routine Description: + + Copies down one character buffer (rather than an + entire packet), starting at offset Offset, for Length + bytes. It uses memory mapping or programmed I/O as + appropriate. This function is used for blanking the padding + at the end of short packets and also for loopback testing. + +Arguments: + + AdaptP - pointer to the adapter block + SourceBuffer - the source data to be copied down + XmitBufferNum - the transmit buffer number + Offset - the offset from the start of the transmit buffer + Length - the number of bytes to blank out + +Return Value: + + Length - the length of the data in the packet in bytes. + TRUE if the transfer completed with no problems. + +--*/ + +{ + PUCHAR CurAddress; + UINT TmpLen, ThisTime; + UCHAR GaStatus; + + if (AdaptP->MemMapped) { + + // + // Memory mapped, just copy over SourceBuffer. + // + + CurAddress = AdaptP->XmitStart + XmitBufferNum*TX_BUF_SIZE + Offset; + + ELNKII_MOVE_MEM_TO_SHARED_RAM(CurAddress, SourceBuffer, Length); + + } else { + + // + // Programmed I/O, have to transfer the data. + // + + CurAddress = (PUCHAR)0x2000 + XmitBufferNum*TX_BUF_SIZE + Offset; + + // + // Set up the Gate Array for programmed I/O transfer. + // + + NdisAcquireSpinLock(&AdaptP->Lock); + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_DMA_ADDR_MSB, + MSB((ULONG)CurAddress)); + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_DMA_ADDR_LSB, + LSB((ULONG)CurAddress)); + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONTROL, + (UCHAR)((CTRL_START | CTRL_DIR_DOWN) | AdaptP->GaControlBits)); + + + // + // Copy the data down in DMA_BURST_SIZE bursts. + // + + TmpLen = Length; + + while (TmpLen > 0) { + + ThisTime = (TmpLen >= DMA_BURST_SIZE) ? DMA_BURST_SIZE : TmpLen; + + NdisRawWritePortBufferUchar( + AdaptP->MappedGaBaseAddr+GA_REG_FILE_MSB, + SourceBuffer, ThisTime); + + + TmpLen -= ThisTime; + + SourceBuffer += ThisTime; + + // + // Wait for the Gate Array FIFO to be ready. + // + + do { + + NdisRawReadPortUchar(AdaptP->MappedGaBaseAddr+GA_STATUS, &GaStatus); + + if (GaStatus & (STREG_UNDERFLOW | STREG_OVERFLOW)) { +#if DBG + DbgPrint("DATA PORT READY ERROR IN ELNKII - CCDB\n"); + DbgPrint("\tStatus = 0x%x, BurstSize = 0x%x, PrevBurstSize = 0x%x\n", + GaStatus, ThisTime, PrevBurstSize); +#endif + + NdisWriteErrorLogEntry( + AdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_DRIVER_FAILURE, + 2, + cardCopyDownBuffer, + ELNKII_ERRMSG_DATA_PORT_READY + ); + + NdisReleaseSpinLock(&AdaptP->Lock); + + return FALSE; + + } + + } while (!(GaStatus & STREG_DP_READY)); + +#if DBG + PrevBurstSize = (UCHAR)ThisTime; +#endif + + } + + // + // Done, turn off the start bit.. + // + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONTROL, + (UCHAR)(CTRL_STOP | AdaptP->GaControlBits)); + + // + // ... and wait for DMA_IN_PROGRESS to go off, + // indicating end of flush. + // + + do { + + NdisRawReadPortUchar(AdaptP->MappedGaBaseAddr+GA_STATUS, &GaStatus); + + } while (GaStatus & STREG_IN_PROG); + + NdisReleaseSpinLock(&AdaptP->Lock); + + } + +#if DBG + IF_ELNKIIDEBUG( ELNKII_DEBUG_TRACK_PACKET_LENS ) { + + if (Offset == 18 && Length == 42) { + UINT i; + for (i=0; i<20; i++) { + SourceBuffer[i] = ' '; + } + } + + } +#endif + + return TRUE; +} + + + + +BOOLEAN +CardCopyUp( + IN PELNKII_ADAPTER AdaptP, + IN PUCHAR Target, + IN PUCHAR Source, + IN UINT Length + ) + +/*++ + +Routine Description: + + Copies data from the card to memory. It uses memory mapping + or programmed I/O as appropriate. + +Arguments: + + AdaptP - pointer to the adapter block + Target - the target address + Source - the source address (on the card) + Length - the number of bytes to copy + +Return Value: + + TRUE if the transfer completed with no problems. + +--*/ + +{ + UINT TmpLen, BurstSize; + UCHAR GaStatus; + + if (Length == 0) { + + return TRUE; + + } + + if (AdaptP->MemMapped) { + + // + // Memory mapped, just copy the data over. + // + + ELNKII_MOVE_SHARED_RAM_TO_MEM(Target, Source, Length); + + } else { // programmed I/O + + // + // Programmed I/O, have to transfer the data. + // + + // + // Adjust the address to be a card address. + // + + Source -= ((ULONG)AdaptP->XmitStart - 0x2000); + + // + // Set up the Gate Array for programmed I/O transfer. + // + + NdisAcquireSpinLock(&AdaptP->Lock); + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_DMA_ADDR_MSB, MSB((ULONG)Source)); + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_DMA_ADDR_LSB, LSB((ULONG)Source)); + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONTROL, + (UCHAR)((CTRL_START | CTRL_DIR_UP) | AdaptP->GaControlBits)); + + + // + // First copy multiples of DMA_BURST_SIZE as USHORTs. + // + + TmpLen = Length; + BurstSize = DMA_BURST_SIZE; + + // + // Before doing this, transfer one byte if needed to + // align on a USHORT boundary. + // + + while (TmpLen >= BurstSize) { + + // + // First wait for the Gate Array FIFO to be ready. + // + + do { + + NdisRawReadPortUchar(AdaptP->MappedGaBaseAddr+GA_STATUS, &GaStatus); + + if (GaStatus & (STREG_UNDERFLOW | STREG_OVERFLOW)) { + +#if DBG + DbgPrint("DATA PORT READY ERROR IN ELNKII - CCU\n"); + DbgPrint("\tStatus = 0x%x, BurstSize = 0x%x, PrevBurstSize = 0x%x\n", + GaStatus, PrevBurstSize, PrevPrevBurstSize); +#endif + + NdisWriteErrorLogEntry( + AdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_DRIVER_FAILURE, + 2, + cardCopyUp, + ELNKII_ERRMSG_DATA_PORT_READY + ); + + NdisReleaseSpinLock(&AdaptP->Lock); + + return FALSE; + + } + + } while (!(GaStatus & STREG_DP_READY)); + + + if ((ULONG)Target & 0x01) { + + // + // This is the first burst, and it starts on + // an odd boundary. + // + + NdisRawReadPortBufferUchar( + AdaptP->MappedGaBaseAddr+GA_REG_FILE_MSB, + (PUCHAR)Target, + BurstSize); + + TmpLen -= BurstSize; + Target += BurstSize; + + +#if DBG + PrevPrevBurstSize = PrevBurstSize; + PrevBurstSize = (UCHAR)BurstSize; +#endif + + } else { + + + NdisRawReadPortBufferUshort( + AdaptP->MappedGaBaseAddr+GA_REG_FILE_MSB, + (PUSHORT)Target, BurstSize/2); + + TmpLen -= BurstSize; + Target += BurstSize; + + +#if DBG + PrevPrevBurstSize = PrevBurstSize; + PrevBurstSize = (UCHAR)BurstSize; +#endif + + } + + } + + // + // Now copy the last bit of data as UCHARs. + // + + do { + + NdisRawReadPortUchar(AdaptP->MappedGaBaseAddr+GA_STATUS, &GaStatus); + + if (GaStatus & (STREG_UNDERFLOW | STREG_OVERFLOW)) { +#if DBG + DbgPrint("DATA PORT READY ERROR IN ELNKII - CCUII\n"); + DbgPrint("\tStatus = 0x%x, BurstSize = 0x%x, PrevBurstSize = 0x%x\n", + GaStatus, BurstSize, PrevBurstSize); +#endif + + NdisWriteErrorLogEntry( + AdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_DRIVER_FAILURE, + 2, + cardCopyUp, + ELNKII_ERRMSG_DATA_PORT_READY + ); + + NdisReleaseSpinLock(&AdaptP->Lock); + + return FALSE; + + } + + } while (!(GaStatus & STREG_DP_READY)); + + NdisRawReadPortBufferUchar( + AdaptP->MappedGaBaseAddr+GA_REG_FILE_MSB, + Target, TmpLen); + + + // + // Done, turn off the start bit. + // + + NdisRawWritePortUchar(AdaptP->MappedGaBaseAddr+GA_CONTROL, + (UCHAR)(CTRL_STOP | AdaptP->GaControlBits)); + + NdisReleaseSpinLock(&AdaptP->Lock); + } + + return TRUE; + +} + +ULONG +CardComputeCrc( + IN PUCHAR Buffer, + IN UINT Length + ) + +/*++ + +Routine Description: + + Runs the AUTODIN II CRC algorithm on buffer Buffer of + length Length. + +Arguments: + + Buffer - the input buffer + Length - the length of Buffer + +Return Value: + + The 32-bit CRC value. + +Note: + + This is adapted from the comments in the assembly language + version in _GENREQ.ASM of the DWB NE1000/2000 driver. + +--*/ + +{ + ULONG Crc, Carry; + UINT i, j; + UCHAR CurByte; + + Crc = 0xffffffff; + + for (i = 0; i < Length; i++) { + + CurByte = Buffer[i]; + + for (j = 0; j < 8; j++) { + + Carry = ((Crc & 0x80000000) ? 1 : 0) ^ (CurByte & 0x01); + + Crc <<= 1; + + CurByte >>= 1; + + if (Carry) { + + Crc = (Crc ^ 0x04c11db6) | Carry; + + } + + } + + } + + return Crc; + +} + +#pragma NDIS_INIT_FUNCTION(CardGetPacketCrc) + +VOID +CardGetPacketCrc( + IN PUCHAR Buffer, + IN UINT Length, + OUT UCHAR Crc[4] + ) + +/*++ + +Routine Description: + + For a given Buffer, computes the packet CRC for it. + It uses CardComputeCrc to determine the CRC value, then + inverts the order and value of all the bits (I don't + know why this is necessary). + +Arguments: + + Buffer - the input buffer + Length - the length of Buffer + +Return Value: + + The CRC will be stored in Crc. + +--*/ + +{ + static UCHAR InvertBits[16] = { 0x0, 0x8, 0x4, 0xc, + 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, + 0x3, 0xb, 0x7, 0xf }; + ULONG CrcValue; + UCHAR Tmp; + UINT i; + + // + // First compute the CRC. + // + + CrcValue = CardComputeCrc(Buffer, Length); + + + // + // Now invert the bits in the result. + // + + for (i=0; i<4; i++) { + + Tmp = ((PUCHAR)&CrcValue)[3-i]; + + Crc[i] = (UCHAR)((InvertBits[Tmp >> 4] + + (InvertBits[Tmp & 0xf] << 4)) ^ 0xff); + + } + +} + + +VOID +CardGetMulticastBit( + IN UCHAR Address[ETH_LENGTH_OF_ADDRESS], + OUT UCHAR * Byte, + OUT UCHAR * Value + ) + +/*++ + +Routine Description: + + For a given multicast address, returns the byte and bit in + the card multicast registers that it hashes to. Calls + CardComputeCrc() to determine the CRC value. + +Arguments: + + Address - the address + Byte - the byte that it hashes to + Value - will have a 1 in the relevant bit + +Return Value: + + None. + +--*/ + +{ + ULONG Crc; + UINT BitNumber; + + // + // First compute the CRC. + // + + Crc = CardComputeCrc(Address, ETH_LENGTH_OF_ADDRESS); + + + // + // The bit number is now in the 6 most significant bits of CRC. + // + + BitNumber = (UINT)((Crc >> 26) & 0x3f); + + *Byte = (UCHAR)(BitNumber / 8); + *Value = (UCHAR)((UCHAR)1 << (BitNumber % 8)); +} + +VOID +CardFillMulticastRegs( + IN PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + Erases and refills the card multicast registers. Used when + an address has been deleted and all bits must be recomputed. + +Arguments: + + AdaptP - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + UINT i; + UCHAR Byte, Bit; + NDIS_STATUS Status; + + + // + // First turn all bits off. + // + + for (i=0; i<8; i++) { + + AdaptP->NicMulticastRegs[i] = 0; + + } + + NdisAcquireSpinLock(&AdaptP->Lock); + + EthQueryGlobalFilterAddresses( + &Status, + AdaptP->FilterDB, + DEFAULT_MULTICASTLISTMAX * ETH_LENGTH_OF_ADDRESS, + &i, + Addresses + ); + + + ASSERT(Status == NDIS_STATUS_SUCCESS); + + // + // Now turn on the bit for each address in the multicast list. + // + + for ( ; i > 0; ) { + + i--; + + CardGetMulticastBit(Addresses[i], &Byte, &Bit); + + AdaptP->NicMulticastRegs[Byte] |= Bit; + + } + + NdisReleaseSpinLock(&AdaptP->Lock); + +} + + + + + + + + +BOOLEAN +SyncCardStop( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Sets the NIC_COMMAND register to stop the card. + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + TRUE if the power has failed. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, CR_STOP | CR_NO_DMA); + + return FALSE; +} + +VOID +CardStartXmit( + IN PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + Sets the NIC_COMMAND register to start a transmission. + The transmit buffer number is taken from AdaptP->CurBufXmitting + and the length from AdaptP->PacketLens[AdaptP->CurBufXmitting]. + +Arguments: + + AdaptP - pointer to the adapter block + +Return Value: + + TRUE if the power has failed. + +--*/ + +{ + XMIT_BUF XmitBufferNum = AdaptP->CurBufXmitting; + UINT Length = AdaptP->PacketLens[XmitBufferNum]; + + + // + // Prepare the NIC registers for transmission. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_START, + (UCHAR)(AdaptP->NicXmitStart + (UCHAR)(XmitBufferNum*BUFS_PER_TX))); + + // + // Pad the length to 60 (plus CRC will be 64) if needed. + // + + if (Length < 60) { + + Length = 60; + + } + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_COUNT_MSB, MSB(Length)); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_COUNT_LSB, LSB(Length)); + + // + // Start transmission, check for power failure first. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_XMIT | CR_NO_DMA); + + + IF_LOG( ElnkiiLog('x');) + +} + +BOOLEAN +SyncCardGetXmitStatus( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Gets the value of the "transmit status" NIC register and stores + it in AdaptP->XmitStatus. + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_STATUS, &AdaptP->XmitStatus); + + return FALSE; + +} + +BOOLEAN +SyncCardGetCurrent( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Gets the value of the CURRENT NIC register and stores + it in AdaptP->Current. + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + + // + // Have to go to page 1 to read this register. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_NO_DMA | CR_PAGE1); + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_CURRENT, &AdaptP->Current); + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_NO_DMA | CR_PAGE0); + + return FALSE; + +} + +VOID +CardSetBoundary( + IN PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + Sets the value of the "boundary" NIC register to one behind + AdaptP->NicNextPacket, to prevent packets from being received + on top of un-indicated ones. + +Arguments: + + AdaptP - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + // + // Have to be careful with "one behin NicNextPacket" when + // NicNextPacket is the first buffer in receive area. + // + + if (AdaptP->NicNextPacket == AdaptP->NicPageStart) { + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_BOUNDARY, + (UCHAR)(AdaptP->NicPageStop-(UCHAR)1)); + + } else { + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_BOUNDARY, + (UCHAR)(AdaptP->NicNextPacket-(UCHAR)1)); + + } + +} + +BOOLEAN +SyncCardSetReceiveConfig( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Sets the value of the "receive configuration" NIC register to + the value of AdaptP->NicReceiveConfig. + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_RCV_CONFIG, AdaptP->NicReceiveConfig); + + return FALSE; + +} + +BOOLEAN +SyncCardSetAllMulticast( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Turns on all the bits in the multicast register. Used when + the card must receive all multicast packets. + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + UINT i; + + // + // Have to move to page 1 to set these registers. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_NO_DMA | CR_PAGE1); + + for (i=0; i<8; i++) { + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+(NIC_MC_ADDR+i), 0xff); + + } + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_NO_DMA | CR_PAGE0); + + return FALSE; + +} + +BOOLEAN +SyncCardCopyMulticastRegs( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Sets the eight bytes in the card multicast registers. + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + UINT i; + + // + // Have to move to page 1 to set these registers. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_NO_DMA | CR_PAGE1); + + for (i=0; i<8; i++) { + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+(NIC_MC_ADDR+i), + AdaptP->NicMulticastRegs[i]); + + } + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_NO_DMA | CR_PAGE0); + + return FALSE; + +} + +BOOLEAN +SyncCardSetInterruptMask( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Sets the "interrupt mask" register of the NIC to the value of + AdaptP->NicInterruptMask. + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_MASK, AdaptP->NicInterruptMask); + + return FALSE; + +} + +BOOLEAN +SyncCardAcknowledgeReceive( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Sets the "packet received" bit in the NIC interrupt status register, + which re-enables interrupts of that type. + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, ISR_RCV); + + // + // Interrupts were previously blocked in the interrupt handler. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_MASK, AdaptP->NicInterruptMask); + + return FALSE; + +} + +BOOLEAN +SyncCardAcknowledgeOverflow( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Sets the "buffer overflow" bit in the NIC interrupt status register, + which re-enables interrupts of that type. + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + UCHAR InterruptStatus; + + CardGetInterruptStatus(AdaptP, &InterruptStatus); + + if (InterruptStatus & ISR_RCV_ERR) { + + SyncCardUpdateCounters(AdaptP); + + } + + NdisRawWritePortUchar( + AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, + ISR_OVERFLOW + ); + + return FALSE; + +} + +BOOLEAN +SyncCardAcknowledgeTransmit( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Sets the "packet transmitted" bit in the NIC interrupt status register, + which re-enables interrupts of that type. + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, ISR_XMIT | ISR_XMIT_ERR); + + // + // Interrupts were previously blocked in the interrupt handler. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_MASK, AdaptP->NicInterruptMask); + + return FALSE; + +} + +BOOLEAN +SyncCardAckAndGetCurrent( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Performs the functions of SyncCardAcknowledgeReceive followed by + SyncCardGetCurrent (since the two are always called + one after the other). + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + + // + // SyncCardAcknowledgeReceive. + // + +#ifdef i386 + + _asm { + cli + } + +#endif + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, ISR_RCV); + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_MASK, AdaptP->NicInterruptMask); + +#ifdef i386 + + _asm { + sti + } + +#endif + + // + // SyncCardGetCurrent. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_NO_DMA | CR_PAGE1); + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_CURRENT, &AdaptP->Current); + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, + CR_START | CR_NO_DMA | CR_PAGE0); + + return FALSE; + +} + +BOOLEAN +SyncCardGetXmitStatusAndAck( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Performs the functions of SyncCardGetXmitStatus followed by + SyncCardAcknowledgeTransmit (since the two are always + called one after the other). + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + + // + // SyncCardGetXmitStatus. + // + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_STATUS, &AdaptP->XmitStatus); + + // + // SyncCardAcknowledgeTransmit. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_STATUS, ISR_XMIT | ISR_XMIT_ERR); + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_INTR_MASK, AdaptP->NicInterruptMask); + + return FALSE; + +} + +BOOLEAN +SyncCardUpdateCounters( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Updates the values of the three counters (frame alignment errors, + CRC errors, and missed packets). + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + UCHAR Tmp; + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_FAE_ERR_CNTR, &Tmp); + AdaptP->FrameAlignmentErrors += Tmp; + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_CRC_ERR_CNTR, &Tmp); + AdaptP->CrcErrors += Tmp; + + NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+NIC_MISSED_CNTR, &Tmp); + AdaptP->MissedPackets += Tmp; + + return FALSE; + +} + +BOOLEAN +SyncCardHandleOverflow( + IN PVOID SynchronizeContext + ) + +/*++ + +Routine Description: + + Sets all the flags for dealing with a receive overflow, stops the card + and acknowledges all outstanding interrupts. + +Arguments: + + SynchronizeContext - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)SynchronizeContext); + + IF_LOG( ElnkiiLog('F');) + + + // + // This is a copy of CardStop(). This is changed in minor ways since + // we are already synchornized with interrupts. + // + + // + // Turn on the STOP bit in the Command register. + // + + SyncCardStop(AdaptP); + + // + // Wait for ISR_RESET, but only for 1.6 milliseconds (as + // described in the March 1991 8390 addendum), since that + // is the maximum time for a software reset to occur. + // + // + + NdisStallExecution(2000); + + + // + // Clear the Remote Byte Count register so that ISR_RESET + // will come on. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_RMT_COUNT_MSB, 0); + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_RMT_COUNT_LSB, 0); + + + // + // According to National Semiconductor, the next check is necessary + // See Step 5. of the overflow process + // + // NOTE: The setting of variables to check if the transmit has completed + // cannot be done here because anything in the ISR has already been ack'ed + // inside the main DPC. Thus, the setting of the variables, described in + // the Handbook was moved to the main DPC. + // + // Continued: If you did the check here, you will doubly transmit most + // packets that happened to be on the card when the overflow occurred. + // + + // + // Put the card in loopback mode, then start it. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_CONFIG, TCR_LOOPBACK); + + // + // Start the card. This does not Undo the loopback mode. + // + + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_COMMAND, CR_START | CR_NO_DMA); + + return FALSE; + +} diff --git a/private/ntos/ndis/elnkii/elnkhrd.h b/private/ntos/ndis/elnkii/elnkhrd.h new file mode 100644 index 000000000..c07fa02e6 --- /dev/null +++ b/private/ntos/ndis/elnkii/elnkhrd.h @@ -0,0 +1,780 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + elnkhrd.h + +Abstract: + + The main program for an Etherlink II MAC driver. + +Author: + + Anthony V. Ercolano (tonye) creation-date 19-Jun-1990 (Driver model) + + Orginal Elnkii code by AdamBa. + +Environment: + + This driver is expected to work in DOS, OS2 and NT at the equivalent + of kernal mode. + + Architecturally, there is an assumption in this driver that we are + on a little endian machine. + +Notes: + + optional-notes + +Revision History: + + Dec-1991 by Sean Selitrennikoff - Conversion of AdamBa's code to TonyE Model + + +--*/ + +#ifndef _ELNKIIHARDWARE_ +#define _ELNKIIHARDWARE_ + +// AdaptP->IoBaseAddr +// +// must match the setting of I/O Base Address jumper on +// the card. Choices are 0x300, 0x310, 0x330, 0x350, 0x250, +// 0x280, 0x2a0, and 0x2e0. + +#define DEFAULT_IOBASEADDR (PVOID)0x300 + + + +// AdaptP->ExternalTransceiver +// +// whether you are using thick Ethernet cable attached to the +// DIX port, or thin Ethernet attached to the BNC port. This +// will probably be TRUE. + +#define DEFAULT_EXTERNALTRANSCEIVER FALSE + + + +// AdaptP->InterruptNumber +// +// the interrupt number the board is using. Choices are 2, 3, +// 4, or 5; some of these are used by other NT drivers. + +#define DEFAULT_INTERRUPTNUMBER 3 + + + +// AdaptP->MemMapped +// +// whether to use memory mapping for data transfer, or programmed +// I/O. If it is TRUE, the Memory base address jumper must be +// moved from its default "Disable" setting. + +#define DEFAULT_MEMMAPPED FALSE + + + +// AdaptP->MaxOpens +// +// the maximum number of protocols that may be bound to this +// adapter at one time. + +#define DEFAULT_MAXOPENS 4 + + + +// AdaptP->MulticastListMax +// +// the maximum number of different multicast addresses that +// may be specified to this adapter (the list is global for +// all protocols). + +#define DEFAULT_MULTICASTLISTMAX 8 + + +// +// Offsets from AdaptP->MappedIoBaseAddr of the ports used to access +// the 8390 NIC registers. +// +// The names in parenthesis are the abbreviations by which +// the registers are referred to in the 8390 data sheet. +// +// Some of the offsets appear more than once +// because they have have relevant page 0 and page 1 values, +// or they are different registers when read than they are +// when written. The notation MSB indicates that only the +// MSB can be set for this register, the LSB is assumed 0. +// + +#define NIC_COMMAND 0x0 // (CR) +#define NIC_PAGE_START 0x1 // (PSTART) MSB, write-only +#define NIC_PHYS_ADDR 0x1 // (PAR0) page 1 +#define NIC_PAGE_STOP 0x2 // (PSTOP) MSB, write-only +#define NIC_BOUNDARY 0x3 // (BNRY) MSB +#define NIC_XMIT_START 0x4 // (TPSR) MSB, write-only +#define NIC_XMIT_STATUS 0x4 // (TSR) read-only +#define NIC_XMIT_COUNT_LSB 0x5 // (TBCR0) write-only +#define NIC_XMIT_COUNT_MSB 0x6 // (TBCR1) write-only +#define NIC_FIFO 0x6 // (FIFO) read-only +#define NIC_INTR_STATUS 0x7 // (ISR) +#define NIC_CURRENT 0x7 // (CURR) page 1 +#define NIC_MC_ADDR 0x8 // (MAR0) page 1 +#define NIC_RMT_COUNT_LSB 0xa // (RBCR0) write-only +#define NIC_RMT_COUNT_MSB 0xb // (RBCR1) write-only +#define NIC_RCV_CONFIG 0xc // (RCR) write-only +#define NIC_RCV_STATUS 0xc // (RSR) read-only +#define NIC_XMIT_CONFIG 0xd // (TCR) write-only +#define NIC_FAE_ERR_CNTR 0xd // (CNTR0) read-only +#define NIC_DATA_CONFIG 0xe // (DCR) write-only +#define NIC_CRC_ERR_CNTR 0xe // (CNTR1) read-only +#define NIC_INTR_MASK 0xf // (IMR) write-only +#define NIC_MISSED_CNTR 0xf // (CNTR2) read-only + + +// +// Constants for the NIC_COMMAND register. +// +// Start/stop the card, start transmissions, and select +// which page of registers was seen through the ports. +// + +#define CR_STOP (UCHAR)0x01 // reset the card +#define CR_START (UCHAR)0x02 // start the card +#define CR_XMIT (UCHAR)0x04 // begin transmission +#define CR_NO_DMA (UCHAR)0x20 // stop remote DMA + +#define CR_PS0 (UCHAR)0x40 // low bit of page number +#define CR_PS1 (UCHAR)0x80 // high bit of page number +#define CR_PAGE0 (UCHAR)0x00 // select page 0 +#define CR_PAGE1 CR_PS0 // select page 1 +#define CR_PAGE2 CR_PS1 // select page 2 + + +// +// Constants for the NIC_XMIT_STATUS register. +// +// Indicate the result of a packet transmission. +// + +#define TSR_XMIT_OK (UCHAR)0x01 // transmit with no errors +#define TSR_COLLISION (UCHAR)0x04 // collided at least once +#define TSR_ABORTED (UCHAR)0x08 // too many collisions +#define TSR_NO_CARRIER (UCHAR)0x10 // carrier lost +#define TSR_NO_CDH (UCHAR)0x40 // no collision detect heartbeat + + +// +// Constants for the NIC_INTR_STATUS register. +// +// Indicate the cause of an interrupt. +// + +#define ISR_EMPTY (UCHAR)0x00 // no bits set in the ISR +#define ISR_RCV (UCHAR)0x01 // packet received with no errors +#define ISR_XMIT (UCHAR)0x02 // packet transmitted with no errors +#define ISR_RCV_ERR (UCHAR)0x04 // error on packet reception +#define ISR_XMIT_ERR (UCHAR)0x08 // error on packet transmission +#define ISR_OVERFLOW (UCHAR)0x10 // receive buffer overflow +#define ISR_COUNTER (UCHAR)0x20 // MSB set on tally counter +#define ISR_RESET (UCHAR)0x80 // (not an interrupt) card is reset + + +// +// Constants for the NIC_RCV_CONFIG register. +// +// Configure what type of packets are received. +// + +#define RCR_REJECT_ERR (UCHAR)0x00 // reject error packets +#define RCR_BROADCAST (UCHAR)0x04 // receive broadcast packets +#define RCR_MULTICAST (UCHAR)0x08 // receive multicast packets +#define RCR_ALL_PHYS (UCHAR)0x10 // receive ALL directed packets + + +// +// Constants for the NIC_RCV_STATUS register. +// +// Indicate the status of a received packet. +// +// These are also used to interpret the status byte in the +// packet header of a received packet. +// + +#define RSR_PACKET_OK (UCHAR)0x01 // packet received with no errors +#define RSR_CRC_ERROR (UCHAR)0x02 // packet received with CRC error +#define RSR_MULTICAST (UCHAR)0x20 // packet received was multicast +#define RSR_DISABLED (UCHAR)0x40 // received is disabled +#define RSR_DEFERRING (UCHAR)0x80 // receiver is deferring + + +// +// Constants for the NIC_XMIT_CONFIG register. +// +// Configures how packets are transmitted. +// + +#define TCR_NO_LOOPBACK (UCHAR)0x00 // normal operation +#define TCR_LOOPBACK (UCHAR)0x02 // loopback (set when NIC is stopped) + +#define TCR_INHIBIT_CRC (UCHAR)0x01 // inhibit appending of CRC + +#define TCR_NIC_LBK (UCHAR)0x02 // loopback through the NIC +#define TCR_SNI_LBK (UCHAR)0x04 // loopback through the SNI +#define TCR_COAX_LBK (UCHAR)0x06 // loopback to the coax + + +// +// Constants for the NIC_DATA_CONFIG register. +// +// Set data transfer sizes. +// + +#define DCR_BYTE_WIDE (UCHAR)0x00 // byte-wide DMA transfers +#define DCR_WORD_WIDE (UCHAR)0x01 // word-wide DMA transfers + +#define DCR_LOOPBACK (UCHAR)0x00 // loopback mode (TCR must be set) +#define DCR_NORMAL (UCHAR)0x08 // normal operation + +#define DCR_FIFO_8_BYTE (UCHAR)0x40 // 8-byte FIFO threshhold + + +// +// Constants for the NIC_INTR_MASK register. +// +// Configure which ISR settings actually cause interrupts. +// + +#define IMR_RCV (UCHAR)0x01 // packet received with no errors +#define IMR_XMIT (UCHAR)0x02 // packet transmitted with no errors +#define IMR_RCV_ERR (UCHAR)0x04 // error on packet reception +#define IMR_XMIT_ERR (UCHAR)0x08 // error on packet transmission +#define IMR_OVERFLOW (UCHAR)0x10 // receive buffer overflow +#define IMR_COUNTER (UCHAR)0x20 // MSB set on tally counter + + + +// +// Offsets from AdaptP->MappedGabaseAddr (which is AdaptP->MappedIoBaseAddr+0x400) +// of the ports used to access the Elnkii Gate Array registers. +// +// The names in parenthesis are the abbreviations by which +// the registers are referred to in the Elnkii Technical +// Reference. +// + +#define GA_PAGE_START 0x0 // (PSTR) MSB +#define GA_PAGE_STOP 0x1 // (PSPR) MSB +#define GA_DRQ_TIMER 0x2 // (DQTR) +#define GA_IO_BASE 0x3 // (BCFR) read-only +#define GA_MEM_BASE 0x4 // (PCFR) read-only +#define GA_CONFIG 0x5 // (GACFR) +#define GA_CONTROL 0x6 // (CTRL) +#define GA_STATUS 0x7 // (STREG) read-only +#define GA_INT_DMA_CONFIG 0x8 // (IDCFR) +#define GA_DMA_ADDR_MSB 0x9 // (DAMSB) +#define GA_DMA_ADDR_LSB 0xa // (DALSB) +#define GA_REG_FILE_MSB 0xe // (RFMSB) +#define GA_REG_FILE_LSB 0xf // (RFLSB) + + +// +// Constants for the GA_DRQ_TIMER register. +// + +#define DQTR_16_BYTE (UCHAR)0x10 // 16-byte programmed I/O bursts +#define DQTR_8_BYTE (UCHAR)0x08 // 8-byte programmed I/O bursts + + +// +// Constants for the GA_CONFIG register. +// + +#define GACFR_TC_MASK (UCHAR)0x40 // block DMA complete interrupts +#define GACFR_RAM_SEL (UCHAR)0x08 // allow memory-mapped mode +#define GACFR_MEM_BANK1 (UCHAR)0x01 // select window for 8K buffer + + +// +// Constants for the GA_CONTROL register. +// + +#define CTRL_START (UCHAR)0x80 // start the DMA controller +#define CTRL_STOP (UCHAR)0x00 // stop the DMA controller + +#define CTRL_DIR_DOWN (UCHAR)0x40 // system->board transfers +#define CTRL_DIR_UP (UCHAR)0x00 // board->system transfers + +#define CTRL_DB_SEL (UCHAR)0x20 // connect FIFOs serially + +#define CTRL_PROM_SEL (UCHAR)0x04 // window PROM into GaAddr ports +#define CTRL_GA_SEL (UCHAR)0x00 // window GA into GaAddr ports + +#define CTRL_BNC (UCHAR)0x02 // internal transceiver +#define CTRL_DIX (UCHAR)0x00 // external transceiver + +#define CTRL_RESET (UCHAR)0x01 // emulate power up reset + + +// +// Constants for the GA_STATUS register. +// + +#define STREG_DP_READY (UCHAR)0x80 // ready for programmed I/O transfer +#define STREG_UNDERFLOW (UCHAR)0x40 // register file underflow +#define STREG_OVERFLOW (UCHAR)0x20 // register file overflow +#define STREG_IN_PROG (UCHAR)0x08 // programmed I/O in progress + + + +//++ +// +// VOID +// CardStart( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// +// Routine Description: +// +// Starts the card. +// +// Arguments: +// +// AdaptP - pointer to the adapter block +// +// Return Value: +// +// None. +// +//-- + // + // Assume that the card has been stopped as in CardStop. + // + +#define CardStart(AdaptP) \ + NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+NIC_XMIT_CONFIG, TCR_NO_LOOPBACK) + + + +//++ +// +// VOID +// CardSetAllMulticast( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Enables every bit in the card multicast bit mask. +// Calls SyncCardSetAllMulticast. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardSetAllMulticast(AdaptP) \ + NdisSynchronizeWithInterrupt(&(AdaptP)->NdisInterrupt, \ + SyncCardSetAllMulticast, (PVOID)(AdaptP)) + + +//++ +// +// VOID +// CardCopyMulticastRegs( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Writes out the entire multicast bit mask to the card from +// AdaptP->NicMulticastRegs. Calls SyncCardCopyMulticastRegs. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardCopyMulticastRegs(AdaptP) \ + NdisSynchronizeWithInterrupt(&(AdaptP)->NdisInterrupt, \ + SyncCardCopyMulticastRegs, (PVOID)(AdaptP)) + + + +//++ +// +// VOID +// CardGetInterruptStatus( +// IN PELNKII_ADAPTER AdaptP, +// OUT PUCHAR InterrupStatus +// ) +// +// Routine Description: +// +// Reads the interrupt status (ISR) register from the card. Only +// called at IRQL INTERRUPT_LEVEL. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// InterruptStatus - Returns the value of ISR. +// +// Return Value: +// +//-- + +#define CardGetInterruptStatus(AdaptP,InterruptStatus) \ + NdisRawReadPortUchar((AdaptP)->MappedIoBaseAddr+NIC_INTR_STATUS, (InterruptStatus)) + + +//++ +// +// VOID +// CardSetReceiveConfig( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Sets the receive configuration (RCR) register on the card. +// The value used is AdaptP->NicReceiveConfig. Calls +// SyncCardSetReceiveConfig. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardSetReceiveConfig(AdaptP) \ + NdisSynchronizeWithInterrupt(&(AdaptP)->NdisInterrupt, \ + SyncCardSetReceiveConfig, (PVOID)(AdaptP)) + + +//++ +// +// VOID +// CardBlockInterrupts( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Blocks all interrupts from the card by clearing the +// interrupt mask (IMR) register. Only called from +// IRQL INTERRUPT_LEVEL. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardBlockInterrupts(AdaptP) \ + NdisRawWritePortUchar((AdaptP)->MappedIoBaseAddr+NIC_INTR_MASK, 0) + + +//++ +// +// VOID +// CardUnblockInterrupts( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Unblocks all interrupts from the card by setting the +// interrupt mask (IMR) register. Only called from IRQL +// INTERRUPT_LEVEL. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardUnblockInterrupts(AdaptP) \ + NdisRawWritePortUchar( \ + (AdaptP)->MappedIoBaseAddr+NIC_INTR_MASK, \ + (AdaptP)->NicInterruptMask) + + +//++ +// +// VOID +// CardDisableReceiveInterrupt( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Turns off the receive bit in AdaptP->NicInterruptMask. +// This function is only called when CardBlockInterrupts have +// been called; it ensures that receive interrupts are not +// reenabled until CardEnableReceiveInterrupt is called, even +// if CardUnblockInterrupts is called. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardDisableReceiveInterrupt(AdaptP) \ + (AdaptP)->NicInterruptMask &= (UCHAR)~IMR_RCV + + +//++ +// +// VOID +// CardEnableReceiveInterrupt( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Reenables receive interrupts by setting the receive bit ibn +// AdaptP->NicInterruptMask, and also writes the new value to +// the card. Calls SyncCardSetInterruptMask. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardEnableReceiveInterrupt(AdaptP) \ + (AdaptP)->NicInterruptMask |= (UCHAR)IMR_RCV, \ + NdisSynchronizeWithInterrupt(&(AdaptP)->NdisInterrupt, \ + SyncCardSetInterruptMask, (PVOID)(AdaptP)) + +//++ +// +// VOID +// CardAcknowledgeReceiveInterrupt( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Acknowledges a receive interrupt by setting the bit in +// the interrupt status (ISR) register. Calls +// SyncCardAcknowledgeReceive. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardAcknowledgeReceiveInterrupt(AdaptP) \ + NdisSynchronizeWithInterrupt(&(AdaptP)->NdisInterrupt, \ + SyncCardAcknowledgeReceive, (PVOID)(AdaptP)) + + +//++ +// +// VOID +// CardAcknowledgeOverflowInterrupt( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Acknowledges an overflow interrupt by setting the bit in +// the interrupt status (ISR) register. Calls +// SyncCardAcknowledgeOverflow. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardAcknowledgeOverflowInterrupt(AdaptP) \ + NdisSynchronizeWithInterrupt(&(AdaptP)->NdisInterrupt, \ + SyncCardAcknowledgeOverflow, (PVOID)(AdaptP)) + + +//++ +// +// VOID +// CardAcknowledgeTransmitInterrupt( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Acknowledges a transmit interrupt by setting the bit in +// the interrupt status (ISR) register. Calls +// SyncCardAcknowledgeTransmit. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardAcknowledgeTransmitInterrupt(AdaptP) \ + NdisSynchronizeWithInterrupt(&(AdaptP)->NdisInterrupt, \ + SyncCardAcknowledgeTransmit, (PVOID)(AdaptP)) + + +//++ +// +// VOID +// CardAcknowledgeCounterInterrupt( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Acknowledges a counter interrupt by setting the bit in +// the interrupt status (ISR) register. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardAcknowledgeCounterInterrupt(AdaptP) \ + NdisRawWritePortUchar((AdaptP)->MappedIoBaseAddr+NIC_INTR_STATUS, ISR_COUNTER) + + +//++ +// +// VOID +// CardAckAndGetCurrent( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Performs the function of CardAcknowledgeReceive followed by +// CardGetCurrent (since the two are always called +// one after the other). Calls SyncCardAckAndGetCurrent. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardAckAndGetCurrent(AdaptP) \ + NdisSynchronizeWithInterrupt(&(AdaptP)->NdisInterrupt, \ + SyncCardAckAndGetCurrent, (PVOID)(AdaptP)) + + +//++ +// +// VOID +// CardGetXmitStatusAndAck( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Performs the function of CardGetXmitStatus followed by +// CardAcknowledgeTransmit (since the two are always called +// one after the other). Calls SyncCardGetXmitStatusAndAck. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardGetXmitStatusAndAck(AdaptP) \ + NdisSynchronizeWithInterrupt(&(AdaptP)->NdisInterrupt, \ + SyncCardGetXmitStatusAndAck, (PVOID)(AdaptP)) + + +//++ +// +// VOID +// CardUpdateCounters( +// IN PELNKII_ADAPTER AdaptP +// ) +// +// Routine Description: +// +// Updates the values of the three counters (frame alignment +// errors, CRC errors, and missed packets) by reading in their +// current values from the card and adding them to the ones +// stored in the AdaptP structure. Calls SyncCardUpdateCounters. +// +// Arguments: +// +// AdaptP - The adapter block. +// +// Return Value: +// +// None. +// +//-- + +#define CardUpdateCounters(AdaptP) \ + NdisSynchronizeWithInterrupt(&(AdaptP)->NdisInterrupt, \ + SyncCardUpdateCounters, (PVOID)(AdaptP)) + + +#endif // _ELNKIIHARDWARE_ diff --git a/private/ntos/ndis/elnkii/elnkii.c b/private/ntos/ndis/elnkii/elnkii.c new file mode 100644 index 000000000..7ba830bc1 --- /dev/null +++ b/private/ntos/ndis/elnkii/elnkii.c @@ -0,0 +1,4010 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + elnkii.c + +Abstract: + + This is the main file for the Etherlink II + Ethernet controller. This driver conforms to the NDIS 3.1 interface. + + The idea for handling loopback and sends simultaneously is largely + adapted from the EtherLink II NDIS driver by Adam Barr. + +Author: + + Anthony V. Ercolano (Tonye) 20-Jul-1990 + +Environment: + + Kernel Mode - Or whatever is the equivalent on OS/2 and DOS. + +Revision History: + + Dec 1991 by Sean Selitrennikoff - Modified Elnkii code by AdamBa to + fit into the model by TonyE. + +--*/ + +#include +#include +#include "elnkhrd.h" +#include "elnksft.h" +#include "keywords.h" + +#if DBG +#define STATIC +#else +#define STATIC static +#endif + + +#if DBG + +extern ULONG ElnkiiSendsCompletedForReset; +ULONG ElnkiiDebugFlag=ELNKII_DEBUG_LOG; +extern ULONG ElnkiiSendsCompletedForReset; + +#endif + +// +// This constant is used for places where NdisAllocateMemory +// needs to be called and the HighestAcceptableAddress does +// not matter. +// + +static const NDIS_PHYSICAL_ADDRESS HighestAcceptableMax = + NDIS_PHYSICAL_ADDRESS_CONST(-1,-1); + + +// +// The global MAC block. +// + +MAC_BLOCK ElnkiiMacBlock={0}; + + + +// +// If you add to this, make sure to add the +// a case in ElnkiiFillInGlobalData() and in +// ElnkiiQueryGlobalStatistics() if global +// information only or +// ElnkiiQueryProtocolStatistics() if it is +// protocol queriable information. +// +STATIC UINT ElnkiiGlobalSupportedOids[] = { + OID_GEN_SUPPORTED_LIST, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_LOOKAHEAD, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_MAC_OPTIONS, + OID_GEN_PROTOCOL_OPTIONS, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_DRIVER_VERSION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_CURRENT_LOOKAHEAD, + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE, + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS + }; + +// +// If you add to this, make sure to add the +// a case in ElnkiiQueryGlobalStatistics() and in +// ElnkiiQueryProtocolInformation() +// +STATIC UINT ElnkiiProtocolSupportedOids[] = { + OID_GEN_SUPPORTED_LIST, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_LOOKAHEAD, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_MAC_OPTIONS, + OID_GEN_PROTOCOL_OPTIONS, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_DRIVER_VERSION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_CURRENT_LOOKAHEAD, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE + }; + + + + + + + + +// +// Determines whether failing the initial card test will prevent +// the adapter from being registered. +// + +#ifdef CARD_TEST + +BOOLEAN InitialCardTest = TRUE; + +#else // CARD_TEST + +BOOLEAN InitialCardTest = FALSE; + +#endif // CARD_TEST + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + + +#pragma NDIS_INIT_FUNCTION(DriverEntry) + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + + +/*++ + +Routine Description: + + This is the transfer address of the driver. It initializes + ElnkiiMacBlock and calls NdisInitializeWrapper() and + NdisRegisterMac(). + +Arguments: + +Return Value: + + Indicates the success or failure of the initialization. + +--*/ + +{ + NDIS_HANDLE NdisWrapperHandle; + PMAC_BLOCK NewMacP = &ElnkiiMacBlock; + NDIS_STATUS Status; + NDIS_STRING MacName = NDIS_STRING_CONST("Elnkii"); + + // + // Ensure that the MAC_RESERVED structure will fit in the + // MacReserved section of a packet. + // + + ASSERT(sizeof(MAC_RESERVED) <= sizeof(((PNDIS_PACKET)NULL)->MacReserved)); + + + // + // Pass the wrapper a pointer to the device object. + // + + NdisInitializeWrapper(&NdisWrapperHandle, + DriverObject, + RegistryPath, + NULL + ); + + // + // Set up the driver object. + // + + NewMacP->DriverObject = DriverObject; + + NdisAllocateSpinLock(&NewMacP->SpinLock); + + NewMacP->NdisWrapperHandle = NdisWrapperHandle; + NewMacP->Unloading = FALSE; + NewMacP->AdapterQueue = (PELNKII_ADAPTER)NULL; + + + // + // Prepare to call NdisRegisterMac. + // + + NewMacP->MacCharacteristics.MajorNdisVersion = ELNKII_NDIS_MAJOR_VERSION; + NewMacP->MacCharacteristics.MinorNdisVersion = ELNKII_NDIS_MINOR_VERSION; + NewMacP->MacCharacteristics.OpenAdapterHandler = ElnkiiOpenAdapter; + NewMacP->MacCharacteristics.CloseAdapterHandler = ElnkiiCloseAdapter; + NewMacP->MacCharacteristics.SendHandler = ElnkiiSend; + NewMacP->MacCharacteristics.TransferDataHandler = ElnkiiTransferData; + NewMacP->MacCharacteristics.ResetHandler = ElnkiiReset; + NewMacP->MacCharacteristics.RequestHandler = ElnkiiRequest; + NewMacP->MacCharacteristics.QueryGlobalStatisticsHandler = + ElnkiiQueryGlobalStatistics; + NewMacP->MacCharacteristics.UnloadMacHandler = ElnkiiUnload; + NewMacP->MacCharacteristics.AddAdapterHandler = ElnkiiAddAdapter; + NewMacP->MacCharacteristics.RemoveAdapterHandler = ElnkiiRemoveAdapter; + + NewMacP->MacCharacteristics.Name = MacName; + + NdisRegisterMac(&Status, + &NewMacP->NdisMacHandle, + NdisWrapperHandle, + (NDIS_HANDLE)&ElnkiiMacBlock, + &NewMacP->MacCharacteristics, + sizeof(NewMacP->MacCharacteristics)); + + + if (Status != NDIS_STATUS_SUCCESS) { + + // + // NdisRegisterMac failed. + // + + NdisFreeSpinLock(&NewMacP->SpinLock); + NdisTerminateWrapper(NdisWrapperHandle, + (PVOID) NULL + ); + IF_LOUD( DbgPrint( "NdisRegisterMac failed with code 0x%x\n", Status );) + return Status; + } + + + IF_LOUD( DbgPrint( "NdisRegisterMac succeeded\n" );) + + IF_LOUD( DbgPrint("Adapter Initialization Complete\n");) + + + return NDIS_STATUS_SUCCESS; +} + +#pragma NDIS_INIT_FUNCTION(ElnkiiAddAdapter) + +NDIS_STATUS +ElnkiiAddAdapter( + IN NDIS_HANDLE MacMacContext, + IN NDIS_HANDLE ConfigurationHandle, + IN PNDIS_STRING AdapterName + ) +/*++ +Routine Description: + + This is the ElinkII MacAddAdapter routine. The system calls this routine + to add support for a particular ElinkII adapter. This routine extracts + configuration information from the configuration data base and registers + the adapter with NDIS. + +Arguments: + + See Ndis3.0 spec... + +Return Value: + + NDIS_STATUS_SUCCESS - Adapter was successfully added. + NDIS_STATUS_FAILURE - Adapter was not added, also MAC deregistered. + +--*/ +{ + PELNKII_ADAPTER NewAdaptP; + + NDIS_HANDLE ConfigHandle; + PNDIS_CONFIGURATION_PARAMETER ReturnedValue; + NDIS_STRING IOAddressStr = IOBASE; + NDIS_STRING InterruptStr = INTERRUPT; + NDIS_STRING MaxMulticastListStr = MAXMULTICAST; + NDIS_STRING NetworkAddressStr = NETWORK_ADDRESS; + NDIS_STRING MemoryMappedStr = MEMORYMAPPED; + NDIS_STRING TransceiverStr = TRANSCEIVER; + +#if NDIS2 + NDIS_STRING EXTERNALStr = NDIS_STRING_CONST("EXTERNAL"); +#endif + + BOOLEAN ConfigError = FALSE; + ULONG ConfigErrorValue = 0; + ULONG Length; + PVOID NetAddress; + + NDIS_STATUS Status; + + + // + // These are used when calling ElnkiiRegisterAdapter. + // + + PVOID IoBaseAddr; + CCHAR InterruptNumber; + BOOLEAN ExternalTransceiver; + BOOLEAN MemMapped; + UINT MaxMulticastList; + + // + // Set default values. + // + + IoBaseAddr = DEFAULT_IOBASEADDR; + InterruptNumber = DEFAULT_INTERRUPTNUMBER; + ExternalTransceiver = DEFAULT_EXTERNALTRANSCEIVER; + MemMapped = DEFAULT_MEMMAPPED; + MaxMulticastList = DEFAULT_MULTICASTLISTMAX; + + // + // Allocate memory for the adapter block now. + // + + Status = NdisAllocateMemory( (PVOID *)&NewAdaptP, sizeof(ELNKII_ADAPTER), 0, HighestAcceptableMax); + + if (Status != NDIS_STATUS_SUCCESS) { + + return(Status); + + } + + NdisZeroMemory (NewAdaptP, + sizeof(ELNKII_ADAPTER) + ); + + NdisOpenConfiguration( + &Status, + &ConfigHandle, + ConfigurationHandle + ); + + if (Status != NDIS_STATUS_SUCCESS) { + + NdisFreeMemory(NewAdaptP, sizeof(ELNKII_ADAPTER), 0); + + return NDIS_STATUS_FAILURE; + + } + + // + // Read I/O Address + // + + NdisReadConfiguration( + &Status, + &ReturnedValue, + ConfigHandle, + &IOAddressStr, + NdisParameterHexInteger + ); + + if (Status == NDIS_STATUS_SUCCESS) { + + IoBaseAddr = (PVOID)(ReturnedValue->ParameterData.IntegerData); + + } + + // + // Confirm value + // + + { + UCHAR Count; + + static PVOID IoBases[] = { (PVOID)0x2e0, (PVOID)0x2a0, + (PVOID)0x280, (PVOID)0x250, + (PVOID)0x350, (PVOID)0x330, + (PVOID)0x310, (PVOID)0x300 }; + + for (Count = 0 ; Count < 8; Count++) { + + if (IoBaseAddr == IoBases[Count]) { + + break; + + } + + } + + if (Count == 8) { + + // + // Error + // + + ConfigError = TRUE; + ConfigErrorValue = (ULONG)IoBaseAddr; + + goto RegisterAdapter; + } + + } + + + // + // Read interrupt number + // + + NdisReadConfiguration( + &Status, + &ReturnedValue, + ConfigHandle, + &InterruptStr, + NdisParameterHexInteger + ); + + if (Status == NDIS_STATUS_SUCCESS) { + + InterruptNumber = (CCHAR)(ReturnedValue->ParameterData.IntegerData); + + } + + + // + // Confirm value + // + + { + UCHAR Count; + + static CCHAR InterruptValues[] = { 2, 3, 4, 5 }; + + for (Count = 0 ; Count < 4; Count++) { + + if (InterruptNumber == InterruptValues[Count]) { + + break; + + } + + } + + if (Count == 4) { + + // + // Error + // + + ConfigError = TRUE; + ConfigErrorValue = InterruptNumber; + + goto RegisterAdapter; + } + + } + +#if !NDIS2 + // + // Read MaxMulticastList + // + + NdisReadConfiguration( + &Status, + &ReturnedValue, + ConfigHandle, + &MaxMulticastListStr, + NdisParameterInteger + ); + + if (Status == NDIS_STATUS_SUCCESS) { + + MaxMulticastList = ReturnedValue->ParameterData.IntegerData; + + } +#endif + +#if NDIS_NT + // + // Read Memory Mapped + // + + NdisReadConfiguration( + &Status, + &ReturnedValue, + ConfigHandle, + &MemoryMappedStr, + NdisParameterHexInteger + ); + + if (Status == NDIS_STATUS_SUCCESS) { + + MemMapped = (ReturnedValue->ParameterData.IntegerData == 0)?FALSE:TRUE; + + } + +#endif + +#if NDIS2 + // + // Read Transceiver + // + + NdisReadConfiguration( + &Status, + &ReturnedValue, + ConfigHandle, + &TransceiverStr, + NdisParameterString + ); + + if (Status == NDIS_STATUS_SUCCESS) { + + if (NdisEqualString (&ReturnedValue->ParameterData.StringData, &EXTERNALStr, 1 )) { + ExternalTransceiver = TRUE; + } + + } + +#else // NDIS3 + // + // Read Transceiver + // + + NdisReadConfiguration( + &Status, + &ReturnedValue, + ConfigHandle, + &TransceiverStr, + NdisParameterInteger + ); + + + if (Status == NDIS_STATUS_SUCCESS) { + + ExternalTransceiver = (ReturnedValue->ParameterData.IntegerData == 1)?TRUE:FALSE; + + } + +#endif + + // + // Read net address + // + + NdisReadNetworkAddress( + &Status, + &NetAddress, + &Length, + ConfigHandle + ); + + if ((Length == ETH_LENGTH_OF_ADDRESS) && (Status == NDIS_STATUS_SUCCESS)) { + + ETH_COPY_NETWORK_ADDRESS( + NewAdaptP->StationAddress, + NetAddress + ); + + } + +RegisterAdapter: + + NdisCloseConfiguration(ConfigHandle); + + IF_LOUD( DbgPrint( "Registering adapter # buffers %ld, " + "I/O base addr 0x%lx, max opens %ld, interrupt number %ld, " + "external %c, memory mapped %c, max multicast %ld\n", + DEFAULT_NUMBUFFERS, IoBaseAddr, DEFAULT_MAXOPENS, + InterruptNumber, + ExternalTransceiver ? 'Y' : 'N', + MemMapped ? 'Y' : 'N', + DEFAULT_MULTICASTLISTMAX );) + + + + // + // Set up the parameters. + // + + NewAdaptP->NumBuffers = DEFAULT_NUMBUFFERS; + NewAdaptP->IoBaseAddr = IoBaseAddr; + NewAdaptP->ExternalTransceiver = ExternalTransceiver; + NewAdaptP->InterruptNumber = InterruptNumber; + NewAdaptP->MemMapped = MemMapped; + NewAdaptP->MaxOpens = DEFAULT_MAXOPENS; + NewAdaptP->MulticastListMax = MaxMulticastList; + + if (ElnkiiRegisterAdapter(NewAdaptP, + ConfigurationHandle, + AdapterName, + ConfigError, + ConfigErrorValue + ) != NDIS_STATUS_SUCCESS) { + + + + // + // ElnkiiRegisterAdapter failed. + // + + NdisFreeMemory(NewAdaptP, sizeof(ELNKII_ADAPTER), 0); + return NDIS_STATUS_FAILURE; + } + + + + IF_LOUD( DbgPrint( "ElnkiiRegisterAdapter succeeded\n" );) + + + return NDIS_STATUS_SUCCESS; +} + +NDIS_STATUS +ElnkiiRegisterAdapter( + IN PELNKII_ADAPTER NewAdaptP, + IN NDIS_HANDLE ConfigurationHandle, + IN PNDIS_STRING AdapterName, + IN BOOLEAN ConfigError, + IN ULONG ConfigErrorValue + ) + +/*++ + +Routine Description: + + Called when a new adapter should be registered. It allocates space for + the adapter and open blocks, initializes the adapters block, and + calls NdisRegisterAdapter(). + +Arguments: + + NewAdaptP - The adapter structure. + + ConfigurationHandle - Handle passed to MacAddAdapter. + + AdapterName - Pointer to the name for this adapter. + + ConfigError - Was there an error during configuration reading. + + ConfigErrorValue - Value to log if there is an error. + +Return Value: + + Indicates the success or failure of the registration. + +--*/ + +{ + UINT i; + BOOLEAN CardPresent, IoBaseCorrect; + + NDIS_STATUS status; //general purpose return from NDIS calls + PNDIS_ADAPTER_INFORMATION AdapterInformation; // needed to register adapter + + // + // check that NumBuffers <= MAX_XMIT_BUFS + // + + if (NewAdaptP->NumBuffers > MAX_XMIT_BUFS) { + + status = NDIS_STATUS_RESOURCES; + + goto fail1; + + } + + NewAdaptP->OpenQueue = (PELNKII_OPEN)NULL; + NewAdaptP->CloseQueue = (PELNKII_OPEN)NULL; + + // + // The adapter is initialized, register it with NDIS. + // This must occur before interrupts are enabled since the + // InitializeInterrupt routine requires the NdisAdapterHandle + // + + // + // Set up the AdapterInformation structure; zero it + // first in case it is extended later. + // + + status = NdisAllocateMemory( (PVOID *)&AdapterInformation, + sizeof(NDIS_ADAPTER_INFORMATION) + + sizeof(NDIS_PORT_DESCRIPTOR), + 0, + HighestAcceptableMax + ); + + if (status != NDIS_STATUS_SUCCESS) { + + return(status); + + } + + NdisZeroMemory (AdapterInformation, + sizeof(NDIS_ADAPTER_INFORMATION) + + sizeof(NDIS_PORT_DESCRIPTOR) + ); + + AdapterInformation->AdapterType = NdisInterfaceIsa; + AdapterInformation->NumberOfPortDescriptors = 2; + AdapterInformation->PortDescriptors[0].InitialPort = (ULONG)NewAdaptP->IoBaseAddr; + AdapterInformation->PortDescriptors[0].NumberOfPorts = 0x10; + AdapterInformation->PortDescriptors[0].PortOffset = (PVOID *)(&(NewAdaptP->MappedIoBaseAddr)); + AdapterInformation->PortDescriptors[1].InitialPort = (ULONG)NewAdaptP->IoBaseAddr + 0x400; + AdapterInformation->PortDescriptors[1].NumberOfPorts = 0x10; + AdapterInformation->PortDescriptors[1].PortOffset = (PVOID *)(&(NewAdaptP->MappedGaBaseAddr)); + + + if ((status = NdisRegisterAdapter(&NewAdaptP->NdisAdapterHandle, + ElnkiiMacBlock.NdisMacHandle, + (NDIS_HANDLE)NewAdaptP, + ConfigurationHandle, + AdapterName, + AdapterInformation)) + != NDIS_STATUS_SUCCESS) { + + // + // NdisRegisterAdapter failed. + // + + + NdisFreeMemory(AdapterInformation, + sizeof(NDIS_ADAPTER_INFORMATION) + + sizeof(NDIS_PORT_DESCRIPTOR), + 0 + ); + + goto fail2; + } + + NdisFreeMemory(AdapterInformation, + sizeof(NDIS_ADAPTER_INFORMATION) + + sizeof(NDIS_PORT_DESCRIPTOR), + 0 + ); + + // + // Allocate the Spin lock. + // + NdisAllocateSpinLock(&NewAdaptP->Lock); + + if (ConfigError) { + + // + // Log Error and exit. + // + + NdisWriteErrorLogEntry( + NewAdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_UNSUPPORTED_CONFIGURATION, + 1, + ConfigErrorValue + ); + + goto fail3; + + } + + // + // Initialize Pending information + // + NewAdaptP->PendQueue = (PELNKII_PEND_DATA)NULL; + NewAdaptP->PendQTail = (PELNKII_PEND_DATA)NULL; + NewAdaptP->PendOp = (PELNKII_PEND_DATA)NULL; + NewAdaptP->DeferredDpc = (PVOID)HandlePendingOperations; + + // + // Initialize References. + // + NewAdaptP->References = 0; + + NdisInitializeTimer(&(NewAdaptP->DeferredTimer), + NewAdaptP->DeferredDpc, + NewAdaptP); + + // + // Map the memory mapped portion of the card. + // + // If NewAdaptP->MemMapped is FALSE, CardGetMemBaseAddr will not + // return the actual MemBaseAddr, but it will still return + // CardPresent and IoBaseCorrect. + // + // + + NewAdaptP->MemBaseAddr = CardGetMemBaseAddr(NewAdaptP, + &CardPresent, + &IoBaseCorrect + ); + + if (!CardPresent) { + + // + // The card does not seem to be there, fail silently. + // + + NdisWriteErrorLogEntry( + NewAdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_ADAPTER_NOT_FOUND, + 0 + ); + + status = NDIS_STATUS_ADAPTER_NOT_FOUND; + + goto fail3; + + } + + if (!IoBaseCorrect) { + + // + // The card is there, but the I/O base address jumper + // is not where we expect it to be. + // + + + NdisWriteErrorLogEntry( + NewAdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_BAD_IO_BASE_ADDRESS, + 0 + ); + + status = NDIS_STATUS_ADAPTER_NOT_FOUND; + + goto fail3; + + } + + if (NewAdaptP->MemMapped && (NewAdaptP->MemBaseAddr == NULL)) { + + // + // The card appears to not be mapped. + // + + NewAdaptP->MemMapped = FALSE; + + } + + // + // For memory-mapped operation, map the card's transmit/receive + // area into memory space. For programmed I/O, we will refer + // to transmit/receive memory in terms of offsets in the + // card's 32K address space; for an 8K card this is always + // the second 8K piece, starting at 0x2000. + // + + if (NewAdaptP->MemMapped) { + + NDIS_PHYSICAL_ADDRESS PhysicalAddress; + + NdisSetPhysicalAddressHigh(PhysicalAddress, 0); + NdisSetPhysicalAddressLow(PhysicalAddress, (ULONG)(NewAdaptP->MemBaseAddr)); + + NdisMapIoSpace( + &status, + (PVOID *)(&NewAdaptP->XmitStart), + NewAdaptP->NdisAdapterHandle, + PhysicalAddress, + 0x2000); + + if (status != NDIS_STATUS_SUCCESS) { + + NdisWriteErrorLogEntry( + NewAdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_RESOURCE_CONFLICT, + 0 + ); + + goto fail3; + + } + + } else { + + NewAdaptP->XmitStart = (PUCHAR)0x2000; + + } + + + // + // For the NicXXX fields, always use the addressing system + // starting at 0x2000 (or 0x20, since they contain the MSB only). + // + + NewAdaptP->NicXmitStart = 0x20; + + + // + // The start of the receive space. + // + + NewAdaptP->PageStart = NewAdaptP->XmitStart + + (NewAdaptP->NumBuffers * TX_BUF_SIZE); + + NewAdaptP->NicPageStart = NewAdaptP->NicXmitStart + + (UCHAR)(NewAdaptP->NumBuffers * BUFS_PER_TX); + + + // + // The end of the receive space. + // + + NewAdaptP->PageStop = NewAdaptP->XmitStart + 0x2000; + NewAdaptP->NicPageStop = NewAdaptP->NicXmitStart + (UCHAR)0x20; + + + + // + // Initialize the receive variables. + // + + NewAdaptP->NicReceiveConfig = RCR_REJECT_ERR; + NewAdaptP->ReceiveInProgress = FALSE; + + // + // Initialize the transmit buffer control. + // + + NewAdaptP->CurBufXmitting = -1; + NewAdaptP->TransmitInterruptPending = FALSE; + NewAdaptP->BufferOverflow = FALSE; + NewAdaptP->OverflowRestartXmitDpc = FALSE; + + for (i=0; iNumBuffers; i++) { + + NewAdaptP->BufferStatus[i] = EMPTY; + + } + + NewAdaptP->ResetInProgress = FALSE; + NewAdaptP->TransmitInterruptPending = FALSE; + + NewAdaptP->WakeUpFoundTransmit = FALSE; + + // + // Clear Interrupt Information + // + + NewAdaptP->ElnkiiHandleXmitCompleteRunning = FALSE; + + + // + // The transmit and loopback queues start out empty. + // + // Already done since structure is zero'd out. + // + + // + // Clear the tally counters. + // + // Already done since structure is zero'd out. + // + + // + // Read the Ethernet address off of the PROM. + // + + CardReadEthernetAddress(NewAdaptP); + + + // + // Initialize Filter Database + // + if (!EthCreateFilter( NewAdaptP->MulticastListMax, + ElnkiiChangeMulticastAddresses, + ElnkiiChangeFilterClasses, + ElnkiiCloseAction, + NewAdaptP->StationAddress, + &NewAdaptP->Lock, + &NewAdaptP->FilterDB + )) { + + + NdisWriteErrorLogEntry( + NewAdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_OUT_OF_RESOURCES, + 0 + ); + + status = NDIS_STATUS_FAILURE; + + goto fail4; + + } + + // + // Now initialize the NIC and Gate Array registers. + // + + NewAdaptP->NicInterruptMask = + IMR_RCV | IMR_XMIT | IMR_XMIT_ERR | IMR_OVERFLOW; + + + // + // Link us on to the chain of adapters for this MAC. + // + + NewAdaptP->MacBlock = &ElnkiiMacBlock; + + NdisAcquireSpinLock(&ElnkiiMacBlock.SpinLock); + + NewAdaptP->NextAdapter = ElnkiiMacBlock.AdapterQueue; + ElnkiiMacBlock.AdapterQueue = NewAdaptP; + + NdisReleaseSpinLock(&ElnkiiMacBlock.SpinLock); + + // + // Turn Off the card. + // + + SyncCardStop(NewAdaptP); + + // + // Set flag to ignore interrupts + // + + NewAdaptP->InCardTest = TRUE; + + // + // Connect to interrupt + // + + NdisInitializeInterrupt(&status, // status of call + &NewAdaptP->NdisInterrupt, // interrupt info str + NewAdaptP->NdisAdapterHandle, // NDIS adapter handle + ElnkiiInterruptHandler, // ptr to ISR + NewAdaptP, // context for ISR, DPC + ElnkiiInterruptDpc, // ptr to int DPC + NewAdaptP->InterruptNumber, // vector + NewAdaptP->InterruptNumber, // level + FALSE, // NOT shared + NdisInterruptLatched // InterruptMode + ); + + if (status != NDIS_STATUS_SUCCESS) { + + // + // The NIC could not be written to. + // + + NdisWriteErrorLogEntry( + NewAdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_INTERRUPT_CONNECT, + 0 + ); + + goto fail6; + + } + + if (!CardSetup(NewAdaptP)) { + + // + // The NIC could not be written to. + // + + NdisWriteErrorLogEntry( + NewAdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_ADAPTER_NOT_FOUND, + 0 + ); + + status = NDIS_STATUS_ADAPTER_NOT_FOUND; + + goto fail7; + } + + + // + // Perform card tests. + // + + if (!CardTest(NewAdaptP)) { + + // + // The tests failed, InitialCardTest determines whether + // this causes the whole initialization to fail. + // + + if (InitialCardTest) { + + NdisWriteErrorLogEntry( + NewAdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_HARDWARE_FAILURE, + 0 + ); + + status = NDIS_STATUS_DEVICE_FAILED; + + goto fail7; + + } + + } + + // + // Normal mode now + // + + NewAdaptP->InCardTest = FALSE; + + NdisInitializeTimer(&NewAdaptP->InterruptTimer, + (PVOID)ElnkiiInterruptDpc, + NewAdaptP + ); + + // + // Initialize the wake up timer to catch transmits that + // don't interrupt when complete. It fires continuously + // every two seconds, and we check if there are any + // uncompleted sends from the previous two-second + // period. + // + + NewAdaptP->WakeUpDpc = (PVOID)ElnkiiWakeUpDpc; + + NdisInitializeTimer(&NewAdaptP->WakeUpTimer, + (PVOID)(NewAdaptP->WakeUpDpc), + NewAdaptP ); + + NdisSetTimer( + &NewAdaptP->WakeUpTimer, + 2000 + ); + + NewAdaptP->Removed = FALSE; + + IF_LOUD( DbgPrint("Interrupt Connected\n");) + + + // + // Initialization completed successfully. + // + + IF_LOUD( DbgPrint(" [ Elnkii ] : OK");) + + return NDIS_STATUS_SUCCESS; + + + + // + // Code to unwind what has already been set up when a part of + // initialization fails, which is jumped into at various + // points based on where the failure occured. Jumping to + // a higher-numbered failure point will execute the code + // for that block and all lower-numbered ones. + // + +fail7: + NdisRemoveInterrupt(&NewAdaptP->NdisInterrupt); + +fail6: + NdisAcquireSpinLock(&ElnkiiMacBlock.SpinLock); + + // + // Take us out of the AdapterQueue. + // + + if (ElnkiiMacBlock.AdapterQueue == NewAdaptP) { + + ElnkiiMacBlock.AdapterQueue = NewAdaptP->NextAdapter; + + } else { + + PELNKII_ADAPTER TmpAdaptP = ElnkiiMacBlock.AdapterQueue; + + while (TmpAdaptP->NextAdapter != NewAdaptP) { + + TmpAdaptP = TmpAdaptP->NextAdapter; + + } + + TmpAdaptP->NextAdapter = TmpAdaptP->NextAdapter->NextAdapter; + } + + NdisReleaseSpinLock(&ElnkiiMacBlock.SpinLock); + + EthDeleteFilter(NewAdaptP->FilterDB); + +fail4: + + // + // We already enabled the interrupt on the card, so + // turn it off. + // + + NdisRawWritePortUchar(NewAdaptP->MappedGaBaseAddr+GA_INT_DMA_CONFIG, 0x00); + + if (NewAdaptP->MemMapped) { + + NdisUnmapIoSpace( + NewAdaptP->NdisAdapterHandle, + NewAdaptP->XmitStart, + 0x2000); + + } + +fail3: + NdisDeregisterAdapter(NewAdaptP->NdisAdapterHandle); + NdisFreeSpinLock(&NewAdaptP->Lock); + +fail2: + +fail1: + + return status; +} + + +NDIS_STATUS +ElnkiiOpenAdapter( + OUT PNDIS_STATUS OpenErrorStatus, + OUT NDIS_HANDLE * MacBindingHandle, + OUT PUINT SelectedMediumIndex, + IN PNDIS_MEDIUM MediumArray, + IN UINT MediumArraySize, + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_HANDLE MacAdapterContext, + IN UINT OpenOptions, + IN PSTRING AddressingInformation OPTIONAL + ) + +/*++ + +Routine Description: + + NDIS function. It initializes the open block and links it in + the appropriate lists. + +Arguments: + + See NDIS 3.0 spec. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)MacAdapterContext); + PELNKII_OPEN NewOpenP; + NDIS_STATUS Status; + + // + // Don't use extended error or OpenOptions for Elnkii + // + + UNREFERENCED_PARAMETER(OpenOptions); + + *OpenErrorStatus=NDIS_STATUS_SUCCESS; + + IF_LOUD( DbgPrint("In Open Adapter\n");) + + // + // Scan the media list for our media type (802.3) + // + + *SelectedMediumIndex = (UINT)-1; + + while (MediumArraySize > 0) { + + if (MediumArray[--MediumArraySize] == NdisMedium802_3 ) { + + *SelectedMediumIndex = MediumArraySize; + + break; + } + } + + + if (*SelectedMediumIndex == -1) { + + return NDIS_STATUS_UNSUPPORTED_MEDIA; + + } + + // + // Allocate memory for the open. + // + + + Status = NdisAllocateMemory((PVOID *)&NewOpenP, sizeof(ELNKII_OPEN), 0, HighestAcceptableMax); + + if (Status != NDIS_STATUS_SUCCESS) { + + NdisWriteErrorLogEntry( + AdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_OUT_OF_RESOURCES, + 0 + ); + + return(NDIS_STATUS_RESOURCES); + + } + + NdisZeroMemory(NewOpenP, sizeof(ELNKII_OPEN)); + + // + // Link this open to the appropriate lists. + // + + NdisAcquireSpinLock(&AdaptP->Lock); + + AdaptP->References++; + + if ((AdaptP->OpenQueue == NULL) && (AdaptP->CloseQueue == NULL)) { + + // + // The first open on this adapter. + // + + CardStart(AdaptP); + + } + + NewOpenP->NextOpen = AdaptP->OpenQueue; + AdaptP->OpenQueue = NewOpenP; + + if (AdaptP->ResetInProgress || !EthNoteFilterOpenAdapter( + AdaptP->FilterDB, + NewOpenP, + NdisBindingContext, + &NewOpenP->NdisFilterHandle + )) { + + AdaptP->References--; + + AdaptP->OpenQueue = NewOpenP->NextOpen; + + NdisReleaseSpinLock(&AdaptP->Lock); + + NdisFreeMemory(NewOpenP, sizeof(ELNKII_OPEN), 0); + + NdisWriteErrorLogEntry( + AdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_OUT_OF_RESOURCES, + 0 + ); + + return NDIS_STATUS_FAILURE; + + } + + // + // Set up the open block. + // + + NewOpenP->Adapter = AdaptP; + NewOpenP->MacBlock = AdaptP->MacBlock; + NewOpenP->NdisBindingContext = NdisBindingContext; + NewOpenP->AddressingInformation = AddressingInformation; + + // + // set the Request Queue empty + // + + + NewOpenP->Closing = FALSE; + NewOpenP->LookAhead = ELNKII_MAX_LOOKAHEAD; + + AdaptP->MaxLookAhead = ELNKII_MAX_LOOKAHEAD; + + NewOpenP->ReferenceCount = 1; + + *MacBindingHandle = (NDIS_HANDLE)NewOpenP; + + ELNKII_DO_DEFERRED(AdaptP); + + IF_LOUD( DbgPrint("Out Open Adapter\n");) + + return NDIS_STATUS_SUCCESS; +} + + +VOID +ElnkiiAdjustMaxLookAhead( + IN PELNKII_ADAPTER Adapter + ) +/*++ + +Routine Description: + + This routine finds the open with the maximum lookahead value and + stores that in the adapter block. + +Arguments: + + Adapter - A pointer to the adapter block. + +Returns: + + None. + +--*/ +{ + ULONG CurrentMax = 0; + PELNKII_OPEN CurrentOpen; + + CurrentOpen = Adapter->OpenQueue; + + while (CurrentOpen != NULL) { + + if (CurrentOpen->LookAhead > CurrentMax) { + + CurrentMax = CurrentOpen->LookAhead; + + } + + CurrentOpen = CurrentOpen->NextOpen; + } + + if (CurrentMax == 0) { + + CurrentMax = ELNKII_MAX_LOOKAHEAD; + + } + + Adapter->MaxLookAhead = CurrentMax; + +} + +NDIS_STATUS +ElnkiiCloseAdapter( + IN NDIS_HANDLE MacBindingHandle + ) + +/*++ + +Routine Description: + + NDIS function. Unlinks the open block and frees it. + +Arguments: + + See NDIS 3.0 spec. + +--*/ + +{ + PELNKII_OPEN OpenP = ((PELNKII_OPEN)MacBindingHandle); + PELNKII_ADAPTER AdaptP = OpenP->Adapter; + PELNKII_OPEN TmpOpenP; + NDIS_STATUS StatusToReturn; + + NdisAcquireSpinLock(&AdaptP->Lock); + + if (OpenP->Closing) { + + // + // The open is already being closed. + // + + NdisReleaseSpinLock(&AdaptP->Lock); + + return NDIS_STATUS_CLOSING; + } + + AdaptP->References++; + + OpenP->ReferenceCount++; + + // + // Remove this open from the list for this adapter. + // + + if (OpenP == AdaptP->OpenQueue) { + + AdaptP->OpenQueue = OpenP->NextOpen; + + } else { + + TmpOpenP = AdaptP->OpenQueue; + + while (TmpOpenP->NextOpen != OpenP) { + + TmpOpenP = TmpOpenP->NextOpen; + + } + + TmpOpenP->NextOpen = OpenP->NextOpen; + } + + // + // Remove from Filter package to block all receives. + // + + StatusToReturn = EthDeleteFilterOpenAdapter( + AdaptP->FilterDB, + OpenP->NdisFilterHandle, + NULL + ); + + // + // If the status is successful that merely implies that + // we were able to delete the reference to the open binding + // from the filtering code. If we have a successful status + // at this point we still need to check whether the reference + // count to determine whether we can close. + // + // + // The delete filter routine can return a "special" status + // that indicates that there is a current NdisIndicateReceive + // on this binding. See below. + // + + if (StatusToReturn == NDIS_STATUS_SUCCESS) { + + // + // Check whether the reference count is two. If + // it is then we can get rid of the memory for + // this open. + // + // A count of two indicates one for this routine + // and one for the filter which we *know* we can + // get rid of. + // + + if (OpenP->ReferenceCount != 2) { + + // + // We are not the only reference to the open. Remove + // it from the open list and delete the memory. + // + + + OpenP->Closing = TRUE; + + // + // Account for this routines reference to the open + // as well as reference because of the original open. + // + + OpenP->ReferenceCount -= 2; + + // + // Change the status to indicate that we will + // be closing this later. + // + + StatusToReturn = NDIS_STATUS_PENDING; + + } else { + + OpenP->ReferenceCount -= 2; + + } + + } else if (StatusToReturn == NDIS_STATUS_PENDING) { + + OpenP->Closing = TRUE; + + // + // Account for this routines reference to the open + // as well as reference because of the original open. + // + + OpenP->ReferenceCount -= 2; + + } else if (StatusToReturn == NDIS_STATUS_CLOSING_INDICATING) { + + // + // When we have this status it indicates that the filtering + // code was currently doing an NdisIndicateReceive. It + // would not be wise to delete the memory for the open at + // this point. The filtering code will call our close action + // routine upon return from NdisIndicateReceive and that + // action routine will decrement the reference count for + // the open. + // + + OpenP->Closing = TRUE; + + // + // This status is private to the filtering routine. Just + // tell the caller the the close is pending. + // + + StatusToReturn = NDIS_STATUS_PENDING; + + // + // Account for this routines reference to the open. + // + + OpenP->ReferenceCount--; + + } else { + + // + // Account for this routines reference to the open. + // + + OpenP->ReferenceCount--; + + } + + // + // See if this is the last reference to this open. + // + + if (OpenP->ReferenceCount == 0) { + + // + // Check if the MaxLookAhead needs adjustment. + // + + if (OpenP->LookAhead == AdaptP->MaxLookAhead) { + + ElnkiiAdjustMaxLookAhead(AdaptP); + + } + + // + // Done, free the open. + // + + NdisFreeMemory(OpenP, sizeof(ELNKII_OPEN), 0); + + if ((AdaptP->OpenQueue == NULL ) && (AdaptP->CloseQueue == NULL)) { + + // + // We can disable the card. + // + + CardStop(AdaptP); + + } + + } else { + + // + // Add it to the close list + // + + OpenP->NextOpen = AdaptP->CloseQueue; + AdaptP->CloseQueue = OpenP; + + // + // Will get removed when count drops to zero. + // + + StatusToReturn = NDIS_STATUS_PENDING; + + } + + + ELNKII_DO_DEFERRED(AdaptP); + + return(StatusToReturn); + +} + +NDIS_STATUS +ElnkiiRequest( + IN NDIS_HANDLE MacBindingHandle, + IN PNDIS_REQUEST NdisRequest + ) + +/*++ + +Routine Description: + + This routine allows a protocol to query and set information + about the MAC. + +Arguments: + + MacBindingHandle - The context value returned by the MAC when the + adapter was opened. In reality, it is a pointer to PELNKII_OPEN. + + NdisRequest - A structure which contains the request type (Set or + Query), an array of operations to perform, and an array for holding + the results of the operations. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NDIS_STATUS StatusToReturn = NDIS_STATUS_SUCCESS; + + PELNKII_OPEN Open = (PELNKII_OPEN)(MacBindingHandle); + PELNKII_ADAPTER Adapter = (Open->Adapter); + + IF_LOUD( DbgPrint("In Request\n");) + + NdisAcquireSpinLock(&Adapter->Lock); + + // + // Ensure that the open does not close while in this function. + // + + Open->ReferenceCount++; + + Adapter->References++; + + // + // Process request + // + + if (Open->Closing) { + + NdisReleaseSpinLock(&Adapter->Lock); + + StatusToReturn = NDIS_STATUS_CLOSING; + + } else if (NdisRequest->RequestType == NdisRequestQueryInformation) { + + NdisReleaseSpinLock(&Adapter->Lock); + + StatusToReturn = ElnkiiQueryInformation(Adapter, Open, NdisRequest); + + } else if (NdisRequest->RequestType == NdisRequestSetInformation) { + + + // + // Make sure Adapter is in a valid state. + // + + // + // All requests are rejected during a reset. + // + + if (Adapter->ResetInProgress) { + + NdisReleaseSpinLock(&Adapter->Lock); + + StatusToReturn = NDIS_STATUS_RESET_IN_PROGRESS; + + } else { + + NdisReleaseSpinLock(&Adapter->Lock); + + StatusToReturn = ElnkiiSetInformation(Adapter,Open,NdisRequest); + + } + + } else { + + NdisReleaseSpinLock(&Adapter->Lock); + + StatusToReturn = NDIS_STATUS_NOT_RECOGNIZED; + + } + + NdisAcquireSpinLock(&Adapter->Lock); + + --Open->ReferenceCount; + + ELNKII_DO_DEFERRED(Adapter); + + IF_LOUD( DbgPrint("Out Request\n");) + + return(StatusToReturn); + +} + +NDIS_STATUS +ElnkiiQueryProtocolInformation( + IN PELNKII_ADAPTER Adapter, + IN PELNKII_OPEN Open, + IN NDIS_OID Oid, + IN BOOLEAN GlobalMode, + IN PVOID InfoBuffer, + IN UINT BytesLeft, + OUT PUINT BytesNeeded, + OUT PUINT BytesWritten +) + +/*++ + +Routine Description: + + The ElnkiiQueryProtocolInformation process a Query request for + NDIS_OIDs that are specific to a binding about the MAC. Note that + some of the OIDs that are specific to bindings are also queryable + on a global basis. Rather than recreate this code to handle the + global queries, I use a flag to indicate if this is a query for the + global data or the binding specific data. + +Arguments: + + Adapter - a pointer to the adapter. + + Open - a pointer to the open instance. + + Oid - the NDIS_OID to process. + + GlobalMode - Some of the binding specific information is also used + when querying global statistics. This is a flag to specify whether + to return the global value, or the binding specific value. + + PlaceInInfoBuffer - a pointer into the NdisRequest->InformationBuffer + into which store the result of the query. + + BytesLeft - the number of bytes left in the InformationBuffer. + + BytesNeeded - If there is not enough room in the information buffer + then this will contain the number of bytes needed to complete the + request. + + BytesWritten - a pointer to the number of bytes written into the + InformationBuffer. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NDIS_MEDIUM Medium = NdisMedium802_3; + ULONG GenericULong; + USHORT GenericUShort; + UCHAR GenericArray[6]; + + NDIS_STATUS StatusToReturn = NDIS_STATUS_SUCCESS; + + // + // Common variables for pointing to result of query + // + + PVOID MoveSource = (PVOID)(&GenericULong); + ULONG MoveBytes = sizeof(GenericULong); + UINT Filter; + + NDIS_HARDWARE_STATUS HardwareStatus = NdisHardwareStatusReady; + + // + // General Algorithm: + // + // Switch(Request) + // Get requested information + // Store results in a common variable. + // Copy result in common variable to result buffer. + // + + // + // Make sure that ulong is 4 bytes. Else GenericULong must change + // to something of size 4. + // + ASSERT(sizeof(ULONG) == 4); + + IF_LOUD( DbgPrint("In QueryProtocol\n");) + + // + // Make sure no changes occur while processing. + // + + NdisAcquireSpinLock(&Adapter->Lock); + + // + // Switch on request type + // + + switch (Oid) { + + case OID_GEN_MAC_OPTIONS: + + GenericULong = (ULONG)(NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | + NDIS_MAC_OPTION_RECEIVE_SERIALIZED | + NDIS_MAC_OPTION_NO_LOOPBACK + ); + + if (!Adapter->MemMapped) { + + GenericULong |= NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA; + + } + + break; + + case OID_GEN_SUPPORTED_LIST: + + if (!GlobalMode) { + + MoveSource = (PVOID)(ElnkiiProtocolSupportedOids); + MoveBytes = sizeof(ElnkiiProtocolSupportedOids); + + } else { + + MoveSource = (PVOID)(ElnkiiGlobalSupportedOids); + MoveBytes = sizeof(ElnkiiGlobalSupportedOids); + + } + break; + + case OID_GEN_HARDWARE_STATUS: + + + if (Adapter->ResetInProgress) { + + HardwareStatus = NdisHardwareStatusReset; + + } else + HardwareStatus = NdisHardwareStatusReady; + + + MoveSource = (PVOID)(&HardwareStatus); + MoveBytes = sizeof(NDIS_HARDWARE_STATUS); + + break; + + case OID_GEN_MEDIA_SUPPORTED: + case OID_GEN_MEDIA_IN_USE: + + MoveSource = (PVOID) (&Medium); + MoveBytes = sizeof(NDIS_MEDIUM); + break; + + case OID_GEN_MAXIMUM_LOOKAHEAD: + + GenericULong = ELNKII_MAX_LOOKAHEAD; + + break; + + + case OID_GEN_MAXIMUM_FRAME_SIZE: + + GenericULong = (ULONG)(1514 - ELNKII_HEADER_SIZE); + + break; + + + case OID_GEN_MAXIMUM_TOTAL_SIZE: + + GenericULong = (ULONG)(1514); + + break; + + + case OID_GEN_LINK_SPEED: + + GenericULong = (ULONG)(100000); + + break; + + + case OID_GEN_TRANSMIT_BUFFER_SPACE: + + GenericULong = (ULONG)(Adapter->NumBuffers * TX_BUF_SIZE); + + break; + + case OID_GEN_RECEIVE_BUFFER_SPACE: + + GenericULong = (ULONG)0x2000; + GenericULong -= (Adapter->NumBuffers * ((TX_BUF_SIZE / 256) + 1) * 256); + + // + // Subtract off receive buffer overhead + // + { + ULONG TmpUlong = GenericULong / 256; + + TmpUlong *= 4; + + GenericULong -= TmpUlong; + + } + + // + // Round to nearest 256 bytes + // + GenericULong = (GenericULong / 256) * 256; + + break; + + case OID_GEN_TRANSMIT_BLOCK_SIZE: + + GenericULong = (ULONG)(TX_BUF_SIZE); + + break; + + case OID_GEN_RECEIVE_BLOCK_SIZE: + + GenericULong = (ULONG)(256); + + break; + + case OID_GEN_VENDOR_ID: + + NdisMoveMemory( + (PVOID)(&GenericULong), + Adapter->PermanentAddress, + 3 + ); + + GenericULong &= 0xFFFFFF00; + break; + + case OID_GEN_VENDOR_DESCRIPTION: + + MoveSource = (PVOID)"Etherlink II Adapter."; + MoveBytes = 22; + + break; + + case OID_GEN_DRIVER_VERSION: + + GenericUShort = ((USHORT)ELNKII_NDIS_MAJOR_VERSION << 8) | + ELNKII_NDIS_MINOR_VERSION; + + MoveSource = (PVOID)(&GenericUShort); + MoveBytes = sizeof(GenericUShort); + break; + + + case OID_GEN_CURRENT_PACKET_FILTER: + + if (GlobalMode) { + + Filter = ETH_QUERY_FILTER_CLASSES(Adapter->FilterDB); + + GenericULong = (ULONG)(Filter); + + } else { + + Filter = ETH_QUERY_PACKET_FILTER(Adapter->FilterDB, + Open->NdisFilterHandle); + + GenericULong = (ULONG)(Filter); + + } + + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + + if ( GlobalMode ) { + + GenericULong = (ULONG)(Adapter->MaxLookAhead); + + } else { + + GenericULong = Open->LookAhead; + + } + + break; + + case OID_802_3_PERMANENT_ADDRESS: + ELNKII_MOVE_MEM((PCHAR)GenericArray, + Adapter->PermanentAddress, + ETH_LENGTH_OF_ADDRESS); + + MoveSource = (PVOID)(GenericArray); + MoveBytes = sizeof(Adapter->PermanentAddress); + break; + + + case OID_802_3_CURRENT_ADDRESS: + + ELNKII_MOVE_MEM((PCHAR)GenericArray, + Adapter->StationAddress, + ETH_LENGTH_OF_ADDRESS); + + MoveSource = (PVOID)(GenericArray); + MoveBytes = sizeof(Adapter->StationAddress); + break; + + case OID_802_3_MULTICAST_LIST: + + { + UINT NumAddresses; + + + if (GlobalMode) { + + NumAddresses = ETH_NUMBER_OF_GLOBAL_FILTER_ADDRESSES(Adapter->FilterDB); + + if ((NumAddresses * ETH_LENGTH_OF_ADDRESS) > BytesLeft) { + + *BytesNeeded = (NumAddresses * ETH_LENGTH_OF_ADDRESS); + + StatusToReturn = NDIS_STATUS_INVALID_LENGTH; + + break; + + } + + EthQueryGlobalFilterAddresses( + &StatusToReturn, + Adapter->FilterDB, + BytesLeft, + &NumAddresses, + InfoBuffer + ); + + *BytesWritten = NumAddresses * ETH_LENGTH_OF_ADDRESS; + + // + // Should not be an error since we held the spinlock + // nothing should have changed. + // + + ASSERT(StatusToReturn == NDIS_STATUS_SUCCESS); + + } else { + + NumAddresses = EthNumberOfOpenFilterAddresses( + Adapter->FilterDB, + Open->NdisFilterHandle + ); + + if ((NumAddresses * ETH_LENGTH_OF_ADDRESS) > BytesLeft) { + + *BytesNeeded = (NumAddresses * ETH_LENGTH_OF_ADDRESS); + + StatusToReturn = NDIS_STATUS_INVALID_LENGTH; + + break; + + } + + EthQueryOpenFilterAddresses( + &StatusToReturn, + Adapter->FilterDB, + Open->NdisFilterHandle, + BytesLeft, + &NumAddresses, + InfoBuffer + ); + + // + // Should not be an error since we held the spinlock + // nothing should have changed. + // + + ASSERT(StatusToReturn == NDIS_STATUS_SUCCESS); + + *BytesWritten = NumAddresses * ETH_LENGTH_OF_ADDRESS; + + } + + } + + break; + + case OID_802_3_MAXIMUM_LIST_SIZE: + + GenericULong = (ULONG) (Adapter->MulticastListMax); + + break; + + + + default: + + StatusToReturn = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + if ((StatusToReturn == NDIS_STATUS_SUCCESS) && + (Oid != OID_802_3_MULTICAST_LIST)) { + + if (MoveBytes > BytesLeft) { + + // + // Not enough room in InformationBuffer. Punt + // + + *BytesNeeded = MoveBytes; + + StatusToReturn = NDIS_STATUS_INVALID_LENGTH; + + } else { + + // + // Store result. + // + + ELNKII_MOVE_MEM(InfoBuffer, MoveSource, MoveBytes); + + (*BytesWritten) += MoveBytes; + + } + } + + NdisReleaseSpinLock(&Adapter->Lock); + + IF_LOUD( DbgPrint("Out QueryProtocol\n");) + + return(StatusToReturn); +} + +NDIS_STATUS +ElnkiiQueryInformation( + IN PELNKII_ADAPTER Adapter, + IN PELNKII_OPEN Open, + IN PNDIS_REQUEST NdisRequest + ) +/*++ + +Routine Description: + + The ElnkiiQueryInformation is used by ElnkiiRequest to query information + about the MAC. + +Arguments: + + Adapter - A pointer to the adapter. + + Open - A pointer to a particular open instance. + + NdisRequest - A structure which contains the request type (Query), + an array of operations to perform, and an array for holding + the results of the operations. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + + UINT BytesWritten = 0; + UINT BytesNeeded = 0; + UINT BytesLeft = NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength; + PUCHAR InfoBuffer = (PUCHAR)(NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer); + + NDIS_STATUS StatusToReturn = NDIS_STATUS_SUCCESS; + + + IF_LOUD( DbgPrint("In QueryInfor\n");) + + StatusToReturn = ElnkiiQueryProtocolInformation( + Adapter, + Open, + NdisRequest->DATA.QUERY_INFORMATION.Oid, + FALSE, + InfoBuffer, + BytesLeft, + &BytesNeeded, + &BytesWritten + ); + + + NdisRequest->DATA.QUERY_INFORMATION.BytesWritten = BytesWritten; + + NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded = BytesNeeded; + + IF_LOUD( DbgPrint("Out QueryInfor\n");) + + return(StatusToReturn); +} + +NDIS_STATUS +ElnkiiSetInformation( + IN PELNKII_ADAPTER Adapter, + IN PELNKII_OPEN Open, + IN PNDIS_REQUEST NdisRequest + ) +/*++ + +Routine Description: + + The ElnkiiSetInformation is used by ElnkiiRequest to set information + about the MAC. + +Arguments: + + Adapter - A pointer to the adapter. + + Open - A pointer to an open instance. + + NdisRequest - A structure which contains the request type (Set), + an array of operations to perform, and an array for holding + the results of the operations. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + + // + // General Algorithm: + // + // Verify length + // Switch(Request) + // Process Request + // + + UINT BytesRead = 0; + UINT BytesNeeded = 0; + UINT BytesLeft = NdisRequest->DATA.SET_INFORMATION.InformationBufferLength; + PUCHAR InfoBuffer = (PUCHAR)(NdisRequest->DATA.SET_INFORMATION.InformationBuffer); + + // + // Variables for a particular request + // + + NDIS_OID Oid; + UINT OidLength; + + // + // Variables for holding the new values to be used. + // + + ULONG LookAhead; + ULONG Filter; + + NDIS_STATUS StatusToReturn = NDIS_STATUS_SUCCESS; + + + IF_LOUD( DbgPrint("In SetInfo\n");) + + // + // Get Oid and Length of request + // + + Oid = NdisRequest->DATA.SET_INFORMATION.Oid; + + OidLength = BytesLeft; + + switch (Oid) { + + + case OID_802_3_MULTICAST_LIST: + + // + // Verify length + // + + if ((OidLength % ETH_LENGTH_OF_ADDRESS) != 0){ + + StatusToReturn = NDIS_STATUS_INVALID_LENGTH; + + NdisRequest->DATA.SET_INFORMATION.BytesRead = 0; + NdisRequest->DATA.SET_INFORMATION.BytesNeeded = 0; + + break; + + } + + StatusToReturn = ElnkiiSetMulticastAddresses( + Adapter, + Open, + NdisRequest, + (UINT)(OidLength / ETH_LENGTH_OF_ADDRESS), + (PVOID)InfoBuffer + ); + + break; + + + case OID_GEN_CURRENT_PACKET_FILTER: + + // + // Verify length + // + + if (OidLength != 4 ) { + + StatusToReturn = NDIS_STATUS_INVALID_LENGTH; + + NdisRequest->DATA.SET_INFORMATION.BytesRead = 0; + NdisRequest->DATA.SET_INFORMATION.BytesNeeded = 0; + + break; + + } + + ELNKII_MOVE_MEM(&Filter, InfoBuffer, 4); + + // + // Verify bits + // + + if (Filter & (NDIS_PACKET_TYPE_SOURCE_ROUTING | + NDIS_PACKET_TYPE_SMT | + NDIS_PACKET_TYPE_MAC_FRAME | + NDIS_PACKET_TYPE_FUNCTIONAL | + NDIS_PACKET_TYPE_ALL_FUNCTIONAL | + NDIS_PACKET_TYPE_GROUP + )) { + + StatusToReturn = NDIS_STATUS_NOT_SUPPORTED; + + NdisRequest->DATA.SET_INFORMATION.BytesRead = 4; + NdisRequest->DATA.SET_INFORMATION.BytesNeeded = 0; + + break; + + } + + StatusToReturn = ElnkiiSetPacketFilter(Adapter, + Open, + NdisRequest, + Filter); + + + + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + + // + // Verify length + // + + if (OidLength != 4) { + + StatusToReturn = NDIS_STATUS_INVALID_LENGTH; + + NdisRequest->DATA.SET_INFORMATION.BytesRead = 0; + NdisRequest->DATA.SET_INFORMATION.BytesNeeded = 0; + + break; + + } + + ELNKII_MOVE_MEM(&LookAhead, InfoBuffer, 4); + + if (LookAhead <= ELNKII_MAX_LOOKAHEAD) { + + if (LookAhead > Adapter->MaxLookAhead) { + + Adapter->MaxLookAhead = LookAhead; + + Open->LookAhead = LookAhead; + + } else { + + if ((Open->LookAhead == Adapter->MaxLookAhead) && + (LookAhead < Open->LookAhead)) { + + Open->LookAhead = LookAhead; + + ElnkiiAdjustMaxLookAhead(Adapter); + + } else { + + Open->LookAhead = LookAhead; + + } + + } + + + } else { + + StatusToReturn = NDIS_STATUS_INVALID_LENGTH; + + } + + break; + + case OID_GEN_PROTOCOL_OPTIONS: + + if (OidLength != 4) { + + StatusToReturn = NDIS_STATUS_INVALID_LENGTH; + + NdisRequest->DATA.SET_INFORMATION.BytesRead = 0; + NdisRequest->DATA.SET_INFORMATION.BytesNeeded = 0; + + break; + + } + + ELNKII_MOVE_MEM(&Open->ProtOptionFlags, InfoBuffer, 4); + StatusToReturn = NDIS_STATUS_SUCCESS; + + break; + + default: + + StatusToReturn = NDIS_STATUS_INVALID_OID; + + NdisRequest->DATA.SET_INFORMATION.BytesRead = 0; + NdisRequest->DATA.SET_INFORMATION.BytesNeeded = 0; + + break; + + } + + + if (StatusToReturn == NDIS_STATUS_SUCCESS) { + + NdisRequest->DATA.SET_INFORMATION.BytesRead = BytesLeft; + NdisRequest->DATA.SET_INFORMATION.BytesNeeded = 0; + + } + + + IF_LOUD( DbgPrint("Out SetInfo\n");) + + return(StatusToReturn); +} + + +STATIC +NDIS_STATUS +ElnkiiSetPacketFilter( + IN PELNKII_ADAPTER Adapter, + IN PELNKII_OPEN Open, + IN PNDIS_REQUEST NdisRequest, + IN UINT PacketFilter + ) + +/*++ + +Routine Description: + + The ElnkiiSetPacketFilter request allows a protocol to control the types + of packets that it receives from the MAC. + +Arguments: + + Adapter - A pointer to the adapter structure. + + Open - A pointer to the open block giving the request. + + NdisRequest - The NDIS_REQUEST with the set packet filter command in it. + + PacketFilter - A bit mask that contains flags that correspond to specific + classes of received packets. If a particular bit is set in the mask, + then packet reception for that class of packet is enabled. If the + bit is clear, then packets that fall into that class are not received + by the client. A single exception to this rule is that if the promiscuous + bit is set, then the client receives all packets on the network, regardless + of the state of the other flags. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + // + // Keeps track of the *MAC's* status. The status will only be + // reset if the filter change action routine is called. + // + NDIS_STATUS StatusOfFilterChange = NDIS_STATUS_SUCCESS; + + NdisAcquireSpinLock(&Adapter->Lock); + + IF_LOUD( DbgPrint("In SetFilter\n");) + + if (!Adapter->ResetInProgress) { + + if (!Open->Closing) { + + // + // Increment the open while it is going through the filtering + // routines. + // + + Open->ReferenceCount++; + + StatusOfFilterChange = EthFilterAdjust( + Adapter->FilterDB, + Open->NdisFilterHandle, + NdisRequest, + PacketFilter, + TRUE + ); + + Open->ReferenceCount--; + + } else { + + StatusOfFilterChange = NDIS_STATUS_CLOSING; + + } + + } else { + + StatusOfFilterChange = NDIS_STATUS_RESET_IN_PROGRESS; + + } + + NdisReleaseSpinLock(&Adapter->Lock); + + IF_LOUD( DbgPrint("Out SetFilter\n");) + + return StatusOfFilterChange; +} + + + + +STATIC +NDIS_STATUS +ElnkiiSetMulticastAddresses( + IN PELNKII_ADAPTER Adapter, + IN PELNKII_OPEN Open, + IN PNDIS_REQUEST NdisRequest, + IN UINT NumAddresses, + IN CHAR AddressList[][ETH_LENGTH_OF_ADDRESS] + ) + +/*++ + +Routine Description: + + This function calls into the filter package in order to set the + multicast address list for the card to the specified list. + +Arguments: + + Adapter - A pointer to the adapter block. + + Open - A pointer to the open block submitting the request. + + NdisRequest - The NDIS_REQUEST with the set multicast address list command + in it. + + NumAddresses - A count of the number of addresses in the addressList. + + AddressList - An array of multicast addresses that this open instance + wishes to accept. + + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + + // + // Keeps track of the *MAC's* status. The status will only be + // reset if the filter change action routine is called. + // + NDIS_STATUS StatusOfFilterChange = NDIS_STATUS_SUCCESS; + + IF_LOUD( DbgPrint("In SetMulticast\n");) + + NdisAcquireSpinLock(&Adapter->Lock); + + if (!Adapter->ResetInProgress) { + + if (!Open->Closing) { + + // + // Increment the open while it is going through the filtering + // routines. + // + + Open->ReferenceCount++; + + StatusOfFilterChange = EthChangeFilterAddresses( + Adapter->FilterDB, + Open->NdisFilterHandle, + NdisRequest, + NumAddresses, + AddressList, + TRUE + ); + + Open->ReferenceCount--; + + } else { + + StatusOfFilterChange = NDIS_STATUS_CLOSING; + + } + + } else { + + StatusOfFilterChange = NDIS_STATUS_RESET_IN_PROGRESS; + + } + + NdisReleaseSpinLock(&Adapter->Lock); + + IF_LOUD( DbgPrint("Out SetMulticast\n");) + + return StatusOfFilterChange; +} + + + +NDIS_STATUS +ElnkiiFillInGlobalData( + IN PELNKII_ADAPTER Adapter, + IN PNDIS_REQUEST NdisRequest + ) + +/*++ + +Routine Description: + + This routine completes a GlobalStatistics request. It is critical that + if information is needed from the Adapter->* fields, they have been + updated before this routine is called. + +Arguments: + + Adapter - A pointer to the Adapter. + + NdisRequest - A structure which contains the request type (Global + Query), an array of operations to perform, and an array for holding + the results of the operations. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + // + // General Algorithm: + // + // Switch(Request) + // Get requested information + // Store results in a common variable. + // default: + // Try protocol query information + // If that fails, fail query. + // + // Copy result in common variable to result buffer. + // Finish processing + + UINT BytesWritten = 0; + UINT BytesNeeded = 0; + UINT BytesLeft = NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength; + PUCHAR InfoBuffer = (PUCHAR)(NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer); + + NDIS_STATUS StatusToReturn = NDIS_STATUS_SUCCESS; + + // + // This variable holds result of query + // + + ULONG GenericULong; + UINT MoveBytes = sizeof(UINT) * 2 + sizeof(NDIS_OID); + + // + // Make sure that int is 4 bytes. Else GenericULong must change + // to something of size 4. + // + ASSERT(sizeof(UINT) == 4); + + + StatusToReturn = ElnkiiQueryProtocolInformation( + Adapter, + NULL, + NdisRequest->DATA.QUERY_INFORMATION.Oid, + TRUE, + InfoBuffer, + BytesLeft, + &BytesNeeded, + &BytesWritten + ); + + if (StatusToReturn == NDIS_STATUS_NOT_SUPPORTED) { + + StatusToReturn = NDIS_STATUS_SUCCESS; + + NdisAcquireSpinLock(&Adapter->Lock); + + // + // Switch on request type + // + + switch (NdisRequest->DATA.QUERY_INFORMATION.Oid) { + + case OID_GEN_XMIT_OK: + + GenericULong = (UINT)(Adapter->FramesXmitGood); + + break; + + case OID_GEN_RCV_OK: + + GenericULong = (UINT)(Adapter->FramesRcvGood); + + break; + + case OID_GEN_XMIT_ERROR: + + GenericULong = (UINT)(Adapter->FramesXmitBad); + + break; + + case OID_GEN_RCV_ERROR: + + GenericULong = (UINT)(Adapter->CrcErrors); + + break; + + case OID_GEN_RCV_NO_BUFFER: + + GenericULong = (UINT)(Adapter->MissedPackets); + + break; + + case OID_802_3_RCV_ERROR_ALIGNMENT: + + GenericULong = (UINT)(Adapter->FrameAlignmentErrors); + + break; + + case OID_802_3_XMIT_ONE_COLLISION: + + GenericULong = (UINT)(Adapter->FramesXmitOneCollision); + + break; + + case OID_802_3_XMIT_MORE_COLLISIONS: + + GenericULong = (UINT)(Adapter->FramesXmitManyCollisions); + + break; + + + default: + + StatusToReturn = NDIS_STATUS_INVALID_OID; + + break; + + } + + NdisReleaseSpinLock(&Adapter->Lock); + + // + // Check to make sure there is enough room in the + // buffer to store the result. + // + + if (BytesLeft >= sizeof(ULONG)) { + + // + // Store the result. + // + + ELNKII_MOVE_MEM( + (PVOID)InfoBuffer, + (PVOID)(&GenericULong), + sizeof(ULONG) + ); + + BytesWritten += sizeof(ULONG); + + } + + } + + NdisRequest->DATA.QUERY_INFORMATION.BytesWritten = BytesWritten; + + NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded = BytesNeeded; + + return(StatusToReturn); +} + +NDIS_STATUS +ElnkiiQueryGlobalStatistics( + IN NDIS_HANDLE MacAdapterContext, + IN PNDIS_REQUEST NdisRequest + ) + +/*++ + +Routine Description: + + The ElnkiiQueryGlobalStatistics is used by the protocol to query + global information about the MAC. + +Arguments: + + MacAdapterContext - The value associated with the adapter that is being + opened when the MAC registered the adapter with NdisRegisterAdapter. + + NdisRequest - A structure which contains the request type (Query), + an array of operations to perform, and an array for holding + the results of the operations. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + + // + // General Algorithm: + // + // + // Check if a request is going to pend... + // If so, pend the entire operation. + // + // Else + // Fill in the request block. + // + // + + PELNKII_ADAPTER Adapter = (PELNKII_ADAPTER)(MacAdapterContext); + + NDIS_STATUS StatusToReturn = NDIS_STATUS_SUCCESS; + + // + // Check if a request is valid and going to pend... + // If so, pend the entire operation. + // + + + // + // Switch on request type + // + + switch (NdisRequest->DATA.QUERY_INFORMATION.Oid) { + case OID_GEN_SUPPORTED_LIST: + case OID_GEN_HARDWARE_STATUS: + case OID_GEN_MEDIA_SUPPORTED: + case OID_GEN_MEDIA_IN_USE: + case OID_GEN_MAXIMUM_LOOKAHEAD: + case OID_GEN_MAXIMUM_FRAME_SIZE: + case OID_GEN_MAXIMUM_TOTAL_SIZE: + case OID_GEN_MAC_OPTIONS: + case OID_GEN_PROTOCOL_OPTIONS: + case OID_GEN_LINK_SPEED: + case OID_GEN_TRANSMIT_BUFFER_SPACE: + case OID_GEN_RECEIVE_BUFFER_SPACE: + case OID_GEN_TRANSMIT_BLOCK_SIZE: + case OID_GEN_RECEIVE_BLOCK_SIZE: + case OID_GEN_VENDOR_ID: + case OID_GEN_VENDOR_DESCRIPTION: + case OID_GEN_DRIVER_VERSION: + case OID_GEN_CURRENT_PACKET_FILTER: + case OID_GEN_CURRENT_LOOKAHEAD: + case OID_802_3_PERMANENT_ADDRESS: + case OID_802_3_CURRENT_ADDRESS: + case OID_GEN_XMIT_OK: + case OID_GEN_RCV_OK: + case OID_GEN_XMIT_ERROR: + case OID_GEN_RCV_ERROR: + case OID_GEN_RCV_NO_BUFFER: + case OID_802_3_MULTICAST_LIST: + case OID_802_3_MAXIMUM_LIST_SIZE: + case OID_802_3_RCV_ERROR_ALIGNMENT: + case OID_802_3_XMIT_ONE_COLLISION: + case OID_802_3_XMIT_MORE_COLLISIONS: + + break; + + default: + + StatusToReturn = NDIS_STATUS_INVALID_OID; + + break; + } + + NdisInterlockedAddUlong(&Adapter->References, 1, &Adapter->Lock); + + if (StatusToReturn == NDIS_STATUS_SUCCESS) { + + StatusToReturn = ElnkiiFillInGlobalData(Adapter, NdisRequest); + + } + + NdisAcquireSpinLock(&Adapter->Lock); + + ELNKII_DO_DEFERRED(Adapter); + + return(StatusToReturn); +} + +VOID +ElnkiiUnload( + IN NDIS_HANDLE MacMacContext + ) + +/*++ + +Routine Description: + + ElnkiiUnload is called when the MAC is to unload itself. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS InitStatus; + + UNREFERENCED_PARAMETER(MacMacContext); + + NdisDeregisterMac( + &InitStatus, + ElnkiiMacBlock.NdisMacHandle + ); + + NdisFreeSpinLock(&ElnkiiMacBlock.SpinLock); + + NdisTerminateWrapper( + ElnkiiMacBlock.NdisWrapperHandle, + (PVOID) NULL + ); + + return; +} + +VOID +ElnkiiRemoveAdapter( + IN PVOID MacAdapterContext + ) +/*++ + +Routine Description: + + ElnkiiRemoveAdapter removes an adapter previously registered + with NdisRegisterAdapter. + +Arguments: + + MacAdapterContext - The context value that the MAC passed + to NdisRegisterAdapter; actually as pointer to an + ELNKII_ADAPTER. + +Return Value: + + None. + +--*/ +{ + PELNKII_ADAPTER Adapter; + + Adapter = PELNKII_ADAPTER_FROM_CONTEXT_HANDLE(MacAdapterContext); + + Adapter->Removed = TRUE; + + ASSERT(Adapter->OpenQueue == (PELNKII_OPEN)NULL); + + // + // There are no opens left, so remove ourselves. + // + + // + // Take us out of the AdapterQueue. + // + + NdisAcquireSpinLock(&ElnkiiMacBlock.SpinLock); + + if (ElnkiiMacBlock.AdapterQueue == Adapter) { + + ElnkiiMacBlock.AdapterQueue = Adapter->NextAdapter; + + } else { + + PELNKII_ADAPTER TmpAdaptP = ElnkiiMacBlock.AdapterQueue; + + while (TmpAdaptP->NextAdapter != Adapter) { + + TmpAdaptP = TmpAdaptP->NextAdapter; + + } + + TmpAdaptP->NextAdapter = TmpAdaptP->NextAdapter->NextAdapter; + } + + NdisReleaseSpinLock(&ElnkiiMacBlock.SpinLock); + + if (Adapter->MemMapped) { + + NdisUnmapIoSpace( + Adapter->NdisAdapterHandle, + Adapter->XmitStart, + 0x2000); + + } + + { + BOOLEAN Canceled; + NdisCancelTimer(&Adapter->WakeUpTimer, &Canceled); + + if (!Canceled) { + NdisStallExecution(500000); + } + } + + EthDeleteFilter(Adapter->FilterDB); + + NdisRemoveInterrupt(&Adapter->NdisInterrupt); + + NdisDeregisterAdapter(Adapter->NdisAdapterHandle); + + NdisFreeSpinLock(&Adapter->Lock); + + NdisFreeMemory(Adapter, sizeof(ELNKII_ADAPTER), 0); + +} + + + +NDIS_STATUS +ElnkiiReset( + IN NDIS_HANDLE MacBindingHandle + ) + +/*++ + +Routine Description: + + NDIS function. + +Arguments: + + See NDIS 3.0 spec. + +--*/ + +{ + PELNKII_OPEN OpenP = ((PELNKII_OPEN)MacBindingHandle); + PELNKII_OPEN TmpOpenP; + PELNKII_ADAPTER AdaptP = OpenP->Adapter; + NDIS_STATUS Status; + + + if (OpenP->Closing) { + + return(NDIS_STATUS_CLOSING); + + } + + NdisAcquireSpinLock(&AdaptP->Lock); + + // + // Ensure that the open does not close while in this function. + // + + OpenP->ReferenceCount++; + + AdaptP->References++; + + + // + // Check that nobody is resetting this adapter, block others. + // + + if (AdaptP->ResetInProgress) { + + --OpenP->ReferenceCount; + + AdaptP->References--; + + NdisReleaseSpinLock(&AdaptP->Lock); + + return NDIS_STATUS_RESET_IN_PROGRESS; + } + + // + // Indicate Reset Start + // + + TmpOpenP = AdaptP->OpenQueue; + + while (TmpOpenP != (PELNKII_OPEN)NULL) { + + PELNKII_OPEN NextOpen; + + AddRefWhileHoldingSpinLock(AdaptP, TmpOpenP); + + NdisReleaseSpinLock(&AdaptP->Lock); + + NdisIndicateStatus(TmpOpenP->NdisBindingContext, + NDIS_STATUS_RESET_START, + NULL, + 0 + ); + + NdisAcquireSpinLock(&AdaptP->Lock); + + NextOpen = TmpOpenP->NextOpen; + + TmpOpenP->ReferenceCount--; + + TmpOpenP = NextOpen; + } + + // + // Set Reset Flag + // + + AdaptP->ResetInProgress = TRUE; + AdaptP->NextResetStage = NONE; + + // + // Needed in case the reset pends somewhere along the line. + // + + AdaptP->ResetOpen = OpenP; + + NdisReleaseSpinLock(&AdaptP->Lock); + + // + // This will take things from here. + // + + Status = ElnkiiStage2Reset(AdaptP); + + NdisAcquireSpinLock(&AdaptP->Lock); + + --OpenP->ReferenceCount; + + ELNKII_DO_DEFERRED(AdaptP); + + return(Status); + +} + +NDIS_STATUS +ElnkiiStage2Reset( + PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + The second stage of a reset. + It removes all requests on the pend queue. + ElnkiiStage3Reset will be called when CurBufXmitting goes to -1. + +Arguments: + + AdaptP - The adapter being reset. + +Return Value: + + NDIS_STATUS_PENDING if the card is currently transmitting. + The result of ElnkiiStage3Reset otherwise. + +--*/ + +{ + NDIS_STATUS Status; + PELNKII_PEND_DATA Op; + PELNKII_OPEN TmpOpen; + + NdisAcquireSpinLock(&AdaptP->Lock); + + AdaptP->References++; + + // + // kill the pend queue. + // + + while (AdaptP->PendQueue != (PELNKII_PEND_DATA)NULL) { + + Op = AdaptP->PendQueue; + + AdaptP->PendQueue = Op->Next; + + TmpOpen = Op->Open; + + NdisReleaseSpinLock(&AdaptP->Lock); + + Status = NDIS_STATUS_REQUEST_ABORTED; + + if ((Op->RequestType != NdisRequestClose) && + (Op->RequestType != NdisRequestGeneric1)) { // Not a close Request + + NdisCompleteRequest(Op->Open->NdisBindingContext, + PNDIS_REQUEST_FROM_PELNKII_PEND_DATA(Op), + NDIS_STATUS_REQUEST_ABORTED); + + } + + // + // This will call NdisCompleteClose if necessary. + // + + NdisAcquireSpinLock(&AdaptP->Lock); + + --TmpOpen->ReferenceCount; + } + + if (AdaptP->CurBufXmitting != -1) { + + // + // ElnkiiHandleXmitComplete will call ElnkiiStage3Reset. + // + + AdaptP->NextResetStage = XMIT_STOPPED; + + AdaptP->References--; + + NdisReleaseSpinLock(&AdaptP->Lock); + + return NDIS_STATUS_PENDING; + + } + + AdaptP->References--; + + NdisReleaseSpinLock(&AdaptP->Lock); + + return ElnkiiStage3Reset(AdaptP); + +} + +NDIS_STATUS +ElnkiiStage3Reset( + PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + The third stage of a reset. When called, CurBufXmitting has + gone to -1. ElnkiiStage4Reset is called when call the + transmit buffers are emptied (i.e. any threads that were + filling them have finished). + +Arguments: + + AdaptP - The adapter being reset. + +Return Value: + + NDIS_STATUS_PENDING if there are still transmit buffers being filled. + The result of ElnkiiStage4Reset otherwise. + +--*/ + +{ + UINT i; + + NdisAcquireSpinLock(&AdaptP->Lock); + + AdaptP->References++; + + // + // Reset these for afterwards. + // + + AdaptP->NextBufToFill = 0; + + AdaptP->NextBufToXmit = 0; + + + // + // Make sure all buffer filling operations are done. + // + + for (i=0; iNumBuffers; i++) { + + if (AdaptP->BufferStatus[i] != EMPTY) { + + // + // ElnkiiSend or ElnkiiCopyAndSend will call ElnkiiStage4Reset. + // + + AdaptP->NextResetStage = BUFFERS_EMPTY; + + AdaptP->References--; + + NdisReleaseSpinLock(&AdaptP->Lock); + + return NDIS_STATUS_PENDING; + } + } + + AdaptP->References--; + + NdisReleaseSpinLock(&AdaptP->Lock); + + return ElnkiiStage4Reset(AdaptP); +} + +NDIS_STATUS +ElnkiiStage4Reset( + PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + The fourth stage of a reset. When called, the last transmit + buffer has been marked empty. At this point the reset can + proceed. + +Arguments: + + AdaptP - The adapter being reset. + +Return Value: + + NDIS_STATUS_SUCCESS if the reset of the card succeeds. + NDIS_STATUS_FAILURE otherwise. + +--*/ +{ + UINT i; + PNDIS_PACKET CurPacket; + PMAC_RESERVED Reserved; + PELNKII_OPEN TmpOpenP; + NDIS_STATUS Status; + + + // + // Complete any packets that are waiting in transmit buffers, + // but are not in the loopback queue. + // + + NdisAcquireSpinLock(&AdaptP->Lock); + + AdaptP->References++; + + for (i=0; iNumBuffers; i++) { + + if (AdaptP->Packets[i] != (PNDIS_PACKET)NULL) { + + Reserved = RESERVED(AdaptP->Packets[i]); + + NdisReleaseSpinLock(&AdaptP->Lock); + +#if DBG + ElnkiiSendsCompletedForReset++; +#endif + + TmpOpenP = Reserved->Open; + + NdisCompleteSend(Reserved->Open->NdisBindingContext, + AdaptP->Packets[i], + NDIS_STATUS_REQUEST_ABORTED); + + NdisAcquireSpinLock(&AdaptP->Lock); + + --TmpOpenP->ReferenceCount; + + AdaptP->Packets[i] = (PNDIS_PACKET)NULL; + } + } + + + // + // Kill any packets waiting in the transmit queue, + // but are not in the loopback queue. + // + + while ((CurPacket = AdaptP->XmitQueue) != (PNDIS_PACKET)NULL) { + + Reserved = RESERVED(CurPacket); + + AdaptP->XmitQueue = Reserved->NextPacket; + + NdisReleaseSpinLock(&AdaptP->Lock); + +#if DBG + ElnkiiSendsCompletedForReset++; +#endif + + TmpOpenP = Reserved->Open; + + NdisCompleteSend(Reserved->Open->NdisBindingContext, + CurPacket, + NDIS_STATUS_REQUEST_ABORTED); + + NdisAcquireSpinLock(&AdaptP->Lock); + + --TmpOpenP->ReferenceCount; + } + + + // + // Now kill everything in the loopback queue. + // + + while ((CurPacket = AdaptP->LoopbackQueue) != (PNDIS_PACKET)NULL) { + + Reserved = RESERVED(CurPacket); + + AdaptP->LoopbackQueue = Reserved->NextPacket; + + NdisReleaseSpinLock(&AdaptP->Lock); + +#if DBG + ElnkiiSendsCompletedForReset++; +#endif + + TmpOpenP = Reserved->Open; + + NdisCompleteSend(Reserved->Open->NdisBindingContext, + CurPacket, + NDIS_STATUS_REQUEST_ABORTED); + + NdisAcquireSpinLock(&AdaptP->Lock); + + --TmpOpenP->ReferenceCount; + } + + NdisReleaseSpinLock(&AdaptP->Lock); + + // + // Wait for packet reception to stop -- this might happen if we + // really blaze through the reset code before the ReceiveDpc gets + // a chance to run. + // + + while (AdaptP->ReceiveInProgress) { + + NdisStallExecution(10000); + } + + // + // Physically reset the card. + // + + AdaptP->NicInterruptMask = + IMR_RCV | IMR_XMIT | IMR_XMIT_ERR | IMR_OVERFLOW; + + Status = CardReset(AdaptP) ? NDIS_STATUS_SUCCESS : NDIS_STATUS_FAILURE; + + + // + // Set the "resetting" flag back to FALSE. + // + + NdisAcquireSpinLock(&AdaptP->Lock); + + AdaptP->ResetInProgress = FALSE; + + // + // Indicate the reset complete to all the protocols. + // + + + TmpOpenP = AdaptP->OpenQueue; + + while (TmpOpenP != (PELNKII_OPEN)NULL) { + + PELNKII_OPEN NextOpen; + + AddRefWhileHoldingSpinLock(AdaptP, TmpOpenP); + + NdisReleaseSpinLock(&AdaptP->Lock); + + if (Status != NDIS_STATUS_SUCCESS) { + + NdisIndicateStatus(TmpOpenP->NdisBindingContext, + NDIS_STATUS_CLOSED, + NULL, + 0 + ); + + } + + NdisIndicateStatus(TmpOpenP->NdisBindingContext, + NDIS_STATUS_RESET_END, + &Status, + sizeof(Status) + ); + + NdisIndicateStatusComplete(TmpOpenP->NdisBindingContext); + + NdisAcquireSpinLock(&AdaptP->Lock); + + NextOpen = TmpOpenP->NextOpen; + + TmpOpenP->ReferenceCount--; + + TmpOpenP = NextOpen; + } + + AdaptP->References--; + + NdisReleaseSpinLock(&AdaptP->Lock); + + return Status; +} + +VOID +ElnkiiResetStageDone( + PELNKII_ADAPTER AdaptP, + RESET_STAGE StageDone + ) + +/*++ + +Routine Description: + + Indicates that a stage in the reset is done. Called by + routines that the reset pended waiting for, to indicate + that they are done. A central clearing house for determining + what the next stage is and calling the appropriate routine. + If a stage completes before it is being pended on, then + StageDone will not equal AdaptP->NextResetStage and no + action will be taken. + +Arguments: + + AdaptP - The adapter being reset. + StageDone - The stage that was just completed. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS Status; + UINT i; + + + // + // Make sure this is the stage that was being waited on. + // + + if (AdaptP->NextResetStage != StageDone) { + return; + } + + + switch (StageDone) { + + case MULTICAST_RESET: + Status = ElnkiiStage2Reset(AdaptP); + break; + + case XMIT_STOPPED: + Status = ElnkiiStage3Reset(AdaptP); + break; + + case BUFFERS_EMPTY: + + // + // Only continue if this is the last buffer waited for. + // + + NdisAcquireSpinLock(&AdaptP->Lock); + + AdaptP->References++; + + for (i=0; iNumBuffers; i++) { + + if (AdaptP->BufferStatus[i] != EMPTY) { + + AdaptP->References--; + + NdisReleaseSpinLock(&AdaptP->Lock); + + return; + + } + + } + + AdaptP->References--; + + NdisReleaseSpinLock(&AdaptP->Lock); + + Status = ElnkiiStage4Reset(AdaptP); + + break; + + } + + if (Status != NDIS_STATUS_PENDING) { + + NdisCompleteReset( + AdaptP->ResetOpen->NdisBindingContext, + Status); + + NdisAcquireSpinLock(&AdaptP->Lock); + + --AdaptP->ResetOpen->ReferenceCount; + + } else { + + NdisAcquireSpinLock(&AdaptP->Lock); + + } + + ELNKII_DO_DEFERRED(AdaptP); +} + + +STATIC +NDIS_STATUS +ElnkiiChangeMulticastAddresses( + IN UINT OldFilterCount, + IN CHAR OldAddresses[][ETH_LENGTH_OF_ADDRESS], + IN UINT NewFilterCount, + IN CHAR NewAddresses[][ETH_LENGTH_OF_ADDRESS], + IN NDIS_HANDLE MacBindingHandle, + IN PNDIS_REQUEST NdisRequest, + IN BOOLEAN Set + ) + +/*++ + +Routine Description: + + Action routine that will get called when a particular filter + class is first used or last cleared. + + NOTE: This routine assumes that it is called with the lock + acquired. + +Arguments: + + + OldFilterCount - The number of addresses that used to be on the card. + + OldAddresses - A list of all the addresses that used to be on the card. + + NewFilterCount - The number of addresses that should now be on the card. + + NewAddresses - A list of addresses that should be put on the card. + + MacBindingHandle - The context value returned by the MAC when the + adapter was opened. In reality, it is a pointer to ELNKII_OPEN. + + NdisRequest - The request which submitted the filter change. + Must use when completing this request with the NdisCompleteRequest + service, if the MAC completes this request asynchronously. + + Set - If true the change resulted from a set, otherwise the + change resulted from a open closing. + +Return Value: + + None. + + +--*/ + +{ + + + PELNKII_ADAPTER Adapter = PELNKII_ADAPTER_FROM_BINDING_HANDLE(MacBindingHandle); + PELNKII_PEND_DATA PendOp = PELNKII_PEND_DATA_FROM_PNDIS_REQUEST(NdisRequest); + UINT Filter; + + // + // The open that made this request. + // + PELNKII_OPEN Open = PELNKII_OPEN_FROM_BINDING_HANDLE(MacBindingHandle); + + // + // Holds the status that should be returned to the filtering package. + // + NDIS_STATUS StatusOfAdd; + + UNREFERENCED_PARAMETER(OldFilterCount); + UNREFERENCED_PARAMETER(OldAddresses); + UNREFERENCED_PARAMETER(NewFilterCount); + UNREFERENCED_PARAMETER(NewAddresses); + + if (NdisRequest == NULL) { + + NdisRequest = &(Open->CloseAddressRequest); + PendOp = PELNKII_PEND_DATA_FROM_PNDIS_REQUEST(NdisRequest); + + } + + // + // Check to see if the device is already resetting. If it is + // then reject this add. + // + + if (Adapter->ResetInProgress) { + + StatusOfAdd = NDIS_STATUS_RESET_IN_PROGRESS; + + } + else + { + // + // Verify that the global filter is not all multicast + // or promiscuous modes. Otherwise adding a multicast + // address will reset the mode. + // + Filter = ETH_QUERY_FILTER_CLASSES(Adapter->FilterDB); + if ((Filter & NDIS_PACKET_TYPE_ALL_MULTICAST) || + (Filter & NDIS_PACKET_TYPE_PROMISCUOUS) + ) + { + return(NDIS_STATUS_SUCCESS); + } + + PendOp->Open = Open; + + // + // We need to add this to the hardware multicast filtering. + // So pend an operation to do it. + // + + // + // Add one to reference count, to be subtracted when the + // operation get completed. + // + + PendOp->Open->ReferenceCount++; + PendOp->RequestType = Set ? + NdisRequestGeneric3 : // Means SetMulticastAddresses + NdisRequestClose ; // Means CloseMulticast + PendOp->Next = NULL; + + + if (Adapter->PendQueue == (PELNKII_PEND_DATA)NULL) { + + Adapter->PendQueue = Adapter->PendQTail = PendOp; + + } else { + + Adapter->PendQTail->Next = PendOp; + Adapter->PendQTail = PendOp; + + } + + + StatusOfAdd = NDIS_STATUS_PENDING; + + } + + return StatusOfAdd; + +} + +STATIC +NDIS_STATUS +ElnkiiChangeFilterClasses( + IN UINT OldFilterClasses, + IN UINT NewFilterClasses, + IN NDIS_HANDLE MacBindingHandle, + IN PNDIS_REQUEST NdisRequest, + IN BOOLEAN Set + ) + +/*++ + +Routine Description: + + Action routine that will get called when an address is added to + the filter that wasn't referenced by any other open binding. + + NOTE: This routine assumes that it is called with the lock + acquired. + +Arguments: + + OldFilterClasses - A bit mask that is currently on the card telling + which packet types to accept. + + NewFilterClasses - A bit mask that should be put on the card telling + which packet types to accept. + + MacBindingHandle - The context value returned by the MAC when the + adapter was opened. In reality, it is a pointer to ELNKII_OPEN. + + NdisRequest - The NDIS_REQUEST which submitted the filter change command. + + Set - A flag telling if the command is a result of a close or not. + +Return Value: + + Status of the change (successful or pending). + + +--*/ + +{ + + PELNKII_ADAPTER Adapter = PELNKII_ADAPTER_FROM_BINDING_HANDLE(MacBindingHandle); + PELNKII_PEND_DATA PendOp = PELNKII_PEND_DATA_FROM_PNDIS_REQUEST(NdisRequest); + + // + // The open that made this request. + // + PELNKII_OPEN Open = PELNKII_OPEN_FROM_BINDING_HANDLE(MacBindingHandle); + + // + // Holds the status that should be returned to the filtering package. + // + NDIS_STATUS StatusOfAdd; + + + UNREFERENCED_PARAMETER(OldFilterClasses); + UNREFERENCED_PARAMETER(NewFilterClasses); + + if (NdisRequest == NULL) { + + NdisRequest = &(Open->CloseFilterRequest); + PendOp = PELNKII_PEND_DATA_FROM_PNDIS_REQUEST(NdisRequest); + + } + + // + // Check to see if the device is already resetting. If it is + // then reject this add. + // + + if (Adapter->ResetInProgress) { + + StatusOfAdd = NDIS_STATUS_RESET_IN_PROGRESS; + + } else { + + PendOp->Open = Open; + + // + // We need to add this to the hardware multicast filtering. + // So queue a request. + // + + PendOp->Open->ReferenceCount++; + PendOp->RequestType = Set ? + NdisRequestGeneric2 : // Means SetPacketFilter + NdisRequestGeneric1 ; // Means CloseFilter + PendOp->Next = NULL; + + if (Adapter->PendQueue == (PELNKII_PEND_DATA)NULL) { + + Adapter->PendQueue = Adapter->PendQTail = PendOp; + + } else { + + Adapter->PendQTail->Next = PendOp; + Adapter->PendQTail = PendOp; + + } + + StatusOfAdd = NDIS_STATUS_PENDING; + + } + + return StatusOfAdd; + +} + +STATIC +VOID +ElnkiiCloseAction( + IN NDIS_HANDLE MacBindingHandle + ) + +/*++ + +Routine Description: + + Action routine that will get called when a particular binding + was closed while it was indicating through NdisIndicateReceive + + All this routine needs to do is to decrement the reference count + of the binding. + + NOTE: This routine assumes that it is called with the lock acquired. + +Arguments: + + MacBindingHandle - The context value returned by the MAC when the + adapter was opened. In reality, it is a pointer to ELNKII_OPEN. + +Return Value: + + None. + + +--*/ + +{ + + PELNKII_OPEN_FROM_BINDING_HANDLE(MacBindingHandle)->ReferenceCount--; + +} diff --git a/private/ntos/ndis/elnkii/elnkii.rc b/private/ntos/ndis/elnkii/elnkii.rc new file mode 100644 index 000000000..73b14edf9 --- /dev/null +++ b/private/ntos/ndis/elnkii/elnkii.rc @@ -0,0 +1,39 @@ +#include +#include + +/*-----------------------------------------------*/ +/* the following lines are specific to this file */ +/*-----------------------------------------------*/ + +/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR + * and VER_INTERNALNAME_STR must be defined before including COMMON.VER + * The strings don't need a '\0', since common.ver has them. + */ +#define VER_FILETYPE VFT_DRV +/* possible values: VFT_UNKNOWN + VFT_APP + VFT_DLL + VFT_DRV + VFT_FONT + VFT_VXD + VFT_STATIC_LIB +*/ +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +/* possible values VFT2_UNKNOWN + VFT2_DRV_PRINTER + VFT2_DRV_KEYBOARD + VFT2_DRV_LANGUAGE + VFT2_DRV_DISPLAY + VFT2_DRV_MOUSE + VFT2_DRV_NETWORK + VFT2_DRV_SYSTEM + VFT2_DRV_INSTALLABLE + VFT2_DRV_SOUND + VFT2_DRV_COMM +*/ +#define VER_FILEDESCRIPTION_STR "3Com Etherlink II and II/16 network driver" +#define VER_INTERNALNAME_STR "ELNKII.SYS" +#define VER_ORIGINALFILENAME_STR "ELNKII.SYS" + +#include "common.ver" + diff --git a/private/ntos/ndis/elnkii/elnksft.h b/private/ntos/ndis/elnkii/elnksft.h new file mode 100644 index 000000000..8715159c6 --- /dev/null +++ b/private/ntos/ndis/elnkii/elnksft.h @@ -0,0 +1,1295 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + elnksft.h + +Abstract: + + The main header for an Etherlink II MAC driver. + +Author: + + Anthony V. Ercolano (tonye) creation-date 19-Jun-1990 (Driver Model) + + Adam Barr (adamba) - original Elnkii code. + +Environment: + + This driver is expected to work in DOS, OS2 and NT at the equivalent + of kernal mode. + + Architecturally, there is an assumption in this driver that we are + on a little endian machine. + +Notes: + + optional-notes + +Revision History: + + Dec-1991 by Sean Selitrennikoff - Fit AdamBa's code into TonyE's model + + +--*/ + +#ifndef _ELNKIISFT_ +#define _ELNKIISFT_ + +#define ELNKII_NDIS_MAJOR_VERSION 3 +#define ELNKII_NDIS_MINOR_VERSION 0 + +// +// This macro is used along with the flags to selectively +// turn on debugging. +// + +#if DBG + +#define IF_ELNKIIDEBUG(f) if (ElnkiiDebugFlag & (f)) +extern ULONG ElnkiiDebugFlag; + +#define ELNKII_DEBUG_LOUD 0x00000001 // debugging info +#define ELNKII_DEBUG_VERY_LOUD 0x00000002 // excessive debugging info +#define ELNKII_DEBUG_LOG 0x00000004 // enable ElnkiiLog +#define ELNKII_DEBUG_CHECK_DUP_SENDS 0x00000008 // check for duplicate sends +#define ELNKII_DEBUG_TRACK_PACKET_LENS 0x00000010 // track directed packet lens +#define ELNKII_DEBUG_WORKAROUND1 0x00000020 // drop DFR/DIS packets +#define ELNKII_DEBUG_CARD_BAD 0x00000040 // dump data if CARD_BAD +#define ELNKII_DEBUG_CARD_TESTS 0x00000080 // print reason for failing + + + +// +// Macro for deciding whether to dump lots of debugging information. +// + +#define IF_LOUD(A) IF_ELNKIIDEBUG( ELNKII_DEBUG_LOUD ) { A } +#define IF_VERY_LOUD(A) IF_ELNKIIDEBUG( ELNKII_DEBUG_VERY_LOUD ) { A } + + +#else + +#define IF_LOUD(A) +#define IF_VERY_LOUD(A) + +#endif + + +// +// Whether to use the ElnkiiLog +// + +#if DBG + +#define IF_LOG(A) IF_ELNKIIDEBUG( ELNKII_DEBUG_LOG ) { A } +extern VOID ElnkiiLog(UCHAR); + +#else + +#define IF_LOG(A) + +#endif + + +// +// Whether to do loud init failure +// + +#if DBG +#define IF_INIT(A) A +#else +#define IF_INIT(A) +#endif + + +// +// Whether to do loud card test failures +// + +#if DBG +#define IF_TEST(A) IF_ELNKIIDEBUG( ELNKII_DEBUG_CARD_TESTS ) { A } +#else +#define IF_TEST(A) +#endif + + + + +// +// Macros for services that differ between DOS and NT, we may consider adding these +// into the NDIS spec. +// + + +// +// AdaptP->NumBuffers +// +// controls the number of transmit buffers on the packet. +// Choices are 1 or 2. +// + +#define DEFAULT_NUMBUFFERS 2 + + +#define ELNKII_MOVE_MEM_TO_SHARED_RAM(dest,src,size) \ + NdisMoveToMappedMemory(dest, src, size) + +#define ELNKII_MOVE_SHARED_RAM_TO_MEM(dest,src,size) \ + NdisMoveFromMappedMemory(dest, src, size) + + + +#define ELNKII_MOVE_MEM(dest,src,size) NdisMoveMemory(dest,src,size) + + + + + + +// +// A broadcast address (for comparing with other addresses). +// + +extern UCHAR ElnkiiBroadcastAddress[]; + + +// +// The status of transmit buffers. +// + +typedef enum { EMPTY, FILLING, FULL } BUFFER_STATUS; + + +// +// Type of an interrupt. +// + +typedef enum { RECEIVE = 0x01, + TRANSMIT = 0x02, + OVERFLOW = 0x04, + COUNTER = 0x08, + UNKNOWN = 0x10} INTERRUPT_TYPE; + +// +// Result of ElnkiiIndicate[Loopback]Packet(). +// + +typedef enum { INDICATE_OK, SKIPPED, ABORT, CARD_BAD } INDICATE_STATUS; + + +// +// Stages in a reset. +// + +typedef enum { NONE, MULTICAST_RESET, XMIT_STOPPED, BUFFERS_EMPTY } RESET_STAGE; + + + +// +// Number of bytes in an ethernet header +// + +#define ELNKII_HEADER_SIZE 14 + +// +// Number of bytes allowed in a lookahead (max) +// + +#define ELNKII_MAX_LOOKAHEAD (252 - ELNKII_HEADER_SIZE) + + + +// +// Maximum number of transmit buffers on the card. +// + +#define MAX_XMIT_BUFS 2 + + +// +// A transmit buffer (usually 0 or 1). +// + +typedef SHORT XMIT_BUF; + + +// +// Number of 256-byte buffers in a transmit buffer. +// + +#define BUFS_PER_TX 6 + + +// +// Size of a single transmit buffer. +// + +#define TX_BUF_SIZE (BUFS_PER_TX*256) + + + +// +// Only have one of these structures. +// + +typedef struct _MAC_BLOCK { + + // + // NDIS wrapper information. + // + + NDIS_HANDLE NdisMacHandle; // returned from NdisRegisterMac + NDIS_HANDLE NdisWrapperHandle; // returned from NdisInitializeWrapper + NDIS_MAC_CHARACTERISTICS MacCharacteristics; + + // + // Adapters registered for this MAC. + // + + struct _ELNKII_ADAPTER * AdapterQueue; + NDIS_SPIN_LOCK SpinLock; // guards NumAdapter and AdapterQueue + + PDRIVER_OBJECT DriverObject; + + BOOLEAN Unloading; + +} MAC_BLOCK, * PMAC_BLOCK; + + +// +// Used to contain a queued operation. +// + +typedef struct _ELNKII_PEND_DATA { + struct _ELNKII_PEND_DATA * Next; + struct _ELNKII_OPEN * Open; + NDIS_REQUEST_TYPE RequestType; +} ELNKII_PEND_DATA, * PELNKII_PEND_DATA; + +// +// This macro will return a pointer to the reserved area of +// a PNDIS_REQUEST. +// +#define PELNKII_PEND_DATA_FROM_PNDIS_REQUEST(Request) \ + ((PELNKII_PEND_DATA)((PVOID)((Request)->MacReserved))) + +// +// This macros returns the enclosing NdisRequest. +// +#define PNDIS_REQUEST_FROM_PELNKII_PEND_DATA(PendOp)\ + ((PNDIS_REQUEST)((PVOID)(PendOp))) + + + + + +// +// One of these structures per adapter registered. +// + +typedef struct _ELNKII_ADAPTER { + + // + // Spin lock for adapter structure + // + NDIS_SPIN_LOCK Lock; + + + // + // NDIS wrapper information. + // + + NDIS_HANDLE NdisAdapterHandle; // returned from NdisRegisterAdapter + NDIS_INTERRUPT NdisInterrupt; // interrupt info used by wrapper + + // + // Links with our MAC. + // + + PMAC_BLOCK MacBlock; + struct _ELNKII_ADAPTER * NextAdapter; // used by MacBlock->AdapterQueue + + // + // Opens for this adapter. + // + + struct _ELNKII_OPEN * OpenQueue; + + // + // Opens for this adapter that are waiting for closes to finish. + // + + struct _ELNKII_OPEN * CloseQueue; + + // + // Number of references to the adapter. + // + ULONG References; + + ULONG ReceivePacketCount; + + // + // Configuration information + // + + UINT NumBuffers; + PVOID IoBaseAddr; + PVOID MemBaseAddr; // actually read off the card + UINT MaxOpens; + CHAR InterruptNumber; + BOOLEAN ExternalTransceiver; + BOOLEAN MemMapped; // actually read off the card + BOOLEAN InCardTest; + UINT MulticastListMax; + PUCHAR MappedIoBaseAddr; + PUCHAR MappedGaBaseAddr; + + // + // InterruptReg tracks interrupt sources that still need to be serviced, + // it is the logical OR of all card interrupts that have been received and not + // processed and cleared. (see also INTERRUPT_TYPE definition in elnkii.h) + // + UINT InterruptReg; + + BOOLEAN ElnkiiHandleXmitCompleteRunning; + UCHAR TimeoutCount; + + // + // Transmit queue. + // + + PNDIS_PACKET XmitQueue; // packets waiting to be transmitted + PNDIS_PACKET XmitQTail; + + // + // Transmit information. + // + + XMIT_BUF NextBufToFill; // where to copy next packet to + XMIT_BUF NextBufToXmit; // valid if CurBufXmitting is -1 + XMIT_BUF CurBufXmitting; // -1 if none is + BOOLEAN TransmitInterruptPending; // transmitting, but DPC not yet queued + BOOLEAN OverflowRestartXmitDpc; // transmitting, but DPC not yet queued + BUFFER_STATUS BufferStatus[MAX_XMIT_BUFS]; + PNDIS_PACKET Packets[MAX_XMIT_BUFS]; // as passed to MacSend + UINT PacketLens[MAX_XMIT_BUFS]; + PUCHAR XmitStart; // start of card transmit area + PUCHAR PageStart; // start of card receive area + PUCHAR PageStop; // end of card receive area + UCHAR NicXmitStart; // MSB, LSB assumed 0 + UCHAR NicPageStart; // MSB, LSB assumed 0 + UCHAR NicPageStop; // MSB, LSB assumed 0 + UCHAR GaControlBits; // values for xsel and dbsel bits + + // + // Receive information + // + + UCHAR NicNextPacket; // MSB, LSB assumed 0 + UCHAR Current; // MSB, LSB assumed 0 (last known value) + UCHAR XmitStatus; // status of last transmit + + // + // These are for the current packet being indicated. + // + + UCHAR PacketHeader[4]; // the NIC appended header + UCHAR Lookahead[252]; // the first 252 bytes of the packet + UINT PacketLen; // the overall length of the packet + + // + // Operational information. + // + + UCHAR StationAddress[ETH_LENGTH_OF_ADDRESS]; // filled in at init time + UCHAR PermanentAddress[ETH_LENGTH_OF_ADDRESS]; // filled in at init time + BOOLEAN BufferOverflow; // does an overflow need to be handled + BOOLEAN ReceiveInProgress; // to prevent reentering indications + + // + // Statistics used by Set/QueryInformation. + // + + ULONG FramesXmitGood; // Good Frames Transmitted + ULONG FramesRcvGood; // Good Frames Received + ULONG FramesXmitBad; // Bad Frames Transmitted + ULONG FramesXmitOneCollision; // Frames Transmitted with one collision + ULONG FramesXmitManyCollisions; // Frames Transmitted with > 1 collision + ULONG FrameAlignmentErrors; // FAE errors counted + ULONG CrcErrors; // CRC errors counted + ULONG MissedPackets; // missed packet counted + + // + // Reset information. + // + + BOOLEAN ResetInProgress; // TRUE during a reset + RESET_STAGE NextResetStage; // where in the reset we are + struct _ELNKII_OPEN * ResetOpen; // who called ElnkiiReset + + + + // + // Pointer to the filter database for the MAC. + // + PETH_FILTER FilterDB; + + UCHAR NicMulticastRegs[8]; // contents of card MC registers + UINT ByteToWrite; // temp storage + + UCHAR NicReceiveConfig; // contents of NIC RCR + UCHAR NicInterruptMask; // contents of NIC IMR + + + // + // Look Ahead information. + // + + ULONG MaxLookAhead; + + + // + // Loopback information + // + + PNDIS_PACKET LoopbackQueue; // queue of packets to loop back + PNDIS_PACKET LoopbackQTail; + PNDIS_PACKET LoopbackPacket; // current one we are looping back + + // + // Pending operations + // + + PELNKII_PEND_DATA PendQueue; // List of operations to complete + PELNKII_PEND_DATA PendQTail; + PELNKII_PEND_DATA PendOp; // Outstanding operation + + + NDIS_TIMER DeferredTimer; + PVOID DeferredDpc; + + NDIS_TIMER InterruptTimer; // handles hung transmit and loopbacks to self + + PVOID WakeUpDpc; + NDIS_TIMER WakeUpTimer; + BOOLEAN WakeUpFoundTransmit; + + BOOLEAN Removed; + +} ELNKII_ADAPTER, * PELNKII_ADAPTER; + + + + +// +// Given a MacBindingHandle this macro returns a pointer to the +// ELNKII_ADAPTER. +// +#define PELNKII_ADAPTER_FROM_BINDING_HANDLE(Handle) \ + (((PELNKII_OPEN)(Handle))->Adapter) + +// +// Given a MacContextHandle return the PELNKII_ADAPTER +// it represents. +// +#define PELNKII_ADAPTER_FROM_CONTEXT_HANDLE(Handle) \ + ((PELNKII_ADAPTER)(Handle)) + +// +// Given a pointer to a ELNKII_ADAPTER return the +// proper MacContextHandle. +// +#define CONTEXT_HANDLE_FROM_PELNKII_ADAPTER(Ptr) \ + ((NDIS_HANDLE)(Ptr)) + + + + +// +// Macros to extract high and low bytes of a word. +// + +#define MSB(Value) ((UCHAR)(((Value) >> 8) & 0xff)) +#define LSB(Value) ((UCHAR)((Value) & 0xff)) + + +// +// One of these per open on an adapter. +// + +typedef struct _ELNKII_OPEN { + + // + // NDIS wrapper information. + // + + NDIS_HANDLE NdisBindingContext; // passed to MacOpenAdapter + PSTRING AddressingInformation; // not used currently + + // + // Links to our adapter. + // + + PELNKII_ADAPTER Adapter; + struct _ELNKII_OPEN * NextOpen; + + // + // Links to our MAC. + // + + PMAC_BLOCK MacBlock; // faster than using AdapterBlock->MacBlock + + + // + // Index of this adapter in the filter database. + // + NDIS_HANDLE NdisFilterHandle; + + // + // Indication information + // + + UINT LookAhead; + + // + // Reset/Close information. + // + + UINT ReferenceCount; // number of reasons this open can't close + BOOLEAN Closing; // is a close pending + + NDIS_REQUEST CloseFilterRequest; // Holds Requests for pending close op + NDIS_REQUEST CloseAddressRequest;// Holds Requests for pending close op + + UINT ProtOptionFlags; + +} ELNKII_OPEN, * PELNKII_OPEN; + + +// +// This macro returns a pointer to a PELNKII_OPEN given a MacBindingHandle. +// +#define PELNKII_OPEN_FROM_BINDING_HANDLE(Handle) \ + ((PELNKII_OPEN)(Handle)) + +// +// This macro returns a NDIS_HANDLE from a PELNKII_OPEN +// +#define BINDING_HANDLE_FROM_PELNKII_OPEN(Open) \ + ((NDIS_HANDLE)(Open)) + + + + + + +typedef struct _ELNKII_REQUEST_RESERVED { + PNDIS_REQUEST Next; // Next NDIS_REQUEST in chain for this binding + ULONG OidsLeft; // Number of Oids left to process + PUCHAR BufferPointer; // Next available byte in information buffer +} ELNKII_REQUEST_RESERVED, *PELNKII_REQUEST_RESERVED; + + + +// A MACRO to return a pointer to the reserved portion of an NDIS request +#define PELNKII_RESERVED_FROM_REQUEST(Request) \ + ((PELNKII_REQUEST_RESERVED)((Request)->MacReserved) + + +// +// What we map into the reserved section of a packet. +// Cannot be more than 16 bytes (see ASSERT in elnkii.c). +// + +typedef struct _MAC_RESERVED { + PNDIS_PACKET NextPacket; // used to link in the queues (4 bytes) + PELNKII_OPEN Open; // open that called ElnkiiSend (4 bytes) + BOOLEAN Loopback; // is this a loopback packet (1 byte) +} MAC_RESERVED, * PMAC_RESERVED; + + +// +// These appear in the status field of MAC_RESERVED; they are +// used because there is not enough room for a full NDIS_HANDLE. +// + +#define RESERVED_SUCCESS ((USHORT)0) +#define RESERVED_FAILURE ((USHORT)1) + + +// +// Retrieve the MAC_RESERVED structure from a packet. +// + +#define RESERVED(Packet) ((PMAC_RESERVED)((Packet)->MacReserved)) + + +// +// Procedures which log errors. +// + +typedef enum _ELNKII_PROC_ID { + openAdapter, + cardReset, + cardCopyDownPacket, + cardCopyDownBuffer, + cardCopyUp +} ELNKII_PROC_ID; + + +#define ELNKII_ERRMSG_CARD_SETUP (ULONG)0x01 +#define ELNKII_ERRMSG_DATA_PORT_READY (ULONG)0x02 +#define ELNKII_ERRMSG_MAX_OPENS (ULONG)0x03 +#define ELNKII_ERRMSG_HANDLE_XMIT_COMPLETE (ULONG)0x04 + + +//++ +// +// XMIT_BUF +// NextBuf( +// IN PELNKII_ADAPTER AdaptP, +// IN XMIT_BUF XmitBuf +// ) +// +// Routine Description: +// +// NextBuf "increments" a transmit buffer number. The next +// buffer is returned; the number goes back to 0 when it +// reaches AdaptP->NumBuffers. +// +// Arguments: +// +// AdaptP - The adapter block. +// XmitBuf - The current transmit buffer number. +// +// Return Value: +// +// The next transmit buffer number. +// +//-- + +#define NextBuf(AdaptP, XmitBuf) \ + ((XMIT_BUF)(((XmitBuf)+1)%(AdaptP)->NumBuffers)) + + + + + + + +// +// This macro will act a "epilogue" to every routine in the +// *interface*. It will check whether any requests need +// to defer their processing. It will also decrement the reference +// count on the adapter. If the reference count is zero and there +// is deferred work to do it will insert the interrupt processing +// routine in the DPC queue. +// +// Note that we don't need to include checking for blocked receives +// since blocked receives imply that there will eventually be an +// interrupt. +// +// NOTE: This macro assumes that it is called with the lock acquired. +// +// +#define ELNKII_DO_DEFERRED(Adapter) \ +{ \ + PELNKII_ADAPTER _A = (Adapter); \ + _A->References--; \ + if ((!_A->References) && \ + (_A->ResetInProgress || \ + (_A->PendQueue != NULL) || \ + (_A->CloseQueue != NULL))) {\ + NdisReleaseSpinLock(&_A->Lock); \ + NdisSetTimer(&_A->DeferredTimer, 1);\ + } else if ((_A->XmitQueue != NULL) && \ + (_A->BufferStatus[_A->NextBufToFill] == EMPTY)) {\ + ElnkiiCopyAndSend(_A); \ + NdisReleaseSpinLock(&_A->Lock); \ + } else { \ + NdisReleaseSpinLock(&_A->Lock); \ + } \ +} + + + + +// +// Declarations for functions in elnkii.c. +// + +NDIS_STATUS +ElnkiiRegisterAdapter( + IN PELNKII_ADAPTER NewAdaptP, + IN NDIS_HANDLE ConfigurationHandle, + IN PNDIS_STRING AdapterName, + IN BOOLEAN ConfigError, + IN ULONG ConfigErrorValue + ); + + +BOOLEAN +ElnkiiInterruptHandler( + IN PVOID ServiceContext // will be a pointer to the adapter block + ); + +VOID +ElnkiiInterruptDpc( + IN PVOID SystemSpecific1, + IN PVOID InterruptContext, + IN PVOID SystemSpecific2, + IN PVOID SystemSpecific3 + ); + +VOID +ElnkiiXmitInterruptDpc( + IN PELNKII_ADAPTER AdaptP + ); + + +BOOLEAN +ElnkiiRcvInterruptDpc( + IN PELNKII_ADAPTER AdaptP + ); + +VOID +ElnkiiWakeUpDpc( + IN PVOID SystemSpecific1, + IN PVOID InterruptContext, + IN PVOID SystemSpecific2, + IN PVOID SystemSpecific3 + ); + +NDIS_STATUS +ElnkiiOpenAdapter( + OUT PNDIS_STATUS OpenErrorStatus, + OUT NDIS_HANDLE * MacBindingHandle, + OUT PUINT SelectedMediumIndex, + IN PNDIS_MEDIUM MediumArray, + IN UINT MediumArraySize, + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_HANDLE MacAdapterContext, + IN UINT OpenOptions, + IN PSTRING AddressingInformation OPTIONAL + ); + + +NDIS_STATUS +ElnkiiCloseAdapter( + IN NDIS_HANDLE MacBindingHandle + ); + +VOID +ElnkiiAdjustMaxLookAhead( + IN PELNKII_ADAPTER Adapter + ); + +BOOLEAN +ElnkiiAddReference( + IN PELNKII_OPEN OpenP + ); + +NDIS_STATUS +ElnkiiReset( + IN NDIS_HANDLE MacBindingHandle + ); + +NDIS_STATUS +ElnkiiRequest( + IN NDIS_HANDLE MacBindingHandle, + IN PNDIS_REQUEST NdisRequest + ); + +NDIS_STATUS +ElnkiiQueryInformation( + IN PELNKII_ADAPTER Adapter, + IN PELNKII_OPEN Open, + IN PNDIS_REQUEST NdisRequest + ); + +NDIS_STATUS +ElnkiiSetInformation( + IN PELNKII_ADAPTER Adapter, + IN PELNKII_OPEN Open, + IN PNDIS_REQUEST NdisRequest + ); + +NDIS_STATUS +ElnkiiSetMulticastAddresses( + IN PELNKII_ADAPTER Adapter, + IN PELNKII_OPEN Open, + IN PNDIS_REQUEST NdisRequest, + IN UINT NumAddresses, + IN CHAR AddressList[][ETH_LENGTH_OF_ADDRESS] + ); + +NDIS_STATUS +ElnkiiSetPacketFilter( + IN PELNKII_ADAPTER Adapter, + IN PELNKII_OPEN Open, + IN PNDIS_REQUEST NdisRequest, + IN UINT PacketFilter + ); + +NDIS_STATUS +ElnkiiQueryGlobalStatistics( + IN NDIS_HANDLE MacBindingHandle, + IN PNDIS_REQUEST NdisRequest + ); + +VOID +ElnkiiUnload( + IN NDIS_HANDLE MacMacContext + ); + +NDIS_STATUS +ElnkiiAddAdapter( + IN NDIS_HANDLE NdisMacContext, + IN NDIS_HANDLE ConfigurationHandle, + IN PNDIS_STRING AdapterName + ); + +VOID +ElnkiiRemoveAdapter( + IN PVOID MacAdapterContext + ); + +VOID +ElnkiiInterruptDpc( + IN PVOID SystemSpecific1, + IN PVOID InterruptContext, + IN PVOID SystemSpecific2, + IN PVOID SystemSpecific3 + ); + +NDIS_STATUS +ElnkiiStage1Reset( + PELNKII_ADAPTER AdaptP + ); + + +NDIS_STATUS +ElnkiiStage2Reset( + PELNKII_ADAPTER AdaptP + ); + + +NDIS_STATUS +ElnkiiStage3Reset( + PELNKII_ADAPTER AdaptP + ); + + +NDIS_STATUS +ElnkiiStage4Reset( + PELNKII_ADAPTER AdaptP + ); + + +VOID +ElnkiiResetStageDone( + PELNKII_ADAPTER AdaptP, + RESET_STAGE StageDone + ); + + +NDIS_STATUS +ElnkiiChangeMulticastAddresses( + IN UINT OldFilterCount, + IN CHAR OldAddresses[][ETH_LENGTH_OF_ADDRESS], + IN UINT NewFilterCount, + IN CHAR NewAddresses[][ETH_LENGTH_OF_ADDRESS], + IN NDIS_HANDLE MacBindingHandle, + IN PNDIS_REQUEST NdisRequest, + IN BOOLEAN Set + ); + +NDIS_STATUS +ElnkiiChangeFilterClasses( + IN UINT OldFilterClasses, + IN UINT NewFilterClasses, + IN NDIS_HANDLE MacBindingHandle, + IN PNDIS_REQUEST NdisRequest, + IN BOOLEAN Set + ); + + +VOID +ElnkiiCloseAction( + IN NDIS_HANDLE MacBindingHandle + ); + + +// +// functions in interrup.c +// + +INDICATE_STATUS +ElnkiiIndicateLoopbackPacket( + IN PELNKII_ADAPTER AdaptP, + IN PNDIS_PACKET Packet + ); + +UINT +ElnkiiCopyOver( + OUT PUCHAR Buf, // destination + IN PNDIS_PACKET Packet, // source packet + IN UINT Offset, // offset in packet + IN UINT Length // number of bytes to copy + ); + + +INDICATE_STATUS +ElnkiiIndicatePacket( + IN PELNKII_ADAPTER AdaptP + ); + + +NDIS_STATUS +ElnkiiTransferData( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ); + +// +// Declarations for functions in pend.c +// + +VOID +HandlePendingOperations( + IN PVOID SystemSpecific1, + IN PVOID DeferredContext, + IN PVOID SystemSpecific2, + IN PVOID SystemSpecific3 + ); + +NDIS_STATUS +DispatchSetPacketFilter( + IN PELNKII_ADAPTER AdaptP + ); + + +NDIS_STATUS +DispatchSetMulticastAddressList( + IN PELNKII_ADAPTER AdaptP + ); + + +// +// Declarations for functions in send.c. +// + + +NDIS_STATUS +ElnkiiSend( + IN NDIS_HANDLE MacBindingHandle, + IN PNDIS_PACKET Packet + ); + +VOID +ElnkiiCopyAndSend( + IN PELNKII_ADAPTER AdaptP + ); + + + + + +//++ +// +// VOID +// AddRefWhileHoldingSpinLock( +// IN PELNKII_ADAPTER AdaptP, +// IN PELNKII_OPEN OpenP +// ) +// +// Routine Description: +// +// Adds a reference to an open. Similar to AddReference, but +// called with AdaptP->Lock held. +// +// Arguments: +// +// AdaptP - The adapter block of OpenP. +// OpenP - The open block that is being referenced. +// +// Return Value: +// +// None. +// +//-- + +#define AddRefWhileHoldingSpinLock(AdaptP, OpenP) { \ + ++((OpenP)->ReferenceCount); \ +} + +// +// Declarations of functions in card.c. +// + + +PUCHAR +CardGetMemBaseAddr( + IN PELNKII_ADAPTER AdaptP, + OUT PBOOLEAN CardPresent, + OUT PBOOLEAN IoBaseCorrect + ); + + +VOID +CardReadEthernetAddress( + IN PELNKII_ADAPTER AdaptP + ); + + +BOOLEAN +CardSetup( + IN PELNKII_ADAPTER AdaptP + ); + + +VOID +CardStop( + IN PELNKII_ADAPTER AdaptP + ); + + +BOOLEAN +CardTest( + IN PELNKII_ADAPTER AdaptP + ); + + +BOOLEAN +CardReset( + IN PELNKII_ADAPTER AdaptP + ); + + +BOOLEAN +CardCopyDownPacket( + IN PELNKII_ADAPTER AdaptP, + IN PNDIS_PACKET Packet, + IN XMIT_BUF XmitBufferNum, + OUT UINT * Length + ); + + +BOOLEAN +CardCopyDownBuffer( + IN PELNKII_ADAPTER AdaptP, + IN PUCHAR SourceBuffer, + IN XMIT_BUF XmitBufferNum, + IN UINT Offset, + IN UINT Length + ); + + +BOOLEAN +CardCopyUp( + IN PELNKII_ADAPTER AdaptP, + IN PUCHAR Target, + IN PUCHAR Source, + IN UINT Length + ); + + +ULONG +CardComputeCrc( + IN PUCHAR Buffer, + IN UINT Length + ); + + +VOID +CardGetPacketCrc( + IN PUCHAR Buffer, + IN UINT Length, + OUT UCHAR Crc[4] + ); + + +VOID +CardGetMulticastBit( + IN UCHAR Address[ETH_LENGTH_OF_ADDRESS], + OUT UCHAR * Byte, + OUT UCHAR * Value + ); + + +VOID +CardFillMulticastRegs( + IN PELNKII_ADAPTER AdaptP + ); + + + +VOID +CardSetBoundary( + IN PELNKII_ADAPTER AdaptP + ); + + +VOID +CardStartXmit( + IN PELNKII_ADAPTER AdaptP + ); + + + +// +// These are the functions that are defined in sync.c and +// are meant to be called through NdisSynchronizeWithInterrupt(). +// + + +BOOLEAN +SyncCardStop( + IN PVOID SynchronizeContext + ); + + +BOOLEAN +SyncCardGetXmitStatus( + IN PVOID SynchronizeContext + ); + + +BOOLEAN +SyncCardGetCurrent( + IN PVOID SynchronizeContext + ); + + +BOOLEAN +SyncCardSetReceiveConfig( + IN PVOID SynchronizeContext + ); + + +BOOLEAN +SyncCardSetAllMulticast( + IN PVOID SynchronizeContext + ); + + +BOOLEAN +SyncCardCopyMulticastRegs( + IN PVOID SynchronizeContext + ); + + +BOOLEAN +SyncCardSetInterruptMask( + IN PVOID SynchronizeContext + ); + + +BOOLEAN +SyncCardAcknowledgeReceive( + IN PVOID SynchronizeContext + ); + + +BOOLEAN +SyncCardAcknowledgeOverflow( + IN PVOID SynchronizeContext + ); + + +BOOLEAN +SyncCardAcknowledgeTransmit( + IN PVOID SynchronizeContext + ); + + +BOOLEAN +SyncCardAckAndGetCurrent( + IN PVOID SynchronizeContext + ); + + +BOOLEAN +SyncCardGetXmitStatusAndAck( + IN PVOID SynchronizeContext + ); + + +BOOLEAN +SyncCardUpdateCounters( + IN PVOID SynchronizeContext + ); + + +BOOLEAN +SyncCardHandleOverflow( + IN PVOID SynchronizeContext + ); + + +/*++ + +Routine Description: + + Determines the type of the interrupt on the card. The order of + importance is overflow, then receive, then transmit complete. + Counter MSB is handled first since it is simple. + +Arguments: + + AdaptP - pointer to the adapter block + + InterruptStatus - Current Interrupt Status. + +Return Value: + + The type of the interrupt + +--*/ + +#define CardGetInterruptType(_A, _I, _R) \ +{ \ + if (_I & ISR_COUNTER) { \ + _R = COUNTER; \ + } else if (_I & ISR_OVERFLOW ) { \ + SyncCardUpdateCounters(_A); \ + _R = OVERFLOW; \ + } else if (_I & ISR_RCV) { \ + if (_A->ReceivePacketCount < 10) { \ + _R = RECEIVE; \ + } else { \ + _A->ReceivePacketCount=0; \ + if (_I & (ISR_XMIT|ISR_XMIT_ERR)) { \ + _R = TRANSMIT; \ + } else { \ + _R = RECEIVE; \ + } \ + } \ + } else { \ + _A->ReceivePacketCount=0; \ + if (_I & (ISR_XMIT|ISR_XMIT_ERR)) { \ + _R = TRANSMIT; \ + } else if (_I & ISR_RCV_ERR) { \ + SyncCardUpdateCounters(_A); \ + _R = RECEIVE; \ + } else { \ + _R = UNKNOWN; \ + } \ + } \ +} + +#endif // ELNKIISFT + + + + + + + diff --git a/private/ntos/ndis/elnkii/interrup.c b/private/ntos/ndis/elnkii/interrup.c new file mode 100644 index 000000000..109d5efc9 --- /dev/null +++ b/private/ntos/ndis/elnkii/interrup.c @@ -0,0 +1,2426 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + interrup.c + +Abstract: + + This is a part of the driver for the National Semiconductor ElnkII + Ethernet controller. It contains the interrupt-handling routines. + This driver conforms to the NDIS 3.0 interface. + + The overall structure and much of the code is taken from + the Lance NDIS driver by Tony Ercolano. + +Author: + + Sean Selitrennikoff (seanse) Dec-1991 + +Environment: + + Kernel Mode - Or whatever is the equivalent on OS/2 and DOS. + +Revision History: + +--*/ + +#include +#include +#include "elnkhrd.h" +#include "elnksft.h" + + +#if DBG +#define STATIC +#else +#define STATIC static +#endif + +#if DBG +extern ULONG ElnkiiSendsCompletedAfterPendOk; +extern ULONG ElnkiiSendsCompletedAfterPendFail; +#endif + + +UCHAR ElnkiiBroadcastAddress[ETH_LENGTH_OF_ADDRESS] = + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + +// +// This is used to pad short packets. +// + +static UCHAR BlankBuffer[60] = " "; + + +#if DBG +ULONG ElnkiiSendsIssued = 0; +ULONG ElnkiiSendsFailed = 0; +ULONG ElnkiiSendsPended = 0; +ULONG ElnkiiSendsCompletedImmediately = 0; +ULONG ElnkiiSendsCompletedAfterPendOk = 0; +ULONG ElnkiiSendsCompletedAfterPendFail = 0; +ULONG ElnkiiSendsCompletedForReset = 0; +#endif + + +#if DBG + +#define ELNKII_LOG_SIZE 256 +UCHAR ElnkiiLogBuffer[ELNKII_LOG_SIZE]={0}; +UCHAR ElnkiiLogSaveBuffer[ELNKII_LOG_SIZE]={0}; +UINT ElnkiiLogLoc = 0; +BOOLEAN ElnkiiLogSave = FALSE; +UINT ElnkiiLogSaveLoc = 0; +UINT ElnkiiLogSaveLeft = 0; + +extern +VOID +ElnkiiLog(UCHAR c) { + + ElnkiiLogBuffer[ElnkiiLogLoc++] = c; + + ElnkiiLogBuffer[(ElnkiiLogLoc + 4) % ELNKII_LOG_SIZE] = '\0'; + + if (ElnkiiLogLoc >= ELNKII_LOG_SIZE) ElnkiiLogLoc = 0; +} + +#endif + + +#if DBG + +#define PACKET_LIST_SIZE 256 + +static PNDIS_PACKET PacketList[PACKET_LIST_SIZE] = {0}; +static PacketListSize = 0; + +VOID +AddPacketToList( + PELNKII_ADAPTER Adapter, + PNDIS_PACKET NewPacket + ) +{ + INT i; + + UNREFERENCED_PARAMETER(Adapter); + + for (i=0; iInCardTest) { + + // + // Ignore these random interrupts + // + + IF_LOG( ElnkiiLog('I'); ) + return(FALSE); + + } + + // + // Force the INT signal from the chip low. When the + // interrupt is acknowledged interrupts will be unblocked, + // which will cause a rising edge on the interrupt line + // if there is another interrupt pending on the card. + // + + IF_LOUD( DbgPrint( " blocking interrupts\n" ); ) + + CardBlockInterrupts(AdaptP); + + IF_LOG( ElnkiiLog('I'); ) + + return(TRUE); + +} + +VOID +ElnkiiInterruptDpc( + IN PVOID SystemSpecific1, + IN PVOID InterruptContext, + IN PVOID SystemSpecific2, + IN PVOID SystemSpecific3 + ) +/*++ + +Routine Description: + + This is the deffered processing routine for interrupts, it examines the + 'InterruptReg' to determine what deffered processing is necessary + and dispatches control to the Rcv and Xmt handlers. + +Arguments: + SystemSpecific1, SystemSpecific2, SystemSpecific3 - not used + InterruptContext - a handle to the adapter block. + +Return Value: + + NONE. + +--*/ +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)InterruptContext); + + UCHAR InterruptStatus; + INTERRUPT_TYPE InterruptType; + + UNREFERENCED_PARAMETER(SystemSpecific1); + UNREFERENCED_PARAMETER(SystemSpecific2); + UNREFERENCED_PARAMETER(SystemSpecific3); + + IF_LOUD( DbgPrint("==>IntDpc\n");) + + NdisDprAcquireSpinLock(&AdaptP->Lock); + + AdaptP->References++; + + // + // Get the interrupt bits + // + + CardGetInterruptStatus(AdaptP, &InterruptStatus); + + if (InterruptStatus != ISR_EMPTY) { + + NdisRawWritePortUchar((AdaptP)->MappedIoBaseAddr+NIC_INTR_STATUS, InterruptStatus); + + // + // InterruptStatus bits are used to dispatch to correct DPC and then cleared. + // + + CardGetInterruptType(AdaptP,InterruptStatus, InterruptType); + + } else { + + InterruptType = UNKNOWN; + + } + + + do { + + while ((InterruptType != UNKNOWN) || + ((AdaptP->LoopbackQueue != NULL) && + !(AdaptP->ReceiveInProgress || AdaptP->ResetInProgress))) { + + // + // Handle interrupts + // + + switch (InterruptType) { + + case COUNTER: + // + // One of the counters' MSB has been set, read in all + // the values just to be sure (and then exit below). + // + + IF_LOUD( DbgPrint("DPC got COUNTER\n"); ) + + SyncCardUpdateCounters((PVOID)AdaptP); + + InterruptStatus &= ~ISR_COUNTER; //clear the COUNTER interrupt bit. + + break; + + case OVERFLOW: + + // + // Overflow interrupts are handled as part of a receive + // interrupt, so set a flag and then pretend to be a + // receive, in case there is no receive already being handled. + // + + AdaptP->BufferOverflow = TRUE; + + // + // Check if a send completed before the overflow came in. + // + + if (AdaptP->TransmitInterruptPending && + !(InterruptStatus & (ISR_XMIT | ISR_XMIT_ERR))) { + + IF_LOG( ElnkiiLog('|');) + + InterruptStatus |= ISR_XMIT; + + AdaptP->OverflowRestartXmitDpc = FALSE; + + } + + IF_LOUD( DbgPrint("Overflow Int\n"); ) + IF_VERY_LOUD( DbgPrint( " overflow interrupt\n" ); ) + + InterruptStatus &= ~ISR_OVERFLOW; + + + case RECEIVE: + + // + // For receives, call this to ensure that another interrupt + // won't happen until the driver is ready. + // + + IF_LOG( ElnkiiLog('R');) + IF_LOUD( DbgPrint("DPC got RCV\n"); ) + + if (!AdaptP->ReceiveInProgress) { + + if (ElnkiiRcvInterruptDpc(AdaptP)) { + + InterruptStatus &= ~(ISR_RCV | ISR_RCV_ERR); + + } + + } else { + + // + // We can do this because the DPC in the RcvDpc will + // handle all the interrupts. + // + + InterruptStatus &= ~(ISR_RCV | ISR_RCV_ERR); + + } + + break; + + case TRANSMIT: + + IF_LOG( ElnkiiLog('X');) + +#if DBG + + ElnkiiLogSave = FALSE; + +#endif + + // + // Acknowledge transmit interrupt now, because of MP systems we + // can get an interrupt from a receive that will look like a + // transmit interrupt because we haven't cleared the bit in the + // ISR. We are not concerned about multiple Receive interrupts + // since the receive handler guards against being entered twice. + // + // Since only one transmit interrupt can be pending at a time + // we know that no-one else can enter here now... + // + // + // This puts the result of the transmit in AdaptP->XmitStatus. + // SyncCardGetXmitStatus(AdaptP); + // + + IF_LOUD( DbgPrint( " acking transmit interrupt\n" ); ) + + SyncCardGetXmitStatus(AdaptP); + + AdaptP->WakeUpFoundTransmit = FALSE; + + // + // This may be false if the card is currently handling an + // overflow and will restart the Dpc itself. + // + // + // If overflow handling then clear the transmit interrupt + // + + ASSERT(!AdaptP->OverflowRestartXmitDpc); + + if (AdaptP->ElnkiiHandleXmitCompleteRunning) { + +#if DBG + DbgBreakPoint(); +#endif + + } else { + + AdaptP->TransmitInterruptPending = FALSE; + + ElnkiiXmitInterruptDpc(AdaptP); + + } + + IF_LOUD( DbgPrint( "DPC got XMIT\n" ); ) + + InterruptStatus &= ~(ISR_XMIT|ISR_XMIT_ERR); + + break; + + default: + + // + // Create a rising edge on the interrupt line. + // + + IF_LOUD( DbgPrint( "unhandled interrupt type: %x", InterruptType); ) + + break; + } + + // + // Handle loopback + // + + if ((AdaptP->LoopbackQueue != NULL) && + !(AdaptP->ReceiveInProgress || AdaptP->ResetInProgress)) { + + ElnkiiRcvInterruptDpc(AdaptP); + + } + + CardGetInterruptType(AdaptP,InterruptStatus, InterruptType); + + } + + CardGetInterruptStatus(AdaptP, &InterruptStatus); + + if (InterruptStatus != ISR_EMPTY) { + + NdisRawWritePortUchar((AdaptP)->MappedIoBaseAddr+NIC_INTR_STATUS, InterruptStatus); + + } + + CardGetInterruptType(AdaptP,InterruptStatus,InterruptType); + + } while (InterruptType != UNKNOWN); // ISR says there's nothing left to do. + + // + // Turn the IMR back on. + // + + IF_LOUD( DbgPrint( " unblocking interrupts\n" ); ) + + AdaptP->NicInterruptMask = IMR_RCV | IMR_XMIT_ERR | IMR_XMIT | IMR_OVERFLOW; + + CardUnblockInterrupts(AdaptP); + + ELNKII_DO_DEFERRED(AdaptP); + + IF_LOUD( DbgPrint("<==IntDpc\n");) + +} + + +BOOLEAN +ElnkiiRcvInterruptDpc( + IN PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + This is the real interrupt handler for receive/overflow interrupt. + The ElnkiiInterruptDpc calls it directly. It calls ElnkiiHandleReceive + if that function is not already executing (since it runs at DPC, this + would only be happening on a multiprocessor system (i.e. later DPC calls + will not run until previous ones are complete on a particular processor)). + + NOTE: Called with the lock held!!! + +Arguments: + + DeferredContext - A pointer to the adapter block. + +Return Value: + + TRUE if done with all receives, else FALSE + +--*/ + +{ + PELNKII_OPEN TmpOpen; + PNDIS_PACKET LPacket; + PMAC_RESERVED Reserved; + BOOLEAN TransmitInterruptWasPending = FALSE; + INDICATE_STATUS IndicateStatus = INDICATE_OK; + BOOLEAN Done = TRUE; + + // + // Do nothing if a RECEIVE is already being handled. + // + + IF_LOUD( DbgPrint( "ElnkiiRcvInterruptDpc entered\n" );) + + AdaptP->ReceiveInProgress = TRUE; + + // + // At this point receive interrupts are disabled. + // + + if (!AdaptP->ResetInProgress && AdaptP->BufferOverflow) { + + NdisSynchronizeWithInterrupt( + &(AdaptP->NdisInterrupt), + (PVOID)SyncCardHandleOverflow, + (PVOID)AdaptP + ); + + } + + // + // Loop + // + + SyncCardGetCurrent(AdaptP); + + while (!AdaptP->ResetInProgress) { + + if (AdaptP->Current != AdaptP->NicNextPacket) { + + AdaptP->LoopbackPacket = (PNDIS_PACKET)NULL; + + AdaptP->ReceivePacketCount++; + + NdisDprReleaseSpinLock(&AdaptP->Lock); + + IndicateStatus = ElnkiiIndicatePacket(AdaptP); + + NdisDprAcquireSpinLock(&AdaptP->Lock); + + if (IndicateStatus == CARD_BAD) { + + IF_LOG( ElnkiiLog('W');) + + AdaptP->NicInterruptMask = IMR_XMIT | IMR_XMIT_ERR | IMR_OVERFLOW ; + + CardReset(AdaptP); + + break; + + } + + // + // Free the space used by packet on card. + // + + AdaptP->NicNextPacket = AdaptP->PacketHeader[1]; + + // + // This will set BOUNDARY to one behind NicNextPacket. + // + + CardSetBoundary(AdaptP); + + if (AdaptP->ReceivePacketCount > 10) { + + // + // Give transmit interrupts a chance + // + + Done = FALSE; + AdaptP->ReceivePacketCount = 0; + break; + + } + + } else { + + SyncCardGetCurrent(AdaptP); + + if (AdaptP->Current == AdaptP->NicNextPacket) { + + // + // End of loop -- no more packets + // + + break; + + } + + } + + } + + + if (AdaptP->BufferOverflow) { + + IF_VERY_LOUD( DbgPrint( " overflow\n" ); ) + + AdaptP->BufferOverflow = FALSE; + + NdisSynchronizeWithInterrupt( &(AdaptP->NdisInterrupt), + (PVOID)SyncCardAcknowledgeOverflow, + (PVOID)AdaptP ); + + // + // Undo loopback mode + // + + CardStart(AdaptP); + + IF_LOG ( ElnkiiLog('f');) + + // + // Check if transmission needs to be queued or not + // + + if (AdaptP->OverflowRestartXmitDpc && (AdaptP->CurBufXmitting != -1)) { + + IF_LOG( ElnkiiLog('?');) + + AdaptP->OverflowRestartXmitDpc = FALSE; + + AdaptP->WakeUpFoundTransmit = FALSE; + + AdaptP->TransmitInterruptPending = TRUE; + + CardStartXmit(AdaptP); + + } + + } + + // + // Now handle loopback packets. + // + + IF_LOUD( DbgPrint( " checking loopback queue\n" );) + + while (AdaptP->LoopbackQueue && !AdaptP->ResetInProgress) { + + // + // Take the first packet off the loopback queue... + // + + LPacket = AdaptP->LoopbackQueue; + + Reserved = RESERVED(LPacket); + + AdaptP->LoopbackQueue = RESERVED(AdaptP->LoopbackQueue)->NextPacket; + + AdaptP->LoopbackPacket = LPacket; + + AdaptP->FramesXmitGood++; + + // + // Save this, since once we complete the send + // Reserved is no longer valid. + // + + TmpOpen = Reserved->Open; + +#if DBG + IF_ELNKIIDEBUG( ELNKII_DEBUG_CHECK_DUP_SENDS ) { + + RemovePacketFromList(AdaptP, LPacket); + + } + + ElnkiiSendsCompletedAfterPendOk++; +#endif + + // + // ... and indicate it. + // + + NdisDprReleaseSpinLock(&AdaptP->Lock); + + ElnkiiIndicateLoopbackPacket(AdaptP, AdaptP->LoopbackPacket); + + // + // Complete the packet send. + // + + NdisCompleteSend( + Reserved->Open->NdisBindingContext, + LPacket, + NDIS_STATUS_SUCCESS + ); + + + NdisDprAcquireSpinLock(&AdaptP->Lock); + + TmpOpen->ReferenceCount--; + } + + // + // All receives are now done. Allow the receive indicator to run again. + // + + AdaptP->ReceiveInProgress = FALSE; + + if (AdaptP->ResetInProgress) { + + return Done; + + } + + IF_LOUD( DbgPrint( " clearing ReceiveInProgress\n" );) + + NdisDprReleaseSpinLock(&AdaptP->Lock); + + // + // Finally, indicate ReceiveComplete to all protocols which received packets + // + + EthFilterIndicateReceiveComplete(AdaptP->FilterDB); + + NdisDprAcquireSpinLock(&AdaptP->Lock); + + IF_LOUD( DbgPrint( "ElnkiiRcvInterruptDpc exiting\n" );) + + return(Done); + +} + +VOID +ElnkiiXmitInterruptDpc( + IN PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + This is the real interrupt handler for a transmit complete interrupt. + ElnkiiInterrupt queues a call to it. It calls ElnkiiHandleXmitComplete. + + NOTE : Called with the spinlock held!! and returns with it released!!! + +Arguments: + + AdaptP - A pointer to the adapter block. + +Return Value: + + None. + +--*/ + +{ + XMIT_BUF TmpBuf; + + PNDIS_PACKET Packet; + PMAC_RESERVED Reserved; + PELNKII_OPEN TmpOpen; + + IF_VERY_LOUD( DbgPrint( "ElnkiiXmitInterruptDpc entered\n" );) + + AdaptP->WakeUpFoundTransmit = FALSE; + + IF_LOG( ElnkiiLog('C');) + + AdaptP->ElnkiiHandleXmitCompleteRunning = TRUE; + + if (AdaptP->CurBufXmitting == -1) + { + AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE; + + return; + } + + // + // CurBufXmitting is not -1, which means nobody else + // will touch it. + // + Packet = AdaptP->Packets[AdaptP->CurBufXmitting]; + + ASSERT(Packet != (PNDIS_PACKET)NULL); + + Reserved = RESERVED(Packet); + + IF_LOUD( DbgPrint( "packet is 0x%lx\n", Packet );) + +#if DBG + + if ((AdaptP->XmitStatus & TSR_XMIT_OK) == 0) { + IF_LOG(ElnkiiLog('E');) + IF_LOG(ElnkiiLog((UCHAR)AdaptP->XmitStatus);) + } + + IF_ELNKIIDEBUG( ELNKII_DEBUG_CHECK_DUP_SENDS ) { + RemovePacketFromList(AdaptP, Packet); + } + +#endif + + + if (!Reserved->Loopback) { + + + // + // Complete the send if it is not to be loopbacked. + // + + if (AdaptP->XmitStatus & TSR_XMIT_OK) { + + AdaptP->FramesXmitGood++; + +#if DBG + ElnkiiSendsCompletedAfterPendOk++; + +#endif + + } else { + + AdaptP->FramesXmitBad++; + +#if DBG + ElnkiiSendsCompletedAfterPendFail++; +#endif + + } + + // + // Save this, since once we complete the send + // Reserved is no longer valid. + // + + TmpOpen = Reserved->Open; + + IF_LOG( ElnkiiLog('p');) + + NdisDprReleaseSpinLock(&AdaptP->Lock); + + NdisCompleteSend(Reserved->Open->NdisBindingContext, + Packet, + AdaptP->XmitStatus & TSR_XMIT_OK ? + NDIS_STATUS_SUCCESS : NDIS_STATUS_FAILURE); + + NdisDprAcquireSpinLock(&AdaptP->Lock); + + TmpOpen->ReferenceCount--; + + } else { + + // + // Put it on the loopback queue + // + + if (AdaptP->LoopbackQueue == (PNDIS_PACKET)NULL) { + + AdaptP->LoopbackQueue = Packet; + AdaptP->LoopbackQTail = Packet; + + } else { + + RESERVED(AdaptP->LoopbackQTail)->NextPacket = Packet; + AdaptP->LoopbackQTail = Packet; + + } + + Reserved->NextPacket = (PNDIS_PACKET)NULL; + + } + + // + // Mark the current transmit as done. + // + + AdaptP->Packets[AdaptP->CurBufXmitting] = (PNDIS_PACKET)NULL; + + AdaptP->BufferStatus[AdaptP->CurBufXmitting] = EMPTY; + + TmpBuf = NextBuf(AdaptP, AdaptP->CurBufXmitting); + + // + // See what to do next. + // + + switch (AdaptP->BufferStatus[TmpBuf]) { + + + case FULL: + + // + // The next packet is ready to go -- only happens with + // more than one transmit buffer. + // + + IF_LOUD( DbgPrint( " next packet ready to go\n" );) + + if (AdaptP->ResetInProgress) { + + // + // A reset just started, abort. + // + + AdaptP->CurBufXmitting = -1; + + AdaptP->BufferStatus[TmpBuf] = EMPTY; // to ack the reset + + AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE; + + NdisDprReleaseSpinLock(&AdaptP->Lock); + + ElnkiiResetStageDone(AdaptP, XMIT_STOPPED); + + NdisDprAcquireSpinLock(&AdaptP->Lock); + + } else { + + // + // Start the transmission and check for more. + // + + AdaptP->CurBufXmitting = TmpBuf; + + IF_LOG( ElnkiiLog('2');) + +#if DBG + + ElnkiiLogSave = TRUE; + ElnkiiLogSaveLeft = 20; + +#endif + + AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE; + + // + // If we are currently handling an overflow, then we need to let + // the overflow handler send this packet... + // + + if (AdaptP->BufferOverflow) { + + AdaptP->OverflowRestartXmitDpc = TRUE; + + IF_LOG( ElnkiiLog('O');) + + } else { + + // + // This is used to check if stopping the chip prevented + // a transmit complete interrupt from coming through (it + // is cleared in the ISR if a transmit DPC is queued). + // + + AdaptP->TransmitInterruptPending = TRUE; + + CardStartXmit(AdaptP); + + } + + ElnkiiCopyAndSend(AdaptP); + + } + + break; + + + case FILLING: + + // + // The next packet will be started when copying down is finished. + // + + IF_LOUD( DbgPrint( " next packet filling\n" );) + + AdaptP->CurBufXmitting = -1; + + AdaptP->NextBufToXmit = TmpBuf; + + // + // If AdaptP->NextBufToFill is not TmpBuf, this + // will check to make sure NextBufToFill is not + // waiting to be filled. + // + + AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE; + + ElnkiiCopyAndSend(AdaptP); + + break; + + + case EMPTY: + + // + // No packet is ready to transmit. + // + + IF_LOUD( DbgPrint( " next packet empty\n" );) + + if (AdaptP->ResetInProgress) { + + // + // A reset has just started, exit. + // + + AdaptP->CurBufXmitting = -1; + + AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE; + + NdisDprReleaseSpinLock(&AdaptP->Lock); + + ElnkiiResetStageDone(AdaptP, XMIT_STOPPED); + + NdisDprAcquireSpinLock(&AdaptP->Lock); + + break; + + } + + + if (AdaptP->XmitQueue != (PNDIS_PACKET)NULL) { + + // + // Take the packet off the head of the queue. + // + // There will be a packet on the queue with + // BufferStatus[TmpBuf] == EMPTY only when we + // have only one transmit buffer. + // + + IF_LOUD( DbgPrint( " transmit queue not empty\n" );) + + Packet = AdaptP->XmitQueue; + + AdaptP->XmitQueue = RESERVED(AdaptP->XmitQueue)->NextPacket; + + + // + // At this point, NextBufToFill should equal TmpBuf. + // + + AdaptP->NextBufToFill = NextBuf(AdaptP, TmpBuf); + + + // + // Set this now, to avoid having to get spinlock between + // copying and transmission start. + // + + AdaptP->BufferStatus[TmpBuf] = FULL; + + AdaptP->Packets[TmpBuf] = Packet; + AdaptP->CurBufXmitting = TmpBuf; + + IF_LOG( ElnkiiLog('3');) + +#if DBG + + ElnkiiLogSave = TRUE; + ElnkiiLogSaveLeft = 20; + +#endif + + NdisDprReleaseSpinLock(&AdaptP->Lock); + + + // + // Copy down the data, pad short packets with blanks. + // + + (VOID)CardCopyDownPacket(AdaptP, Packet, TmpBuf, + &AdaptP->PacketLens[TmpBuf]); + + if (AdaptP->PacketLens[TmpBuf] < 60) { + + (VOID)CardCopyDownBuffer( + AdaptP, + BlankBuffer, + TmpBuf, + AdaptP->PacketLens[TmpBuf], + 60-AdaptP->PacketLens[TmpBuf] + ); + + } + + + NdisDprAcquireSpinLock(&AdaptP->Lock); + + AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE; + + // + // If we are currently handling an overflow, then we need to let + // the overflow handler send this packet... + // + + if (AdaptP->BufferOverflow) { + + AdaptP->OverflowRestartXmitDpc = TRUE; + + IF_LOG( ElnkiiLog('O');) + + } else { + + // + // This is used to check if stopping the chip prevented + // a transmit complete interrupt from coming through (it + // is cleared in the ISR if a transmit DPC is queued). + // + + AdaptP->TransmitInterruptPending = TRUE; + + CardStartXmit(AdaptP); + + } + + // + // It makes no sense to call ElnkiiCopyAndSend because + // there is only one transmit buffer, and it was just + // filled. + // + + } else { + + // + // No packets are waiting on the transmit queue. + // + + AdaptP->CurBufXmitting = -1; + + AdaptP->NextBufToXmit = TmpBuf; + + AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE; + + } + + break; + + default: + + AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE; + + } + + IF_VERY_LOUD( DbgPrint( "ElnkiiXmitInterruptDpc exiting\n" );) + +} + +INDICATE_STATUS +ElnkiiIndicateLoopbackPacket( + IN PELNKII_ADAPTER AdaptP, + IN PNDIS_PACKET Packet + ) + +/*++ + +Routine Description: + + Indicates an NDIS_format packet to the protocols. This is used + for indicating packets from the loopback queue. + +Arguments: + + AdaptP - pointer to the adapter block + Packet - the packet to be indicated + +Return Value: + + SKIPPED if it is a run packet + INDICATE_OK otherwise. + +--*/ + +{ + UINT IndicateLen; + UINT PacketLen; + + // + // Indicate up to 252 bytes. + // + + NdisQueryPacket(Packet, + NULL, + NULL, + NULL, + &PacketLen + ); + + if (PacketLen < ETH_LENGTH_OF_ADDRESS) { + + // + // A runt packet. + // + + return SKIPPED; + + } + + IndicateLen = (PacketLen > AdaptP->MaxLookAhead) ? + AdaptP->MaxLookAhead : PacketLen; + + // + // Copy the lookahead data into a contiguous buffer. + // + + ElnkiiCopyOver(AdaptP->Lookahead, + Packet, + 0, + IndicateLen + ); + + if (IndicateLen < ELNKII_HEADER_SIZE) { + + // + // Must have at least the address + // + + if (IndicateLen > 5) { + + // + // Runt packet + // + + EthFilterIndicateReceive( + AdaptP->FilterDB, + (NDIS_HANDLE)AdaptP, + (PCHAR)AdaptP->Lookahead, + AdaptP->Lookahead, + IndicateLen, + NULL, + 0, + 0 + ); + } + + } else { + + // + // Indicate packet + // + + EthFilterIndicateReceive( + AdaptP->FilterDB, + (NDIS_HANDLE)AdaptP, + (PCHAR)AdaptP->Lookahead, + AdaptP->Lookahead, + ELNKII_HEADER_SIZE, + AdaptP->Lookahead + ELNKII_HEADER_SIZE, + IndicateLen - ELNKII_HEADER_SIZE, + PacketLen - ELNKII_HEADER_SIZE + ); + } + + return INDICATE_OK; +} + +UINT +ElnkiiCopyOver( + OUT PUCHAR Buf, // destination + IN PNDIS_PACKET Packet, // source packet + IN UINT Offset, // offset in packet + IN UINT Length // number of bytes to copy + ) + +/*++ + +Routine Description: + + Copies bytes from a packet into a buffer. Used to copy data + out of a packet during loopback indications. + +Arguments: + + Buf - the destination buffer + Packet - the source packet + Offset - the offset in the packet to start copying at + Length - the number of bytes to copy + +Return Value: + + The actual number of bytes copied; will be less than Length if + the packet length is less than Offset+Length. + +--*/ + +{ + PNDIS_BUFFER CurBuffer; + UINT BytesCopied; + PUCHAR BufVA; + UINT BufLen; + UINT ToCopy; + UINT CurOffset; + + + BytesCopied = 0; + + // + // First find a spot Offset bytes into the packet. + // + + CurOffset = 0; + + NdisQueryPacket(Packet, NULL, NULL, &CurBuffer, NULL); + + while (CurBuffer != (PNDIS_BUFFER)NULL) { + + NdisQueryBuffer(CurBuffer, (PVOID *)&BufVA, &BufLen); + + if (CurOffset + BufLen > Offset) { + + break; + + } + + CurOffset += BufLen; + + NdisGetNextBuffer(CurBuffer, &CurBuffer); + + } + + + // + // See if the end of the packet has already been passed. + // + + if (CurBuffer == (PNDIS_BUFFER)NULL) { + + return 0; + + } + + + // + // Now copy over Length bytes. + // + + BufVA += (Offset - CurOffset); + + BufLen -= (Offset - CurOffset); + + for (;;) { + + ToCopy = (BytesCopied+BufLen > Length) ? Length - BytesCopied : BufLen; + + ELNKII_MOVE_MEM(Buf+BytesCopied, BufVA, ToCopy); + + BytesCopied += ToCopy; + + + if (BytesCopied == Length) { + + return BytesCopied; + + } + + NdisGetNextBuffer(CurBuffer, &CurBuffer); + + if (CurBuffer == (PNDIS_BUFFER)NULL) { + + break; + + } + + NdisQueryBuffer(CurBuffer, (PVOID *)&BufVA, &BufLen); + + } + + return BytesCopied; + +} + +INDICATE_STATUS +ElnkiiIndicatePacket( + IN PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + Indicates the first packet on the card to the protocols. + +Arguments: + + AdaptP - pointer to the adapter block. + +Return Value: + + CARD_BAD if the card should be reset; + INDICATE_OK otherwise. + +--*/ + +{ + UINT PacketLen; + PUCHAR PacketLoc; + PUCHAR IndicateBuf; + UINT IndicateLen; + UCHAR PossibleNextPacket1, PossibleNextPacket2; + + + // + // First copy up the four-byte header the card attaches. + // + + PacketLoc = AdaptP->PageStart + + 256*(AdaptP->NicNextPacket-AdaptP->NicPageStart); + + + if (!CardCopyUp(AdaptP, AdaptP->PacketHeader, PacketLoc, 4)) + return(CARD_BAD); + + // + // Check if the next packet byte agress with the length, as + // described on p. A-3 of the Etherlink II Technical Reference. + // The start of the packet plus the MSB of the length must + // be equal to the start of the next packet minus one or two. + // Otherwise the header is considered corrupted, and the + // card must be reset. + // + + PossibleNextPacket1 = + AdaptP->NicNextPacket + AdaptP->PacketHeader[3] + (UCHAR)1; + + if (PossibleNextPacket1 >= AdaptP->NicPageStop) { + + PossibleNextPacket1 -= (AdaptP->NicPageStop - AdaptP->NicPageStart); + + } + + if (PossibleNextPacket1 != AdaptP->PacketHeader[1]) { + + PossibleNextPacket2 = PossibleNextPacket1+(UCHAR)1; + + if (PossibleNextPacket2 == AdaptP->NicPageStop) { + + PossibleNextPacket2 = AdaptP->NicPageStart; + + } + + if (PossibleNextPacket2 != AdaptP->PacketHeader[1]) { + + IF_LOUD(DbgPrint("F");) + + if ((AdaptP->PacketHeader[1] < AdaptP->NicPageStart) || + (AdaptP->PacketHeader[1] >= AdaptP->NicPageStop)) { + + // + // We return CARD_BAD because the Dpc will set the NicNextPacket + // pointer based on the PacketHeader[1] value if we return + // SKIPPED. + // + + return(CARD_BAD); + + } + + return SKIPPED; + } + + } + +#if DBG + + IF_ELNKIIDEBUG( ELNKII_DEBUG_WORKAROUND1 ) { + // + // Now check for the high order 2 bits being set, as described + // on page A-2 of the Etherlink II Technical Reference. If either + // of the two high order bits is set in the receive status byte + // in the packet header, the packet should be skipped (but + // the adapter does not need to be reset). + // + + if (AdaptP->PacketHeader[0] & (RSR_DISABLED|RSR_DEFERRING)) { + + IF_LOUD (DbgPrint("H");) + + return SKIPPED; + + } + + } + +#endif + + // + // Packet length is in bytes 3 and 4 of the header. + // + PacketLen = AdaptP->PacketHeader[2] + AdaptP->PacketHeader[3] * 256; + if (0 == PacketLen) + { + // + // Packet with no data... + // + IndicateLen = 0; + } + else + { + // + // Don't count the header. + // + PacketLen -= 4; + + // + // See how much to indicate (252 bytes max). + // + IndicateLen = PacketLen < AdaptP->MaxLookAhead ? + PacketLen : AdaptP->MaxLookAhead; + } + + // + // Save the length with the adapter block. + // + AdaptP->PacketLen = PacketLen; + + // + // if not memory mapped, have to copy the lookahead data up first. + // + if (!AdaptP->MemMapped) + { + if (!CardCopyUp(AdaptP, AdaptP->Lookahead, PacketLoc + 4, IndicateLen)) + return(CARD_BAD); + + IndicateBuf = AdaptP->Lookahead; + } + else + { + if (IndicateLen != 0) + { + NdisCreateLookaheadBufferFromSharedMemory( + (PVOID)(PacketLoc + 4), + IndicateLen, + &IndicateBuf + ); + } + } + + if (IndicateBuf != NULL) + { + AdaptP->FramesRcvGood++; + + if (IndicateLen < ELNKII_HEADER_SIZE) + { + // + // Indicate packet + // + EthFilterIndicateReceive( + AdaptP->FilterDB, + (NDIS_HANDLE)AdaptP, + (PCHAR)IndicateBuf, + IndicateBuf, + IndicateLen, + NULL, + 0, + 0 + ); + } + else + { + // + // Indicate packet + // + EthFilterIndicateReceive( + AdaptP->FilterDB, + (NDIS_HANDLE)AdaptP, + (PCHAR)IndicateBuf, + IndicateBuf, + ELNKII_HEADER_SIZE, + IndicateBuf + ELNKII_HEADER_SIZE, + IndicateLen - ELNKII_HEADER_SIZE, + PacketLen - ELNKII_HEADER_SIZE + ); + } + + if (AdaptP->MemMapped && (IndicateLen != 0)) + { + NdisDestroyLookaheadBufferFromSharedMemory(IndicateBuf); + } + } + + return(INDICATE_OK); +} + +NDIS_STATUS +ElnkiiTransferData( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ) + +/*++ + +Routine Description: + + NDIS function. + +Arguments: + + see NDIS 3.0 spec. + +Notes: + + - The MacReceiveContext will be a pointer to the open block for + the packet. + - The LoopbackPacket field in the adapter block will be NULL if this + is a call for a normal packet, otherwise it will be set to point + to the loopback packet. + +--*/ + +{ + UINT BytesLeft, BytesNow, BytesWanted; + PUCHAR CurCardLoc; + PNDIS_BUFFER CurBuffer; + PUCHAR BufVA, BufStart; + UINT BufLen, BufOff, Copied; + UINT CurOff; + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)MacReceiveContext); + + UNREFERENCED_PARAMETER(MacBindingHandle); + + // + // Determine whether this was a loopback indication. + // + + ByteOffset += ELNKII_HEADER_SIZE; + + if (AdaptP->LoopbackPacket != (PNDIS_PACKET)NULL) { + + // + // Yes, have to copy data from AdaptP->LoopbackPacket into Packet. + // + + NdisQueryPacket(Packet, NULL, NULL, &CurBuffer, NULL); + + CurOff = ByteOffset; + + while (CurBuffer != (PNDIS_BUFFER)NULL) { + + NdisQueryBuffer(CurBuffer, (PVOID *)&BufVA, &BufLen); + + Copied = + ElnkiiCopyOver(BufVA, AdaptP->LoopbackPacket, CurOff, BufLen); + + CurOff += Copied; + + if (Copied < BufLen) { + + break; + + } + + NdisGetNextBuffer(CurBuffer, &CurBuffer); + + } + + // + // We are done, return. + // + + + *BytesTransferred = CurOff - ByteOffset; + if (*BytesTransferred > BytesToTransfer) { + *BytesTransferred = BytesToTransfer; + } + + return NDIS_STATUS_SUCCESS; + + } + + + // + // This was NOT a loopback packet, get the data off the card. + // + + // + // See how much data there is to transfer. + // + + if (ByteOffset+BytesToTransfer > AdaptP->PacketLen) { + + BytesWanted = AdaptP->PacketLen - ByteOffset; + + } else { + + BytesWanted = BytesToTransfer; + + } + + BytesLeft = BytesWanted; + + + // + // Determine where the copying should start. + // + + CurCardLoc = AdaptP->PageStart + + 256*(AdaptP->NicNextPacket-AdaptP->NicPageStart) + + 4 + ByteOffset; + + if (CurCardLoc > AdaptP->PageStop) { + + CurCardLoc = CurCardLoc - (AdaptP->PageStop - AdaptP->PageStart); + + } + + + NdisQueryPacket(Packet, NULL, NULL, &CurBuffer, NULL); + + NdisQueryBuffer(CurBuffer, (PVOID *)&BufStart, &BufLen); + + BufOff = 0; + + // + // Loop, filling each buffer in the packet until there + // are no more buffers or the data has all been copied. + // + + while (BytesLeft > 0) { + + // + // See how much data to read into this buffer. + // + + if ((BufLen-BufOff) > BytesLeft) { + + BytesNow = BytesLeft; + + } else { + + BytesNow = (BufLen - BufOff); + + } + + + // + // See if the data for this buffer wraps around the end + // of the receive buffers (if so filling this buffer + // will use two iterations of the loop). + // + + if (CurCardLoc + BytesNow > AdaptP->PageStop) { + + BytesNow = AdaptP->PageStop - CurCardLoc; + + } + + + // + // Copy up the data. + // + + if (!CardCopyUp(AdaptP, BufStart+BufOff, CurCardLoc, BytesNow)) + { + *BytesTransferred = BytesWanted - BytesLeft; + + return(NDIS_STATUS_FAILURE); + } + + CurCardLoc += BytesNow; + + BytesLeft -= BytesNow; + + + // + // Is the transfer done now? + // + + if (BytesLeft == 0) { + + break; + + } + + + // + // Wrap around the end of the receive buffers? + // + + if (CurCardLoc == AdaptP->PageStop) { + + CurCardLoc = AdaptP->PageStart; + + } + + + // + // Was the end of this packet buffer reached? + // + + BufOff += BytesNow; + + if (BufOff == BufLen) { + + NdisGetNextBuffer(CurBuffer, &CurBuffer); + + if (CurBuffer == (PNDIS_BUFFER)NULL) { + + break; + + } + + NdisQueryBuffer(CurBuffer, (PVOID *)&BufStart, &BufLen); + + BufOff = 0; + + } + + } + + + *BytesTransferred = BytesWanted - BytesLeft; + + return NDIS_STATUS_SUCCESS; + +} + + + + + + + + + + +NDIS_STATUS +ElnkiiSend( + IN NDIS_HANDLE MacBindingHandle, + IN PNDIS_PACKET Packet + ) + +/*++ + +Routine Description: + + NDIS function. + +Arguments: + + See NDIS 3.0 spec. + +Notes: + + +--*/ + +{ + PELNKII_OPEN OpenP = ((PELNKII_OPEN)MacBindingHandle); + PELNKII_ADAPTER AdaptP = OpenP->Adapter; + PMAC_RESERVED Reserved = RESERVED(Packet); + + // + // First Buffer + // + PNDIS_BUFFER FirstBuffer; + + // + // Virtual address of first buffer + // + PVOID BufferVA; + + // + // Length of the first buffer + // + UINT Length; + + + NdisAcquireSpinLock(&AdaptP->Lock); + +#if DBG + ElnkiiSendsIssued++; +#endif + + // + // Ensure that the open won't close during this function. + // + + if (OpenP->Closing) { + +#if DBG + ElnkiiSendsFailed++; +#endif + + NdisReleaseSpinLock(&AdaptP->Lock); + + return NDIS_STATUS_CLOSING; + } + + // + // All requests are rejected during a reset. + // + + if (AdaptP->ResetInProgress) { + + NdisReleaseSpinLock(&AdaptP->Lock); + +#if DBG + ElnkiiSendsFailed++; +#endif + + return NDIS_STATUS_RESET_IN_PROGRESS; + + } + + OpenP->ReferenceCount++; + + AdaptP->References++; + + // + // Set up the MacReserved section of the packet. + // + + Reserved->Open = (PELNKII_OPEN)MacBindingHandle; + Reserved->NextPacket = (PNDIS_PACKET)NULL; + +#if DBG + IF_ELNKIIDEBUG( ELNKII_DEBUG_CHECK_DUP_SENDS ) { + + AddPacketToList(AdaptP, Packet); + + } +#endif + + // + // Set Reserved->Loopback. + // + + NdisQueryPacket(Packet, NULL, NULL, &FirstBuffer, NULL); + + // + // Get VA of first buffer + // + + NdisQueryBuffer( + FirstBuffer, + &BufferVA, + &Length + ); + + if (OpenP->ProtOptionFlags & NDIS_PROT_OPTION_NO_LOOPBACK){ + + Reserved->Loopback = FALSE; + + } else{ + + Reserved->Loopback = EthShouldAddressLoopBack(AdaptP->FilterDB, BufferVA); + + } + + // + // Put it on the loopback queue only. All packets go through the + // loopback queue first, and then on to the xmit queue. + // + + IF_LOG( ElnkiiLog('D');) + +#if DBG + ElnkiiSendsPended++; +#endif + + + // + // We do not OpenP->ReferenceCount-- because that will be done when + // then send completes. + // + + // + // Put Packet on queue to hit the wire. + // + + IF_VERY_LOUD( DbgPrint("Putting 0x%x on, after 0x%x\n",Packet,AdaptP->XmitQTail); ) + + if (AdaptP->XmitQueue != NULL) { + + RESERVED(AdaptP->XmitQTail)->NextPacket = Packet; + + AdaptP->XmitQTail = Packet; + + } else { + + AdaptP->XmitQueue = Packet; + + AdaptP->XmitQTail = Packet; + + } + + Reserved->NextPacket = NULL; + + ELNKII_DO_DEFERRED(AdaptP); + + return NDIS_STATUS_PENDING; +} + +UINT +ElnkiiCompareMemory( + IN PUCHAR String1, + IN PUCHAR String2, + IN UINT Length + ) +{ + UINT i; + + for (i=0; iBufferStatus[TmpBuf1=AdaptP->NextBufToFill] == EMPTY) && + (AdaptP->XmitQueue != NULL)) { + + // + // Take the packet off of the transmit queue. + // + + IF_VERY_LOUD( DbgPrint("Removing 0x%x, New Head is 0x%x\n",Packet,RESERVED(Packet)->NextPacket); ) + + Packet = AdaptP->XmitQueue; + + AdaptP->XmitQueue = RESERVED(Packet)->NextPacket; + + AdaptP->BufferStatus[TmpBuf1] = FILLING; + + AdaptP->Packets[TmpBuf1] = Packet; + + AdaptP->NextBufToFill = NextBuf(AdaptP, TmpBuf1); + + NdisReleaseSpinLock(&AdaptP->Lock); + + + // + // copy down the data, pad short packets with blanks. + // + + (VOID)CardCopyDownPacket(AdaptP, Packet, TmpBuf1, + &AdaptP->PacketLens[TmpBuf1]); + + if (AdaptP->PacketLens[TmpBuf1] < 60) { + + (VOID)CardCopyDownBuffer( + AdaptP, + BlankBuffer, + TmpBuf1, + AdaptP->PacketLens[TmpBuf1], + 60 - AdaptP->PacketLens[TmpBuf1]); + } + + NdisAcquireSpinLock(&AdaptP->Lock); + + if (AdaptP->ResetInProgress) + { + PELNKII_OPEN TmpOpen = (PELNKII_OPEN)((RESERVED(Packet)->Open)); + + // + // A reset just started, abort. + // + // + AdaptP->BufferStatus[TmpBuf1] = EMPTY; // to ack the reset + + NdisReleaseSpinLock(&AdaptP->Lock); + + // + // Complete the send. + // + NdisCompleteSend( + RESERVED(Packet)->Open->NdisBindingContext, + Packet, + NDIS_STATUS_SUCCESS + ); + + ElnkiiResetStageDone(AdaptP, BUFFERS_EMPTY); + + NdisAcquireSpinLock(&AdaptP->Lock); + + TmpOpen->ReferenceCount--; + + return; + } + + AdaptP->BufferStatus[TmpBuf1] = FULL; + + + // + // See whether to start the transmission. + // + + if (AdaptP->CurBufXmitting != -1) { + + // + // Another transmit is still in progress. + // + + continue; + } + + if (AdaptP->NextBufToXmit != TmpBuf1) { + + // + // A packet ahead of us is being copied down, this + // transmission can't be started now. + // + + continue; + } + + // + // OK to start transmission. + // + + AdaptP->CurBufXmitting = AdaptP->NextBufToXmit; + + + IF_LOG( ElnkiiLog('4');) + +#if DBG + + ElnkiiLogSave = TRUE; + ElnkiiLogSaveLeft = 20; + +#endif + + // + // If we are currently handling an overflow, then we need to let + // the overflow handler send this packet... + // + + if (AdaptP->BufferOverflow) { + + AdaptP->OverflowRestartXmitDpc = TRUE; + + IF_LOG( ElnkiiLog('O');) + + } else { + + // + // This is used to check if stopping the chip prevented + // a transmit complete interrupt from coming through (it + // is cleared in the ISR if a transmit DPC is queued). + // + + AdaptP->TransmitInterruptPending = TRUE; + + CardStartXmit(AdaptP); + + } + + } + +} + + +VOID +ElnkiiWakeUpDpc( + IN PVOID SystemSpecific1, + IN PVOID Context, + IN PVOID SystemSpecific2, + IN PVOID SystemSpecific3 + ) + +/*++ + +Routine Description: + + This DPC routine is queued every 2 seconds to check on the + transmit queue. If a transmit interrupt was not received + in the last two seconds and there is a transmit in progress, + then we complete the transmit. + +Arguments: + + Context - Really a pointer to the adapter. + +Return Value: + + None. + +--*/ +{ + PELNKII_ADAPTER AdaptP = (PELNKII_ADAPTER)Context; + XMIT_BUF TmpBuf; + PMAC_RESERVED Reserved; + PELNKII_OPEN TmpOpen; + PNDIS_PACKET Packet; + PNDIS_PACKET NextPacket; + + UNREFERENCED_PARAMETER(SystemSpecific1); + UNREFERENCED_PARAMETER(SystemSpecific2); + UNREFERENCED_PARAMETER(SystemSpecific3); + + NdisDprAcquireSpinLock(&AdaptP->Lock); + + if ((AdaptP->WakeUpFoundTransmit) && + (AdaptP->CurBufXmitting != -1)) + { + // + // We had a transmit pending the last time we ran, + // and it has not been completed...we need to complete + // it now. + + AdaptP->TransmitInterruptPending = FALSE; + AdaptP->WakeUpFoundTransmit = FALSE; + + IF_LOG( ElnkiiLog('K');) + + // + // We log the first 5 of these. + // + if (AdaptP->TimeoutCount < 5) + { + NdisWriteErrorLogEntry( + AdaptP->NdisAdapterHandle, + NDIS_ERROR_CODE_HARDWARE_FAILURE, + 2, + 0x3, + AdaptP->TimeoutCount + ); + } + + AdaptP->TimeoutCount++; + +#if DBG + + ELNKII_MOVE_MEM(ElnkiiLogSaveBuffer, ElnkiiLogBuffer, ELNKII_LOG_SIZE); + + ElnkiiLogSave = FALSE; + ElnkiiLogSaveLoc = ElnkiiLogLoc; + +#endif + + // + // We stop and start the card, then queue a DPC to + // handle the receive. + // + + CardStop(AdaptP); + + Packet = AdaptP->Packets[AdaptP->CurBufXmitting]; + + AdaptP->Packets[AdaptP->CurBufXmitting] = (PNDIS_PACKET)NULL; + + AdaptP->BufferStatus[AdaptP->CurBufXmitting] = EMPTY; + + TmpBuf = NextBuf(AdaptP, AdaptP->CurBufXmitting); + + // + // Set this so that we don't access the packets + // somewhere else. + // + AdaptP->CurBufXmitting = -1; + + // + // Abort all sends + // + while (Packet != NULL) { + + Reserved = RESERVED(Packet); + + TmpOpen = Reserved->Open; + + NdisDprReleaseSpinLock(&AdaptP->Lock); + + NdisCompleteSend( + TmpOpen->NdisBindingContext, + Packet, + NDIS_STATUS_SUCCESS + ); + + NdisDprAcquireSpinLock(&AdaptP->Lock); + + TmpOpen->ReferenceCount--; + + // + // Get next packet + // + + if (AdaptP->BufferStatus[TmpBuf] == FULL) { + + Packet = AdaptP->Packets[TmpBuf]; + + AdaptP->Packets[TmpBuf] = NULL; + + AdaptP->BufferStatus[TmpBuf] = EMPTY; + + TmpBuf = NextBuf(AdaptP, TmpBuf); + + } else { + + break; + + } + + } + + // + // Set send variables correctly. + // + AdaptP->NextBufToXmit = TmpBuf; + + + + Packet = AdaptP->XmitQueue; + + AdaptP->XmitQueue = NULL; + + while (Packet != NULL) { + + Reserved = RESERVED(Packet); + + // + // Remove the packet from the queue. + // + + NextPacket = Reserved->NextPacket; + + TmpOpen = Reserved->Open; + + NdisDprReleaseSpinLock(&AdaptP->Lock); + + NdisCompleteSend( + TmpOpen->NdisBindingContext, + Packet, + NDIS_STATUS_SUCCESS + ); + + NdisDprAcquireSpinLock(&AdaptP->Lock); + + TmpOpen->ReferenceCount--; + + Packet = NextPacket; + + } + + // + // Restart the card + // + + CardStart(AdaptP); + + NdisDprReleaseSpinLock(&AdaptP->Lock); + + } else { + + if (AdaptP->CurBufXmitting != -1) { + + AdaptP->WakeUpFoundTransmit = TRUE; + + IF_LOG( ElnkiiLog('L');) + + } + + NdisDprReleaseSpinLock(&AdaptP->Lock); + + + } + + // + // Fire off another Dpc to execute after 2 seconds + // + + NdisSetTimer( + &AdaptP->WakeUpTimer, + 2000 + ); + +} + diff --git a/private/ntos/ndis/elnkii/keywords.h b/private/ntos/ndis/elnkii/keywords.h new file mode 100644 index 000000000..b035a04ea --- /dev/null +++ b/private/ntos/ndis/elnkii/keywords.h @@ -0,0 +1,56 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + keywords.h + +Abstract: + + Contains all Ndis2 and Ndis3 mac-specific keywords. + +Author: + + Bob Noradki + +Environment: + + This driver is expected to work in DOS, OS2 and NT at the equivalent + of kernal mode. + + Architecturally, there is an assumption in this driver that we are + on a little endian machine. + +Notes: + + optional-notes + +Revision History: + + + +--*/ +#ifndef NDIS2 +#define NDIS2 0 +#endif + +#if NDIS2 + +#define IOBASE NDIS_STRING_CONST("IOADDRESS") +#define INTERRUPT NDIS_STRING_CONST("INTERRUPT") +#define MAXMULTICAST NDIS_STRING_CONST("MAXMULTICASTLIST") +#define NETWORK_ADDRESS NDIS_STRING_CONST("NETADDRESS") +#define MEMORYMAPPED NDIS_STRING_CONST("MEMORYMAPPED") +#define TRANSCEIVER NDIS_STRING_CONST("TRANSCEIVER") + +#else // NDIS3 + +#define IOBASE NDIS_STRING_CONST("IoBaseAddress") +#define INTERRUPT NDIS_STRING_CONST("InterruptNumber") +#define MAXMULTICAST NDIS_STRING_CONST("MaximumMulticastList") +#define NETWORK_ADDRESS NDIS_STRING_CONST("NetworkAddress") +#define MEMORYMAPPED NDIS_STRING_CONST("MemoryMapped") +#define TRANSCEIVER NDIS_STRING_CONST("Transceiver") + +#endif diff --git a/private/ntos/ndis/elnkii/makefile b/private/ntos/ndis/elnkii/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/ndis/elnkii/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/ndis/elnkii/pend.c b/private/ntos/ndis/elnkii/pend.c new file mode 100644 index 000000000..b2d343852 --- /dev/null +++ b/private/ntos/ndis/elnkii/pend.c @@ -0,0 +1,440 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + pend.c + +Abstract: + + Multicast and filter functions for the NDIS 3.0 Etherlink II driver. + +Author: + + Adam Barr (adamba) 30-Jul-1990 + Aaron Ogus (aarono) 27-Sep-1991 + +Environment: + + Kernel mode, FSD + +Revision History: + + + Aaron Ogus (aarono) 27-Sep-1991 + Changes to NdisRequest() format required changes to pending operations + Sean Selitrennikoff (seanse) Dec-1991 + Changes to meet standard model for NDIS drivers. + +--*/ + +#include +#include +#include "elnkhrd.h" +#include "elnksft.h" + + +VOID +HandlePendingOperations( + IN PVOID SystemSpecific1, + IN PVOID DeferredContext, // will be a pointer to the adapter block + IN PVOID SystemSpecific2, + IN PVOID SystemSpecific3 + ) + +/*++ + +Routine Description: + + Called by pending functions to process elements on the + pending queue. + +Arguments: + + DeferredContext - will be a pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)DeferredContext); + PELNKII_PEND_DATA PendOp; + PELNKII_OPEN TmpOpen; + NDIS_STATUS Status; + + UNREFERENCED_PARAMETER(SystemSpecific1); + UNREFERENCED_PARAMETER(SystemSpecific2); + UNREFERENCED_PARAMETER(SystemSpecific3); + + NdisAcquireSpinLock(&AdaptP->Lock); + + AdaptP->References++; + + // + // If an operation is being dispatched or a reset is running, exit. + // + + if ((!AdaptP->ResetInProgress) && (AdaptP->PendOp == NULL)) { + + for (;;) { + + // + // We hold SpinLock here. + // + + if (AdaptP->PendQueue != NULL) { + + // + // Take the request off the queue and dispatch it. + // + + PendOp = AdaptP->PendQueue; + + AdaptP->PendQueue = PendOp->Next; + + if (PendOp == AdaptP->PendQTail) { + + AdaptP->PendQTail = NULL; + + } + + AdaptP->PendOp = PendOp; + + NdisReleaseSpinLock(&AdaptP->Lock); + + Status = ((PendOp->RequestType == NdisRequestClose) || + (PendOp->RequestType == NdisRequestGeneric3)) ? + DispatchSetMulticastAddressList(AdaptP) : + DispatchSetPacketFilter(AdaptP); + + TmpOpen = PendOp->Open; + + if ((PendOp->RequestType != NdisRequestGeneric1) && + (PendOp->RequestType != NdisRequestClose)) { // Close Adapter + + + // + // Complete it since it previously pended. + // + + NdisCompleteRequest(PendOp->Open->NdisBindingContext, + PNDIS_REQUEST_FROM_PELNKII_PEND_DATA(PendOp), + Status); + + } + + // + // This will call CompleteClose if necessary. + // + + NdisAcquireSpinLock(&AdaptP->Lock); + + TmpOpen->ReferenceCount--; + + if (AdaptP->ResetInProgress) { + + // + // We have to stop processing requests. + // + + break; // jump to BREAK_LOCATION + } + + } else { + + break; // jump to BREAK_LOCATION + + } + } + + // + // BREAK_LOCATION + // + // Hold Lock here. + // + + AdaptP->PendOp = NULL; + + if (AdaptP->ResetInProgress) { + + // + // Exited due to a reset, indicate that the DPC + // handler is done for now. + // + + AdaptP->References--; + + NdisReleaseSpinLock(&AdaptP->Lock); + + ElnkiiResetStageDone(AdaptP, MULTICAST_RESET); + + return; + } + + } + + if (AdaptP->CloseQueue != NULL) { + + PELNKII_OPEN OpenP; + PELNKII_OPEN TmpOpenP; + PELNKII_OPEN PrevOpenP; + + // + // Check for an open that may have closed + // + + OpenP = AdaptP->CloseQueue; + PrevOpenP = NULL; + + while (OpenP != NULL) { + + if (OpenP->ReferenceCount > 0) { + + OpenP = OpenP->NextOpen; + PrevOpenP = OpenP; + + continue; + + } + +#if DBG + + if (!OpenP->Closing) { + + DbgPrint("BAD CLOSE: %d\n", OpenP->ReferenceCount); + + DbgBreakPoint(); + + OpenP = OpenP->NextOpen; + PrevOpenP = OpenP; + + continue; + + } + +#endif + + + // + // The last reference is completed; a previous call to ElnkiiCloseAdapter + // will have returned NDIS_STATUS_PENDING, so things must be finished + // off now. + // + + // + // Check if MaxLookAhead needs adjusting. + // + + if (OpenP->LookAhead == AdaptP->MaxLookAhead) { + + ElnkiiAdjustMaxLookAhead(AdaptP); + + } + + NdisReleaseSpinLock(&AdaptP->Lock); + + NdisCompleteCloseAdapter (OpenP->NdisBindingContext, NDIS_STATUS_SUCCESS); + + NdisAcquireSpinLock(&AdaptP->Lock); + + // + // Remove from close list + // + + if (PrevOpenP != NULL) { + + PrevOpenP->NextOpen = OpenP->NextOpen; + + } else { + + AdaptP->CloseQueue = OpenP->NextOpen; + } + + // + // Go to next one + // + + TmpOpenP = OpenP; + OpenP = OpenP->NextOpen; + + NdisFreeMemory(TmpOpenP, sizeof(ELNKII_OPEN), 0); + + } + + if ((AdaptP->CloseQueue == NULL) && (AdaptP->OpenQueue == NULL)) { + + // + // We can stop the card. + // + + CardStop(AdaptP); + + } + + } + + ELNKII_DO_DEFERRED(AdaptP); + +} + +NDIS_STATUS +DispatchSetPacketFilter( + IN PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + Sets the appropriate bits in the adapter filters + and modifies the card Receive Configuration Register if needed. + +Arguments: + + AdaptP - Pointer to the adapter block + +Return Value: + + The final status (always NDIS_STATUS_SUCCESS). + +Notes: + + - Note that to receive all multicast packets the multicast + registers on the card must be filled with 1's. To be + promiscuous that must be done as well as setting the + promiscuous physical flag in the RCR. This must be done + as long as ANY protocol bound to this adapter has their + filter set accordingly. + +--*/ + + +{ + UINT PacketFilter; + + PacketFilter = ETH_QUERY_FILTER_CLASSES(AdaptP->FilterDB); + + // + // See what has to be put on the card. + // + + if (PacketFilter & (NDIS_PACKET_TYPE_ALL_MULTICAST | NDIS_PACKET_TYPE_PROMISCUOUS)) { + + // + // need "all multicast" now. + // + + CardSetAllMulticast(AdaptP); // fills it with 1's + + } else { + + // + // No longer need "all multicast". + // + + DispatchSetMulticastAddressList(AdaptP); + + } + + + // + // The multicast bit in the RCR should be on if ANY protocol wants + // multicast/all multicast packets (or is promiscuous). + // + + if (PacketFilter & (NDIS_PACKET_TYPE_ALL_MULTICAST | + NDIS_PACKET_TYPE_MULTICAST | + NDIS_PACKET_TYPE_PROMISCUOUS)) { + + AdaptP->NicReceiveConfig |= RCR_MULTICAST; + + } else { + + AdaptP->NicReceiveConfig &= ~RCR_MULTICAST; + + } + + + // + // The promiscuous physical bit in the RCR should be on if ANY + // protocol wants to be promiscuous. + // + + if (PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) { + + AdaptP->NicReceiveConfig |= RCR_ALL_PHYS; + + } else { + + AdaptP->NicReceiveConfig &= ~RCR_ALL_PHYS; + + } + + + // + // The broadcast bit in the RCR should be on if ANY protocol wants + // broadcast packets (or is promiscuous). + // + + if (PacketFilter & (NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_PROMISCUOUS)) { + + AdaptP->NicReceiveConfig |= RCR_BROADCAST; + + } else { + + AdaptP->NicReceiveConfig &= ~RCR_BROADCAST; + + } + + + CardSetReceiveConfig(AdaptP); + + return NDIS_STATUS_SUCCESS; +} + + + +NDIS_STATUS +DispatchSetMulticastAddressList( + IN PELNKII_ADAPTER AdaptP + ) + +/*++ + +Routine Description: + + Sets the multicast list for this open + +Arguments: + + AdaptP - Pointer to the adapter block + +Return Value: + +Implementation Note: + + When invoked, we are to make it so that the multicast list in the filter + package becomes the multicast list for the adapter. To do this, we + determine the required contents of the NIC multicast registers and + update them. + + +--*/ +{ + + // + // Update the local copy of the NIC multicast regs and copy them to the NIC + // + + CardFillMulticastRegs(AdaptP); + + CardCopyMulticastRegs(AdaptP); + + return NDIS_STATUS_SUCCESS; +} + + diff --git a/private/ntos/ndis/elnkii/sources b/private/ntos/ndis/elnkii/sources new file mode 100644 index 000000000..7ec0c29cc --- /dev/null +++ b/private/ntos/ndis/elnkii/sources @@ -0,0 +1,45 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=ndis + +TARGETNAME=elnkii +TARGETPATH=\nt\public\sdk\lib +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..\..\inc + +SOURCES=elnkii.c \ + interrup.c\ + card.c\ + pend.c \ + elnkii.rc + +RELATIVE_DEPTH=..\.. + +MSC_WARNING_LEVEL=/W3 /WX + -- cgit v1.2.3