/*++ 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 #include #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; iInCardTest) { // // 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; iBufferStatus[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 ); }