diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/ndis/ne2000/interrup.c | 2126 |
1 files changed, 2126 insertions, 0 deletions
diff --git a/private/ntos/ndis/ne2000/interrup.c b/private/ntos/ndis/ne2000/interrup.c new file mode 100644 index 000000000..25f10cd3f --- /dev/null +++ b/private/ntos/ndis/ne2000/interrup.c @@ -0,0 +1,2126 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + interrup.c + +Abstract: + + This is a part of the driver for the National Semiconductor Novell 2000 + 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: + + Bob Noradki - Apr 93 - added piggyback interrupt code. + Jameel Hyder- Dec 94 - Fixed initialization - part of the fixes from JimMcn + +--*/ + +#include <ndis.h> +#include "ne2000hw.h" +#include "ne2000sw.h" + +// +// On debug builds tell the compiler to keep the symbols for +// internal functions, otw throw them out. +// +#if DBG +#define STATIC +#else +#define STATIC static +#endif + + + +INDICATE_STATUS +Ne2000IndicatePacket( + IN PNE2000_ADAPTER Adapter + ); + +VOID +Ne2000DoNextSend( + PNE2000_ADAPTER Adapter + ); + + + +// +// This is used to pad short packets. +// +static UCHAR BlankBuffer[60] = " "; + + + +VOID +Ne2000EnableInterrupt( + IN NDIS_HANDLE MiniportAdapterContext + ) + +/*++ + +Routine Description: + + This routine is used to turn on the interrupt mask. + +Arguments: + + Context - The adapter for the NE2000 to start. + +Return Value: + + None. + +--*/ + +{ + PNE2000_ADAPTER Adapter = (PNE2000_ADAPTER)(MiniportAdapterContext); + + IF_LOG( Ne2000Log('P'); ) + + CardUnblockInterrupts(Adapter); + +} + +VOID +Ne2000DisableInterrupt( + IN NDIS_HANDLE MiniportAdapterContext + ) + +/*++ + +Routine Description: + + This routine is used to turn off the interrupt mask. + +Arguments: + + Context - The adapter for the NE2000 to start. + +Return Value: + + None. + +--*/ + +{ + PNE2000_ADAPTER Adapter = (PNE2000_ADAPTER)(MiniportAdapterContext); + + IF_LOG( Ne2000Log('p'); ) + + CardBlockInterrupts(Adapter); + +} + +VOID +Ne2000Isr( + OUT PBOOLEAN InterruptRecognized, + OUT PBOOLEAN QueueDpc, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This is the interrupt handler which is registered with the operating + system. If several are pending (i.e. transmit complete and receive), + handle them all. Block new interrupts until all pending interrupts + are handled. + +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 - pointer to the adapter object + +Return Value: + + None. +--*/ + +{ + PNE2000_ADAPTER Adapter = ((PNE2000_ADAPTER)Context); + + IF_LOUD( DbgPrint("In Ne2000ISR\n");) + + IF_LOG( Ne2000Log('i'); ) + + IF_VERY_LOUD( DbgPrint( "Ne2000InterruptHandler entered\n" );) + + // + // Force the INT signal from the chip low. When all + // interrupts are acknowledged interrupts will be unblocked, + // + CardBlockInterrupts(Adapter); + + IF_LOG( Ne2000Log('I'); ) + + *InterruptRecognized = TRUE; + *QueueDpc = TRUE; + + return; +} + + +VOID +Ne2000HandleInterrupt( + IN NDIS_HANDLE MiniportAdapterContext + ) +/*++ + +Routine Description: + + This is the defered processing routine for interrupts. It + reads from the Interrupt Status Register any outstanding + interrupts and handles them. + +Arguments: + + MiniportAdapterContext - a handle to the adapter block. + +Return Value: + + NONE. + +--*/ +{ + // + // The adapter to process + // + PNE2000_ADAPTER Adapter = ((PNE2000_ADAPTER)MiniportAdapterContext); + + // + // The most recent port value read. + // + UCHAR InterruptStatus; + + // + // The interrupt type currently being processed. + // + INTERRUPT_TYPE InterruptType; + + IF_LOUD( DbgPrint("==>IntDpc\n");) + IF_LOG( Ne2000Log('d'); ) + + // + // Get the interrupt bits and save them. + // + CardGetInterruptStatus(Adapter, &InterruptStatus); + Adapter->InterruptStatus |= InterruptStatus; + + if (InterruptStatus != ISR_EMPTY) { + + // + // Acknowledge the interrupts + // + NdisRawWritePortUchar(Adapter->IoPAddr+NIC_INTR_STATUS, + InterruptStatus + ); + + } + + // + // Return the type of the most important interrupt waiting on the card. + // Order of importance is COUNTER, OVERFLOW, TRANSMIT,and RECEIVE. + // + InterruptType = CARD_GET_INTERRUPT_TYPE(Adapter, Adapter->InterruptStatus); + + // + // InterruptType is used to dispatch to correct DPC and are then cleared + // + while (InterruptType != UNKNOWN) { + + // + // Handle the 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)Adapter); + + // + // Clear the COUNTER interrupt bit + // + Adapter->InterruptStatus &= ~ISR_COUNTER; + + 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. + // + Adapter->BufferOverflow = TRUE; + + IF_LOUD( DbgPrint("Overflow Int\n"); ) + IF_VERY_LOUD( DbgPrint(" overflow interrupt\n"); ) + + // + // Clear the OVERFLOW interrupt bit + // + Adapter->InterruptStatus &= ~ISR_OVERFLOW; + + case RECEIVE: + + IF_LOG( Ne2000Log('R'); ) + IF_LOUD( DbgPrint("DPC got RCV\n"); ) + + // + // For receives, call this to handle the receive + // + if (Ne2000RcvDpc(Adapter)) { + + // + // Clear the RECEIVE interrupt bits + // + Adapter->InterruptStatus &= ~(ISR_RCV | ISR_RCV_ERR); + + } + + IF_LOG( Ne2000Log('r'); ) + + if (!(Adapter->InterruptStatus & (ISR_XMIT | ISR_XMIT_ERR))) + break; + + case TRANSMIT: + + IF_LOG( Ne2000Log('X'); ) + + ASSERT(!Adapter->OverflowRestartXmitDpc); + + // + // Get the status of the transmit + // + SyncCardGetXmitStatus((PVOID)Adapter); + + // + // We are no longer expecting an interrupts, as + // we just got it. + // + Adapter->TransmitInterruptPending = FALSE; + + IF_LOUD( DbgPrint( "DPC got XMIT\n"); ) + + // + // Handle transmit errors + // + if (Adapter->InterruptStatus & ISR_XMIT_ERR) { + + OctogmetusceratorRevisited(Adapter); + + } + + // + // Handle the transmit + // + if (Adapter->InterruptStatus & ISR_XMIT) { + + Ne2000XmitDpc(Adapter); + + } + + // + // Clear the TRANSMIT interrupt bits + // + Adapter->InterruptStatus &= ~(ISR_XMIT | ISR_XMIT_ERR); + + break; + + default: + + IF_LOUD( DbgPrint("unhandled interrupt type: %x\n", InterruptType); ) + + break; + + } + + // + // Get any new interrupts + // + CardGetInterruptStatus(Adapter, &InterruptStatus); + + if (InterruptStatus != ISR_EMPTY) { + + // + // Acknowledge the interrupt + // + NdisRawWritePortUchar(Adapter->IoPAddr+NIC_INTR_STATUS, + InterruptStatus + ); + } + + // + // Save the interrupt reasons + // + Adapter->InterruptStatus |= InterruptStatus; + + // + // Get next interrupt to process + // + InterruptType = CARD_GET_INTERRUPT_TYPE(Adapter, Adapter->InterruptStatus); + + } + + IF_LOG( Ne2000Log('D'); ) + + IF_LOUD( DbgPrint("<==IntDpc\n"); ) + +} + + +BOOLEAN +Ne2000RcvDpc( + IN PNE2000_ADAPTER Adapter + ) + +/*++ + +Routine Description: + + This is the real interrupt handler for receive/overflow interrupt. + + Called when a receive interrupt is received. It first indicates + all packets on the card and finally indicates ReceiveComplete(). + +Arguments: + + Adapter - Pointer to the adapter block. + +Return Value: + + TRUE if done with all receives, else FALSE. + +--*/ + +{ + // + // Use to restart a transmit if a buffer overflow occured + // during a transmission + // + BOOLEAN TransmitInterruptWasPending = FALSE; + + // + // Status of a received packet. + // + INDICATE_STATUS IndicateStatus = INDICATE_OK; + + // + // Flag to tell when the receive process is complete + // + BOOLEAN Done = TRUE; + + IF_LOUD( DbgPrint( "Ne2000RcvDpc entered\n" );) + + // + // Default to not indicating NdisMEthIndicateReceiveComplete + // + Adapter->IndicateReceiveDone = FALSE; + + // + // At this point, receive interrupts are disabled. + // + SyncCardGetCurrent((PVOID)Adapter); + + // + // Handle a buffer overflow + // + if (Adapter->BufferOverflow) { + + SyncCardHandleOverflow(Adapter); + +#if DBG + if (Adapter->OverflowRestartXmitDpc) { + + IF_LOG( Ne2000Log('O');) + IF_LOUD( DbgPrint ("Adapter->OverflowRestartXmitDpc set:RcvDpc\n"); ) + + } +#endif // DBG + + } + + // + // Loop + // + while (TRUE) + { + if ((Adapter->InterruptStatus & ISR_RCV_ERR) && + !Adapter->BufferOverflow + ) + { + IF_LOUD( DbgPrint ("RCV_ERR, IR=%x\n",Adapter->InterruptStatus); ) + + // + // Skip this packet + // + + SyncCardGetCurrent((PVOID)Adapter); + + Adapter->NicNextPacket = Adapter->Current; + + CardSetBoundary(Adapter); + + break; + + } + + if (Adapter->Current == Adapter->NicNextPacket) { + + // + // Acknowledge previous packet before the check for new ones, + // then read in the Current register. + // The card register Current used to point to + // the end of the packet just received; read + // the new value off the card and see if it + // still does. + // + // This will store the value in Adapter->Current and acknowledge + // the receive interrupt. + // + // + + SyncCardGetCurrent((PVOID)Adapter); + + if (Adapter->Current == Adapter->NicNextPacket) { + + // + // End of Loop -- no more packets + // + + break; + } + + } + + // + // A packet was found on the card, indicate it. + // + + Adapter->ReceivePacketCount++; + + // + // Verify packet is not corrupt + // + if (Ne2000PacketOK(Adapter)) { + + ULONG PacketLen; + + PacketLen = (Adapter->PacketHeader[2]) + ((Adapter->PacketHeader[3])*256) - 4; + + PacketLen = (PacketLen < Adapter->MaxLookAhead)? + PacketLen : + Adapter->MaxLookAhead; + + // + // Copy up the lookahead data + // + if (!CardCopyUp(Adapter, + Adapter->Lookahead, + Adapter->PacketHeaderLoc, + PacketLen + NE2000_HEADER_SIZE + )) { + + // + // Failed! Skip this packet + // + IndicateStatus = SKIPPED; + + } else { + + // + // Indicate the packet to the wrapper + // + IndicateStatus = Ne2000IndicatePacket(Adapter); + + if (IndicateStatus != CARD_BAD) { + + Adapter->FramesRcvGood++; + + } + + } + + } else { + + // + // Packet is corrupt, skip it. + // + IF_LOUD( DbgPrint("Packet did not pass OK check\n"); ) + + IndicateStatus = SKIPPED; + + } + + // + // Handle when the card is unable to indicate good packets + // + if (IndicateStatus == CARD_BAD) { + +#if DBG + + IF_NE2000DEBUG( NE2000_DEBUG_CARD_BAD ) { + + DbgPrint("R: <%x %x %x %x> C %x N %x\n", + Adapter->PacketHeader[0], + Adapter->PacketHeader[1], + Adapter->PacketHeader[2], + Adapter->PacketHeader[3], + Adapter->Current, + Adapter->NicNextPacket); + + } +#endif + + IF_LOG( Ne2000Log('W');) + + // + // Start off with receive interrupts disabled. + // + + Adapter->NicInterruptMask = IMR_XMIT | IMR_XMIT_ERR | IMR_OVERFLOW; + + // + // Reset the adapter + // + CardReset(Adapter); + + // + // Since the adapter was just reset, stop indicating packets. + // + + break; + + } + + // + // (IndicateStatus == SKIPPED) is OK, just move to next packet. + // + if (IndicateStatus == SKIPPED) { + + SyncCardGetCurrent((PVOID)Adapter); + + Adapter->NicNextPacket = Adapter->Current; + + } else { + + // + // Free the space used by packet on card. + // + + Adapter->NicNextPacket = Adapter->PacketHeader[1]; + + } + + // + // This will set BOUNDARY to one behind NicNextPacket. + // + CardSetBoundary(Adapter); + + if (Adapter->ReceivePacketCount > 10) { + + // + // Give transmit interrupts a chance + // + Done = FALSE; + Adapter->ReceivePacketCount = 0; + break; + + } + + } + + // + // See if a buffer overflow occured previously. + // + if (Adapter->BufferOverflow) { + + // + // ... and set a flag to restart the card after receiving + // a packet. + // + Adapter->BufferOverflow = FALSE; + + SyncCardAcknowledgeOverflow(Adapter); + + // + // Undo loopback mode + // + CardStart(Adapter); + + IF_LOG( Ne2000Log('f'); ) + + // + // Check if transmission needs to be queued or not + // + if (Adapter->OverflowRestartXmitDpc && Adapter->CurBufXmitting != -1) { + + IF_LOUD( DbgPrint("queueing xmit in RcvDpc\n"); ) + + Adapter->OverflowRestartXmitDpc = FALSE; + + Adapter->TransmitInterruptPending = TRUE; + + IF_LOG( Ne2000Log('5'); ) + + CardStartXmit(Adapter); + + } + } + + // + // Finally, indicate ReceiveComplete to all protocols which received packets + // + if (Adapter->IndicateReceiveDone) { + + NdisMEthIndicateReceiveComplete(Adapter->MiniportAdapterHandle); + + Adapter->IndicateReceiveDone = FALSE; + + } + + IF_LOUD( DbgPrint( "Ne2000RcvDpc exiting\n" );) + + return (Done); + +} + + +VOID +Ne2000XmitDpc( + IN PNE2000_ADAPTER Adapter + ) + +/*++ + +Routine Description: + + This is the real interrupt handler for a transmit complete interrupt. + Ne2000Dpc queues a call to it. + + Called after a transmit complete interrupt. It checks the + status of the transmission, completes the send if needed, + and sees if any more packets are ready to be sent. + +Arguments: + + Adapter - Pointer to the adapter block. + +Return Value: + + None. + +--*/ + +{ + // + // Packet that was transmitted + // + PNDIS_PACKET Packet; + + // + // Status of the send + // + NDIS_STATUS Status; + + // + // Length of the packet sent + // + ULONG Len; + + // + // Temporary loopnig variable + // + UINT i; + + IF_VERY_LOUD( DbgPrint( "Ne2000XmitDpc entered\n" );) + + // + // Verify that we are transmitting a packet + // + if ( Adapter->CurBufXmitting == -1 ) { + +#if DBG + DbgPrint( "Ne2000HandleXmitComplete called with nothing transmitting!\n" ); +#endif + + NdisWriteErrorLogEntry( + Adapter->MiniportAdapterHandle, + NDIS_ERROR_CODE_DRIVER_FAILURE, + 1, + NE2000_ERRMSG_HANDLE_XMIT_COMPLETE + ); + + return; + } + + IF_LOG( Ne2000Log('C');) + + // + // Get the status of the transmit + // + SyncCardGetXmitStatus((PVOID)Adapter); + + // + // Statistics + // + if (Adapter->XmitStatus & TSR_XMIT_OK) { + + Adapter->FramesXmitGood++; + Status = NDIS_STATUS_SUCCESS; + + } else { + + Adapter->FramesXmitBad++; + Status = NDIS_STATUS_FAILURE; + + } + + // + // Mark the current transmit as done. + // + Len = (Adapter->PacketLens[Adapter->CurBufXmitting] + 255) >> 8; + + ASSERT (Len != 0); + + // + // Free the transmit buffers + // + for (i = Adapter->CurBufXmitting; i < Adapter->CurBufXmitting + Len; i++) { + + Adapter->BufferStatus[i] = EMPTY; + + } + + // + // Set the next buffer to start transmitting. + // + Adapter->NextBufToXmit += Len; + + if (Adapter->NextBufToXmit == MAX_XMIT_BUFS) { + + Adapter->NextBufToXmit = 0; + + } + + if (Adapter->BufferStatus[Adapter->NextBufToXmit] == EMPTY && + Adapter->NextBufToFill != Adapter->NextBufToXmit) { + + Adapter->NextBufToXmit = 0; + + } + + // + // Remove the packet from the outstanding packet list. + // + Packet = Adapter->Packets[Adapter->CurBufXmitting]; + Adapter->Packets[Adapter->CurBufXmitting] = (PNDIS_PACKET)NULL; + + // + // See what to do next. + // + + switch (Adapter->BufferStatus[Adapter->NextBufToXmit]) { + + + 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" );) + + // + // Start the transmission and check for more. + // + + Adapter->CurBufXmitting = Adapter->NextBufToXmit; + + IF_LOG( Ne2000Log('2');) + + // + // 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). + // + + Adapter->TransmitInterruptPending = TRUE; + + IF_LOG( Ne2000Log('6'); ) + CardStartXmit(Adapter); + + break; + + case EMPTY: + + // + // No packet is ready to transmit. + // + + IF_LOUD( DbgPrint( " next packet empty\n" );) + + Adapter->CurBufXmitting = (XMIT_BUF)-1; + + break; + + } + + // + // Start next send + // + + Ne2000DoNextSend(Adapter); + + IF_VERY_LOUD( DbgPrint( "Ne2000XmitDpc exiting\n" );) + +} + + +BOOLEAN +Ne2000PacketOK( + IN PNE2000_ADAPTER Adapter + ) + +/*++ + +Routine Description: + + Reads a packet off the card -- checking if the CRC is good. This is + a workaround for a bug where bytes in the data portion of the packet + are shifted either left or right by two in some weird 8390 cases. + + This routine is a combination of Ne2000TransferData (to copy up data + from the card), CardCalculateCrc and CardCalculatePacketCrc. + +Arguments: + + Adapter - pointer to the adapter block. + +Return Value: + + TRUE if the packet seems ok, else false. + +--*/ + +{ + + // + // Length of the packet + // + UINT PacketLen; + + // + // Guess at where the packet is located + // + PUCHAR PacketLoc; + + // + // Header Validation Variables + // + BOOLEAN FrameAlign; + PUCHAR PacketRcvStatus; + PUCHAR NextPacket; + PUCHAR PacketLenLo; + PUCHAR PacketLenHi; + PUCHAR ReceiveDestAddrLo; + UINT FrameAlignCount; + UCHAR OldPacketLenHi; + UCHAR TempPacketHeader[6]; + PUCHAR BeginPacketHeader; + + // + // First copy up the four-byte header the card attaches + // plus first two bytes of the data packet (which contain + // the destination address of the packet). We use the extra + // two bytes in case the packet was shifted right 1 or 2 bytes + // + PacketLoc = Adapter->PageStart + + 256*(Adapter->NicNextPacket-Adapter->NicPageStart); + + if (!CardCopyUp(Adapter, TempPacketHeader, PacketLoc, 6)) { + + return FALSE; + + } + PacketLoc += 4; + + // + // Validate the header + // + FrameAlignCount = 0; + BeginPacketHeader = TempPacketHeader; + + // + // Sometimes the Ne2000 will misplace a packet and shift the + // entire packet and header by a byte, either up by 1 or 2 bytes. + // This loop will look for the packet in the expected place, + // and then shift up in an effort to find the packet. + // + do { + + // + // Set where we think the packet is + // + PacketRcvStatus = BeginPacketHeader; + NextPacket = BeginPacketHeader + 1; + PacketLenLo = BeginPacketHeader + 2; + PacketLenHi = BeginPacketHeader + 3; + OldPacketLenHi = *PacketLenHi; + ReceiveDestAddrLo = BeginPacketHeader + 4; + FrameAlign = FALSE; + + // + // Check if the status makes sense as is. + // + if (*PacketRcvStatus & 0x05E){ + + FrameAlign = TRUE; + + } else if ((*PacketRcvStatus & RSR_MULTICAST) // If a multicast packet + && (!FrameAlignCount) // and hasn't been aligned + && !(*ReceiveDestAddrLo & 1) // and lsb is set on dest addr + ){ + + FrameAlign = TRUE; + + } else { + + // + // Compare high and low address bytes. If the same, the low + // byte may have been copied into the high byte. + // + + if (*PacketLenLo == *PacketLenHi){ + + // + // Save the old packetlenhi + // + OldPacketLenHi = *PacketLenHi; + + // + // Compute new packet length + // + *PacketLenHi = *NextPacket - Adapter->NicNextPacket - 1; + + if (*PacketLenHi < 0) { + + *PacketLenHi = (Adapter->NicPageStop - Adapter->NicNextPacket) + + (*NextPacket - Adapter->NicPageStart) - 1; + + } + + if (*PacketLenLo > 0xFC) { + + *PacketLenHi++; + } + + } + + PacketLen = (*PacketLenLo) + ((*PacketLenHi)*256) - 4; + + // + // Does it make sense? + // + if ((PacketLen > 1514) || (PacketLen < 60)){ + + // + // Bad length. Restore the old packetlenhi + // + *PacketLenHi = OldPacketLenHi; + + FrameAlign = TRUE; + + } + + // + // Did we recover the frame? + // + if (!FrameAlign && ((*NextPacket < Adapter->NicPageStart) || + (*NextPacket > Adapter->NicPageStop))) { + + IF_LOUD( DbgPrint ("Packet address invalid in HeaderValidation\n"); ) + + FrameAlign = TRUE; + + } + + } + + // + // FrameAlignment - if first time through, shift packetheader right 1 or 2 bytes. + // If second time through, shift it back to where it was and let it through. + // This compensates for a known bug in the 8390D chip. + // + if (FrameAlign){ + + switch (FrameAlignCount){ + + case 0: + + BeginPacketHeader++; + PacketLoc++; + if (!Adapter->EightBitSlot){ + + BeginPacketHeader++; + PacketLoc++; + + } + break; + + case 1: + + BeginPacketHeader--; + PacketLoc--; + if (!Adapter->EightBitSlot){ + BeginPacketHeader--; + PacketLoc--; + } + break; + + } + + FrameAlignCount++; + + } + + } while ( (FrameAlignCount < 2) && FrameAlign ); + + // + // Now grab the packet header information + // + Adapter->PacketHeader[0] = *BeginPacketHeader; + BeginPacketHeader++; + Adapter->PacketHeader[1] = *BeginPacketHeader; + BeginPacketHeader++; + Adapter->PacketHeader[2] = *BeginPacketHeader; + BeginPacketHeader++; + Adapter->PacketHeader[3] = *BeginPacketHeader; + + // + // Packet length is in bytes 3 and 4 of the header. + // + Adapter->PacketHeaderLoc = PacketLoc; + PacketLen = (Adapter->PacketHeader[2]) + ((Adapter->PacketHeader[3])*256) - 4; + + // + // Sanity check the packet + // + if ((PacketLen > 1514) || (PacketLen < 60)){ + + if ((Adapter->PacketHeader[1] < Adapter->NicPageStart) || + (Adapter->PacketHeader[1] > Adapter->NicPageStop)) { + + // + // Return TRUE here since IndicatePacket will notice the error + // and handle it correctly. + // + return(TRUE); + + } + + return(FALSE); + + } + + return(TRUE); +} + + +INDICATE_STATUS +Ne2000IndicatePacket( + IN PNE2000_ADAPTER Adapter + ) + +/*++ + +Routine Description: + + Indicates the first packet on the card to the protocols. + + NOTE: For MP, non-x86 architectures, this assumes that the packet has been + read from the card and into Adapter->PacketHeader and Adapter->Lookahead. + + NOTE: For UP x86 systems this assumes that the packet header has been + read into Adapter->PacketHeader and the minimal lookahead stored in + Adapter->Lookahead + +Arguments: + + Adapter - pointer to the adapter block. + +Return Value: + + CARD_BAD if the card should be reset; + INDICATE_OK otherwise. + +--*/ + +{ + // + // Length of the packet + // + UINT PacketLen; + + // + // Length of the lookahead buffer + // + UINT IndicateLen; + + // + // Variables for checking if the packet header looks valid + // + UCHAR PossibleNextPacket1, PossibleNextPacket2; + + // + // 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 = + Adapter->NicNextPacket + Adapter->PacketHeader[3] + (UCHAR)1; + + if (PossibleNextPacket1 >= Adapter->NicPageStop) { + + PossibleNextPacket1 -= (Adapter->NicPageStop - Adapter->NicPageStart); + + } + + if (PossibleNextPacket1 != Adapter->PacketHeader[1]) { + + PossibleNextPacket2 = PossibleNextPacket1+(UCHAR)1; + + if (PossibleNextPacket2 == Adapter->NicPageStop) { + + PossibleNextPacket2 = Adapter->NicPageStart; + + } + + if (PossibleNextPacket2 != Adapter->PacketHeader[1]) { + + IF_LOUD( DbgPrint("First CARD_BAD check failed\n"); ) + return SKIPPED; + } + + } + + // + // Check that the Next is valid + // + if ((Adapter->PacketHeader[1] < Adapter->NicPageStart) || + (Adapter->PacketHeader[1] > Adapter->NicPageStop)) { + + IF_LOUD( DbgPrint("Second CARD_BAD check failed\n"); ) + return(SKIPPED); + + } + + // + // Sanity check the length + // + PacketLen = Adapter->PacketHeader[2] + Adapter->PacketHeader[3]*256 - 4; + + if (PacketLen > 1514) { + IF_LOUD( DbgPrint("Third CARD_BAD check failed\n"); ) + return(SKIPPED); + + } + +#if DBG + + IF_NE2000DEBUG( NE2000_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 (Adapter->PacketHeader[0] & (RSR_DISABLED|RSR_DEFERRING)) { + + IF_LOUD (DbgPrint("H");) + + return SKIPPED; + + } + + } + +#endif + + // + // Lookahead amount to indicate + // + IndicateLen = (PacketLen > (Adapter->MaxLookAhead + NE2000_HEADER_SIZE)) ? + (Adapter->MaxLookAhead + NE2000_HEADER_SIZE) : + PacketLen; + + // + // Indicate packet + // + + Adapter->PacketLen = PacketLen; + + if (IndicateLen < NE2000_HEADER_SIZE) { + + // + // Runt Packet + // + + NdisMEthIndicateReceive( + Adapter->MiniportAdapterHandle, + (NDIS_HANDLE)Adapter, + (PCHAR)(Adapter->Lookahead), + IndicateLen, + NULL, + 0, + 0 + ); + + } else { + + NdisMEthIndicateReceive( + Adapter->MiniportAdapterHandle, + (NDIS_HANDLE)Adapter, + (PCHAR)(Adapter->Lookahead), + NE2000_HEADER_SIZE, + (PCHAR)(Adapter->Lookahead) + NE2000_HEADER_SIZE, + IndicateLen - NE2000_HEADER_SIZE, + PacketLen - NE2000_HEADER_SIZE + ); + + } + + Adapter->IndicateReceiveDone = TRUE; + + return INDICATE_OK; +} + + +NDIS_STATUS +Ne2000TransferData( + OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred, + IN NDIS_HANDLE MiniportAdapterContext, + IN NDIS_HANDLE MiniportReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer + ) + +/*++ + +Routine Description: + + A protocol calls the Ne2000TransferData request (indirectly via + NdisTransferData) from within its Receive event handler + to instruct the driver to copy the contents of the received packet + a specified packet buffer. + +Arguments: + + MiniportAdapterContext - Context registered with the wrapper, really + a pointer to the adapter. + + MiniportReceiveContext - The context value passed by the driver on its call + to NdisMEthIndicateReceive. The driver can use this value to determine + which packet, on which adapter, is being received. + + ByteOffset - An unsigned integer specifying the offset within the + received packet at which the copy is to begin. If the entire packet + is to be copied, ByteOffset must be zero. + + BytesToTransfer - An unsigned integer specifying the number of bytes + to copy. It is legal to transfer zero bytes; this has no effect. If + the sum of ByteOffset and BytesToTransfer is greater than the size + of the received packet, then the remainder of the packet (starting from + ByteOffset) is transferred, and the trailing portion of the receive + buffer is not modified. + + Packet - A pointer to a descriptor for the packet storage into which + the MAC is to copy the received packet. + + BytesTransfered - A pointer to an unsigned integer. The MAC writes + the actual number of bytes transferred into this location. This value + is not valid if the return status is STATUS_PENDING. + +Notes: + + - The MacReceiveContext will be a pointer to the open block for + the packet. + +--*/ + +{ + // + // Variables for the number of bytes to copy, how much can be + // copied at this moment, and the total number of bytes to copy. + // + UINT BytesLeft, BytesNow, BytesWanted; + + // + // Current NDIS_BUFFER to copy into + // + PNDIS_BUFFER CurBuffer; + + // + // Virtual address of the buffer. + // + XMIT_BUF NextBufToXmit; + PUCHAR BufStart; + + // + // Length and offset into the buffer. + // + UINT BufLen, BufOff; + + // + // The adapter to transfer from. + // + PNE2000_ADAPTER Adapter = ((PNE2000_ADAPTER)MiniportReceiveContext); + + IF_LOG( Ne2000Log('t');) + + // + // Add the packet header onto the offset. + // + ByteOffset += NE2000_HEADER_SIZE; + + // + // See how much data there is to transfer. + // + if (ByteOffset+BytesToTransfer > Adapter->PacketLen) { + + if (Adapter->PacketLen < ByteOffset) { + + *BytesTransferred = 0; + IF_LOG( Ne2000Log('T');) + return(NDIS_STATUS_FAILURE); + } + + BytesWanted = Adapter->PacketLen - ByteOffset; + + } else { + + BytesWanted = BytesToTransfer; + + } + + // + // Set the number of bytes left to transfer + // + BytesLeft = BytesWanted; + + { + + // + // Address on the adapter to copy from + // + PUCHAR CurCardLoc; + + // + // Copy data from the card -- it is not completely stored in the + // adapter structure. + // + // Determine where the copying should start. + // + CurCardLoc = Adapter->PacketHeaderLoc + ByteOffset; + + if (CurCardLoc > Adapter->PageStop) { + + CurCardLoc = CurCardLoc - (Adapter->PageStop - Adapter->PageStart); + + } + + // + // Get location to copy into + // + 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 > Adapter->PageStop) { + + BytesNow = Adapter->PageStop - CurCardLoc; + + } + + // + // Copy up the data. + // + + if (!CardCopyUp(Adapter, BufStart+BufOff, CurCardLoc, BytesNow)) { + + *BytesTransferred = BytesWanted - BytesLeft; + + NdisWriteErrorLogEntry( + Adapter->MiniportAdapterHandle, + NDIS_ERROR_CODE_HARDWARE_FAILURE, + 1, + 0x2 + ); + + return(NDIS_STATUS_FAILURE); + + } + + // + // Update offsets and counts + // + CurCardLoc += BytesNow; + BytesLeft -= BytesNow; + + // + // Is the transfer done now? + // + if (BytesLeft == 0) { + + break; + + } + + // + // Wrap around the end of the receive buffers? + // + if (CurCardLoc == Adapter->PageStop) { + + CurCardLoc = Adapter->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; + + // + // Did a transmit complete while we were doing what we were doing? + // + if (!Adapter->BufferOverflow && Adapter->CurBufXmitting != -1) { + + ULONG Len; + UINT i; + UCHAR Status; + PNDIS_PACKET Packet; + NDIS_STATUS NdisStatus; + + // + // Check if it completed + // + CardGetInterruptStatus(Adapter, &Status); + + if (Status & ISR_XMIT_ERR) { + OctogmetusceratorRevisited(Adapter); + Adapter->InterruptStatus &= ~ISR_XMIT_ERR; + NdisRawWritePortUchar(Adapter->IoPAddr+NIC_INTR_STATUS, (ISR_XMIT_ERR)); + Status &= ~ISR_XMIT_ERR; + + } + + if (Status & (ISR_XMIT)) { + + + IF_LOG( Ne2000Log('*'); ) + + + // + // Update NextBufToXmit + // + Len = (Adapter->PacketLens[Adapter->CurBufXmitting] + 255) >> 8; + NextBufToXmit = Adapter->NextBufToXmit + Len; + +// Adapter->NextBufToXmit += Len; + + if (NextBufToXmit == MAX_XMIT_BUFS) { + NextBufToXmit = 0; + } + + if (Adapter->BufferStatus[NextBufToXmit] == EMPTY && + Adapter->NextBufToFill != NextBufToXmit) { + NextBufToXmit = 0; + } + + + // + // If the next packet is ready to go, start it. + // + if (Adapter->BufferStatus[NextBufToXmit] == FULL) { + + // + // Ack the transmit + // + + // + // Remove the packet from the packet list. + // + Adapter->NextBufToXmit = NextBufToXmit; + Packet = Adapter->Packets[Adapter->CurBufXmitting]; + Adapter->Packets[Adapter->CurBufXmitting] = (PNDIS_PACKET)NULL; + SyncCardGetXmitStatus((PVOID)Adapter); + + + // + // Statistics + // + if (Adapter->XmitStatus & TSR_XMIT_OK) { + + Adapter->FramesXmitGood++; + NdisStatus = NDIS_STATUS_SUCCESS; + + } else { + + Adapter->FramesXmitBad++; + NdisStatus = NDIS_STATUS_FAILURE; + + } + + for (i = Adapter->CurBufXmitting; i < Adapter->CurBufXmitting + Len; i++) { + Adapter->BufferStatus[i] = EMPTY; + } + Adapter->TransmitInterruptPending = FALSE; + NdisRawWritePortUchar(Adapter->IoPAddr+NIC_INTR_STATUS, (ISR_XMIT)); + Adapter->CurBufXmitting = Adapter->NextBufToXmit; + Adapter->TransmitInterruptPending = TRUE; + + IF_LOG( Ne2000Log('8'); ) + Adapter->InterruptStatus &= ~(ISR_XMIT); + CardStartXmit(Adapter); + + } else { + NdisRawWritePortUchar(Adapter->IoPAddr+NIC_INTR_STATUS, (ISR_XMIT)); + Adapter->InterruptStatus |= (Status); + + } + + } + + } + + return(NDIS_STATUS_SUCCESS); + + } + +} + + +NDIS_STATUS +Ne2000Send( + IN NDIS_HANDLE MiniportAdapterContext, + IN PNDIS_PACKET Packet, + IN UINT Flags + ) + +/*++ + +Routine Description: + + + The Ne2000Send request instructs a driver to transmit a packet through + the adapter onto the medium. + +Arguments: + + MiniportAdapterContext - Context registered with the wrapper, really + a pointer to the adapter. + + Packet - A pointer to a descriptor for the packet that is to be + transmitted. + + SendFlags - Optional send flags + +Notes: + + This miniport driver will always accept a send. This is because + the Ne2000 has limited send resources and the driver needs packets + to copy to the adapter immediately after a transmit completes in + order to keep the adapter as busy as possible. + + This is not required for other adapters, as they have enough + resources to keep the transmitter busy until the wrapper submits + the next packet. + +--*/ + +{ + PNE2000_ADAPTER Adapter = (PNE2000_ADAPTER)(MiniportAdapterContext); + + // + // Put the packet on the send queue. + // + if (Adapter->FirstPacket == NULL) { + Adapter->FirstPacket = Packet; + } else { + RESERVED(Adapter->LastPacket)->Next = Packet; + } + + RESERVED(Packet)->Next = NULL; + + Adapter->LastPacket = Packet; + + // + // Process the next send + // + Ne2000DoNextSend(Adapter); + return(NDIS_STATUS_PENDING); + +} + +VOID +Ne2000DoNextSend( + PNE2000_ADAPTER Adapter + ) + +/*++ + +Routine Description: + + This routine examines if the packet at the head of the packet + list can be copied to the adapter, and does so. + +Arguments: + + Adapter - Pointer to the adapter block. + +Return Value: + + None + +--*/ + +{ + // + // The packet to process. + // + PNDIS_PACKET Packet; + + // + // The current destination transmit buffer. + // + XMIT_BUF TmpBuf1; + + // + // Length of the packet + // + ULONG Len; + + // + // Temporary looping variable + // + ULONG i; + + IF_LOG( Ne2000Log('s'); ) + + // + // Check if we have enough resources and a packet to process + // + while((Adapter->FirstPacket != NULL) && + (Adapter->BufferStatus[Adapter->NextBufToFill] == EMPTY)) { + + // + // Get the length of the packet. + // + NdisQueryPacket( + Adapter->FirstPacket, + NULL, + NULL, + NULL, + &Len + ); + + // + // Convert length to the number of transmit buffers needed. + // + Len = (Len + 255) >> 8; + + // + // If not transmitting + // + if (Adapter->CurBufXmitting == -1) { + + // + // Then check from the next free buffer if the packet will + // fit. + // + if (Adapter->BufferStatus[Adapter->NextBufToXmit] == EMPTY) { + + // + // It won't fit at the end, so put it at the first buffer + // + if (Adapter->NextBufToFill + Len > MAX_XMIT_BUFS) { + + Adapter->NextBufToFill = 0; + + } + + } else { + + // + // Check if this packet will fit before the packet on the + // adapter. + // + if (Adapter->NextBufToXmit > Adapter->NextBufToFill) { + + if (Adapter->NextBufToFill + Len > Adapter->NextBufToXmit) { + + IF_LOG( Ne2000Log('^'); ) + IF_LOG( Ne2000Log('S'); ) + + break; + + } + + } else { + + // + // Check if it will fit after the packet already on the + // adapter. + // + if (Adapter->NextBufToFill + Len > MAX_XMIT_BUFS) { + + Adapter->NextBufToFill = 0; + + if (Adapter->NextBufToFill + Len > Adapter->NextBufToXmit){ + + IF_LOG( Ne2000Log('%'); ) + IF_LOG( Ne2000Log('S'); ) + + break; + + } + + } + + } + + } + + } else { + + // + // Check if the packet will fit before the packet currently + // transmitting + // + + if (Adapter->CurBufXmitting > Adapter->NextBufToFill) { + + if (Adapter->NextBufToFill + Len > Adapter->CurBufXmitting) { + + IF_LOG( Ne2000Log('$'); ) + IF_LOG( Ne2000Log('S'); ) + + break; + } + + } else { + + // + // Check if it will fit after the packet currently transmitting + // + if (Adapter->NextBufToFill + Len > MAX_XMIT_BUFS) { + + Adapter->NextBufToFill = 0; + + if (Adapter->NextBufToFill + Len > Adapter->CurBufXmitting){ + + IF_LOG( Ne2000Log('!'); ) + IF_LOG( Ne2000Log('S'); ) + break; + + } + + } + + } + + } + + // + // Set starting location + // + TmpBuf1 = Adapter->NextBufToFill; + + // + // Remove the packet from the queue. + // + Packet = Adapter->FirstPacket; + Adapter->FirstPacket = RESERVED(Packet)->Next; + + if (Packet == Adapter->LastPacket) { + Adapter->LastPacket = NULL; + } + + // + // Store the packet in the list + // + Adapter->Packets[TmpBuf1] = Packet; + + // + // Copy down the packet. + // + if (CardCopyDownPacket(Adapter, Packet, + &Adapter->PacketLens[TmpBuf1]) == FALSE) { + + for (i = TmpBuf1; i < TmpBuf1 + Len; i++) { + Adapter->BufferStatus[i] = EMPTY; + } + Adapter->Packets[TmpBuf1] = NULL; + IF_LOG( Ne2000Log('F'); ) + IF_LOG( Ne2000Log('S'); ) + + NdisMSendComplete( + Adapter->MiniportAdapterHandle, + Packet, + NDIS_STATUS_FAILURE + ); + + continue; + + } + + // + // Pad short packets with blanks. + // + if (Adapter->PacketLens[TmpBuf1] < 60) { + + (VOID)CardCopyDown( + Adapter, + ((PUCHAR)Adapter->XmitStart + + TmpBuf1*TX_BUF_SIZE + + Adapter->PacketLens[TmpBuf1]), + BlankBuffer, + 60-Adapter->PacketLens[TmpBuf1] + ); + + } + + // + // Set the buffer status + // + for (i = TmpBuf1; i < (TmpBuf1 + Len); i++) { + Adapter->BufferStatus[i] = FULL; + } + + // + // Update next free buffer + // + Adapter->NextBufToFill += Len; + + if (Adapter->NextBufToFill == MAX_XMIT_BUFS) { + Adapter->NextBufToFill = 0; + } + + // + // See whether to start the transmission. + // + if (Adapter->CurBufXmitting == -1) { + + // + // OK to start transmission. + // + if (Adapter->BufferStatus[Adapter->NextBufToXmit] == EMPTY && + Adapter->NextBufToFill != Adapter->NextBufToXmit) { + + Adapter->NextBufToXmit = 0; + + } + + Adapter->CurBufXmitting = Adapter->NextBufToXmit; + + + IF_LOG( Ne2000Log('4');) + + // + // If we are currently handling an overflow, then we need to let + // the overflow handler send this packet... + // + + if (Adapter->BufferOverflow) { + + Adapter->OverflowRestartXmitDpc = TRUE; + + IF_LOG( Ne2000Log('O');) + IF_LOUD( DbgPrint ("Adapter->OverflowRestartXmitDpc set:copy and send");) + + } 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). + // + + Adapter->TransmitInterruptPending = TRUE; + + IF_LOG( Ne2000Log('9'); ) + CardStartXmit(Adapter); + + } + + } + + // + // Ack the send immediately. If for some reason it + // should fail, the protocol should be able to handle + // the retransmit. + // + + IF_LOG( Ne2000Log('S'); ) + + NdisMSendComplete( + Adapter->MiniportAdapterHandle, + Packet, + NDIS_STATUS_SUCCESS + ); + } + +} + +VOID +OctogmetusceratorRevisited( + IN PNE2000_ADAPTER Adapter + ) + +/*++ + +Routine Description: + + Recovers the card from a transmit error. + +Arguments: + + Adapter - pointer to the adapter block + +Return Value: + + None. + +--*/ + +{ + + IF_LOUD( DbgPrint("Octogmetuscerator called!"); ) + + IF_LOG( Ne2000Log('y'); ) + + // + // Ack the interrupt, if needed + // + NdisRawWritePortUchar(Adapter->IoPAddr+NIC_INTR_STATUS, ISR_XMIT_ERR); + + // + // Stop the card + // + SyncCardStop(Adapter); + + // + // Wait up to 1.6 milliseconds for any receives to finish + // + NdisStallExecution(2000); + + // + // Place the card in Loopback + // + NdisRawWritePortUchar(Adapter->IoPAddr+NIC_XMIT_CONFIG, TCR_LOOPBACK); + + // + // Start the card in Loopback + // + NdisRawWritePortUchar(Adapter->IoPAddr+NIC_COMMAND, CR_START | CR_NO_DMA); + + // + // Get out of loopback and start the card + // + CardStart(Adapter); + + // + // If there was a packet waiting to get sent, send it. + // + if (Adapter->CurBufXmitting != -1) { + + Adapter->TransmitInterruptPending = TRUE; + CardStartXmit(Adapter); + + } + IF_LOG( Ne2000Log('Y'); ) +} + |