diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/ndis/ne3200/interrup.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/ndis/ne3200/interrup.c')
-rw-r--r-- | private/ntos/ndis/ne3200/interrup.c | 835 |
1 files changed, 835 insertions, 0 deletions
diff --git a/private/ntos/ndis/ne3200/interrup.c b/private/ntos/ndis/ne3200/interrup.c new file mode 100644 index 000000000..d1fef19b0 --- /dev/null +++ b/private/ntos/ndis/ne3200/interrup.c @@ -0,0 +1,835 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + interrup.c + +Abstract: + + This module contains the interrupt-processing code for the Novell + NE3200 NDIS 3.0 miniport driver. + +Author: + + Keith Moore (KeithMo) 04-Feb-1991 + +Environment: + +Revision History: + +--*/ + +#include <ne3200sw.h> + +// +// Forward declarations of functions in this file +// +STATIC +BOOLEAN +FASTCALL +NE3200ProcessReceiveInterrupts( + IN PNE3200_ADAPTER Adapter + ); + +STATIC +BOOLEAN +FASTCALL +NE3200ProcessCommandInterrupts( + IN PNE3200_ADAPTER Adapter + ); + +VOID +NE3200Isr( + OUT PBOOLEAN InterruptRecognized, + OUT PBOOLEAN QueueDpc, + IN PVOID Context + ) + +/*++ + +Routine Description: + + Interrupt service routine for the NE3200. It's main job is + to get the value of the System Doorbell Register and record the + changes in the adapters own list of interrupt reasons. + +Arguments: + + Interrupt - Interrupt object for the NE3200. + + Context - Really a pointer to the adapter. + +Return Value: + + Returns true if the interrupt really was from our NE3200. + +--*/ + +{ + + // + // Will hold the value from the System Doorbell Register. + // + UCHAR SystemDoorbell; + + // + // Holds the pointer to the adapter. + // + PNE3200_ADAPTER Adapter = Context; + + IF_LOG('i'); + + // + // Get the interrupt status + // + NE3200_READ_SYSTEM_DOORBELL_INTERRUPT(Adapter, &SystemDoorbell); + + // + // Are any of the bits expected? + // + if (SystemDoorbell & NE3200_SYSTEM_DOORBELL_MASK) { + + IF_LOG(SystemDoorbell); + + // + // It's our interrupt. Disable further interrupts. + // + NE3200_WRITE_SYSTEM_DOORBELL_MASK( + Adapter, + 0 + ); + + IF_LOG('I'); + + // + // Return that we recognize it + // + *InterruptRecognized = TRUE; + + } else { + + IF_LOG('I'); + + // + // Return that we don't recognize it + // + *InterruptRecognized = FALSE; + + } + + // + // No Dpc call is needed for initialization + // + *QueueDpc = FALSE; + +} + + +STATIC +VOID +NE3200HandleInterrupt( + IN NDIS_HANDLE MiniportAdapterContext + ) + +/*++ + +Routine Description: + + Main routine for processing interrupts. + +Arguments: + + Adapter - The Adapter to process interrupts for. + +Return Value: + + None. + +--*/ + +{ + // + // The adapter for which to handle interrupts. + // + PNE3200_ADAPTER Adapter = ((PNE3200_ADAPTER)MiniportAdapterContext); + + // + // Holds a value of SystemDoorbellInterrupt. + // + USHORT SystemDoorbell = 0; + + // + // Should NdisMEthIndicateReceiveComplete() be called? + // + BOOLEAN IndicateReceiveComplete = FALSE; + + IF_LOG('p'); + + // + // Get the current reason for interrupts + // + NE3200_READ_SYSTEM_DOORBELL_INTERRUPT(Adapter, &SystemDoorbell); + + // + // Acknowledge those interrupts. + // + NE3200_WRITE_SYSTEM_DOORBELL_INTERRUPT( + Adapter, + SystemDoorbell + ); + + // + // Get just the important ones. + // + SystemDoorbell &= NE3200_SYSTEM_DOORBELL_MASK; + + while (TRUE) { + + // + // If we have a reset in progress then start the reset. + // + + if (Adapter->ResetInProgress) goto check_reset; + +not_reset: + + // + // Check the interrupt source and other reasons + // for processing. If there are no reasons to + // process then exit this loop. + // + + // + // Check the interrupt vector and see if there are any + // more receives to process. After we process any + // other interrupt source we always come back to the top + // of the loop to check if any more receive packets have + // come in. This is to lessen the probability that we + // drop a receive. + // + if (SystemDoorbell & NE3200_SYSTEM_DOORBELL_PACKET_RECEIVED) { + + IF_LOG('r'); + + // + // Process receive interrupts. + // + if (NE3200ProcessReceiveInterrupts(Adapter)) { + + // + // If done with all receives, then clear the interrupt + // from our status. + // + SystemDoorbell &= ~NE3200_SYSTEM_DOORBELL_PACKET_RECEIVED; + + } + + // + // Note that we got a receive. + // + Adapter->ReceiveInterrupt = TRUE; + IndicateReceiveComplete = TRUE; + + IF_LOG('R'); + + } else if ((SystemDoorbell & + NE3200_SYSTEM_DOORBELL_COMMAND_COMPLETE) == 0 ) { + + // + // If the command is not completed, and no receives, then + // exit the loop. + // + break; + } + + // + // First we check that this is a packet that was transmitted + // but not already processed. Recall that this routine + // will be called repeatedly until this tests false, Or we + // hit a packet that we don't completely own. + // + if ((Adapter->FirstCommandOnCard == NULL) || + (Adapter->FirstCommandOnCard->Hardware.State != NE3200_STATE_EXECUTION_COMPLETE)) { + + // + // No more work to do, clear the interrupt status bit. + // + IF_LOG('V'); + SystemDoorbell &= ~NE3200_SYSTEM_DOORBELL_COMMAND_COMPLETE; + + } else { + + IF_LOG('c'); + + // + // Complete this transmit. + // + if ( NE3200ProcessCommandInterrupts(Adapter) ) { + SystemDoorbell &= ~NE3200_SYSTEM_DOORBELL_COMMAND_COMPLETE; + } + + IF_LOG('C'); + + } + + // + // Get more interrupt bits for processing + // + if (SystemDoorbell == 0) { + + // + // Get the current reason for interrupts + // + NE3200_READ_SYSTEM_DOORBELL_INTERRUPT(Adapter, &SystemDoorbell); + + // + // Acknowledge those interrupts. + // + NE3200_WRITE_SYSTEM_DOORBELL_INTERRUPT( + Adapter, + SystemDoorbell + ); + + // + // Get just the important ones. + // + SystemDoorbell &= NE3200_SYSTEM_DOORBELL_MASK; + + } + + } + +done: + + IF_LOG('P'); + + if (IndicateReceiveComplete) { + + NdisMEthIndicateReceiveComplete(Adapter->MiniportAdapterHandle); + + } + + return; + +check_reset: + + if (Adapter->ResetState != NE3200ResetStateComplete) { + + // + // The adapter is not in a state where it can process a reset. + // + goto not_reset; + + } + + // + // Start the reset + // + NE3200DoAdapterReset(Adapter); + + goto done; + +} + +STATIC +BOOLEAN +FASTCALL +NE3200ProcessReceiveInterrupts( + IN PNE3200_ADAPTER Adapter + ) + +/*++ + +Routine Description: + + Process the packets that have the adapter has finished receiving. + +Arguments: + + Adapter - The adapter to indicate to. + +Return Value: + + Whether to clear interrupt bit or not. + +--*/ + +{ + + // + // We don't get here unless there was a receive. Loop through + // the receive blocks starting at the last known block owned by + // the hardware. + // + // Examine each receive block for errors. + // + // We keep an array whose elements are indexed by the block + // index of the receive blocks. The arrays elements are the + // virtual addresses of the buffers pointed to by each block. + // + // After we find a packet we give the routine that process the + // packet through the filter, the buffers virtual address (which + // is always the lookahead size) and as the MAC Context the + // index to the receive block. + // + + // + // Pointer to the receive block being examined. + // + PNE3200_SUPER_RECEIVE_ENTRY CurrentEntry = Adapter->ReceiveQueueHead; + + // + // Pointer to last receive block in the queue. + // + PNE3200_SUPER_RECEIVE_ENTRY LastEntry; + + // + // Limit the number of consecutive receives we will do. This way + // we do not starve transmit interrupts when processing many, many + // receives + // +#define MAX_RECEIVES_PROCESSED 10 + ULONG ReceivePacketCount = 0; + + + // + // Loop forever + // + while (TRUE) { + + // + // Ensure that our Receive Entry is on an even boundary. + // + ASSERT(!(NdisGetPhysicalAddressLow(CurrentEntry->Self) & 1)); + + // + // Check to see whether we own the packet. If + // we don't then simply return to the caller. + // + if (CurrentEntry->Hardware.State != NE3200_STATE_FREE) { + + // + // We've found a packet. Prepare the parameters + // for indication, then indicate. + // + if (ReceivePacketCount < MAX_RECEIVES_PROCESSED) { + + // + // Increment the limit. + // + ReceivePacketCount++; + + // + // Flush the receive buffer + // + NdisFlushBuffer(CurrentEntry->FlushBuffer, FALSE); + + // + // Check the packet for a runt + // + if ((UINT)(CurrentEntry->Hardware.FrameSize) < + NE3200_HEADER_SIZE) { + + if ((UINT)(CurrentEntry->Hardware.FrameSize) >= + NE3200_LENGTH_OF_ADDRESS) { + + // + // Runt Packet, indicate it. + // + NdisMEthIndicateReceive( + Adapter->MiniportAdapterHandle, + (NDIS_HANDLE)(CurrentEntry->ReceiveBuffer), + CurrentEntry->ReceiveBuffer, + (UINT)CurrentEntry->Hardware.FrameSize, + NULL, + 0, + 0 + ); + + } + + } else { + + // + // Good frame, indicate it + // + NdisMEthIndicateReceive( + Adapter->MiniportAdapterHandle, + (NDIS_HANDLE)(CurrentEntry->ReceiveBuffer), + CurrentEntry->ReceiveBuffer, + NE3200_HEADER_SIZE, + ((PUCHAR)CurrentEntry->ReceiveBuffer) + NE3200_HEADER_SIZE, + (UINT)CurrentEntry->Hardware.FrameSize - NE3200_HEADER_SIZE, + (UINT)CurrentEntry->Hardware.FrameSize - NE3200_HEADER_SIZE + ); + + } + + // + // Give the packet back to the hardware. + // + // Chain the current block onto the tail of the Receive Queue. + // + CurrentEntry->Hardware.NextPending = NE3200_NULL; + CurrentEntry->Hardware.State = NE3200_STATE_FREE; + + // + // Update receive ring + // + LastEntry = Adapter->ReceiveQueueTail; + LastEntry->Hardware.NextPending = + NdisGetPhysicalAddressLow(CurrentEntry->Self); + + // + // Update the queue tail. + // + Adapter->ReceiveQueueTail = LastEntry->NextEntry; + + // + // Advance to the next block. + // + CurrentEntry = CurrentEntry->NextEntry; + + // + // See if the adapter needs to be restarted. The NE3200 + // stops if it runs out receive buffers. Since we just + // released one, we restart the adapter. + // + if (LastEntry->Hardware.State != NE3200_STATE_FREE) { + + // + // We've exhausted all Receive Blocks. Now we + // must restart the adapter. + // + IF_LOG('O'); + NE3200StartChipAndDisableInterrupts(Adapter, Adapter->ReceiveQueueTail); + + } + + } else { + + // + // Update statistics, we are exiting to check for + // transmit interrupts. + // + Adapter->ReceiveQueueHead = CurrentEntry; + Adapter->GoodReceives += MAX_RECEIVES_PROCESSED+1; + + IF_LOG('o'); + + return FALSE; + + } + + } else { + + // + // All done, update statistics and exit. + // + Adapter->ReceiveQueueHead = CurrentEntry; + Adapter->GoodReceives += ReceivePacketCount; + return TRUE; + + } + + } + +} + + +STATIC +BOOLEAN +FASTCALL +NE3200ProcessCommandInterrupts( + IN PNE3200_ADAPTER Adapter + ) + +/*++ + +Routine Description: + + Process the Command Complete interrupts. + +Arguments: + + Adapter - The adapter that was sent from. + +Return Value: + + None. + +--*/ + +{ + + // + // Pointer to command block being processed. + // + PNE3200_SUPER_COMMAND_BLOCK CurrentCommandBlock = Adapter->FirstCommandOnCard; + + // + // Holds whether the packet successfully transmitted or not. + // + NDIS_STATUS StatusToReturn; + + // + // Pointer to the packet that started this transmission. + // + PNDIS_PACKET OwningPacket; + + // + // Points to the reserved part of the OwningPacket. + // + PNE3200_RESERVED Reserved; + + // + // Ensure that the Command Block is on an even boundary. + // + ASSERT(!(NdisGetPhysicalAddressLow(CurrentCommandBlock->Self) & 1)); + + IF_LOG('t'); + + if (CurrentCommandBlock->Hardware.CommandCode == NE3200_COMMAND_TRANSMIT) { + + // + // The current command block is from a transmit. + // + Adapter->SendInterrupt = TRUE; + + // + // Get a pointer to the owning packet and the reserved part of + // the packet. + // + OwningPacket = CurrentCommandBlock->OwningPacket; + Reserved = PNE3200_RESERVED_FROM_PACKET(OwningPacket); + + if (CurrentCommandBlock->UsedNE3200Buffer) { + + // + // This packet used adapter buffers. We can + // now return these buffers to the adapter. + // + + // + // The adapter buffer descriptor that was allocated to this packet. + // + PNE3200_BUFFER_DESCRIPTOR BufferDescriptor = Adapter->NE3200Buffers + + CurrentCommandBlock->NE3200BuffersIndex; + + // + // Put the adapter buffer back on the free list. + // + BufferDescriptor->Next = Adapter->NE3200BufferListHead; + Adapter->NE3200BufferListHead = CurrentCommandBlock->NE3200BuffersIndex; + + } else { + + // + // Ndis buffer mapped + // + PNDIS_BUFFER CurrentBuffer; + + // + // Map register that was used + // + UINT CurMapRegister; + + // + // The transmit is finished, so we can release + // the physical mapping used for it. + // + NdisQueryPacket( + OwningPacket, + NULL, + NULL, + &CurrentBuffer, + NULL + ); + + // + // Get starting map register + // + CurMapRegister = CurrentCommandBlock->CommandBlockIndex * + NE3200_MAXIMUM_BLOCKS_PER_PACKET; + + // + // For each buffer + // + while (CurrentBuffer) { + + // + // Finish the mapping + // + NdisMCompleteBufferPhysicalMapping( + Adapter->MiniportAdapterHandle, + CurrentBuffer, + CurMapRegister + ); + + ++CurMapRegister; + + NdisGetNextBuffer( + CurrentBuffer, + &CurrentBuffer + ); + + } + + } + + // + // If there was an error transmitting this + // packet, update our error counters. + // + if (CurrentCommandBlock->Hardware.Status & NE3200_STATUS_FATALERROR_MASK) { + + if (CurrentCommandBlock->Hardware.Status & + NE3200_STATUS_MAXIMUM_COLLISIONS) { + + Adapter->RetryFailure++; + + } else if (CurrentCommandBlock->Hardware.Status & + NE3200_STATUS_NO_CARRIER) { + + Adapter->LostCarrier++; + + } else if (CurrentCommandBlock->Hardware.Status & + NE3200_STATUS_HEART_BEAT) { + + Adapter->NoClearToSend++; + + } else if (CurrentCommandBlock->Hardware.Status & + NE3200_STATUS_DMA_UNDERRUN) { + + Adapter->UnderFlow++; + + } + + StatusToReturn = NDIS_STATUS_FAILURE; + + } else { + + // + // Update good transmit counter + // + StatusToReturn = NDIS_STATUS_SUCCESS; + Adapter->GoodTransmits++; + } + + ASSERT(sizeof(UINT) == sizeof(PNDIS_PACKET)); + + // + // Release the command block. + // + NE3200RelinquishCommandBlock(Adapter, CurrentCommandBlock); + + // + // The transmit is now complete + // + NdisMSendComplete( + Adapter->MiniportAdapterHandle, + OwningPacket, + StatusToReturn + ); + + } else if (CurrentCommandBlock->Hardware.CommandCode == + NE3200_COMMAND_READ_ADAPTER_STATISTICS) { + + // + // Release the command block. + // + Adapter->OutOfResources = + CurrentCommandBlock->Hardware.PARAMETERS.STATISTICS.ResourceErrors; + + Adapter->CrcErrors = + CurrentCommandBlock->Hardware.PARAMETERS.STATISTICS.CrcErrors; + + Adapter->AlignmentErrors = + CurrentCommandBlock->Hardware.PARAMETERS.STATISTICS.AlignmentErrors; + + Adapter->DmaOverruns = + CurrentCommandBlock->Hardware.PARAMETERS.STATISTICS.OverrunErrors; + + + // + // If this was from a request, complete it + // + if (Adapter->RequestInProgress) { + + NE3200FinishQueryInformation(Adapter); + + } + + // + // Release the command block + // + NE3200RelinquishCommandBlock(Adapter, CurrentCommandBlock); + + } else if (CurrentCommandBlock->Hardware.CommandCode == + NE3200_COMMAND_CLEAR_ADAPTER_STATISTICS) { + + // + // Release the command block. + // + NE3200RelinquishCommandBlock(Adapter, CurrentCommandBlock); + + } else if (CurrentCommandBlock->Hardware.CommandCode == + NE3200_COMMAND_SET_STATION_ADDRESS) { + + // + // Ignore + // + + } else { + + // + // The current command block is not from a transmit. + // + // Complete the request. + // + // if the CurrentCommandBlock->Set is FALSE, + // it means this multicast operation was not caused by + // a SetInformation request. + // + + if (CurrentCommandBlock->Set) { + + // + // Release the command block. + // + NE3200RelinquishCommandBlock(Adapter, CurrentCommandBlock); + + if (!Adapter->RequestInProgress) { + + // + // Bogus interrupt. Ignore it + // + + } else { + + IF_LOG(']'); + + Adapter->RequestInProgress = FALSE; + + // + // Complete the request + // + NdisMSetInformationComplete( + Adapter->MiniportAdapterHandle, + NDIS_STATUS_SUCCESS); + + } + + } else { + IF_LOG('T'); + return TRUE; + } + + } + + return FALSE; + +} + |