mirror of https://github.com/lianthony/NT4.0
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.
1722 lines
45 KiB
1722 lines
45 KiB
/*++
|
|
|
|
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;
|
|
|
|
}
|