Sample Code

Windows Driver Samples/ Native Wi-Fi Miniport Sample Driver/ C++/ hw/ hw_recv.c/

/*++

Copyright (c) Microsoft Corporation. All rights reserved.

Module Name:
    hw_recv.c

Abstract:
    Implements the receive functionality for the HW layer
    
Revision History:
      When        What
    ----------    ----------------------------------------------
    09-04-2007    Created

Notes:

--*/
#include "precomp.h"
#include "hw_recv.h"
#include "hw_crypto.h"
#include "hw_context.h"
#include "hw_mac.h"

#if DOT11_TRACE_ENABLED
#include "hw_recv.tmh"
#endif

NDIS_STATUS
Hw11InitializeReceiveEngine(
    _In_  PHW                     Hw,
    _Out_ NDIS_ERROR_CODE*        ErrorCode,
    _Out_ PULONG                  ErrorValue
    )
{
    NDIS_STATUS                 ndisStatus = NDIS_STATUS_SUCCESS;
    BOOLEAN                     descAllocated = FALSE;
    ULONG                       i;
    PHW_RX_MPDU                 mpdu = NULL;

    *ErrorCode = NDIS_STATUS_SUCCESS;
    *ErrorValue = 0;

    do
    {
        NdisInitializeListHead(&Hw->RxInfo.AvailableMPDUList);
        NdisInitializeListHead(&Hw->RxInfo.UnusedMPDUList);

        // The lookaside list for the Rx MPDUs
        NdisInitializeNPagedLookasideList(&Hw->RxInfo.RxFragmentLookaside,
            NULL,
            NULL,
            0,
            sizeof(HW_RX_MPDU),
            HW_MEMORY_TAG,
            0);

        // Lookaside list for the RX MSDUs
        NdisInitializeNPagedLookasideList(&Hw->RxInfo.RxPacketLookaside,
            NULL,
            NULL,
            0,
            sizeof(HW_RX_MSDU),
            HW_MEMORY_TAG,
            0);

        // Allocate the HAL Rx descriptors
        ndisStatus = HalAllocateRxDescs(Hw->Hal, Hw->RegInfo.NumRXBuffers);
        if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            MpTrace(COMP_SEND, DBG_SERIOUS,  ("Failed to allocate the HAL RX descriptors. Status = 0x%08x\n", 
                ndisStatus
                ));
            *ErrorCode = NDIS_ERROR_CODE_OUT_OF_RESOURCES;                
            break;
        }
        descAllocated = TRUE;

        HalResetRxDescs(Hw->Hal);

        // Now we preallocate a whole bunch of RX MSDUs and place them in our list
        for (i = 0; i < Hw->RegInfo.NumRXBuffers; i++)
        {
            // Allocate an MPDU structure
            mpdu = NdisAllocateFromNPagedLookasideList(&Hw->RxInfo.RxFragmentLookaside);
            if (mpdu == NULL)
            {
                MpTrace(COMP_SEND, DBG_NORMAL, ("Unable to allocate HW_RX_MPDU %d\n", i));
                ndisStatus = NDIS_STATUS_RESOURCES;
                *ErrorCode = NDIS_ERROR_CODE_OUT_OF_RESOURCES;                
                break;
            }
            // Initialize the data structure
            HW_INITIALIZE_RX_MPDU(mpdu, Hw);

            //
            // Allocate the shared memory buffer for this MPDU            
            // This implementation is wasting memory. We can be
            // more efficient by allocating shared memory in larger
            // chunks and managing them ourselves
            //
            NdisMAllocateSharedMemory(Hw->MiniportAdapterHandle,
                MAX_TX_RX_PACKET_SIZE,
                FALSE,
                (void **)&mpdu->BufferVa,
                &mpdu->BufferPa
                );
            if ((!mpdu->BufferVa) || (mpdu->BufferPa.QuadPart == 0))
            {
                ndisStatus=NDIS_STATUS_RESOURCES;
                *ErrorCode = NDIS_ERROR_CODE_OUT_OF_RESOURCES;
                MpTrace(COMP_SEND, DBG_SERIOUS, ("Allocation of RX buffer %d failed\n", i));
                NdisFreeToNPagedLookasideList(&Hw->RxInfo.RxFragmentLookaside, mpdu);
                break;
            }

            HW_INCREMENT_TOTAL_RX_MPDU_ALLOCATED(Hw);
            
            // Return this to the HW
            HwSubmitRxMPDU(Hw, mpdu);

        }

        if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            // If we have managed to allocate atleast the minimum number of RX MPDU's we
            // are OK with failure
            if (Hw->RxInfo.NumMPDUAllocated >= (LONG)Hw->RegInfo.NumRXBuffersLowerBase)
            {
                // This is fine, we work only with these many MPDUs
                MpTrace(COMP_SEND, DBG_SERIOUS, ("Able to allocate only %d HW_RX_MPDUs\n", Hw->RxInfo.NumMPDUAllocated));
                ndisStatus = NDIS_STATUS_SUCCESS;
            }
            else
            {
                MpTrace(COMP_SEND, DBG_SERIOUS, ("Unable to allocate minimum required number of HW_RX_MPDU\n"));
                break;
            }
        }        

#if DBG
        // Initialize some debugging state
        Hw->RxInfo.Debug_BreakOnReceiveCount = 0;
        Hw->RxInfo.Debug_BreakOnReceiveType = DOT11_FRAME_TYPE_DATA;
        Hw->RxInfo.Debug_BreakOnReceiveSubType = DOT11_DATA_SUBTYPE_DATA;
        Hw->RxInfo.Debug_BreakOnReceiveMatchSource = FALSE;
        NdisMoveMemory(Hw->RxInfo.Debug_BreakOnReceiveDestinationAddress, Hw->MacState.MacAddress, DOT11_ADDRESS_SIZE);
        NdisMoveMemory(Hw->RxInfo.Debug_BreakOnReceiveSourceAddress, Hw->MacState.MacAddress, DOT11_ADDRESS_SIZE);

#endif
        
    }while (FALSE);

    if (ndisStatus != NDIS_STATUS_SUCCESS)
    {
        if (descAllocated)
            Hw11TerminateReceiveEngine(Hw);
    }


    return ndisStatus;
}

VOID
Hw11TerminateReceiveEngine(
    _In_  PHW                     Hw
    )
{
    PHW_RX_MPDU                 mpdu = NULL;

    MPASSERT((Hw->RxInfo.NumMPDUAvailable + Hw->RxInfo.NumMPDUUnused) == (Hw->RxInfo.NumMPDUAllocated));
    
    // 
    // Merge unused queue into available queue so we only need one resource free code
    //
    while (!IsListEmpty(&Hw->RxInfo.UnusedMPDUList))
    {
        mpdu = (PHW_RX_MPDU)RemoveHeadList(&Hw->RxInfo.UnusedMPDUList);
        InsertTailList(&Hw->RxInfo.AvailableMPDUList, &mpdu->ListEntry);

        HW_DECREMENT_UNUSED_RX_MPDU(Hw);
        HW_INCREMENT_AVAILABLE_RX_MPDU(Hw);
    }

    //
    // Free the whole list
    //
    while (Hw->RxInfo.NumMPDUAvailable > 0)
    {
        mpdu = (PHW_RX_MPDU)RemoveHeadList(&Hw->RxInfo.AvailableMPDUList);
        HW_DECREMENT_AVAILABLE_RX_MPDU(Hw);

        NdisMFreeSharedMemory(Hw->MiniportAdapterHandle,
            MAX_TX_RX_PACKET_SIZE,
            FALSE,
            mpdu->BufferVa,
            mpdu->BufferPa
            );

        NdisFreeToNPagedLookasideList(&Hw->RxInfo.RxFragmentLookaside, mpdu);        
    }
    
    HalReleaseRxDescs(Hw->Hal);

    NdisDeleteNPagedLookasideList(&Hw->RxInfo.RxFragmentLookaside);
    NdisDeleteNPagedLookasideList(&Hw->RxInfo.RxPacketLookaside);
}


__inline PHW_RX_MSDU
HwAllocateRxMSDU(
    _In_  PHW                     Hw
    )
{
    PHW_RX_MSDU                 msdu = NULL;
    NDIS_STATUS                 ndisStatus = NDIS_STATUS_SUCCESS;
    
    do
    {
        // Allocate the MSDU structure. We use the lookaside list
        msdu = NdisAllocateFromNPagedLookasideList(&Hw->RxInfo.RxPacketLookaside);
        if (msdu == NULL)
        {
            ndisStatus = NDIS_STATUS_RESOURCES;
            MpTrace(COMP_SEND, DBG_NORMAL, ("Unable to allocate HW_RX_MSDU\n"));
            break;
        }

        NdisZeroMemory(msdu, sizeof(HW_RX_MSDU));
        
        // Set the appropriate pointers in the HW & MP MSDU
        msdu->MpMsdu = &msdu->PrivateMpMsdu;
        msdu->MpMsdu->HwMsdu = msdu;

    } while (FALSE);

    if (ndisStatus != NDIS_STATUS_SUCCESS)
    {
        if (msdu)
        {        
            NdisFreeToNPagedLookasideList(&Hw->RxInfo.RxFragmentLookaside, msdu);
            msdu = NULL;
        }
    }

    return msdu;
}

__inline VOID
HwFreeRxMSDU(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MSDU             Msdu
    )
{
    NdisFreeToNPagedLookasideList(&Hw->RxInfo.RxFragmentLookaside, Msdu);
}

BOOLEAN
HwCheckPhyParameters(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MPDU             Mpdu
    )
{
    //
    // Save the current PHY parameters into this packet
    //
    Mpdu->PhyId = Hw->PhyState.OperatingPhyId;
    Mpdu->Timestamp = Mpdu->DescStatus.TimeStamp;
    Mpdu->Rate = Mpdu->DescStatus.Rate;
    Mpdu->RSSI = HalGetRSSI(Hw->Hal, &Mpdu->DescStatus, (UCHAR *)Mpdu->DataStart); 
    Mpdu->LinkQuality = (UCHAR)HalGetCalibratedRSSI(Hw->Hal, &Mpdu->DescStatus, (UCHAR *)Mpdu->DataStart); 
    Mpdu->Channel = HalGetPhyMIB(Hw->Hal, Mpdu->PhyId)->Channel;

    //
    // Update PHY statistics
    //
    if (Mpdu->DescStatus.CRCError)
    {
        // FCS error count
        Hw->Stats.PhyCounters[Mpdu->PhyId].ullFCSErrorCount++;
        return FALSE;
    }

    // Hardware specific errors
    if (Mpdu->DescStatus.FifoOverflow || Mpdu->DescStatus.HardwareError)
    {
        // Incomplete packet
        Hw->Stats.NumRxError++;
        return FALSE;
    }

    // This is an indication of a bad receive
    if (Mpdu->DescStatus.Length < 4)
    {
        Hw->Stats.NumRxError++;
        return FALSE;
    }

    // Fragment is good from the PHY point
    Hw->Stats.PhyCounters[Mpdu->PhyId].ullReceivedFragmentCount++;
    MPASSERT(Hw->PhyState.Debug_SoftwareRadioOff == FALSE);
    
    return TRUE;
}

// Returns TRUE if this fragment is duplicate and should be dropped. FALSE otherwise
BOOLEAN
HwMPDUIsDuplicate(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MPDU             Mpdu,
    _In_  BOOLEAN                 IsGoodPacket
    )
{
    PDOT11_MAC_HEADER           macHeader = (PDOT11_MAC_HEADER)Mpdu->DataStart;

    //
    // Reject packets which are not correct length
    //
    switch(macHeader->FrameControl.Type)
    {
    case DOT11_FRAME_TYPE_MANAGEMENT:
    case DOT11_FRAME_TYPE_DATA:
        if (Mpdu->DataLength < sizeof(DOT11_MGMT_DATA_MAC_HEADER))
            return TRUE;    // Drop the packet

        // We dont cache broadcast/multicast packet information
        if (Mpdu->MulticastDestination)
            return FALSE;
            
        //
        // Additional packet filtering - duplication detection
        //
        if (HwCheckForDuplicateMPDU(Hw, Mpdu->DataStart, IsGoodPacket))
        {
            // Update duplicate counter
            Hw->Stats.PhyCounters[Mpdu->PhyId].ullFrameDuplicateCount++;        
            return TRUE;    // Drop the packet
        }

        break;
        
    case DOT11_FRAME_TYPE_CONTROL:    
        break;
        
    default:
        //
        // Reserved packet are filtered out
        //
        return TRUE;    // Drop the packet
    }

    // Not duplicate
    return FALSE;
}



