2020-09-30 17:12:29 +02:00

1723 lines
45 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
receive.c
Abstract:
Ndis 3.0 MAC driver for the 3Com Etherlink III
FYI: The ELNK III behaves maginally if it is overflowing.
A note to the curious:
This module supports two different methods of handling received frames.
Method 0: the default method, basically tries to keep the stinking 2k
receive fifo as empty as possible. In order to do this, it needs to
port i/o the data into system memory and then copy from this system
memory to packets during transferdata. Obviously it would be best to
not have this memory copy.
Method 1: tries to avoid the memory copy by port i/o'ing straigth
into the packet buffers during transfer data. The problem with method
is that by the time you indicate the frame up, their is going to be
about 1300 bytes sitting in fifo from this packet. This obviously
means that if the total latency to transfer data port i/o'ing is
more than ~700 byte times, you are going to under run the next packet.
The more protocol's bound, the more delay.
Method zero is the best at avoiding underruns, but you pay a price in
the extra memory copy. An offsetting factor in addition to the fewer
overruns is that it appears that pending transferdata and later completing
it adds a fair amount of overhead to the code path.
Another interesting thing is that on MP machines the speed that you
can port i/o is signifigantly reduced do to contention for the bus
from the other processor(s). This increases the danger of overflowing.
Also, depending on how RISC platforms, deal with ISA boards and ports,
I think there is also a risk here of slower port i/o'ing.
As long as DPC latencies are fairly low and constant, either method will
work OK. The problem occures when the DPC latency increases. Like up around
a millisecond. In looking at this occurance it looks like the DPC is getting
stuck behind the HD DPC. If you get an 1ms latency you are almost assured of
overflowing if you are receiving full size frames back to back.
Bascially I believe that method 0 is the most solid and likely to
work on varied platforms.
If only 3Com had not been so cheap and had put another 2k in the fifo.
You should also take note that the asic on the isa and EISA cards have
a problem with loosing there place in the fifo. This is delt with in the
packet discard code. It also seems the these boards will also falsly detect
an adapter failure on the receive side.
Also, according to the 3com specs it is not possible for the card to
have both the packet incomplete bit set and receive error bit set
at the same time, but I have seen this happen. I suspect that this
is related to the above asic problem
Another neat feature of the card is that when it is overflowing it
has a tendancy to concatenate frames together to form really large
frames. When the frame completes it is marked with an error in the
receive status.
Author:
Brian Lieuallen (BrianLie) 12/14/92
Environment:
Kernel Mode Operating Systems : NT
Revision History:
Portions borrowed from ELNK3 driver by
Earle R. Horton (EarleH)
--*/
#include <ndis.h>
//#include <efilter.h>
#include "debug.h"
#include "elnk3hrd.h"
#include "elnk3sft.h"
#include "elnk3.h"
VOID
Elnk3DiscardIfBroadcast(
IN PELNK3_ADAPTER pAdapter,
IN PTRANSFERDATA_CONTEXT TransDataContext
);
BOOLEAN
Elnk3QuickPacketTest(
IN PNIC_RCV_HEADER Frame,
IN UINT NdisFilter
);
ULONG
Elnk3GuessFrameSize(
IN PNIC_RCV_HEADER Frame,
IN ULONG BytesNow
);
BOOLEAN
Elnk3IndicateLoopbackPacket(
IN PELNK3_ADAPTER pAdapter,
IN PNDIS_PACKET Packet
);
VOID
Elnk3TransferDataCompletion(
PTRANSFERDATA_CONTEXT TransDataContext
);
VOID
Elnk3DiscardPacketSync(
IN PTRANSFERDATA_CONTEXT TransDataContext
);
BOOLEAN
Elnk3CheckFifoSync(
IN PTRANSFERDATA_CONTEXT TransDataContext
);
BOOLEAN
Elnk3IndicatePackets2(
PELNK3_ADAPTER pAdapter
)
/*++
Routine Description:
This routine Indicates all of the packets in the ring one at a time
Arguments:
Return Value:
--*/
{
USHORT RcvStatus;
ULONG AdditionalData;
ULONG GoodReceives=0;
ULONG BadReceives=0;
BOOLEAN Indicated=FALSE;
UINT CurrentPacket;
UINT NextPacket;
USHORT InterruptReason;
ULONG PossibleLength;
PTRANSFERDATA_CONTEXT TransDataContext;
PTRANSFERDATA_CONTEXT AltTransDataContext;
CurrentPacket=pAdapter->CurrentPacket;
TransDataContext= &pAdapter->TransContext[CurrentPacket];
if ((TransDataContext->BytesAlreadyRead == 0) && pAdapter->RejectBroadcast) {
//
// First interrupt for this packet
//
Elnk3DiscardIfBroadcast(
pAdapter,
TransDataContext
);
}
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
IF_LOG(0xdc,0xdc,TransDataContext->BytesAlreadyRead);
IF_RCV_LOUD(DbgPrint("ELNK3: IndicatePackets RX status=%04x\n",RcvStatus);)
while ((BYTES_IN_FIFO(RcvStatus) > pAdapter->LowWaterMark ) ||
!(RcvStatus & RX_STATUS_INCOMPLETE)) {
//
// There is a completed packet or some data from a yet to be completed
// packet
//
if (ELNK3_PACKET_COMPLETE(RcvStatus) || (RcvStatus & RX_STATUS_ERROR)) {
//
// The packet has completed
//
if (!(RcvStatus & RX_STATUS_ERROR)) {
//
// The packet completed with out error
//
//
// Get the total packet length by adding the number of bytes still
// in the fifo to the number already taken out
//
TransDataContext->PacketLength=BYTES_IN_FIFO(RcvStatus);
TransDataContext->PacketLength+=TransDataContext->BytesAlreadyRead;
IF_LOG(0xdc,0x01,TransDataContext->PacketLength);
//
// Have we read all of the packet in already?
//
if (TransDataContext->PacketLength <= 1514) {
//
// It is a valid length
//
if (TransDataContext->PacketLength > TransDataContext->BytesAlreadyRead) {
//
// No, Need this much more
// The data in the fifo is padded to a dword boundary
//
AdditionalData=ELNK3_ROUND_TO_DWORD(TransDataContext->PacketLength)-TransDataContext->BytesAlreadyRead;
IF_LOG(0xad,0x01,AdditionalData);
//
// Go and get the rest
//
NdisRawReadPortBufferUlong(
pAdapter->PortOffsets[PORT_RxFIFO],
(PULONG)(((PUCHAR)TransDataContext->LookAhead)+TransDataContext->BytesAlreadyRead),
(AdditionalData>>2)
);
IF_RCV_LOUD(DbgPrint("IP: Da=%d Ad=%d\n",TransDataContext->BytesAlreadyRead, AdditionalData);)
//
// Now we have this much
//
TransDataContext->BytesAlreadyRead+=AdditionalData;
}
}
//
// Now discard the packet before indicating
//
Elnk3DiscardPacketSync(TransDataContext);
pAdapter->FramesRcvGood+=1;
//
// See if any data from the next packet is in the fifo
//
// If there is any data then it will go in the other unused buffer
//
NextPacket=(CurrentPacket+1) % 2;
AltTransDataContext= &pAdapter->TransContext[NextPacket];
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
AdditionalData=BYTES_IN_FIFO_DW(RcvStatus);
//
// Go and get the data
//
while ((AdditionalData >= (pAdapter->LowWaterMark*2))
&&
(AdditionalData+AltTransDataContext->BytesAlreadyRead<=1514)
&&
!(RcvStatus & RX_STATUS_ERROR)) {
IF_LOG(0xaf,0x00,RcvStatus & 0xc7fc);
NdisRawReadPortBufferUlong(
pAdapter->PortOffsets[PORT_RxFIFO],
(PULONG)((PUCHAR)AltTransDataContext->LookAhead+AltTransDataContext->BytesAlreadyRead),
(AdditionalData >> 2)
);
AltTransDataContext->BytesAlreadyRead += AdditionalData;
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
AdditionalData=BYTES_IN_FIFO_DW(RcvStatus);
}
//
// It seems that some asic's will generate an adapter failure
// when there really isn't one. Just to be safe we will check
// now. Since we have port i/o the whole packet now we will
// check. Better to not indicate a bad packet than to loose a
// good one
//
InterruptReason=ELNK3_READ_PORT_USHORT(pAdapter,PORT_CmdStatus);
if ((TransDataContext->PacketLength <=1514)
&&
(TransDataContext->PacketLength >= 14)
&&
!(InterruptReason & EC_INT_ADAPTER_FAILURE)) {
//
// The packet seems to be OK
// Subtract the header length(14) from the packet length
//
TransDataContext->PacketLength-=ELNK3_ETHERNET_HEADER_SIZE;
GoodReceives++;
NdisMEthIndicateReceive(
pAdapter->NdisAdapterHandle,
TransDataContext,
(PUCHAR)&TransDataContext->LookAhead->EthHeader,
ELNK3_ETHERNET_HEADER_SIZE,
TransDataContext->LookAhead->LookAheadData,
TransDataContext->PacketLength,
TransDataContext->PacketLength
);
DEBUG_STAT(pAdapter->Stats.PacketIndicated);
Indicated=TRUE;
} else {
IF_RCV_LOUD(DbgPrint("ELNK3: IndicatePacket: bad size or adapter failed -> not idicated\n");)
}
} else {
//
// The packet completed with some sort of error, just
// discard it
//
IF_RCV_LOUD(DbgPrint("ELNK3: IndicatePackets: error %04x\n", RcvStatus & 0xf800);)
IF_LOG(0xff,0xff,RcvStatus & 0xf800);
Elnk3DiscardPacketSync(TransDataContext);
pAdapter->MissedPackets++;
//
// This will be the next packet that we are using
//
NextPacket=(CurrentPacket+1) % 2;
AltTransDataContext= &pAdapter->TransContext[NextPacket];
DEBUG_STAT(pAdapter->Stats.BadReceives);
} // if (!(RcvStatus & RX_STATUS_ERROR))
//
// Re-initialize the info for this buffer
//
TransDataContext->BytesAlreadyRead=0;
// TransDataContext->PacketLength=0;
//
// Switch buffers now. The one we are switching to may already
// have data in it from above.
//
// pAdapter->CurrentPacket=NextPacket;
CurrentPacket=NextPacket;
//
// Update the pointer
//
TransDataContext=AltTransDataContext;
} // if (packet complete)
//
// The packet in the fifo is not complete yet.
// If there is enough data in the fifo copy it out now
// to reduce the chance of overflow
//
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
if ((BYTES_IN_FIFO_DW(RcvStatus) >= pAdapter->LowWaterMark)
&&
(BYTES_IN_FIFO_DW(RcvStatus)+TransDataContext->BytesAlreadyRead<=1514)
&&
((RcvStatus & RX_STATUS_INCOMPLETE))
&&
!(RcvStatus & RX_STATUS_ERROR)) {
IF_LOG(0xdc,0xea,(RcvStatus & 0xc7fc));
NdisRawReadPortBufferUlong(
pAdapter->PortOffsets[PORT_RxFIFO],
(PULONG)((PUCHAR)TransDataContext->LookAhead+TransDataContext->BytesAlreadyRead),
((RcvStatus & 0x7fc) >> 2)
);
TransDataContext->BytesAlreadyRead += BYTES_IN_FIFO_DW(RcvStatus);
}
//
// if the receiver should happen to fail we might
// not ever exit this loop. The receiver should
// only fail if we read past our packet in the
// fifo
//
InterruptReason=ELNK3_READ_PORT_USHORT(pAdapter,PORT_CmdStatus);
if (InterruptReason & EC_INT_ADAPTER_FAILURE) {
IF_LOUD(
DbgPrint("Elnk3: Adapter failed\n");
DbgBreakPoint();
)
break;
}
//
// get a new receive for the logic up top
//
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
} // While (!complete and > 64 bytes in fifo
//
// Figure out where to set the early receive threshold depending
// how much of the packet we have so far
//
if (TransDataContext->BytesAlreadyRead > 16) {
PossibleLength=Elnk3GuessFrameSize(TransDataContext->LookAhead,
TransDataContext->BytesAlreadyRead);
IF_RCV_LOUD(DbgPrint("Elnk3: Possible length is %d\n",PossibleLength);)
if (PossibleLength > pAdapter->LatencyAdjustment) {
//
// There is enough time to set the threshold before
// the packet passes the new threshold
//
IF_LOG(0xef,0x02,((PossibleLength)-pAdapter->LatencyAdjustment) & 0x7ff);
ELNK3_COMMAND(pAdapter,EC_SET_RX_EARLY, ((PossibleLength)-pAdapter->LatencyAdjustment) & 0x7fc);
}
} else {
//
// Set rx early to catch the front of the packet
//
IF_LOG(0xef,0x03,(pAdapter->LookAheadLatencyAdjustment));
ELNK3_COMMAND(pAdapter,EC_SET_RX_EARLY, (pAdapter->LookAheadLatencyAdjustment) );
}
//
// save this so we know where we are
//
pAdapter->CurrentPacket=CurrentPacket;
if (Indicated) {
DEBUG_STAT(pAdapter->Stats.IndicationCompleted);
NdisMEthIndicateReceiveComplete(pAdapter->NdisAdapterHandle);
}
#if DBG
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
IF_LOG(0xdc,0xdd,RcvStatus & 0xc7ff);
#endif
return TRUE;
}
VOID
Elnk3DiscardPacketSync(
IN PTRANSFERDATA_CONTEXT TransDataContext
)
/*++
Routine Description:
Discard tap packet in receive fifo
Arguments:
Notes:
We do this as a sync with interrupt routine because the discard
command does not complete with in the time it takes the I/O command
to complete. Since another command cannot be issued during the time
that one is in progress we must do this to protect our selves against
the mask command issued in the ISR.
--*/
{
PELNK3_ADAPTER pAdapter=TransDataContext->pAdapter;
ULONG RcvStatus;
ULONG Sum;
ULONG FreeBytes;
ELNK3_COMMAND(pAdapter, EC_RX_DISCARD_TOP_PACKET, 0);
//
// An interesting thing to do at raised IRQL.
// Unfortuanly I don't really see an alternative
// The wait appears to be very short any way
//
ELNK3_WAIT_NOT_BUSY(pAdapter);
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
if (RcvStatus & RX_STATUS_INCOMPLETE) {
//
// If the status is incomplete then we need to check
// to make sure that the fifo is not fucked up with a
// packet lost in it. To find out we read the
// the number of free bytes out of page 3 and add
// the number of bytes received from the RX status.
// If this value isn't with in 100 of the total
// fifo size then we conclude that is screwed up
// and we reset the receiver
//
ELNK3_SELECT_WINDOW(pAdapter,WNO_FIFO);
FreeBytes=ELNK3_READ_PORT_USHORT(pAdapter,PORT_FREE_RX_BYTES);
ELNK3_SELECT_WINDOW(pAdapter,WNO_OPERATING);
Sum=(BYTES_IN_FIFO(RcvStatus) + FreeBytes);
if ((Sum + 90 < pAdapter->RxFifoSize)
||
((pAdapter->RxFifoSize==FreeBytes) && (BYTES_IN_FIFO(RcvStatus)!=0))) {
BOOLEAN FifoBad;
IF_LOG(0xde,0xad,0xffff);
IF_RCV_LOUD(DbgPrint("ELNK3: RX fifo problem used=%d free=%d size=%d\n",RcvStatus & 0x7ff,FreeBytes, pAdapter->RxFifoSize);)
//
// Looks like the fifo is messed up, try it again
// syncronized with interrupts
//
FifoBad=NdisMSynchronizeWithInterrupt(
&pAdapter->NdisInterrupt,
Elnk3CheckFifoSync,
TransDataContext
);
if (FifoBad) {
//
// It's fucked up. Reset the receiver
//
ELNK3_COMMAND(pAdapter,EC_RX_RESET,0);
ELNK3_WAIT_NOT_BUSY(pAdapter);
ELNK3_COMMAND(pAdapter,EC_RX_ENABLE,0);
ELNK3_COMMAND(pAdapter, EC_SET_RX_FILTER, pAdapter->RxFilter);
}
}
}
}
BOOLEAN
Elnk3CheckFifoSync(
IN PTRANSFERDATA_CONTEXT TransDataContext
)
/*++
Routine Description:
Check the fifo's state to try to determine if a packet
is lost in it. We do this syncronized with interrupts
to reduce the chance of interrupt occuring between the
port reads.
Arguments:
Notes:
--*/
{
PELNK3_ADAPTER pAdapter=TransDataContext->pAdapter;
ULONG RcvStatus;
ULONG Sum;
ULONG FreeBytes;
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
if (!(RcvStatus & RX_STATUS_INCOMPLETE)) {
//
// It's completed now
//
return FALSE;
}
ELNK3_SELECT_WINDOW(pAdapter,WNO_FIFO);
FreeBytes=ELNK3_READ_PORT_USHORT(pAdapter,PORT_FREE_RX_BYTES);
ELNK3_SELECT_WINDOW(pAdapter,WNO_OPERATING);
Sum=(BYTES_IN_FIFO(RcvStatus) + FreeBytes);
#if DBG
if ((Sum + 90 < pAdapter->RxFifoSize)
||
((pAdapter->RxFifoSize==FreeBytes) && (BYTES_IN_FIFO(RcvStatus)!=0))) {
IF_LOUD(DbgPrint("ELNK3: RX fifo problem used=%d free=%d size=%d\n",RcvStatus & 0x7ff,FreeBytes, pAdapter->RxFifoSize);)
}
#endif
return ((Sum + 90 < pAdapter->RxFifoSize)
||
((pAdapter->RxFifoSize==FreeBytes) && (BYTES_IN_FIFO(RcvStatus)!=0)));
}
NDIS_STATUS
Elnk3TransferData(
OUT PNDIS_PACKET Packet,
OUT PUINT BytesTransferred,
IN NDIS_HANDLE MacBindingHandle,
IN NDIS_HANDLE MacReceiveContext,
IN UINT ByteOffset,
IN UINT BytesToTransfer
)
/*++
Routine Description:
NDIS function.
Arguments:
see NDIS 3.0 spec.
Notes:
The receive context is a pointer to a structure that describes the packet
--*/
{
UINT BytesLeft, BytesNow, BytesWanted;
PNDIS_BUFFER CurBuffer;
PUCHAR BufStart;
UINT BufLen;
ULONG DataAvailible;
PUCHAR pBuffer;
PTRANSFERDATA_CONTEXT TransDataContext= ((PTRANSFERDATA_CONTEXT)MacReceiveContext);
PELNK3_ADAPTER pAdapter = MacBindingHandle;
IF_LOG(0xDD,0xdd,TransDataContext->PacketLength);
IF_RCV_LOUD(DbgPrint("Elnk3: Transferdata: bo=%d bt=%d\n",ByteOffset, BytesToTransfer);)
DEBUG_STAT(pAdapter->Stats.TransferDataCount);
if (ByteOffset+BytesToTransfer > TransDataContext->PacketLength) {
//
// Adjust the amount of data the protocol wants
//
IF_LOUD(DbgPrint("Elnk3: TD() Protocol asked for too much data bo=%d btt=%d pl=%d Da=%d\n",
ByteOffset,
BytesToTransfer,
TransDataContext->PacketLength
);)
if (TransDataContext->PacketLength > ByteOffset) {
BytesToTransfer = TransDataContext->PacketLength - ByteOffset;
} else {
//
// the offset is past the packet length, bad protocol bad
//
*BytesTransferred = 0;
return NDIS_STATUS_SUCCESS;
}
}
DataAvailible=TransDataContext->BytesAlreadyRead;
if (DataAvailible < (TransDataContext->PacketLength+ELNK3_ETHERNET_HEADER_SIZE)) {
//
// We haven't read all of the data out of the fifo yet, so
// let our transfer data completetion handler do it
//
// See how much data there is to transfer.
//
PACKET_RESERVED(Packet)->u.TransData.ByteOffset = ByteOffset;
PACKET_RESERVED(Packet)->u.TransData.BytesToTransfer = BytesToTransfer;
PACKET_RESERVED(Packet)->Next=TransDataContext->Stack;
TransDataContext->Stack=Packet;
return NDIS_STATUS_PENDING;
}
//
// The whole packet fit in the lookahead data, so copy it out
// right now
//
BytesWanted = BytesToTransfer;
IF_RCV_LOUD(DbgPrint("TD: bo=%d bt=%d ps=%d\n",ByteOffset, BytesToTransfer, TransDataContext->PacketLength);)
BytesLeft = BytesWanted;
//
// Get a pointer to the begining of the data to be copied.
// The 14 byte header is handled by lookahead element
//
pBuffer=TransDataContext->LookAhead->LookAheadData+ByteOffset;
NdisQueryPacket(Packet, NULL, NULL, &CurBuffer, NULL);
if (BytesLeft > 0) {
while (1) {
NdisQueryBuffer(CurBuffer, (PVOID *)&BufStart, &BufLen);
//
// See how much data to read into this buffer.
//
BytesNow= BufLen < BytesLeft ? BufLen : BytesLeft;
NdisMoveMemory(
BufStart,
pBuffer,
BytesNow
);
pBuffer += BytesNow;
BytesLeft -= BytesNow;
//
// Is the transfer done now?
//
if (BytesLeft == 0) {
break;
}
NdisGetNextBuffer(CurBuffer, &CurBuffer);
if (CurBuffer == (PNDIS_BUFFER)NULL) {
break;
}
}
}
*BytesTransferred = BytesWanted - BytesLeft;
return NDIS_STATUS_SUCCESS;
}
BOOLEAN
Elnk3EarlyReceive(
PELNK3_ADAPTER pAdapter
)
/*++
Routine Description:
This routine Indicates all of the packets in the ring one at a time
Arguments:
Return Value:
--*/
{
ULONG PacketLength;
USHORT RcvStatus;
ULONG DataAvailible;
UINT PossibleLength;
PTRANSFERDATA_CONTEXT TransDataContext= &pAdapter->TransContext[0];
DataAvailible=TransDataContext->BytesAlreadyRead;
if (DataAvailible == 0) {
//
// First early receive get the lookahead and set the second early receive
//
// With any luck the packet will complete and the packet complete
// interrupt will mask the this early receive thus reducing the effective
// delay in processing the interrupt
//
// If not then this routine will run again and kill some time reading
// in data from the card. This should happen to often and when it
// does we should not have missed by much. The data that we do copy
// will most likely be used any way so not much will be lost.
//
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
PacketLength=BYTES_IN_FIFO_DW(RcvStatus);
NdisRawReadPortBufferUlong(
pAdapter->PortOffsets[PORT_RxFIFO],
(PULONG)((PUCHAR)TransDataContext->LookAhead+DataAvailible),
(PacketLength >> 2)
);
TransDataContext->BytesAlreadyRead += PacketLength;
IF_LOG(0xee,0x01,PacketLength);
PossibleLength=Elnk3GuessFrameSize(TransDataContext->LookAhead,
TransDataContext->BytesAlreadyRead);
IF_RCV_LOUD(DbgPrint("Elnk3: Possible length is %d\n",PossibleLength);)
if (PossibleLength-pAdapter->RxMinimumThreshold > (UINT)pAdapter->LatencyAdjustment) {
ELNK3_COMMAND(pAdapter,EC_SET_RX_EARLY, ((PossibleLength)-pAdapter->LatencyAdjustment) & 0x7ff);
IF_LOG(0xef,0x01,((PossibleLength)-pAdapter->LatencyAdjustment) & 0x7ff);
}
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
if (!(RcvStatus & RX_STATUS_INCOMPLETE)) {
//
// The packet completed while we were reading it in
//
Elnk3IndicatePackets(pAdapter);
}
} else {
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
IF_LOG(0xee,0x02,BYTES_IN_FIFO(RcvStatus));
//
// Second early receive, Copy data until it completes
// and then call indicatepackets
//
while ((RcvStatus & RX_STATUS_INCOMPLETE) && !(RcvStatus & RX_STATUS_ERROR)) {
IF_LOG(0xee,0x03,RcvStatus & 0xc7fc);
if (BYTES_IN_FIFO_DW(RcvStatus) >= 32) {
NdisRawReadPortBufferUlong(
pAdapter->PortOffsets[PORT_RxFIFO],
(PULONG)((PUCHAR)TransDataContext->LookAhead+TransDataContext->BytesAlreadyRead),
32 >> 2
);
TransDataContext->BytesAlreadyRead += 32;
}
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
}
Elnk3IndicatePackets(pAdapter);
DEBUG_STAT(pAdapter->Stats.SecondEarlyReceive);
}
return TRUE;
}
BOOLEAN
Elnk3IndicatePackets(
PELNK3_ADAPTER pAdapter
)
/*++
Routine Description:
This routine Indicates all of the packets in the ring one at a time
Arguments:
Return Value:
--*/
{
ULONG LookAheadSize;
USHORT RcvStatus;
ULONG DataAvailible;
ULONG AdditionalData;
ULONG GoodReceives=0;
ULONG BadReceives=0;
ULONG PossibleLength;
PTRANSFERDATA_CONTEXT TransDataContext= &pAdapter->TransContext[0];
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
IF_LOG(0xdc,0xdc,RcvStatus & 0xc7ff);
IF_RCV_LOUD(DbgPrint("ELNK3: IndicatePackets RX status=%04x\n",RcvStatus);)
do {
while (!(RcvStatus & RX_STATUS_INCOMPLETE)) {
if (!(RcvStatus & RX_STATUS_ERROR)) {
TransDataContext->PacketLength=BYTES_IN_FIFO(RcvStatus);
DataAvailible=TransDataContext->BytesAlreadyRead;
TransDataContext->PacketLength+=DataAvailible;
IF_LOG(0xdc,0x01,TransDataContext->PacketLength);
if ((TransDataContext->PacketLength<=1514) &&
(TransDataContext->PacketLength >= 14)) {
//
// The packet seems to be OK
// Subtract the header length(14) from the packet length
//
TransDataContext->PacketLength-=ELNK3_ETHERNET_HEADER_SIZE;
//
// Lookahead is the smaller of Packetsize and the current lookahead size
//
LookAheadSize=TransDataContext->PacketLength < pAdapter->MaxLookAhead ?
TransDataContext->PacketLength : pAdapter->MaxLookAhead;
if (LookAheadSize+ELNK3_ETHERNET_HEADER_SIZE > DataAvailible) {
//
// We did not get an early receive for this packet.
// Most likly it is a small packet that is less than our threshold.
// Or the latency is too great
//
AdditionalData=ELNK3_ROUND_TO_DWORD(LookAheadSize+ELNK3_ETHERNET_HEADER_SIZE)-DataAvailible;
IF_LOG(0xad,0x01,AdditionalData);
NdisRawReadPortBufferUlong(
pAdapter->PortOffsets[PORT_RxFIFO],
(PULONG)(((PUCHAR)TransDataContext->LookAhead)+DataAvailible),
(AdditionalData>>2)
);
IF_RCV_LOUD(DbgPrint("Elnk3: Indicate: Da=%d Ad=%d\n",DataAvailible, AdditionalData);)
TransDataContext->BytesAlreadyRead+=AdditionalData;
} else {
//
// The lookahead data is waiting for us, so just go ahead and indicate.
//
DEBUG_STAT(pAdapter->Stats.IndicateWithDataReady);
}
GoodReceives++;
IF_RCV_LOUD(DbgPrint("Elnk3: Indicate: ls=%d pl=%d\n", LookAheadSize, TransDataContext->PacketLength);)
TransDataContext->Stack=NULL;
NdisMEthIndicateReceive(
pAdapter->NdisAdapterHandle,
TransDataContext,
(PUCHAR)&TransDataContext->LookAhead->EthHeader,
ELNK3_ETHERNET_HEADER_SIZE,
TransDataContext->LookAhead->LookAheadData,
LookAheadSize,
TransDataContext->PacketLength
);
DEBUG_STAT(pAdapter->Stats.PacketIndicated);
if (TransDataContext->Stack!=NULL) {
//
// at least one protocol called transferdata
//
Elnk3TransferDataCompletion(
TransDataContext
);
}
} else {
IF_RCV_LOUD(DbgPrint("ELNK3: (Packet>1514) || (error in packet) -> not idicated\n");)
}
} else {
IF_RCV_LOUD(DbgPrint("ELNK3: IndicatePackets: error %04x\n", RcvStatus & 0xf800);)
IF_LOG(0xff,0xff,0xffff);
DEBUG_STAT(pAdapter->Stats.BadReceives);
}
Elnk3DiscardPacketSync(TransDataContext);
pAdapter->FramesRcvGood+=GoodReceives;
TransDataContext->BytesAlreadyRead=0;
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
} // while complete
DEBUG_STAT(pAdapter->Stats.IndicationCompleted);
NdisMEthIndicateReceiveComplete(pAdapter->NdisAdapterHandle);
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
//
// See if there is a lookahead's worth of data from the next yet
//
if ( BYTES_IN_FIFO_DW(RcvStatus) > pAdapter->EarlyReceiveThreshold) {
if ( BYTES_IN_FIFO_DW(RcvStatus) > 1200 && (RcvStatus & RX_STATUS_INCOMPLETE)) {
//
// There is alot of data in the fifo, but the packet has not completed yet.
// In order to try to prevent an over flow, lets empty now
//
IF_LOG(0xdc,0xeb,RcvStatus & 0xc7fc);
NdisRawReadPortBufferUlong(
pAdapter->PortOffsets[PORT_RxFIFO],
(PULONG)((PUCHAR)TransDataContext->LookAhead+TransDataContext->BytesAlreadyRead),
BYTES_IN_FIFO_DW(RcvStatus) >> 2
);
TransDataContext->BytesAlreadyRead += BYTES_IN_FIFO_DW(RcvStatus);
} else {
IF_LOG(0xdc,0xea,RcvStatus & 0xc7fc);
NdisRawReadPortBufferUlong(
pAdapter->PortOffsets[PORT_RxFIFO],
(PULONG)((PUCHAR)TransDataContext->LookAhead+TransDataContext->BytesAlreadyRead),
pAdapter->EarlyReceiveThreshold >> 2
);
TransDataContext->BytesAlreadyRead += pAdapter->EarlyReceiveThreshold;
}
}
//
// In the time it took to port i/o in the lookahead data the packet may
// have completed
//
} while (!(RcvStatus & RX_STATUS_INCOMPLETE));
//
// At this point there is either 0 or EarlyReceiveThreshold bytes in the buffer
//
if (TransDataContext->BytesAlreadyRead > 16) {
PossibleLength=Elnk3GuessFrameSize(TransDataContext->LookAhead,
TransDataContext->BytesAlreadyRead);
IF_RCV_LOUD(DbgPrint("Elnk3: Possible length is %d\n",PossibleLength);)
if (PossibleLength > pAdapter->LatencyAdjustment) {
//
// There is enough time to set the threshold before
// the packet passes the new threshold
//
IF_LOG(0xef,0x02,((PossibleLength)-pAdapter->LatencyAdjustment) & 0x7ff);
ELNK3_COMMAND(pAdapter,EC_SET_RX_EARLY, ((PossibleLength)-pAdapter->LatencyAdjustment) & 0x7fc);
}
} else {
//
// Set rx early to catch the front of the packet
//
IF_LOG(0xef,0x02,(pAdapter->LookAheadLatencyAdjustment));
ELNK3_COMMAND(pAdapter,EC_SET_RX_EARLY, (pAdapter->LookAheadLatencyAdjustment) );
}
#if DBG
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
IF_LOG(0xdc,0xdd,RcvStatus & 0xc7ff);
#endif
return TRUE;
}
ULONG
Elnk3GuessFrameSize(
IN PNIC_RCV_HEADER Frame,
IN ULONG BytesNow
)
/*++
Routine Description:
This rountine endevers to determine the size of the packet
from the packet header.
This scheme was borrowed from 3com's ndis 2 driver
Arguments:
Return Value:
--*/
{
ULONG PossibleLength;
PossibleLength=(Frame->EthHeader.EthLength[0]<<8)+
Frame->EthHeader.EthLength[1];
//
// Is it 802.3
//
if (PossibleLength < 0x600 ) {
//
// Looks to an 802.3 frame
//
return (PossibleLength+14);
}
//
// Xns, IP, IPX ?
//
if (PossibleLength==XNS_FRAME_TYPE ||
PossibleLength==IP_FRAME_TYPE ||
PossibleLength==IPX_FRAME_TYPE ) {
PossibleLength=(Frame->LookAheadData[2]<<8)+
Frame->LookAheadData[3]+14;
} else {
//
// Not one we recognise
//
return 1514;
}
if (PossibleLength<=1514 && PossibleLength>=BytesNow) {
//
// Looks reasonable
//
return PossibleLength;
} else {
//
// Don't look like we got the length, just go with max
//
return 1514;
}
}
VOID
Elnk3DiscardIfBroadcast(
IN PELNK3_ADAPTER pAdapter,
IN PTRANSFERDATA_CONTEXT TransDataContext
)
{
USHORT RcvStatus;
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
while (BYTES_IN_FIFO_DW(RcvStatus) > 8) {
IF_LOG(0xaf,0xff,BYTES_IN_FIFO_DW(RcvStatus));
#if DBG
if (TransDataContext->BytesAlreadyRead != 0) {
DbgPrint("Elnk3: FindNextPacket called with BytesAlreadyRead != 0\n");
DbgBreakPoint();
}
#endif
NdisRawReadPortBufferUlong(
pAdapter->PortOffsets[PORT_RxFIFO],
(PULONG)((PUCHAR)TransDataContext->LookAhead),
(8 >> 2)
);
TransDataContext->BytesAlreadyRead = 8;
if (ETH_IS_BROADCAST(&TransDataContext->LookAhead->EthHeader.Destination[0])) {
//
// We don't want this one so, we discard it now
//
Elnk3DiscardPacketSync(TransDataContext);
DEBUG_STAT(pAdapter->Stats.BroadcastsRejected);
//
// No bytes anymore
//
TransDataContext->BytesAlreadyRead = 0;
//
// we chucked it, see what the next one holds in store for us
//
RcvStatus=ELNK3_READ_PORT_USHORT(pAdapter,PORT_RxStatus);
continue;
}
//
// We need to indicate it
//
break;
}
}
VOID
Elnk3TransferDataCompletion(
PTRANSFERDATA_CONTEXT TransDataContext
)
{
PELNK3_ADAPTER pAdapter;
PNDIS_PACKET Packet;
PNDIS_BUFFER CurBuffer;
PUCHAR BufferAddress;
ULONG BufferLength;
ULONG DataAvailible;
ULONG OffsetInLookAhead;
ULONG BytesLeft;
ULONG BytesNow;
ULONG ByteOffset;
ULONG BytesToTransfer;
ULONG AdditionalData;
pAdapter=TransDataContext->pAdapter;
//
// See if more than one binding called transferdata
//
if (PACKET_RESERVED(TransDataContext->Stack)->Next==NULL) {
//
// just one, just port io into the ndis buffer
//
IF_RCV_LOUD(DbgPrint("Elnk3: TransComp: One transferdata call\n");)
Packet=TransDataContext->Stack;
//
// See just what this protocol wants
//
BytesToTransfer = PACKET_RESERVED(Packet)->u.TransData.BytesToTransfer;
ByteOffset = PACKET_RESERVED(Packet)->u.TransData.ByteOffset;
IF_RCV_LOUD(DbgPrint("Elnk3: TransComp: bo=%d bt=%d\n",ByteOffset,BytesToTransfer);)
BytesLeft=BytesToTransfer;
OffsetInLookAhead=ByteOffset+ELNK3_ETHERNET_HEADER_SIZE;
NdisQueryPacket(Packet, NULL, NULL, &CurBuffer, NULL);
NdisQueryBuffer(CurBuffer, (PVOID *)&BufferAddress, &BufferLength);
DataAvailible=TransDataContext->BytesAlreadyRead;
IF_RCV_LOUD(DbgPrint("Elnk3: TransComp: DataAvailible=%d offset=%d\n",DataAvailible,OffsetInLookAhead);)
//
// copy the data out of the lookahead buffer
//
// Note, that this loop assumes that whole packet does not fit
// in the lookahead data. If it does TransferData does not pend
// and this routine is not called. This is a problem because
// we read data out of the fifo in multiples of dwords and we
// get padding bytes in our data availible count
//
if (OffsetInLookAhead > DataAvailible) {
//
// The byte offset is past the lookahead data.
// We need to port some more stuff out of the fifo
// to get to where the data is that we want
//
NdisRawReadPortBufferUchar(
pAdapter->PortOffsets[PORT_RxFIFO],
(PUCHAR)((PUCHAR)TransDataContext->LookAhead+TransDataContext->BytesAlreadyRead),
OffsetInLookAhead - DataAvailible
);
TransDataContext->BytesAlreadyRead+=(OffsetInLookAhead - DataAvailible);
} else {
//
// the byteoffset is with in the lookahead data
// copy it to the ndis buffer
//
while (OffsetInLookAhead < DataAvailible) {
//
// Need to copy some of lookeahead into protocol packet
//
BytesNow= BufferLength < (DataAvailible-OffsetInLookAhead) ?
BufferLength : (DataAvailible-OffsetInLookAhead);
IF_RCV_LOUD(DbgPrint("Elnk3: TransComp: lookahead bn=%d offset=%d\n",BytesNow,OffsetInLookAhead);)
NdisMoveMemory(
BufferAddress,
(PUCHAR)TransDataContext->LookAhead+OffsetInLookAhead,
BytesNow
);
BytesLeft-=BytesNow;
OffsetInLookAhead+=BytesNow;
BufferAddress+=BytesNow;
BufferLength-=BytesNow;
if (BufferLength==0) {
NdisGetNextBuffer(CurBuffer, &CurBuffer);
if (CurBuffer==NULL) {
break;
}
NdisQueryBuffer(CurBuffer, (PVOID *)&BufferAddress, &BufferLength);
}
}
}
//
// port i/o in the rest of the packet into the protocols packet
//
while (BytesLeft > 0) {
BytesNow= BufferLength < BytesLeft ?
BufferLength : BytesLeft;
IF_RCV_LOUD(DbgPrint("Elnk3: TransComp: bl=%d bn=%d\n",BytesLeft,BytesNow);)
IF_LOG(0xad,0xd1,BytesNow);
NdisRawReadPortBufferUlong(
pAdapter->PortOffsets[PORT_RxFIFO],
(PULONG)BufferAddress,
BytesNow>>2
);
if (BytesNow & 3) {
NdisRawReadPortBufferUchar(
pAdapter->PortOffsets[PORT_RxFIFO],
(PUCHAR)((PUCHAR)BufferAddress+(BytesNow & 0xfffffffc)),
BytesNow & 3
);
}
BytesLeft-=BytesNow;
NdisGetNextBuffer(CurBuffer, &CurBuffer);
if (CurBuffer == NULL) {
break;
}
NdisQueryBuffer(CurBuffer, (PVOID *)&BufferAddress, &BufferLength);
}
NdisMTransferDataComplete(
pAdapter->NdisAdapterHandle,
Packet,
NDIS_STATUS_SUCCESS,
BytesToTransfer
);
return ;
}
//
// More than one protocol wants the packet.
// We will port i/o the whole thing to one of our buffers
// and then copy the data into the protocols packets
//
IF_RCV_LOUD(DbgPrint("Elnk3: TransComp: Multiple transferdata calls\n");)
while ((Packet=TransDataContext->Stack) != NULL) {
TransDataContext->Stack=PACKET_RESERVED(Packet)->Next;
//
// See just what this protocol wants
//
BytesToTransfer = PACKET_RESERVED(Packet)->u.TransData.BytesToTransfer;
ByteOffset = PACKET_RESERVED(Packet)->u.TransData.ByteOffset;
IF_RCV_LOUD(DbgPrint("Elnk3: TransComp: bo=%d bt=%d\n",ByteOffset,BytesToTransfer);)
IF_RCV_LOUD(DbgPrint("TD: bo=%d bt=%d ps=%d\n",ByteOffset, BytesToTransfer, TransDataContext->PacketLength);)
//
// We got this much so far
//
DataAvailible=TransDataContext->BytesAlreadyRead;
//
// See if the whole thing has been removed from the fifo
//
if (DataAvailible < (TransDataContext->PacketLength+ELNK3_ETHERNET_HEADER_SIZE)) {
//
// Get the rest of the data from the fifo
//
AdditionalData=ELNK3_ROUND_TO_DWORD((TransDataContext->PacketLength+ELNK3_ETHERNET_HEADER_SIZE)-DataAvailible);
NdisRawReadPortBufferUlong(
pAdapter->PortOffsets[PORT_RxFIFO],
(PULONG)((PUCHAR)TransDataContext->LookAhead+DataAvailible),
AdditionalData>>2
);
IF_LOG(0xad,0x02,AdditionalData);
IF_RCV_LOUD(DbgPrint("Elnk3: TransComp: Da=%d Ad=%d\n",DataAvailible, AdditionalData);)
TransDataContext->BytesAlreadyRead+=AdditionalData;
}
BytesLeft = BytesToTransfer;
OffsetInLookAhead=ByteOffset;
NdisQueryPacket(Packet, NULL, NULL, &CurBuffer, NULL);
while (BytesLeft > 0) {
NdisQueryBuffer(CurBuffer, (PVOID *)&BufferAddress, &BufferLength);
//
// See how much data to read into this buffer.
//
BytesNow= BufferLength < BytesLeft ? BufferLength : BytesLeft;
//
// Note: the LookAheadData element of the structure handles the
// 14 byte header discrepency
//
NdisMoveMemory(
BufferAddress,
TransDataContext->LookAhead->LookAheadData+OffsetInLookAhead,
BytesNow
);
OffsetInLookAhead += BytesNow;
BytesLeft -= BytesNow;
//
// Is the transfer done now?
//
if (BytesLeft == 0) {
break;
}
NdisGetNextBuffer(CurBuffer, &CurBuffer);
if (CurBuffer == (PNDIS_BUFFER)NULL) {
break;
}
}
NdisMTransferDataComplete(
pAdapter->NdisAdapterHandle,
Packet,
NDIS_STATUS_SUCCESS,
BytesToTransfer
);
} // while packets
return;
}