Sample Code
windows driver samples/ Native Wi-Fi Miniport Sample Driver/ C++/ hw/ hw_send.c/
/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: hw_send.c Abstract: Implements the send functionality for the HW layer Revision History: When What ---------- ---------------------------------------------- 09-04-2007 Created Notes: --*/ #include "precomp.h" #include "hw_send.h" #include "hw_crypto.h" #include "hw_phy.h" #include "hw_mac.h" #include "hw_rate.h" #if DOT11_TRACE_ENABLED #include "hw_send.tmh" #endif #if 0 // TEST CODE __inline void HW_CHECK_TX_MSDU_TIME(PHW_TX_MSDU _Msdu, ULONG _BreakTime) { LARGE_INTEGER _currentTime; UNREFERENCED_PARAMETER(_BreakTime); UNREFERENCED_PARAMETER(_Msdu); NdisGetCurrentSystemTime(&_currentTime); /* Returns in 100 nanoseconds */ MpTrace(COMP_SEND, DBG_SERIOUS, ("# %4d %s %d\n", _Msdu->TotalMSDULength, (_Msdu->TxSucceeded ? "OK " : "ERR"), (ULONG)(_currentTime.QuadPart - _Msdu->SnapshotTime.QuadPart))); } #endif NDIS_STATUS Hw11InitializeSendEngine( _In_ PHW Hw, _Out_ NDIS_ERROR_CODE* ErrorCode, _Out_ PULONG ErrorValue ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; ULONG i, j, size; PHW_TX_QUEUE currentQueue; HAL_TX_QUEUE_SETUP_INFO txQueueSetupInfo; PHW_TX_MSDU msdu; BOOLEAN halQueuesAllocated = FALSE, halQueuesSetup = FALSE; BOOLEAN lookasideAllocated = FALSE; UNREFERENCED_PARAMETER(ErrorValue); do { // // Before we allocate the queue structures, etc let us populate // some initial values so that we can do some stuff in a loop // // Data queue Hw->TxInfo.TxQueue[HW11_DEFAULT_QUEUE].NumMSDUAllocated = Hw->RegInfo.NumTXBuffers; Hw->TxInfo.TxQueue[HW11_DEFAULT_QUEUE].HalQueueType = HAL_QUEUE_TYPE_DATA; Hw->TxInfo.TxQueue[HW11_DEFAULT_QUEUE].HalQueueIndex = LOW_QUEUE; Hw->TxInfo.TxQueue[HW11_DEFAULT_QUEUE].MaxPendingTx = MAXULONG; // Management queue (mainly internal send) Hw->TxInfo.TxQueue[HW11_INTERNAL_SEND_QUEUE].NumMSDUAllocated = HW_INTERNAL_SEND_QUEUE_BUFFER_COUNT; Hw->TxInfo.TxQueue[HW11_INTERNAL_SEND_QUEUE].HalQueueType = HAL_QUEUE_TYPE_MANAGEMENT; Hw->TxInfo.TxQueue[HW11_INTERNAL_SEND_QUEUE].HalQueueIndex = NORMAL_QUEUE; Hw->TxInfo.TxQueue[HW11_INTERNAL_SEND_QUEUE].MaxPendingTx = 0; // High priority (unused) Hw->TxInfo.TxQueue[HW11_UNUSED_QUEUE].NumMSDUAllocated = HW_UNUSED_SEND_QUEUE_BUFFER_COUNT; Hw->TxInfo.TxQueue[HW11_UNUSED_QUEUE].HalQueueType = HAL_QUEUE_TYPE_HIGH_PRIORITY; Hw->TxInfo.TxQueue[HW11_UNUSED_QUEUE].HalQueueIndex = HIGH_QUEUE; Hw->TxInfo.TxQueue[HW11_UNUSED_QUEUE].MaxPendingTx = 0; // Beacon queue Hw->TxInfo.TxQueue[HW11_BEACON_QUEUE].NumMSDUAllocated = HW_BEACON_QUEUE_BUFFER_COUNT; // This is all that we need Hw->TxInfo.TxQueue[HW11_BEACON_QUEUE].HalQueueType = HAL_QUEUE_TYPE_BEACON; Hw->TxInfo.TxQueue[HW11_BEACON_QUEUE].HalQueueIndex = BEACON_QUEUE; Hw->TxInfo.TxQueue[HW11_BEACON_QUEUE].MaxPendingTx = 0; // // Allocate content for each of the HW queues // for (i = 0; i < HW11_NUM_TX_QUEUE; i++) { currentQueue = &Hw->TxInfo.TxQueue[i]; // // Empty pending queues // MpInitPacketQueue(&(currentQueue->PendingTxQueue)); // // Allocate the TX_MSDU's array // MP_ALLOCATE_MEMORY(Hw->MiniportAdapterHandle, ¤tQueue->MSDUArray, (currentQueue->NumMSDUAllocated * sizeof(HW_TX_MSDU)), HW_MEMORY_TAG); if (currentQueue->MSDUArray == NULL) { *ErrorCode = NDIS_ERROR_CODE_OUT_OF_RESOURCES; ndisStatus = NDIS_STATUS_RESOURCES; MpTrace(COMP_SEND, DBG_SERIOUS, ("Failed to allocate the requested number (%d) of TX_MSDUs for queue %d.\n", currentQueue->NumMSDUAllocated, i )); break; } NdisZeroMemory(currentQueue->MSDUArray, (currentQueue->NumMSDUAllocated * sizeof(HW_TX_MSDU))); // We leave one descriptor empty for easy synchronization currentQueue->NumMSDUAvailable = currentQueue->NumMSDUAllocated - 1; currentQueue->NextToSend = 0; // Start from descriptor 0 currentQueue->NextToComplete = 0; currentQueue->NextToReserve = 0; MP_OPEN_RECORDER(Hw->MiniportAdapterHandle, currentQueue->Tracking_SendRecorder); } if (ndisStatus != NDIS_STATUS_SUCCESS) { break; } // // Now we allocate the HAL queues // ndisStatus = HalAllocateTxQueues(Hw->Hal, HW11_NUM_TX_QUEUE); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_SEND, DBG_SERIOUS, ("Failed to allocate HAL TX queues. Status = 0x%08x\n", ndisStatus)); *ErrorCode = NDIS_ERROR_CODE_OUT_OF_RESOURCES; break; } halQueuesAllocated = TRUE; // // Allocate TX descriptors // for (i = 0; i < HW11_NUM_TX_QUEUE; i++) { txQueueSetupInfo.DescNum = Hw->TxInfo.TxQueue[i].NumMSDUAllocated; txQueueSetupInfo.QueueType = Hw->TxInfo.TxQueue[i].HalQueueType; ndisStatus = HalSetupTxQueue(Hw->Hal, Hw->TxInfo.TxQueue[i].HalQueueIndex, &txQueueSetupInfo); if (ndisStatus != NDIS_STATUS_SUCCESS) { *ErrorCode = NDIS_ERROR_CODE_OUT_OF_RESOURCES; MpTrace(COMP_SEND, DBG_SERIOUS, ("Failed to setup the HAL TX queue for queue %d. Status = 0x%08x\n", i, ndisStatus )); break; } HalResetTxDescs(Hw->Hal, Hw->TxInfo.TxQueue[i].HalQueueIndex); } if (ndisStatus != NDIS_STATUS_SUCCESS) { // Free the partial allocation if (i > 0) { do { i--; HalReleaseTxDescs(Hw->Hal, Hw->TxInfo.TxQueue[i].HalQueueIndex); }while (i != 0); } break; } halQueuesSetup = TRUE; // For the default queue, we do some extra allocations - SG buffers and coalesce buffers currentQueue = &Hw->TxInfo.TxQueue[HW11_DEFAULT_QUEUE]; // For integer overflow ndisStatus = RtlULongMult(currentQueue->NumMSDUAllocated, MP_TX_FRAGMENTS_MAX_COUNT, &size); if (ndisStatus != STATUS_SUCCESS) break; ndisStatus = RtlULongMult(size, Hw->TxInfo.ScatterGatherListSize, &size); if (ndisStatus != STATUS_SUCCESS) break; // Allocate SG buffers MP_ALLOCATE_MEMORY( Hw->MiniportAdapterHandle, &Hw->TxInfo.ScatterGatherListBuffers, size, HW_MEMORY_TAG ); if (Hw->TxInfo.ScatterGatherListBuffers == NULL) { *ErrorCode = NDIS_ERROR_CODE_OUT_OF_RESOURCES; ndisStatus = NDIS_STATUS_RESOURCES; MpTrace(COMP_SEND, DBG_SERIOUS, ("Failed to allocate scatter gather resources\n")); break; } NdisZeroMemory(Hw->TxInfo.ScatterGatherListBuffers, size); // // Initialize the TX MSDUs for the default queue // for (i = 0; i < currentQueue->NumMSDUAllocated; i++) { msdu = &(currentQueue->MSDUArray[i]); // // Assign the preallocate SG buffer to this MSDU // msdu->ScatterGatherList = Hw->TxInfo.ScatterGatherListBuffers + (i * Hw->TxInfo.ScatterGatherListSize * MP_TX_FRAGMENTS_MAX_COUNT); msdu->Hw = Hw; msdu->Index = i; msdu->QueueID = HW11_DEFAULT_QUEUE; // Allocate the coalesce buffers NdisMAllocateSharedMemory(Hw->MiniportAdapterHandle, MAX_TX_RX_PACKET_SIZE, FALSE, (void **)&msdu->BufferVa, &msdu->BufferPa); if (msdu->BufferVa == NULL) { ndisStatus=NDIS_STATUS_RESOURCES; *ErrorCode = NDIS_ERROR_CODE_OUT_OF_RESOURCES; MpTrace(COMP_SEND, DBG_SERIOUS, ("Allocation of coalesce buffer %d failed\n", i)); break; } } if (ndisStatus != NDIS_STATUS_SUCCESS) { break; } for (j = 1; j < HW11_NUM_TX_QUEUE; j++) { // For the other queue, we only allocate the data buffer currentQueue = &Hw->TxInfo.TxQueue[j]; for (i = 0; i < currentQueue->NumMSDUAllocated; i++) { msdu = &(currentQueue->MSDUArray[i]); msdu->Hw = Hw; msdu->Index = i; msdu->QueueID = (UCHAR)j; NdisMAllocateSharedMemory(Hw->MiniportAdapterHandle, MAX_TX_RX_PACKET_SIZE, FALSE, (void **)&msdu->BufferVa, &msdu->BufferPa); if (msdu->BufferVa == NULL) { ndisStatus=NDIS_STATUS_RESOURCES; *ErrorCode = NDIS_ERROR_CODE_OUT_OF_RESOURCES; MpTrace(COMP_SEND, DBG_SERIOUS, ("Allocation of buffer %d for queued %d failed\n", i, j)); break; } } if (ndisStatus != NDIS_STATUS_SUCCESS) { break; } } if (ndisStatus != NDIS_STATUS_SUCCESS) { break; } // // Some basic allocations // // // Lookaside list for HW_TX_MPDU. These are allocated on the fly as and when // we need them. // NdisInitializeNPagedLookasideList( &Hw->TxInfo.TxMPDULookaside, NULL, NULL, 0, sizeof(HW_TX_MPDU), HW_MEMORY_TAG, 0 ); NdisInitializeNPagedLookasideList( &Hw->TxInfo.TxPacketLookaside, NULL, NULL, 0, sizeof(MP_TX_MSDU), HW_MEMORY_TAG, 0 ); NdisInitializeNPagedLookasideList( &Hw->TxInfo.TxFragmentLookaside, NULL, NULL, 0, sizeof(MP_TX_MPDU), HW_MEMORY_TAG, 0 ); lookasideAllocated = TRUE; } while (FALSE); if (ndisStatus != NDIS_STATUS_SUCCESS) { // Other allocations if (lookasideAllocated) { NdisDeleteNPagedLookasideList(&Hw->TxInfo.TxFragmentLookaside); NdisDeleteNPagedLookasideList(&Hw->TxInfo.TxPacketLookaside); NdisDeleteNPagedLookasideList(&Hw->TxInfo.TxMPDULookaside); } // // Free the coalesce and other data buffers // for (j = 0; j < HW11_NUM_TX_QUEUE; j++) { currentQueue = &Hw->TxInfo.TxQueue[j]; if (currentQueue->MSDUArray != NULL) { for (i = 0; i < currentQueue->NumMSDUAllocated; i++) { msdu = &(currentQueue->MSDUArray[i]); if (msdu->BufferVa != NULL) { NdisMFreeSharedMemory(Hw->MiniportAdapterHandle, MAX_TX_RX_PACKET_SIZE, FALSE, (void *)msdu->BufferVa, msdu->BufferPa ); } } } } // The SG buffers if (Hw->TxInfo.ScatterGatherListBuffers) MP_FREE_MEMORY(Hw->TxInfo.ScatterGatherListBuffers); // Release the HAL data structures if (halQueuesSetup) { for (i = 0; i < HW11_NUM_TX_QUEUE; i++) { HalReleaseTxDescs(Hw->Hal, Hw->TxInfo.TxQueue[i].HalQueueIndex); } } if (halQueuesAllocated) HalReleaseTxQueues(Hw->Hal); // Release the HW data queues for (i = 0; i < HW11_NUM_TX_QUEUE; i++) { currentQueue = &Hw->TxInfo.TxQueue[i]; if (currentQueue->MSDUArray != NULL) { MP_FREE_MEMORY(currentQueue->MSDUArray); } currentQueue->MSDUArray = NULL; MP_CLOSE_RECORDER(currentQueue->Tracking_SendRecorder); } } return ndisStatus; } VOID Hw11TerminateSendEngine( _In_ PHW Hw ) { PHW_TX_QUEUE currentQueue; PHW_TX_MSDU msdu; ULONG i, j; // Other allocations NdisDeleteNPagedLookasideList(&Hw->TxInfo.TxFragmentLookaside); NdisDeleteNPagedLookasideList(&Hw->TxInfo.TxPacketLookaside); NdisDeleteNPagedLookasideList(&Hw->TxInfo.TxMPDULookaside); // // Free the coalesce and other data buffers // for (j = 0; j < HW11_NUM_TX_QUEUE; j++) { currentQueue = &Hw->TxInfo.TxQueue[j]; if (currentQueue->MSDUArray != NULL) { for (i = 0; i < currentQueue->NumMSDUAllocated; i++) { msdu = &(currentQueue->MSDUArray[i]); if (msdu->BufferVa != NULL) { NdisMFreeSharedMemory(Hw->MiniportAdapterHandle, MAX_TX_RX_PACKET_SIZE, FALSE, (void *)msdu->BufferVa, msdu->BufferPa ); } } } } // The SG buffers if (Hw->TxInfo.ScatterGatherListBuffers) MP_FREE_MEMORY(Hw->TxInfo.ScatterGatherListBuffers); // Release the HAL data structures for (i = 0; i < HW11_NUM_TX_QUEUE; i++) { currentQueue = &Hw->TxInfo.TxQueue[i]; HalReleaseTxDescs(Hw->Hal, currentQueue->HalQueueIndex); } HalReleaseTxQueues(Hw->Hal); // Release the HW data queues for (i = 0; i < HW11_NUM_TX_QUEUE; i++) { currentQueue = &Hw->TxInfo.TxQueue[i]; if (currentQueue->MSDUArray != NULL) { MP_FREE_MEMORY(currentQueue->MSDUArray); } currentQueue->MSDUArray = NULL; MP_CLOSE_RECORDER(currentQueue->Tracking_SendRecorder); } } /** * This function is called once the Hw has made any and all changes that * were needed to the 802.11 frame to be transmitted. After the scatter gather * operation has been performed, the NIC must NOT make any changes to the * packet as they may not get reflected in the SG Elements. * */ VOID HwPerformScatterGather( _In_ PHW Hw, _In_ PHW_TX_MSDU Msdu ) { PNET_BUFFER_LIST NetBufferList; PNET_BUFFER CurrentNetBuffer; NDIS_STATUS ndisStatus = NDIS_STATUS_FAILURE; ULONG ulSize; // // Place a ref count on the Scatter Gather allocation count // Prevents it from completing the operation prematurely // HW_INCREMENT_PENDING_TX_MSDU_SG_OP(Msdu); NetBufferList = MP_TX_MSDU_WRAPPED_NBL(Msdu->MpMsdu); Msdu->TotalSGRequested = 0; for (CurrentNetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList); CurrentNetBuffer != NULL; CurrentNetBuffer = NET_BUFFER_NEXT_NB(CurrentNetBuffer)) { // // If reset is occuring we must abandon attempts to SG // if (HW_TEST_ADAPTER_STATUS(Hw, HW_CANNOT_SEND_FLAGS)) { // We fail this packet right now ndisStatus = HwGetAdapterStatus(Hw); break; } // // One more NetBuffer submitted for scatter gather // HW_INCREMENT_PENDING_TX_MSDU_SG_OP(Msdu); HW_INCREMENT_ACTIVE_OPERATION_REF(Hw); // // Ask NDIS to scatter gather this NetBuffer for us // if (Msdu->TotalSGRequested < HW_MAX_NUM_OF_FRAGMENTS) { ndisStatus = RtlULongMult(Hw->TxInfo.ScatterGatherListSize, Msdu->TotalSGRequested, &ulSize); if (ndisStatus != STATUS_SUCCESS) { MpTrace(COMP_SEND, DBG_SERIOUS, ("Overflow when computing SG buffer size\n")); break; } // // Use the preallocated scatter gather resources // ndisStatus = NdisMAllocateNetBufferSGList( Hw->MiniportDmaHandle, CurrentNetBuffer, Msdu, NDIS_SG_LIST_WRITE_TO_DEVICE, (Msdu->ScatterGatherList + ulSize), Hw->TxInfo.ScatterGatherListSize ); } else { // // We have run out of preallocated resources of scatter gather. // We will let OS allocate some on the fly. This is unoptimal // but we expect this situation to get hit very rarely. // ndisStatus = NdisMAllocateNetBufferSGList( Hw->MiniportDmaHandle, CurrentNetBuffer, Msdu, NDIS_SG_LIST_WRITE_TO_DEVICE, NULL, 0 ); } if (ndisStatus != NDIS_STATUS_SUCCESS) { // // Scatter Gather call failed. Remove the ref count added above // MpTrace(COMP_SEND, DBG_NORMAL, ("Failed to scatter gather NetBuffer %p\n", CurrentNetBuffer)); HW_DECREMENT_PENDING_TX_MSDU_SG_OP(Msdu); HW_DECREMENT_ACTIVE_OPERATION_REF(Hw); break; } // // SG successfully requested. The requst cannot fail now. // This number reflects the total number of SG operations that will // have successfully completed as well. // Msdu->TotalSGRequested++; } if (ndisStatus == NDIS_STATUS_SUCCESS) { // // Successful with requesting scatter gather for all NBs. // Msdu->ScatterGatherRequested = TRUE; } else { Msdu->WaitForSendToComplete = FALSE; Msdu->FailedDuringSend = TRUE; // These packets have failed. But we set the Ready for send flag // on the packets irrespective HW_TX_MSDU_SET_FLAG(Msdu, HW_TX_MSDU_IS_READY_FOR_SEND); } // // Remove the Ref count added at start of SG for this Tx MSDU // if (HW_DECREMENT_PENDING_TX_MSDU_SG_OP(Msdu) == 0) { // // Scatter gather has been successfully completed // for this MSDU. // HwSGComplete(Msdu, TRUE); } } VOID HWProcessSGList( _In_ PDEVICE_OBJECT DeviceObject, _In_ PVOID Irp, _In_ PSCATTER_GATHER_LIST SGList, _In_ PVOID Context ) { PHW_TX_MSDU Msdu; UNREFERENCED_PARAMETER(DeviceObject); UNREFERENCED_PARAMETER(Irp); Msdu = (PHW_TX_MSDU) Context; // // One more NetBuffer has been scatter gathered successfully // Msdu->SGElementList[Msdu->SGElementListCount] = SGList; Msdu->SGElementListCount++; // // Check if we are done with SG for this NBL // if (HW_DECREMENT_PENDING_TX_MSDU_SG_OP(Msdu) == 0) { HwSGComplete(Msdu, TRUE); } // // Remove the HW level count kept on pending SG operations // HW_DECREMENT_ACTIVE_OPERATION_REF(Msdu->Hw); } VOID HwSGComplete( _In_ PHW_TX_MSDU Msdu, _In_ BOOLEAN DispatchLevel ) { MPASSERT(Msdu->OutstandingSGAllocationCount == 0); if (Msdu->ScatterGatherRequested) { // // All SG operations for this NBL have been completed // successfully. // // This packet is ready for sending HW_TX_MSDU_SET_FLAG(Msdu, HW_TX_MSDU_IS_READY_FOR_SEND); } else { // // Scatter gather has failed // MPASSERT(Msdu->OutstandingSGAllocationCount == 0); // Ensure we dont have SG operations pending MPASSERT(Msdu->FailedDuringSend == TRUE); } // // Flush the ready packets to the hardware // HwSubmitReadyMSDUs(Msdu->Hw, &Msdu->Hw->TxInfo.TxQueue[Msdu->QueueID], DispatchLevel); } __inline BOOLEAN HwCanTransmit( _In_ PHW_TX_QUEUE TxQueue ) { // // The requirements to be able to transmit are: // 1. HW has available TX_MSDUs for sending // return ((TxQueue->NumMSDUAvailable > 0) ? TRUE : FALSE); } NDIS_STATUS HwPrepareTxMSDUForSend( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue, _In_ PHW_TX_MSDU Msdu ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PDOT11_MAC_HEADER fragmentHeader; PDOT11_MGMT_DATA_MAC_HEADER mgmtdataHeader; PHW_MAC_CONTEXT macContext; PHW_PEER_NODE peerNode = NULL; PMP_TX_MPDU mpMpdu; PHW_TX_MPDU hwMpdu; PHW_KEY_ENTRY key = NULL; ULONG fragmentLength; ULONG i; USHORT duration; HW_TX_CIPHER_SETTING cipherSetting; UCHAR packetType; USHORT initialTxRate; UNREFERENCED_PARAMETER(TxQueue); // OS currently only sends 1 MPDU per MSDU. The miniport does not // do fragmentation on its own. So we only expect to have 1 MPDU per MSDU MPASSERT(Msdu->MpduCount == 1); do { // // Get the fragment header from the first MPDU. This would also apply for all // the MSDUs in the packets // mpMpdu = HW_TX_MSDU_MPDU_AT(Msdu, 0)->MpMpdu; fragmentLength = MP_TX_MPDU_LENGTH(mpMpdu); // We shouldnt ever send really small packets through this code path MPASSERT(fragmentLength > sizeof(DOT11_MAC_HEADER)); fragmentHeader = MP_TX_MPDU_DATA(mpMpdu, sizeof(DOT11_MAC_HEADER)); if ((fragmentHeader == NULL) || (fragmentLength < sizeof(DOT11_MAC_HEADER))) { MpTrace(COMP_SEND, DBG_SERIOUS, ("Not enough data in packet to be sent\n")); ndisStatus = NDIS_STATUS_INVALID_DATA; break; } // The MAC context that is trying to send the packet macContext = Msdu->MpMsdu->MacContext; // Find the peer destination node. If we cannot find an existing one, // we use the default peer node peerNode = HwFindPeerNode(macContext, fragmentHeader->Address1, FALSE); if (peerNode == NULL) peerNode = &macContext->DefaultPeer; Msdu->PeerNode = peerNode; // Collect packet information Msdu->MulticastDestination = DOT11_IS_MULTICAST(fragmentHeader->Address1); packetType = (UCHAR)fragmentHeader->FrameControl.Type; if ((packetType == DOT11_FRAME_TYPE_DATA) || (packetType == DOT11_FRAME_TYPE_MANAGEMENT)) { if (fragmentLength < sizeof(DOT11_MGMT_DATA_MAC_HEADER)) { // Bad packet MpTrace(COMP_SEND, DBG_SERIOUS, ("Not enough data in management/data packet to be sent\n")); ndisStatus = NDIS_STATUS_INVALID_DATA; break; } } // Determine if this packet must be encrypted cipherSetting = HwDetermineCipherSettings(macContext, peerNode, Msdu, fragmentHeader); if (cipherSetting != HW_TX_NEVER_ENCRYPT) { // Find the key to be used for the encryption key = HwFindEncryptionKey(macContext, peerNode, Msdu, fragmentHeader, cipherSetting); if (key == NULL) { if (cipherSetting == HW_TX_CAN_ENCRYPT) { // // If this is a unicast frame or if the BSSPrivacy is on, reject the frame // if ((!Msdu->MulticastDestination) || (MP_TEST_FLAG(peerNode->CapabilityInfo, DOT11_CAPABILITY_INFO_PRIVACY))) { // We must encrypt the packet MpTrace(COMP_SEND, DBG_SERIOUS, ("Dropping packet because we are unable to find key to encrypt packet\n")); ndisStatus = NDIS_STATUS_NOT_ACCEPTED; break; } } // Packet should not be encrypted Msdu->SendEncrypted = FALSE; } else { // Found a key. Packet should be sent encrypted Msdu->SendEncrypted = TRUE; Msdu->Key = key; } } else { Msdu->SendEncrypted = FALSE; } // Determine the TX rate for this packet initialTxRate = HwDetermineStartTxRate(macContext, peerNode, Msdu, fragmentHeader); if (cipherSetting == HW_TX_ENCRYPT_IF_KEY_MAPPING_KEY_AVAILABLE) { // // This is an indication by the OS that is sending a 1x packet. We use // lower rates for these packets // initialTxRate = macContext->DefaultTXMgmtRate; } HwFillTxRateTable(macContext, Msdu, initialTxRate); Msdu->UseShortPreamble = FALSE; Msdu->CTSToSelfEnabled = FALSE; Msdu->PsBitSetting = TxDescPsBitUnspecified; if (MP_TX_MSDU_IS_PRIVATE(Msdu->MpMsdu)) { // For internal packets, set the PS bit based on the fragment header if (fragmentHeader->FrameControl.PwrMgt) { // PS bit should be set Msdu->PsBitSetting = TxDescPsBitSet; } else { // PS bit should be clear Msdu->PsBitSetting = TxDescPsBitClear; } } // We do not support sending on any PHY UNREFERENCED_PARAMETER(MP_TX_MSDU_SEND_CONTEXT(Msdu->MpMsdu)->uPhyId); Msdu->PhyId = macContext->OperatingPhyId; if (HalGetPhyMIB(Hw->Hal, Msdu->PhyId)->PhyType == dot11_phy_type_erp) { if (MP_TEST_FLAG(peerNode->CapabilityInfo, DOT11_CAPABILITY_SHORT_PREAMBLE) && (packetType != DOT11_FRAME_TYPE_MANAGEMENT)) Msdu->UseShortPreamble = TRUE; // if the data rate is at 11mbps or less, there is no need to send CTS to self if (macContext->CTSToSelfEnabled && (initialTxRate > 22)) Msdu->CTSToSelfEnabled = TRUE; } // RTS enable if (((fragmentLength + 4) > macContext->RTSThreshold) && (macContext->FragmentationThreshold > macContext->RTSThreshold)) { Msdu->RTSEnabled = TRUE; } else { Msdu->RTSEnabled = FALSE; } Msdu->SendItem.DescNum = 0; Msdu->SendItem.firstIterator = (HAL_TX_ITERATOR)(-1); Msdu->SendItem.lastIterator = (HAL_TX_ITERATOR)(-1); Msdu->TotalMSDULength = 0; // Process all the MPDUs in the packet (currently 1) for (i = 0; i < HW_TX_MSDU_MPDU_COUNT(Msdu); i++) { hwMpdu = HW_TX_MSDU_MPDU_AT(Msdu, i); mpMpdu = hwMpdu->MpMpdu; // We need to fill stuff into the fragment header fragmentHeader = MP_TX_MPDU_DATA(mpMpdu, sizeof(DOT11_MAC_HEADER)); fragmentLength = MP_TX_MPDU_LENGTH(mpMpdu); if (key != NULL) { // This frame would be sent encrypted fragmentHeader->FrameControl.WEP = 1; // Perform TX encryption ndisStatus = HwSetupTxCipher(Hw, macContext, Msdu, hwMpdu, key); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_SEND, DBG_SERIOUS, ("Failed to setup TX encryption for \n")); break; } // Update the length and header since we may have moved the MAC headers fragmentLength = MP_TX_MPDU_LENGTH(mpMpdu); fragmentHeader = MP_TX_MPDU_DATA(mpMpdu, sizeof(DOT11_MAC_HEADER)); } else if (!Hw->MacState.SafeModeEnabled) { // We arent encrypting, so clear the WEP bit fragmentHeader->FrameControl.WEP = 0; } // Fill bits that the OS does not set (We dont do fragmentation or power mgmt yet) if (!MP_TX_MSDU_IS_PRIVATE(Msdu->MpMsdu)) { fragmentHeader->FrameControl.MoreFrag = 0; fragmentHeader->FrameControl.Retry = 0; fragmentHeader->FrameControl.MoreData = 0; fragmentHeader->FrameControl.PwrMgt = 0; } Msdu->TotalMSDULength += fragmentLength; // Compute and fill the duration into the packet duration = 0; if (!Msdu->MulticastDestination) { // Calculate the duration field value duration = HwComputeTxTime( Msdu, packetType, fragmentLength, (i == (ULONG)(HW_TX_MSDU_MPDU_COUNT(Msdu) - 1) ? TRUE : FALSE) ); } NdisMoveMemory(&fragmentHeader->DurationID, &duration, sizeof(USHORT)); // Fill the sequence number into the packet if ((packetType == DOT11_FRAME_TYPE_DATA) || (packetType == DOT11_FRAME_TYPE_MANAGEMENT)) { mgmtdataHeader = (PDOT11_MGMT_DATA_MAC_HEADER)fragmentHeader; mgmtdataHeader->SequenceControl.FragmentNumber = (USHORT)i; mgmtdataHeader->SequenceControl.SequenceNumber = (USHORT)macContext->SequenceNumber; } if (i == (ULONG)(HW_TX_MSDU_MPDU_COUNT(Msdu) - 1)) { // When we reach the last fragment we increment the sequence number MP_INCREMENT_LIMIT_UNSAFE(macContext->SequenceNumber, 4096); } } if (ndisStatus != NDIS_STATUS_SUCCESS) { break; } } while (FALSE); return ndisStatus; } // Lock held __inline NDIS_STATUS HwReserveTxResources( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue, _In_ PMP_TX_MSDU Packet ) { PHW_TX_MSDU reservedMsdu; PHW_TX_MPDU mpdu; NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; ULONG i; // // One more TX_MSDU will now be in use. Decrement available count and // update TX_MSDU fields. // HW_DECREMENT_AVAILABLE_TX_MSDU(TxQueue); reservedMsdu = &TxQueue->MSDUArray[TxQueue->NextToReserve]; reservedMsdu->MpduCount = 0; MpTrace(COMP_SEND, DBG_LOUD, ("Reserved %d on %d\n", TxQueue->NextToReserve, TxQueue->HalQueueIndex)); // // Allocate MPDUs for (i = 0; i < MP_TX_MSDU_MPDU_COUNT(Packet); i++) { mpdu = NdisAllocateFromNPagedLookasideList(&(Hw->TxInfo.TxMPDULookaside)); if (mpdu == NULL) { MpTrace(COMP_SEND, DBG_SERIOUS, ("Unable to allocate HW_TX_MPDU during TX resource reservation %d\n", i)); ndisStatus = NDIS_STATUS_RESOURCES; break; } NdisZeroMemory(mpdu, sizeof(HW_TX_MPDU)); // Save the MPDU in the MSDU list reservedMsdu->MpduList[i] = mpdu; reservedMsdu->MpduCount++; // Link them mpdu->MpMpdu = MP_TX_MSDU_MPDU_AT(Packet, i); } if (ndisStatus != NDIS_STATUS_SUCCESS) { // Free the MPDUs for (i = 0; i < reservedMsdu->MpduCount; i++) { NdisFreeToNPagedLookasideList(&(Hw->TxInfo.TxMPDULookaside), reservedMsdu->MpduList[i]); } reservedMsdu->MpduCount = 0; HW_INCREMENT_AVAILABLE_TX_MSDU(TxQueue); return ndisStatus; } // Only support single MPDU per MSDU MPASSERT(reservedMsdu->MpduCount == 1); // Update the next descriptor to reserve MP_INCREMENT_LIMIT_UNSAFE(TxQueue->NextToReserve, (LONG)TxQueue->NumMSDUAllocated); HW_RESERVE_TX_MSDU(reservedMsdu, Packet); return NDIS_STATUS_SUCCESS; } VOID HwProcessReservedTxPackets( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue, _In_ LONG StartIndex, _In_ ULONG NumTxReady, _In_ ULONG SendFlags ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PHW_TX_MSDU hwMsdu; ULONG i = 0; KIRQL oldIrql = 0; BOOLEAN dispatchLevel = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags); if (NumTxReady != 0) { HW_TX_ACQUIRE_QUEUE_LOCK(Hw, TxQueue, dispatchLevel); // // Process each of the reserved MSDUs // for (i = 0; i < NumTxReady; i++) { hwMsdu = &TxQueue->MSDUArray[(StartIndex + i) % (TxQueue->NumMSDUAllocated)]; ndisStatus = HwPrepareTxMSDUForSend(Hw, TxQueue, hwMsdu); if (ndisStatus != NDIS_STATUS_SUCCESS) { // // We cannot send this packet. Fail it during SendComplete // MpTrace(COMP_SEND, DBG_SERIOUS, ("Could not prepare TX MSDU %p for sending\n", hwMsdu)); hwMsdu->WaitForSendToComplete = FALSE; hwMsdu->FailedDuringSend = TRUE; } } HW_TX_RELEASE_QUEUE_LOCK(Hw, TxQueue, dispatchLevel); // // Now go through our list of packets and request scatter gather for those // that need it // if (!dispatchLevel) NDIS_RAISE_IRQL_TO_DISPATCH(&oldIrql); // Scatter gather must be called at Dispatch for (i = 0; i < NumTxReady; i++) { hwMsdu = &TxQueue->MSDUArray[(StartIndex + i) % (TxQueue->NumMSDUAllocated)]; if (hwMsdu->FailedDuringSend == FALSE) { // If necessary, call NDIS to request scatter gather resources for this packet if (HW_TX_MSDU_IS_INTERNAL(hwMsdu)) { // For internal packets, we dont do scatter gather it is essentially // ready for seng HW_TX_MSDU_SET_FLAG(hwMsdu, HW_TX_MSDU_IS_READY_FOR_SEND); } else { // We cannot send yet. We need to do scatter gather and send // once SG completes HwPerformScatterGather(Hw, hwMsdu); } } else { // These packets have failed. But we set the Ready for send flag // on the packets irrespective HW_TX_MSDU_SET_FLAG(hwMsdu, HW_TX_MSDU_IS_READY_FOR_SEND); } } if (!dispatchLevel) NDIS_LOWER_IRQL(oldIrql, DISPATCH_LEVEL); } // // Now we have either finished prepared the packets for sending or have // submitted them to NDIS for scatter gather. Now we try to determine if we // can submit them to the HAL // if (!HW_TEST_ADAPTER_STATUS(Hw, HW_CANNOT_SEND_FLAGS)) { HwSubmitReadyMSDUs(Hw, TxQueue, (BOOLEAN)dispatchLevel); } } __inline USHORT HwComputeDot11TxTime( _In_ USHORT FrameLength, _In_ ULONG DataRate, _In_ BOOLEAN ManagementFrame, _In_ BOOLEAN ShortPreamble ) { USHORT frameTime; if(ManagementFrame || !ShortPreamble) { // long preamble frameTime = (USHORT)(144+48+(FrameLength*8*2/DataRate)); } else { // Short preamble frameTime = (USHORT)(72+24+(FrameLength*8*2/DataRate)); } if( ( FrameLength*8*2 % DataRate ) != 0 ) frameTime ++; return frameTime; } __inline USHORT HwComputeTxTime( _In_ PHW_TX_MSDU Msdu, _In_ DOT11_FRAME_TYPE FrameType, _In_ ULONG FragmentLength, _In_ BOOLEAN LastFragment ) { BOOLEAN isManagement = (FrameType == DOT11_FRAME_TYPE_MANAGEMENT) ? TRUE : FALSE; USHORT AckCtsTime; AckCtsTime = HwComputeDot11TxTime(sAckCtsLng / 8, Msdu->TxRateTable[0], isManagement, Msdu->UseShortPreamble); if (LastFragment) { return (aSifsTime + AckCtsTime); } else { return (3 * aSifsTime + 2 * AckCtsTime + HwComputeDot11TxTime((USHORT)(FragmentLength + sCrcLng), Msdu->TxRateTable[0], isManagement, Msdu->UseShortPreamble ) ); } } __inline HW_TX_CIPHER_SETTING HwDetermineCipherSettings( _In_ PHW_MAC_CONTEXT MacContext, _In_ PHW_PEER_NODE PeerNode, _In_ PHW_TX_MSDU Msdu, _In_ PDOT11_MAC_HEADER FragmentHeader ) { UNREFERENCED_PARAMETER(PeerNode); if (MacContext->Hw->MacState.SafeModeEnabled) { // If we are in safe mode we never encrypt return HW_TX_NEVER_ENCRYPT; } // // Else, if encryption is enabled determine wheter or not we should be encrypting // if (MacContext->UnicastCipher != DOT11_CIPHER_ALGO_NONE) { if (FragmentHeader->FrameControl.Type == DOT11_FRAME_TYPE_DATA) { // Return the cipher settings from the send context switch(MP_TX_MSDU_SEND_CONTEXT(Msdu->MpMsdu)->usExemptionActionType) { case DOT11_EXEMPT_NO_EXEMPTION: // // We want to encrypt this frame. // return HW_TX_CAN_ENCRYPT; break; case DOT11_EXEMPT_ALWAYS: // // We don't encrypt this frame. // return HW_TX_NEVER_ENCRYPT; break; case DOT11_EXEMPT_ON_KEY_MAPPING_KEY_UNAVAILABLE: // // We encrypt this frame if and only if a key mapping key is set. // return HW_TX_ENCRYPT_IF_KEY_MAPPING_KEY_AVAILABLE; break; default: MPASSERT(FALSE); return HW_TX_CAN_ENCRYPT; break; } } else if (FragmentHeader->FrameControl.WEP) { // Management packets that need encryption for cases like shared key authentication return HW_TX_CAN_ENCRYPT; } } // // All other packets are never encrypted return HW_TX_NEVER_ENCRYPT; } PHW_KEY_ENTRY HwFindEncryptionKey( _In_ PHW_MAC_CONTEXT MacContext, _In_ PHW_PEER_NODE PeerNode, _In_ PHW_TX_MSDU Msdu, _In_ PDOT11_MAC_HEADER FragmentHeader, _In_ HW_TX_CIPHER_SETTING CipherSettings ) { PHW_KEY_ENTRY keyEntry = NULL; ULONG index = 0; NDIS_STATUS ndisStatus; UNREFERENCED_PARAMETER(PeerNode); // // Find the key that would be used to encrypt the frame if the frame were to be encrypted // For unicast frame, search the matching key in the key mapping table first. If not found, // use default key. For multicast frame, only use the default key. // if (!Msdu->MulticastDestination) { ndisStatus = HwFindKeyMappingKeyIndex(MacContext, FragmentHeader->Address1, FALSE, &index); if (ndisStatus == NDIS_STATUS_SUCCESS) { keyEntry = &MacContext->KeyTable[index]; return keyEntry; } } if (CipherSettings == HW_TX_ENCRYPT_IF_KEY_MAPPING_KEY_AVAILABLE) { // We are only supposed to use a key mapping key for encryption. We didnt find // one. Return return NULL; } // // Return the corresponding default key // keyEntry = &MacContext->KeyTable[MacContext->DefaultKeyIndex]; if (!keyEntry->Key.Valid) { // // Check if we would be using the private key table // if ((Msdu->MulticastDestination) && (MacContext->BssType == dot11_BSS_type_independent) && (MacContext->AuthAlgorithm == DOT11_AUTH_ALGO_RSNA_PSK) ) { // Check the private key table since we didnt put the TX default // key into the regular key table keyEntry = &PeerNode->PrivateKeyTable[MacContext->DefaultKeyIndex]; if (!keyEntry->Key.Valid) { return NULL; } } else { return NULL; } } return keyEntry; } NDIS_STATUS HwSetupTxCipher( _In_ PHW Hw, _In_ PHW_MAC_CONTEXT MacContext, _In_ PHW_TX_MSDU Msdu, _In_ PHW_TX_MPDU Mpdu, _In_ PHW_KEY_ENTRY Key ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PNICKEY nicKey; PMDL lastMdl = NULL; BOOLEAN doSoftwareEncryption = FALSE; PNET_BUFFER netBuffer = NULL; ULONG headerSize; ULONG remainingDataLength = 0, byteCount = 0; PUCHAR encryptionIV; PDOT11_MGMT_DATA_MAC_HEADER fragmentHeader; UCHAR keyId; if (HalGetEncryptionCapabilities(Hw->Hal) & HAL_ENCRYPTION_RESERVE_IV_FIELD) { do { Mpdu->RetreatedSize = 0; Mpdu->MICMdlAdded = FALSE; Mpdu->LastMdlByteCount = 0; Mpdu->MICMdl = NULL; nicKey = &Key->Key; // // Find the size of header and IV field. // fragmentHeader = MP_TX_MPDU_DATA(Mpdu->MpMpdu, sizeof(DOT11_MGMT_DATA_MAC_HEADER)); if (fragmentHeader) { headerSize = (fragmentHeader->FrameControl.FromDS && fragmentHeader->FrameControl.ToDS) ? sizeof(DOT11_DATA_LONG_HEADER) : sizeof(DOT11_DATA_SHORT_HEADER); } else { headerSize = 0; } if (nicKey->AlgoId == DOT11_CIPHER_ALGO_TKIP || nicKey->AlgoId == DOT11_CIPHER_ALGO_CCMP) { // These ciphers are not supported for internal sends MPASSERT(!HW_TX_MSDU_IS_INTERNAL(Msdu)); Mpdu->RetreatedSize = 8; } else { Mpdu->RetreatedSize = 4; } // // The IV is saved at different locations for internal and external packets // identify the location // if (!HW_TX_MSDU_IS_INTERNAL(Msdu)) { // Packet coming from the OS - We use the backfill // for the IV, etc netBuffer = MP_TX_MPDU_WRAPPED_NB(Mpdu->MpMpdu); // // For TKIP, we need to calculate MIC and append it at the end of MSDU. // For WPA2PSK adhoc, we need to do software encryption. Also allocate // a MIC, but don't do MIC calculation. // if ((nicKey->AlgoId == DOT11_CIPHER_ALGO_TKIP) || ((nicKey->AlgoId == DOT11_CIPHER_ALGO_CCMP) && (Msdu->MulticastDestination) && (MacContext->BssType == dot11_BSS_type_independent) && (MacContext->AuthAlgorithm == DOT11_AUTH_ALGO_RSNA_PSK))) { // Allocate the MDL for the MIC Mpdu->MICMdl = NdisAllocateMdl(MacContext->Hw->MiniportAdapterHandle, Mpdu->MICData, 8); if (!Mpdu->MICMdl) { ndisStatus = NDIS_STATUS_RESOURCES; MpTrace(COMP_SEND, DBG_SERIOUS, ("Unable to allocate MDL for MIC\n")); break; } // // Calculate MIC // if (nicKey->AlgoId == DOT11_CIPHER_ALGO_TKIP) { ndisStatus = HwCalculateTxMIC(netBuffer, 0, nicKey->TxMICKey, Mpdu->MICData); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_SEND, DBG_SERIOUS, ("Unable to calculate TX MIC\n")); break; } } else { doSoftwareEncryption = TRUE; } // // Chain the new MDL at the end of the NET_BUFFER. // lastMdl = NET_BUFFER_FIRST_MDL(netBuffer); remainingDataLength = NET_BUFFER_DATA_OFFSET(netBuffer) + NET_BUFFER_DATA_LENGTH(netBuffer); byteCount = MmGetMdlByteCount(lastMdl); while(remainingDataLength > byteCount) { remainingDataLength -= byteCount; lastMdl = lastMdl->Next; byteCount = MmGetMdlByteCount(lastMdl); } Mpdu->LastMdlByteCount = byteCount; Mpdu->LastMdl = lastMdl; NdisAdjustMdlLength(lastMdl, remainingDataLength); // Add the MIC Mdl behind the last MDL Mpdu->MICMdl->Next = lastMdl->Next; lastMdl->Next = Mpdu->MICMdl; // Set the flag so that we will clean this up on completion Mpdu->MICMdlAdded = TRUE; NET_BUFFER_DATA_LENGTH(netBuffer) += 8; } // // OS must guarantee 8 available bytes for retreat. WEP IV will use 4 bytes, // CCMP/TKIP IV will use 8 bytes. // MPASSERT(NET_BUFFER_DATA_OFFSET(netBuffer) >= HW11_REQUIRED_BACKFILL_SIZE); MPASSERT(NET_BUFFER_FIRST_MDL(netBuffer) == NET_BUFFER_CURRENT_MDL(netBuffer)); // Note that because we had enough space for retreat and we are at the // first MDL, the retreated portion is still in the first MDL. ndisStatus = NdisRetreatNetBufferDataStart(netBuffer, Mpdu->RetreatedSize, 0, NULL ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_SEND, DBG_SERIOUS, ("NdisRetreatNetBufferDataStart failed for TX IV \n")); ndisStatus = NDIS_STATUS_RESOURCES; Mpdu->RetreatedSize = 0; break; } } else { // Internal packets - There is extra backfill in the // send buffer. Use that for the IV MPASSERT(Mpdu->MpMpdu->InternalSendBuffer < Mpdu->MpMpdu->InternalSendDataStart); // Move the data start behind and change the total data length Mpdu->MpMpdu->InternalSendDataStart -= Mpdu->RetreatedSize; Mpdu->MpMpdu->InternalSendLength = Mpdu->MpMpdu->InternalSendLength + Mpdu->RetreatedSize; } // // We move the 802.11 header to make room for encryption IV // Note: cannot call NdisMoveMemory since source and destination overlaps. // fragmentHeader = (PDOT11_MGMT_DATA_MAC_HEADER) (((PCHAR)fragmentHeader) - Mpdu->RetreatedSize); RtlMoveMemory(fragmentHeader, ((PCHAR)fragmentHeader) + Mpdu->RetreatedSize, headerSize); // // Now initialize the IV field. // encryptionIV = Add2Ptr(fragmentHeader, headerSize); keyId = Key->PeerKeyIndex; if (keyId >= DOT11_MAX_NUM_DEFAULT_KEY) keyId = 0; ndisStatus = NDIS_STATUS_SUCCESS; switch (nicKey->AlgoId) { case DOT11_CIPHER_ALGO_WEP40: case DOT11_CIPHER_ALGO_WEP104: encryptionIV[0] = (UCHAR)(Key->IV & 0xff); encryptionIV[1] = (UCHAR)((Key->IV >> 8) & 0xff); encryptionIV[2] = (UCHAR)((Key->IV >> 16) & 0xff); encryptionIV[3] = (keyId << 6); Key->IV++; break; case DOT11_CIPHER_ALGO_CCMP: encryptionIV[0] = (UCHAR)(Key->PN & 0xff); encryptionIV[1] = (UCHAR)((Key->PN >> 8) & 0xff); encryptionIV[2] = 0; encryptionIV[3] = 0x20 | (keyId << 6); encryptionIV[4] = (UCHAR)((Key->PN >> 16) & 0xff); encryptionIV[5] = (UCHAR)((Key->PN >> 24) & 0xff); encryptionIV[6] = (UCHAR)((Key->PN >> 32) & 0xff); encryptionIV[7] = (UCHAR)((Key->PN >> 40) & 0xff); Key->PN++; if (doSoftwareEncryption) { // Not supported for internal sends MPASSERT(!HW_TX_MSDU_IS_INTERNAL(Msdu)); ndisStatus = HwEncryptCCMP(Hw, Key->hCNGKey, netBuffer); // Encryption is already done. Hardware does not // need to do it Msdu->SendEncrypted = FALSE; } break; case DOT11_CIPHER_ALGO_TKIP: encryptionIV[0] = (UCHAR)((Key->TSC >> 8) & 0xff); encryptionIV[1] = (encryptionIV[0] | 0x20) & 0x7f; encryptionIV[2] = (UCHAR)(Key->TSC & 0xff); encryptionIV[3] = 0x20 | (keyId << 6); encryptionIV[4] = (UCHAR)((Key->TSC >> 16) & 0xff); encryptionIV[5] = (UCHAR)((Key->TSC >> 24) & 0xff); encryptionIV[6] = (UCHAR)((Key->TSC >> 32) & 0xff); encryptionIV[7] = (UCHAR)((Key->TSC >> 40) & 0xff); Key->TSC++; break; case DOT11_CIPHER_ALGO_WEP: // this should not be there. default: MPASSERT(FALSE); break; } }while (FALSE); if (ndisStatus != NDIS_STATUS_SUCCESS) { if (lastMdl) { lastMdl->Next = NULL; NET_BUFFER_DATA_LENGTH(netBuffer) -= 8; } if (Mpdu->MICMdl) { NdisFreeMdl(Mpdu->MICMdl); Mpdu->MICMdl = NULL; } Mpdu->MICMdlAdded = FALSE; } } return ndisStatus; } VOID HwSubmitReadyMSDUs( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue, _In_ BOOLEAN DispatchLevel ) { PHW_TX_MSDU Msdu; NDIS_STATUS ndisStatus; UNREFERENCED_PARAMETER(DispatchLevel); // Note that when a reset is running, it would conflict with this // code path. Currently this is protected by the AsyncFunction counter in the HW // // Starting from NextTxMSDUToSend, we will try to send, in a thread-safe manner, // as many TX_MSDUs as are ready and possible to send. // Msdu = &TxQueue->MSDUArray[TxQueue->NextToSend]; while (HW_TX_MSDU_TEST_FLAG(Msdu, HW_TX_MSDU_IS_READY_FOR_SEND)) { // // Ofcourse, multiple threads can be here simultaneouly. They all think this // Tx MSDU is the next one to be transmitted and is ready for sending. However, we // want only one thread to do the transmission (to avoid multiple sends of same // packet). All threads will try to become the sender of this packet through the // following Interlocked operation but only one will succeed. // if (HW_ACQUIRE_TX_MSDU_SEND_OWNERSHIP(Msdu)) { // // This TX_MSDU is ready for sending and this thread has won the contention // for ownership of sending. Any thread that fails to win contention will // abondon trying to acquire it and will leave immediately // if (!Msdu->FailedDuringSend) { ndisStatus = HwSubmitMSDU(Hw, TxQueue, Msdu); if (ndisStatus == NDIS_STATUS_RESOURCES) { // The packet could not be sent at this time. An attempt should // be made to resend this packet again later. Stop sending any // more packets to guarantee order of delivery HW_RELEASE_TX_MSDU_SEND_OWNERSHIP(Msdu); MpTrace(COMP_SEND, DBG_LOUD, ("Not enough resources to submit ready MSDU to HAL\n")); break; } if (ndisStatus != NDIS_STATUS_SUCCESS) { // Packet failed sending Msdu->WaitForSendToComplete = FALSE; Msdu->FailedDuringSend = TRUE; } // Update the next Tx descriptor to send MP_INCREMENT_LIMIT_SAFE(TxQueue->NextToSend, TxQueue->NumMSDUAllocated); } else { // We skip over failed packets. We dont need to send them MP_INCREMENT_LIMIT_SAFE(TxQueue->NextToSend, TxQueue->NumMSDUAllocated); } // // Move onto next TX_MSDU to see if it is ready for sending // Msdu = &TxQueue->MSDUArray[TxQueue->NextToSend]; } else { break; } } } ULONG HwGetTxFreeDescNum( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue ) { return TxQueue->NumMSDUAllocated - HalGetTxDescBusyNum(Hw->Hal, TxQueue->HalQueueIndex); } // // Return values: // NDIS_STATUS_SUCCESS: The packet was handed to hardware // NDIS_STATUS_RESOURCES: The packet could not be handed // to hardware due to lack of resources // NDIS_STATUS_FAILURE: Send failed due to reasons other // than low Hw resources // NDIS_STATUS HwSubmitMSDU( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue, _In_ PHW_TX_MSDU Msdu ) { BOOLEAN UseCoalesce; ULONG NumTxDescNeeded, NumTxDescAvailable; NDIS_STATUS ndisStatus; BOOLEAN CanTransmit = FALSE; NumTxDescNeeded = HwCountTxResourceNeeded(Msdu, &UseCoalesce); NumTxDescAvailable = HwGetTxFreeDescNum(Hw, TxQueue); if (NumTxDescAvailable >= NumTxDescNeeded) { // // Enough descriptors to send the packet // CanTransmit = TRUE; } else { // // Not enough descriptors to send the NET_BUFFER_LIST as is, // but might be able to transmit by coalescing // MpTrace(COMP_SEND, DBG_LOUD, ("Coalescing MSDU. Need %d descriptors, %d available\n", NumTxDescNeeded, NumTxDescAvailable) ); NumTxDescNeeded = Msdu->SGElementListCount; // One Desc per fragment if (NumTxDescAvailable > NumTxDescNeeded) { UseCoalesce = TRUE; CanTransmit = TRUE; } } if (CanTransmit) { if (HwAwake(Hw, FALSE) == FALSE) { // Radio is OFF, drop this packet ndisStatus = NDIS_STATUS_DOT11_POWER_STATE_INVALID; } else { if (UseCoalesce == FALSE) { ndisStatus = HwTransmitMSDU(Hw, TxQueue, Msdu, (USHORT)NumTxDescNeeded); } else { ndisStatus = HwTransmitMSDUCoalesce(Hw, TxQueue, Msdu, (USHORT)NumTxDescNeeded); if (ndisStatus != NDIS_STATUS_SUCCESS && ndisStatus != NDIS_STATUS_RESOURCES) { ndisStatus = NDIS_STATUS_FAILURE; // Just a failure } } } } else { MpTrace(COMP_TESTING, DBG_LOUD, ("Tx Queued: Needed %d, Available %d\n", NumTxDescNeeded, NumTxDescAvailable) ); ndisStatus = NDIS_STATUS_RESOURCES; } return ndisStatus; } ULONG HwCountTxResourceNeeded( _In_ PHW_TX_MSDU Msdu, _Out_ BOOLEAN *UseCoalesce ) { ULONG i, TotalDescNeeded = 0; USHORT usNumberOfMPDU = 0; SCATTER_GATHER_LIST *pFragList; PNET_BUFFER CurrentNetBuffer; PNET_BUFFER_LIST pNetBufferList = MP_TX_MSDU_WRAPPED_NBL(Msdu->MpMsdu); if (HW_TX_MSDU_IS_INTERNAL(Msdu)) { // For internal packets, we also use the coalesce buffer for sending *UseCoalesce = TRUE; return 1; } // // Initially assume that we do not need coalescing. // *UseCoalesce = FALSE; // // Each NB in the NBL could be a MPDU. However the OS // only gives us a single NET_BUFFER in the NET_BUFFER_LIST. // CurrentNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); while (CurrentNetBuffer != NULL) { pFragList = (PSCATTER_GATHER_LIST) Msdu->SGElementList[usNumberOfMPDU]; if (pFragList->NumberOfElements >= HW_TX_COALESCE_THRESHOLD) { // It is better to coalesce this MSDU if (UseCoalesce != NULL) *UseCoalesce = TRUE; TotalDescNeeded = Msdu->SGElementListCount; goto done; } else { TotalDescNeeded += pFragList->NumberOfElements; } for (i = 0; i < pFragList->NumberOfElements; i++) { // This is the hardware limitation if (pFragList->Elements[i].Length <= 4) { if (UseCoalesce != NULL) *UseCoalesce = TRUE; TotalDescNeeded = Msdu->SGElementListCount; goto done; } } usNumberOfMPDU++; CurrentNetBuffer = NET_BUFFER_NEXT_NB(CurrentNetBuffer); } done: return TotalDescNeeded; } NDIS_STATUS HwTransmitMSDU( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue, _In_ PHW_TX_MSDU Msdu, _In_ USHORT NumTxDescNeeded ) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; USHORT i, j, k; PSCATTER_GATHER_LIST SGList; PSCATTER_GATHER_ELEMENT SGElement; ULONG mdlDataOffset; USHORT totalSGElementCount = 0, mdlSkipCount = 0; PNET_BUFFER currentNetBuffer; PUCHAR header = NULL; ULONG length; USHORT numTxDescUsed; HAL_TX_ITERATOR firstIter, lastIter, descIter; HAL_TX_DESC_INFO descInfo; BOOLEAN firstSegment; PDOT11_MAC_HEADER macHeader; // We may skip some empty MDLs at the start. So the passed in number of // TxDescNeeded is ignored and we compute a new one UNREFERENCED_PARAMETER(NumTxDescNeeded); // Internal packets must be sent via the coalesce code path MPASSERT(!HW_TX_MSDU_IS_INTERNAL(Msdu)); // // Fill a TX_DESC for each scatter gather element for each MPDU // currentNetBuffer = NET_BUFFER_LIST_FIRST_NB(MP_TX_MSDU_WRAPPED_NBL(Msdu->MpMsdu)); for (i = 0; i < Msdu->SGElementListCount; i++) { SGList = (PSCATTER_GATHER_LIST) Msdu->SGElementList[i]; mdlDataOffset = NET_BUFFER_CURRENT_MDL_OFFSET(currentNetBuffer); totalSGElementCount = totalSGElementCount + (USHORT)SGList->NumberOfElements; NdisQueryMdl(NET_BUFFER_FIRST_MDL(currentNetBuffer), &header, &length, NormalPagePriority ); if (!header) return NDIS_STATUS_FAILURE; for (j = 0; j < (USHORT)SGList->NumberOfElements; j++) { // // Get the scatter gather element for this TX_DESC // SGElement = &SGList->Elements[j]; if (mdlDataOffset > 0 && SGElement->Length <= mdlDataOffset) { mdlDataOffset -= SGElement->Length; mdlSkipCount++; } else { break; } } currentNetBuffer = NET_BUFFER_NEXT_NB(currentNetBuffer); } numTxDescUsed = totalSGElementCount - mdlSkipCount; // Reserve TX descriptor chain for this send status = HalReserveNextTxDescsForTransmit(Hw->Hal, TxQueue->HalQueueIndex, NumTxDescNeeded, &firstIter); if (status != NDIS_STATUS_SUCCESS) return status; descIter = firstIter; lastIter = firstIter; // Process each NET_BUFFER currentNetBuffer = NET_BUFFER_LIST_FIRST_NB(MP_TX_MSDU_WRAPPED_NBL(Msdu->MpMsdu)); for (i = 0; i < Msdu->SGElementListCount; i++) { SGList = (PSCATTER_GATHER_LIST) Msdu->SGElementList[i]; mdlDataOffset = NET_BUFFER_CURRENT_MDL_OFFSET(currentNetBuffer); firstSegment = TRUE; NdisQueryBufferSafe(NET_BUFFER_FIRST_MDL(currentNetBuffer), &header, &length, NormalPagePriority); MPASSERT(header != NULL); _Analysis_assume_( header != NULL ); header += mdlDataOffset; for (j = 0; j < (USHORT)SGList->NumberOfElements; j++) { // // Get the scatter gather element for this TX_DESC // SGElement = &SGList->Elements[j]; if (mdlDataOffset > 0 && SGElement->Length <= mdlDataOffset) { mdlDataOffset -= SGElement->Length; } else { descInfo.MacHeader = header; descInfo.FirstSeg = firstSegment; descInfo.LastSeg = (BOOLEAN)(j == (USHORT)SGList->NumberOfElements - 1); descInfo.PhysicalAddressLow = SGElement->Address.LowPart + mdlDataOffset; descInfo.BufferLen = SGElement->Length - mdlDataOffset; descInfo.PacketLen = NET_BUFFER_DATA_LENGTH(currentNetBuffer); for (k = 0; k < HW_TX_RATE_TABLE_SIZE; k++) descInfo.RateTable[k] = Msdu->TxRateTable[k]; descInfo.Multicast = Msdu->MulticastDestination; descInfo.ShortPreamble = Msdu->UseShortPreamble; descInfo.MoreFrag = (BOOLEAN) ((i != Msdu->SGElementListCount - 1) && (j != SGList->NumberOfElements - 1)); descInfo.RTSEnabled = Msdu->RTSEnabled; descInfo.CTSToSelf = Msdu->CTSToSelfEnabled; descInfo.RTSCTSRate = 22; // CTS to self is always 11 Mbpa descInfo.NoEncrypt = !Msdu->SendEncrypted; if (Msdu->SendEncrypted) { // Set the Key Index MPASSERT(Msdu->Key != NULL); descInfo.KeyIndex = Msdu->Key->NicKeyIndex; } descInfo.Broadcast = DOT11_IS_BROADCAST((header + FIELD_OFFSET(DOT11_MAC_HEADER, Address1))); descInfo.FirstDescIter = firstIter; // Populate correct packet type descInfo.PacketType = HAL_PACKET_DATA; macHeader = (PDOT11_MAC_HEADER)descInfo.MacHeader; if (macHeader->FrameControl.Type == DOT11_FRAME_TYPE_CONTROL) { if (macHeader->FrameControl.Subtype == DOT11_CTRL_SUBTYPE_PS_POLL) { descInfo.PacketType = HAL_PACKET_PS_POLL; } } else if (macHeader->FrameControl.Type == DOT11_FRAME_TYPE_MANAGEMENT) { if (macHeader->FrameControl.Subtype == DOT11_MGMT_SUBTYPE_PROBE_RESPONSE) { descInfo.PacketType = HAL_PACKET_PROBE_RESPONSE; } } descInfo.EOL = FALSE; descInfo.PsBitSetting = Msdu->PsBitSetting; HalFillTxDescriptor(Hw->Hal, &descInfo, descIter); lastIter = descIter; HalTxIterMoveNext(Hw->Hal, &descIter); // // We only want to reduce MdlDataOffset from the first SG element // mdlDataOffset= 0; firstSegment = FALSE; } } currentNetBuffer = NET_BUFFER_NEXT_NB(currentNetBuffer); } MPASSERT(currentNetBuffer == NULL); // We need this info for send completion Msdu->TotalDescUsed = numTxDescUsed; MPASSERT(numTxDescUsed <= NumTxDescNeeded); Msdu->SendItem.DescNum = numTxDescUsed; Msdu->SendItem.firstIterator = firstIter; Msdu->SendItem.lastIterator = lastIter; MP_RECORD_STRING_3(TxQueue->Tracking_SendRecorder, "SEND: %d %p %p", numTxDescUsed, Msdu->SendItem.firstIterator, Msdu->SendItem.lastIterator ); // It is in send HW_TX_MSDU_SET_FLAG(Msdu, HW_TX_MSDU_IS_IN_SEND); HW_TIMESTAMP_TX_MSDU(Msdu, TxQueue); HW_INCREMENT_HAL_PENDING_TX(TxQueue); HalTransmit(Hw->Hal, &Msdu->SendItem); return NDIS_STATUS_SUCCESS; } NDIS_STATUS HwCopyNBLToBuffer( _In_ PNET_BUFFER_LIST NetBufferList, _Out_writes_bytes_(MAX_TX_RX_PACKET_SIZE) PUCHAR pDest, _Out_ PULONG BytesCopied ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PNET_BUFFER CurrentNetBuffer; PMDL CurrentMdl; LONG CurrLength; ULONG TotalLength; PUCHAR pSrc; __try { *BytesCopied = 0; NdisZeroMemory(pDest, MAX_TX_RX_PACKET_SIZE); for (CurrentNetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList); CurrentNetBuffer != NULL; CurrentNetBuffer = NET_BUFFER_NEXT_NB(CurrentNetBuffer)) { CurrentMdl = NET_BUFFER_CURRENT_MDL(CurrentNetBuffer); pSrc = MmGetSystemAddressForMdlSafe(CurrentMdl, NormalPagePriority); if (!pSrc) { ndisStatus = NDIS_STATUS_RESOURCES; __leave; } // // For the first MDL with data, we need to skip the free space // pSrc += NET_BUFFER_CURRENT_MDL_OFFSET(CurrentNetBuffer); CurrLength = MmGetMdlByteCount(CurrentMdl) - NET_BUFFER_CURRENT_MDL_OFFSET(CurrentNetBuffer); if (CurrLength > 0) { TotalLength = ((ULONG) CurrLength) + *BytesCopied; if ((TotalLength < ((ULONG) CurrLength)) || (TotalLength > HW11_MAX_FRAME_SIZE)) { MpTrace(COMP_SEND, DBG_SERIOUS, ("%s: The total MSDU size (%d) is greater than max " "allowed (%d)\n", __FUNCTION__, CurrLength + *BytesCopied, HW11_MAX_FRAME_SIZE)); ndisStatus = NDIS_STATUS_INVALID_LENGTH; break; /* break out for loop */ } // // Copy the data. // NdisMoveMemory(pDest, pSrc, CurrLength); *BytesCopied += CurrLength; pDest += CurrLength; // MPASSERTMSG("The total MSDU size is greater than max allowed\n", // *BytesCopied <= MP_802_11_MAX_FRAME_SIZE); } CurrentMdl = NDIS_MDL_LINKAGE(CurrentMdl); while (CurrentMdl) { pSrc = MmGetSystemAddressForMdlSafe(CurrentMdl, NormalPagePriority); if (!pSrc) { ndisStatus = NDIS_STATUS_RESOURCES; __leave; } CurrLength = MmGetMdlByteCount(CurrentMdl); if (CurrLength > 0) { TotalLength = ((ULONG) CurrLength) + *BytesCopied; if ((TotalLength < ((ULONG) CurrLength)) || (TotalLength > HW11_MAX_FRAME_SIZE)) { MpTrace(COMP_SEND, DBG_SERIOUS, ("%s: The total MSDU size (%d) is greater than max " "allowed (%d)\n", __FUNCTION__, CurrLength + *BytesCopied, HW11_MAX_FRAME_SIZE)); ndisStatus = NDIS_STATUS_INVALID_LENGTH; break; /* break out while and for loop */ } // // Copy the data. // #pragma prefast(suppress:__WARNING_INCORRECT_VALIDATION, "Filed Esp:696") NdisMoveMemory(pDest, pSrc, CurrLength); *BytesCopied += CurrLength; pDest += CurrLength; // MPASSERTMSG("The total MSDU size is greater than max allowed\n", // *BytesCopied <= MP_802_11_MAX_FRAME_SIZE); } CurrentMdl = NDIS_MDL_LINKAGE(CurrentMdl); } } } __finally { } return ndisStatus; } NDIS_STATUS HwTransmitMSDUCoalesce( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue, _In_ PHW_TX_MSDU Msdu, _In_ USHORT NumTxDescNeeded ) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; ULONG bytesCopied; PMP_TX_MPDU mpMpdu; HAL_TX_ITERATOR firstIter, descIter; HAL_TX_DESC_INFO descInfo; USHORT i, j; ULONG dataOffset; PDOT11_MAC_HEADER macHeader; // // Coalesce all the data in the coalesce buffer // if (HW_TX_MSDU_IS_INTERNAL(Msdu)) { // Copy the internal buffer into this packet mpMpdu = MP_TX_MSDU_MPDU_AT(Msdu->MpMsdu, 0); bytesCopied = MP_TX_MPDU_LENGTH(mpMpdu); NdisMoveMemory(Msdu->BufferVa, MP_TX_MPDU_DATA(mpMpdu, bytesCopied), bytesCopied); } else { status = HwCopyNBLToBuffer(MP_TX_MSDU_WRAPPED_NBL(Msdu->MpMsdu), Msdu->BufferVa, &bytesCopied ); } if (status != NDIS_STATUS_SUCCESS) { // // Failed to coalesce this packet. We will fail the MP_TX_MSDU // MpTrace(COMP_SEND, DBG_NORMAL, ("Failed to Coalesce TX_MSDU into single buffer\n")); return status; } // Reserve TX descriptor chain for this send status = HalReserveNextTxDescsForTransmit(Hw->Hal, TxQueue->HalQueueIndex, NumTxDescNeeded, &firstIter); if (status != NDIS_STATUS_SUCCESS) { return status; } // // Fill a TX_DESC for each scatter gather element for each MPDU // if (NumTxDescNeeded > 1) { // // We will start from the 2nd MPDU (if present) and will fill in the // first one when we are done with all the others. 2nd MPDU means 2nd NB // We will also need to skip the data offset (free space) and data length // of the first NetBuffer as well. // MPASSERT(Msdu->MpduCount > 1); MPASSERT(!HW_TX_MSDU_IS_INTERNAL(Msdu)); mpMpdu = MP_TX_MSDU_MPDU_AT(Msdu->MpMsdu, 0); dataOffset = NET_BUFFER_DATA_LENGTH(MP_TX_MPDU_WRAPPED_NB(mpMpdu)); } else { // // There is only on MPDU so only one TX_DESC needed // We will not enter the loop and directly fill the // TX_DESC for the first NetBuffer // dataOffset = 0; } descIter = firstIter; for (i = 1; i < NumTxDescNeeded; i++) { mpMpdu = MP_TX_MSDU_MPDU_AT(Msdu->MpMsdu, i); // // Get the next descriptor we will be filling. We leave the first // descriptor for later use // HalTxIterMoveNext(Hw->Hal, &descIter); descInfo.MacHeader = Msdu->BufferVa + dataOffset; descInfo.FirstSeg = TRUE; descInfo.LastSeg = TRUE; descInfo.PhysicalAddressLow = Msdu->BufferPa.LowPart + dataOffset; descInfo.BufferLen = MP_TX_MPDU_LENGTH(mpMpdu); descInfo.PacketLen = MP_TX_MPDU_LENGTH(mpMpdu); for (j = 0; j < HW_TX_RATE_TABLE_SIZE; j++) descInfo.RateTable[j] = Msdu->TxRateTable[j]; descInfo.Multicast = Msdu->MulticastDestination; descInfo.ShortPreamble = Msdu->UseShortPreamble; descInfo.MoreFrag = (BOOLEAN)(i != NumTxDescNeeded - 1); descInfo.RTSEnabled = Msdu->RTSEnabled; descInfo.CTSToSelf = Msdu->CTSToSelfEnabled; descInfo.RTSCTSRate = 22; // 11 Mbps descInfo.NoEncrypt = !Msdu->SendEncrypted; if (Msdu->SendEncrypted) { // Set the Key Index MPASSERT(Msdu->Key != NULL); descInfo.KeyIndex = Msdu->Key->NicKeyIndex; } descInfo.Broadcast = DOT11_IS_BROADCAST((Msdu->BufferVa + dataOffset + FIELD_OFFSET(DOT11_MAC_HEADER, Address1))); descInfo.FirstDescIter = firstIter; // Populate correct packet type descInfo.PacketType = HAL_PACKET_DATA; macHeader = (PDOT11_MAC_HEADER)descInfo.MacHeader; if (macHeader->FrameControl.Type == DOT11_FRAME_TYPE_CONTROL) { if (macHeader->FrameControl.Subtype == DOT11_CTRL_SUBTYPE_PS_POLL) { descInfo.PacketType = HAL_PACKET_PS_POLL; } } else if (macHeader->FrameControl.Type == DOT11_FRAME_TYPE_MANAGEMENT) { if (macHeader->FrameControl.Subtype == DOT11_MGMT_SUBTYPE_PROBE_RESPONSE) { descInfo.PacketType = HAL_PACKET_PROBE_RESPONSE; } } descInfo.EOL = FALSE; descInfo.PsBitSetting = Msdu->PsBitSetting; HalFillTxDescriptor(Hw->Hal, &descInfo, descIter); // // One more TX_DESC used up. // Move to the next MPDU // dataOffset += MP_TX_MPDU_LENGTH(mpMpdu); } // // Now fill out TX_DESC for the first MPDU // mpMpdu = MP_TX_MSDU_MPDU_AT(Msdu->MpMsdu, 0); descInfo.MacHeader = Msdu->BufferVa; descInfo.FirstSeg = TRUE; descInfo.LastSeg = TRUE; descInfo.PhysicalAddressLow = Msdu->BufferPa.LowPart; descInfo.BufferLen = MP_TX_MPDU_LENGTH(mpMpdu); descInfo.PacketLen = MP_TX_MPDU_LENGTH(mpMpdu); for (j = 0; j < HW_TX_RATE_TABLE_SIZE; j++) descInfo.RateTable[j] = Msdu->TxRateTable[j]; descInfo.Multicast = Msdu->MulticastDestination; descInfo.ShortPreamble = Msdu->UseShortPreamble; descInfo.MoreFrag = (BOOLEAN)(NumTxDescNeeded > 1); descInfo.RTSEnabled = Msdu->RTSEnabled; descInfo.CTSToSelf = Msdu->CTSToSelfEnabled; descInfo.RTSCTSRate = 22; // 11 Mbps descInfo.NoEncrypt = !Msdu->SendEncrypted; if (Msdu->SendEncrypted) { // Set the Key Index MPASSERT(Msdu->Key != NULL); descInfo.KeyIndex = Msdu->Key->NicKeyIndex; } descInfo.Broadcast = DOT11_IS_BROADCAST((Msdu->BufferVa + FIELD_OFFSET(DOT11_MAC_HEADER, Address1))); descInfo.FirstDescIter = firstIter; // Populate correct packet type descInfo.PacketType = HAL_PACKET_DATA; macHeader = (PDOT11_MAC_HEADER)descInfo.MacHeader; if (macHeader->FrameControl.Type == DOT11_FRAME_TYPE_CONTROL) { if (macHeader->FrameControl.Subtype == DOT11_CTRL_SUBTYPE_PS_POLL) { descInfo.PacketType = HAL_PACKET_PS_POLL; } } else if (macHeader->FrameControl.Type == DOT11_FRAME_TYPE_MANAGEMENT) { if (macHeader->FrameControl.Subtype == DOT11_MGMT_SUBTYPE_PROBE_RESPONSE) { descInfo.PacketType = HAL_PACKET_PROBE_RESPONSE; } } descInfo.EOL = FALSE; descInfo.PsBitSetting = Msdu->PsBitSetting; HalFillTxDescriptor(Hw->Hal, &descInfo, descIter); // We need this info for send completion Msdu->TotalDescUsed = NumTxDescNeeded; Msdu->SendItem.DescNum = NumTxDescNeeded; Msdu->SendItem.firstIterator = firstIter; Msdu->SendItem.lastIterator = descIter; MP_RECORD_STRING_3(TxQueue->Tracking_SendRecorder, "SEND: %d %p %p", NumTxDescNeeded, Msdu->SendItem.firstIterator, Msdu->SendItem.lastIterator ); // It is in send HW_TX_MSDU_SET_FLAG(Msdu, HW_TX_MSDU_IS_IN_SEND); HW_INCREMENT_HAL_PENDING_TX(TxQueue); HalTransmit(Hw->Hal, &Msdu->SendItem); return NDIS_STATUS_SUCCESS; } NDIS_STATUS HwTransmitBeacon( _In_ PHW Hw, _In_ PHW_TX_MSDU Msdu ) { HAL_TX_ITERATOR descIterator; HAL_TX_DESC_INFO descInfo; NDIS_STATUS ndisStatus; BOOLEAN autoResend = TRUE; UCHAR i; ndisStatus = HalReserveNextTxDescsForTransmit(Hw->Hal, BEACON_QUEUE, 1, &descIterator); if (ndisStatus != NDIS_STATUS_SUCCESS) { return ndisStatus; } if (Hw->MacState.OperationMode == DOT11_OPERATION_MODE_EXTENSIBLE_AP) { // For AP mode, we create a fresh beacon everytime autoResend = FALSE; } //MpTrace(COMP_TESTING, DBG_SERIOUS, ("BEACON: Transmit Beacon 0x%08x", (ULONG)descIterator)); descInfo.MacHeader = Msdu->BufferVa; descInfo.FirstSeg = TRUE; descInfo.LastSeg = TRUE; descInfo.PhysicalAddressLow = Msdu->BufferPa.LowPart; descInfo.BufferLen = Msdu->TotalMSDULength; descInfo.PacketLen = Msdu->TotalMSDULength; for (i = 0; i < HW_TX_RATE_TABLE_SIZE; i++) descInfo.RateTable[i] = Msdu->TxRateTable[i]; descInfo.Multicast = TRUE; descInfo.ShortPreamble = FALSE; descInfo.MoreFrag = FALSE; descInfo.RTSEnabled = FALSE; descInfo.CTSToSelf = FALSE; descInfo.RTSCTSRate = 0; descInfo.NoEncrypt = TRUE; descInfo.Broadcast = TRUE; descInfo.FirstDescIter = descIterator; descInfo.PacketType = HAL_PACKET_BEACON; descInfo.EOL = autoResend ; descInfo.PsBitSetting = TxDescPsBitClear; descInfo.KeyIndex = 0; HalFillTxDescriptor(Hw->Hal, &descInfo, descIterator); Msdu->SendItem.DescNum = 1; Msdu->SendItem.firstIterator = descIterator; Msdu->SendItem.lastIterator = descIterator; HalTransmitBeacon(Hw->Hal, &Msdu->SendItem, autoResend ); return NDIS_STATUS_SUCCESS; } BOOLEAN HwTxMSDUIsComplete( _In_ PHW Hw, _In_ PHW_TX_MSDU Msdu ) { ULONG i; HAL_TX_DESC_STATUS currentDescStatus; HAL_TX_ITERATOR descIter; BOOLEAN msduFailed = FALSE; PDOT11_PHY_FRAME_STATISTICS phyStats; if (!HW_TX_MSDU_TEST_FLAG(Msdu, HW_TX_MSDU_HAS_SENDER)) { // The packet is not yet been "done" by the TX path. We cannot send // complete this yet return FALSE; } if (Msdu->FailedDuringSend) { MPASSERT(Msdu->MpMsdu->MacContext != NULL); // // Failed during send. We can complete this packet // if (Msdu->MulticastDestination) Msdu->MpMsdu->MacContext->MulticastCounters.ullTransmittedFailureFrameCount++; else Msdu->MpMsdu->MacContext->UnicastCounters.ullTransmittedFailureFrameCount++; return TRUE; } else if (!HW_TX_MSDU_TEST_FLAG(Msdu, HW_TX_MSDU_IS_IN_SEND)) { // Packet has not yet been submitted to the HAL return FALSE; } MPASSERT(Msdu->WaitForSendToComplete); if (HalGetTxDescBusyNum(Hw->Hal, Hw->TxInfo.TxQueue[Msdu->QueueID].HalQueueIndex) > 0) { if (HalIsSendItemCompleted(Hw->Hal, &Msdu->SendItem)) { Msdu->SucceedMPDUCount = 0; phyStats = &Hw->Stats.PhyCounters[Msdu->PhyId]; MP_RECORD_STRING_2(Hw->TxInfo.TxQueue[Msdu->QueueID].Tracking_SendRecorder, "COMP: %p %p", Msdu->SendItem.firstIterator, Msdu->SendItem.lastIterator ); descIter = Msdu->SendItem.firstIterator; for (i = 0; i < Msdu->TotalDescUsed; i++) { HalGetTxDescStatus(Hw->Hal, descIter, ¤tDescStatus); if (currentDescStatus.LastSegment) { if (currentDescStatus.TransmitSuccess) { phyStats->ullTransmittedFrameCount++; phyStats->ullTransmittedFragmentCount++; if(Msdu->MulticastDestination) { phyStats->ullMulticastTransmittedFrameCount++; } if(currentDescStatus.RetryCount > 0) { phyStats->ullRetryCount++; if (currentDescStatus.RetryCount > 1) { phyStats->ullMultipleRetryCount++; } } } else { phyStats->ullFailedCount++; msduFailed = TRUE; } HwUpdatePeerTxStatistics(Msdu->MpMsdu->MacContext, Msdu, currentDescStatus ); if(currentDescStatus.RetryCount > 0) { phyStats->ullACKFailureCount += currentDescStatus.RetryCount; } if (Msdu->RTSEnabled) { phyStats->ullRTSFailureCount++; if (currentDescStatus.RtcRetryCount < Msdu->MpMsdu->MacContext->LongRetryLimit) { phyStats->ullRTSSuccessCount++; } } } HalTxIterMoveNext(Hw->Hal, &descIter); } if (msduFailed == FALSE) { Msdu->TxSucceeded = TRUE; if (Msdu->MulticastDestination) Msdu->MpMsdu->MacContext->MulticastCounters.ullTransmittedFrameCount++; else Msdu->MpMsdu->MacContext->UnicastCounters.ullTransmittedFrameCount++; } Msdu->WaitForSendToComplete = FALSE; HW_DECREMENT_HAL_PENDING_TX(&Hw->TxInfo.TxQueue[Msdu->QueueID]); HalReleaseTxDesc(Hw->Hal, Msdu->SendItem.firstIterator, Msdu->SendItem.lastIterator, Msdu->SendItem.DescNum); Msdu->TotalDescUsed = 0; return TRUE; } } return FALSE; } VOID HwFreeTxResources( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue, _In_ PHW_TX_MSDU Msdu ) { PHW_TX_MPDU mpdu; ULONG i; PNET_BUFFER currentNetBuffer; UNREFERENCED_PARAMETER(TxQueue); // // Free the SG lists // for (i = 0; i < Msdu->SGElementListCount; i++) { mpdu = HW_TX_MSDU_MPDU_AT(Msdu, i); currentNetBuffer = MP_TX_MPDU_WRAPPED_NB(mpdu->MpMpdu); MPASSERT(currentNetBuffer != NULL); NdisMFreeNetBufferSGList( Hw->MiniportDmaHandle, Msdu->SGElementList[i], currentNetBuffer ); } // // Free the MPDUs // for (i = 0; i < HW_TX_MSDU_MPDU_COUNT(Msdu); i++) { mpdu = HW_TX_MSDU_MPDU_AT(Msdu, i); // If we retreated for the IV if (mpdu->RetreatedSize > 0) { if (!HW_TX_MSDU_IS_INTERNAL(Msdu)) { NdisAdvanceNetBufferDataStart(MP_TX_MPDU_WRAPPED_NB(mpdu->MpMpdu), mpdu->RetreatedSize, FALSE, NULL ); } else { mpdu->MpMpdu->InternalSendDataStart -= mpdu->RetreatedSize; mpdu->MpMpdu->InternalSendLength = mpdu->MpMpdu->InternalSendLength + mpdu->RetreatedSize; } } // If we appended a MIC MDL if (mpdu->MICMdlAdded) { MPASSERT(!HW_TX_MSDU_IS_INTERNAL(Msdu)); MPASSERT(mpdu->MICMdl != NULL); NdisAdjustMdlLength(mpdu->LastMdl, mpdu->LastMdlByteCount); mpdu->LastMdl->Next = mpdu->MICMdl->Next; NET_BUFFER_DATA_LENGTH(MP_TX_MPDU_WRAPPED_NB(mpdu->MpMpdu)) -= 8; NdisFreeMdl(mpdu->MICMdl); } NdisFreeToNPagedLookasideList(&(Hw->TxInfo.TxMPDULookaside), mpdu); } // Release the MSDU structure HW_RELEASE_TX_MSDU(Msdu); } VOID HwCompleteTxMSDU( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue, _In_ PHW_TX_MSDU Msdu, _In_ BOOLEAN Success ) { if (Success) MP_TX_MSDU_STATUS(Msdu->MpMsdu) = NDIS_STATUS_SUCCESS; else MP_TX_MSDU_STATUS(Msdu->MpMsdu) = NDIS_STATUS_FAILURE; // Free the MPDUs assigned for this MSDU HwFreeTxResources(Hw, TxQueue, Msdu); // Next to complete is incremented MP_INCREMENT_LIMIT_UNSAFE(TxQueue->NextToComplete, (LONG)TxQueue->NumMSDUAllocated); HW_INCREMENT_AVAILABLE_TX_MSDU(TxQueue); } VOID HwCheckSendQueueForCompletion( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue ) { PHW_TX_MSDU msdu; PMP_TX_MSDU mpMsduToComplete = NULL; PHW_MAC_CONTEXT currentMacContext = NULL; ULONG completedCount = 0; PMP_TX_MSDU currentPacket = NULL; NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; LONG firstMsduIndex, numMsduReserved = 0; HW_TX_ACQUIRE_QUEUE_LOCK(Hw, TxQueue, TRUE); msdu = &TxQueue->MSDUArray[TxQueue->NextToComplete]; if ((TxQueue->NumMSDUAvailable < (LONG)(TxQueue->NumMSDUAllocated - 1)) && (HwTxMSDUIsComplete(Hw, msdu))) { // HW_CHECK_TX_MSDU_TIME(msdu, 0); do { MpTrace(COMP_SEND, DBG_LOUD, ("Completing %d on %d\n", TxQueue->NextToComplete, TxQueue->HalQueueIndex)); // Add this to the chain of MSDUs to complete to the upper layer MP_TX_MSDU_NEXT_MSDU(msdu->MpMsdu) = mpMsduToComplete; mpMsduToComplete = msdu->MpMsdu; currentMacContext = MP_TX_MSDU_MAC_CONTEXT(msdu->MpMsdu); // Cleanup and do appropriate completion for the MSDU // // If the packet failed during send, then we report failure to the OS // We do not report failure if the packet failed due to too many retries // HwCompleteTxMSDU(Hw, TxQueue, msdu, !msdu->FailedDuringSend); // Decrement the recount on the MAC HW_REMOVE_MAC_CONTEXT_SEND_REF(currentMacContext, 1); // Next to complete has already been incremented msdu = &TxQueue->MSDUArray[TxQueue->NextToComplete]; completedCount++; } while ((TxQueue->NumMSDUAvailable < (LONG)(TxQueue->NumMSDUAllocated - 1)) && HwTxMSDUIsComplete(Hw, msdu)); // // Since we completed some packets, reset the send ticks // TxQueue->StalledSendTicks = 0; HW_TX_RELEASE_QUEUE_LOCK(Hw, TxQueue, TRUE); HwSendCompletePackets(Hw, mpMsduToComplete, completedCount, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL ); HW_TX_ACQUIRE_QUEUE_LOCK(Hw, TxQueue, TRUE); } // Check if we have any pending packets that should be put on the TX list of this array // This is the first index we would use for sending. From this index, upto // the numMsduReserved are the MSDUs that were prepared by this code and should // be used for sending firstMsduIndex = TxQueue->NextToReserve; while (!MpPacketQueueIsEmpty(&TxQueue->PendingTxQueue) && !HW_TEST_ADAPTER_STATUS(Hw, HW_CANNOT_SEND_FLAGS) && (numMsduReserved < MAX_SEND_MSDU_TO_PROCESS)) { // Remove a packet from the TX queue currentPacket = MP_MSDU_FROM_QUEUE_ENTRY(MpDequeuePacket(&TxQueue->PendingTxQueue)); // // Check if there are any sends sitting in the pending queue. If not // we can process the new ones for sending. Else we should not to avoid // out of order delivery // if (HwCanTransmit(TxQueue)) { // // Lets reserve HW_TX_MSDU for this packet. Reservation guarantees // that no one else will use the reserved TX_MSDU and also avoids // synchronization issues later // ndisStatus = HwReserveTxResources(Hw, TxQueue, currentPacket); if (ndisStatus == NDIS_STATUS_SUCCESS) { numMsduReserved++; } } else { // Cannot send now ndisStatus = NDIS_STATUS_PENDING; } if (ndisStatus != NDIS_STATUS_SUCCESS) { // // Put it back at the head of the pending sends queue // MpQueuePacketPriority(&TxQueue->PendingTxQueue, QUEUE_ENTRY_FROM_MP_MSDU(currentPacket)); break; } } HW_TX_RELEASE_QUEUE_LOCK(Hw, TxQueue, TRUE); // // Process all the packets that we have been queued (this should be called even // if we dont have any new packets since it would forward existing packets to the HW) // HwProcessReservedTxPackets(Hw, TxQueue, firstMsduIndex, numMsduReserved, NDIS_SEND_FLAGS_DISPATCH_LEVEL ); } VOID HwReinitializeSendQueue( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue, _In_ BOOLEAN DispatchLevel ) { ULONG i; PHW_TX_MSDU msdu; PVOID Caller, CallersCaller; // // We save the address of the caller in the pool header, for debugging. // RtlGetCallersAddress(&Caller, &CallersCaller); HW_TX_ACQUIRE_QUEUE_LOCK(Hw, TxQueue, DispatchLevel); // We leave one descriptor empty for easy synchronization TxQueue->NumMSDUAvailable = TxQueue->NumMSDUAllocated - 1; // Reset the MSDUs for (i = 0; i < TxQueue->NumMSDUAllocated; i++) { msdu = &TxQueue->MSDUArray[i]; msdu->Flags = 0; msdu->MpMsdu = NULL; msdu->PeerNode = NULL; msdu->PhyId = DOT11_PHY_ID_ANY; msdu->SGElementListCount = 0; } TxQueue->StalledSendTicks = 0; // Reset the HAL queues if (HalResetTxDescs(Hw->Hal, TxQueue->HalQueueIndex) != NDIS_STATUS_SUCCESS) { // The HAL TX queues did not successfully reset. We do not // reset send from the current descriptor // MpTrace(COMP_INIT_PNP, DBG_SERIOUS, ("HalResetTxDescs for queue %d failed\n", TxQueue->HalQueueIndex)); } else { TxQueue->NextToSend = 0; // Start from descriptor 0 TxQueue->NextToComplete = 0; TxQueue->NextToReserve = 0; } MP_RECORD_STRING_3(TxQueue->Tracking_SendRecorder, "RSET: %d %p %p", TxQueue->NextToSend, Caller, CallersCaller ); TxQueue->NumPending = 0; HW_TX_RELEASE_QUEUE_LOCK(Hw, TxQueue, DispatchLevel); } VOID HwSendCompletePackets( _In_ PHW Hw, _In_ PMP_TX_MSDU PacketList, _In_ ULONG NumberOfPackets, _In_ ULONG SendCompleteFlags ) { PMP_TX_MSDU currentPacket, nextPacket; PMP_TX_MSDU prevPacket = NULL, packetsToComplete = NULL; BOOLEAN indicateList = FALSE, newCompletion; ULONG processedCount = 0; UNREFERENCED_PARAMETER(NumberOfPackets); UNREFERENCED_PARAMETER(Hw); currentPacket = PacketList; while (currentPacket != NULL) { // Save the next in case we do the complete nextPacket = MP_TX_MSDU_NEXT_MSDU(currentPacket); MP_TX_MSDU_NEXT_MSDU(currentPacket) = NULL; newCompletion = FALSE; if (prevPacket != NULL) { // Compare the completion point of this packet with the // previous ones if (MP_TX_MSDU_IS_PRIVATE(currentPacket) != MP_TX_MSDU_IS_PRIVATE(prevPacket)) { // One of the two packets is private & the other is not. Complete the set // we have formed so far newCompletion = TRUE; } else if (MP_TX_MSDU_MAC_CONTEXT(currentPacket) != MP_TX_MSDU_MAC_CONTEXT(prevPacket)) { // The MAC contexts of the two packets is not the same. Complete the set // we have formed so far newCompletion = TRUE; } } if (newCompletion == FALSE) { // This is an optimization. Since this MSDU belongs to the same completion point as // previous one, lets add it to the list now and check if this was the last MSDU of the // chain. If yes, we can do the indication and be done MP_TX_MSDU_NEXT_MSDU(currentPacket) = packetsToComplete; packetsToComplete = currentPacket; prevPacket = currentPacket; processedCount++; currentPacket = NULL; // This way code below wont add this to the list if (nextPacket == NULL) { // This was the last packet in the PacketList, lets complete // the chain we have indicateList = TRUE; } } else { // The code below (indicateList == TRUE & newCompletion == TRUE) would append the // currentPacket to the list to the new list indicateList = TRUE; } if (indicateList) { // Complete the list of packets that we have processed so far to // the MAC context we have done so far if (MP_TX_MSDU_IS_PRIVATE(packetsToComplete)) { HwSendCompletePrivatePackets(MP_TX_MSDU_MAC_CONTEXT(packetsToComplete), packetsToComplete, processedCount, SendCompleteFlags ); } else { Hvl11SendCompletePackets(MP_TX_MSDU_MAC_CONTEXT(packetsToComplete)->VNic, packetsToComplete, processedCount, SendCompleteFlags ); } // Reset state indicateList = FALSE; // Add the new (different) MSDU to the list we would use going forward if (newCompletion) { // Indicate was done because the completion point was changing MPASSERT(currentPacket != NULL); processedCount = 1; prevPacket = currentPacket; packetsToComplete = currentPacket; } else { // Indicate was done because we were at the end. Nothing to add processedCount = 0; prevPacket = NULL; packetsToComplete = NULL; } } currentPacket = nextPacket; } // Complete any remaining packet if (packetsToComplete != NULL) { // Because of the optimization above, this should only get hit as a corner case // if the last MSDU is different MPASSERT(processedCount == 1); if (MP_TX_MSDU_IS_PRIVATE(packetsToComplete)) { HwSendCompletePrivatePackets(MP_TX_MSDU_MAC_CONTEXT(packetsToComplete), packetsToComplete, processedCount, SendCompleteFlags ); } else { Hvl11SendCompletePackets(MP_TX_MSDU_MAC_CONTEXT(packetsToComplete)->VNic, packetsToComplete, processedCount, SendCompleteFlags ); } } } _IRQL_requires_(DISPATCH_LEVEL) VOID HwFlushQueuedTxMSDUs( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue, _In_ BOOLEAN DispatchLevel ) { NDIS_STATUS ndisStatus = HwGetAdapterStatus(Hw); PMP_TX_MSDU currentMsdu, mpMsduToComplete = NULL; ULONG completedCount = 0; PHW_MAC_CONTEXT currentMacContext = NULL; // Note: At this point, the interrupt may be running. So // stuff here needs to be done with the lock held _Analysis_suppress_lock_checking_((& Hw->Lock)->SpinLock) while (!MpPacketQueueIsEmpty(&TxQueue->PendingTxQueue)) { // Remove a packet from the TX queue currentMsdu = MP_MSDU_FROM_QUEUE_ENTRY(MpDequeuePacket(&TxQueue->PendingTxQueue)); MP_TX_MSDU_NEXT_MSDU(currentMsdu) = mpMsduToComplete; mpMsduToComplete = currentMsdu; MP_TX_MSDU_STATUS(currentMsdu) = ndisStatus; currentMacContext = MP_TX_MSDU_MAC_CONTEXT(currentMsdu); HW_REMOVE_MAC_CONTEXT_SEND_REF(currentMacContext, 1); completedCount++; // The stuff in the PendingTxQueue has not yet been processed // so we dont do cleanup on it } // // Fix the TX_QUEUE so it will be empty. // TxQueue->PendingTxQueue.Head = NULL; TxQueue->PendingTxQueue.Tail = NULL; TxQueue->PendingTxQueue.Count = 0; if (mpMsduToComplete) { // // Send Complete the NBLs to NDIS // HW_TX_RELEASE_QUEUE_LOCK(Hw, TxQueue, DispatchLevel); MpTrace(COMP_SEND, DBG_LOUD, ("Flushed %d queued TX MSDU\n", completedCount)); HwSendCompletePackets(Hw, mpMsduToComplete, completedCount, 0); HW_TX_ACQUIRE_QUEUE_LOCK(Hw, TxQueue, DispatchLevel); } } _IRQL_requires_(DISPATCH_LEVEL) VOID HwFlushReservedTxMSDUs( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue, _In_ BOOLEAN DispatchLevel ) { PMP_TX_MSDU mpMsduToComplete = NULL; PHW_TX_MSDU currentMsdu; ULONG completedCount = 0, numTxDesc; PHW_MAC_CONTEXT currentMacContext = NULL; // Note: At this point, the interrupt may be running. So // stuff here needs to be done with the lock held _Analysis_suppress_lock_checking_((& Hw->Lock)->SpinLock) // We may have submitted some to the hardware, but we dont know // what state the hardware is currently in. Lets complete them all // Free all the MSDUs (total -1 since we leave one spot empty) numTxDesc = TxQueue->NumMSDUAllocated - TxQueue->NumMSDUAvailable - 1; while (numTxDesc > 0) { currentMsdu = &TxQueue->MSDUArray[TxQueue->NextToComplete]; // Attempt to return the descriptor to the hardware. This is best effort, // since we are anyways going to reset later if((TxQueue->NumPending > 0) && HW_TX_MSDU_TEST_FLAG(currentMsdu, HW_TX_MSDU_IS_IN_SEND)) { // This MSDU has been previously submitted to the HW, lets // return it to the Hal since we would be resetting and not // all HW can do reset TX individually HW_DECREMENT_HAL_PENDING_TX(TxQueue); HalReleaseTxDesc(Hw->Hal, currentMsdu->SendItem.firstIterator, currentMsdu->SendItem.lastIterator, currentMsdu->SendItem.DescNum ); } MP_TX_MSDU_NEXT_MSDU(currentMsdu->MpMsdu) = mpMsduToComplete; mpMsduToComplete = currentMsdu->MpMsdu; currentMacContext = MP_TX_MSDU_MAC_CONTEXT(currentMsdu->MpMsdu); // Cleanup and do appropriate completion for the MSDU HwCompleteTxMSDU(Hw, TxQueue, currentMsdu, FALSE); // Decrement the recount on the MAC HW_REMOVE_MAC_CONTEXT_SEND_REF(currentMacContext, 1); completedCount++; numTxDesc--; } if (mpMsduToComplete != NULL) { HW_TX_RELEASE_QUEUE_LOCK(Hw, TxQueue, DispatchLevel); MpTrace(COMP_SEND, DBG_LOUD, ("Flushed %d reserved TX MSDU\n", completedCount)); HwSendCompletePackets(Hw, mpMsduToComplete, completedCount, 0 ); HW_TX_ACQUIRE_QUEUE_LOCK(Hw, TxQueue, DispatchLevel); } } VOID HwFlushSendQueue( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue, _In_ BOOLEAN DispatchLevel ) { HW_TX_ACQUIRE_QUEUE_LOCK(Hw, TxQueue, DispatchLevel); // Flush the queued TX descriptors HwFlushQueuedTxMSDUs(Hw, TxQueue, DispatchLevel); // Flush the descriptors that we have reserved HwFlushReservedTxMSDUs(Hw, TxQueue, DispatchLevel); HW_TX_RELEASE_QUEUE_LOCK(Hw, TxQueue, DispatchLevel); } VOID HwFlushSendEngine( _In_ PHW Hw, _In_ BOOLEAN DispatchLevel ) { // Complete pending packets from the queue HwFlushSendQueue(Hw, &Hw->TxInfo.TxQueue[HW11_DEFAULT_QUEUE], DispatchLevel); HwFlushSendQueue(Hw, &Hw->TxInfo.TxQueue[HW11_INTERNAL_SEND_QUEUE], DispatchLevel); } VOID HwResetSendEngine( _In_ PHW Hw, _In_ BOOLEAN DispatchLevel ) { // Note: At this point, the interrupt may be running. So // stuff here needs to be done with the lock held // Complete pending packets from the queue HwFlushSendEngine(Hw, DispatchLevel); // Reset the queues if (!HW_TEST_ADAPTER_STATUS(Hw, HW_CANNOT_ACCESS_HARDWARE) ) { HwReinitializeSendQueue(Hw, &Hw->TxInfo.TxQueue[HW11_DEFAULT_QUEUE], DispatchLevel); HwReinitializeSendQueue(Hw, &Hw->TxInfo.TxQueue[HW11_INTERNAL_SEND_QUEUE], DispatchLevel); HwReinitializeSendQueue(Hw, &Hw->TxInfo.TxQueue[HW11_UNUSED_QUEUE], DispatchLevel); HwReinitializeSendQueue(Hw, &Hw->TxInfo.TxQueue[HW11_BEACON_QUEUE], DispatchLevel); } } BOOLEAN HwArePktsPendingInSendQueue( _In_ PHW_TX_QUEUE TxQueue ) { if (TxQueue->NumMSDUAvailable < ((LONG)TxQueue->NumMSDUAllocated - 1)) { return TRUE; } else { return FALSE; } } BOOLEAN Hw11ArePktsPending( _In_ PHW Hw ) { BOOLEAN pktsPending = FALSE; // Check the default queue pktsPending = HwArePktsPendingInSendQueue(&Hw->TxInfo.TxQueue[HW11_DEFAULT_QUEUE]); if (pktsPending) { return TRUE; } // Check the internal send queue pktsPending = HwArePktsPendingInSendQueue(&Hw->TxInfo.TxQueue[HW11_INTERNAL_SEND_QUEUE]); if (pktsPending) { return TRUE; } return FALSE; } BOOLEAN HwCheckSendQueueForHang( _In_ PHW Hw, _In_ PHW_TX_QUEUE TxQueue ) { BOOLEAN isHung = FALSE; // // If we have pending packets in the HAL and the hardware is not flushing them it // maybe due to the timing window between software updating and hardware looking // at the TX descriptors // if (TxQueue->NumPending > 0) { // There are packets pending HalCheckForSends(Hw->Hal, TxQueue->HalQueueIndex, TRUE); } // // If we have packets pending in the HW layer, check if we may be hung // if (TxQueue->NumMSDUAvailable < ((LONG)TxQueue->NumMSDUAllocated - 1)) { // Check if we suspect we are in a bad state if (TxQueue->StalledSendTicks == 0) { // First time that we are tracking sends (or this has been reset on a send complete) // Save the next to complete TxQueue->StalledSendNextToCompleteSnapshot = TxQueue->NextToComplete; // Starting to track TxQueue->StalledSendTicks++; } else { // // We have previously seen pending sends. Check if anything has completed // since then // if (TxQueue->StalledSendNextToCompleteSnapshot == TxQueue->NextToComplete) { // The next to send has not changed. MpTrace(COMP_MISC, DBG_LOUD, ("%d sample periods of NextToComplete have occured\n", TxQueue->StalledSendTicks)); // // If the number of sends pended on the Hw11 has not changed we will // count a tick interval of stalled send period. // TxQueue->StalledSendTicks++; if (TxQueue->StalledSendTicks == (HW_SENDS_HAVE_STALLED_PERIOD - 1)) { MpTrace(COMP_MISC, DBG_LOUD, ("If one more CheckForHang detects stalled sends, we will reset! Investigate\n")); #if DEBUG_RESET MPASSERTMSG( "If one more CheckForHang detects stalled sends, we will reset! Investigate\n", FALSE ); #endif } else if(TxQueue->StalledSendTicks >= HW_SENDS_HAVE_STALLED_PERIOD) { MpTrace(COMP_MISC, DBG_SERIOUS, ("Send Engine seems to be stalled. Requesting reset\n")); isHung = TRUE; } } else { // Some sends have completed. Reset the ticks TxQueue->StalledSendTicks = 0; } } } return isHung; } BOOLEAN HwCheckForSendHang( _In_ PHW Hw ) { BOOLEAN isHung = FALSE; // Check the default queue isHung = HwCheckSendQueueForHang(Hw, &Hw->TxInfo.TxQueue[HW11_DEFAULT_QUEUE]); if (isHung) { MpTrace(COMP_MISC, DBG_SERIOUS, ("Requesting reset from NDIS\n")); #if DEBUG_RESET MPASSERTMSG("Reset should not occur normally! Investigate\n", FALSE); #endif return TRUE; } // Check the internal send queue isHung = HwCheckSendQueueForHang(Hw, &Hw->TxInfo.TxQueue[HW11_INTERNAL_SEND_QUEUE]); if (isHung) { MpTrace(COMP_MISC, DBG_SERIOUS, ("Requesting reset from NDIS\n")); #if DEBUG_RESET MPASSERTMSG("Reset should not occur normally! Investigate\n", FALSE); #endif return TRUE; } return FALSE; } PMP_TX_MSDU HwAllocatePrivatePacket( _In_ PHW Hw, _In_ USHORT PacketSize ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PMP_TX_MSDU txPacket = NULL; PMP_TX_MPDU txFragment = NULL; PUCHAR dataBuffer = NULL; do { // TX Packet txPacket = NdisAllocateFromNPagedLookasideList(&(Hw->TxInfo.TxPacketLookaside)); if (txPacket == NULL) { ndisStatus = NDIS_STATUS_RESOURCES; break; } NdisZeroMemory(txPacket, sizeof(MP_TX_MSDU )); // TX Fragment txFragment = NdisAllocateFromNPagedLookasideList(&(Hw->TxInfo.TxFragmentLookaside)); if (txFragment == NULL) { ndisStatus = NDIS_STATUS_RESOURCES; break; } NdisZeroMemory(txFragment, sizeof(MP_TX_MPDU)); // Data buffer. We allocate extra space for the backfill that the H/W requires MP_ALLOCATE_MEMORY(Hw->MiniportAdapterHandle, &dataBuffer, PacketSize + HW11_REQUIRED_BACKFILL_SIZE, HW_MEMORY_TAG ); if (dataBuffer == NULL) { ndisStatus = NDIS_STATUS_RESOURCES; break; } // Save this internal send buffer txFragment->InternalSendBuffer = dataBuffer; // Skip the backfill to get to the data start dataBuffer += HW11_REQUIRED_BACKFILL_SIZE; txFragment->InternalSendDataStart = dataBuffer; txFragment->InternalSendLength = PacketSize; MP_TX_MPDU_MSDU(txFragment) = txPacket; MP_TX_MSDU_MPDU_AT(txPacket, 0) = txFragment; MP_TX_MSDU_MPDU_COUNT(txPacket) = 1; txPacket->HardwarePrivate = TRUE; // These packets are always exempt from encryption. The HW layer // makes an assumption about this too & it wont encrypt the packets sent this way MP_TX_MSDU_SEND_CONTEXT(txPacket)->usExemptionActionType = DOT11_EXEMPT_ALWAYS; // Also must be sent using the current phy MP_TX_MSDU_SEND_CONTEXT(txPacket)->uPhyId = DOT11_PHY_ID_ANY; }while (FALSE); if (ndisStatus != NDIS_STATUS_SUCCESS) { if (dataBuffer != NULL) MP_FREE_MEMORY(dataBuffer); if (txFragment != NULL) NdisFreeToNPagedLookasideList(&(Hw->TxInfo.TxFragmentLookaside), txFragment); // Delete the packet if (txPacket != NULL) NdisFreeToNPagedLookasideList(&(Hw->TxInfo.TxPacketLookaside), txPacket); txPacket = NULL; } return txPacket; } VOID HwFreePrivatePacket( _In_ PHW Hw, _In_ PMP_TX_MSDU Packet ) { MPASSERT(MP_TX_MSDU_IS_PRIVATE(Packet)); MP_FREE_MEMORY(MP_TX_MSDU_MPDU_AT(Packet, 0)->InternalSendBuffer); NdisFreeToNPagedLookasideList(&(Hw->TxInfo.TxFragmentLookaside), MP_TX_MSDU_MPDU_AT(Packet, 0)); NdisFreeToNPagedLookasideList(&(Hw->TxInfo.TxPacketLookaside), Packet); } VOID HwSendPrivatePackets( _In_ PHW_MAC_CONTEXT MacContext, _In_ PMP_TX_MSDU PacketList, _In_ ULONG SendFlags ) { // Only single sends permitted MPASSERT(MP_TX_MSDU_NEXT_MSDU(PacketList) == NULL); // We forward this to the main send routine Hw11SendPackets(MacContext, PacketList, SendFlags); } VOID HwSendCompletePrivatePackets( _In_ PHW_MAC_CONTEXT MacContext, _In_ PMP_TX_MSDU PacketList, _In_ ULONG NumberOfPackets, _In_ ULONG SendCompleteFlags ) { PMP_TX_MSDU currentPacket = PacketList, nextPacket; UNREFERENCED_PARAMETER(NumberOfPackets); UNREFERENCED_PARAMETER(SendCompleteFlags); while (currentPacket != NULL) { nextPacket = MP_TX_MSDU_NEXT_MSDU(currentPacket); MP_TX_MSDU_NEXT_MSDU(currentPacket) = NULL; HwFreePrivatePacket(MacContext->Hw, currentPacket); currentPacket = nextPacket; } } NDIS_STATUS Hw11SendNullPkt( _In_ PHW_MAC_CONTEXT MacContext, _In_ BOOLEAN PowerSaveBitSet ) { #ifndef PARTIAL_HAL PMP_TX_MSDU nullPacket = NULL; PDOT11_DATA_SHORT_HEADER packetHeader; PUCHAR buffer; USHORT bufferLength = sizeof(DOT11_DATA_SHORT_HEADER); MpTrace(COMP_SEND, DBG_LOUD, ("HwMac(%d): Transmitting Null packet with PS bit %s\n", HW_MAC_PORT_NO(MacContext), (PowerSaveBitSet ? "SET" : "CLEAR" ))); nullPacket = HwAllocatePrivatePacket(MacContext->Hw, bufferLength); if (nullPacket == NULL) { return NDIS_STATUS_RESOURCES; } buffer = MP_TX_MPDU_DATA(MP_TX_MSDU_MPDU_AT(nullPacket, 0), bufferLength); NdisZeroMemory(buffer, bufferLength); // Populate the fields packetHeader = (PDOT11_DATA_SHORT_HEADER)buffer; if (!packetHeader) { return NDIS_STATUS_RESOURCES; } packetHeader->FrameControl.Type = DOT11_FRAME_TYPE_DATA; packetHeader->FrameControl.Subtype = DOT11_DATA_SUBTYPE_NULL; packetHeader->FrameControl.ToDS = 1; if (PowerSaveBitSet) packetHeader->FrameControl.PwrMgt = 1; else packetHeader->FrameControl.PwrMgt = 0; NdisMoveMemory(packetHeader->Address1, MacContext->DesiredBSSID, DOT11_ADDRESS_SIZE); NdisMoveMemory(packetHeader->Address2, MacContext->MacAddress, DOT11_ADDRESS_SIZE); NdisMoveMemory(packetHeader->Address3, MacContext->DesiredBSSID, DOT11_ADDRESS_SIZE); packetHeader->SequenceControl.usValue = 0; // Total length (MP_TX_MSDU_MPDU_AT(nullPacket, 0))->InternalSendLength = bufferLength; // Send the packet HwSendPrivatePackets(MacContext, nullPacket, 0); #else UNREFERENCED_PARAMETER(MacContext); UNREFERENCED_PARAMETER(PowerSaveBitSet); #endif return NDIS_STATUS_SUCCESS; } BOOLEAN Hw11CanTransmit( _In_ PHW_MAC_CONTEXT MacContext ) { // // When the upper layer calls asking whether packets can be sent or not, // we only report FALSE if we are in a state where packets would be failed. // We do not report for low resources. // /* if (HW_TEST_ADAPTER_STATUS(MacContext->Hw, HW_CANNOT_SEND_FLAGS)) { return FALSE; } */ UNREFERENCED_PARAMETER (MacContext); // BUGBUG: Add comments why we are returning true return TRUE; } VOID Hw11SendPackets( _In_ PHW_MAC_CONTEXT MacContext, _In_ PMP_TX_MSDU PacketList, _In_ ULONG SendFlags ) { PMP_TX_MSDU currentPacket, nextPacket; PMP_TX_MSDU failedPackets = NULL; ULONG failedCount = 0; NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PHW_TX_QUEUE currentQueue = NULL; LONG firstMsduIndex, numMsduReserved = 0; BOOLEAN dispatchLevel = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags); // // Add a send reference to the HW layer. This is tracking the number // of operations that are currently pending on the HW // HW_INCREMENT_ACTIVE_SEND_REF(MacContext->Hw); do { // // Check if the packets can be sent or not // if (HW_TEST_ADAPTER_STATUS(MacContext->Hw, HW_CANNOT_SEND_FLAGS)) { ndisStatus = HwGetAdapterStatus(MacContext->Hw); for (currentPacket = PacketList; currentPacket != NULL; currentPacket = MP_TX_MSDU_NEXT_MSDU(currentPacket)) { MpTrace(COMP_SEND, DBG_LOUD, ("HW cannot transmit (0x%08x). Dropping send packets with status 0x%08x\n", MacContext->Hw->Status, ndisStatus)); MP_TX_MSDU_STATUS(currentPacket) = ndisStatus; // The HwSendCompletePackets function needs the MAC context MP_TX_MSDU_MAC_CONTEXT(currentPacket) = MacContext; failedCount++; } failedPackets = PacketList; break; } currentPacket = PacketList; // NOTE: If we used multiple queues for send, the initialization below needs to // be updated to work with an array if (MP_TX_MSDU_IS_PRIVATE(PacketList)) { // Use the internal send queue for packets internal to the driver currentQueue = &MacContext->Hw->TxInfo.TxQueue[HW11_INTERNAL_SEND_QUEUE]; } else { // Use the default queue for all other packet types currentQueue = &MacContext->Hw->TxInfo.TxQueue[HW11_DEFAULT_QUEUE]; } HW_TX_ACQUIRE_QUEUE_LOCK(MacContext->Hw, currentQueue, dispatchLevel); // This is the first index we would use for sending. From this index, upto // the numMsduReserved are the MSDUs that were prepared by this code and should // be used for sending firstMsduIndex = currentQueue->NextToReserve; while (currentPacket != NULL) { nextPacket = MP_TX_MSDU_NEXT_MSDU(currentPacket); MP_TX_MSDU_NEXT_MSDU(currentPacket) = NULL; // Save the MAC context so that on send complete we // can identify the sending MAC context. Also add a ref to the MAC MP_TX_MSDU_MAC_CONTEXT(currentPacket) = MacContext; HW_ADD_MAC_CONTEXT_SEND_REF(MacContext, 1); // // Check if there are any sends sitting in the pending queue. If not // we can process the new ones for sending. Else we should not to avoid // out of order delivery // if (MpPacketQueueIsEmpty(¤tQueue->PendingTxQueue) && HwCanTransmit(currentQueue)) { // // Lets reserve HW_TX_MSDU for this packet. Reservation guarantees // that no one else will use the reserved TX_MSDU and also avoids // synchronization issues later // ndisStatus = HwReserveTxResources(MacContext->Hw, currentQueue, currentPacket); if (ndisStatus == NDIS_STATUS_SUCCESS) { numMsduReserved++; } else { // // If we cannot reserve resources to send this packet, // put it in the pending queue // ndisStatus = NDIS_STATUS_PENDING; } } else { // Cannot send now ndisStatus = NDIS_STATUS_PENDING; } if (ndisStatus != NDIS_STATUS_SUCCESS) { // // Put it on the pending sends queue // if (currentQueue->PendingTxQueue.Count < currentQueue->MaxPendingTx) { MpQueuePacket(¤tQueue->PendingTxQueue, QUEUE_ENTRY_FROM_MP_MSDU(currentPacket)); } else { // // There are already too many packets in the pending queue. Or we are // not allowed to queue packets on this queue. Drop the packet // MP_TX_MSDU_NEXT_MSDU(currentPacket) = failedPackets; MP_TX_MSDU_STATUS(currentPacket) = NDIS_STATUS_RESOURCES; // This packet would get completed remove the extra MAC ref HW_REMOVE_MAC_CONTEXT_SEND_REF(MacContext, 1); failedPackets = currentPacket; failedCount++; } } currentPacket = nextPacket; } HW_TX_RELEASE_QUEUE_LOCK(MacContext->Hw, currentQueue, dispatchLevel); // // Process all the packets that we have been queued // HwProcessReservedTxPackets(MacContext->Hw, currentQueue, firstMsduIndex, numMsduReserved, SendFlags ); } while (FALSE); if (failedPackets != NULL) { // // Complete the failed packets // HwSendCompletePackets(MacContext->Hw, PacketList, failedCount, (NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags) ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0) ); } // // Remove the active send ref // HW_DECREMENT_ACTIVE_SEND_REF(MacContext->Hw); } VOID HwHandleSendCompleteInterrupt( _In_ PHW Hw ) { // Increment the adapter send ref count. This blocks resets from proceeding HW_INCREMENT_ACTIVE_SEND_REF(Hw); if (HW_TEST_ADAPTER_STATUS(Hw, HW_CANNOT_SEND_FLAGS)) { // // While a context switch or a reset or pause is in progress, we dont want // to do sends to be processed // HW_DECREMENT_ACTIVE_SEND_REF(Hw); return; } // Need to check the default data and internal send queue for completion HwCheckSendQueueForCompletion(Hw, &Hw->TxInfo.TxQueue[HW11_DEFAULT_QUEUE]); HwCheckSendQueueForCompletion(Hw, &Hw->TxInfo.TxQueue[HW11_INTERNAL_SEND_QUEUE]); HW_DECREMENT_ACTIVE_SEND_REF(Hw); }
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials