/*************************************************************************** Copyright (c) 1999 Microsoft Corporation Module Name: SEND.C Abstract: Multiple packet send routines for Remote NDIS Miniport driver 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/13/99 : created Author: Tom Green ****************************************************************************/ #include "precomp.h" ULONG MdlsAllocated = 0; ULONG PktWrapperAllocated = 0; ULONG SndPacketCount = 0; ULONG SndTimerCount = 0; ULONG SndMaxPackets = 0; BOOLEAN FirstDbg = FALSE; BOOLEAN PrintPkts = FALSE; /****************************************************************************/ /* RndismpMultipleSend */ /****************************************************************************/ /* */ /* Routine Description: */ /* */ /* NDIS Entry point to send an array of NDIS packets on the specified */ /* adapter. */ /* */ /* Arguments: */ /* */ /* MiniportAdapterContext - a context version of our Adapter pointer */ /* PacketArray - An array of pointers to NDIS packets */ /* NumberOfPackets - Number of packets in array */ /* */ /* Return: */ /* */ /* VOID */ /* */ /****************************************************************************/ VOID RndismpMultipleSend(IN NDIS_HANDLE MiniportAdapterContext, IN PPNDIS_PACKET PacketArray, IN UINT NumberOfPackets) { PRNDISMP_ADAPTER pAdapter; PNDIS_PACKET pNdisPacket; PRNDISMP_SEND_PKT_RESERVED_TEMP pSendRsvdTemp; ULONG i; // get adapter context pAdapter = PRNDISMP_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext); CHECK_VALID_ADAPTER(pAdapter); TRACE2(("RndismpMultipleSend\n")); if (pAdapter->bRunningOnWin9x) { RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter); for (i = 0; i < NumberOfPackets; i++) { pNdisPacket = PacketArray[i]; pSendRsvdTemp = PRNDISMP_RESERVED_TEMP_FROM_SEND_PACKET(pNdisPacket); InsertTailList(&pAdapter->PendingSendProcessList, &pSendRsvdTemp->Link); } if (!pAdapter->SendProcessInProgress) { pAdapter->SendProcessInProgress = TRUE; NdisSetTimer(&pAdapter->SendProcessTimer, 0); } RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter); } else { // // Running on NT. // ASSERT(pAdapter->MultipleSendFunc != NULL); pAdapter->MultipleSendFunc(pAdapter, NULL, PacketArray, NumberOfPackets); } } /****************************************************************************/ /* DoMultipleSend */ /****************************************************************************/ /* */ /* Routine Description: */ /* */ /* NDIS Entry point to send an array of NDIS packets on the specified */ /* adapter. Handles both connection-less and connection-oriented data. */ /* */ /* Arguments: */ /* */ /* pAdapter - pointer to Adapter structure */ /* pVc - pointer to VC structure (NULL if CL send) */ /* PacketArray - An array of pointers to NDIS packets */ /* NumberOfPackets - Number of packets in array */ /* */ /* Return: */ /* */ /* VOID */ /* */ /****************************************************************************/ VOID DoMultipleSend(IN PRNDISMP_ADAPTER pAdapter, IN PRNDISMP_VC pVc OPTIONAL, IN PPNDIS_PACKET PacketArray, IN UINT NumberOfPackets) { UINT PacketCount; PNDIS_PACKET pNdisPacket; PNDIS_PACKET * pPacketArray; PNDIS_PACKET * pPktPointer; PRNDISMP_SEND_PKT_RESERVED pResvd, pPrevResvd; PRNDISMP_MESSAGE_FRAME pMsgFrame; PRNDISMP_PACKET_WRAPPER pPktWrapper; PMDL pMdl; ULONG TotalMessageLength; // of current message ULONG MessagePacketCount; // # of NDIS_PACKETS in this message ULONG CurPacketLength; PULONG pNextPacketOffset; NDIS_STATUS Status; ULONG i; BOOLEAN bMorePackets; pNextPacketOffset = NULL; pMsgFrame = NULL; Status = NDIS_STATUS_SUCCESS; PacketCount = 0; do { if (pAdapter->Halting) { Status = NDIS_STATUS_NOT_ACCEPTED; break; } pPacketArray = &PacketArray[0]; #if DBG if (NumberOfPackets > 1) { if (FirstDbg) { FirstDbg = FALSE; PrintPkts = TRUE; } else { PrintPkts = FALSE; } } #endif for (PacketCount = 0; PacketCount < NumberOfPackets; NOTHING) { pNdisPacket = *pPacketArray; NdisQueryPacket(pNdisPacket, NULL, NULL, NULL, &CurPacketLength); TRACE2(("Send: Pkt %d bytes\n", CurPacketLength)); bMorePackets = (pAdapter->bMultiPacketSupported && (PacketCount < NumberOfPackets - 1)); if (pMsgFrame == NULL) { // // Allocate a frame. // pMsgFrame = AllocateMsgFrame(pAdapter); if (pMsgFrame == NULL) { Status = NDIS_STATUS_RESOURCES; break; } pMsgFrame->NdisMessageType = REMOTE_NDIS_PACKET_MSG; pMsgFrame->pVc = pVc; pMsgFrame->pNdisPacket = NULL; pPktPointer = &pMsgFrame->pNdisPacket; TotalMessageLength = 0; MessagePacketCount = 0; pPrevResvd = NULL; } // // Allocate and fill up the RNDIS message header for this packet. // pPktWrapper = PrepareDataMessage( pNdisPacket, pAdapter, pVc, &TotalMessageLength); if (pPktWrapper != NULL) { pPktWrapper->pMsgFrame = pMsgFrame; pMdl = pPktWrapper->pHeaderMdl; // // Initialize our context in this packet. // pResvd = PRNDISMP_RESERVED_FROM_SEND_PACKET(pNdisPacket); pResvd->pPktWrapper = pPktWrapper; pResvd->pNext = NULL; if (pMsgFrame->pMessageMdl == NULL) { pMsgFrame->pMessageMdl = pMdl; } // // Link this packet to the list. // *pPktPointer = pNdisPacket; MessagePacketCount++; if (pPrevResvd != NULL) { pPrevResvd->pPktWrapper->pTailMdl->Next = pMdl; } } if ((pPktWrapper == NULL) || (!bMorePackets) || (MessagePacketCount == pAdapter->MaxPacketsPerMessage)) { // // Check if we have some data that we can send. // if (MessagePacketCount != 0) { // // Send this off to the microport. // #if DBG if (NumberOfPackets != 1) { TRACE2(("Send: MsgFrame %x, FirstPkt %x, %d/%d pkts\n", pMsgFrame, pMsgFrame->pNdisPacket, MessagePacketCount, NumberOfPackets)); } { PMDL pTmpMdl; PUCHAR pBuf; ULONG Length; for (pTmpMdl = pMsgFrame->pMessageMdl; pTmpMdl != NULL; pTmpMdl = pTmpMdl->Next) { Length = MmGetMdlByteCount(pTmpMdl); pBuf = MmGetSystemAddressForMdl(pTmpMdl); TRACEDUMP(("MDL %x\n", pTmpMdl), pBuf, Length); } } #endif // DBG { ULONG k; for (k = 0; k < MessagePacketCount; k++) { RNDISMP_INCR_STAT(pAdapter, XmitToMicroport); } } RNDISMP_SEND_TO_MICROPORT(pAdapter, pMsgFrame, FALSE, CompleteSendData); MessagePacketCount = 0; pMsgFrame = NULL; if (pPktWrapper != NULL) { PacketCount++; pPacketArray++; } continue; } else { TRACE1(("RndismpMultipleSend: Adapter %x, fail: PktWrp %x, bMore %d, MsgPktCount %d\n", pAdapter, pPktWrapper, bMorePackets, MessagePacketCount)); Status = NDIS_STATUS_RESOURCES; break; } } pPktPointer = &pResvd->pNext; pPrevResvd = pResvd; PacketCount++; pPacketArray++; } if (PacketCount < NumberOfPackets) { break; } Status = NDIS_STATUS_SUCCESS; } while (FALSE); if (Status != NDIS_STATUS_SUCCESS) { TRACE1(("DoMultipleSend: Adapter %x, failure Status %x, PktCount %d, TotalPkts %d\n", pAdapter, Status, PacketCount, NumberOfPackets)); // // Undo all we have done so far. // for (i = PacketCount; i < NumberOfPackets; i++) { RNDISMP_INCR_STAT(pAdapter, XmitError); if (pVc == NULL) { TRACE1(("DoMultipleSend: Adapter %x, failing pkt %x\n", pAdapter, PacketArray[i])); NdisMSendComplete(pAdapter->MiniportAdapterHandle, PacketArray[i], Status); } else { CompleteSendDataOnVc(pVc, PacketArray[i], Status); } } if (pMsgFrame) { pMsgFrame->pMessageMdl = NULL; DereferenceMsgFrame(pMsgFrame); } } return; } /****************************************************************************/ /* DoMultipleSendRaw */ /****************************************************************************/ /* */ /* Routine Description: */ /* */ /* NDIS Entry point to send an array of NDIS packets on the specified */ /* adapter. Unlike DoMultipleSend, this handles raw encapsulation. */ /* */ /* Arguments: */ /* */ /* pAdapter - pointer to Adapter structure */ /* pVc - pointer to VC structure (NULL if CL send) */ /* PacketArray - An array of pointers to NDIS packets */ /* NumberOfPackets - Number of packets in array */ /* */ /* Return: */ /* */ /* VOID */ /* */ /****************************************************************************/ VOID DoMultipleSendRaw(IN PRNDISMP_ADAPTER pAdapter, IN PRNDISMP_VC pVc OPTIONAL, IN PPNDIS_PACKET PacketArray, IN UINT NumberOfPackets) { UINT PacketCount; PNDIS_PACKET pNdisPacket; PNDIS_PACKET * pPacketArray; PNDIS_PACKET * pPktPointer; PRNDISMP_SEND_PKT_RESERVED pResvd, pPrevResvd; PRNDISMP_MESSAGE_FRAME pMsgFrame; PRNDISMP_PACKET_WRAPPER pPktWrapper; PMDL pMdl; ULONG TotalMessageLength; // of current message ULONG MessagePacketCount; // # of NDIS_PACKETS in this message ULONG CurPacketLength; PULONG pNextPacketOffset; NDIS_STATUS Status; ULONG i; BOOLEAN bMorePackets; pNextPacketOffset = NULL; pMsgFrame = NULL; Status = NDIS_STATUS_SUCCESS; PacketCount = 0; if (pAdapter->Halting) { Status = NDIS_STATUS_NOT_ACCEPTED; } else { pPacketArray = &PacketArray[0]; #if DBG if (NumberOfPackets > 1) { if (FirstDbg) { FirstDbg = FALSE; PrintPkts = TRUE; } else { PrintPkts = FALSE; } } #endif for (PacketCount = 0; PacketCount < NumberOfPackets; NOTHING) { pNdisPacket = *pPacketArray; NdisQueryPacket(pNdisPacket, NULL, NULL, NULL, &CurPacketLength); TRACE2(("Send: Pkt %d bytes\n", CurPacketLength)); // // Allocate a frame. // pMsgFrame = AllocateMsgFrame(pAdapter); if (pMsgFrame == NULL) { Status = NDIS_STATUS_RESOURCES; break; } pMsgFrame->NdisMessageType = REMOTE_NDIS_PACKET_MSG; pMsgFrame->pNdisPacket = pNdisPacket; NdisQueryPacket(pNdisPacket,NULL,NULL,&(pMsgFrame->pMessageMdl),NULL); TotalMessageLength = 0; pPktWrapper = PrepareDataMessageRaw( pNdisPacket, pAdapter, &TotalMessageLength); if (pPktWrapper != NULL) { pPktWrapper->pMsgFrame = pMsgFrame; pMdl = pPktWrapper->pHeaderMdl; pResvd = PRNDISMP_RESERVED_FROM_SEND_PACKET(pNdisPacket); pResvd->pPktWrapper = pPktWrapper; pResvd->pNext = NULL; pMsgFrame->pMessageMdl = pMdl; #ifdef DBG TRACE2(("Send: MsgFrame %x, Pkt %x\n",pMsgFrame, pMsgFrame->pNdisPacket)); #endif RNDISMP_INCR_STAT(pAdapter,XmitToMicroport); RNDISMP_SEND_TO_MICROPORT(pAdapter,pMsgFrame,FALSE,CompleteSendData); } PacketCount++; } } if (Status != NDIS_STATUS_SUCCESS) { TRACE1(("DoMultipleSendRaw: Adapter %x, failure Status %x, PktCount %d, TotalPkts %d\n", pAdapter, Status, PacketCount, NumberOfPackets)); // // Undo all we have done so far. // for (i = PacketCount; i < NumberOfPackets; i++) { RNDISMP_INCR_STAT(pAdapter, XmitError); TRACE1(("DoMultipleSendRaw: Adapter %x, failing pkt %x\n", pAdapter, PacketArray[i])); NdisMSendComplete(pAdapter->MiniportAdapterHandle, PacketArray[i], Status); } if (pMsgFrame) { pMsgFrame->pMessageMdl = NULL; DereferenceMsgFrame(pMsgFrame); } } return; } /****************************************************************************/ /* PrepareDataMessage */ /****************************************************************************/ /* */ /* Routine Description: */ /* */ /* Utility routine to prepare a complete or part of a data message. */ /* */ /* Arguments: */ /* */ /* pNdisPacket - the NDIS packet to be converted */ /* pAdapter - Adapter on which the packet is being sent */ /* pVc - VC on which the packet is sent (NULL if no VC context) */ /* pTotalMessageLength - On input, contains the total message length */ /* filled in so far. Updated on output. */ /* */ /* */ /* Return: */ /* */ /* PRNDISMP_PACKET_WRAPPER */ /* */ /****************************************************************************/ PRNDISMP_PACKET_WRAPPER PrepareDataMessage(IN PNDIS_PACKET pNdisPacket, IN PRNDISMP_ADAPTER pAdapter, IN PRNDISMP_VC pVc OPTIONAL, IN OUT PULONG pTotalMessageLength) { PMDL pMdl, pNextMdl; PMDL * ppNextMdl; PRNDISMP_PACKET_WRAPPER pPktWrapper; RNDIS_MESSAGE UNALIGNED * pRndisMessage; RNDIS_PACKET UNALIGNED * pPacketMsg; PNDIS_BUFFER pNdisBuffer; PNDIS_BUFFER pNextNdisBuffer; ULONG TotalMessageLength; ULONG PacketMsgLength; ULONG OobDataLength; ULONG PerPacketInfoLength; ULONG AlignedLength; ULONG AlignmentOffset; ULONG TotalDataLength; ULONG TcpipChecksum, TcpLargeSend, PacketPriority; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; pPktWrapper = NULL; pMdl = NULL; do { TotalMessageLength = 0; RNDISMP_GET_ALIGNED_LENGTH(AlignedLength, *pTotalMessageLength, pAdapter); AlignmentOffset = (AlignedLength - *pTotalMessageLength); // // Compute attachments. Zero for now. // TBD -- do the real thing. // OobDataLength = 0; PerPacketInfoLength = 0; // // Look for per-packet info elements, only on Win2K/Whistler. // if (!pAdapter->bRunningOnWin9x) { // // TCP/IP checksum offload? // TcpipChecksum = PtrToUlong(NDIS_PER_PACKET_INFO_FROM_PACKET(pNdisPacket, TcpIpChecksumPacketInfo)); if (TcpipChecksum != 0) { PerPacketInfoLength += sizeof(RNDIS_PER_PACKET_INFO) + sizeof(ULONG); TRACE1(("Send: Pkt %p has TCP checksum %x\n", pNdisPacket, TcpipChecksum)); } // // TCP large send offload? // TcpLargeSend = PtrToUlong(NDIS_PER_PACKET_INFO_FROM_PACKET(pNdisPacket, TcpLargeSendPacketInfo)); if (TcpLargeSend != 0) { PerPacketInfoLength += sizeof(RNDIS_PER_PACKET_INFO) + sizeof(ULONG); TRACE1(("Send: Pkt %p has TCP large send %x\n", pNdisPacket, TcpLargeSend)); } // // Packet priority? // PacketPriority = PtrToUlong(NDIS_PER_PACKET_INFO_FROM_PACKET(pNdisPacket, Ieee8021pPriority)); if (PacketPriority != 0) { PerPacketInfoLength += sizeof(RNDIS_PER_PACKET_INFO) + sizeof(ULONG); TRACE1(("Send: Pkt %p has priority %x\n", pNdisPacket, PacketPriority)); } } PacketMsgLength = sizeof(*pPacketMsg) + OobDataLength + PerPacketInfoLength + AlignmentOffset; // // Need space for common RNDIS message header. // PacketMsgLength += (sizeof(RNDIS_MESSAGE) - sizeof(RNDIS_MESSAGE_CONTAINER)); NdisQueryPacket(pNdisPacket, NULL, NULL, NULL, &TotalDataLength); // // We know the max transfer size of any message that we are allowed // to send to the device. Is this going beyond that limit? // if (*pTotalMessageLength + PacketMsgLength + TotalDataLength > pAdapter->MaxTransferSize) { TRACE2(("PrepareDataMessage: Adapter %x, pkt %x, length %d > device limit (%d)\n", pAdapter, pNdisPacket, *pTotalMessageLength + PacketMsgLength + TotalDataLength, pAdapter->MaxTransferSize)); break; } // // Allocate an RNDIS_PACKET buffer. // pPktWrapper = AllocatePacketMsgWrapper(pAdapter, PacketMsgLength); if (pPktWrapper == NULL) { TRACE1(("PrepareDataMessage: failed to alloc wrapper, Adapter %x, Length %d\n", pAdapter, PacketMsgLength)); ASSERT(FALSE); Status = NDIS_STATUS_RESOURCES; break; } pPktWrapper->pNdisPacket = pNdisPacket; pPktWrapper->pVc = pVc; pRndisMessage = (PRNDIS_MESSAGE) ((ULONG_PTR)&pPktWrapper->Packet[0] + AlignmentOffset); pPacketMsg = (PRNDIS_PACKET)(&pRndisMessage->Message); pRndisMessage->NdisMessageType = REMOTE_NDIS_PACKET_MSG; if (pVc == NULL) { pPacketMsg->VcHandle = 0; } else { pPacketMsg->VcHandle = pVc->DeviceVcContext; } #if DBG if (PrintPkts) { TRACE1((" Offs %d/x%x AlignOff %d/x%x, DataLen %d/x%x\n", *pTotalMessageLength, *pTotalMessageLength, AlignmentOffset, AlignmentOffset, TotalDataLength, TotalDataLength)); } #endif // DBG // // Allocate MDLs for the RNDIS_PACKET header and for each // component NDIS buffer in the packet. // pMdl = IoAllocateMdl( &pPktWrapper->Packet[0], PacketMsgLength, FALSE, FALSE, NULL); if (pMdl == NULL) { TRACE1(("PrepareDataMsg: Adapter %x failed to alloc MDL for header\n", pAdapter)); Status = NDIS_STATUS_RESOURCES; TRACE1(("PrepareDataMsg: outstanding MDL count %d, at %x\n", MdlsAllocated, &MdlsAllocated)); ASSERT(FALSE); break; } NdisInterlockedIncrement(&MdlsAllocated); MmBuildMdlForNonPagedPool(pMdl); pMdl->Next = NULL; pPktWrapper->pHeaderMdl = pMdl; ppNextMdl = &pMdl->Next; TRACE2(("PrepareDataMsg: NdisPkt %x, PacketMsgLen %d, TotalDatalen %d, Mdl %x, pRndisMessage %x\n", pNdisPacket, PacketMsgLength, TotalDataLength, pMdl, pRndisMessage)); TotalDataLength = 0; for (pNdisBuffer = pNdisPacket->Private.Head; pNdisBuffer != NULL; pNdisBuffer = pNextNdisBuffer) { PVOID VirtualAddress; UINT BufferLength; NdisGetNextBuffer(pNdisBuffer, &pNextNdisBuffer); #ifndef BUILD_WIN9X NdisQueryBufferSafe(pNdisBuffer, &VirtualAddress, &BufferLength, NormalPagePriority); if ((BufferLength != 0) && (VirtualAddress == NULL)) { TRACE1(("PrepareDataMsg: Adapter %x failed to query buffer %p, Pkt %p\n", pAdapter, pNdisBuffer, pNdisPacket)); Status = NDIS_STATUS_RESOURCES; break; } #else NdisQueryBuffer(pNdisBuffer, &VirtualAddress, &BufferLength); #endif // BUILD_WIN9X // // Skip any 0-length buffers given to us by IP or NDISTEST // if (BufferLength != 0) { TotalDataLength += BufferLength; pMdl = IoAllocateMdl( VirtualAddress, BufferLength, FALSE, FALSE, NULL); if (pMdl == NULL) { TRACE1(("PrepareDataMsg: Adapter %x failed to alloc MDL\n", pAdapter)); Status = NDIS_STATUS_RESOURCES; TRACE1(("PrepareDataMsg: outstanding MDL count %d, at %x\n", MdlsAllocated, &MdlsAllocated)); ASSERT(FALSE); break; } NdisInterlockedIncrement(&MdlsAllocated); MmBuildMdlForNonPagedPool(pMdl); *ppNextMdl = pMdl; ppNextMdl = &pMdl->Next; pMdl->Next = NULL; } } if (pNdisBuffer != NULL) { // // We bailed out before reaching the end of the list. // break; } *ppNextMdl = NULL; pPktWrapper->pTailMdl = pMdl; TotalMessageLength += (PacketMsgLength + TotalDataLength); pRndisMessage->MessageLength = PacketMsgLength + TotalDataLength; *pTotalMessageLength += TotalMessageLength; // // Fill in the RNDIS_PACKET message completely now. // pPacketMsg->DataOffset = sizeof(RNDIS_PACKET) + OobDataLength + PerPacketInfoLength; pPacketMsg->DataLength = TotalDataLength; if (PerPacketInfoLength) { PRNDIS_PER_PACKET_INFO pPerPacketInfo; pPacketMsg->PerPacketInfoOffset = sizeof(RNDIS_PACKET); pPacketMsg->PerPacketInfoLength = PerPacketInfoLength; pPerPacketInfo = (PRNDIS_PER_PACKET_INFO)((PUCHAR)pPacketMsg + sizeof(RNDIS_PACKET)); if (TcpipChecksum) { pPerPacketInfo->Size = sizeof(RNDIS_PER_PACKET_INFO) + sizeof(ULONG); pPerPacketInfo->Type = TcpIpChecksumPacketInfo; pPerPacketInfo->PerPacketInformationOffset = sizeof(RNDIS_PER_PACKET_INFO); *(PULONG)(pPerPacketInfo + 1) = TcpipChecksum; pPerPacketInfo = (PRNDIS_PER_PACKET_INFO)((PUCHAR)pPerPacketInfo + pPerPacketInfo->Size); } if (TcpLargeSend) { pPerPacketInfo->Size = sizeof(RNDIS_PER_PACKET_INFO) + sizeof(ULONG); pPerPacketInfo->Type = TcpLargeSendPacketInfo; pPerPacketInfo->PerPacketInformationOffset = sizeof(RNDIS_PER_PACKET_INFO); *(PULONG)(pPerPacketInfo + 1) = TcpLargeSend; pPerPacketInfo = (PRNDIS_PER_PACKET_INFO)((PUCHAR)pPerPacketInfo + pPerPacketInfo->Size); // // Since we do not have a send-completion message, we fill up // the "ack" for large send right here. // NDIS_PER_PACKET_INFO_FROM_PACKET(pNdisPacket, TcpLargeSendPacketInfo) = UlongToPtr(TotalDataLength); } if (PacketPriority) { pPerPacketInfo->Size = sizeof(RNDIS_PER_PACKET_INFO) + sizeof(ULONG); pPerPacketInfo->Type = Ieee8021pPriority; pPerPacketInfo->PerPacketInformationOffset = sizeof(RNDIS_PER_PACKET_INFO); *(PULONG)(pPerPacketInfo + 1) = PacketPriority; pPerPacketInfo = (PRNDIS_PER_PACKET_INFO)((PUCHAR)pPerPacketInfo + pPerPacketInfo->Size); } } } while (FALSE); if (Status != NDIS_STATUS_SUCCESS) { TRACE1(("PrepareDataMessage: Adapter %x, failed %x\n", pAdapter, Status)); // // Undo all we have done so far. // if (pPktWrapper) { for (pMdl = pPktWrapper->pHeaderMdl; pMdl != NULL; pMdl = pNextMdl) { pNextMdl = pMdl->Next; IoFreeMdl(pMdl); NdisInterlockedDecrement(&MdlsAllocated); } FreePacketMsgWrapper(pPktWrapper); pPktWrapper = NULL; } } TRACE2(("PrepareDataMessage (%08X)\n", pPktWrapper)); return (pPktWrapper); } /****************************************************************************/ /* PrepareDataMessageRaw */ /****************************************************************************/ /* */ /* Routine Description: */ /* */ /* Utility routine to prepare a complete or part of a data message. */ /* */ /* Arguments: */ /* */ /* pNdisPacket - the NDIS packet to be converted */ /* pAdapter - Adapter on which the packet is being sent */ /* pVc - VC on which the packet is sent (NULL if no VC context) */ /* pTotalMessageLength - On input, contains the total message length */ /* filled in so far. Updated on output. */ /* */ /* */ /* Return: */ /* */ /* PRNDISMP_PACKET_WRAPPER */ /* */ /****************************************************************************/ PRNDISMP_PACKET_WRAPPER PrepareDataMessageRaw(IN PNDIS_PACKET pNdisPacket, IN PRNDISMP_ADAPTER pAdapter, IN OUT PULONG pTotalMessageLength) { PMDL pMdl, pNextMdl; PMDL * ppNextMdl; PRNDISMP_PACKET_WRAPPER pPktWrapper; RNDIS_MESSAGE UNALIGNED * pRndisMessage; PNDIS_BUFFER pNdisBuffer; PNDIS_BUFFER pNextNdisBuffer; ULONG TotalMessageLength; ULONG TotalDataLength; ULONG AlignedLength; ULONG AlignmentOffset; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; pPktWrapper = NULL; pMdl = NULL; RNDISMP_GET_ALIGNED_LENGTH(AlignedLength, *pTotalMessageLength, pAdapter); AlignmentOffset = (AlignedLength - *pTotalMessageLength); do { TotalMessageLength = 0; // // Allocate an RNDIS_PACKET buffer. // pPktWrapper = AllocatePacketMsgWrapper(pAdapter, 0); if (pPktWrapper == NULL) { TRACE1(("PrepareDataMessage: failed to alloc wrapper, Adapter %x\n", pAdapter)); ASSERT(FALSE); Status = NDIS_STATUS_RESOURCES; break; } pPktWrapper->pNdisPacket = pNdisPacket; pPktWrapper->pVc = NULL; pPktWrapper->pHeaderMdl = NULL; TotalDataLength = 0; for (pNdisBuffer = pNdisPacket->Private.Head; pNdisBuffer != NULL; pNdisBuffer = pNextNdisBuffer) { PVOID VirtualAddress; UINT BufferLength; NdisGetNextBuffer(pNdisBuffer, &pNextNdisBuffer); #ifndef BUILD_WIN9X NdisQueryBufferSafe(pNdisBuffer, &VirtualAddress, &BufferLength, NormalPagePriority); if ((BufferLength != 0) && (VirtualAddress == NULL)) { TRACE1(("PrepareDataMsg: Adapter %x failed to query buffer %p, Pkt %p\n", pAdapter, pNdisBuffer, pNdisPacket)); Status = NDIS_STATUS_RESOURCES; break; } #else NdisQueryBuffer(pNdisBuffer, &VirtualAddress, &BufferLength); #endif // BUILD_WIN9X // // Skip any 0-length buffers given to us by IP or NDISTEST // if (BufferLength != 0) { TotalDataLength += BufferLength; pMdl = IoAllocateMdl( VirtualAddress, BufferLength, FALSE, FALSE, NULL); if (pMdl == NULL) { TRACE1(("PrepareDataMsg: Adapter %x failed to alloc MDL\n", pAdapter)); Status = NDIS_STATUS_RESOURCES; TRACE1(("PrepareDataMsg: outstanding MDL count %d, at %x\n", MdlsAllocated, &MdlsAllocated)); ASSERT(FALSE); break; } pMdl->Next = NULL; if (pPktWrapper->pHeaderMdl == NULL) { pPktWrapper->pHeaderMdl = pMdl; pPktWrapper->pTailMdl = pMdl; } else { pPktWrapper->pTailMdl->Next = pMdl; pPktWrapper->pTailMdl = pMdl; } NdisInterlockedIncrement(&MdlsAllocated); MmBuildMdlForNonPagedPool(pMdl); } } if (pNdisBuffer != NULL) { // // We bailed out before reaching the end of the list. // break; } *pTotalMessageLength += TotalDataLength; } while (FALSE); if (Status != NDIS_STATUS_SUCCESS) { TRACE1(("PrepareDataMessage: Adapter %x, failed %x\n", pAdapter, Status)); // // Undo all we have done so far. // if (pPktWrapper) { for (pMdl = pPktWrapper->pHeaderMdl; pMdl != NULL; pMdl = pNextMdl) { pNextMdl = pMdl->Next; IoFreeMdl(pMdl); NdisInterlockedDecrement(&MdlsAllocated); } FreePacketMsgWrapper(pPktWrapper); pPktWrapper = NULL; } } TRACE2(("PrepareDataMessage (%08X)\n", pPktWrapper)); return (pPktWrapper); } /****************************************************************************/ /* AllocatePacketMsgWrapper */ /****************************************************************************/ /* */ /* Routine Description: */ /* */ /* Allocate a structure to keep information about one NDIS packet sent */ /* through the microport. */ /* */ /* Arguments: */ /* */ /* pAdapter - Adapter on which this packet is going to be sent. */ /* MsgHeaderLength - Total length of the wrapper structure */ /* */ /* Return: */ /* */ /* PRNDISMP_PACKET_WRAPPER */ /* */ /****************************************************************************/ PRNDISMP_PACKET_WRAPPER AllocatePacketMsgWrapper(IN PRNDISMP_ADAPTER pAdapter, IN ULONG MsgHeaderLength) { PRNDISMP_PACKET_WRAPPER pPktWrapper; NDIS_STATUS Status; ULONG TotalLength; TotalLength = sizeof(RNDISMP_PACKET_WRAPPER) + MsgHeaderLength; Status = MemAlloc(&pPktWrapper, TotalLength); if (Status == NDIS_STATUS_SUCCESS) { NdisZeroMemory(pPktWrapper, TotalLength); NdisInterlockedIncrement(&PktWrapperAllocated); } else { TRACE1(("AllocPacketMsgWrapper failed, adapter %x, alloc count %d at %x\n", pAdapter, PktWrapperAllocated, &PktWrapperAllocated)); ASSERT(FALSE); pPktWrapper = NULL; } return (pPktWrapper); } /****************************************************************************/ /* FreePacketMsgWrapper */ /****************************************************************************/ /* */ /* Routine Description: */ /* */ /* Free a structure used to keep information about one NDIS packet sent */ /* through the microport. */ /* */ /* Arguments: */ /* */ /* pPktWrapper - Pointer to wrapper structure. */ /* */ /* Return: */ /* */ /* VOID */ /* */ /****************************************************************************/ VOID FreePacketMsgWrapper(IN PRNDISMP_PACKET_WRAPPER pPktWrapper) { MemFree(pPktWrapper, sizeof(RNDISMP_PACKET_WRAPPER)); NdisInterlockedDecrement(&PktWrapperAllocated); } /****************************************************************************/ /* CompleteSendData */ /****************************************************************************/ /* */ /* Routine Description: */ /* */ /* Callback function to handle completion of send data message sent */ /* down to microport */ /* */ /* Arguments: */ /* */ /* pMsgFrame - our frame structure holding information about a send */ /* SendStatus - indicate status of send message */ /* */ /* Return: */ /* */ /* VOID */ /* */ /****************************************************************************/ VOID CompleteSendData(IN PRNDISMP_MESSAGE_FRAME pMsgFrame, IN NDIS_STATUS SendStatus) { PRNDISMP_ADAPTER Adapter; PNDIS_PACKET Packet; PMDL pMdl, pNextMdl; PRNDISMP_PACKET_WRAPPER pPktWrapper, pNextPktWrapper; PRNDISMP_SEND_PKT_RESERVED pResvd; PNDIS_PACKET NextPacket; PRNDISMP_VC pVc; Adapter = pMsgFrame->pAdapter; TRACE2(("CompleteSendData: Adapter %x, MsgFrame %x, SendStatus %x\n", Adapter, pMsgFrame, SendStatus)); #if DBG_TIME_STAMPS { ULONG NowTime; ULONG PendedTime; RNDISMP_GET_TIME_STAMP(&NowTime); PendedTime = NowTime - pMsgFrame->TimeSent; if (PendedTime > Adapter->MaxSendCompleteTime) { TRACE1(("CompleteSendData: Adapter %x: pend time %d millisec\n", Adapter, PendedTime)); Adapter->MaxSendCompleteTime = PendedTime; } } #endif // DBG_TIME_STAMPS // // free all MDLs we had allocated. // for (pMdl = pMsgFrame->pMessageMdl; pMdl != NULL; pMdl = pNextMdl) { pNextMdl = pMdl->Next; IoFreeMdl(pMdl); NdisInterlockedDecrement(&MdlsAllocated); } // // we may have sent several NDIS packets in one message // so we have to walk the list and complete each one // for (Packet = pMsgFrame->pNdisPacket; Packet != NULL; Packet = NextPacket) { pResvd = PRNDISMP_RESERVED_FROM_SEND_PACKET(Packet); // get the next packet linked NextPacket = pResvd->pNext; pPktWrapper = pResvd->pPktWrapper; #if DBG if (NextPacket != NULL) { TRACE2(("CompleteSendData: multi: MsgFrame %x, tpkt %x, wrapper %x\n", pMsgFrame, Packet, // *(PULONG)((PUCHAR)Packet + 0x98), pPktWrapper)); } #endif // DBG pVc = pPktWrapper->pVc; // free the wrapper structure for this packet. FreePacketMsgWrapper(pPktWrapper); // send completion to upper layers TRACE2(("CompleteSendData: Adapter %x, completing pkt %x\n", Adapter, Packet)); if (SendStatus == NDIS_STATUS_SUCCESS) { RNDISMP_INCR_STAT(Adapter, XmitOk); } else { RNDISMP_INCR_STAT(Adapter, XmitError); } if (pVc == NULL) { NdisMSendComplete(Adapter->MiniportAdapterHandle, Packet, SendStatus); } else { CompleteSendDataOnVc(pVc, Packet, SendStatus); } } // free up frame and resources pMsgFrame->pMessageMdl = NULL; DereferenceMsgFrame(pMsgFrame); } // CompleteSendData /****************************************************************************/ /* FreeMsgAfterSend */ /****************************************************************************/ /* */ /* Routine Description: */ /* */ /* Called by microport to indicate completion of send data message sent */ /* down by miniport */ /* */ /* Arguments: */ /* */ /* pMsgFrame - our frame structure holding information about a send */ /* SendStatus - indicate status of send message */ /* */ /* Return: */ /* */ /* VOID */ /* */ /****************************************************************************/ VOID FreeMsgAfterSend(IN PRNDISMP_MESSAGE_FRAME pMsgFrame, IN NDIS_STATUS SendStatus) { DereferenceMsgFrame(pMsgFrame); } #if THROTTLE_MESSAGES /****************************************************************************/ /* QueueMessageToMicroport */ /****************************************************************************/ /* */ /* Routine Description: */ /* */ /* Queue the given message on the list of messages to be send to the */ /* microport, and start sending down these, if we haven't sent too many */ /* already. */ /* */ /* Arguments: */ /* */ /* pAdapter - our Adapter structure */ /* pMsgFrame - our frame structure holding information about a send */ /* bQueueMessageForResponse - add this message to the pending-response */ /* list on the adapter. We expect a response */ /* for this from the device. */ /* */ /* Return: */ /* */ /* VOID */ /* */ /****************************************************************************/ VOID QueueMessageToMicroport(IN PRNDISMP_ADAPTER pAdapter, IN PRNDISMP_MESSAGE_FRAME pMsgFrame, IN BOOLEAN bQueueMessageForResponse) { PLIST_ENTRY pEnt; PRNDISMP_MESSAGE_FRAME pFrame; RM_CHANNEL_TYPE ChannelType; RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter); do { if (pMsgFrame) { // // Add to waiting queue. // InsertTailList(&pAdapter->WaitingMessageList, &pMsgFrame->PendLink); if (bQueueMessageForResponse) { InsertTailList(&pAdapter->PendingFrameList, &pMsgFrame->Link); } } // // Prevent more than one thread from executing below. // if (pAdapter->SendInProgress) { break; } pAdapter->SendInProgress = TRUE; // // Send as many messages to the microport as we can, without exceeding // the high-water mark for messages pending at the microport. // while ((pAdapter->CurPendedMessages < pAdapter->HiWatPendedMessages) && !IsListEmpty(&pAdapter->WaitingMessageList)) { // // Take out the first message in the waiting queue. // pEnt = pAdapter->WaitingMessageList.Flink; pFrame = CONTAINING_RECORD(pEnt, RNDISMP_MESSAGE_FRAME, PendLink); RemoveEntryList(pEnt); CHECK_VALID_FRAME(pFrame); pAdapter->CurPendedMessages++; InsertTailList(&pAdapter->PendingAtMicroportList, pEnt); RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter); RNDISMP_GET_TIME_STAMP(&pFrame->TimeSent); DBG_LOG_SEND_MSG(pAdapter, pFrame); // // Check if we are halting the adapter, fail if so. // NOTE: the only message we let thru is a HALT. // if (pAdapter->Halting && (pFrame->NdisMessageType != REMOTE_NDIS_HALT_MSG)) { TRACE1(("QueueMsg: Adapter %x is halting, dropped msg 0x%x!\n", pAdapter, pFrame->NdisMessageType)); RndisMSendComplete( (NDIS_HANDLE)pAdapter, pFrame, NDIS_STATUS_NOT_ACCEPTED); RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter); continue; } // // Send the message to the microport. The microport will // call RndisMSendComplete when it is done with it. // #if DBG { ULONG Length; PUCHAR pBuf; Length = MmGetMdlByteCount(pFrame->pMessageMdl); pBuf = MmGetSystemAddressForMdl(pFrame->pMessageMdl); TRACEDUMP(("Sending msg type %x (%d bytes):\n", pFrame->NdisMessageType, Length), pBuf, Length); } #endif // // Does this go on the data or control channel of the microport? // if (pFrame->NdisMessageType == REMOTE_NDIS_PACKET_MSG) { ChannelType = RMC_DATA; } else { ChannelType = RMC_CONTROL; } (pAdapter)->RmSendMessageHandler(pAdapter->MicroportAdapterContext, pFrame->pMessageMdl, (NDIS_HANDLE)pFrame, ChannelType); RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter); } pAdapter->SendInProgress = FALSE; } while (FALSE); RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter); } /****************************************************************************/ /* FlushPendingMessages */ /****************************************************************************/ /* */ /* Routine Description: */ /* */ /* Remove and send-complete any messages pending to be sent to the */ /* microport. */ /* */ /* Arguments: */ /* */ /* pAdapter - our Adapter structure */ /* */ /* Return: */ /* */ /* VOID */ /* */ /****************************************************************************/ VOID FlushPendingMessages(IN PRNDISMP_ADAPTER pAdapter) { PLIST_ENTRY pEnt; PRNDISMP_MESSAGE_FRAME pFrame; RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter); // // Prevent further sends to microport. // pAdapter->SendInProgress = TRUE; while (!IsListEmpty(&pAdapter->WaitingMessageList)) { // // Take out the first message in the waiting queue. // pEnt = pAdapter->WaitingMessageList.Flink; pFrame = CONTAINING_RECORD(pEnt, RNDISMP_MESSAGE_FRAME, PendLink); RemoveEntryList(pEnt); CHECK_VALID_FRAME(pFrame); // // Fake send to microport // pAdapter->CurPendedMessages++; InsertTailList(&pAdapter->PendingAtMicroportList, pEnt); RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter); TRACE1(("Flush: Adapter %x, MsgFrame %x, MsgType %x\n", pAdapter, pFrame, pFrame->NdisMessageType)); // // Complete it right here. // RndisMSendComplete( (NDIS_HANDLE)pAdapter, pFrame, NDIS_STATUS_NOT_ACCEPTED); RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter); } pAdapter->SendInProgress = FALSE; RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter); TRACE1(("Flush done, adapter %x\n", pAdapter)); } #endif // THROTTLE_MESSAGES /****************************************************************************/ /* SendProcessTimeout */ /****************************************************************************/ /* */ /* Routine Description: */ /* */ /* Timeout callback routine to handle all sends. This is to avoid issues */ /* with TCP/IP stack preemption on WinME. */ /* */ /* Arguments: */ /* */ /* SystemSpecific[1-3] - Ignored */ /* Context - Pointer to our Adapter structure */ /* */ /* Return: */ /* */ /* VOID */ /* */ /****************************************************************************/ VOID SendProcessTimeout(IN PVOID SystemSpecific1, IN PVOID Context, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3) { PRNDISMP_ADAPTER pAdapter; PNDIS_PACKET pNdisPacket; PRNDISMP_SEND_PKT_RESERVED_TEMP pSendResvdTemp; PLIST_ENTRY pEntry; NDIS_STATUS Status; ULONG NumPkts; ULONG CurPkts; #define MAX_MULTI_SEND 20 PNDIS_PACKET PacketArray[MAX_MULTI_SEND]; pAdapter = (PRNDISMP_ADAPTER)Context; CHECK_VALID_ADAPTER(pAdapter); ASSERT(pAdapter->SendProcessInProgress == TRUE); SndTimerCount++; NumPkts = 0; CurPkts = 0; RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter); while (!IsListEmpty(&pAdapter->PendingSendProcessList)) { pEntry = RemoveHeadList(&pAdapter->PendingSendProcessList); RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter); SndPacketCount++; CurPkts++; pSendResvdTemp = CONTAINING_RECORD(pEntry, RNDISMP_SEND_PKT_RESERVED_TEMP, Link); pNdisPacket = CONTAINING_RECORD(pSendResvdTemp, NDIS_PACKET, MiniportReserved); PacketArray[NumPkts] = pNdisPacket; NumPkts++; if (NumPkts == MAX_MULTI_SEND) { pAdapter->MultipleSendFunc(pAdapter, NULL, PacketArray, NumPkts); NumPkts = 0; } RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter); } pAdapter->SendProcessInProgress = FALSE; SndMaxPackets = MAX(SndMaxPackets, CurPkts); RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter); if (NumPkts != 0) { pAdapter->MultipleSendFunc(pAdapter, NULL, PacketArray, NumPkts); } } // SendProcessTimeout