summaryrefslogtreecommitdiffstats
path: root/private/ntos/ndis/elnkii
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/ndis/elnkii')
-rw-r--r--private/ntos/ndis/elnkii/card.c2780
-rw-r--r--private/ntos/ndis/elnkii/elnkhrd.h780
-rw-r--r--private/ntos/ndis/elnkii/elnkii.c4010
-rw-r--r--private/ntos/ndis/elnkii/elnkii.rc39
-rw-r--r--private/ntos/ndis/elnkii/elnksft.h1295
-rw-r--r--private/ntos/ndis/elnkii/interrup.c2426
-rw-r--r--private/ntos/ndis/elnkii/keywords.h56
-rw-r--r--private/ntos/ndis/elnkii/makefile6
-rw-r--r--private/ntos/ndis/elnkii/pend.c440
-rw-r--r--private/ntos/ndis/elnkii/sources45
10 files changed, 11877 insertions, 0 deletions
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 <ndis.h>
+#include <efilter.h>
+#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; i<ETH_LENGTH_OF_ADDRESS; i++) {
+
+ NdisRawReadPortUchar(AdaptP->MappedIoBaseAddr+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; i<ETH_LENGTH_OF_ADDRESS; i++) {
+
+ NdisRawWritePortUchar(AdaptP->MappedIoBaseAddr+(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 <ndis.h>
+#include <efilter.h>
+#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; i<NewAdaptP->NumBuffers; 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; i<AdaptP->NumBuffers; 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; i<AdaptP->NumBuffers; 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; i<AdaptP->NumBuffers; 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 <windows.h>
+#include <ntverp.h>
+
+/*-----------------------------------------------*/
+/* 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 <ndis.h>
+#include <efilter.h>
+#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; i<PacketListSize; i++) {
+
+ if (PacketList[i] == NewPacket) {
+
+ DbgPrint("dup send of %lx\n", NewPacket);
+
+ }
+
+ }
+
+ PacketList[PacketListSize] = NewPacket;
+
+ ++PacketListSize;
+
+}
+
+VOID
+RemovePacketFromList(
+ PELNKII_ADAPTER Adapter,
+ PNDIS_PACKET OldPacket
+ )
+{
+ INT i;
+
+ UNREFERENCED_PARAMETER(Adapter);
+
+ for (i=0; i<PacketListSize; i++) {
+
+ if (PacketList[i] == OldPacket) {
+
+ break;
+
+ }
+
+ }
+
+ if (i == PacketListSize) {
+
+ DbgPrint("bad remove of %lx\n", OldPacket);
+
+ } else {
+
+ --PacketListSize;
+
+ PacketList[i] = PacketList[PacketListSize];
+
+ }
+
+}
+
+#endif // DBG
+
+
+
+BOOLEAN
+ElnkiiInterruptHandler(
+ IN PVOID ServiceContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the interrupt handler which is registered with the operating
+ system. Only one interrupt is handled at one time, even if several
+ are pending (i.e. transmit complete and receive).
+
+Arguments:
+
+ ServiceContext - pointer to the adapter object
+
+Return Value:
+
+ TRUE, if the DPC is to be executed, otherwise FALSE.
+
+--*/
+
+{
+ PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)ServiceContext);
+
+ IF_LOG( ElnkiiLog('i');)
+ IF_LOUD( DbgPrint("In ElnkISR\n");)
+
+ IF_VERY_LOUD( DbgPrint( "ElnkiiInterruptHandler entered\n" );)
+
+ if (AdaptP->InCardTest) {
+
+ //
+ // 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; i<Length; i++) {
+ if (String1[i] != String2[i]) {
+ return (UINT)(-1);
+ }
+ }
+ return 0;
+}
+
+VOID
+ElnkiiCopyAndSend(
+ IN PELNKII_ADAPTER AdaptP
+ )
+
+/*++
+
+Routine Description:
+
+ Copies packets from the transmit queue to the board and starts
+ transmission as long as there is data waiting. Must be called
+ with Lock held.
+
+Arguments:
+
+ AdaptP - pointer to the adapter block
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ XMIT_BUF TmpBuf1;
+ PNDIS_PACKET Packet;
+
+
+ //
+ // Loop as long as there is data on the transmit queue
+ // and space for it on the card.
+ //
+
+ while ((AdaptP->BufferStatus[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 <ndis.h>
+#include <efilter.h>
+#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
+