// Called for Data and Management packets only
BOOLEAN
HwCheckForDuplicateMPDU(
    _In_  PHW                     Hw,
    _In_reads_bytes_(sizeof(DOT11_MGMT_DATA_MAC_HEADER))  PUCHAR                  PacketBuffer,
    _In_  BOOLEAN                 IsGoodPacket
    )
{
    UCHAR                               index;
    PDOT11_MGMT_DATA_MAC_HEADER         packetHeader = (PDOT11_MGMT_DATA_MAC_HEADER)PacketBuffer;
    UCHAR                               startIndex, endIndex;

    //
    // For duplicate detection, we compare the <Address 2, sequence-number, fragment-number> tuple to
    // the last received value. If all match, this is a retransmitted packet. However we 
    // determine if we should drop this or not based on whether we have indicated a good MPDU
    // before or not. If this is a bad version of this MPDU (CRC error or something), we drop
    // it. If this is a good one and we have already received a good version of this MPDU before
    // (no-CRC error, etc) we drop this one. Else we indicate this up (because all previous duplicates
    // were corrupted packets)
    //

    //
    // A bunch of probe responses come in during scans, etc. This could cause us
    // to indicate up retry data packets. We handle this by separating cached entries for data
    // packets from other packet types
    //
    if (packetHeader->FrameControl.Type == DOT11_FRAME_TYPE_DATA)
    {
        startIndex = 0;
        endIndex = HW_DUPLICATE_DETECTION_CACHE_LENGTH;
    }
    else
    {
        startIndex = HW_DUPLICATE_DETECTION_CACHE_LENGTH;
        endIndex = 2 * HW_DUPLICATE_DETECTION_CACHE_LENGTH;
    }    

    //
    // The duplicate's cache is indexed using addresses
    //
    for (index = startIndex; index < endIndex; index++)
    {
        if (MP_COMPARE_MAC_ADDRESS(Hw->RxInfo.DupePacketCache[index].Address2, packetHeader->Address2))
        {
            if (packetHeader->FrameControl.Retry)
            {
                //
                // This is a retry if same sequence control
                //
                if (Hw->RxInfo.DupePacketCache[index].SequenceControl == packetHeader->SequenceControl.usValue)
                {
                    // This is a retransmission. Check if we should drop it
                    if (!IsGoodPacket)
                        return TRUE;        // Duplicate & it is a bad MPDU, drop it

                    if (Hw->RxInfo.DupePacketCache[index].ReceivedGoodMPDU)
                        return TRUE;        // Duplicate & we have already received a good MPDU, drop this

                    Hw->RxInfo.DupePacketCache[index].ReceivedGoodMPDU = TRUE;
                    return FALSE;           // Duplicate, but havent received a good one before, dont drop
                }
            }

            //
            // Save latest sequence number & whether we have received a good or bad MPDU
            //
            Hw->RxInfo.DupePacketCache[index].SequenceControl = packetHeader->SequenceControl.usValue;
            Hw->RxInfo.DupePacketCache[index].ReceivedGoodMPDU = IsGoodPacket;

            return FALSE;
        }
    }

    //
    // Store the tuple information from this packet
    //

    //
    // This address is not cached. We just add it to the next index in the cache
    //
    if (packetHeader->FrameControl.Type == DOT11_FRAME_TYPE_DATA)
    {
        index = ((Hw->RxInfo.NextDupeCacheIndexData + 1) % HW_DUPLICATE_DETECTION_CACHE_LENGTH);
        Hw->RxInfo.NextDupeCacheIndexData = index;
    }
    else
    {
        index = ((Hw->RxInfo.NextDupeCacheIndexOther + 1) % HW_DUPLICATE_DETECTION_CACHE_LENGTH)
                    + HW_DUPLICATE_DETECTION_CACHE_LENGTH;
        Hw->RxInfo.NextDupeCacheIndexOther = index;
    }
    
    NdisMoveMemory(Hw->RxInfo.DupePacketCache[index].Address2, 
        packetHeader->Address2, 
        sizeof(DOT11_MAC_ADDRESS)
        );
    Hw->RxInfo.DupePacketCache[index].SequenceControl = packetHeader->SequenceControl.usValue;
    Hw->RxInfo.DupePacketCache[index].ReceivedGoodMPDU = IsGoodPacket;

    return FALSE;
}

BOOLEAN
HwCheckMacParameters(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MPDU             Mpdu
    )
{
    PNICKEY                     key;
    PHW_MAC_CONTEXT             macContext = HW_MAC_CONTEXT_FOR_RX_STATISTICS(Hw, Mpdu);

    if (Mpdu->DescStatus.ICVError)
    {
        if (Mpdu->Encrypted)
        {
            //
            // For MAC counters, we shouldnt count duplicates, filter those 
            // counters out
            //
            if (HwMPDUIsDuplicate(Hw, Mpdu, FALSE))
            {
                // Drop and dont count
                return FALSE;
            }
        
            if (Mpdu->Key != NULL)
            {
                key = &Mpdu->Key->Key;
                if (key->Valid)
                {
                    //
                    // Decryption failed even though we think we have
                    // a valid key. This counts as an ICV error
                    //
                    switch (key->AlgoId)
                    {
                        case DOT11_CIPHER_ALGO_CCMP:
                            if (Mpdu->MulticastDestination)
                            {
                                macContext->MulticastCounters.ullCCMPDecryptErrors++;
                            }
                            else
                            {
                                macContext->UnicastCounters.ullCCMPDecryptErrors++;
                            }
                            break;
                            
                        case DOT11_CIPHER_ALGO_TKIP:
                            if (Mpdu->MulticastDestination)
                            {
                                macContext->MulticastCounters.ullTKIPICVErrorCount++;
                            }
                            else
                            {
                                macContext->UnicastCounters.ullTKIPICVErrorCount++;
                            }
                            break;
                        
                        case DOT11_CIPHER_ALGO_WEP104:
                        case DOT11_CIPHER_ALGO_WEP40:
                            if (Mpdu->MulticastDestination)
                            {
                                macContext->MulticastCounters.ullWEPICVErrorCount++;
                            }
                            else
                            {
                                macContext->UnicastCounters.ullWEPICVErrorCount++;
                            }
                            break;
                            
                        default:
                            break;
                    }
                }
                else
                {
                    //
                    // Decryption failed and the key we have is invalid. This happens 
                    // when the hardware uses a default key for decrypting a packet, but
                    // the default key was not set
                    //
                    if (Mpdu->MulticastDestination)
                    {
                        macContext->MulticastCounters.ullWEPUndecryptableCount++;
                    }
                    else
                    {
                        macContext->UnicastCounters.ullWEPUndecryptableCount++;
                    }
                }
            }
            else
            {
                //
                // No key found - Our hardware may still have done the decryption (using a bogus
                // default key id) but we would not have found the key it used
                //
                if (Mpdu->MulticastDestination)
                {
                    macContext->MulticastCounters.ullWEPUndecryptableCount++;
                }
                else
                {
                    macContext->UnicastCounters.ullWEPUndecryptableCount++;
                }
            }
            if (Mpdu->MulticastDestination)
            {
                macContext->MulticastCounters.ullDecryptFailureCount++;
            }
            else
            {
                macContext->UnicastCounters.ullDecryptFailureCount++;
            }
        }
        
        return FALSE;
    }

    if (!(Mpdu->DescStatus.FirstSegment && Mpdu->DescStatus.LastSegment))
    {
        if (Mpdu->MulticastDestination)
        {
            macContext->MulticastCounters.ullReceivedFailureFrameCount++;
        }
        else
        {
            macContext->UnicastCounters.ullReceivedFailureFrameCount++;
        }

        // Fragments spanning multiple descriptors are not supported
        return FALSE;
    }

    //
    // Drop any duplicate frames
    //
    return !HwMPDUIsDuplicate(Hw, Mpdu, TRUE);
}

BOOLEAN
HwIsReceiveAvailable(
    _In_  PHW                     Hw,
    _In_  BOOLEAN                 DispatchLevel
    )
{
    PHW_RX_MPDU                 mpdu = NULL;
    BOOLEAN                     recvAvailable = FALSE;

    //
    // The Available fragment list needs to be protected with a lock since it
    // gets accessed by the return code paths also
    //
    HW_RX_ACQUIRE_LOCK(Hw, DispatchLevel);
    if (!IsListEmpty(&Hw->RxInfo.AvailableMPDUList))
    {
        MPASSERT(Hw->RxInfo.NumMPDUAvailable > 0);

        // Get the next fragment that we expect to be filled by the hardware
        mpdu = (PHW_RX_MPDU)MP_PEEK_LIST_HEAD(&Hw->RxInfo.AvailableMPDUList);

        // Check with the HAL if this RX_MPDU is ready for receive indication
        if (HalIsRxDescReady(Hw->Hal, mpdu->DescIter))
        {
            recvAvailable = TRUE;
        }
    }
    else
    {
        //
        // There cannot be a receive frame, since hardware has no RX_MPDU free 
        // available to use.
        //
    }
    HW_RX_RELEASE_LOCK(Hw, DispatchLevel);

    return recvAvailable;
}

PHW_RX_MPDU
HwGetReceivedMPDU(
    _In_  PHW                     Hw
    )
{
    PHW_RX_MPDU                 mpdu = NULL, unusedMpdu;

    HW_RX_ACQUIRE_LOCK(Hw, TRUE);
    
    // One less descriptor available to the hardware. 
    HW_DECREMENT_AVAILABLE_RX_MPDU(Hw);
    
    mpdu = (PHW_RX_MPDU)RemoveHeadList(&Hw->RxInfo.AvailableMPDUList);

    // Copy the status of this receive into our local copy
    HalReserveRxDesc(Hw->Hal, mpdu->DescIter);
    HalGetRxStatus(Hw->Hal, mpdu->DescIter, &mpdu->DescStatus);

    //
    // This hardware RX_DESC is now free. If we have Unused MPDU, we can assign this
    // MPDU to such a frag so that we can immediately receive a frame that comes
    // along and not have to wait for a return to occur
    //
    while (Hw->RxInfo.NumMPDUAvailable < (LONG)Hw->RegInfo.NumRXBuffers)
    {
        // Nothing is available
        if (IsListEmpty(&Hw->RxInfo.UnusedMPDUList))
            break;
        
        unusedMpdu = (PHW_RX_MPDU)RemoveHeadList(&Hw->RxInfo.UnusedMPDUList);
        HW_DECREMENT_UNUSED_RX_MPDU(Hw);

        // Return the descriptor held by this MPDU to the hardware
        HwSubmitRxMPDU(Hw, unusedMpdu);
    }

    // This MPDU should be owned by the OS
    MPASSERT(mpdu->DescStatus.ReceiveFinished);
    
    // We no longer need the lock. We have pulled the MPDU out of the available list,
    // and can now process it individually
    HW_RX_RELEASE_LOCK(Hw, TRUE);

    // Fill some default information about the packet
    mpdu->DataLength = mpdu->DescStatus.Length;
    mpdu->DataStart = mpdu->BufferVa;
    

    return mpdu;
}


// Called at Dispatch
NDIS_STATUS
HwProcessReceivedMPDU(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MPDU             Mpdu
    )
{
    PDOT11_MAC_HEADER           macHeader = (PDOT11_MAC_HEADER)Mpdu->DataStart;
    PHW_MAC_CONTEXT             macContext = Mpdu->MacContext;
    PDOT11_MGMT_DATA_MAC_HEADER mgmtHeader;
    NDIS_STATUS                 ndisStatus = NDIS_STATUS_SUCCESS;

    UNREFERENCED_PARAMETER(Hw);
    
    if (macContext == NULL)
    {
        // If we havent identified the single MAC this
        // packet should go to, we dont do much processing
#if 0
        //
        // During a scan operation, for beacon and probe response frames, check its timestamp. 
        // If it's not out-dated, save the channel number at which it was received. 
        // Otherwise, drop the frame.
        //
        if (Hw->ScanContext.ScanInProgress &&
            (macHeader->FrameControl.Type == DOT11_FRAME_TYPE_MANAGEMENT) &&
            ((macHeader->FrameControl.Type == DOT11_MGMT_SUBTYPE_BEACON) || 
             (macHeader->FrameControl.Type == DOT11_MGMT_SUBTYPE_BEACON)))
        {            
            if (Mpdu->Timestamp < Hw->ScanContext.ChannelSwitchTime)
            {
                return NDIS_STATUS_NOT_ACCEPTED;
            }

            //
            // Update the channel number
            //
            Mpdu->Channel = Hw->ScanContext.CurrentChannel;
        }
#endif
        switch(macHeader->FrameControl.Type)
        {
            case DOT11_FRAME_TYPE_MANAGEMENT:
                {
                    switch (macHeader->FrameControl.Subtype)
                    {
                        case DOT11_MGMT_SUBTYPE_PROBE_REQUEST:
                            {
#ifndef PARTIAL_HAL
                                // Check if we are running in AP or IBSS mode.
                                // If yes & we are active, respond to this probe request 
                                macContext = Hw->MacState.BSSMac;
                                if ((Hw->MacState.BSSStarted) && 
                                    (macContext != NULL) &&
                                    (HW_MAC_CONTEXT_IS_ACTIVE(macContext)) &&
                                    (macContext->BSSStarted))
                                {
                                    // This MAC must be active
                                    ndisStatus = HwProcessProbeRequest(Hw, macContext, Mpdu);
                                    if (ndisStatus != NDIS_STATUS_SUCCESS)
                                    {
                                        MpTrace(COMP_MISC, DBG_NORMAL, ("Failed to respond to probe request\n"));
                                    }
                                }
#endif
                            }
                            break;
                    }
                }
        }
        return NDIS_STATUS_SUCCESS;
    }

    // Check if there is any operation that needs to be done on this MPDU
    switch(macHeader->FrameControl.Type)
    {
        case DOT11_FRAME_TYPE_MANAGEMENT:
            {
                mgmtHeader = (PDOT11_MGMT_DATA_MAC_HEADER)Mpdu->DataStart;
                switch (macHeader->FrameControl.Subtype)
                {
                    case DOT11_MGMT_SUBTYPE_PROBE_REQUEST:
                        {
#ifndef PARTIAL_HAL
                            // Check if we are running in AP or IBSS mode.
                            // If yes & we are active, respond                            
                            if ((macContext->BSSStarted) && 
                                (HW_MAC_CONTEXT_IS_ACTIVE(macContext)))
                            {
                                // This MAC must be active
                                ndisStatus = HwProcessProbeRequest(Hw, macContext, Mpdu);
                            }
#endif
                        }
                        break;
                        
                    case DOT11_MGMT_SUBTYPE_BEACON:
                        {
                            // Check if we are waiting for a join to this AP
                            // If yes, indicate join completion
                            if (macContext->JoinWaitForBeacon &&
                                MP_COMPARE_MAC_ADDRESS(mgmtHeader->Address3, macContext->DesiredBSSID) &&
                                HW_STOP_WAITING_FOR_JOIN(macContext))
                            {
                                //
                                // We have received the beacon that synchronises us with the BSS.
                                // We will complete the pending Join request
                                //
                                if (NdisCancelTimerObject(macContext->Timer_JoinTimeout) == TRUE)
                                {
                                    // Timer was cancelled, remove the async operation ref
                                    HW_DECREMENT_ACTIVE_OPERATION_REF(Hw);
                                }

                                // Inform the MAC context that the Join has completed
                                HwJoinBSSComplete(macContext, Mpdu, NDIS_STATUS_SUCCESS);
                            }
                            // TODO:
                            // Check if power save is enabled. If yes,
                            // check if there are buffered packets
                        }
                        break;

                    default:
                        break;
                }
            }
            break;

        case DOT11_FRAME_TYPE_DATA:
            {
                // TODO: check is something needs to be done for power save
                // If in AP mode, check peer table. If in STA mode
                // check for PS/More bit

                // Update rate adaptation table based on received packet
                // data rate. Plus note that if we do this based                
                // on MPDU, someone can spoof and cause our adaptation
                // table to get messed up
            }
            break;


        default:
            break;
    }

    return ndisStatus;
}


