/*************************************************************************** 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