VOID BasePortFlushQueuedTxPackets( __in PMP_PORT Port ) { PMP_TX_MSDU currentPacket, packetListToComplete = NULL; ULONG count = 0; NDIS_STATUS completionStatus = BasePortGetPortStatus(Port); // Empty the pending TX queue MP_ACQUIRE_PORT_LOCK(Port, FALSE); while (!MpPacketQueueIsEmpty(&Port->PendingTxQueue)) { currentPacket = MP_MSDU_FROM_QUEUE_ENTRY(MpDequeuePacket(&Port->PendingTxQueue)); count++; MP_TX_MSDU_NEXT_MSDU(currentPacket) = NULL; MP_TX_MSDU_STATUS(currentPacket) = completionStatus; MP_TX_MSDU_NEXT_MSDU(currentPacket) = packetListToComplete; packetListToComplete = currentPacket; } MP_RELEASE_PORT_LOCK(Port, FALSE); if (packetListToComplete != NULL) { BasePortCompleteFailedPackets(Port, packetListToComplete, 0 ); } }
VOID BasePortFreeTranslatedTxPackets( __in PMP_PORT Port, __out PMP_TX_MSDU PacketList ) { PMP_TX_MSDU currentPacket = PacketList; ULONG i = 0; while (currentPacket != NULL) { // Save the next since we are going to delete this packet PacketList = MP_TX_MSDU_NEXT_MSDU(currentPacket); // Delete the fragments from this packet for (i = 0; i < MP_TX_MSDU_MPDU_COUNT(currentPacket); i++) { NdisFreeToNPagedLookasideList(&(Port->TxFragmentLookaside), MP_TX_MSDU_MPDU_AT(currentPacket, i)); } // Delete the packet NdisFreeToNPagedLookasideList(&(Port->TxPacketLookaside), currentPacket); currentPacket = PacketList; } }
/* NOTE This packet gives up the VNIC lock before calling into the hardware. The VNIC state might get changed during the course of the call */ VOID VNicSendPktsToHw( _In_ PVNIC pVNic, _In_ ULONG ulNumPkts, _In_ PMP_TX_MSDU PacketList, _In_ ULONG SendFlags ) { BOOLEAN fDispatchLevel = SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? TRUE : FALSE; PMP_TX_MSDU currentPacket = NULL; ULONG myCount = 0; currentPacket = PacketList; while (currentPacket != NULL) { myCount++; currentPacket = MP_TX_MSDU_NEXT_MSDU(currentPacket); } MPASSERT(myCount == ulNumPkts); ASSERT(VNicIsLocked(pVNic)); ASSERT(VNicIsActive(pVNic)); MpTrace(COMP_HVL, DBG_LOUD, ("VNic(%d): Sending %d packets to the hardware \n", VNIC_PORT_NO, ulNumPkts)); /* We can call the hardware for sending packets. We need to increment the context switch ref count to avoid becoming inactive. The context switch ref count will be decremented when we receive the send completionAlso we need to give up our lock before calling the hardware. */ VNicIncCtxSRef(pVNic, ulNumPkts, REF_SEND_PKTS); // we also need to keep track of the sends outstanding to the hardware VNicIncOutstandingSends(pVNic, ulNumPkts); _Analysis_assume_lock_held_((& pVNic->Lock)->SpinLock); VNicUnlockAtDispatch(pVNic, fDispatchLevel); Hw11SendPackets(pVNic->pvHwContext, PacketList, SendFlags); VNicLockAtDispatch(pVNic, fDispatchLevel); }
VOID VNic11SendPackets( __in PVNIC pVNic, __in PMP_TX_MSDU PacketList, __in ULONG ulNumPkts, __in ULONG SendFlags ) { BOOLEAN fDispatchLevel = SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? TRUE : FALSE; BOOLEAN fFailSends = FALSE; #if DBG PMP_TX_MSDU currentPacket; ULONG myCount = 0; currentPacket = PacketList; while (currentPacket != NULL) { myCount++; currentPacket = MP_TX_MSDU_NEXT_MSDU(currentPacket); } MPASSERT(myCount == ulNumPkts); #endif VNicLockAtDispatch(pVNic, fDispatchLevel); do { if (VNicIsInReset(pVNic)) { MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d) is in reset. Failing %d sends. \n", VNIC_PORT_NO, ulNumPkts)); fFailSends = TRUE; break; } if (VNicIsActive(pVNic) && Hw11CanTransmit(pVNic->pvHwContext) && (0 == PktQueueDepth(&pVNic->TxQueue))) { VNicSendPktsToHw(pVNic, ulNumPkts, PacketList, SendFlags); } else { /* We are either a. not currently active or b. the packets cannot be submitted to the hardware or c. there are packets pending in the send queue already Queue the send requests internally */ VNicQueueSendRequests(pVNic, ulNumPkts, PacketList); } } while (FALSE); VNicUnlockAtDispatch(pVNic, fDispatchLevel); if (fFailSends) { Port11SendCompletePackets(pVNic->pvPort, PacketList, SendFlags); } return; }
NDIS_STATUS BasePortTranslateTxNBLsToTxPackets( __in PMP_PORT Port, __in PNET_BUFFER_LIST NetBufferLists, __out PMP_TX_MSDU * PacketList ) { PMP_TX_MSDU outputPacketList = NULL; PMP_TX_MSDU currentPacket, prevPacket = NULL; PMP_TX_MPDU currentFragment; USHORT fragmentIndex = 0; PNET_BUFFER_LIST currentNetBufferList = NetBufferLists, nextNetBufferList; PNET_BUFFER currentNetBuffer; NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PDOT11_EXTSTA_SEND_CONTEXT osSendContext, mySendContext; // This is same for ExtAP & ExtSTA *PacketList = NULL; // Convert each NBL and NB to our structure while (currentNetBufferList != NULL) { nextNetBufferList = NET_BUFFER_LIST_NEXT_NBL(currentNetBufferList); // First the MP_TX_MSDU currentPacket = NdisAllocateFromNPagedLookasideList(&(Port->TxPacketLookaside)); if (currentPacket == NULL) { MpTrace(COMP_SEND, DBG_SERIOUS, ("Failed to allocate MP_TX_MSDU for NET_BUFFER_LIST\n")); ndisStatus = NDIS_STATUS_RESOURCES; break; } NdisZeroMemory(currentPacket, sizeof(MP_TX_MSDU )); // Populate the TX_PACKET MP_TX_MSDU_WRAPPED_NBL(currentPacket) = currentNetBufferList; // Save the TX_MSDU in the NET_BUFFER_LIST for debugging purpose MP_NBL_WRAPPED_TX_MSDU(currentNetBufferList) = currentPacket; osSendContext = MP_GET_SEND_CONTEXT(currentNetBufferList); mySendContext = MP_TX_MSDU_SEND_CONTEXT(currentPacket); NdisMoveMemory(mySendContext, osSendContext, sizeof(DOT11_EXTSTA_SEND_CONTEXT)); if (outputPacketList == NULL) { outputPacketList = currentPacket; } else { MP_TX_MSDU_NEXT_MSDU(prevPacket) = currentPacket; } // The Next NBL's PACKET would be added after the current NBL's PACKET prevPacket = currentPacket; // Now we go through the NBs in this NB fragmentIndex = 0; for (currentNetBuffer = NET_BUFFER_LIST_FIRST_NB(currentNetBufferList); currentNetBuffer != NULL; currentNetBuffer = NET_BUFFER_NEXT_NB(currentNetBuffer)) { currentFragment = NdisAllocateFromNPagedLookasideList(&(Port->TxFragmentLookaside)); if (currentFragment == NULL) { MpTrace(COMP_SEND, DBG_SERIOUS, ("Failed to allocate MP_TX_MPDU for NET_BUFFER\n")); ndisStatus = NDIS_STATUS_RESOURCES; break; } NdisZeroMemory(currentFragment, sizeof(MP_TX_MPDU )); // Populate the TX_FRAGMENT MP_TX_MPDU_WRAPPED_NB(currentFragment) = currentNetBuffer; MP_TX_MPDU_MSDU(currentFragment) = currentPacket; // Add it to the fragment list of the packet MP_TX_MSDU_MPDU_AT(currentPacket, fragmentIndex) = currentFragment; fragmentIndex++; } MP_TX_MSDU_MPDU_COUNT(currentPacket) = fragmentIndex; if (ndisStatus != NDIS_STATUS_SUCCESS) { break; } currentNetBufferList = nextNetBufferList; } if (ndisStatus != NDIS_STATUS_SUCCESS) { if (outputPacketList != NULL) { BasePortFreeTranslatedTxPackets(Port, outputPacketList); outputPacketList = NULL; } } *PacketList = outputPacketList; return ndisStatus; }
VOID BasePortTransmitQueuedPackets( __in PMP_PORT Port, __in ULONG SendFlags ) { PMP_TX_MSDU currentPacket, prevPacketToForward = NULL, packetListToForward = NULL; ULONG count = 0, numPktToFwd = 0; NDIS_STATUS ndisStatus; PMP_TX_MSDU packetListToFail = NULL; // PORT_LOCK must be held // If I am pausing, this flag is set. We would not // be processing any pending packets. These packets get flushed on a pause // then not process these pending packets if (MP_TEST_PORT_STATUS(Port, MP_PORT_CANNOT_SEND_MASK)) { // // We dont do anything // MpTrace(COMP_SEND, DBG_NORMAL, ("Dropping sends as port should not be sending\n")); return; } while ((!MpPacketQueueIsEmpty(&Port->PendingTxQueue) && (count < MAX_SEND_MSDU_TO_PROCESS))) { // // Dequeue the first packet from the list // currentPacket = MP_MSDU_FROM_QUEUE_ENTRY(MpDequeuePacket(&Port->PendingTxQueue)); count++; // // Let the port pre-process the packets. We only let one // packet to get processed at a time // MP_TX_MSDU_NEXT_MSDU(currentPacket) = NULL; MP_TX_MSDU_STATUS(currentPacket) = NDIS_STATUS_SUCCESS; ndisStatus = Port11NotifySend(Port, currentPacket, SendFlags); if (ndisStatus == NDIS_STATUS_SUCCESS) { // // Look at the per-packet status values // ndisStatus = MP_TX_MSDU_STATUS(currentPacket); if (ndisStatus == NDIS_STATUS_SUCCESS) { // // Port is fine with processing these packets. Let us check if we can // continue sending this down. We only check with the VNIC // if (VNic11CanTransmit(PORT_GET_VNIC(Port)) == FALSE) { // // Some reason we cannot submit this packet to the // HW yet. Queue it in the pending Tx queue // ndisStatus = NDIS_STATUS_PENDING; // // In this case we indicate the packet to the Port again // the next time we attempt to send. Ensure that the port // can handle that // } } } else { // // Port returned PENDING or FAILURE for the packet list. We wont // be sending any of these packets // } // // All the above processing would give us one of the following status codes // // NDIS_STATUS_SUCCESS - The packets can be processed furthers. In this case // we forward the packet to the lower layer // NDIS_STATUS_PENDING - This packet should not be sent now, but can be sent later // In this case we requeue the packet and stop processing // further packets // NDIS_STATUS_FAILURE or anything other failure status // - The packet is not sent and we continue processing // other packets if (ndisStatus == NDIS_STATUS_SUCCESS) { numPktToFwd++; // Add this to the end of the chain we are forwarding to the HW if (packetListToForward == NULL) { packetListToForward = currentPacket; } else { MP_TX_MSDU_NEXT_MSDU(prevPacketToForward) = currentPacket; } prevPacketToForward = currentPacket; // // Increment the counter for the number of packets we have submitted // to the hardware. This would block the port from pausing, etc // PORT_INCREMENT_PNP_REFCOUNT(Port); } else if (ndisStatus == NDIS_STATUS_PENDING) { // // Put the packet back at the head of the packet queue. To avoid out of // order delivery we dont go forward and give any more packets to the // lower layer // MpQueuePacketPriority(&Port->PendingTxQueue, QUEUE_ENTRY_FROM_MP_MSDU(currentPacket)); break; } else { // // Put this packet in the list of packets to be failed // MpTrace(COMP_SEND, DBG_NORMAL, ("Port or VNic failed sends with status 0x%08x\n", ndisStatus)); MP_TX_MSDU_STATUS(currentPacket) = ndisStatus; MP_TX_MSDU_NEXT_MSDU(currentPacket) = packetListToFail; packetListToFail = currentPacket; } } if ((packetListToForward != NULL) || (packetListToFail != NULL)) { // Forward this list to the VNIC MP_RELEASE_PORT_LOCK(Port, NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags)); if (packetListToForward != NULL) { VNic11SendPackets(PORT_GET_VNIC(Port), packetListToForward, numPktToFwd, SendFlags); } // // Complete the failed packets // if (packetListToFail != NULL) { BasePortCompleteFailedPackets(Port, packetListToFail, NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags) ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0 ); } MP_ACQUIRE_PORT_LOCK(Port, NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags)); } }
VOID BasePortCompleteFailedPackets( __in PMP_PORT Port, __in PMP_TX_MSDU PacketList, __in ULONG SendCompleteFlags ) { PMP_TX_MSDU currentPacket = PacketList; PNET_BUFFER_LIST currentNetBufferList, prevNetBufferList = NULL; PNET_BUFFER_LIST netBufferListsToComplete = NULL; #if DBG ULONG ulNumNBLs = 0, ulInternalSends = 0; #endif while (currentPacket != NULL) { // // No refcount is added yet, so we dont need to remove anything // if (MP_TX_MSDU_WRAPPED_NBL(currentPacket) == NULL) { #if DBG ulInternalSends++; #endif // Internal packet submitted from BasePortSendInternalPacket, free the memory // we allocated for the buffer MP_FREE_MEMORY(MP_TX_MSDU_MPDU_AT(currentPacket, 0)->InternalSendBuffer); } else { #if DBG ulNumNBLs++; #endif // // There were from the OS. We need to convert back to NBLs and // complete them to the OS // // // Get the NBLs out // currentNetBufferList = currentPacket->NetBufferList; NET_BUFFER_LIST_NEXT_NBL(currentNetBufferList) = NULL; NET_BUFFER_LIST_STATUS(currentNetBufferList) = currentPacket->Status; if (netBufferListsToComplete == NULL) { netBufferListsToComplete = currentNetBufferList; } else { NET_BUFFER_LIST_NEXT_NBL(prevNetBufferList) = currentNetBufferList; } prevNetBufferList = currentNetBufferList; } currentPacket = MP_TX_MSDU_NEXT_MSDU(currentPacket); } // // Free our packet wrapper structures for all the packets // BasePortFreeTranslatedTxPackets(Port, PacketList); #if DBG MpTrace(COMP_SEND, DBG_NORMAL, ("Port(%d): NdisMSendNetBufferListsComplete called with %d NBLs\n", Port->PortNumber, ulNumNBLs)); MpTrace(COMP_SEND, DBG_NORMAL, ("Port(%d): Completed %d internal sends\n", Port->PortNumber, ulInternalSends)); #endif if (netBufferListsToComplete != NULL) { // Complete the NBLs back to NDIS NdisMSendNetBufferListsComplete( Port->MiniportAdapterHandle, netBufferListsToComplete, SendCompleteFlags ); } }