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
51 KiB
2165 lines
51 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 tmpPacket;
|
|
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;
|
|
tmpPacket = 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'); )
|
|
}
|
|
|