// Returns TRUE if we do software decryption (Even if decryption gave ICV errors, etc)
BOOLEAN
HwRxDecrypt(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MPDU             Mpdu
    )
{
    PHW_MAC_CONTEXT             currentMacContext;
    ULONG                       i, keyId;
    PDOT11_DATA_SHORT_HEADER    dataFragmentHeader;
    PHW_PEER_NODE               peerNode;
    NDIS_STATUS                 decryptStatus;
    PHW_KEY_ENTRY               decryptKey;

    // We only handle it for data packets
    dataFragmentHeader = (PDOT11_DATA_SHORT_HEADER)Mpdu->DataStart;
    if (dataFragmentHeader->FrameControl.Type != DOT11_FRAME_TYPE_DATA)
    {
        return FALSE;
    }

    // Information about synchronization of MacContext is given in HwFindDecryptionKey
    for (i = HW_DEFAULT_PORT_MAC_INDEX ; i < HW_MAX_NUMBER_OF_MAC; i++)
    {
        currentMacContext = &Hw->MacContext[i];
        if (!HW_MAC_CONTEXT_IS_VALID(currentMacContext) || !HW_MAC_CONTEXT_IS_ACTIVE(currentMacContext))
        {
            continue;
        }

        // The only case in which we do software decryption today is for
        // multicast packets for Adhoc WPA2
        if ((currentMacContext->BssType == dot11_BSS_type_independent) &&
            (currentMacContext->AuthAlgorithm == DOT11_AUTH_ALGO_RSNA_PSK) &&
            (dataFragmentHeader->FrameControl.FromDS == 0) &&
            (dataFragmentHeader->FrameControl.ToDS == 0) &&
            (Mpdu->MulticastDestination) &&
            (MP_COMPARE_MAC_ADDRESS(dataFragmentHeader->Address3, currentMacContext->DesiredBSSID)))
        {
            // Lets look for the peer MAC address
            peerNode = HwFindPeerNode(currentMacContext, dataFragmentHeader->Address2, FALSE);

            // Check if we can find a valid Key ID in the peer (to & from DS = 0, so its a short header)
            keyId = *((PUCHAR)Add2Ptr(dataFragmentHeader, sizeof(DOT11_DATA_SHORT_HEADER) + 3));
            keyId = (keyId >> 6);
            
            if (peerNode && peerNode->PrivateKeyTable[keyId].Key.Valid)
            {
                // Found the key to use for decryption
                decryptKey = &peerNode->PrivateKeyTable[keyId];

                // If this is CCMP, we would be doing software decryption
                if (decryptKey->Key.AlgoId == DOT11_CIPHER_ALGO_CCMP)
                {
                    decryptStatus = HwDecryptCCMP(decryptKey->hCNGKey,
                                        Mpdu->DataStart,
                                        Mpdu->DataLength
                                        );
                    // It is decrypted. If there was a decrypt failure, 
                    // we still continue with the ICV error flag set
                    Mpdu->DescStatus.Decrypted = 1;
                    if (decryptStatus != NDIS_STATUS_SUCCESS)
                        Mpdu->DescStatus.ICVError = 1;
                    
                    // We assume that if we found a valid matching key, we 
                    // are OK. This would break if two MACs connect to the same PEER and each
                    // of them has a unique key for the same key ID
                    Mpdu->Key = decryptKey;

                    return TRUE;
                }
            }
        }
    }

    return FALSE;
}

// Failure only for catastrophic issues like corrupted packet
NDIS_STATUS
HwProcessRxCipher(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MPDU             Mpdu
    )
{
    PDOT11_MAC_HEADER           macHeader;
    BOOLEAN                     softwareDecrypted = FALSE;
    PHW_KEY_ENTRY               decryptKey;
    ULONG                       headerSize, ivSize, overhead;
    PUCHAR                      encryptionIV;
    NDIS_STATUS                 ndisStatus = NDIS_STATUS_SUCCESS;

    MPASSERT(Hw->MacState.SafeModeEnabled == FALSE);
    
    macHeader = (PDOT11_MAC_HEADER)Mpdu->DataStart;

    if (macHeader->FrameControl.WEP == 1)
    {
        headerSize = (macHeader->FrameControl.FromDS && macHeader->FrameControl.ToDS) ?
                     sizeof(DOT11_DATA_LONG_HEADER) : sizeof(DOT11_DATA_SHORT_HEADER);
                     
        //
        // If the MPDU is smaller than minimum encrypted data packet size,
        // then there is no point going ahead
        //
        if (Mpdu->DataLength < (headerSize + HW_ENCRYPTED_MPDU_MIN_OVERHEAD))
        {
            // Not enough data for MGMT/DATA packet + IV/ICV. Drop this packet
            // without further processing
            MpTrace(COMP_RECV, DBG_LOUD, ("Encrypted MPDU data length less than minimum required\n"));
            return NDIS_STATUS_INVALID_PACKET;
        }

        //
        // Check on whether we need to do software decryption
        //
        if (Mpdu->DescStatus.Decrypted == FALSE)
        {
            // Packet received is encrypted and the hardware has not decrypted it. 
            // Lets check if we should do software decryption        
            softwareDecrypted = HwRxDecrypt(Hw, Mpdu);

            // The above function changes the Decrypted flag, so dont merge if/else
            // below
        }

        if (Mpdu->DescStatus.Decrypted == TRUE)
        {
            // The hardware (or software) has decrypted this packet. It would not have removed 
            // the IV and ICV/MIC fields. We would manually remove it
            Mpdu->Encrypted = TRUE;

            // Next look for the key that was used for decrypting this packet
            if (softwareDecrypted)
            {
                decryptKey = Mpdu->Key;
            }
            else
            {
                decryptKey = HwFindDecryptionKey(Hw, Mpdu);    

                // Some hardware would claim to have decrypted the packet even if they
                // havent found the key. Check again for the case that we do software decryption

                if ((decryptKey == NULL) &&
                    (Hw->MacState.BssType == dot11_BSS_type_independent) &&
                    (Mpdu->MulticastDestination))
                {
                    // Check for software decryption
                    softwareDecrypted = HwRxDecrypt(Hw, Mpdu);
                    if (softwareDecrypted)
                        decryptKey = Mpdu->Key;
                }
            }

            // Clear the WEP flag
            macHeader->FrameControl.WEP = 0;

            if (decryptKey && decryptKey->Key.Valid)
            {
                //
                // Find the IVSize and total overhead due to encryption and CRC.
                //
                encryptionIV = Add2Ptr(macHeader, headerSize);
                switch (decryptKey->Key.AlgoId)
                {
                    case DOT11_CIPHER_ALGO_TKIP:

                        // 
                        // 8 bytes IV, 4 byte ICV. The per-MSDU MIC is removed after frame 
                        // reassembling and MIC failure checking.
                        //
                        ivSize = 8;
                        overhead = 12;
                        if (Mpdu->DataLength < (headerSize + overhead))
                        {
                            MpTrace(COMP_RECV, DBG_LOUD, ("Encrypted TKIP MPDU data length less than minimum required\n"));
                            ndisStatus = NDIS_STATUS_INVALID_PACKET;
                        }
                        else
                        {
                            Mpdu->FrameNumber = (((ULONGLONG)encryptionIV[0]) << 8) |
                                                 ((ULONGLONG)encryptionIV[2]) |
                                                 (((ULONGLONG)encryptionIV[4]) << 16) |
                                                 (((ULONGLONG)encryptionIV[5]) << 24) |
                                                 (((ULONGLONG)encryptionIV[6]) << 32) |
                                                 (((ULONGLONG)encryptionIV[7]) << 40);
                        }
                        break;

                    case DOT11_CIPHER_ALGO_CCMP:

                        // 
                        // 8 bytes IV, 8 bytes MIC. 
                        //
                        ivSize = 8;
                        overhead = 16;
                        if (Mpdu->DataLength < (headerSize + overhead))
                        {
                            MpTrace(COMP_RECV, DBG_LOUD, ("Encrypted CCMP MPDU data length less than minimum required\n"));
                            ndisStatus = NDIS_STATUS_INVALID_PACKET;
                        }
                        else
                        {
                            Mpdu->FrameNumber = ((ULONGLONG)encryptionIV[0]) |
                                                (((ULONGLONG)encryptionIV[1]) << 8) |
                                                (((ULONGLONG)encryptionIV[4]) << 16) |
                                                (((ULONGLONG)encryptionIV[5]) << 24) |
                                                (((ULONGLONG)encryptionIV[6]) << 32) |
                                                (((ULONGLONG)encryptionIV[7]) << 40);
                        }
                        break;

                    case DOT11_CIPHER_ALGO_WEP104:
                    case DOT11_CIPHER_ALGO_WEP40:

                        // 
                        // 4 bytes IV, 4 bytes ICV. 
                        //
                        ivSize = 4;
                        overhead = 8;
                        if (Mpdu->DataLength < (headerSize + overhead))
                        {
                            MpTrace(COMP_RECV, DBG_LOUD, ("Encrypted WEP MPDU data length less than minimum required\n"));
                            ndisStatus = NDIS_STATUS_INVALID_PACKET;
                        }                        
                        break;

                    default:
                        MPASSERT(FALSE);
                        ivSize = 0;
                        overhead = 0;
                        break;
                }

                if (ndisStatus == NDIS_STATUS_SUCCESS)
                {
                    //
                    // Copy the header. It is probably the most efficient way to get rid of the IV field.
                    // Note: cannot call NdisMoveMemory since source and destination overlaps.
                    //
                    RtlMoveMemory(((PCHAR)macHeader) + ivSize, macHeader, headerSize);
                }
                
                //
                // Set the data size and offset.
                //
                Mpdu->DataStart += ivSize;
                Mpdu->DataLength -= overhead;
                Mpdu->Encrypted = TRUE;
                Mpdu->Key = decryptKey;
            }
            else
            {
                // 
                // A key if used for decryption was wrong (or is no longer valid)
                //
                Mpdu->Key = decryptKey;
                Mpdu->DescStatus.ICVError = 1;
            }

        }
        else
        {
            // Neither could decrypt the packet
            Mpdu->Encrypted = TRUE;
        }
    }
    else
    {
        Mpdu->Encrypted = FALSE;
    }

    return ndisStatus;
}

