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

524 lines
11 KiB
C

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
ltrecv.c
Abstract:
This module contains the receive processing routines.
Author:
Stephen Hou (stephh@microsoft.com)
Nikhil Kamkolkar (nikhilk@microsoft.com)
Revision History:
19 Jun 1992 Initial Version (dch@pacvax.pacersoft.com)
Notes: Tab stop: 4
--*/
#define LTRECV_H_LOCALS
#include "ltmain.h"
// Define file id for errorlogging
#define FILENUM LTRECV
VOID
LtRecvProcessQueue(
IN PLT_ADAPTER Adapter
)
/*++
Routine Description:
This routine is called by the timer poll routine to process the receive
queue. Note that the actual receives from the card happen in the timer
poll routine itself.
Arguments:
Adapter : Pointer to the Adapter on which receive completion
needs to happen.
Return Value:
None.
--*/
{
UINT PacketLength, DgramLength;
PUCHAR Packet, Dgram;
PLIST_ENTRY p;
PRECV_DESC RecvDesc;
DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_ENTRY,
("LtRecvProcessQueue: Entering...\n"));
NdisAcquireSpinLock(&Adapter->Lock);
while((!IsListEmpty(&Adapter->Receive)) &&
((Adapter->Flags & ADAPTER_RESET_IN_PROGRESS) == 0))
{
p = RemoveHeadList(&Adapter->Receive);
RecvDesc = CONTAINING_RECORD(
p,
RECV_DESC,
Linkage);
// We will always have the link header at minimum
PacketLength = RecvDesc->BufferLength;
DgramLength = PacketLength - LT_LINK_HEADER_LENGTH;
ASSERTMSG("LtRecvProcessQueue: Packet length 0!\n", PacketLength != 0);
Packet = (PUCHAR)((PUCHAR)RecvDesc+sizeof(RECV_DESC));
Dgram = Packet + LT_LINK_HEADER_LENGTH;
if (IsListEmpty(&Adapter->OpenBindings))
{
DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_WARN,
("LtRecvProcessQueue: No Binding! Discarding packet!\n"));
// No body to receive this, free up the buffer;
NdisFreeMemory(
RecvDesc,
sizeof(RecvDesc)+PacketLength,
0);
continue;
}
// Indicate the packet to all the open bindings on this adapter.
// After return from this routine, we should be able to free up
// the packet.
LtRecvIndicatePacket(
Adapter,
Packet,
Dgram,
DgramLength,
DgramLength,
(NDIS_HANDLE)Packet);
NdisFreeMemory(
RecvDesc,
sizeof(RecvDesc)+PacketLength,
0);
}
// Check if we need to do any receive completes.
LtRecvQueueCompletion(Adapter);
NdisReleaseSpinLock(&Adapter->Lock);
DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_ENTRY,
("LTProcessReceiveQueue: Leaving...\n"));
return;
}
NDIS_STATUS
LtRecvTransferData(
IN NDIS_HANDLE MacBindingHandle,
IN NDIS_HANDLE MacReceiveContext,
IN UINT ByteOffset,
IN UINT BytesToTransfer,
OUT PNDIS_PACKET Packet,
OUT PUINT BytesTransferred
)
/*++
Routine Description:
This is called by ndis to transfer previously indicated data. A
MacReceiveContext of NULL is used to transfer data from the current
loopback packet.
Arguments:
As described in NDIS 3.0.
MacReceiveContext : NULL - Use current loopback packet
Otherwise it is a pointer to a RECV_DESC.
Return Value:
NDIS_STATUS_SUCCESS : If successful, error otherwise.
--*/
{
BOOLEAN DerefAdapter = FALSE;
BOOLEAN DerefBinding = FALSE;
PLT_OPEN Binding = (PLT_OPEN)MacBindingHandle;
PLT_ADAPTER Adapter = Binding->LtAdapter;
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_ENTRY,
("LtRecvTransferData: Entered\n"));
NdisAcquireSpinLock(&Adapter->Lock);
do
{
LtReferenceAdapterNonInterlock(Adapter, &Status);
if (Status != NDIS_STATUS_SUCCESS)
{
Status = NDIS_STATUS_REQUEST_ABORTED;
break;
}
else
{
DerefAdapter = TRUE;
LtReferenceBindingNonInterlock(Binding, &Status);
if (Status != NDIS_STATUS_SUCCESS)
{
Status = NDIS_STATUS_REQUEST_ABORTED;
break;
}
DerefBinding = TRUE;
}
if (Adapter->Flags & ADAPTER_RESET_IN_PROGRESS)
{
Status = NDIS_STATUS_RESET_IN_PROGRESS;
break;
}
} while (FALSE);
NdisReleaseSpinLock(&Adapter->Lock);
if (Status == NDIS_STATUS_SUCCESS)
{
if (MacReceiveContext == NULL)
{
DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_INFO,
("LtRecvTransferData: CurrentLookXfer\n"));
NdisCopyFromPacketToPacket(
Packet,
0,
BytesToTransfer,
Adapter->CurrentLoopbackPacket,
ByteOffset + LT_LINK_HEADER_LENGTH,
BytesTransferred);
}
else
{
DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_INFO,
("LtRecvTransferData: NormalXfer\n"));
LtUtilsCopyFromBufferToPacket(
(PUCHAR)MacReceiveContext,
ByteOffset + LT_LINK_HEADER_LENGTH,
BytesToTransfer,
Packet,
BytesTransferred);
}
}
if (DerefAdapter)
LtDeReferenceAdapter(Adapter);
if (DerefBinding)
LtDeReferenceBinding(Binding);
return Status;
}
VOID
LtRecvIndicatePacket(
IN PLT_ADAPTER Adapter,
IN PUCHAR LinkHdr,
IN PUCHAR LookAheadBuffer,
IN UINT LookAheadSize,
IN UINT DgramLength,
IN NDIS_HANDLE IndicateCtx
)
/*++
Routine Description:
This routine is called to indicate a specific packet to all bindings
on an adapter.
Arguments:
Adapter : Pointer to the adapter
LinkHdr : Link header, guaranteed to be 3 bytes
LookAheadBuffer : Lookahead buffer to indicate
LookAheadSize : Size of lookahead buffer (excludes link header)
DgramLength : Size of the complete packet (excludes link header)
IndicateCtx : Ctx to pass as indicate context to NDIS
Return Value:
None.
--*/
{
NDIS_STATUS RefStatus, Status;
PLT_OPEN NextBinding, Binding;
UINT CurLookAheadSize;
NextBinding = NULL;
LtReferenceBindingNextNcNonInterlock(
Adapter->OpenBindings.Flink,
&Adapter->OpenBindings,
&Binding,
&RefStatus);
while (RefStatus == NDIS_STATUS_SUCCESS)
{
// Reference the next non-closing binding
LtReferenceBindingNextNcNonInterlock(
Binding->Linkage.Flink,
&Adapter->OpenBindings,
&NextBinding,
&RefStatus);
// Never more than one binding usually, remove when not true.
ASSERT(RefStatus != NDIS_STATUS_SUCCESS);
// Go ahead and do the indicate.
CurLookAheadSize = Binding->CurrentLookAheadSize;
if (((LtUtilsUcharPacketType(LinkHdr[0], LinkHdr[1]) == LT_BROADCAST) &&
(Binding->CurrentPacketFilter & NDIS_PACKET_TYPE_BROADCAST))
||
((LtUtilsUcharPacketType(LinkHdr[0], LinkHdr[1]) != LT_BROADCAST) &&
(Binding->CurrentPacketFilter & NDIS_PACKET_TYPE_DIRECTED)))
{
DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_INFO,
("LtRecvIndicatePacket: Indicating packet on bind %lx\n",
Binding));
NdisReleaseSpinLock(&Adapter->Lock);
NdisIndicateReceive(
&Status,
Binding->NdisBindingContext,
IndicateCtx,
LinkHdr,
LT_LINK_HEADER_LENGTH,
LookAheadBuffer,
((LookAheadSize > CurLookAheadSize) ? \
CurLookAheadSize : LookAheadSize),
DgramLength);
NdisAcquireSpinLock(&Adapter->Lock);
if (Status != NDIS_STATUS_SUCCESS)
{
Adapter->MediaOptional[MO_NO_HANDLERS] ++;
}
// Since this routine is called within a loop in LtRecvProcessQueue,
// and since we need only one reference per binding for receive
// completion, we do the following to avoid over-referencing the
// binding structure.
if (Binding->Flags & BINDING_DO_RECV_COMPLETION)
{
NdisReleaseSpinLock(&Adapter->Lock);
LtDeReferenceBinding(Binding);
NdisAcquireSpinLock(&Adapter->Lock);
}
else
{
// Remember this binding needs a receive completion
Binding->Flags |= BINDING_DO_RECV_COMPLETION;
}
// Also, make a note in the adapter so that the receive
// completion handler is enqueued.
Adapter->Flags |= ADAPTER_QUEUE_RECV_COMPLETION;
}
else
{
// Remove the reference on this binding.
NdisReleaseSpinLock(&Adapter->Lock);
LtDeReferenceBinding(Binding);
NdisAcquireSpinLock(&Adapter->Lock);
}
// Never more than one binding usually, remove when not true.
ASSERT(RefStatus != NDIS_STATUS_SUCCESS);
Binding = NextBinding;
}
return;
}
VOID
LtRecvQueueCompletion(
IN PLT_ADAPTER Adapter
)
/*++
Routine Description:
This routine is used to queue up a completion handler on the adapter.
This handler will then indicate receive completion to all necessary
bindings on the adapter.
ASSUMES: Adapter->Lock is HELD.
Arguments:
Adapter : Pointer to the adapter
Return Value:
None.
--*/
{
NDIS_STATUS Status;
BOOLEAN Queue = FALSE;
// !!! ASSUMES Adapter->Lock is held!!!
if (Adapter->Flags & ADAPTER_QUEUE_RECV_COMPLETION)
{
if ((Adapter->Flags & ADAPTER_QUEUED_RECV_COMPLETION) == 0)
{
Adapter->Flags |= ADAPTER_QUEUED_RECV_COMPLETION;
Queue = TRUE;
}
Adapter->Flags &= ~ADAPTER_QUEUE_RECV_COMPLETION;
}
if (Queue)
{
DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_INFO,
("LtRecvQueueCompletion: queing receive complete\n"));
// !!! NOTE !!!
// This should never fail!! If Queue is set, then a binding
// was referenced for receive completion. So it couldnt have
// gone away. And so the adapter cannot be in a CLOSING state.
// As all binding need to have gone away, before the RemovAdapter
// is called by NDIS.
LtReferenceAdapterNonInterlock(Adapter, &Status);
ASSERTMSG("LtRecvQueueCompletion: Adapter is closing!\n",
(Status == NDIS_STATUS_SUCCESS));
if (Status != NDIS_STATUS_SUCCESS)
{
// !!! KEBUGCHECK() !!!
KeBugCheck((ULONG)__LINE__);
}
NdisReleaseSpinLock(&Adapter->Lock);
LtRecvCompletion(Adapter);
LtDeReferenceAdapter(Adapter);
NdisAcquireSpinLock(&Adapter->Lock);
}
return;
}
NTSTATUS
LtRecvCompletion(
IN PLT_ADAPTER Adapter
)
/*++
Routine Description:
Called to indicate receive completion on all binding on this adapter.
This will loop until all receive completions are done. This might
tend to starve bindings towards the end of the list, but we wont
worry about that.
Arguments:
Adapter : Pointer to the Adapter on which receive completion
needs to happen.
Return Value:
STATUS_SUCCESS
--*/
{
PLIST_ENTRY p;
PLT_OPEN Binding;
// For each binding, if recv completion is to be called, do it.
NdisAcquireSpinLock(&Adapter->Lock);
DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_INFO,
("LtRecvCompletion: Indicating receive completion\n"));
for (p = Adapter->OpenBindings.Flink; (p != &Adapter->OpenBindings);)
{
Binding = CONTAINING_RECORD(
p,
LT_OPEN,
Linkage);
if (Binding->Flags & BINDING_DO_RECV_COMPLETION)
{
Binding->Flags &= ~BINDING_DO_RECV_COMPLETION;
}
else
{
// !!! Note the continue in here !!!
DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_WARN,
("LtRecvCompletion: No recv comp flag on binding\n"));
p = p->Flink;
// Never more than one binding usually, remove when not true.
ASSERT(p == &Adapter->OpenBindings);
continue;
}
NdisReleaseSpinLock(&Adapter->Lock);
// Call NdisReceiveCompletion for this binding.
NdisIndicateReceiveComplete(Binding->NdisBindingContext);
// Dereference the binding, this was added in the process queue
// routine.
LtDeReferenceBinding(Binding);
NdisAcquireSpinLock(&Adapter->Lock);
// Restart the search
p = Adapter->OpenBindings.Flink;
}
DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_INFO,
("LtRecvCompletion: Enabling receive queing\n"));
// Enable any new queue requests to take effect.
Adapter->Flags &= ~ADAPTER_QUEUED_RECV_COMPLETION;
NdisReleaseSpinLock(&Adapter->Lock);
return STATUS_SUCCESS;
}