diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/ndis/elnk3/receive.c | 1722 |
1 files changed, 1722 insertions, 0 deletions
diff --git a/private/ntos/ndis/elnk3/receive.c b/private/ntos/ndis/elnk3/receive.c new file mode 100644 index 000000000..2acd4d6a9 --- /dev/null +++ b/private/ntos/ndis/elnk3/receive.c @@ -0,0 +1,1722 @@ +/*++ + +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; + +} |