PHW_KEY_ENTRY
HwFindDecryptionKey(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MPDU             Mpdu
    )
{
    NDIS_STATUS                 ndisStatus;
    PHW_MAC_CONTEXT             currentMacContext;
    ULONG                       i, keyIndex;
    PDOT11_MGMT_DATA_MAC_HEADER fragmentHeader;
    PHW_KEY_ENTRY               keyUsed = NULL;
    USHORT                      headerSize;
    BOOLEAN                     doBSSIDMatching = FALSE;
    PUCHAR                      bssid = NULL;

    // Should be only called for packets that have been decrypted
    MPASSERT(Mpdu->Key == NULL);    // Software decrypted packets shouldnt come here
    MPASSERT(Mpdu->Encrypted);
    MPASSERT(Mpdu->DescStatus.Decrypted);

    fragmentHeader = (PDOT11_MGMT_DATA_MAC_HEADER)Mpdu->DataStart;

    //
    // Synchronization Note: The receive handler function would have incremented 
    // the HW AsyncCount. This AsyncCount stops the Pause routines
    // from proceeding while the receive is running. Unless we are paused, 
    // the Mac context cannot be deleted. So we are OK here. 
    //
    // Note that a MAC context can become invalid after we have assigned a receive to it. 
    // We would still indicate the packet to that MAC
    //

    for (i = HW_DEFAULT_PORT_MAC_INDEX ; i < HW_MAX_NUMBER_OF_MAC; i++)
    {
        currentMacContext = &Hw->MacContext[i];
        doBSSIDMatching = FALSE;

        // We only look for valid MACs. A MAC context can be invalid 
        if (!HW_MAC_CONTEXT_IS_VALID(currentMacContext) || 
            HW_TEST_MAC_CONTEXT_STATUS(currentMacContext, HW_MAC_CONTEXT_CANNOT_RECEIVE_FLAGS))
        {
            continue;
        }

        if (!HW_MAC_CONTEXT_IS_ACTIVE(currentMacContext))
        {
            // The current MAC context is not active (or is being deactivated). But it maybe 
            // that the hardware has already decrypted the packet using a previously set key
            // If we find a matching key for this MAC, we only accept this key if we 
            // find the BSSID matches
            doBSSIDMatching = TRUE;
        }

        //
        // Lets try to use the keys to determine if an encrypted packet was decrypted
        // using one of the keys that this MAC set on the hardware
        //
        if (currentMacContext->UnicastCipher != DOT11_CIPHER_ALGO_NONE)
        {
            //
            // First we check if we have a key mapping key in this MAC context
            // that matches this node
            //
            if (!Mpdu->MulticastDestination)
            {                    
                ndisStatus = HwFindKeyMappingKeyIndex(currentMacContext, 
                                fragmentHeader->Address2, 
                                FALSE, 
                                &keyIndex
                                );

                if (ndisStatus == NDIS_STATUS_SUCCESS)
                {
                    // We found a key mapping 
                    keyUsed = &currentMacContext->KeyTable[keyIndex];
                }

                ndisStatus = NDIS_STATUS_SUCCESS;   // Reset status
            }

            //
            // Else, look for the default Key ID that was used for decryption
            //
            if (keyUsed == NULL)
            {
                headerSize = (fragmentHeader->FrameControl.FromDS && fragmentHeader->FrameControl.ToDS)?
                                sizeof(DOT11_DATA_LONG_HEADER) : sizeof(DOT11_DATA_SHORT_HEADER);
            
                keyIndex = *((PUCHAR)Add2Ptr(fragmentHeader, headerSize + 3));
                keyIndex = keyIndex >> 6;

                //
                // Determine if the key at this index from this MAC programmed on the hardware at
                // this same index
                //
                if ((currentMacContext->KeyTable[keyIndex].Key.Valid) && 
                    (currentMacContext->KeyTable[keyIndex].NicKeyIndex == keyIndex))
                {
                    // Yes. This key is set at the key index in the hardware at this key index
                    keyUsed = &currentMacContext->KeyTable[keyIndex];

                    // If we select a default key, we perform BSSID matching. This is to handle the
                    // two conditions:
                    // 1. Two MAC contexts are using the same default key ID. They may not be active
                    //    at the same time, but since we dont consider active/inactive when 
                    //    finding keys, we do BSSID matching
                    // 2. Two MAC contexts are active, with the first one in the list having default key 
                    //    at 0 and the second one using key mapping keys. Since packets with Key mapping 
                    //    keys can point to index 0 as their default key, we have to ensure that we 
                    //    dont incorrectly map this received packet to the incorrect context
                    doBSSIDMatching = TRUE;
                }
            }

            // If we found a key, lets check if we need to do BSSID matching
            if (keyUsed != NULL)
            {
                if (doBSSIDMatching)
                {
                    bssid = Dot11GetBSSID(Mpdu->DataStart, Mpdu->DataLength);
                    if (bssid != NULL)
                    {
                        if (!MP_COMPARE_MAC_ADDRESS(currentMacContext->DesiredBSSID, bssid))
                        {
                            // BSSID does not match, we dont select this MAC
                            keyUsed = NULL;
                        }
                    }
                    else
                    {
                        MPASSERT(bssid);    // These are all data or management packets that must have a BSSID
                        keyUsed = NULL;
                    }
                }
            }

            if (keyUsed != NULL)
            {
                // Found the key
                break;
            }
        }

    }

    return keyUsed;
}

NDIS_STATUS
HwIdentifyReceiveMac(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MPDU             Mpdu
    )
{
    PHW_MAC_CONTEXT             currentMacContext;
    ULONG                       i;
    PUCHAR                      mpduBssid;

    //
    // The code below is needed because we do not currently have unique MAC 
    // addresses per context. If we enable unique MAC addresses on the hardware
    // we can do the split based on MAC addresses & dont need to rely on
    // keys
    //

    //
    // If this is called after software decryption, we may have
    // the key already set
    //
    if (Mpdu->Key != NULL)
    {
        // Someone has already determined the key that is used for
        // encrypting this packet. We just need to look at the Mac context
        
        currentMacContext = Mpdu->Key->MacContext;

        // If the Mac context is already set, then we should
        // have the same one, else something went really wrong
        MPASSERT((Mpdu->MacContext == NULL) || (currentMacContext == Mpdu->MacContext));

        Mpdu->MacContext = currentMacContext;
        return NDIS_STATUS_SUCCESS;
    }

    MPASSERT(Mpdu->MacContext == NULL);

    // If there is only a single (non-helper) MAC active, we indicate the packet
    // on it
    if (!HW_MULTIPLE_MAC_ENABLED(Hw))
    {
        // The context at index 1 is the one we should use
        currentMacContext = &Hw->MacContext[HW_DEFAULT_PORT_MAC_INDEX];
        MPASSERT(HW_MAC_CONTEXT_IS_VALID(currentMacContext));
        Mpdu->MacContext = currentMacContext;
        return NDIS_STATUS_SUCCESS;        
    }

    // If we do not find any specific node to indicate this packet to. Indicate the
    // packet to all the MACs that are valid
    Mpdu->MacContext = NULL;

    // Information about synchronization of MacContext is given in HwFindDecryptionKey
    // We walk through the list of MAC contexts (ignoring MAC 0)
    for (i = HW_DEFAULT_PORT_MAC_INDEX ; i < HW_MAX_NUMBER_OF_MAC; i++)        
    {
        currentMacContext = &Hw->MacContext[i];
        if (!HW_MAC_CONTEXT_IS_VALID(currentMacContext) ||
            HW_TEST_MAC_CONTEXT_STATUS(currentMacContext, HW_MAC_CONTEXT_CANNOT_RECEIVE_FLAGS)
            )
        {
            continue;
        }
        
        //
        // Try to do BSSID based matching
        //
        mpduBssid = Dot11GetBSSID(Mpdu->DataStart, Mpdu->DataLength);
        if (mpduBssid != NULL)
        {
            if (DOT11_IS_UNICAST(currentMacContext->DesiredBSSID) &&
                MP_COMPARE_MAC_ADDRESS(mpduBssid, currentMacContext->DesiredBSSID))
            {
                // If BSSID matches, indicate the packet on this MAC

                // If we have 2 MACs that connect to a single BSSID, there is an issue 
                // here, only one of them would see the BSSID
                Mpdu->MacContext = currentMacContext;
                break;
#if 0
                macHeader = (PDOT11_MAC_HEADER)Mpdu->DataStart;

                // For data packets, we consider BSSID matching to be good enough since
                // we only one MAC can establish a connection to a BSSID 
                if (macHeader->FrameControl.Type == DOT11_FRAME_TYPE_DATA)
                {
                    Mpdu->MacContext = currentMacContext;
                    break;
                }

                // For management packets BSSID matching is applied only for unicast packets.
                // Broadcast packets are indicated to everyone
                if (!Mpdu->MulticastDestination)
                {
                    Mpdu->MacContext = currentMacContext;
                    break;
                }
#endif
            }
        }
    }

    return NDIS_STATUS_SUCCESS;
}

NDIS_STATUS
HwFilterMPDU(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MPDU             Mpdu
    )
{
    PDOT11_MAC_HEADER           macHeader = (PDOT11_MAC_HEADER)Mpdu->DataStart;
    ULONG                       packetFilter;
    BOOLEAN                     addressMatch;
    PHW_MAC_CONTEXT             currentMacContext;
    ULONG                       i;
    PDOT11_MAC_ADDRESS          multicastAddressList;
    ULONG                       multicastAddressListCount;

    if (Mpdu->MacContext != NULL)
    {
        // If the MAC context is NULL, we use its packet filter & MAC address
        packetFilter = Mpdu->MacContext->PacketFilter;
        addressMatch = MP_COMPARE_MAC_ADDRESS(Mpdu->MacContext->MacAddress, macHeader->Address1);
    }
    else
    {
        // Else we use the HW packet filter and check if we have any MAC address that matches
        packetFilter = Hw->MacState.PacketFilter;
        addressMatch = FALSE;

        // Here we start from MAC context 0 since it can also receive probe responses as directed
        // packets
        for (i = 0; i < HW_MAX_NUMBER_OF_MAC; i++)        
        {
            currentMacContext = &Hw->MacContext[i];
            if (HW_MAC_CONTEXT_IS_VALID(currentMacContext))
            {
                // Check if this MAC has this address
                addressMatch = MP_COMPARE_MAC_ADDRESS(currentMacContext->MacAddress, macHeader->Address1);
                if (addressMatch)
                    break;
            }
        }
    }

    switch(macHeader->FrameControl.Type)
    {
    case DOT11_FRAME_TYPE_MANAGEMENT:
        if (addressMatch && (packetFilter & NDIS_PACKET_TYPE_802_11_DIRECTED_MGMT))
            return NDIS_STATUS_SUCCESS;
        
        if (Mpdu->DescStatus.Broadcast && (packetFilter & NDIS_PACKET_TYPE_802_11_BROADCAST_MGMT))
            return NDIS_STATUS_SUCCESS;

        if (Mpdu->DescStatus.Multicast  && (packetFilter & (NDIS_PACKET_TYPE_802_11_MULTICAST_MGMT | 
                                                           NDIS_PACKET_TYPE_802_11_ALL_MULTICAST_MGMT)))
            return NDIS_STATUS_SUCCESS;

        if (packetFilter & NDIS_PACKET_TYPE_802_11_PROMISCUOUS_MGMT)
        {
            Hw->Stats.PhyCounters[Mpdu->PhyId].ullPromiscuousReceivedFragmentCount++;
            return NDIS_STATUS_SUCCESS;
        }

        //
        // We are dropping this packet. Check if we should indicate it upto the helper port
        //
        if ((macHeader->FrameControl.Subtype == DOT11_MGMT_SUBTYPE_BEACON) ||
            (macHeader->FrameControl.Subtype == DOT11_MGMT_SUBTYPE_PROBE_RESPONSE))
        {
            //
            // The MAC context currently (if any) flagged to get this does not want it. 
            // So only the helper port gets this, change the MAC context in the MPDU. We may 
            // have updated statistics for the old MAC context, but that is OK.
            //
            Mpdu->MacContext = &Hw->MacContext[HW_HELPER_PORT_MAC_INDEX];
            return NDIS_STATUS_SUCCESS;
        }

        break;
        
    case DOT11_FRAME_TYPE_CONTROL:
        if (addressMatch && (packetFilter & NDIS_PACKET_TYPE_802_11_DIRECTED_CTRL))
            return NDIS_STATUS_SUCCESS;

        if (Mpdu->DescStatus.Broadcast && (packetFilter & NDIS_PACKET_TYPE_802_11_BROADCAST_CTRL))
            return NDIS_STATUS_SUCCESS;

        if (packetFilter & NDIS_PACKET_TYPE_802_11_PROMISCUOUS_CTRL)
        {
            Hw->Stats.PhyCounters[Mpdu->PhyId].ullPromiscuousReceivedFragmentCount++;
            return NDIS_STATUS_SUCCESS;
        }        

        //
        // Note: no multicast control frame.
        //

        break;
        
    case DOT11_FRAME_TYPE_DATA:
        if (addressMatch && (packetFilter & NDIS_PACKET_TYPE_DIRECTED))
            return NDIS_STATUS_SUCCESS;

        if (Mpdu->DescStatus.Broadcast && (packetFilter & NDIS_PACKET_TYPE_BROADCAST))
            return NDIS_STATUS_SUCCESS;

        if (Mpdu->DescStatus.Multicast && (packetFilter & NDIS_PACKET_TYPE_ALL_MULTICAST))
            return NDIS_STATUS_SUCCESS;

        if (Mpdu->DescStatus.Multicast && (packetFilter & NDIS_PACKET_TYPE_MULTICAST))
        {
            // Perform filtering for multicast addresses
            if (Mpdu->MacContext)
            {
                multicastAddressListCount = Mpdu->MacContext->MulticastAddressCount;
                multicastAddressList = Mpdu->MacContext->MulticastAddressList;
            }
            else
            {
                multicastAddressListCount = Hw->MacState.MulticastAddressCount;
                multicastAddressList = Hw->MacState.MulticastAddressList;
            }
            
            for (i = 0; i < multicastAddressListCount; i++)
            {
                if (MP_COMPARE_MAC_ADDRESS(macHeader->Address1, multicastAddressList[i]))
                {
                    return NDIS_STATUS_SUCCESS;
                }
            }
            return NDIS_STATUS_NOT_ACCEPTED;
        }

        if (packetFilter & (NDIS_PACKET_TYPE_PROMISCUOUS | 
                                          NDIS_PACKET_TYPE_802_11_RAW_DATA))
        {
            Hw->Stats.PhyCounters[Mpdu->PhyId].ullPromiscuousReceivedFragmentCount++;
            return NDIS_STATUS_SUCCESS;
        }

        break;
        
    default:
        //
        // Reserved packet should always be filtered
        //
        return NDIS_STATUS_NOT_ACCEPTED;
    }

    return NDIS_STATUS_NOT_ACCEPTED;
}

