2020-09-30 17:12:29 +02:00

836 lines
20 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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;
}