summaryrefslogtreecommitdiffstats
path: root/private/ntos/ndis/ne3200/interrup.c
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/ndis/ne3200/interrup.c
downloadNT4.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.c835
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;
+
+}
+