NDIS_STATUS
HwPrepareReceivedMPDU(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MPDU             Mpdu
    )
{
    NDIS_STATUS                 ndisStatus = NDIS_STATUS_SUCCESS;
    PDOT11_MAC_HEADER           macHeader;
    BOOLEAN                     goodMpdu = FALSE;
    
    do
    {
#if DBG
        if (Hw->RxInfo.Debug_BreakOnReceiveCount > 0)
        {
            // For debugging purpose
            if (MpDebugMatchPacketType(
                    Mpdu->DataStart,
                    Hw->RxInfo.Debug_BreakOnReceiveType,
                    Hw->RxInfo.Debug_BreakOnReceiveSubType,
                    &Hw->RxInfo.Debug_BreakOnReceiveDestinationAddress,
                    (Hw->RxInfo.Debug_BreakOnReceiveMatchSource ? &Hw->RxInfo.Debug_BreakOnReceiveSourceAddress
                        : NULL)
                    ))
            {
                if (Hw->RxInfo.Debug_BreakOnReceiveCount)
                {
                    DbgPrint("Receive packet matching Rx BreakOnReceive filter %p\n", Mpdu->DataStart);
                    Hw->RxInfo.Debug_BreakOnReceiveCount--;
                    DbgBreakPoint();
                }
            }
        }
#endif

        // The unicast bit is easy
        if (Mpdu->DescStatus.Unicast)
            Mpdu->MulticastDestination = FALSE;
        else        
            Mpdu->MulticastDestination = TRUE;

        // Check phy level state
        goodMpdu = HwCheckPhyParameters(Hw, Mpdu);

        // For some modes, we dont do a lot of work
        if (Hw->MacState.NetmonModeEnabled)
        {
            // If we are in netmon mode, we dont need to do much preparation
            Mpdu->RawPacket = TRUE;

            // Sometimes in netmon mode, we get partial packets that we shouldnt
            // indicate up. Drop those else the upper layers may crash
            if (Mpdu->DataLength < (4 + sizeof(DOT11_MAC_HEADER)))
            {
                ndisStatus = NDIS_STATUS_NOT_ACCEPTED;
            }
            
            // Indicate on MAC 1
            MPASSERT(HW_MAC_CONTEXT_IS_VALID(&Hw->MacContext[HW_DEFAULT_PORT_MAC_INDEX]));
            Mpdu->MacContext = &Hw->MacContext[HW_DEFAULT_PORT_MAC_INDEX];
            break;
        }

        if (!goodMpdu)
        {
            // This packet is bad at the PHY layer. Drop it now
            ndisStatus = NDIS_STATUS_NOT_ACCEPTED;
            break;
        }
        else
        {
            // The hardware includes the FCS in the DataLength. Adjust for that
            Mpdu->DataLength -= 4;
            if (Mpdu->DataLength < sizeof(DOT11_MAC_HEADER))
            {
                // Too short a packet. We just reject it
                ndisStatus = NDIS_STATUS_NOT_ACCEPTED;
                break;
            }

            if (Mpdu->DataLength > MAX_TX_RX_PACKET_SIZE)
            {
                // Too large a packet. We again reject it
                ndisStatus = NDIS_STATUS_NOT_ACCEPTED;
                break;
            }
        }

        //
        // Packet Integrity: From this point on, its verified that packets are
        // are atleast MAC_HEAER size
        //
        if (Hw->MacState.SafeModeEnabled)
        {
            // And set the encrypted bit based on whether or not the WEP bit is set
            // in the received packet
            macHeader = (PDOT11_MAC_HEADER)Mpdu->DataStart;
            Mpdu->Encrypted = (macHeader->FrameControl.WEP == 1) ? TRUE: FALSE;

            // Rest of the filtering happens as before
        }
        else
        {
            // Do anything that is relevant for ciphers
            ndisStatus = HwProcessRxCipher(Hw, Mpdu);
            if (ndisStatus != NDIS_STATUS_SUCCESS)
            {
                // Something went wrong when deciphering the packet. We wont try
                // to identify the MAC
                MpTrace(COMP_RECV, DBG_LOUD, ("Dropped RX MPDU as HwProcessRxCipher failed with status 0x%08x\n", ndisStatus));
                break;
            }
        }

        // Try to identify the MAC context that the packet must get indicated on
        ndisStatus = HwIdentifyReceiveMac(Hw, Mpdu);
        if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            // This is a bad packet that should not be indicate to any MACs
            MpTrace(COMP_RECV, DBG_LOUD, ("Dropped RX MPDU as HwIdentifyReceiveMac failed with status 0x%08x\n", ndisStatus));
            break;
        }


        // Check MAC level state information about the MPDU
        goodMpdu = HwCheckMacParameters(Hw, Mpdu);
        if (!goodMpdu)
        {
            // This packet is bad at the MAC layer. Drop it now
            ndisStatus = NDIS_STATUS_NOT_ACCEPTED;
            break;
        }

        //
        // Packet Integrity: From this point on, its verified that management and
        // data packets are atleast the minimum size required. Control packet length
        // must be verified
        //
        

        // Check if the HW layer wants to respond to this packet, etc
        // This is done before filtering since the HW layer may want to
        // respond to some packets that dont need to be indicated up
        ndisStatus = HwProcessReceivedMPDU(Hw, Mpdu);
        if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            // Mpdu should not be processed
            MpTrace(COMP_RECV, DBG_LOUD, ("Dropped RX MPDU as HwProcessReceivedMPDU failed with status 0x%08x\n", ndisStatus));
            ndisStatus = NDIS_STATUS_NOT_ACCEPTED;
            break;            
        }

        // Filter the non-important packets out
        ndisStatus = HwFilterMPDU(Hw, Mpdu);
        if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            break;
        }
    } while (FALSE);

    return ndisStatus;
}

NDIS_STATUS
HwSecurityCheck(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MSDU             Msdu
    )
{
    NDIS_STATUS                 ndisStatus = NDIS_STATUS_SUCCESS;
    PHW_RX_MPDU                 firstMpdu, currentMpdu;
    PHW_KEY_ENTRY               key;
    PHW_MAC_CONTEXT             macContext;
    UCHAR                       receivedMIC[HW_MIC_LENGTH];
    UCHAR                       calculatedMIC[HW_MIC_LENGTH];
    ULONG                       length, mpduIndex, offset, micIndex;
    ULONG                       usedLength;

    UNREFERENCED_PARAMETER(Hw);
    //
    // This function does the MIC failure checking and replay counter checking.
    // If either one is done by hardware, its respective portion can be skipped.
    //

    //
    // Get the key that decrypted this MSDU. If we did not decrypt this MSDU, as
    // indicated by a NULL key, simply return success.
    //
    MPASSERT(Msdu->MpduCount >= 1);
    _Analysis_assume_(Msdu->MpduCount >= 1);

    firstMpdu = Msdu->MpduList[0];
    key = firstMpdu->Key;
    if (key == NULL)
    {
        return NDIS_STATUS_SUCCESS;
    }

    // This should be skipped for safe mode and netmon mode
    MPASSERT((Hw->MacState.SafeModeEnabled == FALSE) && (Hw->MacState.NetmonModeEnabled == FALSE));

    // Since we have a key, we should have MAC context
    MPASSERT(firstMpdu->MacContext != NULL);
    macContext = firstMpdu->MacContext;

    if (key->Key.AlgoId == DOT11_CIPHER_ALGO_TKIP)
    {
        //
        // If the MSDU was decrypted by TKIP, check MIC and replay counter.
        // First, get the MIC field. Note that MIC could span across two or more MPDUs.
        //
        
        length = Msdu->DataLength;
        MPASSERT(length > HW_MIC_LENGTH);

        //
        // Find the MPDU that contains (at least the first byte of) MIC.
        // This is the MPDU which contains the 8th byte from the end of the MSDU
        //
        mpduIndex = 0;
        currentMpdu = firstMpdu;
        while ((length - currentMpdu->DataLength) >= HW_MIC_LENGTH)
        {
            length = length - currentMpdu->DataLength;
            mpduIndex++;
            currentMpdu = Msdu->MpduList[mpduIndex];
        }

        //
        // Found that MDL. Calculate the offset of MIC in the MDL. Copy MIC to our buffer.
        //
        offset = length - HW_MIC_LENGTH;
        usedLength = 0;
        for (micIndex = 0; micIndex < HW_MIC_LENGTH; micIndex++)
        {
            receivedMIC[micIndex] = *((PUCHAR)Add2Ptr(currentMpdu->DataStart, offset));
            offset++;
            usedLength++;

            if ((offset >= currentMpdu->DataLength) && (usedLength < HW_MIC_LENGTH))
            {
                // The rest of the MIC is in following MPDUs
                
                // Adjust the length of the MPDU to remove the MIC
                currentMpdu->DataLength -= usedLength;
                usedLength = 0;

                // Move to the next MPDU
                mpduIndex++;
                currentMpdu = Msdu->MpduList[mpduIndex];
                offset = 0;
            }
        }
        // Adjust the length of the MPDU to remove the MIC
        currentMpdu->DataLength -= usedLength;
        

        //
        // Shorten the total data by size of MIC. Then calculate the MIC based on
        // receiving data and Rx MIC key.
        //
        Msdu->DataLength -= HW_MIC_LENGTH;

        ndisStatus = HwCalculateRxMIC(Msdu, 0, key->Key.RxMICKey, calculatedMIC);
        MPASSERT(ndisStatus == NDIS_STATUS_SUCCESS);

        //
        // Compare the received MIC vs. calculated MIC. If there is mismatch, indicated
        // MIC failure.
        //
        if (NdisEqualMemory(receivedMIC, calculatedMIC, HW_MIC_LENGTH) != 1)
        {
        
            HwIndicateMICFailure(macContext,
                (PDOT11_MGMT_DATA_MAC_HEADER)firstMpdu->DataStart,
                ((key->PeerKeyIndex < DOT11_MAX_NUM_DEFAULT_KEY) ? key->PeerKeyIndex : 0),
                ((key->PeerKeyIndex < DOT11_MAX_NUM_DEFAULT_KEY) ? TRUE : FALSE)
                );

            MpTrace(COMP_RECV, DBG_SERIOUS, ("TKIP MIC failure detected\n"));

            if (firstMpdu->MulticastDestination)
            {
                macContext->MulticastCounters.ullTKIPLocalMICFailures++;
            }
            else
            {
                macContext->UnicastCounters.ullTKIPLocalMICFailures++;
            }
            return NDIS_STATUS_NOT_ACCEPTED;
        }

        //
        // Replay counter checking for TKIP. This is done after MIC verification. 
        // Two things are checked:
        //   1. The frame number of the first fragment in a MSDU must be greater than
        //      the replay counter for the key that decrypted the MSDU.
        //   2. The frame number of the all fragments in a MSDU must be monotonically
        //      increased, but not necessarily sequential (unlike in CCMP).
        //
        if (firstMpdu->FrameNumber <= key->ReplayCounter)
        {
            MpTrace(COMP_RECV, DBG_SERIOUS, ("TKIP Replay counter failed for first MPDU\n"));

            if (firstMpdu->MulticastDestination)
            {
                macContext->MulticastCounters.ullTKIPReplays++;
            }
            else
            {
                macContext->UnicastCounters.ullTKIPReplays++;
            }
            
            return NDIS_STATUS_NOT_ACCEPTED;
        }

        for (mpduIndex = 0; mpduIndex < (ULONG)Msdu->MpduCount - 1; mpduIndex++)
        {
            if (Msdu->MpduList[mpduIndex]->FrameNumber >= Msdu->MpduList[mpduIndex + 1]->FrameNumber)
            {
                MpTrace(COMP_RECV, DBG_SERIOUS, ("TKIP Replay counter failed for middle MPDU\n"));
                if (firstMpdu->MulticastDestination)
                {
                    macContext->MulticastCounters.ullTKIPReplays++;
                }
                else
                {
                    macContext->UnicastCounters.ullTKIPReplays++;
                }            
                return NDIS_STATUS_NOT_ACCEPTED;
            }
        }

        //
        // Passed replay counter check. Update the replay counter in the key to frame number of
        // the last fragment in the MSDU.
        //

        // MPDU Count > 0
        key->ReplayCounter = Msdu->MpduList[Msdu->MpduCount - 1]->FrameNumber;
    }
    else if (key->Key.AlgoId == DOT11_CIPHER_ALGO_CCMP)
    {
        //
        // If the MSDU was decrypted by CCMP, check replay counter.
        // Two things are checked:
        //   1. The frame number of the first fragment in a MSDU must be greater than
        //      the replay counter for the key that decrypted the MSDU.
        //   2. The frame number of the all fragments in a MSDU must be sequential.
        //
        if (firstMpdu->FrameNumber <= key->ReplayCounter)
        {
            MpTrace(COMP_RECV, DBG_SERIOUS, ("CCMP Replay counter failed for first MPDU\n"));

            if (firstMpdu->MulticastDestination)
            {
                macContext->MulticastCounters.ullCCMPReplays++;
            }
            else
            {
                macContext->UnicastCounters.ullCCMPReplays++;
            }
            
            return NDIS_STATUS_NOT_ACCEPTED;
        }

        for (mpduIndex = 0; mpduIndex < (ULONG)Msdu->MpduCount - 1; mpduIndex++)
        {
            if ((Msdu->MpduList[mpduIndex]->FrameNumber + 1) != Msdu->MpduList[mpduIndex + 1]->FrameNumber)
            {
                MpTrace(COMP_RECV, DBG_SERIOUS, ("TKIP Replay counter failed for middle MPDU\n"));
                if (firstMpdu->MulticastDestination)
                {
                    macContext->MulticastCounters.ullCCMPReplays++;
                }
                else
                {
                    macContext->UnicastCounters.ullCCMPReplays++;
                }            
                return NDIS_STATUS_NOT_ACCEPTED;
            }
        }

        //
        // Passed replay counter check. Update the replay counter in the key to frame number of
        // the last fragment in the MSDU.
        //
        key->ReplayCounter = Msdu->MpduList[Msdu->MpduCount - 1]->FrameNumber;

    }

    // Update the decryption counters
    if (firstMpdu->MulticastDestination)
    {
        macContext->MulticastCounters.ullDecryptSuccessCount++;
    }
    else
    {
        macContext->UnicastCounters.ullDecryptSuccessCount++;
    }

    return NDIS_STATUS_SUCCESS;
}

