diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/ndis/elnkii/elnkii.c | 4010 |
1 files changed, 4010 insertions, 0 deletions
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--; + +} |