windows-nt/Source/XPSP1/NT/net/rndis/rndismp/receive.c
2020-09-26 16:20:57 +08:00

1285 lines
50 KiB
C

/***************************************************************************
Copyright (c) 1999 Microsoft Corporation
Module Name:
RECEIVE.C
Abstract:
Packet and message receive routines
Environment:
kernel mode only
Notes:
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE.
Copyright (c) 1999 Microsoft Corporation. All Rights Reserved.
Revision History:
5/20/99 : created
Author:
Tom Green
****************************************************************************/
#include "precomp.h"
//
// Some debug stuff, not critical to operation:
//
ULONG RcvFrameAllocs = 0;
ULONG RcvTimerCount = 0;
ULONG RcvPacketCount = 0;
ULONG RcvMaxPackets = 0;
ULONG RcvIndicateCount = 0;
ULONG RcvReturnCount = 0;
//
// For raw encapsulation test
//
extern ULONG gRawEncap;
/****************************************************************************/
/* RndismpGetReturnedPackets */
/****************************************************************************/
/* */
/* Routine Description: */
/* */
/* This function is called by NDIS to return to our possession a packet */
/* that we had indicated up. */
/* */
/* Arguments: */
/* */
/* MiniportAdapterContext - a context version of our Adapter pointer */
/* pNdisPacket - the packet that is being freed */
/* */
/* Return: */
/* */
/* VOID */
/* */
/****************************************************************************/
VOID
RndismpReturnPacket(IN NDIS_HANDLE MiniportAdapterContext,
IN PNDIS_PACKET pNdisPacket)
{
PRNDISMP_ADAPTER pAdapter;
PRNDISMP_RECV_PKT_RESERVED pRcvResvd;
PRNDISMP_RECV_DATA_FRAME pRcvFrame;
PRNDISMP_VC pVc;
PNDIS_BUFFER pNdisBuffer;
ULONG RefCount;
// get adapter context
pAdapter = PRNDISMP_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
CHECK_VALID_ADAPTER(pAdapter);
TRACE2(("RndismpReturnPacket: Adapter %x, Pkt %x\n", pAdapter, pNdisPacket));
// get receive frame context
pRcvResvd = PRNDISMP_RESERVED_FROM_RECV_PACKET(pNdisPacket);
pRcvFrame = pRcvResvd->pRcvFrame;
pVc = pRcvResvd->pVc;
// Free the buffer.
NdisQueryPacket(pNdisPacket,
NULL,
NULL,
&pNdisBuffer,
NULL);
NdisFreeBuffer(pNdisBuffer);
DereferenceRcvFrame(pRcvFrame, pAdapter);
if (pVc != NULL)
{
RNDISMP_DEREF_VC(pVc, &RefCount);
}
NdisFreePacket(pNdisPacket);
RcvReturnCount++;
} // RndismpReturnPacket
/****************************************************************************/
/* DereferenceRcvFrame */
/****************************************************************************/
/* */
/* Routine Description: */
/* */
/* Utility routine to deref a receive frame structure, e.g. when a */
/* received packet is returned to us from higher layers. */
/* */
/* Arguments: */
/* */
/* pRcvFrame - Pointer to receive frame to be deref'ed. */
/* pAdapter - Pointer to adapter structure */
/* */
/* Return: */
/* */
/* VOID */
/* */
/****************************************************************************/
VOID
DereferenceRcvFrame(IN PRNDISMP_RECV_DATA_FRAME pRcvFrame,
IN PRNDISMP_ADAPTER pAdapter)
{
ULONG ReturnsPending;
ReturnsPending = NdisInterlockedDecrement(&pRcvFrame->ReturnsPending);
if (ReturnsPending == 0)
{
TRACE3(("DerefRcvFrame: Adapter %x, Frame %p, uPcontext %x, LocalCopy %d\n",
pAdapter, pRcvFrame, pRcvFrame->MicroportMessageContext, pRcvFrame->bMessageCopy));
if (pRcvFrame->bMessageCopy)
{
FreeRcvMessageCopy(pRcvFrame->pLocalMessageCopy);
}
else
{
TRACE3(("DerefRcvFrame: uP MDL %x, uPContext %x\n",
pRcvFrame->pMicroportMdl,
pRcvFrame->MicroportMessageContext));
RNDISMP_RETURN_TO_MICROPORT(pAdapter,
pRcvFrame->pMicroportMdl,
pRcvFrame->MicroportMessageContext);
}
FreeReceiveFrame(pRcvFrame, pAdapter);
}
} // DereferenceRcvFrame
/****************************************************************************/
/* RndisMIndicateReceive */
/****************************************************************************/
/* */
/* Routine Description: */
/* */
/* Called by microport to indicate receiving RNDIS messages */
/* */
/* Arguments: */
/* */
/* MiniportAdapterContext - a context version of our Adapter pointer */
/* pMdl - pointer to MDL chain describing RNDIS message */
/* MicroportMessageContext - context for message from micorport */
/* ChannelType - channel on which this message arrived (control/data) */
/* ReceiveStatus - used by microport to indicate it is low on resource */
/* */
/* Return: */
/* */
/* VOID */
/* */
/****************************************************************************/
VOID
RndisMIndicateReceive(IN NDIS_HANDLE MiniportAdapterContext,
IN PMDL pMdl,
IN NDIS_HANDLE MicroportMessageContext,
IN RM_CHANNEL_TYPE ChannelType,
IN NDIS_STATUS ReceiveStatus)
{
PRNDISMP_ADAPTER Adapter;
PRNDIS_MESSAGE pMessage;
BOOLEAN bMessageCopied = FALSE;
PRNDISMP_MSG_HANDLER_FUNC pMsgHandlerFunc;
BOOLEAN bReturnToMicroport;
NDIS_STATUS Status;
PRNDISMP_RECV_MSG_CONTEXT pRcvMsg;
PMDL pTmpMdl;
ULONG TotalLength;
Adapter = PRNDISMP_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
CHECK_VALID_ADAPTER(Adapter);
TRACE2(("RndisIndicateReceive: Adapter %x, Mdl %x\n", Adapter, pMdl));
RNDISMP_ASSERT_AT_DISPATCH();
bReturnToMicroport = TRUE;
#if DBG
NdisInterlockedIncrement(&Adapter->MicroportReceivesOutstanding);
#endif
do
{
//
// Find the total length first.
//
TotalLength = 0;
for (pTmpMdl = pMdl; pTmpMdl != NULL; pTmpMdl = RNDISMP_GET_MDL_NEXT(pTmpMdl))
{
TotalLength += RNDISMP_GET_MDL_LENGTH(pTmpMdl);
}
//
// Check if the entire message is in a single MDL - if not, make a copy
// TBD -- handle multi-MDL messages without copying.
//
if ((RNDISMP_GET_MDL_NEXT(pMdl) == NULL) &&
(!Adapter->bRunningOnWin9x || (ReceiveStatus != NDIS_STATUS_RESOURCES)))
{
pMessage = RNDISMP_GET_MDL_ADDRESS(pMdl);
if (pMessage == NULL)
{
TRACE0(("RndisMIndicateReceive: Adapter %x: failed to"
" access msg from MDL %x\n", Adapter, pMdl));
break;
}
}
else
{
pMessage = CoalesceMultiMdlMessage(pMdl, TotalLength);
if (pMessage == NULL)
{
break;
}
bMessageCopied = TRUE;
}
TRACEDUMP(("Received msg (%d bytes):\n", TotalLength),
pMessage, TotalLength);
// get timer tick for this message
NdisGetSystemUpTime(&Adapter->LastMessageFromDevice);
if (Adapter->bRunningOnWin9x)
{
Status = MemAlloc(&pRcvMsg, sizeof(RNDISMP_RECV_MSG_CONTEXT));
if (Status != NDIS_STATUS_SUCCESS)
{
bReturnToMicroport = TRUE;
TRACE1(("RndisMIndicateReceive: Adapter %x, failed to alloc rcv msg\n",
Adapter));
break;
}
pRcvMsg->MicroportMessageContext = MicroportMessageContext;
pRcvMsg->pMdl = pMdl;
pRcvMsg->TotalLength = TotalLength;
pRcvMsg->pMessage = pMessage;
pRcvMsg->ReceiveStatus = ReceiveStatus;
pRcvMsg->bMessageCopied = bMessageCopied;
pRcvMsg->ChannelType = ChannelType;
//
// Queue all packets for indicating receives up to protocols.
// We do this rather than indicate packets directly because
// we are in a DPC context, and need to be in a "global event"
// context to make the upper layers happy. One way to be in a
// global event context is to be in the context of an NDIS timer
// callback function.
//
// So, queue this up on the adapter and start a timer
// routine if necessary.
//
bReturnToMicroport = FALSE;
RNDISMP_ACQUIRE_ADAPTER_LOCK(Adapter);
InsertTailList(&Adapter->PendingRcvMessageList, &pRcvMsg->Link);
if (!Adapter->IndicatingReceives)
{
Adapter->IndicatingReceives = TRUE;
NdisSetTimer(&Adapter->IndicateTimer, 0);
}
RNDISMP_RELEASE_ADAPTER_LOCK(Adapter);
}
else
{
//
// Running on NT.
if ((Adapter->DeviceFlags & RNDIS_DF_RAW_DATA) || (gRawEncap))
{
if (ChannelType == RMC_CONTROL)
{
RNDISMP_GET_MSG_HANDLER(pMsgHandlerFunc,pMessage->NdisMessageType);
#if DBG
ASSERT(pMessage->NdisMessageType != REMOTE_NDIS_PACKET_MSG);
#endif
} else
{
pMsgHandlerFunc = ReceivePacketMessageRaw;
}
} else
{
RNDISMP_GET_MSG_HANDLER(pMsgHandlerFunc, pMessage->NdisMessageType);
#if DBG
if (pMessage->NdisMessageType == REMOTE_NDIS_PACKET_MSG)
{
ASSERT(ChannelType == RMC_DATA);
}
else
{
ASSERT(ChannelType == RMC_CONTROL);
}
#endif
}
bReturnToMicroport = (*pMsgHandlerFunc)(
Adapter,
pMessage,
pMdl,
TotalLength,
MicroportMessageContext,
ReceiveStatus,
bMessageCopied);
}
}
while (FALSE);
//
// Are we done with the microport's message?
//
if (bReturnToMicroport || bMessageCopied)
{
RNDISMP_RETURN_TO_MICROPORT(Adapter,
pMdl,
MicroportMessageContext);
}
//
// If we had made a copy of the microport's message, are we done with
// this copy?
//
if (bMessageCopied && bReturnToMicroport)
{
FreeRcvMessageCopy(pMessage);
}
}
/****************************************************************************/
/* CoalesceMultiMdlMessage */
/****************************************************************************/
/* */
/* Routine Description: */
/* */
/* Make a copy of a received message that is in a chain of multiple */
/* MDLs, into one single buffer. */
/* */
/* Arguments: */
/* */
/* pMdl - pointer to MDL that is the head of the chain. */
/* TotalLength - length of data contained in entire chain. */
/* */
/* Return: */
/* */
/* PRNDIS_MESSAGE */
/* */
/****************************************************************************/
PRNDIS_MESSAGE
CoalesceMultiMdlMessage(IN PMDL pMdl,
IN ULONG TotalLength)
{
ULONG MdlLength;
PRNDIS_MESSAGE pMessage;
NDIS_STATUS Status;
PMDL pTmpMdl;
PUCHAR pDest;
TRACE2(("Coalesce: Mdl %x\n", pMdl));
Status = MemAlloc(&pMessage, TotalLength);
if (Status == NDIS_STATUS_SUCCESS)
{
pDest = (PUCHAR)pMessage;
for (pTmpMdl = pMdl; pTmpMdl != NULL; pTmpMdl = RNDISMP_GET_MDL_NEXT(pTmpMdl))
{
MdlLength = RNDISMP_GET_MDL_LENGTH(pTmpMdl);
RNDISMP_MOVE_MEM(pDest,
RNDISMP_GET_MDL_ADDRESS(pTmpMdl),
MdlLength);
pDest = (PUCHAR)pDest + MdlLength;
}
}
else
{
pMessage = NULL;
}
return (pMessage);
}
/****************************************************************************/
/* FreeRcvMessageCopy */
/****************************************************************************/
/* */
/* Routine Description: */
/* */
/* Free the local copy of a received RNDIS message. */
/* */
/* Arguments: */
/* */
/* pMessage - pointer to RNDIS message */
/* */
/* Return: */
/* */
/* VOID */
/* */
/****************************************************************************/
VOID
FreeRcvMessageCopy(IN PRNDIS_MESSAGE pMessage)
{
TRACE3(("FreeRcvMessageCopy: pMessage %x\n", pMessage));
MemFree(pMessage, -1);
}
/****************************************************************************/
/* ReceivePacketMessage */
/****************************************************************************/
/* */
/* Routine Description: */
/* */
/* Got a packet message, so send it to the upper layers */
/* */
/* Arguments: */
/* */
/* pAdapter - pointer to our Adapter structure */
/* pMessage - pointer to RNDIS message */
/* pMdl - pointer to MDL received from microport */
/* TotalLength - length of complete message */
/* MicroportMessageContext - context for message from micorport */
/* ReceiveStatus - used by microport to indicate it is low on resource */
/* bMessageCopied - is this a copy of the original message? */
/* */
/* Return: */
/* */
/* BOOLEAN - should the message be returned to the microport? */
/* */
/****************************************************************************/
BOOLEAN
ReceivePacketMessage(IN PRNDISMP_ADAPTER pAdapter,
IN PRNDIS_MESSAGE pMessage,
IN PMDL pMdl,
IN ULONG TotalLength,
IN NDIS_HANDLE MicroportMessageContext,
IN NDIS_STATUS ReceiveStatus,
IN BOOLEAN bMessageCopied)
{
ULONG LengthRemaining; // in entire message
PMDL pTmpMdl;
PRNDISMP_RECV_DATA_FRAME pRcvFrame;
ULONG NumberOfPackets;
PRNDIS_PACKET pRndisPacket;
ULONG i;
#define MAX_RECV_PACKETS_IN_MSG 40
PNDIS_PACKET PacketArray[MAX_RECV_PACKETS_IN_MSG];
ULONG NumPackets;
PNDIS_PACKET pNdisPacket;
PRNDISMP_RECV_PKT_RESERVED pRcvResvd;
PNDIS_BUFFER pNdisBuffer;
NDIS_STATUS BufferStatus;
NDIS_STATUS Status;
BOOLEAN bDiscardPkt;
PRNDISMP_VC pVc;
bDiscardPkt = FALSE;
pVc = NULL;
do
{
#ifndef BUILD_WIN9X
if (bMessageCopied)
{
ReceiveStatus = NDIS_STATUS_SUCCESS;
}
#else
//
// Rur ReturnPacket handler never gets called on
// Win98 Gold, so we force the status to be able
// to reclaim the indicated packet immediately.
//
ReceiveStatus = NDIS_STATUS_RESOURCES;
#endif
//
// Allocate a receive frame to keep track of this RNDIS packet message.
//
pRcvFrame = AllocateReceiveFrame(pAdapter);
if (pRcvFrame == NULL)
{
bDiscardPkt = TRUE;
break;
}
pRcvFrame->MicroportMessageContext = MicroportMessageContext;
if (bMessageCopied)
{
pRcvFrame->pLocalMessageCopy = pMessage;
pRcvFrame->bMessageCopy = TRUE;
}
else
{
pRcvFrame->pMicroportMdl = pMdl;
pRcvFrame->bMessageCopy = FALSE;
}
NumberOfPackets = 0;
LengthRemaining = TotalLength;
//
// TBD - Check that the received message is well-formed!
//
//
// Temp ref to take care of multiple indications.
//
pRcvFrame->ReturnsPending = 1;
//
// Prepare NDIS packets for indicating up.
//
do
{
pRndisPacket = RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(pMessage);
//
// Some sanity checks. TBD - do better checks!
//
if ((pMessage->MessageLength > LengthRemaining) ||
(pMessage->NdisMessageType != REMOTE_NDIS_PACKET_MSG) ||
(pMessage->MessageLength < RNDIS_MESSAGE_SIZE(RNDIS_PACKET)))
{
TRACE1(("ReceivePacketMessage: Msg %x: length %d or type %x has a problem\n",
pMessage, pMessage->MessageLength, pMessage->NdisMessageType));
ASSERT(FALSE);
RNDISMP_INCR_STAT(pAdapter, RecvError);
break;
}
if (pRndisPacket->DataLength > pMessage->MessageLength)
{
TRACE1(("ReceivePacketMessage: invalid data length (%d) > Msg length (%d)\n",
pRndisPacket->DataLength, pMessage->MessageLength));
RNDISMP_INCR_STAT(pAdapter, RecvError);
break;
}
if (pRndisPacket->VcHandle != 0)
{
pVc = LookupVcId(pAdapter, pRndisPacket->VcHandle);
if (pVc == NULL)
{
TRACE1(("ReceivePacketMessage: invalid Vc handle %x\n", pRndisPacket->VcHandle));
RNDISMP_INCR_STAT(pAdapter, RecvError);
break;
}
}
//
// Allocate an NDIS packet to do the indication with.
//
NdisAllocatePacket(&Status, &pNdisPacket, pAdapter->ReceivePacketPool);
if (Status != NDIS_STATUS_SUCCESS)
{
pNdisPacket = NULL;
TRACE2(("ReceivePacketMessage: failed to allocate packet, Adapter %X\n",
pAdapter));
RNDISMP_INCR_STAT(pAdapter, RecvNoBuf);
break;
}
NDIS_SET_PACKET_STATUS(pNdisPacket, ReceiveStatus);
switch (pAdapter->Medium)
{
case NdisMedium802_3:
NDIS_SET_PACKET_HEADER_SIZE(pNdisPacket, ETHERNET_HEADER_SIZE);
break;
default:
break;
}
NdisAllocateBuffer(&BufferStatus,
&pNdisBuffer,
pAdapter->ReceiveBufferPool,
GET_PTR_TO_RNDIS_DATA_BUFF(pRndisPacket),
pRndisPacket->DataLength);
if (BufferStatus != NDIS_STATUS_SUCCESS)
{
TRACE1(("ReceivePacketMessage: failed to allocate"
" buffer, Adapter %X\n", pAdapter));
NdisFreePacket(pNdisPacket);
RNDISMP_INCR_STAT(pAdapter, RecvNoBuf);
break;
}
TRACE2(("Rcv: msg Pkt %d bytes\n", pRndisPacket->DataLength));
TRACEDUMP(("Rcv %d bytes\n", pRndisPacket->DataLength),
GET_PTR_TO_RNDIS_DATA_BUFF(pRndisPacket),
MIN(pRndisPacket->DataLength, 32));
NdisChainBufferAtFront(pNdisPacket, pNdisBuffer);
//
// Check if there is per-packet info.
//
if (!pAdapter->bRunningOnWin9x)
{
PRNDIS_PER_PACKET_INFO pPerPacketInfo;
ULONG PerPacketInfoLength;
if (PerPacketInfoLength = pRndisPacket->PerPacketInfoLength)
{
TRACE1(("ReceivePacketMessage: Adapter %p, Pkt %p:"
" non-zero perpacket length %d\n",
pAdapter, pRndisPacket, PerPacketInfoLength));
pPerPacketInfo = (PRNDIS_PER_PACKET_INFO)((PUCHAR)pRndisPacket +
pRndisPacket->PerPacketInfoOffset);
while (PerPacketInfoLength != 0)
{
switch (pPerPacketInfo->Type)
{
case TcpIpChecksumPacketInfo:
NDIS_PER_PACKET_INFO_FROM_PACKET(pNdisPacket, TcpIpChecksumPacketInfo) =
UlongToPtr(*(PULONG)((PUCHAR)pPerPacketInfo + pPerPacketInfo->PerPacketInformationOffset));
break;
case Ieee8021pPriority:
NDIS_PER_PACKET_INFO_FROM_PACKET(pNdisPacket, Ieee8021pPriority) =
UlongToPtr(*(PULONG)((PUCHAR)pPerPacketInfo + pPerPacketInfo->PerPacketInformationOffset));
default:
break;
}
PerPacketInfoLength -= pPerPacketInfo->Size;
pPerPacketInfo = (PRNDIS_PER_PACKET_INFO)((PUCHAR)pPerPacketInfo +
pPerPacketInfo->Size);
}
}
}
//
// Add this to the array of packets to be indicated up.
//
PacketArray[NumberOfPackets] = pNdisPacket;
NumberOfPackets++;
RNDISMP_INCR_STAT(pAdapter, RecvOk);
pRcvResvd = PRNDISMP_RESERVED_FROM_RECV_PACKET(pNdisPacket);
pRcvResvd->pRcvFrame = pRcvFrame;
pRcvResvd->pVc = pVc;
TRACE2(("ReceivePacketMessage: pRndisPkt %X, MsgLen %d,"
" DataLen %d, Stat %x, Discard %d\n",
pRndisPacket, pMessage->MessageLength,
pRndisPacket->DataLength, ReceiveStatus, bDiscardPkt));
LengthRemaining -= pMessage->MessageLength;
pMessage = (PRNDIS_MESSAGE)((ULONG_PTR)pMessage + pMessage->MessageLength);
NdisInterlockedIncrement(&pRcvFrame->ReturnsPending);
if ((NumberOfPackets == MAX_RECV_PACKETS_IN_MSG) ||
(LengthRemaining < RNDIS_MESSAGE_SIZE(RNDIS_PACKET)))
{
if (pVc == NULL)
{
RcvIndicateCount += NumberOfPackets;
NdisMIndicateReceivePacket(pAdapter->MiniportAdapterHandle,
PacketArray,
NumberOfPackets);
}
else
{
IndicateReceiveDataOnVc(pVc, PacketArray, NumberOfPackets);
}
if (ReceiveStatus == NDIS_STATUS_RESOURCES)
{
for (i = 0; i < NumberOfPackets; i++)
{
RNDISMP_INCR_STAT(pAdapter, RecvLowRes);
RndismpReturnPacket(pAdapter,
PacketArray[i]);
}
}
NumberOfPackets = 0;
}
}
while (LengthRemaining >= RNDIS_MESSAGE_SIZE(RNDIS_PACKET));
if (NumberOfPackets != 0)
{
//
// We bailed out of the above loop. Return what we
// have collected so far.
//
for (i = 0; i < NumberOfPackets; i++)
{
RndismpReturnPacket(pAdapter,
PacketArray[i]);
}
}
//
// Remove temp ref we added at the top.
//
DereferenceRcvFrame(pRcvFrame, pAdapter);
}
while (FALSE);
if (pVc != NULL)
{
ULONG RefCount;
RNDISMP_DEREF_VC(pVc, &RefCount);
}
return (bDiscardPkt);
}
/****************************************************************************/
/* ReceivePacketMessageRaw */
/****************************************************************************/
/* */
/* Routine Description: */
/* */
/* Got a packet message, so send it to the upper layers */
/* */
/* Arguments: */
/* */
/* pAdapter - pointer to our Adapter structure */
/* pMessage - pointer to RNDIS message */
/* pMdl - pointer to MDL received from microport */
/* TotalLength - length of complete message */
/* MicroportMessageContext - context for message from micorport */
/* ReceiveStatus - used by microport to indicate it is low on resource */
/* bMessageCopied - is this a copy of the original message? */
/* */
/* Return: */
/* */
/* BOOLEAN - should the message be returned to the microport? */
/* */
/****************************************************************************/
BOOLEAN
ReceivePacketMessageRaw(IN PRNDISMP_ADAPTER pAdapter,
IN PRNDIS_MESSAGE pMessage,
IN PMDL pMdl,
IN ULONG TotalLength,
IN NDIS_HANDLE MicroportMessageContext,
IN NDIS_STATUS ReceiveStatus,
IN BOOLEAN bMessageCopied)
{
ULONG LengthRemaining; // in entire message
PMDL pTmpMdl;
PRNDISMP_RECV_DATA_FRAME pRcvFrame;
ULONG NumberOfPackets;
PRNDIS_PACKET pRndisPacket;
ULONG i;
#define MAX_RECV_PACKETS_IN_MSG 40
PNDIS_PACKET PacketArray[MAX_RECV_PACKETS_IN_MSG];
ULONG NumPackets;
PNDIS_PACKET pNdisPacket;
PRNDISMP_RECV_PKT_RESERVED pRcvResvd;
PNDIS_BUFFER pNdisBuffer;
NDIS_STATUS BufferStatus;
NDIS_STATUS Status;
BOOLEAN bDiscardPkt;
PRNDISMP_VC pVc;
bDiscardPkt = FALSE;
pVc = NULL;
pRcvFrame = NULL;
do
{
#ifndef BUILD_WIN9X
if (bMessageCopied)
{
ReceiveStatus = NDIS_STATUS_SUCCESS;
}
#else
//
// Rur ReturnPacket handler never gets called on
// Win98 Gold, so we force the status to be able
// to reclaim the indicated packet immediately.
//
ReceiveStatus = NDIS_STATUS_RESOURCES;
#endif
//
// Allocate a receive frame to keep track of this RNDIS packet message.
//
pRcvFrame = AllocateReceiveFrame(pAdapter);
if (pRcvFrame == NULL)
{
bDiscardPkt = TRUE;
break;
}
pRcvFrame->MicroportMessageContext = MicroportMessageContext;
if (bMessageCopied)
{
pRcvFrame->pLocalMessageCopy = pMessage;
pRcvFrame->bMessageCopy = TRUE;
}
else
{
pRcvFrame->pMicroportMdl = pMdl;
pRcvFrame->bMessageCopy = FALSE;
}
NumberOfPackets = 0;
LengthRemaining = TotalLength;
//
// Temp ref to take care of multiple indications.
//
pRcvFrame->ReturnsPending = 1;
//
// Prepare NDIS packets for indicating up.
//
{
pRndisPacket = RNDIS_MESSAGE_RAW_PTR_TO_MESSAGE_PTR(pMessage);
//
// Allocate an NDIS packet to do the indication with.
//
NdisAllocatePacket(&Status, &pNdisPacket, pAdapter->ReceivePacketPool);
if (Status != NDIS_STATUS_SUCCESS)
{
pNdisPacket = NULL;
TRACE2(("ReceivePacketMessage: failed to allocate packet, Adapter %X\n",
pAdapter));
RNDISMP_INCR_STAT(pAdapter, RecvNoBuf);
bDiscardPkt = TRUE;
break;
}
NDIS_SET_PACKET_STATUS(pNdisPacket, ReceiveStatus);
switch (pAdapter->Medium)
{
case NdisMedium802_3:
NDIS_SET_PACKET_HEADER_SIZE(pNdisPacket, ETHERNET_HEADER_SIZE);
break;
default:
break;
}
NdisAllocateBuffer(&BufferStatus,
&pNdisBuffer,
pAdapter->ReceiveBufferPool,
pRndisPacket,
TotalLength);
if (BufferStatus != NDIS_STATUS_SUCCESS)
{
TRACE1(("ReceivePacketMessage: failed to allocate"
" buffer, Adapter %X\n", pAdapter));
NdisFreePacket(pNdisPacket);
RNDISMP_INCR_STAT(pAdapter, RecvNoBuf);
bDiscardPkt = TRUE;
break;
}
TRACE2(("Rcv: msg Pkt %d bytes\n", pMessage->MessageLength));
TRACEDUMP(("Rcv %d bytes\n", pMessage->MessageLength),
pRndisPacket,
MIN(pMessage->MessageLength, 32));
NdisChainBufferAtFront(pNdisPacket, pNdisBuffer);
//
// Add this to the array of packets to be indicated up.
//
PacketArray[NumberOfPackets] = pNdisPacket;
NumberOfPackets++;
RNDISMP_INCR_STAT(pAdapter, RecvOk);
pRcvResvd = PRNDISMP_RESERVED_FROM_RECV_PACKET(pNdisPacket);
pRcvResvd->pRcvFrame = pRcvFrame;
pRcvResvd->pVc = pVc;
TRACE1(("ReceivePacketMessageRaw: pRcvFrame %p/%d, pRndisPkt %p,"
" DataLen %d, Stat %x, Discard %d\n",
pRcvFrame, pRcvFrame->ReturnsPending,
pRndisPacket,
pRndisPacket->DataLength,
ReceiveStatus,
bDiscardPkt));
LengthRemaining -= pMessage->MessageLength;
NdisInterlockedIncrement(&pRcvFrame->ReturnsPending);
NdisMIndicateReceivePacket(pAdapter->MiniportAdapterHandle,
PacketArray,
NumberOfPackets);
if (ReceiveStatus == NDIS_STATUS_RESOURCES)
{
for (i = 0; i < NumberOfPackets; i++)
{
RNDISMP_INCR_STAT(pAdapter, RecvLowRes);
RndismpReturnPacket(pAdapter,
PacketArray[i]);
}
}
}
//
// Remove temp ref we added at the top.
//
DereferenceRcvFrame(pRcvFrame, pAdapter);
}
while (FALSE);
if (bDiscardPkt)
{
//
// Some failure occured above.
//
if (pRcvFrame != NULL)
{
FreeReceiveFrame(pRcvFrame, pAdapter);
}
}
return (bDiscardPkt);
}
/****************************************************************************/
/* IndicateStatusMessage */
/****************************************************************************/
/* */
/* Routine Description: */
/* */
/* Got an indicate status message, so send to upper layers */
/* */
/* Arguments: */
/* */
/* pAdapter - Pointer to our Adapter structure */
/* pMessage - pointer to RNDIS message */
/* pMdl - Pointer to MDL from microport */
/* TotalLength - length of complete message */
/* MicroportMessageContext - context for message from microport */
/* ReceiveStatus - used by microport to indicate it is low on resource */
/* bMessageCopied - is this a copy of the original message? */
/* */
/* Return: */
/* */
/* BOOLEAN - should the message be returned to the microport? */
/* */
/****************************************************************************/
BOOLEAN
IndicateStatusMessage(IN PRNDISMP_ADAPTER pAdapter,
IN PRNDIS_MESSAGE pMessage,
IN PMDL pMdl,
IN ULONG TotalLength,
IN NDIS_HANDLE MicroportMessageContext,
IN NDIS_STATUS ReceiveStatus,
IN BOOLEAN bMessageCopied)
{
PRNDIS_INDICATE_STATUS pRndisIndicateStatus;
TRACE3(("IndicateStatusMessage: Adapter %x, Mdl %x\n", pAdapter, pMdl));
// get a pointer to the indicate status message
pRndisIndicateStatus = RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(pMessage);
if (!pAdapter->Initing)
{
#if DBG
if (pRndisIndicateStatus->Status == NDIS_STATUS_MEDIA_CONNECT)
{
TRACE1(("Adapter %x: +++ Media Connect +++\n", pAdapter));
}
else if (pRndisIndicateStatus->Status == NDIS_STATUS_MEDIA_DISCONNECT)
{
TRACE1(("Adapter %x: --- Media Disconnect ---\n", pAdapter));
}
#endif // DBG
// send status indication to upper layers
NdisMIndicateStatus(pAdapter->MiniportAdapterHandle,
(NDIS_STATUS) pRndisIndicateStatus->Status,
MESSAGE_TO_STATUS_BUFFER(pRndisIndicateStatus),
pRndisIndicateStatus->StatusBufferLength);
// always have to indicate status complete
NdisMIndicateStatusComplete(pAdapter->MiniportAdapterHandle);
}
else
{
//
// drop status indications that arrive when we are
// in the process of initializing.
//
TRACE1(("Adapter %x: indicated status %x when still initializing\n",
pAdapter, (NDIS_STATUS) pRndisIndicateStatus->Status));
}
return (TRUE);
} // IndicateStatusMessage
/****************************************************************************/
/* UnknownMessage */
/****************************************************************************/
/* */
/* Routine Description: */
/* */
/* Process a message with unknown message type. We simply drop it for now. */
/* */
/* Arguments: */
/* */
/* pAdapter - Pointer to our Adapter structure */
/* pMessage - pointer to RNDIS message */
/* pMdl - Pointer to MDL from microport */
/* TotalLength - length of complete message */
/* MicroportMessageContext - context for message from microport */
/* ReceiveStatus - used by microport to indicate it is low on resource */
/* bMessageCopied - is this a copy of the original message? */
/* */
/* Return: */
/* */
/* BOOLEAN - should the message be returned to the microport? */
/* */
/****************************************************************************/
BOOLEAN
UnknownMessage(IN PRNDISMP_ADAPTER pAdapter,
IN PRNDIS_MESSAGE pMessage,
IN PMDL pMdl,
IN ULONG TotalLength,
IN NDIS_HANDLE MicroportMessageContext,
IN NDIS_STATUS ReceiveStatus,
IN BOOLEAN bMessageCopied)
{
TRACE1(("Unknown Message on Adapter %x, type %x, MDL %x, uPContext %x\n",
pAdapter, pMessage->NdisMessageType, pMdl, MicroportMessageContext));
ASSERT(FALSE);
return TRUE;
}
/****************************************************************************/
/* AllocateReceiveFrame */
/****************************************************************************/
/* */
/* Routine Description: */
/* */
/* Allocate a receive frame to keep context about a single RNDIS_PACKET */
/* message. */
/* */
/* Arguments: */
/* */
/* pAdapter - Pointer to our Adapter structure */
/* */
/* Return: */
/* */
/* PRNDISMP_RECV_DATA_FRAME */
/* */
/****************************************************************************/
PRNDISMP_RECV_DATA_FRAME
AllocateReceiveFrame(IN PRNDISMP_ADAPTER pAdapter)
{
PRNDISMP_RECV_DATA_FRAME pRcvFrame;
#ifndef DONT_USE_LOOKASIDE_LIST
pRcvFrame = (PRNDISMP_RECV_DATA_FRAME)
NdisAllocateFromNPagedLookasideList(&pAdapter->RcvFramePool);
#else
{
NDIS_STATUS Status;
Status = MemAlloc(&pRcvFrame, sizeof(RNDISMP_RECV_DATA_FRAME));
if (Status == NDIS_STATUS_SUCCESS)
{
NdisInterlockedIncrement(&RcvFrameAllocs);
}
else
{
pRcvFrame = NULL;
}
}
#endif // DONT_USE_LOOKASIDE_LIST
return (pRcvFrame);
}
/****************************************************************************/
/* FreeReceiveFrame */
/****************************************************************************/
/* */
/* Routine Description: */
/* */
/* Allocate a receive frame to keep context about a single RNDIS_PACKET */
/* message. */
/* */
/* Arguments: */
/* */
/* pRcvFrame - Pointer to receive frame being freed */
/* pAdapter - Pointer to our Adapter structure */
/* */
/* Return: */
/* */
/* VOID */
/* */
/****************************************************************************/
VOID
FreeReceiveFrame(IN PRNDISMP_RECV_DATA_FRAME pRcvFrame,
IN PRNDISMP_ADAPTER pAdapter)
{
#ifndef DONT_USE_LOOKASIDE_LIST
NdisFreeToNPagedLookasideList(&pAdapter->RcvFramePool, pRcvFrame);
#else
MemFree(pRcvFrame, sizeof(RNDISMP_RECV_DATA_FRAME));
NdisInterlockedDecrement(&RcvFrameAllocs);
#endif // DONT_USE_LOOKASIDE_LIST
}
/****************************************************************************/
/* IndicateTimeout */
/****************************************************************************/
/* */
/* Routine Description: */
/* */
/* Timeout callback routine to handle all receive indications. The actual */
/* NDIS routines to indicate receives is done from here, since this */
/* function runs in the right environment for protocols on WinME. */
/* */
/* Arguments: */
/* */
/* SystemSpecific[1-3] - Ignored */
/* Context - Pointer to our Adapter structure */
/* */
/* Return: */
/* */
/* VOID */
/* */
/****************************************************************************/
VOID
IndicateTimeout(IN PVOID SystemSpecific1,
IN PVOID Context,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3)
{
PRNDISMP_ADAPTER pAdapter;
PLIST_ENTRY pEntry;
PRNDISMP_RECV_MSG_CONTEXT pRcvMsg;
PRNDISMP_MSG_HANDLER_FUNC pMsgHandlerFunc;
PRNDIS_MESSAGE pMessage;
BOOLEAN bMessageCopied;
BOOLEAN bReturnToMicroport;
ULONG CurMsgs;
pAdapter = (PRNDISMP_ADAPTER)Context;
CHECK_VALID_ADAPTER(pAdapter);
ASSERT(pAdapter->IndicatingReceives == TRUE);
CurMsgs = 0;
RcvTimerCount++;
RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter);
while (!IsListEmpty(&pAdapter->PendingRcvMessageList))
{
pEntry = RemoveHeadList(&pAdapter->PendingRcvMessageList);
RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter);
CurMsgs++;
pRcvMsg = CONTAINING_RECORD(pEntry, RNDISMP_RECV_MSG_CONTEXT, Link);
RNDISMP_GET_MSG_HANDLER(pMsgHandlerFunc, pRcvMsg->pMessage->NdisMessageType);
bMessageCopied = pRcvMsg->bMessageCopied;
pMessage = pRcvMsg->pMessage;
bReturnToMicroport = (*pMsgHandlerFunc)(
pAdapter,
pMessage,
pRcvMsg->pMdl,
pRcvMsg->TotalLength,
pRcvMsg->MicroportMessageContext,
pRcvMsg->ReceiveStatus,
bMessageCopied);
//
// Are we done with the message?
//
if (bReturnToMicroport)
{
if (!bMessageCopied)
{
RNDISMP_RETURN_TO_MICROPORT(pAdapter,
pRcvMsg->pMdl,
pRcvMsg->MicroportMessageContext);
}
else
{
FreeRcvMessageCopy(pMessage);
}
}
MemFree(pRcvMsg, sizeof(RNDISMP_RECV_MSG_CONTEXT));
RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter);
}
pAdapter->IndicatingReceives = FALSE;
RcvMaxPackets = MAX(RcvMaxPackets, CurMsgs);
RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter);
} // IndicateTimeout