NDIS_STATUS
HwPrepareReceivedMSDU(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MSDU             Msdu
    )
{
    NDIS_STATUS                 ndisStatus = NDIS_STATUS_SUCCESS;
    PHW_RX_MPDU                 firstMpdu;
    PDOT11_MAC_HEADER           macHeader;
    UCHAR                       headerSize;
    ULONG                       totalMsduLength, i;
    PHW_MAC_CONTEXT             macContext;

    do
    {
        //
        // Compute the real length of the MSDU
        //
        firstMpdu = Msdu->MpduList[0];

        // The length of the packet header
        macHeader = (PDOT11_MAC_HEADER)firstMpdu->DataStart;
        if ((macHeader->FrameControl.Type == DOT11_FRAME_TYPE_DATA) ||
                 (macHeader->FrameControl.Type == DOT11_FRAME_TYPE_MANAGEMENT))
        {
            if ((macHeader->FrameControl.ToDS == 1) && (macHeader->FrameControl.FromDS == 1))
                headerSize = DOT11_DATA_LONG_HEADER_SIZE;
            else
                headerSize = DOT11_DATA_SHORT_HEADER_SIZE;        
        }
        else
        {
            // Control
            headerSize = sizeof(DOT11_MAC_HEADER);
        }

        // For fragmented packets, remove the headers from second MPDU
        // onwards. The first MPDU does not get modified
        totalMsduLength = firstMpdu->DataLength;

        for (i = 1; i < Msdu->MpduCount; i++)
        {
            // For the total length of the MSDU we are NOT including the
            // fragment header length the MPDUs
            totalMsduLength = totalMsduLength + Msdu->MpduList[i]->DataLength 
                                - headerSize;

            // Adjust the MPDU's data start & data length
            Msdu->MpduList[i]->DataStart += headerSize;
            Msdu->MpduList[i]->DataLength -= headerSize;
            
        }
        Msdu->DataLength = totalMsduLength;

        //
        // Do security checking. This includes
        // MIC failure checking and replay counter checking.
        //
        ndisStatus = HwSecurityCheck(Hw, Msdu);
        if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            break;
        }

        // Update the frame statistics
        macContext = HW_MAC_CONTEXT_FOR_RX_STATISTICS(Hw, firstMpdu);     
        if (firstMpdu->MulticastDestination)
        {
            macContext->MulticastCounters.ullReceivedFrameCount++;
        }
        else
        {
            macContext->UnicastCounters.ullReceivedFrameCount++;
        }
        Hw->Stats.PhyCounters[firstMpdu->PhyId].ullReceivedFrameCount++;

        ndisStatus = NDIS_STATUS_SUCCESS;
    }while (FALSE);

    return ndisStatus;
}


// Lock held (or called from Initialize)
// Used to return a RxMPDU to the HAL for filling
__inline VOID
HwSubmitRxMPDU(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MPDU             Mpdu
    )
{
    //
    // Return this to the HAL
    //
    HalReturnRxDesc(Hw->Hal,
        MAX_TX_RX_PACKET_SIZE,
        (PUCHAR)Mpdu->BufferVa,
        Mpdu->BufferPa,
        &Mpdu->DescIter
        );

    // Add this to the RX queue
    InsertTailList(&Hw->RxInfo.AvailableMPDUList, &Mpdu->ListEntry);

    HW_INCREMENT_AVAILABLE_RX_MPDU(Hw);
}
VOID
HwReturnRxMPDU(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MPDU             Mpdu,
    _In_  BOOLEAN                 DispatchLevel
    )
{
    BOOLEAN                     addToUnusedList = TRUE;

    HW_REINITIALIZE_RX_MPDU(Mpdu);
    
    HW_RX_ACQUIRE_LOCK(Hw, DispatchLevel);    
    if (Hw->RxInfo.NumMPDUAvailable >= (LONG)Hw->RegInfo.NumRXBuffers)
    {
#if 0 
        ULONG                       percentMPDUListUnunsed;
        //
        // Check if we need to shrink the Rx MSDU list.
        // We have been sampling how many Rx MSDUs remain unused at every
        // check for hang interval. If the average number of free Rx MSDUs remain
        // above a threshold for a statistically significant time interval,
        // we will free this Rx MSDU in order to shrink the MSDU list.
        //
        if (Hw->RxInfo.UnusedMPDUListSampleTicks > HW_RX_MSDU_LIST_SAMPLING_PERIOD)
        {
            
            //
            // Determine the percentage of Rx MSDU list that has remained underutilized
            // for last RxMSDUListSampleTicks time interval
            //
            percentMPDUListUnunsed = 
                      ((Hw->RxInfo.NumMPDUUnused * 100)  / (Hw->RxInfo.UnusedMPDUListSampleTicks * Hw->RxInfo->NumMPDUAllocated))));

            //
            // Restart sampling for the next sampling period
            //
            Hw->RxInfo.UnusedMPDUListSampleTicks = 0;
            Hw->RxInfo.UnusedMPDUListSampleCount = 0;
            
            if (percentMPDUListUnunsed > HW_RX_MSDU_LIST_UNDER_UTILIZATION_THRESHOLD)
            {
                MpTrace(COMP_RECV, DBG_NORMAL,  ("Shrinking the Rx MPDU pool. Current Size: %d   Percentage Under Utilization: %d\n",
                                Hw->RxInfo->NumMPDUAllocated, percentMPDUListUnunsed));
                //
                // The threshold was exceeded, lets free this MSDU
                //

                //
                // We will free the last fragment in this Rx MSDU and return
                // the rest of the fragments to the Hw11 for reuse
                //
                Hw11FreeFragment(
                    pAdapter->pNic,
                    pMpRxd->Fragments[--pMpRxd->FragmentCount]
                    );
                NextNetBufferList = NET_BUFFER_LIST_NEXT_NBL(CurrentNetBufferList);
                MpDropRxMSDU(pAdapter, pMpRxd, DispatchLevel);
                MpFreeRxMSDU(pAdapter, pMpRxd);
                MP_DECREMENT_TOTAL_RX_MSDU_ALLOCATED(pAdapter);
                addToUnusedList = FALSE;
            }


        }
#endif
        if (addToUnusedList)
        {
            //
            // If the MPDUs available to HAL is at the maximum posible value already
            // in we cannot add any right now. We put this in the Unused list and
            // would transfer it later when we remove some from the HAL (on a receive
            // indication)
            // 
            InsertTailList(&Hw->RxInfo.UnusedMPDUList, &Mpdu->ListEntry);
            HW_INCREMENT_UNUSED_RX_MPDU(Hw);
        }
    }
    else 
    {
        //
        // Return the MPDU to the HAL
        //
        HwSubmitRxMPDU(Hw, Mpdu);
    }
    HW_RX_RELEASE_LOCK(Hw, DispatchLevel);
}

NDIS_STATUS
HwGrowMPDUPool(
    _In_  PHW                     Hw,
    _In_  ULONG                   NumToAllocate
    )
{
    ULONG                       i;
    PHW_RX_MPDU                 mpdu;
    NDIS_STATUS                 ndisStatus = NDIS_STATUS_SUCCESS;

    for (i = 0; i < NumToAllocate; i++) 
    {
        // Allocate an MPDU structure
        mpdu = NdisAllocateFromNPagedLookasideList(&Hw->RxInfo.RxFragmentLookaside);
        if (mpdu == NULL)
        {
            MpTrace(COMP_SEND, DBG_NORMAL, ("Unable to allocate extra HW_RX_MPDU %d\n", i));
            ndisStatus = NDIS_STATUS_RESOURCES;
            break;
        }
        // Initialize the data structure
        HW_INITIALIZE_RX_MPDU(mpdu, Hw);

        // Increment the adapter ref count. This blocks resets, pauses from proceeding
        HW_INCREMENT_ACTIVE_OPERATION_REF(Hw);

        // Call the Async allocate handler. This calls HWAllocateComplete
        if ((ndisStatus = NdisMAllocateSharedMemoryAsyncEx(
                Hw->MiniportDmaHandle,
                MAX_TX_RX_PACKET_SIZE,
                FALSE,
                mpdu
                )) == NDIS_STATUS_FAILURE)
        {
            ndisStatus=NDIS_STATUS_RESOURCES;
            MpTrace(COMP_SEND, DBG_SERIOUS, ("Allocation of Extra RX buffer %d failed\n", i));
            NdisFreeToNPagedLookasideList(&Hw->RxInfo.RxFragmentLookaside, mpdu);
            
            // Remove the ref
            HW_DECREMENT_ACTIVE_OPERATION_REF(Hw);
            break;
        }

    }
    
    return ndisStatus;
}

VOID 
HWAllocateComplete(
    _In_  NDIS_HANDLE             MiniportAdapterContext,
    _In_  PVOID                   VirtualAddress,
    _In_  PNDIS_PHYSICAL_ADDRESS  PhysicalAddress,
    _In_  ULONG                   Length,
    _In_  PVOID                   Context
    )
{
    PHW_RX_MPDU                 mpdu;
    PHW                         hw;

    UNREFERENCED_PARAMETER(Length);
    
    mpdu = (PHW_RX_MPDU)Context;

    // We need the hardware context for the completion and not the AdapterContext
    // which is passed into the SharedMemoryComplete function
    UNREFERENCED_PARAMETER(MiniportAdapterContext);
    hw = mpdu->Hw;
    
    if (VirtualAddress == NULL) 
    {
        MpTrace(COMP_SEND, DBG_SERIOUS, ("Allocation of Extra RX buffer failed\n"));
        NdisFreeToNPagedLookasideList(&hw->RxInfo.RxFragmentLookaside, mpdu);
        HW_DECREMENT_ACTIVE_OPERATION_REF(hw);
    }
    else 
    {
        mpdu->BufferVa = VirtualAddress;
        mpdu->BufferPa = *PhysicalAddress;

        // Increment the counter for total number of MPDU allocated
        HW_INCREMENT_TOTAL_RX_MPDU_ALLOCATED(hw);

        // Return the MPDU. This may either go to the hardware
        // or the unused list depending on how many are free
        HwReturnRxMPDU(hw, mpdu, FALSE); // NOT Called at DISPATCH

        // Remove the ref
        HW_DECREMENT_ACTIVE_OPERATION_REF(hw);
    }
}



VOID
HwReturnRxMSDU(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MSDU             Msdu,
    _In_  BOOLEAN                 DispatchLevel
    )
{
    ULONG                       i;

    // Return the MPDUs    
    for (i = 0; i < Msdu->MpduCount; i++)
    {
        HwReturnRxMPDU(Hw, Msdu->MpduList[i], DispatchLevel);
    }

    Msdu->MpduCount = 0;

    HwFreeRxMSDU(Hw, Msdu);
}

VOID
HwIndicateReceivedMSDUs(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MSDU             MsduList,
    _In_  ULONG                   ReceiveFlags
    )
{
    PHW_RX_MSDU                 currentMsdu, nextMsdu;
    PHW_RX_MPDU                 firstMpdu;
    PDOT11_MAC_HEADER           macHeader;
    PHW_MAC_CONTEXT             macContext;
    BOOLEAN                     returnMSDU = TRUE;
    ULONG                       i;

    currentMsdu = MsduList;
    while (currentMsdu != NULL)
    {
        nextMsdu = currentMsdu->Next;
        currentMsdu->Next = NULL;

        // If we indicate with status resources, the MSDU
        // wont be returned, else it would be
        returnMSDU = TRUE;
        
        firstMpdu = currentMsdu->MpduList[0];
        macHeader = (PDOT11_MAC_HEADER)firstMpdu->DataStart;

        // If a packet is a management beacon or probe response packet, 
        // it must be indicated to the HELPER_PORT mac context
        if ((macHeader->FrameControl.Type == DOT11_FRAME_TYPE_MANAGEMENT) &&
            ((macHeader->FrameControl.Subtype == DOT11_MGMT_SUBTYPE_BEACON) ||
             (macHeader->FrameControl.Subtype == DOT11_MGMT_SUBTYPE_PROBE_RESPONSE)))
        {
            // All management packet are indicated to helper port. Indication
            // is with status resources since we dont expect the helper port to ever
            // indicate these packets up to the OS
            HwIndicateMSDUOnMACContext(
                &Hw->MacContext[HW_HELPER_PORT_MAC_INDEX],
                currentMsdu, 
                ReceiveFlags | NDIS_RECEIVE_FLAGS_RESOURCES
                );
        }

        //
        // Some packets only need to be indicated to the helper port. Lets check
        // if this is one of them
        //
        if (firstMpdu->MacContext != &Hw->MacContext[HW_HELPER_PORT_MAC_INDEX])
        {
            if (firstMpdu->MacContext != NULL)
            {
                // Mac context has been identified
                macContext = firstMpdu->MacContext;
                
                MPASSERT(HW_MAC_CONTEXT_IS_VALID(macContext));

                // Indicate this to the specific MAC
                HwIndicateMSDUOnMACContext(
                    macContext,
                    currentMsdu, 
                    ReceiveFlags
                    );

                // The MSDU would be returned from the return handler
                returnMSDU = FALSE;
            }
            else
            {
                // Indicate the MSDU to all the valid MACs. Everyone gets the packet
                // with status resources
                
                for (i = HW_DEFAULT_PORT_MAC_INDEX; i < HW_MAX_NUMBER_OF_MAC; i++)
                {
                    macContext = &Hw->MacContext[i];

                    if (HW_MAC_CONTEXT_IS_VALID(macContext))
                    {
                        // Indicate this the specific MAC
                        HwIndicateMSDUOnMACContext(
                            macContext,
                            currentMsdu, 
                            ReceiveFlags | NDIS_RECEIVE_FLAGS_RESOURCES
                            );
                    }
                }
            }
        }
        
        // We return the MSDU if either we indicate with resources
        // or the caller set the flag that it should be indicated with 
        // status resources
        if ((returnMSDU == TRUE) ||
            (NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags)))
        {
            // The MSDU is done. Return
            HwReturnRxMSDU(
                Hw, 
                currentMsdu, 
                (NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags) ? TRUE : FALSE)
                );
        }

        currentMsdu = nextMsdu;
    }
}

