NT4/private/ntos/ndis/sonic/interrup.c
2020-09-30 17:12:29 +02:00

1695 lines
42 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-1992 Microsoft Corporation
Module Name:
interrup.c
Abstract:
This is a part of the driver for the National Semiconductor SONIC
Ethernet controller. It contains the interrupt-handling routines.
This driver conforms to the NDIS 3.0 miniport interface.
Author:
Adam Barr (adamba) 16-Jan-1991
Environment:
Kernel Mode - Or whatever is the equivalent.
Revision History:
--*/
#include <ndis.h>
#include <sonichrd.h>
#include <sonicsft.h>
#define REMOVE_EOL_AND_ACK(A,L) \
{ \
PSONIC_ADAPTER _A = A; \
SONIC_REMOVE_END_OF_LIST(L); \
if ((_A)->ReceiveDescriptorsExhausted) { \
SONIC_WRITE_PORT((_A), SONIC_INTERRUPT_STATUS, \
SONIC_INT_RECEIVE_DESCRIPTORS \
); \
(_A)->ReceiveDescriptorsExhausted = FALSE; \
} \
}
#define WRITE_RWP_AND_ACK(A,RWP) \
{ \
PSONIC_ADAPTER _A = A; \
SONIC_WRITE_PORT((_A), SONIC_RESOURCE_WRITE, (RWP)); \
if ((_A)->ReceiveBuffersExhausted) { \
SONIC_WRITE_PORT((_A), SONIC_INTERRUPT_STATUS, \
SONIC_INT_RECEIVE_BUFFERS \
); \
(_A)->ReceiveBuffersExhausted = FALSE; \
} \
}
VOID
SonicDisableInterrupt(
IN NDIS_HANDLE MiniportAdapterContext
)
/*++
Routine Description:
This routine is used to turn off all interrupts from the adapter.
Arguments:
Context - A pointer to the adapter block
Return Value:
None.
--*/
{
PSONIC_ADAPTER Adapter = PSONIC_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
SONIC_WRITE_PORT(Adapter, SONIC_INTERRUPT_MASK,
0
);
}
VOID
SonicEnableInterrupt(
IN NDIS_HANDLE MiniportAdapterContext
)
/*++
Routine Description:
This routine is used to turn on all interrupts from the adapter.
Arguments:
Context - A pointer to the adapter block
Return Value:
None.
--*/
{
PSONIC_ADAPTER Adapter = PSONIC_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
SONIC_WRITE_PORT(Adapter, SONIC_INTERRUPT_MASK,
SONIC_INT_DEFAULT_VALUE
);
}
STATIC
BOOLEAN
ProcessReceiveInterrupts(
IN PSONIC_ADAPTER Adapter
);
STATIC
BOOLEAN
ProcessTransmitInterrupts(
IN PSONIC_ADAPTER Adapter
);
STATIC
VOID
ProcessInterrupt(
IN PSONIC_ADAPTER Adapter
);
extern
VOID
SonicInterruptService(
OUT PBOOLEAN InterruptRecognized,
OUT PBOOLEAN QueueDpc,
IN PVOID Context
)
/*++
Routine Description:
Interrupt service routine for the sonic. This routine only gets
called during initial initialization of the adapter.
Arguments:
InterruptRecognized - Boolean value which returns TRUE if the
ISR recognizes the interrupt as coming from this adapter.
QueueDpc - TRUE if a DPC should be queued.
Context - Really a pointer to the adapter.
Return Value:
Returns true if the card ISR is non-zero.
--*/
{
//
// Will hold the value from the ISR.
//
USHORT LocalIsrValue;
//
// Holds the pointer to the adapter.
//
PSONIC_ADAPTER Adapter = Context;
SONIC_READ_PORT(Adapter, SONIC_INTERRUPT_STATUS, &LocalIsrValue);
if (LocalIsrValue != 0x0000) {
#if DBG
if (SonicDbg) {
if (LocalIsrValue & (
SONIC_INT_BUS_RETRY |
SONIC_INT_LOAD_CAM_DONE |
SONIC_INT_PROG_INTERRUPT |
SONIC_INT_TRANSMIT_ERROR |
SONIC_INT_RECEIVE_DESCRIPTORS |
SONIC_INT_RECEIVE_BUFFERS |
SONIC_INT_RECEIVE_OVERFLOW |
SONIC_INT_CRC_TALLY_ROLLOVER |
SONIC_INT_FAE_TALLY_ROLLOVER |
SONIC_INT_MP_TALLY_ROLLOVER
)) {
DbgPrint("ISR %x\n", LocalIsrValue);
}
}
#endif
//
// Check for exhausted receive descriptors.
//
if ( LocalIsrValue & SONIC_INT_RECEIVE_DESCRIPTORS ) {
Adapter->ReceiveDescriptorsExhausted = TRUE;
LocalIsrValue &= ~SONIC_INT_RECEIVE_DESCRIPTORS;
}
//
// Check for exhausted receive buffers.
//
if ( LocalIsrValue & SONIC_INT_RECEIVE_BUFFERS ) {
Adapter->ReceiveBuffersExhausted = TRUE;
LocalIsrValue &= ~SONIC_INT_RECEIVE_BUFFERS;
}
//
// It's our interrupt. Clear only those bits that we got
// in this read of ISR.
//
*InterruptRecognized = TRUE;
SONIC_WRITE_PORT(
Adapter,
SONIC_INTERRUPT_STATUS,
(USHORT)(LocalIsrValue)
);
//
// If we got a LOAD_CAM_DONE interrupt, it may be
// because our first initialization is complete.
// We check this here because on some systems the
// DeferredProcessing call might not interrupt
// the initialization process.
//
if (LocalIsrValue & SONIC_INT_LOAD_CAM_DONE) {
if (Adapter->FirstInitialization) {
Adapter->FirstInitialization = FALSE;
#if DBG
{
USHORT PortValue;
SONIC_READ_PORT(Adapter, SONIC_SILICON_REVISION, &PortValue);
if (SonicDbg) {
DbgPrint("SONIC Initialized: Revision %d\n", PortValue);
}
}
#endif
}
}
//
// No deferred processing is needed.
//
*QueueDpc = FALSE;
return;
} else {
*InterruptRecognized = FALSE;
*QueueDpc = FALSE;
return;
}
}
VOID
SonicHandleInterrupt(
IN NDIS_HANDLE MiniportAdapterContext
)
/*++
Routine Description:
This DPR routine is queued by the wrapper after every interrupt
and also by other routines within the driver that notice that
some deferred processing needs to be done. It's main
job is to call the interrupt processing code.
Arguments:
MiniportAdapterContext - Really a pointer to the adapter.
Return Value:
None.
--*/
{
//
// A pointer to the adapter object.
//
PSONIC_ADAPTER Adapter = (PSONIC_ADAPTER)MiniportAdapterContext;
//
// Holds a value of the Interrupt Status register.
//
USHORT Isr;
USHORT ThisIsrValue;
//
// TRUE if the main loop did something.
//
BOOLEAN DidSomething = TRUE;
//
// TRUE if ReceiveComplete needs to be indicated.
//
BOOLEAN IndicateReceiveComplete = FALSE;
//
// Grab any simulated interrupts
//
Isr = Adapter->SimulatedIsr;
Adapter->SimulatedIsr = 0;
//
// Loop until there are no more processing sources.
//
#if DBG
if (SonicDbg) {
DbgPrint("In Dpr\n");
}
#endif
//
// If the hardware has failed, do nothing until the reset completes
//
if (Adapter->HardwareFailure) {
return;
}
while (DidSomething) {
//
// Set this FALSE now, so if nothing happens we
// will exit.
//
DidSomething = FALSE;
//
// Read in all outstanding interrupt reasons.
//
SONIC_READ_PORT(Adapter, SONIC_INTERRUPT_STATUS, &ThisIsrValue);
//
// Check for exhausted receive descriptors.
//
if ( ThisIsrValue & SONIC_INT_RECEIVE_DESCRIPTORS ) {
Adapter->ReceiveDescriptorsExhausted = TRUE;
ThisIsrValue &= ~SONIC_INT_RECEIVE_DESCRIPTORS;
}
//
// Check for exhausted receive buffers.
//
if ( ThisIsrValue & SONIC_INT_RECEIVE_BUFFERS ) {
Adapter->ReceiveBuffersExhausted = TRUE;
ThisIsrValue &= ~SONIC_INT_RECEIVE_BUFFERS;
}
//
// Acknowledge these interrupts
//
SONIC_WRITE_PORT(Adapter, SONIC_INTERRUPT_STATUS, ThisIsrValue);
//
// Save these bits.
//
Isr |= ThisIsrValue;
//
// Check for receive interrupts.
//
if (Isr & SONIC_INT_PACKET_RECEIVED) {
DidSomething = TRUE;
} else {
goto DoneProcessingReceives;
}
//
// 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.
//
// ProcessReceiveInterrupts may exit early if it has
// processed too many receives in a row. In this case
// it returns FALSE, we don't clear the PACKET_RECEIVED
// bit, and we will loop through here again.
//
if (ProcessReceiveInterrupts(Adapter)) {
Isr &= ~SONIC_INT_PACKET_RECEIVED;
}
//
// If the hardware failed, then exit
//
if (Adapter->HardwareFailure) {
return;
}
IndicateReceiveComplete = TRUE;
//
// We set ProcessingReceiveInterrupt to FALSE here so
// that we can issue new receive indications while
// the rest of the loop is proceeding.
//
DoneProcessingReceives:;
//
// Check the interrupt source and other reasons
// for processing. If there are no reasons to
// process then exit this loop.
//
if ((Isr & (SONIC_INT_LOAD_CAM_DONE |
SONIC_INT_PROG_INTERRUPT |
SONIC_INT_PACKET_TRANSMITTED |
SONIC_INT_TRANSMIT_ERROR |
SONIC_INT_CRC_TALLY_ROLLOVER |
SONIC_INT_FAE_TALLY_ROLLOVER |
SONIC_INT_MP_TALLY_ROLLOVER))) {
DidSomething = TRUE;
} else {
goto DoneProcessingGeneral;
}
//
// Check for a Load CAM completing.
//
// This can happen due to a change in the CAM, due to
// initialization (in which case we won't save the bit
// and will not come through this code), or a reset
// (in which case ResetInProgress will be TRUE).
//
//
// Check for non-packet related happenings.
//
if (Isr & SONIC_INT_LOAD_CAM_DONE) {
Isr &= ~SONIC_INT_LOAD_CAM_DONE;
if (Adapter->ResetInProgress) {
//
// This initialization is from a reset.
//
Adapter->ResetInProgress = FALSE;
//
// Restart the chip.
//
SonicStartChip(Adapter);
//
// Complete the reset.
//
NdisMResetComplete(
Adapter->MiniportAdapterHandle,
NDIS_STATUS_SUCCESS,
TRUE
);
} else { // ResetInProgress FALSE
NdisMSetInformationComplete(
Adapter->MiniportAdapterHandle,
NDIS_STATUS_SUCCESS);
}
}
//
// Now process any remaining interrupts.
//
if (Isr & (SONIC_INT_CRC_TALLY_ROLLOVER |
SONIC_INT_FAE_TALLY_ROLLOVER |
SONIC_INT_MP_TALLY_ROLLOVER)) {
//
// If any of the counters overflowed, then we update
// the counter by adding one to the high sixteen bits
// and reading the register for the low sixteen bits.
//
if (Isr & SONIC_INT_CRC_TALLY_ROLLOVER) {
USHORT CrcError;
SONIC_READ_PORT(Adapter, SONIC_CRC_ERROR, &CrcError);
Adapter->GeneralOptional[GO_RECEIVE_CRC - GO_ARRAY_START] =
(Adapter->GeneralOptional[GO_RECEIVE_CRC - GO_ARRAY_START] & 0xffff0000) +
0x10000 +
CrcError;
}
if (Isr & SONIC_INT_FAE_TALLY_ROLLOVER) {
USHORT FaError;
SONIC_READ_PORT(Adapter, SONIC_FRAME_ALIGNMENT_ERROR, &FaError);
Adapter->MediaMandatory[MM_RECEIVE_ERROR_ALIGNMENT] =
(Adapter->MediaMandatory[MM_RECEIVE_ERROR_ALIGNMENT] & 0xffff0000) +
0x10000 +
FaError;
}
if (Isr & SONIC_INT_MP_TALLY_ROLLOVER) {
USHORT MissedPacket;
SONIC_READ_PORT(Adapter, SONIC_MISSED_PACKET, &MissedPacket);
Adapter->GeneralMandatory[GM_RECEIVE_NO_BUFFER] =
(Adapter->GeneralMandatory[GM_RECEIVE_NO_BUFFER] & 0xffff0000) +
0x10000 +
MissedPacket;
}
Isr &= ~(SONIC_INT_CRC_TALLY_ROLLOVER |
SONIC_INT_FAE_TALLY_ROLLOVER |
SONIC_INT_MP_TALLY_ROLLOVER);
}
//
// Process the transmit interrupts if there are any.
//
if (Isr & (SONIC_INT_PROG_INTERRUPT |
SONIC_INT_PACKET_TRANSMITTED |
SONIC_INT_TRANSMIT_ERROR)) {
{
if (!ProcessTransmitInterrupts(Adapter)) {
//
// Process interrupts returns false if it
// finds no more work to do. If this so we
// turn off the transmitter interrupt source.
//
Isr &= ~ (SONIC_INT_PROG_INTERRUPT |
SONIC_INT_PACKET_TRANSMITTED |
SONIC_INT_TRANSMIT_ERROR);
}
}
}
DoneProcessingGeneral:;
}
if (IndicateReceiveComplete) {
//
// We have indicated at least one packet, we now
// need to signal that the receives are complete.
//
NdisMEthIndicateReceiveComplete(Adapter->MiniportAdapterHandle);
}
}
#define SONIC_RECEIVE_LIMIT 10
STATIC
BOOLEAN
ProcessReceiveInterrupts(
IN PSONIC_ADAPTER Adapter
)
/*++
Routine Description:
Process the packets that have finished receiving.
Arguments:
Adapter - The adapter to indicate to.
Return Value:
FALSE if we exit because we have indicated SONIC_RECEIVE_LIMIT
packets, TRUE if there are no more packets.
--*/
{
//
// We don't get here unless there was a receive. Loop through
// the receive descriptors starting at the last known descriptor
// owned by the hardware that begins a packet.
//
// Examine each receive ring descriptor for errors.
//
// We keep an array whose elements are indexed by the ring
// index of the receive descriptors. The arrays elements are
// the virtual addresses of the buffers pointed to by
// each ring descriptor.
//
// When we have the entire packet (and error processing doesn't
// prevent us from indicating it), we give the routine that
// processes the packet through the filter, the buffers virtual
// address (which is always the lookahead size) and as the
// MAC context the index to the first and last ring descriptors
// comprising the packet.
//
//
// Pointer to the receive descriptor being examined.
//
PSONIC_RECEIVE_DESCRIPTOR CurrentDescriptor =
&Adapter->ReceiveDescriptorArea[
Adapter->CurrentReceiveDescriptorIndex];
//
// Index of the RBA that the next packet should
// come out of.
//
UINT CurrentRbaIndex = Adapter->CurrentReceiveBufferIndex;
//
// Virtual address of the start of that RBA.
//
PVOID CurrentRbaVa = Adapter->ReceiveBufferArea[CurrentRbaIndex];
//
// Physical address of the start of that RBA.
//
SONIC_PHYSICAL_ADDRESS CurrentRbaPhysical =
SONIC_GET_RECEIVE_RESOURCE_ADDRESS(&Adapter->ReceiveResourceArea[CurrentRbaIndex]);
//
// The size of the packet.
//
UINT PacketSize;
//
// The amount of data received in the RBA (will be PacketSize +
// 4 for the CRC).
USHORT ByteCount;
//
// The amount of lookahead data to indicate.
//
UINT LookAheadSize;
//
// The offset of the start of the packet in its receive buffer.
//
UINT PacketOffsetInRba;
//
// The Physical address of the packet.
//
SONIC_PHYSICAL_ADDRESS PacketPhysical;
//
// A pointer to the link field at the end of the receive
// descriptor before the one we are processing.
//
PSONIC_PHYSICAL_ADDRESS PrevLinkFieldAddr;
//
// The virtual address of the packet.
//
PVOID PacketVa;
//
// The status of the packet.
//
USHORT ReceiveStatus;
//
// Is the descriptor in use by the sonic.
//
USHORT InUse;
//
// Used tempoerarily to determine PacketPhysical.
//
USHORT PacketAddress;
//
// How many packets we have indicated this time.
//
UINT PacketsIndicated = 0;
//
// Used with update shared memory.
//
NDIS_PHYSICAL_ADDRESS TempAddress;
#if DBG
//
// For debugging, save the previous receive descriptor.
//
static SONIC_RECEIVE_DESCRIPTOR PreviousDescriptor;
#endif
do {
//
// Ensure that the system memory copy of the
// receive descriptor is up-to-date.
//
NdisMUpdateSharedMemory(
Adapter->MiniportAdapterHandle,
sizeof(SONIC_RECEIVE_DESCRIPTOR) *
Adapter->NumberOfReceiveDescriptors,
Adapter->ReceiveDescriptorArea,
Adapter->ReceiveDescriptorAreaPhysical
);
//
// Check to see whether we own the packet. If
// we don't then simply return to the caller.
//
NdisReadRegisterUshort(&CurrentDescriptor->InUse, &InUse);
if (InUse != SONIC_OWNED_BY_SYSTEM) {
return TRUE;
}
//
// Figure out the virtual address of the packet.
//
NdisReadRegisterUshort((PUSHORT)&CurrentDescriptor->LowPacketAddress,
(PUSHORT)&PacketAddress);
PacketPhysical = PacketAddress;
NdisReadRegisterUshort((PUSHORT)&CurrentDescriptor->HighPacketAddress,
(PUSHORT)&PacketAddress);
PacketPhysical += PacketAddress << 16;
if ((PacketPhysical < CurrentRbaPhysical) ||
(PacketPhysical > (CurrentRbaPhysical + SONIC_SIZE_OF_RECEIVE_BUFFERS)) ) {
//
// Something is wrong, the packet is not in the
// receive buffer that we expect it in.
//
SONIC_PHYSICAL_ADDRESS ResourcePhysical;
PSONIC_RECEIVE_RESOURCE CurrentReceiveResource;
UINT i;
if (Adapter->WrongRbaErrorLogCount++ < 5) {
//
// Log an error the first five times this happens.
//
NdisWriteErrorLogEntry(
Adapter->MiniportAdapterHandle,
NDIS_ERROR_CODE_HARDWARE_FAILURE,
6,
processReceiveInterrupts,
SONIC_ERRMSG_WRONG_RBA,
(ULONG)CurrentRbaPhysical,
(ULONG)PacketPhysical,
(ULONG)CurrentDescriptor,
(ULONG)Adapter->ReceiveDescriptorArea
);
#if DBG
DbgPrint("SONIC: RBA at %lx [%lx], Packet at %lx\n", CurrentRbaPhysical, CurrentRbaVa, PacketPhysical);
DbgPrint("descriptor %lx, start %lx, prev %lx\n",
(ULONG)CurrentDescriptor,
(ULONG)Adapter->ReceiveDescriptorArea,
&PreviousDescriptor);
#endif
}
//
// Attempt to recover by advancing the relevant pointers
// to where the SONIC thinks the packet is. First we need
// to find the receive buffer that matches the indicated
// physical address.
//
for (
i = 0, CurrentReceiveResource = Adapter->ReceiveResourceArea;
i < Adapter->NumberOfReceiveBuffers;
i++,CurrentReceiveResource++
) {
ResourcePhysical = SONIC_GET_RECEIVE_RESOURCE_ADDRESS(CurrentReceiveResource);
if ((PacketPhysical >= ResourcePhysical) &&
(PacketPhysical <
(ResourcePhysical + SONIC_SIZE_OF_RECEIVE_BUFFERS))) {
//
// We found the receive resource.
//
break;
}
}
if (i == Adapter->NumberOfReceiveBuffers) {
//
// Quit now, there is a failure by the chip, and we
// cannot find the receive.
//
Adapter->HardwareFailure = TRUE;
return FALSE;
}
//
// Update our pointers.
//
Adapter->CurrentReceiveBufferIndex = i;
CurrentRbaIndex = i;
CurrentRbaVa = Adapter->ReceiveBufferArea[i];
CurrentRbaPhysical =
SONIC_GET_RECEIVE_RESOURCE_ADDRESS(&Adapter->ReceiveResourceArea[i]);
//
// Flush the receive buffer.
//
NdisFlushBuffer(
Adapter->ReceiveNdisBufferArea[i],
FALSE
);
//
// Ensure that we release buffers before this one
// back to the sonic.
//
WRITE_RWP_AND_ACK(
Adapter,
(USHORT)(CurrentRbaPhysical & 0xffff)
);
}
PacketOffsetInRba = PacketPhysical - CurrentRbaPhysical;
//
// Check that the packet was received correctly...note that
// we always compute PacketOffsetInRba and ByteCount,
// which are needed to skip the packet even if we do not
// indicate it.
//
NdisReadRegisterUshort(&CurrentDescriptor->ReceiveStatus, &ReceiveStatus);
NdisReadRegisterUshort(&CurrentDescriptor->ByteCount, &ByteCount);
if (!(ReceiveStatus & SONIC_RCR_PACKET_RECEIVED_OK)) {
#if DBG
if (SonicDbg) {
DbgPrint("SONIC: Skipping %lx\n", ReceiveStatus);
}
#endif
goto SkipIndication;
}
//
// Prepare to indicate the packet.
//
PacketSize = ByteCount - 4;
if ( PacketSize > 1514 ) {
#if DBG
DbgPrint("SONIC: Skipping packet, length %d\n", PacketSize);
#endif
goto SkipIndication;
}
if (PacketSize < SONIC_INDICATE_MAXIMUM) {
LookAheadSize = PacketSize;
} else {
LookAheadSize = SONIC_INDICATE_MAXIMUM;
}
PacketVa = (PUCHAR) CurrentRbaVa + PacketOffsetInRba;
//
// Ensure that the system memory version of this RBA is up-to-date.
//
NdisFlushBuffer(
Adapter->ReceiveNdisBufferArea[CurrentRbaIndex],
FALSE
);
NdisSetPhysicalAddressLow(
TempAddress,
SONIC_GET_RECEIVE_RESOURCE_ADDRESS(&Adapter->ReceiveResourceArea[CurrentRbaIndex])
);
NdisSetPhysicalAddressHigh(TempAddress, 0);
NdisMUpdateSharedMemory(
Adapter->MiniportAdapterHandle,
SONIC_SIZE_OF_RECEIVE_BUFFERS,
Adapter->ReceiveBufferArea[CurrentRbaIndex],
TempAddress
);
//
// Indicate the packet to the protocol.
//
if ( PacketSize < 14 ) {
//
// Must have at least the destination address
//
if (PacketSize >= ETH_LENGTH_OF_ADDRESS) {
//
// Runt packet
//
NdisMEthIndicateReceive(
Adapter->MiniportAdapterHandle,
(NDIS_HANDLE)((PUCHAR)PacketVa + 14), // context
PacketVa, // header buffer
PacketSize, // header buffer size
NULL, // lookahead buffer
0, // lookahead buffer size
0 // packet size
);
}
} else {
NdisMEthIndicateReceive(
Adapter->MiniportAdapterHandle,
(NDIS_HANDLE)((PUCHAR)PacketVa + 14), // context
PacketVa, // header buffer
14, // header buffer size
(PUCHAR)PacketVa + 14, // lookahead buffer
LookAheadSize - 14, // lookahead buffer size
PacketSize - 14 // packet size
);
}
SkipIndication:;
#if DBG
SONIC_MOVE_MEMORY (&PreviousDescriptor, CurrentDescriptor, sizeof(SONIC_RECEIVE_DESCRIPTOR));
#endif
//
// Give the packet back to the hardware.
//
NdisWriteRegisterUlong(&CurrentDescriptor->InUse, SONIC_OWNED_BY_SONIC);
//
// And re-set the EOL fields correctly.
//
SONIC_SET_END_OF_LIST(
&(CurrentDescriptor->Link)
);
if (CurrentDescriptor == Adapter->ReceiveDescriptorArea) {
//
// we are at the first one
//
PrevLinkFieldAddr = &(Adapter->LastReceiveDescriptor->Link);
} else {
PrevLinkFieldAddr = &((CurrentDescriptor-1)->Link);
}
REMOVE_EOL_AND_ACK(
Adapter,
PrevLinkFieldAddr
);
//
// Now figure out if the RBA is done with.
//
if (ReceiveStatus & SONIC_RCR_LAST_PACKET_IN_RBA) {
//
// Advance which RBA we are looking at.
//
++CurrentRbaIndex;
if (CurrentRbaIndex == Adapter->NumberOfReceiveBuffers) {
CurrentRbaIndex = 0;
}
Adapter->CurrentReceiveBufferIndex = CurrentRbaIndex;
CurrentRbaVa = Adapter->ReceiveBufferArea[CurrentRbaIndex];
CurrentRbaPhysical =
SONIC_GET_RECEIVE_RESOURCE_ADDRESS(&Adapter->ReceiveResourceArea[CurrentRbaIndex]);
WRITE_RWP_AND_ACK(
Adapter,
(USHORT)(CurrentRbaPhysical & 0xffff)
);
}
//
// Update statistics now based on the receive status.
//
if (ReceiveStatus & SONIC_RCR_PACKET_RECEIVED_OK) {
++Adapter->GeneralMandatory[GM_RECEIVE_GOOD];
if (ReceiveStatus & SONIC_RCR_BROADCAST_RECEIVED) {
++Adapter->GeneralOptionalFrameCount[GO_BROADCAST_RECEIVES];
SonicAddUlongToLargeInteger(
&Adapter->GeneralOptionalByteCount[GO_BROADCAST_RECEIVES],
PacketSize);
} else if (ReceiveStatus & SONIC_RCR_MULTICAST_RECEIVED) {
++Adapter->GeneralOptionalFrameCount[GO_MULTICAST_RECEIVES];
SonicAddUlongToLargeInteger(
&Adapter->GeneralOptionalByteCount[GO_MULTICAST_RECEIVES],
PacketSize);
} else {
++Adapter->GeneralOptionalFrameCount[GO_DIRECTED_RECEIVES];
SonicAddUlongToLargeInteger(
&Adapter->GeneralOptionalByteCount[GO_DIRECTED_RECEIVES],
PacketSize);
}
} else {
++Adapter->GeneralMandatory[GM_RECEIVE_BAD];
if (ReceiveStatus & SONIC_RCR_CRC_ERROR) {
++Adapter->GeneralOptional[GO_RECEIVE_CRC - GO_ARRAY_START];
} else if (ReceiveStatus & SONIC_RCR_FRAME_ALIGNMENT) {
++Adapter->MediaMandatory[MM_RECEIVE_ERROR_ALIGNMENT];
}
}
//
// Advance our pointers to the next packet.
if (CurrentDescriptor == Adapter->LastReceiveDescriptor) {
Adapter->CurrentReceiveDescriptorIndex = 0;
} else {
++(Adapter->CurrentReceiveDescriptorIndex);
}
CurrentDescriptor = &Adapter->ReceiveDescriptorArea[
Adapter->CurrentReceiveDescriptorIndex];
++PacketsIndicated;
} while (PacketsIndicated < SONIC_RECEIVE_LIMIT);
//
// Indicate that we returned because we indicated SONIC_RECEIVE_
// LIMIT packets, not because we ran out of packets to indicate.
//
return FALSE;
}
STATIC
BOOLEAN
ProcessTransmitInterrupts(
IN PSONIC_ADAPTER Adapter
)
/*++
Routine Description:
Process the packets that have finished transmitting.
Arguments:
Adapter - The adapter that was sent from.
Return Value:
This function will return TRUE if it finished up the
send on a packet. It will return FALSE if for some
reason there was no packet to process.
--*/
{
//
// Index into the ring to packet structure. This index points
// to the first ring entry for the first buffer used for transmitting
// the packet.
//
UINT DescriptorIndex;
//
// The transmit desctiptor for the packet at Transmitting Descriptor
//
PSONIC_TRANSMIT_DESCRIPTOR TransmitDescriptor;
//
// Temporarily holds the transmit descriptor after TransmitDescriptor
//
PSONIC_TRANSMIT_DESCRIPTOR NextTransmitDescriptor;
//
// Pointer to the packet that started this transmission.
//
PNDIS_PACKET OwningPacket;
//
// Points to the reserved part of the OwningPacket.
//
PSONIC_PACKET_RESERVED Reserved;
//
// Used to hold the ring to packet mapping information so that
// we can release the ring entries as quickly as possible.
//
SONIC_DESCRIPTOR_TO_PACKET SavedDescriptorMapping;
//
// The status of the transmit.
//
USHORT TransmitStatus;
//
// Get hold of the first transmitted packet.
//
//
// 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->TransmittingDescriptor !=
Adapter->FirstUncommittedDescriptor) {
DescriptorIndex =
Adapter->TransmittingDescriptor - Adapter->TransmitDescriptorArea;
} else {
return FALSE;
}
//
// We put the mapping into a local variable so that we
// can return the mapping as soon as possible.
//
SavedDescriptorMapping = Adapter->DescriptorToPacket[DescriptorIndex];
//
// Get a pointer to the transmit descriptor for this packet.
//
TransmitDescriptor = Adapter->TransmitDescriptorArea + DescriptorIndex;
//
// Get a pointer to the owning packet and the reserved part of
// the packet.
//
OwningPacket = SavedDescriptorMapping.OwningPacket;
Reserved = PSONIC_RESERVED_FROM_PACKET(OwningPacket);
//
// Check that status bits were written into the transmit
// descriptor.
//
NdisReadRegisterUshort((PUSHORT)&TransmitDescriptor->TransmitStatus, &TransmitStatus);
if (!(TransmitStatus & SONIC_TCR_STATUS_MASK)) {
//
// The transmit has not completed.
//
return FALSE;
} else {
//
// Holds whether the packet successfully transmitted or not.
//
BOOLEAN Successful = TRUE;
//
// Length of the packet
//
UINT PacketLength;
//
// Points to data in NDIS_BUFFER
//
PUCHAR BufferVa;
//
// Points to the current ndis buffer being walked.
//
PNDIS_BUFFER CurrentBuffer;
Adapter->WakeUpTimeout = FALSE;
if (SavedDescriptorMapping.UsedSonicBuffer) {
//
// This packet used adapter buffers. We can
// now return these buffers to the adapter.
//
//
// The adapter buffer descriptor that was allocated to this packet.
//
PSONIC_BUFFER_DESCRIPTOR BufferDescriptor = Adapter->SonicBuffers +
SavedDescriptorMapping.SonicBuffersIndex;
//
// Index of the listhead that heads the list that the adapter
// buffer descriptor belongs too.
//
INT ListHeadIndex = BufferDescriptor->Next;
//
// Put the adapter buffer back on the free list.
//
BufferDescriptor->Next = Adapter->SonicBufferListHeads[ListHeadIndex];
Adapter->SonicBufferListHeads[ListHeadIndex] = SavedDescriptorMapping.SonicBuffersIndex;
} else {
//
// Which map register we use for this buffer.
//
UINT CurMapRegister;
//
// The transmit is finished, so we can release
// the physical mapping used for it.
//
NdisQueryPacket(
OwningPacket,
NULL,
NULL,
&CurrentBuffer,
NULL
);
CurMapRegister = DescriptorIndex * SONIC_MAX_FRAGMENTS;
while (CurrentBuffer) {
NdisMCompleteBufferPhysicalMapping(
Adapter->MiniportAdapterHandle,
CurrentBuffer,
CurMapRegister
);
++CurMapRegister;
NdisGetNextBuffer(
CurrentBuffer,
&CurrentBuffer
);
}
}
//
// Now release the transmit descriptor, since we have
// gotten all the information we need from it.
//
if (TransmitDescriptor == Adapter->LastTransmitDescriptor) {
NextTransmitDescriptor = Adapter->TransmitDescriptorArea;
} else {
NextTransmitDescriptor = Adapter->TransmittingDescriptor + 1;
}
if (TransmitStatus &
(SONIC_TCR_EXCESSIVE_DEFERRAL |
SONIC_TCR_EXCESSIVE_COLLISIONS |
SONIC_TCR_FIFO_UNDERRUN |
SONIC_TCR_BYTE_COUNT_MISMATCH)) {
//
// If the packet completed with an abort state, then we
// need to restart the transmitter unless we are the
// last transmit queued up. We set CTDA to point after
// this descriptor in any case.
//
#if DBG
if (SonicDbg) {
DbgPrint ("SONIC: Advancing CTDA after abort\n");
}
#endif
SONIC_WRITE_PORT(Adapter, SONIC_CURR_TRANSMIT_DESCRIPTOR,
SONIC_GET_LOW_PART_ADDRESS(
NdisGetPhysicalAddressLow(Adapter->TransmitDescriptorAreaPhysical) +
((PUCHAR)NextTransmitDescriptor -
(PUCHAR)Adapter->TransmitDescriptorArea))
);
if (Adapter->FirstUncommittedDescriptor != NextTransmitDescriptor) {
#if DBG
if (SonicDbg) {
DbgPrint ("SONIC: Restarting transmit after abort\n");
}
#endif
SONIC_WRITE_PORT(Adapter, SONIC_COMMAND, SONIC_CR_TRANSMIT_PACKETS);
}
}
Adapter->TransmittingDescriptor = NextTransmitDescriptor;
Adapter->NumberOfAvailableDescriptors++;
//
// Check if the packet completed OK, and update statistics.
//
if (!(TransmitStatus & SONIC_TCR_PACKET_TRANSMITTED_OK)) {
#if DBG
if (SonicDbg) {
DbgPrint("SONIC: Transmit failed: %lx\n", TransmitStatus);
}
#endif
Successful = FALSE;
++Adapter->GeneralMandatory[GM_TRANSMIT_BAD];
if (TransmitStatus & SONIC_TCR_EXCESSIVE_COLLISIONS) {
++Adapter->MediaOptional[MO_TRANSMIT_MAX_COLLISIONS];
}
if (TransmitStatus & SONIC_TCR_FIFO_UNDERRUN) {
++Adapter->MediaOptional[MO_TRANSMIT_UNDERRUN];
}
} else {
INT Collisions = (TransmitStatus & SONIC_TCR_COLLISIONS_MASK) >> SONIC_TCR_COLLISIONS_SHIFT;
UINT Tmp;
Successful = TRUE;
++Adapter->GeneralMandatory[GM_TRANSMIT_GOOD];
if (Collisions > 0) {
if (Collisions == 1) {
++Adapter->MediaMandatory[MM_TRANSMIT_ONE_COLLISION];
} else {
++Adapter->MediaMandatory[MM_TRANSMIT_MORE_COLLISIONS];
}
}
if (TransmitStatus &
(SONIC_TCR_DEFERRED_TRANSMISSION |
SONIC_TCR_NO_CARRIER_SENSE |
SONIC_TCR_CARRIER_LOST |
SONIC_TCR_OUT_OF_WINDOW)) {
if (TransmitStatus & SONIC_TCR_DEFERRED_TRANSMISSION) {
++Adapter->MediaOptional[MO_TRANSMIT_DEFERRED];
}
if (TransmitStatus & SONIC_TCR_NO_CARRIER_SENSE) {
++Adapter->MediaOptional[MO_TRANSMIT_HEARTBEAT_FAILURE];
}
if (TransmitStatus & SONIC_TCR_CARRIER_LOST) {
++Adapter->MediaOptional[MO_TRANSMIT_TIMES_CRS_LOST];
}
if (TransmitStatus & SONIC_TCR_OUT_OF_WINDOW) {
++Adapter->MediaOptional[MO_TRANSMIT_LATE_COLLISIONS];
}
}
NdisQueryPacket(
OwningPacket,
NULL,
NULL,
&CurrentBuffer,
&PacketLength
);
NdisQueryBuffer(
CurrentBuffer,
(PVOID *)&BufferVa,
&Tmp
);
if (BufferVa[0] == 0xFF) {
++Adapter->GeneralOptionalFrameCount[GO_BROADCAST_TRANSMITS];
SonicAddUlongToLargeInteger(
&Adapter->GeneralOptionalByteCount[GO_BROADCAST_TRANSMITS],
PacketLength);
} else if (BufferVa[0] & 0x01) {
++Adapter->GeneralOptionalFrameCount[GO_MULTICAST_TRANSMITS];
SonicAddUlongToLargeInteger(
&Adapter->GeneralOptionalByteCount[GO_MULTICAST_TRANSMITS],
PacketLength);
} else {
++Adapter->GeneralOptionalFrameCount[GO_DIRECTED_TRANSMITS];
SonicAddUlongToLargeInteger(
&Adapter->GeneralOptionalByteCount[GO_DIRECTED_TRANSMITS],
PacketLength);
}
}
//
// Remove packet from queue.
//
if (Adapter->LastFinishTransmit == OwningPacket) {
Adapter->FirstFinishTransmit = NULL;
Adapter->LastFinishTransmit = NULL;
} else {
Adapter->FirstFinishTransmit = Reserved->Next;
}
#ifdef CHECK_DUP_SENDS
{
VOID SonicRemovePacketFromList(PSONIC_ADAPTER, PNDIS_PACKET);
SonicRemovePacketFromList(Adapter, OwningPacket);
}
#endif
NdisMSendComplete(
Adapter->MiniportAdapterHandle,
OwningPacket,
((Successful)?(NDIS_STATUS_SUCCESS):(NDIS_STATUS_FAILURE))
);
Adapter->PacketsSinceLastInterrupt = 0;
return TRUE;
}
}
BOOLEAN
SonicCheckForHang(
IN PVOID MiniportAdapterContext
)
/*++
Routine Description:
This routine checks on the transmit descriptor ring. This is
to solve problems where no status is written into the currently
transmitting transmit descriptor, which hangs our transmit
completion processing. If we detect this state, we simulate
a transmit interrupt.
Arguments:
MiniportAdapterContext - Really a pointer to the adapter.
Return Value:
FALSE - This routine actually does a wake up, rather than having
the wrapper do it.
--*/
{
PSONIC_ADAPTER Adapter = (PSONIC_ADAPTER)MiniportAdapterContext;
UINT DescriptorIndex;
PSONIC_TRANSMIT_DESCRIPTOR TransmitDescriptor;
USHORT TransmitStatus;
//
// If hardware failed, then return now
//
if (Adapter->HardwareFailure) {
return(TRUE);
}
if (Adapter->WakeUpTimeout) {
//
// We had a pending send the last time we ran,
// and it has not been completed...we need to fake
// its completion.
//
ASSERT (Adapter->TransmittingDescriptor !=
Adapter->FirstUncommittedDescriptor);
DescriptorIndex =
Adapter->TransmittingDescriptor - Adapter->TransmitDescriptorArea;
TransmitDescriptor = Adapter->TransmitDescriptorArea + DescriptorIndex;
NdisReadRegisterUshort((PUSHORT)&TransmitDescriptor->TransmitStatus, &TransmitStatus);
if (!(TransmitStatus & SONIC_TCR_STATUS_MASK)) {
NdisWriteRegisterUshort ((PUSHORT)&TransmitDescriptor->TransmitStatus,
SONIC_TCR_PACKET_TRANSMITTED_OK);
#if DBG
DbgPrint ("SONIC: Woke up descriptor at %lx\n", TransmitDescriptor);
#endif
}
Adapter->SimulatedIsr |= SONIC_INT_PACKET_TRANSMITTED;
Adapter->WakeUpTimeout = FALSE;
if (Adapter->WakeUpErrorCount < 10) {
Adapter->WakeUpErrorCount++;
NdisWriteErrorLogEntry(
Adapter->MiniportAdapterHandle,
NDIS_ERROR_CODE_HARDWARE_FAILURE,
1,
(ULONG)0xFFFFFFFF
);
}
} else if (Adapter->TransmittingDescriptor !=
Adapter->FirstUncommittedDescriptor) {
DescriptorIndex =
Adapter->TransmittingDescriptor - Adapter->TransmitDescriptorArea;
TransmitDescriptor = Adapter->TransmitDescriptorArea + DescriptorIndex;
NdisReadRegisterUshort((PUSHORT)&TransmitDescriptor->TransmitStatus, &TransmitStatus);
if (!(TransmitStatus & SONIC_TCR_STATUS_MASK)) {
Adapter->WakeUpTimeout = TRUE;
}
}
return(FALSE);
}