Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2165 lines
49 KiB

/*++
Copyright (c) 1990-1998 Microsoft Corporation, All Rights Reserved.
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 "precomp.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);
Adapter->InterruptsEnabled = TRUE;
}
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);
Adapter->InterruptsEnabled = FALSE;
}
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);
UCHAR InterruptStatus;
UCHAR InterruptMask;
IF_LOUD( DbgPrint("In Ne2000ISR\n");)
IF_LOG( Ne2000Log('i'); )
IF_VERY_LOUD( DbgPrint( "Ne2000InterruptHandler entered\n" );)
if (!Adapter->InterruptsEnabled) {
*InterruptRecognized = FALSE;
*QueueDpc = FALSE;
return;
}
//
// Look to see if an interrupt has been asserted
//
CardGetInterruptStatus(Adapter, &InterruptStatus);
if (InterruptStatus == 0) {
*InterruptRecognized = FALSE;
*QueueDpc = FALSE;
return;
}
//
// It appears to be our interrupt.
// Force the INT signal from the chip low. When all
// interrupts are acknowledged interrupts will be unblocked,
//
CardBlockInterrupts(Adapter);
*InterruptRecognized = TRUE;
*QueueDpc = TRUE;
IF_LOG( Ne2000Log('I'); )
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;
ULONG CardTestCount = 0;
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 == 0xff) && (++CardTestCount > 10)) {
//
// this card appears dead
//
break;
}
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 (Adapter->InterruptMode == NdisInterruptLevelSensitive) {
//
// Re-enable the interrupt (disabled in Isr)
//
NdisMSynchronizeWithInterrupt(&Adapter->Interrupt,
Ne2000EnableInterrupt,
Adapter);
}
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 = (UINT)(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'); )
}