PMP_RX_MSDU
HwMapHwRxMSDUToMpRxMSDU(
    _In_  PHW_RX_MSDU             Msdu
    )
{
    PMP_RX_MSDU                 mpMsdu;
    PMP_RX_MPDU                 mpMpdu;
    PHW_RX_MPDU                 mpdu;
    ULONG                       i;

    // The MP_RX_MSDU is preallocated as part of the HW_RX_MSDU
    mpMsdu = Msdu->MpMsdu;

    for (i = 0; i < Msdu->MpduCount; i++)
    {
        // Populate the MP_RX_MPDU
        mpdu = Msdu->MpduList[i];
        mpMpdu = mpdu->MpMpdu;
        mpMpdu->Data = mpdu->DataStart;
        mpMpdu->DataLength = mpdu->DataLength;
        mpMpdu->Msdu = mpMsdu;
        
        // Add it to the list
        mpMsdu->MpduList[i] = mpMpdu;
    }
    mpMsdu->MpduCount = Msdu->MpduCount;

    // Copy the receive context from the first MPDU
    mpdu = Msdu->MpduList[0];

    mpMsdu->RecvContext.lRSSI = mpdu->RSSI;
    mpMsdu->RecvContext.ucDataRate = (UCHAR)mpdu->Rate;
    mpMsdu->RecvContext.uPhyId = mpdu->PhyId;
    mpMsdu->RecvContext.uReceiveFlags = (mpdu->RawPacket ? DOT11_RECV_FLAG_RAW_PACKET : 0);
    mpMsdu->RecvContext.usNumberOfMPDUsReceived = mpMsdu->MpduCount;
    mpMsdu->Channel = (UCHAR)mpdu->Channel;
    mpMsdu->RecvContext.uChCenterFrequency = HwChannelToFrequency(mpMsdu->Channel);
    mpMsdu->LinkQuality = mpdu->LinkQuality;
    mpMsdu->Flags = (mpdu->Encrypted ? MP_RX_MSDU_FLAG_ENCRYPTED : 0);

    mpMsdu->Next = NULL;

    return mpMsdu;
}

VOID
HwIndicateMSDUOnMACContext(
    _In_  PHW_MAC_CONTEXT         MacContext,
    _In_  PHW_RX_MSDU             Msdu,
    _In_  ULONG                   ReceiveFlags
    )
{
    PMP_RX_MSDU                 mpMsdu;

    mpMsdu = HwMapHwRxMSDUToMpRxMSDU(Msdu);

    // Add a ref count so that context dont change
    // while packets are pending on the MAC
    HW_ADD_MAC_CONTEXT_RECV_REF(MacContext, 1);

    // We only indicate packets on running ports. Else we may
    // end up indicating packets up to ports that are in the middle of
    // opmode change, etc
    if (!HW_TEST_MAC_CONTEXT_STATUS(MacContext, HW_MAC_CONTEXT_CANNOT_SEND_FLAGS))
    {
        // Call Hvl
        Hvl11IndicateReceivePackets(MacContext->VNic,
            mpMsdu,
            ReceiveFlags
            );
    }
    else
    {
        // Cannot indicate this up
        if (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags))
        {
            // Return the packet back to the HW
            Hw11ReturnPackets(MacContext, 
                mpMsdu, 
                (NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags) ? 
                    NDIS_RETURN_FLAGS_DISPATCH_LEVEL: 0)
                );
        }
    }

    // If the packet cannot pend at the above layer remove the
    // the refcount that blocks context switches, pauses, etc
    if (NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags))
        HW_REMOVE_MAC_CONTEXT_RECV_REF(MacContext, 1);  
}


__inline NDIS_STATUS
HwAddRxMPDUToMSDU(
    _In_  PHW_RX_MSDU                 Msdu,
    _In_  PHW_RX_MPDU                 Mpdu,
    _In_  PDOT11_MGMT_DATA_MAC_HEADER FragmentHeader,
    _In_  UCHAR                       FragNumber
    )
{
    NDIS_STATUS     ndisStatus = NDIS_STATUS_PENDING;
    
    do
    {
        //
        // If this is a duplicate fragment, drop it
        //
        if (Msdu->MpduList[FragNumber] != NULL)
        {
            MpTrace(COMP_RECV, DBG_LOUD,  ("Duplicate fragment received\n"));
            ndisStatus = NDIS_STATUS_NOT_ACCEPTED;
            break;
        }
        
        //
        // If a fragment in this 802.11 packet was lost, fail the entire packet
        //
        if (FragNumber > Msdu->MpduCount)
        {
            MpTrace(COMP_RECV, DBG_LOUD,  ("A fragment in the packet was lost. Dropping packet\n"));
            ndisStatus = NDIS_STATUS_FAILURE;
            break;
        }
        
        //
        // Is this the last fragment in the frame, we are done
        //
        if (FragmentHeader->FrameControl.MoreFrag == 0)
        {
            //
            // Tell the caller that the Rx MSDU is now complete.
            //
            ndisStatus = NDIS_STATUS_SUCCESS;
        }
        else
        {
            if(FragmentHeader->FrameControl.Type != DOT11_FRAME_TYPE_DATA)
            {
                //
                // Only data frames can be fragmented. Drop the frame if it is suspicious.
                //
                MpTrace(COMP_RECV, DBG_LOUD,  ("Error! A non-data frame was received which is fragmented!\n"));
                ndisStatus = NDIS_STATUS_FAILURE;
                break;
            } else
            {
                ndisStatus = NDIS_STATUS_PENDING;
            }
        }

        //
        // This is a good fragment. Add it to the MSDU
        //
        Msdu->MpduList[FragNumber] = Mpdu;
        Msdu->MpduCount++;
        
    } while(FALSE);
    
    return ndisStatus;
}


NDIS_STATUS
HwAddPartialMSDUToReassemblyLine(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MSDU             Msdu,
    _In_  BOOLEAN                 DispatchLevel
    )
{
    USHORT                      i;

    //
    // If we are running low on reassembly line space,
    // expire some ReceiveLifeTime packets
    //
    if (Hw->RxInfo.TotalRxMSDUInReassembly >= HW_REASSEMBLY_LINE_LOW_RESOURCES_THRESHOLD)
    {
        HwExpireReassemblingMSDUs(Hw, DispatchLevel);
    }

    for(i = 0; i < HW_MAX_REASSEMBLY_LINE_SIZE; i++)
    {
        if(Hw->RxInfo.ReassemblyLine[i] == NULL)
        {
            //
            // Found an empty spot for this Rx MSDU in the assembly line
            //
            Hw->RxInfo.ReassemblyLine[i] = Msdu;
            Hw->RxInfo.TotalRxMSDUInReassembly++;
            
            //
            // store position index in the Rx MSDU & Timestamp this MSDU
            //
            Msdu->ReassemblyLinePosition = i;
            HW_SET_RX_EXPIRATION_TIME(Msdu, Hw->MacState.MaxReceiveLifetime);
            
            //
            // The Most Recently Used Assembly Rx MSDU becomes this one
            // This gives us an optimization by avoiding search
            // as we expect next receive to belong to this MSDU
            //
            Hw->RxInfo.MRUReassemblyRxMSDU = Msdu;
            
            return NDIS_STATUS_SUCCESS;
        }
    }

    return NDIS_STATUS_RESOURCES;
}


__inline PHW_RX_MSDU
HwFindPartialMSDUInReassemblyLine(
    _In_  PHW                     Hw,
    _In_  PDOT11_MGMT_DATA_MAC_HEADER FragmentHdr, 
    _In_  USHORT                  SequenceNumber
    )
{
    PHW_RX_MSDU                 msdu;
    USHORT                      i;
    
    //
    // First check the MRU Reassembly MSDU.
    // Make sure there is some Rx MSDU in reassembly
    //
    if (Hw->RxInfo.TotalRxMSDUInReassembly == 0)
    {
        return NULL;
    }
    else
    {
        msdu = Hw->RxInfo.MRUReassemblyRxMSDU;
    }
    if (msdu && HW_REASSEMBLY_RX_MSDU_MATCH(msdu, 
                SequenceNumber, &FragmentHdr->Address2))
    {
        //
        // Match found. Optimal search.
        //
        return msdu;
    }
    else
    {
        //
        // We will have to go through the reassembly line and
        // search manually
        //
        for(i = 0; i < HW_MAX_REASSEMBLY_LINE_SIZE; i++)
        {
            msdu = Hw->RxInfo.ReassemblyLine[i];
            if (msdu && HW_REASSEMBLY_RX_MSDU_MATCH(msdu, 
                        SequenceNumber, &FragmentHdr->Address2))
            {
                //
                // Found the match through manual search
                // Set this as the MRU and return it
                //
                MpTrace(COMP_RECV, DBG_LOUD,  ("*** UNOPTIMAL SRCH ***\n"));
                Hw->RxInfo.MRUReassemblyRxMSDU = msdu;
                return msdu;
            }
        }

        return NULL;
    }
}



VOID
HwRemoveMSDUFromReassemblyLine(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MSDU             Msdu
    )
{
    //
    // The position of this Rx MSDU in the Reassembly line is
    // stored in the MSDU. Use that to quickly remove the
    // Rx MSDU from reassembly line
    //
    MPASSERT(Hw->RxInfo.ReassemblyLine[Msdu->ReassemblyLinePosition] == Msdu);
    Hw->RxInfo.ReassemblyLine[Msdu->ReassemblyLinePosition] = NULL;
    Hw->RxInfo.TotalRxMSDUInReassembly--;

    //
    // If this is the MRU Reassembly MSDU, clear that
    //
    if (Hw->RxInfo.MRUReassemblyRxMSDU == Msdu)
        Hw->RxInfo.MRUReassemblyRxMSDU = NULL;

    MPASSERT(Msdu->Next == NULL);
}


__inline VOID
HwExpireReassemblingMSDUs(
    _In_  PHW                     Hw,
    _In_  BOOLEAN                 DispatchLevel
    )
{
    ULONG                       i, numReassemblies;
    PHW_RX_MSDU                 msdu;
    LARGE_INTEGER               currentTickCount;

    numReassemblies = Hw->RxInfo.TotalRxMSDUInReassembly;   // for optimization
    
    //
    // Get the current time
    //
    KeQueryTickCount(&currentTickCount);
    
    for(i=0; ((numReassemblies > 0) && (i < HW_REASSEMBLY_LINE_LOW_RESOURCES_THRESHOLD)); i++)
    {
        if ((msdu = Hw->RxInfo.ReassemblyLine[i]) != NULL)
        {
            //
            // If this packet has exceeded the MaxRxLifetime, expire it
            //
            if (HW_GET_RX_EXPIRATION_TIME(msdu) < (ULONGLONG)currentTickCount.QuadPart)
            {
                MpTrace(COMP_RECV, DBG_NORMAL,  ("Expiring Rx MSDU with Seq: %d\n", msdu->SequenceNumber));
                HwRemoveMSDUFromReassemblyLine(Hw, msdu);
                HwReturnRxMSDU(Hw, msdu, DispatchLevel);
                Hw->Stats.NumRxReassemblyError++;
            }

            //
            // We found one more Reassembly.
            //
            numReassemblies--;
        }
    }
}

VOID
HwFlushMSDUReassemblyLine(
    _In_  PHW                     Hw
    )
{
    ULONG                       i;
    PHW_RX_MSDU                 msdu;
    
    for(i=0; i < HW_MAX_REASSEMBLY_LINE_SIZE; i++)
    {
        if ((msdu = Hw->RxInfo.ReassemblyLine[i]) != NULL)
        {
            HwRemoveMSDUFromReassemblyLine(Hw, msdu);
            HwReturnRxMSDU(Hw, msdu, FALSE);
            Hw->Stats.NumRxReassemblyError++;
        }
    }
}

