summaryrefslogtreecommitdiffstats
path: root/private/ntos/ndis/elnkii/interrup.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/ndis/elnkii/interrup.c')
-rw-r--r--private/ntos/ndis/elnkii/interrup.c2426
1 files changed, 2426 insertions, 0 deletions
diff --git a/private/ntos/ndis/elnkii/interrup.c b/private/ntos/ndis/elnkii/interrup.c
new file mode 100644
index 000000000..109d5efc9
--- /dev/null
+++ b/private/ntos/ndis/elnkii/interrup.c
@@ -0,0 +1,2426 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ interrup.c
+
+Abstract:
+
+ This is a part of the driver for the National Semiconductor ElnkII
+ Ethernet controller. It contains the interrupt-handling routines.
+ This driver conforms to the NDIS 3.0 interface.
+
+ The overall structure and much of the code is taken from
+ the Lance NDIS driver by Tony Ercolano.
+
+Author:
+
+ Sean Selitrennikoff (seanse) Dec-1991
+
+Environment:
+
+ Kernel Mode - Or whatever is the equivalent on OS/2 and DOS.
+
+Revision History:
+
+--*/
+
+#include <ndis.h>
+#include <efilter.h>
+#include "elnkhrd.h"
+#include "elnksft.h"
+
+
+#if DBG
+#define STATIC
+#else
+#define STATIC static
+#endif
+
+#if DBG
+extern ULONG ElnkiiSendsCompletedAfterPendOk;
+extern ULONG ElnkiiSendsCompletedAfterPendFail;
+#endif
+
+
+UCHAR ElnkiiBroadcastAddress[ETH_LENGTH_OF_ADDRESS] =
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+
+//
+// This is used to pad short packets.
+//
+
+static UCHAR BlankBuffer[60] = " ";
+
+
+#if DBG
+ULONG ElnkiiSendsIssued = 0;
+ULONG ElnkiiSendsFailed = 0;
+ULONG ElnkiiSendsPended = 0;
+ULONG ElnkiiSendsCompletedImmediately = 0;
+ULONG ElnkiiSendsCompletedAfterPendOk = 0;
+ULONG ElnkiiSendsCompletedAfterPendFail = 0;
+ULONG ElnkiiSendsCompletedForReset = 0;
+#endif
+
+
+#if DBG
+
+#define ELNKII_LOG_SIZE 256
+UCHAR ElnkiiLogBuffer[ELNKII_LOG_SIZE]={0};
+UCHAR ElnkiiLogSaveBuffer[ELNKII_LOG_SIZE]={0};
+UINT ElnkiiLogLoc = 0;
+BOOLEAN ElnkiiLogSave = FALSE;
+UINT ElnkiiLogSaveLoc = 0;
+UINT ElnkiiLogSaveLeft = 0;
+
+extern
+VOID
+ElnkiiLog(UCHAR c) {
+
+ ElnkiiLogBuffer[ElnkiiLogLoc++] = c;
+
+ ElnkiiLogBuffer[(ElnkiiLogLoc + 4) % ELNKII_LOG_SIZE] = '\0';
+
+ if (ElnkiiLogLoc >= ELNKII_LOG_SIZE) ElnkiiLogLoc = 0;
+}
+
+#endif
+
+
+#if DBG
+
+#define PACKET_LIST_SIZE 256
+
+static PNDIS_PACKET PacketList[PACKET_LIST_SIZE] = {0};
+static PacketListSize = 0;
+
+VOID
+AddPacketToList(
+ PELNKII_ADAPTER Adapter,
+ PNDIS_PACKET NewPacket
+ )
+{
+ INT i;
+
+ UNREFERENCED_PARAMETER(Adapter);
+
+ for (i=0; i<PacketListSize; i++) {
+
+ if (PacketList[i] == NewPacket) {
+
+ DbgPrint("dup send of %lx\n", NewPacket);
+
+ }
+
+ }
+
+ PacketList[PacketListSize] = NewPacket;
+
+ ++PacketListSize;
+
+}
+
+VOID
+RemovePacketFromList(
+ PELNKII_ADAPTER Adapter,
+ PNDIS_PACKET OldPacket
+ )
+{
+ INT i;
+
+ UNREFERENCED_PARAMETER(Adapter);
+
+ for (i=0; i<PacketListSize; i++) {
+
+ if (PacketList[i] == OldPacket) {
+
+ break;
+
+ }
+
+ }
+
+ if (i == PacketListSize) {
+
+ DbgPrint("bad remove of %lx\n", OldPacket);
+
+ } else {
+
+ --PacketListSize;
+
+ PacketList[i] = PacketList[PacketListSize];
+
+ }
+
+}
+
+#endif // DBG
+
+
+
+BOOLEAN
+ElnkiiInterruptHandler(
+ IN PVOID ServiceContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the interrupt handler which is registered with the operating
+ system. Only one interrupt is handled at one time, even if several
+ are pending (i.e. transmit complete and receive).
+
+Arguments:
+
+ ServiceContext - pointer to the adapter object
+
+Return Value:
+
+ TRUE, if the DPC is to be executed, otherwise FALSE.
+
+--*/
+
+{
+ PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)ServiceContext);
+
+ IF_LOG( ElnkiiLog('i');)
+ IF_LOUD( DbgPrint("In ElnkISR\n");)
+
+ IF_VERY_LOUD( DbgPrint( "ElnkiiInterruptHandler entered\n" );)
+
+ if (AdaptP->InCardTest) {
+
+ //
+ // Ignore these random interrupts
+ //
+
+ IF_LOG( ElnkiiLog('I'); )
+ return(FALSE);
+
+ }
+
+ //
+ // Force the INT signal from the chip low. When the
+ // interrupt is acknowledged interrupts will be unblocked,
+ // which will cause a rising edge on the interrupt line
+ // if there is another interrupt pending on the card.
+ //
+
+ IF_LOUD( DbgPrint( " blocking interrupts\n" ); )
+
+ CardBlockInterrupts(AdaptP);
+
+ IF_LOG( ElnkiiLog('I'); )
+
+ return(TRUE);
+
+}
+
+VOID
+ElnkiiInterruptDpc(
+ IN PVOID SystemSpecific1,
+ IN PVOID InterruptContext,
+ IN PVOID SystemSpecific2,
+ IN PVOID SystemSpecific3
+ )
+/*++
+
+Routine Description:
+
+ This is the deffered processing routine for interrupts, it examines the
+ 'InterruptReg' to determine what deffered processing is necessary
+ and dispatches control to the Rcv and Xmt handlers.
+
+Arguments:
+ SystemSpecific1, SystemSpecific2, SystemSpecific3 - not used
+ InterruptContext - a handle to the adapter block.
+
+Return Value:
+
+ NONE.
+
+--*/
+{
+ PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)InterruptContext);
+
+ UCHAR InterruptStatus;
+ INTERRUPT_TYPE InterruptType;
+
+ UNREFERENCED_PARAMETER(SystemSpecific1);
+ UNREFERENCED_PARAMETER(SystemSpecific2);
+ UNREFERENCED_PARAMETER(SystemSpecific3);
+
+ IF_LOUD( DbgPrint("==>IntDpc\n");)
+
+ NdisDprAcquireSpinLock(&AdaptP->Lock);
+
+ AdaptP->References++;
+
+ //
+ // Get the interrupt bits
+ //
+
+ CardGetInterruptStatus(AdaptP, &InterruptStatus);
+
+ if (InterruptStatus != ISR_EMPTY) {
+
+ NdisRawWritePortUchar((AdaptP)->MappedIoBaseAddr+NIC_INTR_STATUS, InterruptStatus);
+
+ //
+ // InterruptStatus bits are used to dispatch to correct DPC and then cleared.
+ //
+
+ CardGetInterruptType(AdaptP,InterruptStatus, InterruptType);
+
+ } else {
+
+ InterruptType = UNKNOWN;
+
+ }
+
+
+ do {
+
+ while ((InterruptType != UNKNOWN) ||
+ ((AdaptP->LoopbackQueue != NULL) &&
+ !(AdaptP->ReceiveInProgress || AdaptP->ResetInProgress))) {
+
+ //
+ // Handle interrupts
+ //
+
+ switch (InterruptType) {
+
+ case COUNTER:
+ //
+ // One of the counters' MSB has been set, read in all
+ // the values just to be sure (and then exit below).
+ //
+
+ IF_LOUD( DbgPrint("DPC got COUNTER\n"); )
+
+ SyncCardUpdateCounters((PVOID)AdaptP);
+
+ InterruptStatus &= ~ISR_COUNTER; //clear the COUNTER interrupt bit.
+
+ break;
+
+ case OVERFLOW:
+
+ //
+ // Overflow interrupts are handled as part of a receive
+ // interrupt, so set a flag and then pretend to be a
+ // receive, in case there is no receive already being handled.
+ //
+
+ AdaptP->BufferOverflow = TRUE;
+
+ //
+ // Check if a send completed before the overflow came in.
+ //
+
+ if (AdaptP->TransmitInterruptPending &&
+ !(InterruptStatus & (ISR_XMIT | ISR_XMIT_ERR))) {
+
+ IF_LOG( ElnkiiLog('|');)
+
+ InterruptStatus |= ISR_XMIT;
+
+ AdaptP->OverflowRestartXmitDpc = FALSE;
+
+ }
+
+ IF_LOUD( DbgPrint("Overflow Int\n"); )
+ IF_VERY_LOUD( DbgPrint( " overflow interrupt\n" ); )
+
+ InterruptStatus &= ~ISR_OVERFLOW;
+
+
+ case RECEIVE:
+
+ //
+ // For receives, call this to ensure that another interrupt
+ // won't happen until the driver is ready.
+ //
+
+ IF_LOG( ElnkiiLog('R');)
+ IF_LOUD( DbgPrint("DPC got RCV\n"); )
+
+ if (!AdaptP->ReceiveInProgress) {
+
+ if (ElnkiiRcvInterruptDpc(AdaptP)) {
+
+ InterruptStatus &= ~(ISR_RCV | ISR_RCV_ERR);
+
+ }
+
+ } else {
+
+ //
+ // We can do this because the DPC in the RcvDpc will
+ // handle all the interrupts.
+ //
+
+ InterruptStatus &= ~(ISR_RCV | ISR_RCV_ERR);
+
+ }
+
+ break;
+
+ case TRANSMIT:
+
+ IF_LOG( ElnkiiLog('X');)
+
+#if DBG
+
+ ElnkiiLogSave = FALSE;
+
+#endif
+
+ //
+ // Acknowledge transmit interrupt now, because of MP systems we
+ // can get an interrupt from a receive that will look like a
+ // transmit interrupt because we haven't cleared the bit in the
+ // ISR. We are not concerned about multiple Receive interrupts
+ // since the receive handler guards against being entered twice.
+ //
+ // Since only one transmit interrupt can be pending at a time
+ // we know that no-one else can enter here now...
+ //
+ //
+ // This puts the result of the transmit in AdaptP->XmitStatus.
+ // SyncCardGetXmitStatus(AdaptP);
+ //
+
+ IF_LOUD( DbgPrint( " acking transmit interrupt\n" ); )
+
+ SyncCardGetXmitStatus(AdaptP);
+
+ AdaptP->WakeUpFoundTransmit = FALSE;
+
+ //
+ // This may be false if the card is currently handling an
+ // overflow and will restart the Dpc itself.
+ //
+ //
+ // If overflow handling then clear the transmit interrupt
+ //
+
+ ASSERT(!AdaptP->OverflowRestartXmitDpc);
+
+ if (AdaptP->ElnkiiHandleXmitCompleteRunning) {
+
+#if DBG
+ DbgBreakPoint();
+#endif
+
+ } else {
+
+ AdaptP->TransmitInterruptPending = FALSE;
+
+ ElnkiiXmitInterruptDpc(AdaptP);
+
+ }
+
+ IF_LOUD( DbgPrint( "DPC got XMIT\n" ); )
+
+ InterruptStatus &= ~(ISR_XMIT|ISR_XMIT_ERR);
+
+ break;
+
+ default:
+
+ //
+ // Create a rising edge on the interrupt line.
+ //
+
+ IF_LOUD( DbgPrint( "unhandled interrupt type: %x", InterruptType); )
+
+ break;
+ }
+
+ //
+ // Handle loopback
+ //
+
+ if ((AdaptP->LoopbackQueue != NULL) &&
+ !(AdaptP->ReceiveInProgress || AdaptP->ResetInProgress)) {
+
+ ElnkiiRcvInterruptDpc(AdaptP);
+
+ }
+
+ CardGetInterruptType(AdaptP,InterruptStatus, InterruptType);
+
+ }
+
+ CardGetInterruptStatus(AdaptP, &InterruptStatus);
+
+ if (InterruptStatus != ISR_EMPTY) {
+
+ NdisRawWritePortUchar((AdaptP)->MappedIoBaseAddr+NIC_INTR_STATUS, InterruptStatus);
+
+ }
+
+ CardGetInterruptType(AdaptP,InterruptStatus,InterruptType);
+
+ } while (InterruptType != UNKNOWN); // ISR says there's nothing left to do.
+
+ //
+ // Turn the IMR back on.
+ //
+
+ IF_LOUD( DbgPrint( " unblocking interrupts\n" ); )
+
+ AdaptP->NicInterruptMask = IMR_RCV | IMR_XMIT_ERR | IMR_XMIT | IMR_OVERFLOW;
+
+ CardUnblockInterrupts(AdaptP);
+
+ ELNKII_DO_DEFERRED(AdaptP);
+
+ IF_LOUD( DbgPrint("<==IntDpc\n");)
+
+}
+
+
+BOOLEAN
+ElnkiiRcvInterruptDpc(
+ IN PELNKII_ADAPTER AdaptP
+ )
+
+/*++
+
+Routine Description:
+
+ This is the real interrupt handler for receive/overflow interrupt.
+ The ElnkiiInterruptDpc calls it directly. It calls ElnkiiHandleReceive
+ if that function is not already executing (since it runs at DPC, this
+ would only be happening on a multiprocessor system (i.e. later DPC calls
+ will not run until previous ones are complete on a particular processor)).
+
+ NOTE: Called with the lock held!!!
+
+Arguments:
+
+ DeferredContext - A pointer to the adapter block.
+
+Return Value:
+
+ TRUE if done with all receives, else FALSE
+
+--*/
+
+{
+ PELNKII_OPEN TmpOpen;
+ PNDIS_PACKET LPacket;
+ PMAC_RESERVED Reserved;
+ BOOLEAN TransmitInterruptWasPending = FALSE;
+ INDICATE_STATUS IndicateStatus = INDICATE_OK;
+ BOOLEAN Done = TRUE;
+
+ //
+ // Do nothing if a RECEIVE is already being handled.
+ //
+
+ IF_LOUD( DbgPrint( "ElnkiiRcvInterruptDpc entered\n" );)
+
+ AdaptP->ReceiveInProgress = TRUE;
+
+ //
+ // At this point receive interrupts are disabled.
+ //
+
+ if (!AdaptP->ResetInProgress && AdaptP->BufferOverflow) {
+
+ NdisSynchronizeWithInterrupt(
+ &(AdaptP->NdisInterrupt),
+ (PVOID)SyncCardHandleOverflow,
+ (PVOID)AdaptP
+ );
+
+ }
+
+ //
+ // Loop
+ //
+
+ SyncCardGetCurrent(AdaptP);
+
+ while (!AdaptP->ResetInProgress) {
+
+ if (AdaptP->Current != AdaptP->NicNextPacket) {
+
+ AdaptP->LoopbackPacket = (PNDIS_PACKET)NULL;
+
+ AdaptP->ReceivePacketCount++;
+
+ NdisDprReleaseSpinLock(&AdaptP->Lock);
+
+ IndicateStatus = ElnkiiIndicatePacket(AdaptP);
+
+ NdisDprAcquireSpinLock(&AdaptP->Lock);
+
+ if (IndicateStatus == CARD_BAD) {
+
+ IF_LOG( ElnkiiLog('W');)
+
+ AdaptP->NicInterruptMask = IMR_XMIT | IMR_XMIT_ERR | IMR_OVERFLOW ;
+
+ CardReset(AdaptP);
+
+ break;
+
+ }
+
+ //
+ // Free the space used by packet on card.
+ //
+
+ AdaptP->NicNextPacket = AdaptP->PacketHeader[1];
+
+ //
+ // This will set BOUNDARY to one behind NicNextPacket.
+ //
+
+ CardSetBoundary(AdaptP);
+
+ if (AdaptP->ReceivePacketCount > 10) {
+
+ //
+ // Give transmit interrupts a chance
+ //
+
+ Done = FALSE;
+ AdaptP->ReceivePacketCount = 0;
+ break;
+
+ }
+
+ } else {
+
+ SyncCardGetCurrent(AdaptP);
+
+ if (AdaptP->Current == AdaptP->NicNextPacket) {
+
+ //
+ // End of loop -- no more packets
+ //
+
+ break;
+
+ }
+
+ }
+
+ }
+
+
+ if (AdaptP->BufferOverflow) {
+
+ IF_VERY_LOUD( DbgPrint( " overflow\n" ); )
+
+ AdaptP->BufferOverflow = FALSE;
+
+ NdisSynchronizeWithInterrupt( &(AdaptP->NdisInterrupt),
+ (PVOID)SyncCardAcknowledgeOverflow,
+ (PVOID)AdaptP );
+
+ //
+ // Undo loopback mode
+ //
+
+ CardStart(AdaptP);
+
+ IF_LOG ( ElnkiiLog('f');)
+
+ //
+ // Check if transmission needs to be queued or not
+ //
+
+ if (AdaptP->OverflowRestartXmitDpc && (AdaptP->CurBufXmitting != -1)) {
+
+ IF_LOG( ElnkiiLog('?');)
+
+ AdaptP->OverflowRestartXmitDpc = FALSE;
+
+ AdaptP->WakeUpFoundTransmit = FALSE;
+
+ AdaptP->TransmitInterruptPending = TRUE;
+
+ CardStartXmit(AdaptP);
+
+ }
+
+ }
+
+ //
+ // Now handle loopback packets.
+ //
+
+ IF_LOUD( DbgPrint( " checking loopback queue\n" );)
+
+ while (AdaptP->LoopbackQueue && !AdaptP->ResetInProgress) {
+
+ //
+ // Take the first packet off the loopback queue...
+ //
+
+ LPacket = AdaptP->LoopbackQueue;
+
+ Reserved = RESERVED(LPacket);
+
+ AdaptP->LoopbackQueue = RESERVED(AdaptP->LoopbackQueue)->NextPacket;
+
+ AdaptP->LoopbackPacket = LPacket;
+
+ AdaptP->FramesXmitGood++;
+
+ //
+ // Save this, since once we complete the send
+ // Reserved is no longer valid.
+ //
+
+ TmpOpen = Reserved->Open;
+
+#if DBG
+ IF_ELNKIIDEBUG( ELNKII_DEBUG_CHECK_DUP_SENDS ) {
+
+ RemovePacketFromList(AdaptP, LPacket);
+
+ }
+
+ ElnkiiSendsCompletedAfterPendOk++;
+#endif
+
+ //
+ // ... and indicate it.
+ //
+
+ NdisDprReleaseSpinLock(&AdaptP->Lock);
+
+ ElnkiiIndicateLoopbackPacket(AdaptP, AdaptP->LoopbackPacket);
+
+ //
+ // Complete the packet send.
+ //
+
+ NdisCompleteSend(
+ Reserved->Open->NdisBindingContext,
+ LPacket,
+ NDIS_STATUS_SUCCESS
+ );
+
+
+ NdisDprAcquireSpinLock(&AdaptP->Lock);
+
+ TmpOpen->ReferenceCount--;
+ }
+
+ //
+ // All receives are now done. Allow the receive indicator to run again.
+ //
+
+ AdaptP->ReceiveInProgress = FALSE;
+
+ if (AdaptP->ResetInProgress) {
+
+ return Done;
+
+ }
+
+ IF_LOUD( DbgPrint( " clearing ReceiveInProgress\n" );)
+
+ NdisDprReleaseSpinLock(&AdaptP->Lock);
+
+ //
+ // Finally, indicate ReceiveComplete to all protocols which received packets
+ //
+
+ EthFilterIndicateReceiveComplete(AdaptP->FilterDB);
+
+ NdisDprAcquireSpinLock(&AdaptP->Lock);
+
+ IF_LOUD( DbgPrint( "ElnkiiRcvInterruptDpc exiting\n" );)
+
+ return(Done);
+
+}
+
+VOID
+ElnkiiXmitInterruptDpc(
+ IN PELNKII_ADAPTER AdaptP
+ )
+
+/*++
+
+Routine Description:
+
+ This is the real interrupt handler for a transmit complete interrupt.
+ ElnkiiInterrupt queues a call to it. It calls ElnkiiHandleXmitComplete.
+
+ NOTE : Called with the spinlock held!! and returns with it released!!!
+
+Arguments:
+
+ AdaptP - A pointer to the adapter block.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ XMIT_BUF TmpBuf;
+
+ PNDIS_PACKET Packet;
+ PMAC_RESERVED Reserved;
+ PELNKII_OPEN TmpOpen;
+
+ IF_VERY_LOUD( DbgPrint( "ElnkiiXmitInterruptDpc entered\n" );)
+
+ AdaptP->WakeUpFoundTransmit = FALSE;
+
+ IF_LOG( ElnkiiLog('C');)
+
+ AdaptP->ElnkiiHandleXmitCompleteRunning = TRUE;
+
+ if (AdaptP->CurBufXmitting == -1)
+ {
+ AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE;
+
+ return;
+ }
+
+ //
+ // CurBufXmitting is not -1, which means nobody else
+ // will touch it.
+ //
+ Packet = AdaptP->Packets[AdaptP->CurBufXmitting];
+
+ ASSERT(Packet != (PNDIS_PACKET)NULL);
+
+ Reserved = RESERVED(Packet);
+
+ IF_LOUD( DbgPrint( "packet is 0x%lx\n", Packet );)
+
+#if DBG
+
+ if ((AdaptP->XmitStatus & TSR_XMIT_OK) == 0) {
+ IF_LOG(ElnkiiLog('E');)
+ IF_LOG(ElnkiiLog((UCHAR)AdaptP->XmitStatus);)
+ }
+
+ IF_ELNKIIDEBUG( ELNKII_DEBUG_CHECK_DUP_SENDS ) {
+ RemovePacketFromList(AdaptP, Packet);
+ }
+
+#endif
+
+
+ if (!Reserved->Loopback) {
+
+
+ //
+ // Complete the send if it is not to be loopbacked.
+ //
+
+ if (AdaptP->XmitStatus & TSR_XMIT_OK) {
+
+ AdaptP->FramesXmitGood++;
+
+#if DBG
+ ElnkiiSendsCompletedAfterPendOk++;
+
+#endif
+
+ } else {
+
+ AdaptP->FramesXmitBad++;
+
+#if DBG
+ ElnkiiSendsCompletedAfterPendFail++;
+#endif
+
+ }
+
+ //
+ // Save this, since once we complete the send
+ // Reserved is no longer valid.
+ //
+
+ TmpOpen = Reserved->Open;
+
+ IF_LOG( ElnkiiLog('p');)
+
+ NdisDprReleaseSpinLock(&AdaptP->Lock);
+
+ NdisCompleteSend(Reserved->Open->NdisBindingContext,
+ Packet,
+ AdaptP->XmitStatus & TSR_XMIT_OK ?
+ NDIS_STATUS_SUCCESS : NDIS_STATUS_FAILURE);
+
+ NdisDprAcquireSpinLock(&AdaptP->Lock);
+
+ TmpOpen->ReferenceCount--;
+
+ } else {
+
+ //
+ // Put it on the loopback queue
+ //
+
+ if (AdaptP->LoopbackQueue == (PNDIS_PACKET)NULL) {
+
+ AdaptP->LoopbackQueue = Packet;
+ AdaptP->LoopbackQTail = Packet;
+
+ } else {
+
+ RESERVED(AdaptP->LoopbackQTail)->NextPacket = Packet;
+ AdaptP->LoopbackQTail = Packet;
+
+ }
+
+ Reserved->NextPacket = (PNDIS_PACKET)NULL;
+
+ }
+
+ //
+ // Mark the current transmit as done.
+ //
+
+ AdaptP->Packets[AdaptP->CurBufXmitting] = (PNDIS_PACKET)NULL;
+
+ AdaptP->BufferStatus[AdaptP->CurBufXmitting] = EMPTY;
+
+ TmpBuf = NextBuf(AdaptP, AdaptP->CurBufXmitting);
+
+ //
+ // See what to do next.
+ //
+
+ switch (AdaptP->BufferStatus[TmpBuf]) {
+
+
+ case FULL:
+
+ //
+ // The next packet is ready to go -- only happens with
+ // more than one transmit buffer.
+ //
+
+ IF_LOUD( DbgPrint( " next packet ready to go\n" );)
+
+ if (AdaptP->ResetInProgress) {
+
+ //
+ // A reset just started, abort.
+ //
+
+ AdaptP->CurBufXmitting = -1;
+
+ AdaptP->BufferStatus[TmpBuf] = EMPTY; // to ack the reset
+
+ AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE;
+
+ NdisDprReleaseSpinLock(&AdaptP->Lock);
+
+ ElnkiiResetStageDone(AdaptP, XMIT_STOPPED);
+
+ NdisDprAcquireSpinLock(&AdaptP->Lock);
+
+ } else {
+
+ //
+ // Start the transmission and check for more.
+ //
+
+ AdaptP->CurBufXmitting = TmpBuf;
+
+ IF_LOG( ElnkiiLog('2');)
+
+#if DBG
+
+ ElnkiiLogSave = TRUE;
+ ElnkiiLogSaveLeft = 20;
+
+#endif
+
+ AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE;
+
+ //
+ // If we are currently handling an overflow, then we need to let
+ // the overflow handler send this packet...
+ //
+
+ if (AdaptP->BufferOverflow) {
+
+ AdaptP->OverflowRestartXmitDpc = TRUE;
+
+ IF_LOG( ElnkiiLog('O');)
+
+ } else {
+
+ //
+ // This is used to check if stopping the chip prevented
+ // a transmit complete interrupt from coming through (it
+ // is cleared in the ISR if a transmit DPC is queued).
+ //
+
+ AdaptP->TransmitInterruptPending = TRUE;
+
+ CardStartXmit(AdaptP);
+
+ }
+
+ ElnkiiCopyAndSend(AdaptP);
+
+ }
+
+ break;
+
+
+ case FILLING:
+
+ //
+ // The next packet will be started when copying down is finished.
+ //
+
+ IF_LOUD( DbgPrint( " next packet filling\n" );)
+
+ AdaptP->CurBufXmitting = -1;
+
+ AdaptP->NextBufToXmit = TmpBuf;
+
+ //
+ // If AdaptP->NextBufToFill is not TmpBuf, this
+ // will check to make sure NextBufToFill is not
+ // waiting to be filled.
+ //
+
+ AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE;
+
+ ElnkiiCopyAndSend(AdaptP);
+
+ break;
+
+
+ case EMPTY:
+
+ //
+ // No packet is ready to transmit.
+ //
+
+ IF_LOUD( DbgPrint( " next packet empty\n" );)
+
+ if (AdaptP->ResetInProgress) {
+
+ //
+ // A reset has just started, exit.
+ //
+
+ AdaptP->CurBufXmitting = -1;
+
+ AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE;
+
+ NdisDprReleaseSpinLock(&AdaptP->Lock);
+
+ ElnkiiResetStageDone(AdaptP, XMIT_STOPPED);
+
+ NdisDprAcquireSpinLock(&AdaptP->Lock);
+
+ break;
+
+ }
+
+
+ if (AdaptP->XmitQueue != (PNDIS_PACKET)NULL) {
+
+ //
+ // Take the packet off the head of the queue.
+ //
+ // There will be a packet on the queue with
+ // BufferStatus[TmpBuf] == EMPTY only when we
+ // have only one transmit buffer.
+ //
+
+ IF_LOUD( DbgPrint( " transmit queue not empty\n" );)
+
+ Packet = AdaptP->XmitQueue;
+
+ AdaptP->XmitQueue = RESERVED(AdaptP->XmitQueue)->NextPacket;
+
+
+ //
+ // At this point, NextBufToFill should equal TmpBuf.
+ //
+
+ AdaptP->NextBufToFill = NextBuf(AdaptP, TmpBuf);
+
+
+ //
+ // Set this now, to avoid having to get spinlock between
+ // copying and transmission start.
+ //
+
+ AdaptP->BufferStatus[TmpBuf] = FULL;
+
+ AdaptP->Packets[TmpBuf] = Packet;
+ AdaptP->CurBufXmitting = TmpBuf;
+
+ IF_LOG( ElnkiiLog('3');)
+
+#if DBG
+
+ ElnkiiLogSave = TRUE;
+ ElnkiiLogSaveLeft = 20;
+
+#endif
+
+ NdisDprReleaseSpinLock(&AdaptP->Lock);
+
+
+ //
+ // Copy down the data, pad short packets with blanks.
+ //
+
+ (VOID)CardCopyDownPacket(AdaptP, Packet, TmpBuf,
+ &AdaptP->PacketLens[TmpBuf]);
+
+ if (AdaptP->PacketLens[TmpBuf] < 60) {
+
+ (VOID)CardCopyDownBuffer(
+ AdaptP,
+ BlankBuffer,
+ TmpBuf,
+ AdaptP->PacketLens[TmpBuf],
+ 60-AdaptP->PacketLens[TmpBuf]
+ );
+
+ }
+
+
+ NdisDprAcquireSpinLock(&AdaptP->Lock);
+
+ AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE;
+
+ //
+ // If we are currently handling an overflow, then we need to let
+ // the overflow handler send this packet...
+ //
+
+ if (AdaptP->BufferOverflow) {
+
+ AdaptP->OverflowRestartXmitDpc = TRUE;
+
+ IF_LOG( ElnkiiLog('O');)
+
+ } else {
+
+ //
+ // This is used to check if stopping the chip prevented
+ // a transmit complete interrupt from coming through (it
+ // is cleared in the ISR if a transmit DPC is queued).
+ //
+
+ AdaptP->TransmitInterruptPending = TRUE;
+
+ CardStartXmit(AdaptP);
+
+ }
+
+ //
+ // It makes no sense to call ElnkiiCopyAndSend because
+ // there is only one transmit buffer, and it was just
+ // filled.
+ //
+
+ } else {
+
+ //
+ // No packets are waiting on the transmit queue.
+ //
+
+ AdaptP->CurBufXmitting = -1;
+
+ AdaptP->NextBufToXmit = TmpBuf;
+
+ AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE;
+
+ }
+
+ break;
+
+ default:
+
+ AdaptP->ElnkiiHandleXmitCompleteRunning = FALSE;
+
+ }
+
+ IF_VERY_LOUD( DbgPrint( "ElnkiiXmitInterruptDpc exiting\n" );)
+
+}
+
+INDICATE_STATUS
+ElnkiiIndicateLoopbackPacket(
+ IN PELNKII_ADAPTER AdaptP,
+ IN PNDIS_PACKET Packet
+ )
+
+/*++
+
+Routine Description:
+
+ Indicates an NDIS_format packet to the protocols. This is used
+ for indicating packets from the loopback queue.
+
+Arguments:
+
+ AdaptP - pointer to the adapter block
+ Packet - the packet to be indicated
+
+Return Value:
+
+ SKIPPED if it is a run packet
+ INDICATE_OK otherwise.
+
+--*/
+
+{
+ UINT IndicateLen;
+ UINT PacketLen;
+
+ //
+ // Indicate up to 252 bytes.
+ //
+
+ NdisQueryPacket(Packet,
+ NULL,
+ NULL,
+ NULL,
+ &PacketLen
+ );
+
+ if (PacketLen < ETH_LENGTH_OF_ADDRESS) {
+
+ //
+ // A runt packet.
+ //
+
+ return SKIPPED;
+
+ }
+
+ IndicateLen = (PacketLen > AdaptP->MaxLookAhead) ?
+ AdaptP->MaxLookAhead : PacketLen;
+
+ //
+ // Copy the lookahead data into a contiguous buffer.
+ //
+
+ ElnkiiCopyOver(AdaptP->Lookahead,
+ Packet,
+ 0,
+ IndicateLen
+ );
+
+ if (IndicateLen < ELNKII_HEADER_SIZE) {
+
+ //
+ // Must have at least the address
+ //
+
+ if (IndicateLen > 5) {
+
+ //
+ // Runt packet
+ //
+
+ EthFilterIndicateReceive(
+ AdaptP->FilterDB,
+ (NDIS_HANDLE)AdaptP,
+ (PCHAR)AdaptP->Lookahead,
+ AdaptP->Lookahead,
+ IndicateLen,
+ NULL,
+ 0,
+ 0
+ );
+ }
+
+ } else {
+
+ //
+ // Indicate packet
+ //
+
+ EthFilterIndicateReceive(
+ AdaptP->FilterDB,
+ (NDIS_HANDLE)AdaptP,
+ (PCHAR)AdaptP->Lookahead,
+ AdaptP->Lookahead,
+ ELNKII_HEADER_SIZE,
+ AdaptP->Lookahead + ELNKII_HEADER_SIZE,
+ IndicateLen - ELNKII_HEADER_SIZE,
+ PacketLen - ELNKII_HEADER_SIZE
+ );
+ }
+
+ return INDICATE_OK;
+}
+
+UINT
+ElnkiiCopyOver(
+ OUT PUCHAR Buf, // destination
+ IN PNDIS_PACKET Packet, // source packet
+ IN UINT Offset, // offset in packet
+ IN UINT Length // number of bytes to copy
+ )
+
+/*++
+
+Routine Description:
+
+ Copies bytes from a packet into a buffer. Used to copy data
+ out of a packet during loopback indications.
+
+Arguments:
+
+ Buf - the destination buffer
+ Packet - the source packet
+ Offset - the offset in the packet to start copying at
+ Length - the number of bytes to copy
+
+Return Value:
+
+ The actual number of bytes copied; will be less than Length if
+ the packet length is less than Offset+Length.
+
+--*/
+
+{
+ PNDIS_BUFFER CurBuffer;
+ UINT BytesCopied;
+ PUCHAR BufVA;
+ UINT BufLen;
+ UINT ToCopy;
+ UINT CurOffset;
+
+
+ BytesCopied = 0;
+
+ //
+ // First find a spot Offset bytes into the packet.
+ //
+
+ CurOffset = 0;
+
+ NdisQueryPacket(Packet, NULL, NULL, &CurBuffer, NULL);
+
+ while (CurBuffer != (PNDIS_BUFFER)NULL) {
+
+ NdisQueryBuffer(CurBuffer, (PVOID *)&BufVA, &BufLen);
+
+ if (CurOffset + BufLen > Offset) {
+
+ break;
+
+ }
+
+ CurOffset += BufLen;
+
+ NdisGetNextBuffer(CurBuffer, &CurBuffer);
+
+ }
+
+
+ //
+ // See if the end of the packet has already been passed.
+ //
+
+ if (CurBuffer == (PNDIS_BUFFER)NULL) {
+
+ return 0;
+
+ }
+
+
+ //
+ // Now copy over Length bytes.
+ //
+
+ BufVA += (Offset - CurOffset);
+
+ BufLen -= (Offset - CurOffset);
+
+ for (;;) {
+
+ ToCopy = (BytesCopied+BufLen > Length) ? Length - BytesCopied : BufLen;
+
+ ELNKII_MOVE_MEM(Buf+BytesCopied, BufVA, ToCopy);
+
+ BytesCopied += ToCopy;
+
+
+ if (BytesCopied == Length) {
+
+ return BytesCopied;
+
+ }
+
+ NdisGetNextBuffer(CurBuffer, &CurBuffer);
+
+ if (CurBuffer == (PNDIS_BUFFER)NULL) {
+
+ break;
+
+ }
+
+ NdisQueryBuffer(CurBuffer, (PVOID *)&BufVA, &BufLen);
+
+ }
+
+ return BytesCopied;
+
+}
+
+INDICATE_STATUS
+ElnkiiIndicatePacket(
+ IN PELNKII_ADAPTER AdaptP
+ )
+
+/*++
+
+Routine Description:
+
+ Indicates the first packet on the card to the protocols.
+
+Arguments:
+
+ AdaptP - pointer to the adapter block.
+
+Return Value:
+
+ CARD_BAD if the card should be reset;
+ INDICATE_OK otherwise.
+
+--*/
+
+{
+ UINT PacketLen;
+ PUCHAR PacketLoc;
+ PUCHAR IndicateBuf;
+ UINT IndicateLen;
+ UCHAR PossibleNextPacket1, PossibleNextPacket2;
+
+
+ //
+ // First copy up the four-byte header the card attaches.
+ //
+
+ PacketLoc = AdaptP->PageStart +
+ 256*(AdaptP->NicNextPacket-AdaptP->NicPageStart);
+
+
+ if (!CardCopyUp(AdaptP, AdaptP->PacketHeader, PacketLoc, 4))
+ return(CARD_BAD);
+
+ //
+ // Check if the next packet byte agress with the length, as
+ // described on p. A-3 of the Etherlink II Technical Reference.
+ // The start of the packet plus the MSB of the length must
+ // be equal to the start of the next packet minus one or two.
+ // Otherwise the header is considered corrupted, and the
+ // card must be reset.
+ //
+
+ PossibleNextPacket1 =
+ AdaptP->NicNextPacket + AdaptP->PacketHeader[3] + (UCHAR)1;
+
+ if (PossibleNextPacket1 >= AdaptP->NicPageStop) {
+
+ PossibleNextPacket1 -= (AdaptP->NicPageStop - AdaptP->NicPageStart);
+
+ }
+
+ if (PossibleNextPacket1 != AdaptP->PacketHeader[1]) {
+
+ PossibleNextPacket2 = PossibleNextPacket1+(UCHAR)1;
+
+ if (PossibleNextPacket2 == AdaptP->NicPageStop) {
+
+ PossibleNextPacket2 = AdaptP->NicPageStart;
+
+ }
+
+ if (PossibleNextPacket2 != AdaptP->PacketHeader[1]) {
+
+ IF_LOUD(DbgPrint("F");)
+
+ if ((AdaptP->PacketHeader[1] < AdaptP->NicPageStart) ||
+ (AdaptP->PacketHeader[1] >= AdaptP->NicPageStop)) {
+
+ //
+ // We return CARD_BAD because the Dpc will set the NicNextPacket
+ // pointer based on the PacketHeader[1] value if we return
+ // SKIPPED.
+ //
+
+ return(CARD_BAD);
+
+ }
+
+ return SKIPPED;
+ }
+
+ }
+
+#if DBG
+
+ IF_ELNKIIDEBUG( ELNKII_DEBUG_WORKAROUND1 ) {
+ //
+ // Now check for the high order 2 bits being set, as described
+ // on page A-2 of the Etherlink II Technical Reference. If either
+ // of the two high order bits is set in the receive status byte
+ // in the packet header, the packet should be skipped (but
+ // the adapter does not need to be reset).
+ //
+
+ if (AdaptP->PacketHeader[0] & (RSR_DISABLED|RSR_DEFERRING)) {
+
+ IF_LOUD (DbgPrint("H");)
+
+ return SKIPPED;
+
+ }
+
+ }
+
+#endif
+
+ //
+ // Packet length is in bytes 3 and 4 of the header.
+ //
+ PacketLen = AdaptP->PacketHeader[2] + AdaptP->PacketHeader[3] * 256;
+ if (0 == PacketLen)
+ {
+ //
+ // Packet with no data...
+ //
+ IndicateLen = 0;
+ }
+ else
+ {
+ //
+ // Don't count the header.
+ //
+ PacketLen -= 4;
+
+ //
+ // See how much to indicate (252 bytes max).
+ //
+ IndicateLen = PacketLen < AdaptP->MaxLookAhead ?
+ PacketLen : AdaptP->MaxLookAhead;
+ }
+
+ //
+ // Save the length with the adapter block.
+ //
+ AdaptP->PacketLen = PacketLen;
+
+ //
+ // if not memory mapped, have to copy the lookahead data up first.
+ //
+ if (!AdaptP->MemMapped)
+ {
+ if (!CardCopyUp(AdaptP, AdaptP->Lookahead, PacketLoc + 4, IndicateLen))
+ return(CARD_BAD);
+
+ IndicateBuf = AdaptP->Lookahead;
+ }
+ else
+ {
+ if (IndicateLen != 0)
+ {
+ NdisCreateLookaheadBufferFromSharedMemory(
+ (PVOID)(PacketLoc + 4),
+ IndicateLen,
+ &IndicateBuf
+ );
+ }
+ }
+
+ if (IndicateBuf != NULL)
+ {
+ AdaptP->FramesRcvGood++;
+
+ if (IndicateLen < ELNKII_HEADER_SIZE)
+ {
+ //
+ // Indicate packet
+ //
+ EthFilterIndicateReceive(
+ AdaptP->FilterDB,
+ (NDIS_HANDLE)AdaptP,
+ (PCHAR)IndicateBuf,
+ IndicateBuf,
+ IndicateLen,
+ NULL,
+ 0,
+ 0
+ );
+ }
+ else
+ {
+ //
+ // Indicate packet
+ //
+ EthFilterIndicateReceive(
+ AdaptP->FilterDB,
+ (NDIS_HANDLE)AdaptP,
+ (PCHAR)IndicateBuf,
+ IndicateBuf,
+ ELNKII_HEADER_SIZE,
+ IndicateBuf + ELNKII_HEADER_SIZE,
+ IndicateLen - ELNKII_HEADER_SIZE,
+ PacketLen - ELNKII_HEADER_SIZE
+ );
+ }
+
+ if (AdaptP->MemMapped && (IndicateLen != 0))
+ {
+ NdisDestroyLookaheadBufferFromSharedMemory(IndicateBuf);
+ }
+ }
+
+ return(INDICATE_OK);
+}
+
+NDIS_STATUS
+ElnkiiTransferData(
+ IN NDIS_HANDLE MacBindingHandle,
+ IN NDIS_HANDLE MacReceiveContext,
+ IN UINT ByteOffset,
+ IN UINT BytesToTransfer,
+ OUT PNDIS_PACKET Packet,
+ OUT PUINT BytesTransferred
+ )
+
+/*++
+
+Routine Description:
+
+ NDIS function.
+
+Arguments:
+
+ see NDIS 3.0 spec.
+
+Notes:
+
+ - The MacReceiveContext will be a pointer to the open block for
+ the packet.
+ - The LoopbackPacket field in the adapter block will be NULL if this
+ is a call for a normal packet, otherwise it will be set to point
+ to the loopback packet.
+
+--*/
+
+{
+ UINT BytesLeft, BytesNow, BytesWanted;
+ PUCHAR CurCardLoc;
+ PNDIS_BUFFER CurBuffer;
+ PUCHAR BufVA, BufStart;
+ UINT BufLen, BufOff, Copied;
+ UINT CurOff;
+ PELNKII_ADAPTER AdaptP = ((PELNKII_ADAPTER)MacReceiveContext);
+
+ UNREFERENCED_PARAMETER(MacBindingHandle);
+
+ //
+ // Determine whether this was a loopback indication.
+ //
+
+ ByteOffset += ELNKII_HEADER_SIZE;
+
+ if (AdaptP->LoopbackPacket != (PNDIS_PACKET)NULL) {
+
+ //
+ // Yes, have to copy data from AdaptP->LoopbackPacket into Packet.
+ //
+
+ NdisQueryPacket(Packet, NULL, NULL, &CurBuffer, NULL);
+
+ CurOff = ByteOffset;
+
+ while (CurBuffer != (PNDIS_BUFFER)NULL) {
+
+ NdisQueryBuffer(CurBuffer, (PVOID *)&BufVA, &BufLen);
+
+ Copied =
+ ElnkiiCopyOver(BufVA, AdaptP->LoopbackPacket, CurOff, BufLen);
+
+ CurOff += Copied;
+
+ if (Copied < BufLen) {
+
+ break;
+
+ }
+
+ NdisGetNextBuffer(CurBuffer, &CurBuffer);
+
+ }
+
+ //
+ // We are done, return.
+ //
+
+
+ *BytesTransferred = CurOff - ByteOffset;
+ if (*BytesTransferred > BytesToTransfer) {
+ *BytesTransferred = BytesToTransfer;
+ }
+
+ return NDIS_STATUS_SUCCESS;
+
+ }
+
+
+ //
+ // This was NOT a loopback packet, get the data off the card.
+ //
+
+ //
+ // See how much data there is to transfer.
+ //
+
+ if (ByteOffset+BytesToTransfer > AdaptP->PacketLen) {
+
+ BytesWanted = AdaptP->PacketLen - ByteOffset;
+
+ } else {
+
+ BytesWanted = BytesToTransfer;
+
+ }
+
+ BytesLeft = BytesWanted;
+
+
+ //
+ // Determine where the copying should start.
+ //
+
+ CurCardLoc = AdaptP->PageStart +
+ 256*(AdaptP->NicNextPacket-AdaptP->NicPageStart) +
+ 4 + ByteOffset;
+
+ if (CurCardLoc > AdaptP->PageStop) {
+
+ CurCardLoc = CurCardLoc - (AdaptP->PageStop - AdaptP->PageStart);
+
+ }
+
+
+ NdisQueryPacket(Packet, NULL, NULL, &CurBuffer, NULL);
+
+ NdisQueryBuffer(CurBuffer, (PVOID *)&BufStart, &BufLen);
+
+ BufOff = 0;
+
+ //
+ // Loop, filling each buffer in the packet until there
+ // are no more buffers or the data has all been copied.
+ //
+
+ while (BytesLeft > 0) {
+
+ //
+ // See how much data to read into this buffer.
+ //
+
+ if ((BufLen-BufOff) > BytesLeft) {
+
+ BytesNow = BytesLeft;
+
+ } else {
+
+ BytesNow = (BufLen - BufOff);
+
+ }
+
+
+ //
+ // See if the data for this buffer wraps around the end
+ // of the receive buffers (if so filling this buffer
+ // will use two iterations of the loop).
+ //
+
+ if (CurCardLoc + BytesNow > AdaptP->PageStop) {
+
+ BytesNow = AdaptP->PageStop - CurCardLoc;
+
+ }
+
+
+ //
+ // Copy up the data.
+ //
+
+ if (!CardCopyUp(AdaptP, BufStart+BufOff, CurCardLoc, BytesNow))
+ {
+ *BytesTransferred = BytesWanted - BytesLeft;
+
+ return(NDIS_STATUS_FAILURE);
+ }
+
+ CurCardLoc += BytesNow;
+
+ BytesLeft -= BytesNow;
+
+
+ //
+ // Is the transfer done now?
+ //
+
+ if (BytesLeft == 0) {
+
+ break;
+
+ }
+
+
+ //
+ // Wrap around the end of the receive buffers?
+ //
+
+ if (CurCardLoc == AdaptP->PageStop) {
+
+ CurCardLoc = AdaptP->PageStart;
+
+ }
+
+
+ //
+ // Was the end of this packet buffer reached?
+ //
+
+ BufOff += BytesNow;
+
+ if (BufOff == BufLen) {
+
+ NdisGetNextBuffer(CurBuffer, &CurBuffer);
+
+ if (CurBuffer == (PNDIS_BUFFER)NULL) {
+
+ break;
+
+ }
+
+ NdisQueryBuffer(CurBuffer, (PVOID *)&BufStart, &BufLen);
+
+ BufOff = 0;
+
+ }
+
+ }
+
+
+ *BytesTransferred = BytesWanted - BytesLeft;
+
+ return NDIS_STATUS_SUCCESS;
+
+}
+
+
+
+
+
+
+
+
+
+
+NDIS_STATUS
+ElnkiiSend(
+ IN NDIS_HANDLE MacBindingHandle,
+ IN PNDIS_PACKET Packet
+ )
+
+/*++
+
+Routine Description:
+
+ NDIS function.
+
+Arguments:
+
+ See NDIS 3.0 spec.
+
+Notes:
+
+
+--*/
+
+{
+ PELNKII_OPEN OpenP = ((PELNKII_OPEN)MacBindingHandle);
+ PELNKII_ADAPTER AdaptP = OpenP->Adapter;
+ PMAC_RESERVED Reserved = RESERVED(Packet);
+
+ //
+ // First Buffer
+ //
+ PNDIS_BUFFER FirstBuffer;
+
+ //
+ // Virtual address of first buffer
+ //
+ PVOID BufferVA;
+
+ //
+ // Length of the first buffer
+ //
+ UINT Length;
+
+
+ NdisAcquireSpinLock(&AdaptP->Lock);
+
+#if DBG
+ ElnkiiSendsIssued++;
+#endif
+
+ //
+ // Ensure that the open won't close during this function.
+ //
+
+ if (OpenP->Closing) {
+
+#if DBG
+ ElnkiiSendsFailed++;
+#endif
+
+ NdisReleaseSpinLock(&AdaptP->Lock);
+
+ return NDIS_STATUS_CLOSING;
+ }
+
+ //
+ // All requests are rejected during a reset.
+ //
+
+ if (AdaptP->ResetInProgress) {
+
+ NdisReleaseSpinLock(&AdaptP->Lock);
+
+#if DBG
+ ElnkiiSendsFailed++;
+#endif
+
+ return NDIS_STATUS_RESET_IN_PROGRESS;
+
+ }
+
+ OpenP->ReferenceCount++;
+
+ AdaptP->References++;
+
+ //
+ // Set up the MacReserved section of the packet.
+ //
+
+ Reserved->Open = (PELNKII_OPEN)MacBindingHandle;
+ Reserved->NextPacket = (PNDIS_PACKET)NULL;
+
+#if DBG
+ IF_ELNKIIDEBUG( ELNKII_DEBUG_CHECK_DUP_SENDS ) {
+
+ AddPacketToList(AdaptP, Packet);
+
+ }
+#endif
+
+ //
+ // Set Reserved->Loopback.
+ //
+
+ NdisQueryPacket(Packet, NULL, NULL, &FirstBuffer, NULL);
+
+ //
+ // Get VA of first buffer
+ //
+
+ NdisQueryBuffer(
+ FirstBuffer,
+ &BufferVA,
+ &Length
+ );
+
+ if (OpenP->ProtOptionFlags & NDIS_PROT_OPTION_NO_LOOPBACK){
+
+ Reserved->Loopback = FALSE;
+
+ } else{
+
+ Reserved->Loopback = EthShouldAddressLoopBack(AdaptP->FilterDB, BufferVA);
+
+ }
+
+ //
+ // Put it on the loopback queue only. All packets go through the
+ // loopback queue first, and then on to the xmit queue.
+ //
+
+ IF_LOG( ElnkiiLog('D');)
+
+#if DBG
+ ElnkiiSendsPended++;
+#endif
+
+
+ //
+ // We do not OpenP->ReferenceCount-- because that will be done when
+ // then send completes.
+ //
+
+ //
+ // Put Packet on queue to hit the wire.
+ //
+
+ IF_VERY_LOUD( DbgPrint("Putting 0x%x on, after 0x%x\n",Packet,AdaptP->XmitQTail); )
+
+ if (AdaptP->XmitQueue != NULL) {
+
+ RESERVED(AdaptP->XmitQTail)->NextPacket = Packet;
+
+ AdaptP->XmitQTail = Packet;
+
+ } else {
+
+ AdaptP->XmitQueue = Packet;
+
+ AdaptP->XmitQTail = Packet;
+
+ }
+
+ Reserved->NextPacket = NULL;
+
+ ELNKII_DO_DEFERRED(AdaptP);
+
+ return NDIS_STATUS_PENDING;
+}
+
+UINT
+ElnkiiCompareMemory(
+ IN PUCHAR String1,
+ IN PUCHAR String2,
+ IN UINT Length
+ )
+{
+ UINT i;
+
+ for (i=0; i<Length; i++) {
+ if (String1[i] != String2[i]) {
+ return (UINT)(-1);
+ }
+ }
+ return 0;
+}
+
+VOID
+ElnkiiCopyAndSend(
+ IN PELNKII_ADAPTER AdaptP
+ )
+
+/*++
+
+Routine Description:
+
+ Copies packets from the transmit queue to the board and starts
+ transmission as long as there is data waiting. Must be called
+ with Lock held.
+
+Arguments:
+
+ AdaptP - pointer to the adapter block
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ XMIT_BUF TmpBuf1;
+ PNDIS_PACKET Packet;
+
+
+ //
+ // Loop as long as there is data on the transmit queue
+ // and space for it on the card.
+ //
+
+ while ((AdaptP->BufferStatus[TmpBuf1=AdaptP->NextBufToFill] == EMPTY) &&
+ (AdaptP->XmitQueue != NULL)) {
+
+ //
+ // Take the packet off of the transmit queue.
+ //
+
+ IF_VERY_LOUD( DbgPrint("Removing 0x%x, New Head is 0x%x\n",Packet,RESERVED(Packet)->NextPacket); )
+
+ Packet = AdaptP->XmitQueue;
+
+ AdaptP->XmitQueue = RESERVED(Packet)->NextPacket;
+
+ AdaptP->BufferStatus[TmpBuf1] = FILLING;
+
+ AdaptP->Packets[TmpBuf1] = Packet;
+
+ AdaptP->NextBufToFill = NextBuf(AdaptP, TmpBuf1);
+
+ NdisReleaseSpinLock(&AdaptP->Lock);
+
+
+ //
+ // copy down the data, pad short packets with blanks.
+ //
+
+ (VOID)CardCopyDownPacket(AdaptP, Packet, TmpBuf1,
+ &AdaptP->PacketLens[TmpBuf1]);
+
+ if (AdaptP->PacketLens[TmpBuf1] < 60) {
+
+ (VOID)CardCopyDownBuffer(
+ AdaptP,
+ BlankBuffer,
+ TmpBuf1,
+ AdaptP->PacketLens[TmpBuf1],
+ 60 - AdaptP->PacketLens[TmpBuf1]);
+ }
+
+ NdisAcquireSpinLock(&AdaptP->Lock);
+
+ if (AdaptP->ResetInProgress)
+ {
+ PELNKII_OPEN TmpOpen = (PELNKII_OPEN)((RESERVED(Packet)->Open));
+
+ //
+ // A reset just started, abort.
+ //
+ //
+ AdaptP->BufferStatus[TmpBuf1] = EMPTY; // to ack the reset
+
+ NdisReleaseSpinLock(&AdaptP->Lock);
+
+ //
+ // Complete the send.
+ //
+ NdisCompleteSend(
+ RESERVED(Packet)->Open->NdisBindingContext,
+ Packet,
+ NDIS_STATUS_SUCCESS
+ );
+
+ ElnkiiResetStageDone(AdaptP, BUFFERS_EMPTY);
+
+ NdisAcquireSpinLock(&AdaptP->Lock);
+
+ TmpOpen->ReferenceCount--;
+
+ return;
+ }
+
+ AdaptP->BufferStatus[TmpBuf1] = FULL;
+
+
+ //
+ // See whether to start the transmission.
+ //
+
+ if (AdaptP->CurBufXmitting != -1) {
+
+ //
+ // Another transmit is still in progress.
+ //
+
+ continue;
+ }
+
+ if (AdaptP->NextBufToXmit != TmpBuf1) {
+
+ //
+ // A packet ahead of us is being copied down, this
+ // transmission can't be started now.
+ //
+
+ continue;
+ }
+
+ //
+ // OK to start transmission.
+ //
+
+ AdaptP->CurBufXmitting = AdaptP->NextBufToXmit;
+
+
+ IF_LOG( ElnkiiLog('4');)
+
+#if DBG
+
+ ElnkiiLogSave = TRUE;
+ ElnkiiLogSaveLeft = 20;
+
+#endif
+
+ //
+ // If we are currently handling an overflow, then we need to let
+ // the overflow handler send this packet...
+ //
+
+ if (AdaptP->BufferOverflow) {
+
+ AdaptP->OverflowRestartXmitDpc = TRUE;
+
+ IF_LOG( ElnkiiLog('O');)
+
+ } else {
+
+ //
+ // This is used to check if stopping the chip prevented
+ // a transmit complete interrupt from coming through (it
+ // is cleared in the ISR if a transmit DPC is queued).
+ //
+
+ AdaptP->TransmitInterruptPending = TRUE;
+
+ CardStartXmit(AdaptP);
+
+ }
+
+ }
+
+}
+
+
+VOID
+ElnkiiWakeUpDpc(
+ IN PVOID SystemSpecific1,
+ IN PVOID Context,
+ IN PVOID SystemSpecific2,
+ IN PVOID SystemSpecific3
+ )
+
+/*++
+
+Routine Description:
+
+ This DPC routine is queued every 2 seconds to check on the
+ transmit queue. If a transmit interrupt was not received
+ in the last two seconds and there is a transmit in progress,
+ then we complete the transmit.
+
+Arguments:
+
+ Context - Really a pointer to the adapter.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PELNKII_ADAPTER AdaptP = (PELNKII_ADAPTER)Context;
+ XMIT_BUF TmpBuf;
+ PMAC_RESERVED Reserved;
+ PELNKII_OPEN TmpOpen;
+ PNDIS_PACKET Packet;
+ PNDIS_PACKET NextPacket;
+
+ UNREFERENCED_PARAMETER(SystemSpecific1);
+ UNREFERENCED_PARAMETER(SystemSpecific2);
+ UNREFERENCED_PARAMETER(SystemSpecific3);
+
+ NdisDprAcquireSpinLock(&AdaptP->Lock);
+
+ if ((AdaptP->WakeUpFoundTransmit) &&
+ (AdaptP->CurBufXmitting != -1))
+ {
+ //
+ // We had a transmit pending the last time we ran,
+ // and it has not been completed...we need to complete
+ // it now.
+
+ AdaptP->TransmitInterruptPending = FALSE;
+ AdaptP->WakeUpFoundTransmit = FALSE;
+
+ IF_LOG( ElnkiiLog('K');)
+
+ //
+ // We log the first 5 of these.
+ //
+ if (AdaptP->TimeoutCount < 5)
+ {
+ NdisWriteErrorLogEntry(
+ AdaptP->NdisAdapterHandle,
+ NDIS_ERROR_CODE_HARDWARE_FAILURE,
+ 2,
+ 0x3,
+ AdaptP->TimeoutCount
+ );
+ }
+
+ AdaptP->TimeoutCount++;
+
+#if DBG
+
+ ELNKII_MOVE_MEM(ElnkiiLogSaveBuffer, ElnkiiLogBuffer, ELNKII_LOG_SIZE);
+
+ ElnkiiLogSave = FALSE;
+ ElnkiiLogSaveLoc = ElnkiiLogLoc;
+
+#endif
+
+ //
+ // We stop and start the card, then queue a DPC to
+ // handle the receive.
+ //
+
+ CardStop(AdaptP);
+
+ Packet = AdaptP->Packets[AdaptP->CurBufXmitting];
+
+ AdaptP->Packets[AdaptP->CurBufXmitting] = (PNDIS_PACKET)NULL;
+
+ AdaptP->BufferStatus[AdaptP->CurBufXmitting] = EMPTY;
+
+ TmpBuf = NextBuf(AdaptP, AdaptP->CurBufXmitting);
+
+ //
+ // Set this so that we don't access the packets
+ // somewhere else.
+ //
+ AdaptP->CurBufXmitting = -1;
+
+ //
+ // Abort all sends
+ //
+ while (Packet != NULL) {
+
+ Reserved = RESERVED(Packet);
+
+ TmpOpen = Reserved->Open;
+
+ NdisDprReleaseSpinLock(&AdaptP->Lock);
+
+ NdisCompleteSend(
+ TmpOpen->NdisBindingContext,
+ Packet,
+ NDIS_STATUS_SUCCESS
+ );
+
+ NdisDprAcquireSpinLock(&AdaptP->Lock);
+
+ TmpOpen->ReferenceCount--;
+
+ //
+ // Get next packet
+ //
+
+ if (AdaptP->BufferStatus[TmpBuf] == FULL) {
+
+ Packet = AdaptP->Packets[TmpBuf];
+
+ AdaptP->Packets[TmpBuf] = NULL;
+
+ AdaptP->BufferStatus[TmpBuf] = EMPTY;
+
+ TmpBuf = NextBuf(AdaptP, TmpBuf);
+
+ } else {
+
+ break;
+
+ }
+
+ }
+
+ //
+ // Set send variables correctly.
+ //
+ AdaptP->NextBufToXmit = TmpBuf;
+
+
+
+ Packet = AdaptP->XmitQueue;
+
+ AdaptP->XmitQueue = NULL;
+
+ while (Packet != NULL) {
+
+ Reserved = RESERVED(Packet);
+
+ //
+ // Remove the packet from the queue.
+ //
+
+ NextPacket = Reserved->NextPacket;
+
+ TmpOpen = Reserved->Open;
+
+ NdisDprReleaseSpinLock(&AdaptP->Lock);
+
+ NdisCompleteSend(
+ TmpOpen->NdisBindingContext,
+ Packet,
+ NDIS_STATUS_SUCCESS
+ );
+
+ NdisDprAcquireSpinLock(&AdaptP->Lock);
+
+ TmpOpen->ReferenceCount--;
+
+ Packet = NextPacket;
+
+ }
+
+ //
+ // Restart the card
+ //
+
+ CardStart(AdaptP);
+
+ NdisDprReleaseSpinLock(&AdaptP->Lock);
+
+ } else {
+
+ if (AdaptP->CurBufXmitting != -1) {
+
+ AdaptP->WakeUpFoundTransmit = TRUE;
+
+ IF_LOG( ElnkiiLog('L');)
+
+ }
+
+ NdisDprReleaseSpinLock(&AdaptP->Lock);
+
+
+ }
+
+ //
+ // Fire off another Dpc to execute after 2 seconds
+ //
+
+ NdisSetTimer(
+ &AdaptP->WakeUpTimer,
+ 2000
+ );
+
+}
+