PHW_RX_MSDU
HwBuildMSDUForMPDU(
    _In_  PHW                     Hw,
    _In_  PHW_RX_MPDU             Mpdu
    )
{
    NDIS_STATUS                 ndisStatus = NDIS_STATUS_SUCCESS;
    PHW_RX_MSDU                 msdu = NULL;
    PDOT11_MGMT_DATA_MAC_HEADER fragmentHeader;
    DOT11_SEQUENCE_CONTROL      sequenceControl;
    
    //
    // If we are in safe mode or in netmon mode, we dont need to
    // do any reassembly. We just need to convert the MPDU to MSDU
    // and we are done
    //
    if (Hw->MacState.NetmonModeEnabled || Hw->MacState.SafeModeEnabled)
    {
        // Create a MSDU and we are done
        msdu = HwAllocateRxMSDU(Hw);
        if (msdu == NULL)
        {
            // Unable to allocate a MSDU
            MpTrace(COMP_RECV, DBG_LOUD,  ("Unable to allocate RX MSDU for received fragment\n"));
            HwReturnRxMPDU(Hw, Mpdu, TRUE);
            return NULL;    // NDIS_STATUS_RESOURCES
        }

        // Populate the MPDU into the MSDU
        msdu->MpduList[0] = Mpdu;
        msdu->MpduCount++;

        return msdu;
    }

    //
    // Else we attempt fragment reassembly
    //
    fragmentHeader = (PDOT11_MGMT_DATA_MAC_HEADER)Mpdu->DataStart;

    //
    // If this is a new fragment for a new frame, we need an
    // Rx MSDU for it. We need sequence number to determine this.
    // Control frames will not have Sequence Control field.
    // 
    if (fragmentHeader->FrameControl.Type != DOT11_FRAME_TYPE_CONTROL)
    {
        NdisMoveMemory(&sequenceControl.usValue, &fragmentHeader->SequenceControl, 2);
    }
    else
    {
        sequenceControl.usValue = 0;
    }

    if (sequenceControl.FragmentNumber == 0)
    {
        //
        // This is a new fragment. Allocate an Rx MSDU for it.
        //
        msdu = HwAllocateRxMSDU(Hw);
        if (msdu == NULL)
        {
            // Unable to allocate a MSDU
            MpTrace(COMP_RECV, DBG_LOUD,  ("Unable to allocate RX MSDU for received fragment\n"));
            HwReturnRxMPDU(Hw, Mpdu, TRUE);
            return NULL;    // NDIS_STATUS_RESOURCES;
        }
        
        //
        // Add the received fragment to this MSDU.
        //
        ndisStatus = HwAddRxMPDUToMSDU(msdu, Mpdu, fragmentHeader,(UCHAR)sequenceControl.FragmentNumber);
        if(ndisStatus == NDIS_STATUS_PENDING)
        {
            MPASSERT(fragmentHeader->FrameControl.Type == DOT11_FRAME_TYPE_DATA);

            //
            // We expect more fragments to arrive for this MSDU.
            // Save the MAC address in MSDU. Need to avoid confusion if two
            // different stations transmit MSDUs with the same sequence number.
            //
            NdisMoveMemory(
                &msdu->PeerAddress,
                &fragmentHeader->Address2,
                sizeof(DOT11_MAC_ADDRESS)
                );
                
            //
            // Also save the sequence number in the MSDU
            //
            msdu->SequenceNumber = sequenceControl.SequenceNumber;
            
            //
            // Add this Rx MSDU into the Reassembly Line
            //
            if (HwAddPartialMSDUToReassemblyLine(Hw, msdu, TRUE) != NDIS_STATUS_SUCCESS)
            {
                MpTrace(COMP_RECV, DBG_LOUD, ("Failed to find a place in Reassembly line. Dropping frame\n"));

                // Return the MSDU
                HwReturnRxMSDU(Hw, msdu, TRUE);
                Hw->Stats.NumRxReassemblyError++;                
                return NULL; // NDIS_STATUS_RESOURCES;
            }

            //
            // More fragments expected. Its already added to the list
            //
            return NULL; // NDIS_STATUS_PENDING;
        }
        else if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            MpTrace(COMP_RECV, DBG_LOUD,  ("Dropping duplicate or out of order fragment\n"));
            Hw->Stats.NumRxReassemblyError++;
            // Free the newly allocated MSDU
            HwReturnRxMSDU(Hw, msdu, TRUE);

            // And the current MPDU that we failed to add to the MSDU
            HwReturnRxMPDU(Hw, Mpdu, TRUE);
            
            return NULL; // FAILURE;
        }
        else
        {
            //
            // Fragment is the only one 
            //
            return msdu;
        }
    }
    else
    {
        //
        // This is potentially a fragment belonging to an Rx MSDU in reassembly
        // Find the Rx MSDU it belongs to.
        //
        msdu = HwFindPartialMSDUInReassemblyLine(Hw, fragmentHeader, sequenceControl.SequenceNumber);
        if (msdu == NULL)
        {
            //
            // There is no Rx MSDU is reassembly for this fragment. Return it.
            // This implies first fragment was never received successfully
            // or that there is a malicious station on the BSS
            //
            MpTrace(COMP_RECV, DBG_LOUD,  ("Could not find Rx MSDU for seq num: %d\n", sequenceControl.SequenceNumber));
            HwReturnRxMPDU(Hw, Mpdu, TRUE);
            Hw->Stats.NumRxReassemblyError++;
            return NULL; // NDIS_STATUS_NOT_ACCEPTED;
        }
        
        //
        // Add this fragment to the MSDU
        //
        ndisStatus = HwAddRxMPDUToMSDU(msdu, Mpdu, fragmentHeader, (UCHAR)sequenceControl.FragmentNumber);
        switch(ndisStatus)
        {
            case NDIS_STATUS_SUCCESS:
                //
                // The MSDU is complete. We will be indicating this Rx MSDU up.
                // Remove from reassembly line
                //
                HwRemoveMSDUFromReassemblyLine(Hw, msdu);
                return msdu;

            case NDIS_STATUS_PENDING:
                //
                // There are more fragments to come. We dont indicate this up
                //
                return NULL;

            case NDIS_STATUS_NOT_ACCEPTED:
                //
                // This fragment could not be accepted. Return it to hardware.
                //
                MpTrace(COMP_RECV, DBG_LOUD,  ("Dropping the fragment received\n"));
                HwReturnRxMPDU(Hw, Mpdu, TRUE);
                return NULL;

            case NDIS_STATUS_FAILURE:
                //
                // The Rx MSDU has failed. We need to drop the entire frame.
                //
                MpTrace(COMP_RECV, DBG_LOUD,  ("Dropping the entire 802.11 frame in reassembly\n"));
                HwRemoveMSDUFromReassemblyLine(Hw, msdu);
                HwReturnRxMSDU(Hw, msdu, TRUE);

                // Free the MPDU also. Its not yet added to the MSDU
                HwReturnRxMPDU(Hw, Mpdu, TRUE);
                Hw->Stats.NumRxReassemblyError++;
                return NULL;

            default:
                MPASSERT(FALSE);
        }

        return NULL;
    }
    
}

VOID
HwWaitForPendingReceives(
    _In_  PHW                     Hw,
    _In_opt_ PHW_MAC_CONTEXT      MacContext
    )
{
    UCHAR                       i;

    //
    // Wait for receives to return
    //
    if (MacContext != NULL)
    {
        // Only wait for receives from this MAC_CONTEXT to return
        while (MacContext->RecvRefCount > 0)
            NdisMSleep(1000);
    }
    else
    {
        for (i = 0; i < HW_MAX_NUMBER_OF_MAC; i++)
        {
            while (Hw->MacContext[i].RecvRefCount > 0)
                NdisMSleep(1000);
        }
    }
}

VOID
HwResetReceiveEngine(
    _In_  PHW                     Hw,
    _In_  BOOLEAN                 DispatchLevel
    )
{
    PHW_RX_MPDU                 mpdu;
    LONG                        loopCount = 0;

    if (HW_TEST_ADAPTER_STATUS(Hw, HW_CANNOT_ACCESS_HARDWARE) )
    {
        return;
    }

    // Note that we may be reset while we have packets pending at the
    // upper layer. That is OK.

    // Note: At this point, the interrupt may be running. So
    // stuff here needs to be done with the lock held
    HW_RX_ACQUIRE_LOCK(Hw, DispatchLevel);    

    //
    // Reset the descriptors from the hardware
    //
    if (HalResetRxDescs(Hw->Hal) != NDIS_STATUS_SUCCESS)
    {
        HW_RX_RELEASE_LOCK(Hw, DispatchLevel); 
        // MpTrace(COMP_INIT_PNP, DBG_SERIOUS, ("HalResetRxDescs failed\n"));
        return;
    }
    
    //
    // Resubmit each of the available MPDUs to the hardware again
    //
    mpdu = (PHW_RX_MPDU)MP_PEEK_LIST_HEAD(&(Hw->RxInfo.AvailableMPDUList));    
    while (mpdu)
    {
        if ((PVOID)mpdu == (PVOID)&Hw->RxInfo.AvailableMPDUList)
        {
            // Reached the end of the list
            break;
        }
        
        //
        // Return this to the HAL
        //
        HalReturnRxDesc(Hw->Hal,
            MAX_TX_RX_PACKET_SIZE,
            (PUCHAR)mpdu->BufferVa,
            mpdu->BufferPa,
            &mpdu->DescIter
            );

        mpdu = (PHW_RX_MPDU)MP_PEEK_LIST_HEAD(&mpdu->ListEntry);
        loopCount++;
    }
    MPASSERT(Hw->RxInfo.NumMPDUAvailable == loopCount);
    
    HW_RX_RELEASE_LOCK(Hw, DispatchLevel);    
}

    
VOID
HwHandleReceiveInterrupt(
    _In_  PHW                     Hw,
    _In_  ULONG                   MaxNblsToIndicate
    )
{
    ULONG                       numMSDUToIndicate = 0;
    PHW_RX_MPDU                 mpdu;
    PHW_RX_MSDU                 msdu, msduToIndicate = NULL, lastMsduToIndicate = NULL;
    NDIS_STATUS                 ndisStatus = NDIS_STATUS_SUCCESS;

    //
    // We have to make sure that the we can receive right now. If a Pause or reset
    // is in progress, we should not indicate packets up to the protocol anymore.
    // Since this function is not protected by a spinlock (for perf reasons), we have
    // to use Ref Counts to make sure that this DPC is not running if Pause or reset
    // etc. is running.
    //
    HW_INCREMENT_ACTIVE_OPERATION_REF(Hw);

    if (HW_TEST_ADAPTER_STATUS(Hw, HW_CANNOT_RECEIVE_FLAGS))
    {
        //
        // While a context switch or a reset or pause is in progress, we dont want 
        // to do receive indications.
        //
        HW_DECREMENT_ACTIVE_OPERATION_REF(Hw);    
        return;
    }
    
    while (!HW_TEST_ADAPTER_STATUS(Hw, HW_CANNOT_RECEIVE_FLAGS) && 
           (numMSDUToIndicate < MaxNblsToIndicate) &&
           (Hw->RxInfo.NumMPDUAvailable > 0) &&
           (HwIsReceiveAvailable(Hw, TRUE))
           )
    {

        //
        // Get the receive MPDU 
        //
        mpdu = HwGetReceivedMPDU(Hw);
        MPASSERT(mpdu != NULL);

       
        // Perform processing on this MPDU so that it is ready for receive indication
        ndisStatus = HwPrepareReceivedMPDU(Hw, mpdu);
        if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            HwReturnRxMPDU(Hw, mpdu, TRUE);
            continue;
        }

        // This is a good packet. Lets create an MSDU for this MPDU
        msdu = HwBuildMSDUForMPDU(Hw, mpdu);
        if (msdu == NULL)
        {
            // Either this is part of a partial MSDU or was a bad
            // MPDU
            continue;
        }

        // Now we have an MSDU, lets prepare the MSDU for indication
        ndisStatus = HwPrepareReceivedMSDU(Hw, msdu);
        if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            HwReturnRxMSDU(Hw, msdu, TRUE);
            continue;
        }

        // This needs to be indicated up to the ports
        numMSDUToIndicate++;
        if (msduToIndicate == NULL)
            msduToIndicate = msdu;
        else
            lastMsduToIndicate->Next = msdu;

        lastMsduToIndicate = msdu;

    }

    //
    // Periodically, run through the list of Rx MSDUs in reassembly and drop
    // any which have expired. This is an effort to get rid of any orphan
    // Rx MSDUs stuck in reassembly. Watch Dog timer solution is an overkill
    // and CheckForHang can cause sync issues on multiproc machines.
    //
    if (Hw->RxInfo.ReassemblyLineCleanupCountdown-- == 0)
    {
        HwExpireReassemblingMSDUs(Hw, TRUE);
        Hw->RxInfo.ReassemblyLineCleanupCountdown = HW_REASSEMBLY_CLEANUP_COUNTDOWN_VALUE;
    }    

    if (numMSDUToIndicate > 0)
    {
        // We are ready to indicate this MSDU to the upper layer

        //
        // If the number of free descriptors is getting low, indicate this
        // packet with low resources set.
        //
        if (Hw->RxInfo.NumMPDUAvailable < HW_RX_MSDU_LOW_RESOURCE_THRESHOLD)
        {
            HwIndicateReceivedMSDUs(Hw, 
                msduToIndicate, 
                NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL | NDIS_RECEIVE_FLAGS_RESOURCES
                );

            //
            // And grow the size of the MPDU pool
            //
            
            //
            // We must not exceed the upper limit on Num Rx MSDU set by User
            //
            if (Hw->RxInfo.NumMPDUAllocated < (LONG)Hw->RegInfo.NumRXBuffersUpperLimit)
            {
                //
                // Return status is not important.
                //
                HwGrowMPDUPool(
                    Hw,
                    MIN(HW_RX_MSDU_GROW_POOL_SIZE, (Hw->RegInfo.NumRXBuffersUpperLimit - Hw->RxInfo.NumMPDUAllocated))
                    );
            }
            
            //
            // Start the sampling again. This disables the shrinking algorithm.
            // We don't want to shrink while Rx MSDU resources are running low
            //
            Hw->RxInfo.UnusedMPDUListSampleTicks = 0;
            Hw->RxInfo.UnusedMPDUListSampleCount = 0;
        }
        else
        {
            HwIndicateReceivedMSDUs(Hw, 
                msduToIndicate, 
                NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL
                );
        }
    }

    HW_DECREMENT_ACTIVE_OPERATION_REF(Hw);    
}


/**
 * This function is called by NDIS when the protocol above returns NetBufferLists
 * previously indicated by this miniport.
 * 
 * \param MiniportAdapterContext    The adapter context for this miniport
 * \param NetBufferLists            The NBLs that was previously indicated to NDIS
 * \param ReturnFlags               Flags for return information (dispatch level, etc)
 * \sa Hw11ReturnFragment
 */
VOID 
Hw11ReturnPackets(
    _In_  PHW_MAC_CONTEXT         MacContext,
    _In_  PMP_RX_MSDU             PacketList,
    _In_  ULONG                   ReturnFlags
    )
{
    PMP_RX_MSDU                 currentMsdu = PacketList;
    PHW_RX_MSDU                 hwMsdu;
    
    while (currentMsdu != NULL)
    {
        // Save the next since we are going to delete this packet
        PacketList = MP_RX_MSDU_NEXT_MSDU(currentMsdu);
        MP_RX_MSDU_NEXT_MSDU(currentMsdu) = NULL;

        hwMsdu = currentMsdu->HwMsdu;

        HwReturnRxMSDU(MacContext->Hw, 
            hwMsdu, 
            (NDIS_TEST_RETURN_AT_DISPATCH_LEVEL(ReturnFlags) ? TRUE : FALSE)
            );
        
        // Remove the ref added on indication
        HW_REMOVE_MAC_CONTEXT_RECV_REF(MacContext, 1);

        currentMsdu = PacketList;
    }
}





Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS