/*
* Copyright (c) 2005-2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:   Implementation of the WlanDot11Associated class
*
*/

/*
* %version: 97 %
*/

#include "config.h"
#include "UmacDot11Associated.h"
#include "UmacContextImpl.h"
#include "UmacWsaWriteMib.h"
#include "umacinternaldefinitions.h"

/**
* Mask to determine the used encryption from WHA::ReceivePacket's aFlags 
* parameter
*/
const TUint32 KReceivePacketEncryptionMask = 0x00038000;


// ================= MEMBER FUNCTIONS =======================

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::OnBeaconFrameRx( 
    WlanContextImpl& aCtxImpl,
    const TAny* /*aFrame*/,
    const TUint32 /*aLength*/,
    WHA::TRcpi /*aRcpi*/,
    TUint8* aBuffer )
    {
    // release the Rx buffer
    aCtxImpl.iUmac.MarkRxBufFree( aBuffer );
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::OnProbeResponseFrameRx( 
    WlanContextImpl& aCtxImpl,
    const TAny* /*aFrame*/,
    const TUint32 /*aLength*/,
    WHA::TRcpi /*aRcpi*/,
    TUint8* aBuffer )
    {
    // release the Rx buffer
    aCtxImpl.iUmac.MarkRxBufFree( aBuffer );
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::OnDeauthenticateFrameRx( 
    WlanContextImpl& aCtxImpl,
    TUint8* aBuffer )
    {
    // release the Rx buffer
    aCtxImpl.iUmac.MarkRxBufFree( aBuffer );
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::OnDisassociateFrameRx( 
    WlanContextImpl& aCtxImpl,
    TUint8* aBuffer )
    {
    // release the Rx buffer
    aCtxImpl.iUmac.MarkRxBufFree( aBuffer );
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TSnapStatus WlanDot11Associated::ValiDateSnapHeader( 
    WlanContextImpl& aCtxImpl,
    const TUint8* aStartOfSnap ) const
    {
    TSnapStatus snapStatus( ESnapUnknown );

    if ( !os_memcmp( 
            aStartOfSnap, 
            &KEncapsulatingRfc1042SnapHeader, 
            sizeof( KEncapsulatingRfc1042SnapHeader ) ) )
        {
        snapStatus = ESnapDot11Ok;
        }
    else if ( !os_memcmp( 
                aStartOfSnap, 
                &KEncapsulating802_1hSnapHeader, 
                sizeof( KEncapsulating802_1hSnapHeader ) ) )
        {
        snapStatus = ESnapDot11Ok;
        }
    else
        {
        // unknown SNAP. Status already correct. No action needed
        }

    if ( snapStatus == ESnapUnknown &&
         aCtxImpl.NetworkOperationMode() == WHA::EBSS &&
         aCtxImpl.GetProprietarySnapHeader() != KUndefinedSnapHeader )
        {
        // SNAP still unknown, but we a are in a infrastructure network and
        // a proprietary SNAP header has been defined.
        // Check if the SNAP is this proprietary SNAP header
        
        if ( !os_memcmp( 
                aStartOfSnap, 
                &(aCtxImpl.GetProprietarySnapHeader()), 
                sizeof( aCtxImpl.GetProprietarySnapHeader() ) ) )
            {
            snapStatus = ESnapProprietaryOk;
            }        
        }
    
    return snapStatus;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
inline TBool WlanDot11Associated::RxDataMpduValid(
    const TUint32 aLength,
    const SDataFrameHeader& aFrameHeader,
    TBool aQosData,
    TBool aAmsdu,
    TUint aHtControlLen,
    TUint aSecurityHeaderLen,
    TUint aSecurityTrailerLen ) const
    {
    //========================================================================
    // validate frame length
    //========================================================================
    
    const TUint KMinAcceptableRxDataMpduLen ( 
        MinAcceptableRxDataMpduLen( 
            aQosData,
            aAmsdu,
            aHtControlLen,
            aSecurityHeaderLen,
            aSecurityTrailerLen ) );
    
    if ( aLength < KMinAcceptableRxDataMpduLen )
        {
        // frame shorter than min acceptable length; reject it 
        OsTracePrint( KRxFrame | KWarningLevel, (TUint8*)
            ("UMAC: WlanDot11Associated::RxDataMpduValid: WARNING: MPDU rejected because its shorter than min acceptable length of %d"),
            KMinAcceptableRxDataMpduLen );        

        return EFalse;
        }
    
    //========================================================================
    // validate To DS & From DS bits
    //========================================================================

    if ( !DoIsValidAddressBitCombination( aFrameHeader ) )
        {
        OsTracePrint( KRxFrame, (TUint8*)
            ("UMAC: WlanDot11Associated::RxDataMpduValid: MPDU rejected because of invalid To DS and/or From DS bit values") );

        return EFalse;
        }
            
    return ETrue;
    }

// ---------------------------------------------------------------------------
// defines the minimum acceptable Data MPDU length as:
// dot11 MAC header length including SNAP and possibly existing HT Control 
// field
// + security header length
// + subframe header length if the QoS Data MPDU contains an A-MSDU
// + security trailer length
// + Ethernet type field length
// ---------------------------------------------------------------------------
//
inline TUint WlanDot11Associated::MinAcceptableRxDataMpduLen(
    TBool aQosData,
    TBool aAmsdu,
    TUint aHtControlLen,
    TUint aSecurityHeaderLen,
    TUint aSecurityTrailerLen ) const
    {
    return aQosData ? 
        sizeof( SQosDataMpduHeader ) + aHtControlLen
        + aSecurityHeaderLen
        + ( aAmsdu ? sizeof( SAmsduSubframeHeader ) : 0 )
        + aSecurityTrailerLen
        + sizeof( SEthernetType )
        :         
        sizeof( SDataMpduHeader )
        + aSecurityHeaderLen
        + aSecurityTrailerLen
        + sizeof( SEthernetType );
    }

// ---------------------------------------------------------------------------
// Note that address 1 is the DA (Destination Address) both in infrastructure
// and IBSS network Rx frames
// ---------------------------------------------------------------------------
//
inline TDaType WlanDot11Associated::RxFrameDaType(
    const SDataFrameHeader& aFrameHeader ) const
    {
    if ( IsGroupBitSet( aFrameHeader.iAddress1 ) )
        {
        // a multicast
        if ( aFrameHeader.iAddress1 == KBroadcastMacAddr )
            {
            // also a brodcast
            return EBroadcastAddress;
            }
        else
            {
            // a multicast but not a broadcast
            return EMulticastAddress;
            }
        }
    else
        {
        // a unicast
        return EUnicastAddress;
        }
    }

// ---------------------------------------------------------------------------
//  
// ---------------------------------------------------------------------------
//
inline TBool WlanDot11Associated::RxMsduValid(
    WlanContextImpl& aCtxImpl,
    const SDataFrameHeader& aFrameHeader,
    const SAmsduSubframeHeader* aSubFrameHeader,
    const TUint8* aStartOfSnap,
    TUint16 aEtherType,
    TBool aMulticast,
    TUint32 aFlags,
    TSnapStatus& aSnapStatus ) const
    {
    //========================================================================
    // validate SNAP header
    //========================================================================
    
    aSnapStatus = ValiDateSnapHeader( aCtxImpl, aStartOfSnap );
    
    if ( aSnapStatus == ESnapUnknown )
        {
        // SNAP header is invalid
        
        OsTracePrint( KRxFrame, (TUint8*)
            ("UMAC: WlanDot11Associated::RxMsduValid: MSDU rejected because of invalid snap:"),
            aStartOfSnap, aStartOfSnap + sizeof( SSnapHeader ) );    

        return EFalse;
        }

    // determine if SA is our MAC address
    const TBool KSaIsOurMac ( 
        DoIsRxFrameSAourAddress( aCtxImpl, aFrameHeader, aSubFrameHeader ) );           

    //========================================================================
    // if this is a proprietary test MSDU but not sent by us, its not valid 
    // for us
    //========================================================================

    if ( aEtherType == KBounceType && !KSaIsOurMac )
        {
#ifndef NDEBUG
        OsTracePrint( KRxFrame, (TUint8*)
            ("UMAC: WlanDot11Associated::RxMsduValid: proprietary test MSDU, but not sent by us. MSDU rejected") );

        if ( aSubFrameHeader )
            {
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: Subframe DA:"), aSubFrameHeader->iDa );                
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: Subframe SA:"), aSubFrameHeader->iSa );                
            }
        else
            {
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: Address1:"), aFrameHeader.iAddress1);    
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: Address2:"), aFrameHeader.iAddress2);    
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: Address3:"), aFrameHeader.iAddress3);
            }
#endif        

        return EFalse;
        }

    //========================================================================
    // non-test multicast MSDUs sent by us are not relevant for us
    //========================================================================

    if ( // SA is our MAC address
         KSaIsOurMac
         // AND this is a multicast frame
         && aMulticast
         // AND this is not a test frame
         && aEtherType != KBounceType )
        {
        // we have received a non-test multicast frame that was sent by us.
        // Ignore it.
        
#ifndef NDEBUG
        OsTracePrint( KRxFrame, (TUint8*)
            ("UMAC: WlanDot11Associated::RxMsduValid: MSDU rejected because its a non-test multicast MSDU sent by us"));
        
        if ( aSubFrameHeader )
            {
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: Subframe DA:"), aSubFrameHeader->iDa );                
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: Subframe SA:"), aSubFrameHeader->iSa );                
            }
        else
            {
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: Address1:"), aFrameHeader.iAddress1);    
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: Address2:"), aFrameHeader.iAddress2);    
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: Address3:"), aFrameHeader.iAddress3);
            }
#endif

        return EFalse;
        }
        
    // determine the encryption of the frame
    const TUint32 KEncryption ( aFlags & KReceivePacketEncryptionMask );

    if ( aSnapStatus != ESnapProprietaryOk )
        {
        //====================================================================
        // validate MSDU with the applicable privacy mode filter
        //====================================================================
        if ( !aCtxImpl.ExecuteActivePrivacyModeFilter(
                aFrameHeader,
                aCtxImpl.iEnableUserData,
                aEtherType,
                aCtxImpl.PairWiseKeyType() != WHA::EKeyNone,
                ( KEncryption == WHA::KEncryptAes || 
                  KEncryption == WHA::KEncryptTkip ||
                  KEncryption == WHA::KEncryptWapi ) ) )
            {
            return EFalse;
            }
        }

    // this MSDU has passed all our checks, so it is valid for us
    return ETrue;
    }

// ---------------------------------------------------------------------------
//  
// ---------------------------------------------------------------------------
//
inline TUint WlanDot11Associated::RxMsduEthernetPayloadLength(
    const TUint32 aMpduLength, 
    TUint aSubframeLength, 
    TBool aQosData,
    TUint aHtControlLen,
    TUint aSecurityHeaderLen,
    TUint aSecurityTrailerLen ) const
    {
    TUint length ( 0 );
    
    if ( aSubframeLength )
        {
        // this ethernet payload is carried in an A-MSDU subframe
        
        length = aSubframeLength - sizeof( SSnapHeader );

        OsTracePrint( KRxFrame, (TUint8*)
            ("UMAC: subframe eth payload len: %d"),
            length );                        
        }
    else
        {
        // this ethernet payload is carried in a non-A-MSDU frame

        if ( aQosData )
            {
            length = 
                // total length of the Rx frame
                aMpduLength
                // - MAC header length including SNAP
                - ( sizeof( SQosDataMpduHeader ) + aHtControlLen )
                // - security header length
                - aSecurityHeaderLen
                // - security trailer length
                - aSecurityTrailerLen;
                
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: qos data eth payload len: %d"),
                length );                        
            }
        else
            {
            length =
                // total length of the Rx frame
                aMpduLength
                // MAC header length including SNAP
                - sizeof( SDataMpduHeader )
                // - security header length
                - aSecurityHeaderLen
                // - security trailer length
                - aSecurityTrailerLen;
        
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: non-qos data eth payload len: %d"),
                length );                        
            }
        }
    
    return length;
    }

// ---------------------------------------------------------------------------
//   
// ---------------------------------------------------------------------------
//
inline TUint8* WlanDot11Associated::NewBufForMgmtClientRxFrame(
    WlanContextImpl& aCtxImpl,
    TBool aProprieatarySnapFrame,
    TBool aQosFrame,
    TUint aHtControlLen,
    TUint aEtherPayloadLength ) const
    {
    TUint requiredLength ( 0 );
    
    if ( aProprieatarySnapFrame )
        {
        // forwarded as a Data MPDU
        
        requiredLength = aQosFrame ? 
            sizeof( SQosDataFrameHeader ) 
            + aHtControlLen 
            + aEtherPayloadLength :
            sizeof( SDataFrameHeader ) 
            + aEtherPayloadLength;        
        }
    else
        {
        // forwarded as Ethernet frame
        
        requiredLength = 2 * sizeof( TMacAddress ) + aEtherPayloadLength;
        }
    
    // request for a new Rx buffer
    return aCtxImpl.GetRxBuffer( 
        requiredLength, 
        // tell that this is an internally triggered buffer request
        ETrue );
    }
        
// ---------------------------------------------------------------------------
//  
// ---------------------------------------------------------------------------
//
inline TBool WlanDot11Associated::RxMsduForUser(
    TUint16 aEtherType,
    TSnapStatus aSnapstatus ) const
    {
    if ( // not an EAPOL frame AND
         aEtherType != KEapolType &&
         // not a WAI frame AND
         aEtherType != KWaiType &&
         // not a WLAN Mgmt Client test frame
         aEtherType != KBounceType &&
         // not a proprietary SNAP frame
         aSnapstatus != ESnapProprietaryOk )
        {
        // a user data / protocol stack data frame

        return ETrue;
        }
    else
        {
        return EFalse;
        }
    }

// ---------------------------------------------------------------------------
//  
// ---------------------------------------------------------------------------
//
inline void WlanDot11Associated::UpdateDataFrameRxStatistics(
    WlanContextImpl& aCtxImpl,
    TUint16 aEtherType,
    TBool aMulticast,
    WHA::TQueueId aAccessCategory ) const
    {
    if ( aEtherType != KBounceType )
        {
        // other than WLAN Mgmt Client test data frame received
        
        if ( aMulticast )
            {
            // multicast frame
    
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: inc rx mcast cnt for AC: %d"),
                aAccessCategory );
    
            // update frame statistics
            aCtxImpl.IncrementRxMulticastDataFrameCount( aAccessCategory );            
            }
        else
            {
            // unicast frame
    
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: inc rx unicast cnt for AC: %d"),
                aAccessCategory );
    
            // update frame statistics
            aCtxImpl.IncrementRxUnicastDataFrameCount( aAccessCategory );
            }
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::OnDataFrameRx(
    WlanContextImpl& aCtxImpl,
    const TAny* aFrame,
    const TUint32 aLength,
    TUint32 aFlags,
    WHA::TRcpi aRcpi,
    TUint8* aBuffer )
    {
    OsTracePrint( KRxFrame, (TUint8*)
        ("UMAC: WlanDot11Associated::OnDataFrameRx") );    

    SDataMpduHeader* hdr 
        = reinterpret_cast<SDataMpduHeader*>(const_cast<TAny*>(aFrame));
    SQosDataMpduHeader* qoshdr 
        = reinterpret_cast<SQosDataMpduHeader*>(const_cast<TAny*>(aFrame));

    // determine the length of the security header (e.g. IV etc.) ...
    const TUint KSecurityHeaderLen( DecryptHdrOffset( aCtxImpl, aFlags ) );

    OsTracePrint( KRxFrame, (TUint8*)("UMAC: KSecurityHeaderLen: %d"),
        KSecurityHeaderLen );    

    // ... and the security trailer (e.g. MIC etc.)
    const TUint KSecurityTrailerLen( DecryptTrailerOffset( aCtxImpl, aFlags ) );

    OsTracePrint( KRxFrame, (TUint8*)("UMAC: KSecurityTrailerLen: %d"),
        KSecurityTrailerLen );    

    // as the MAC header of a QoS data MPDU has an additional field
    // we need to take that into account; so make a note if we indeed have 
    // received a QoS data MPDU
    const TBool KQosData = 
        ( hdr->iHdr.iFrameControl.iType == E802Dot11FrameTypeQosData ) ? 
        ETrue : EFalse;

    // the MAC header of HT QoS data MPDU also has an additional field,
    // so determine if that field is present and hence has a non-zero length
    const TUint KHtControlLen ( 
        KQosData && HtcFieldPresent( aCtxImpl, aFrame, aFlags ) ? 
            KHtControlFieldLength : 0 );

    // determine if the MPDU contains an A-MSDU
    const TBool KAmsdu ( 
        KQosData && qoshdr->iHdr.AmsduPresent() ? ETrue : EFalse );
    
    if ( !RxDataMpduValid(
            aLength,
            hdr->iHdr,
            KQosData,
            KAmsdu,
            KHtControlLen,
            KSecurityHeaderLen,
            KSecurityTrailerLen ) )
        {
        // the overall MAC MPDU (disregarding the included MSDU(s) at this
        // point) is not valid for us. Release the Rx buffer & abort
        aCtxImpl.iUmac.MarkRxBufFree( aBuffer );                            
        return;        
        }
    
    TUint8* framePtr ( 0 );
    WHA::TQueueId accessCategory( WHA::ELegacy );

    // User Priority is regarded as Best Effort (i.e. Legacy, i.e. zero) unless
    // the received MPDU is a QoS Data MPDU & a different priority is 
    // specified in its MAC header
    TUint8 userPriority( 0 );
    
    if ( KQosData )
        {
        // a QoS Data MPDU
        
        // move framePtr past the MAC header
        framePtr = reinterpret_cast<TUint8 *>(
            reinterpret_cast<SQosDataFrameHeader *>( hdr ) + 1 ) 
            + KHtControlLen;        

        // get the User Priority
        userPriority = qoshdr->iHdr.UserPriority();

        // make a note of the Access Category corresponding to the
        // user priority of the MPDU (note that there's a one to one 
        // mapping between Queue id and Access Category)       
        accessCategory = Queue( userPriority );
        }
    else
        {
        // move framePtr past the MAC header
        framePtr = reinterpret_cast<TUint8 *>(
            reinterpret_cast<SDataFrameHeader *>( hdr ) + 1 );
        }

    // move after possibly existing security header and possibly existing
    // A-MSDU subframe header, i.e. beginning of SNAP header
    framePtr += KAmsdu ? 
                KSecurityHeaderLen + sizeof( SAmsduSubframeHeader ) : 
                KSecurityHeaderLen;
    TUint8* snapLocation ( framePtr );
    
    // determine the DA type
    const TDaType KDaType ( RxFrameDaType( hdr->iHdr ) );

    TSnapStatus snapstatus( ESnapUnknown );
    TDataBuffer* latestValidPacketForUsr ( NULL );
    TUint nbrOfpacketsForUsr ( 0 );
    TUint nbrOfpacketsForMgmtClient ( 0 );
    const TUint KMaxNbrOfPacketsForUsr ( 30 );
    const TUint KMaxNbrOfPacketsForMgmtClient ( 10 );
    TDataBuffer* packetsForUsr[KMaxNbrOfPacketsForUsr];
    TDataBuffer* packetsForMgmtClient[KMaxNbrOfPacketsForMgmtClient];
    // one byte past the last actual payload byte (so excluding the potentially
    // present security trailer) of the MPDU
    const TUint8* const KMpduPayloadEnd ( 
        reinterpret_cast<const TUint8*>(aFrame) 
        + aLength 
        - KSecurityTrailerLen );
    TPowerMgmtModeChange powerMgmtModeChange ( ENoChange );
    
    // handle every MSDU contained in this MPDU. If this is not an A-MSDU
    // there's only a single MSDU in it.
    do
        {
        TUint8* rxBuffer ( aBuffer );
        
        // move after SNAP header to Ethernet type field
        framePtr += sizeof( SSnapHeader );     
        const SEthernetType* const KEtherTypeLocation 
            = reinterpret_cast<const SEthernetType*>( framePtr );
    
        // determine Ethernet type
        const TUint16 KEtherType = KEtherTypeLocation->Type();
    
        OsTracePrint( KRxFrame, (TUint8*)
            ("UMAC: ether type: 0x%04x"), 
            KEtherType );

        // pointer to subframe heaader; or NULL if the MPDU doesn't contain
        // an A-MSDU
        const SAmsduSubframeHeader* KSubframeHdr ( KAmsdu ?  
            ( reinterpret_cast<const SAmsduSubframeHeader*>(
                    snapLocation) - 1 ) : 
            NULL );
        
        // determine if this is a multicast MSDU
        TBool KMulticastMsdu ( EFalse );
        if ( KSubframeHdr )
            {
            KMulticastMsdu = IsGroupBitSet( KSubframeHdr->iDa );
            }
        else
            {
            KMulticastMsdu = ( KDaType == EUnicastAddress ? EFalse : ETrue );
            }

        // determine subframe length, which can be non-zero only if the MPDU
        // contains an A-MSDU
        const TUint KSubframeLen ( KSubframeHdr ?  KSubframeHdr->Length() : 0 );
        
        if ( !RxMsduValid(
                aCtxImpl,
                hdr->iHdr,
                KSubframeHdr,
                snapLocation,
                KEtherType,
                KMulticastMsdu,
                aFlags,
                snapstatus ) )
            {
            // this MSDU is not valid/relevant for us

            if ( KAmsdu )
                {
                // move pointer to the SNAP of the possibly existing next 
                // subframe
                snapLocation += 
                    Align4( sizeof( SAmsduSubframeHeader ) + KSubframeLen );
                // update frame pointer accordingly
                framePtr = snapLocation;

                continue;
                }
            else
                {
                // this MPDU doesn't contain an A-MSDU, so there cannot be
                // more than a single MSDU in it
                break;
                }
            }
            
        // determine if this MSDU is for user / protocol stack
        const TBool KMsduForUser ( RxMsduForUser( KEtherType, snapstatus) );
        
        if ( KMsduForUser && !aCtxImpl.iUmac.ProtocolStackSideClientReady() )
            {
            // this MSDU should be forwarded up the protocol stack but the 
            // protocol stack client is not ready. So we need to skip at
            // least this MSDU. 
            // Move pointer to the SNAP of the possibly existing next subframe
            snapLocation += 
                Align4( sizeof( SAmsduSubframeHeader ) + KSubframeLen );            
            // update frame pointer accordingly
            framePtr = snapLocation;

            OsTracePrint( KRxFrame | KWarningLevel, (TUint8*)
                ("UMAC: WARNING: protocol stack client not ready; MSDU ignored") );

            continue;            
            }

        TDataBuffer* metaHdr ( aCtxImpl.GetRxFrameMetaHeader() );        
        if ( !metaHdr )
            {
            // no memory available for Rx frame meta header. This means that
            // we are not able to forward to higher layers the current MSDU or 
            // any MSDU(s) that possibly follow it in this same MPDU

            OsTracePrint( KRxFrame | KWarningLevel, (TUint8*)
                ("UMAC: WARNING: no memory for Rx frame meta hdr; MSDU ignored") );
            break;
            }        
    
        const TUint KEtherPayloadLen( RxMsduEthernetPayloadLength(
                aLength, 
                KSubframeLen, 
                KQosData,
                KHtControlLen,
                KSecurityHeaderLen,
                KSecurityTrailerLen ) );
    
        if ( !KMsduForUser && KAmsdu )
            {
            // an MSDU for WLAN Mgmt Client which is also a part of an 
            // A-MSDU. In this case we need to allocate a new buffer for
            // this packet and copy it there. This is necessary as an 
            // A-MSDU may contain MSDUs both for the protocol stack side 
            // and for WLAN Mgmt Client. Either one of those clients may 
            // complete the handling of its MSDUs first at which point the
            // Rx buffer(s) for its MSDU(s) is freed. So we need to make 
            // sure that a buffer is not freed while a client is still 
            // handling MSDU(s) contained in it. We do this by always 
            // copying WLAN Mgmt client MSDU(s) part of an A-MSDU to 
            // new buffers.
            
            rxBuffer = NewBufForMgmtClientRxFrame(
                aCtxImpl,
                snapstatus == ESnapProprietaryOk,
                KQosData,
                KHtControlLen,
                snapstatus == ESnapProprietaryOk ? 
                    KSubframeLen : KEtherPayloadLen );
            
            if ( !rxBuffer )
                {
                // allocation failed so we need to skip at least this MSDU. 

                // Move pointer to the SNAP of the possibly existing next 
                // subframe
                snapLocation += 
                    Align4( sizeof( SAmsduSubframeHeader ) + KSubframeLen );                // update frame pointer accordingly
                // update frame pointer accordingly
                framePtr = snapLocation;

                // also release the meta hdr which was planned to be used for
                // this MSDU 
                aCtxImpl.FreeRxFrameMetaHeader( metaHdr );

                OsTracePrint( KRxFrame | KWarningLevel, (TUint8*)
                    ("UMAC: WARNING: new buf alloc for mgmt client frame failed; MSDU ignored") );
                continue;
                }
            }
        
        // set the offset to the beginning of the Rx buffer from the beginning
        // of the meta header. Note that this may be also negative
        metaHdr->KeSetBufferOffset(
            rxBuffer
            - reinterpret_cast<TUint8*>(metaHdr) );
        
        if ( snapstatus == ESnapProprietaryOk )
            {
            HandleProprietarySnapRxFrame( 
                *metaHdr, 
                KQosData,
                aFrame, 
                KSubframeHdr,
                aLength, 
                KSecurityHeaderLen, 
                KSecurityTrailerLen,
                KHtControlLen,
                KAmsdu ? rxBuffer : NULL );
            }
        else
            {
            DoBuildEthernetFrame( 
                *metaHdr, 
                *hdr, 
                // start of ethernet payload (includes ether type field)
                reinterpret_cast<const TUint8*>(KEtherTypeLocation), 
                // length of ethernet payload
                KEtherPayloadLen,
                KAmsdu,
                !KMsduForUser && KAmsdu ? rxBuffer : NULL );
            }
        
        // set the frame's User Priority to the Rx buffer being passed 
        // to the client
        metaHdr->SetUserPriority( userPriority );                    
        // set the RCPI
        metaHdr->KeSetRcpi( aRcpi );
    
        if ( KEtherType == KBounceType )
            {
            metaHdr->FrameType( 
                TDataBuffer::KEthernetTestFrame );
            }

        if ( KMsduForUser )
            {
            // a user data / protocol stack data frame

            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: data frame rx"));    

            packetsForUsr[nbrOfpacketsForUsr++] = metaHdr;            
            latestValidPacketForUsr = metaHdr;
            if ( KAmsdu )
                {
                metaHdr->KeSetFlags( TDataBuffer::KDontReleaseBuffer );
                }
            }
        else
            {
            // a WLAN Mgmt Client data frame
            
            OsTracePrint( KRxFrame, (TUint8*)
                ("UMAC: mgmt client frame rx"));    

            packetsForMgmtClient[nbrOfpacketsForMgmtClient++] = metaHdr;
            }

        //  move pointer to the SNAP of the possibly existing next subframe
        snapLocation += Align4( sizeof( SAmsduSubframeHeader ) + KSubframeLen );
        // update frame pointer accordingly
        framePtr = snapLocation;

        UpdateDataFrameRxStatistics(
            aCtxImpl,
            KEtherType,
            KMulticastMsdu,
            accessCategory );        

        // inform dynamic power mode mgr about Rx data frame acceptance        
        if ( // if this is not our test frame AND 
             KEtherType != KBounceType &&
             // need to change power mgmt mode hasn't already been detected
             // based on this received (A-)MSDU
             powerMgmtModeChange == ENoChange )
            {
            powerMgmtModeChange = aCtxImpl.OnFrameRx( 
                accessCategory, 
                KEtherType, 
                KEtherPayloadLen, 
                KDaType );
            }
        
        // inform Null Send Controller about data frame Rx
        aCtxImpl.OnDataRxCompleted( 
            ( KEtherType == KEapolType || KEtherType == KWaiType ) ?
            // as EAPOL and WAI frames are not really Voice data (they
            // are just sometimes sent with Voice priority), handle them 
            // here as Best Effort (Legacy)
            WHA::ELegacy : 
            accessCategory, 
            KEtherPayloadLen );

          // for a non-A-MSDU this loop is executed only once
        } while ( KAmsdu && snapLocation < KMpduPayloadEnd );

    if ( nbrOfpacketsForUsr )
        {
        latestValidPacketForUsr->KeClearFlags( 
            TDataBuffer::KDontReleaseBuffer );

        // complete user data / protocol stack data frame(s)
        if ( !aCtxImpl.iUmac.ProtocolStackDataReceiveComplete( 
                packetsForUsr[0], 
                nbrOfpacketsForUsr ) )
            {
            // there's no protocol stack client to whom to complete Rx packets.
            // (Actually the control should never arrive here as we have 
            // checked the readiness of the protocol stack client already
            // earlier)
            nbrOfpacketsForUsr = 0;
            }
        }

    if ( nbrOfpacketsForMgmtClient )
        {
        // complete WLAN Mgmt Client data frame(s)
        aCtxImpl.iUmac.MgmtDataReceiveComplete( 
            packetsForMgmtClient[0], 
            nbrOfpacketsForMgmtClient );
        }

    if ( ( !nbrOfpacketsForUsr && !nbrOfpacketsForMgmtClient ) ||
         ( KAmsdu && !nbrOfpacketsForUsr ) )
        {
        // the contents of the original Rx buffer are not needed any more,
        // so deallocate it        
        OsTracePrint( KRxFrame, (TUint8*)
            ("UMAC: free original Rx buf"));    
        aCtxImpl.iUmac.MarkRxBufFree( aBuffer );
        }
    
    // if any change is needed regarding our power mgmt mode,
    // proceed with it
    PowerMgmtModeChange( aCtxImpl, powerMgmtModeChange );    
    }
    
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::OnManagementActionFrameRx(
    WlanContextImpl& aCtxImpl,
    const TAny* aFrame,
    const TUint32 aLength,
    WHA::TRcpi aRcpi,
    TUint8* aBuffer ) const
    {
    OsTracePrint(  KRxFrame, (TUint8*)
        ("UMAC: WlanDot11Associated::OnManagementActionFrameRx"));    

    TDataBuffer* metaHdr ( aCtxImpl.GetRxFrameMetaHeader() );

    if ( metaHdr )
        {
        // set length and type
        metaHdr->KeSetLength( aLength );
        metaHdr->FrameType( TDataBuffer::KDot11Frame );
        // set RCPI
        metaHdr->KeSetRcpi( aRcpi );
        
        // set the offset to the beginning of the Rx buffer from the beginning
        // of the meta header. Note that this may be also negative
        metaHdr->KeSetBufferOffset(
            aBuffer
            - reinterpret_cast<TUint8*>(metaHdr) );
        
        // set the offset to the beginning of the actual frame within the
        // Rx buffer
        metaHdr->KeSetOffsetToFrameBeginning( 
            reinterpret_cast<const TUint8*>(aFrame)   // frame beginning
            - aBuffer );                              // buffer beginning
        
        // complete
        aCtxImpl.iUmac.MgmtDataReceiveComplete( metaHdr, 1 );
        }
    else
        {
        // no memory available for the meta header. In this case we have no
        // other choice than to discard the received frame. 
        aCtxImpl.iUmac.MarkRxBufFree( aBuffer );
        OsTracePrint( KWarningLevel | KRxFrame, (TUint8*)
            ("UMAC: WlanDot11Associated::OnManagementActionFrameRx: WARNING: no memory for meta hdr => abort rx") );            
        }
    }
        
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TAny* WlanDot11Associated::RequestForBuffer( 
    WlanContextImpl& aCtxImpl,
    TUint16 aLength )
    {
    return aCtxImpl.GetRxBuffer( aLength );
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::ReceivePacket( 
    WlanContextImpl& aCtxImpl, 
    WHA::TStatus aStatus,
    const void* aFrame,
    TUint16 aLength,
    WHA::TRate /*aRate*/,
    WHA::TRcpi aRcpi,
    WHA::TChannelNumber /*aChannel*/,
    TUint8* aBuffer,
    TUint32 aFlags )
    {
    SDataMpduHeader* hdr( 
        reinterpret_cast<SDataMpduHeader*>(const_cast<TAny*>(aFrame)) );

    if ( aStatus == WHA::KSuccess )
        {
        // packet reception success lets see what type of frame we have
        OsTracePrint( KRxFrame, 
            (TUint8*)
            ("UMAC: WlanDot11Associated::ReceivePacket:frame receive success, frame type: 0x%02x"), 
            hdr->iHdr.iFrameControl.iType );

        if ( ( hdr->iHdr.iFrameControl.iType == E802Dot11FrameTypeData )
             || ( hdr->iHdr.iFrameControl.iType == E802Dot11FrameTypeQosData ) )
            {
            OnDataFrameRx( aCtxImpl, aFrame, aLength, aFlags, aRcpi, aBuffer );
            
            if ( aCtxImpl.InsertNewRcpiIntoPredictor( os_systemTime(), aRcpi ) )
                {
                // indicate WLAN signal loss prediction to WLAN Mgmt Client
                OnInDicationEvent( aCtxImpl, ESignalLossPrediction );
                }
            }        
        else if ( hdr->iHdr.iFrameControl.iType 
            == E802Dot11FrameTypeManagementAction )
            {
            OnManagementActionFrameRx( 
                aCtxImpl, 
                aFrame, 
                aLength, 
                aRcpi, 
                aBuffer );

            if ( aCtxImpl.InsertNewRcpiIntoPredictor( os_systemTime(), aRcpi ) )
                {
                // indicate WLAN signal loss prediction to WLAN Mgmt Client
                OnInDicationEvent( aCtxImpl, ESignalLossPrediction );
                }
            }
        else if ( hdr->iHdr.iFrameControl.iType 
            == E802Dot11FrameTypeDeauthentication )
            {
            OnDeauthenticateFrameRx( aCtxImpl, aBuffer );
            }
        else if ( hdr->iHdr.iFrameControl.iType 
            == E802Dot11FrameTypeDisassociation )
            {
            OnDisassociateFrameRx( aCtxImpl, aBuffer );
            }
        else if ( hdr->iHdr.iFrameControl.iType 
            == E802Dot11FrameTypeBeacon )
            {
            OnBeaconFrameRx( aCtxImpl, aFrame, aLength, aRcpi, aBuffer );
            }
        else if ( hdr->iHdr.iFrameControl.iType 
            == E802Dot11FrameTypeProbeResp )
            {
            OnProbeResponseFrameRx( aCtxImpl, aFrame, aLength, aRcpi, aBuffer );
            }
        else
            {
            OsTracePrint( KRxFrame | KWarningLevel, (TUint8*)
                ("UMAC: WlanDot11Associated::ReceivePacket: unsupported frame type: 0x%02x"), 
                hdr->iHdr.iFrameControl.iType );            

            // release the Rx buffer
            aCtxImpl.iUmac.MarkRxBufFree( aBuffer );
            }
        }
    else if ( aStatus == WHA::KDecryptFailure )
        {
        // decryption error
        OsTracePrint( KRxFrame | KWarningLevel, (TUint8*)
            ("UMAC: WlanDot11Associated::ReceivePacket: decrypt error for frame:"), hdr->iHdr );    

        OnInDicationEvent( aCtxImpl, EWepDecryptFailure );

        // release the Rx buffer
        aCtxImpl.iUmac.MarkRxBufFree( aBuffer );
        }
    else if ( aStatus == WHA::KMicFailure )
        {
        // MIC failed
        OsTracePrint( KRxFrame | KWarningLevel, (TUint8*)
            ("UMAC: WlanDot11Associated::ReceivePacket: MIC error for frame:"), hdr->iHdr );    

        // address 1 is always the DA in our case
        OnInDicationEvent( aCtxImpl, 
            (IsGroupBitSet( hdr->iHdr.iAddress1 )) 
            ? EGroupKeyMicFailure : EPairwiseKeyMicFailure 
            );

        // release the Rx buffer
        aCtxImpl.iUmac.MarkRxBufFree( aBuffer );
        }    
    else
        {
        // packet rececption failed 
        OsTracePrint( KRxFrame | KWarningLevel, (TUint8*)
            ("UMAC: WlanDot11Associated::ReceivePacket: frame receive failure"));

        // release the Rx buffer
        aCtxImpl.iUmac.MarkRxBufFree( aBuffer );
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11Associated::EncryptTxFrames( 
    WlanContextImpl& aCtxImpl,
    const TDataBuffer& aDataBuffer ) const
    {
    TBool encrypt ( EFalse );

    if ( aDataBuffer.KeFlags() & TDataBuffer::KTxFrameMustNotBeEncrypted )
        {
        // our client has instructed us not the encrypt this frame under
        // any circumstances. EFalse will be returned. 
        // No further action needed
        }
    else
        {
        const WHA::TKeyType pairwiseKey ( aCtxImpl.PairWiseKeyType() );
        const WHA::TKeyType groupKey ( aCtxImpl.GroupKeyType() );
    
        if ( pairwiseKey != WHA::EKeyNone )
            {
            // pairwise key set => use encryption
            encrypt = ETrue;
            }
        else
            {
            // pairwise key not set
            
            if ( groupKey == WHA::EWepGroupKey )
                {
                // wep group key set => use encryption
                encrypt = ETrue;
                }        
            }
        }
        
    return encrypt;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TUint WlanDot11Associated::DecryptHdrOffset( 
    WlanContextImpl& aCtxImpl, 
    TUint32 aFlags ) const
    {
    TUint offset( 0 );

    const TUint32 encryption ( aFlags & KReceivePacketEncryptionMask );

    if ( aCtxImpl.WHASettings().iCapability & WHA::SSettings::KNoSecHdrAndTrailer )
        {
        // no security header is present on this sw layer. It is removed
        // on lower layers; when necessary.
        // We will return zero, so no further actions
        }
    else
        {
        // IV and/or Ext IV or CCMP header is present
        // on this sw layer; when applicable
        
        if ( encryption == WHA::KEncryptAes )
            {
            offset = KCcmpHeaderLength;
            }
        else if ( encryption == WHA::KEncryptTkip )
            {
            offset = KWepIVLength + KWepExtendedIVLength;  
            }
        else if ( encryption == WHA::KEncryptWep )
            {
            offset = KWepIVLength;
            }
        else if ( encryption == WHA::KEncryptWapi )
            {
            offset = KWapiHeaderLength;
            }
        else
            {
            // frame not encrypted; returns zero
            }        
        }

    return offset;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TUint WlanDot11Associated::DecryptTrailerOffset( 
    WlanContextImpl& aCtxImpl, 
    TUint32 aFlags ) const
    {
    TUint offset( 0 );
    
    const TUint32 encryption ( aFlags & KReceivePacketEncryptionMask );

    if ( aCtxImpl.WHASettings().iCapability & WHA::SSettings::KNoSecHdrAndTrailer )
        {
        // no security trailer is present on this sw layer. It is removed
        // on lower layers; when necessary.
        // We will return zero, so no further actions
        }
    else
        {
        // ICV and/or MIC field is present on this sw layer; when applicable
    
        if ( encryption == WHA::KEncryptAes )
            {
            offset = KMicLength;
            }
        else if ( encryption == WHA::KEncryptTkip )
            {
            offset = KMicLength + KWEPICVLength;
            }
        else if ( encryption == WHA::KEncryptWep )
            {
            offset = KWEPICVLength;
            }
        else if ( encryption == WHA::KEncryptWapi )
            {
            offset = KWapiMicLength;
            }
        else
            {
            // frame not encrypted; returns zero
            }        
        }

    return offset;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TUint WlanDot11Associated::ComputeEncryptionOffsetAmount(
    const WlanContextImpl& aCtxImpl,
    const TDataBuffer& aDataBuffer ) const
    {
    TUint offset( 0 );

    if ( // our client has instructed us not the encrypt this frame under
         // any circumstances OR
         ( aDataBuffer.KeFlags() & TDataBuffer::KTxFrameMustNotBeEncrypted ) ||
         // no space is reserved for security header on this sw layer. It is
         // done on lower layers; when necessary.
         ( aCtxImpl.WHASettings().iCapability & 
           WHA::SSettings::KNoSecHdrAndTrailer ) )
        {
        // We will return zero, so no further actions
        }
    else
        {
        // encryption is allowed if relevant and
        // space is reserved for IV and/or Ext IV or CCMP header
        // on this sw layer; when necessary

        const WHA::TKeyType groupKey( aCtxImpl.GroupKeyType() );
        const WHA::TKeyType pairwiseKey( aCtxImpl.PairWiseKeyType() );

        if ( pairwiseKey == WHA::EAesPairWiseKey )
            {
            offset = KCcmpHeaderLength;
            }
        else if ( pairwiseKey == WHA::ETkipPairWiseKey )
            {
            offset = KWepIVLength + KWepExtendedIVLength;
            }
        else if ( pairwiseKey == WHA::EWepPairWiseKey )
            {
            offset = KWepIVLength;
            }
        else if ( pairwiseKey == WHA::EWapiPairWiseKey )
            {
            offset = KWapiHeaderLength;
            }
        else
            {
            // don't care of anything else
            }

        if ( !offset )
            {
            // no encryption used based on pairwise key presence
            // check for WEP groupkey presence
            if ( groupKey == WHA::EWepGroupKey )
                {
                offset = KWepIVLength;
                }
            else
                {
                // don't care of anything else as group key encyption is not 
                // supported for the other key types. For them we always have 
                // a pairwise key
                }
            }        
        }

    return offset;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TUint WlanDot11Associated::EncryptTrailerLength(
    WlanContextImpl& aCtxImpl,
    const TDataBuffer& aDataBuffer ) const
    {
    TUint length( 0 );

    if ( // our client has instructed us not the encrypt this frame under
         // any circumstances OR
         ( aDataBuffer.KeFlags() & TDataBuffer::KTxFrameMustNotBeEncrypted ) ||
         // no space is reserved for security trailer on this sw layer. It is
         // done on lower layers; when necessary.
         ( aCtxImpl.WHASettings().iCapability & 
           WHA::SSettings::KNoSecHdrAndTrailer ) )
        {
        // We will return zero, so no further actions
        }
    else
        {
        // encryption is allowed if relevant and
        // space is reserved for ICV and/or MIC
        // on this sw layer; when necessary

        const WHA::TKeyType groupKey( aCtxImpl.GroupKeyType() );
        const WHA::TKeyType pairwiseKey( aCtxImpl.PairWiseKeyType() );    

        if ( pairwiseKey == WHA::EAesPairWiseKey )
            {
            length = KMicLength;
            }
        else if ( pairwiseKey == WHA::ETkipPairWiseKey )    
            {
            length = KMicLength + KWEPICVLength;
            }
        else if ( pairwiseKey == WHA::EWepPairWiseKey )
            {
            length = KWEPICVLength;
            }
        else if ( pairwiseKey == WHA::EWapiPairWiseKey )
            {
            length = KWapiMicLength;
            }
        else
            {
            // don't care of anything else
            }

        if ( !length )
            {
            // no pairwise key present
            // check for groupkey
            if ( groupKey == WHA::EWepGroupKey )
                {
                length = KWEPICVLength;
                }
            else
                {
                // don't care of anything else as group key encyption is not 
                // supported for the other key types. For them we always have 
                // a pairwise key
                }
            }        
        }

    return length;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TUint WlanDot11Associated::ComputeQosOffsetAmount(
    WlanContextImpl& aCtxImpl ) const
    {
    const TUint KNoQosHeader( 0 );
    
    if ( aCtxImpl.QosEnabled() )        
        {
        return sizeof( T802Dot11QosControl );
        }
    else
        {
        return KNoQosHeader;
        }    
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::EncapsulateEthernetFrame(
    WlanContextImpl& aCtxImpl,
    TWlanUserTxDataCntx& aDataCntx,
    TDataBuffer& aDataBuffer,
    TUint16& aEtherType ) const
    {
    TUint8* etherFrameBeginning ( aDataBuffer.GetBuffer() );

    OsTracePrint( KWsaTxDetails, (TUint8*)
        ("UMAC: WlanDot11Associated::EncapsulateEthernetFrame: supplied ether frame start address: 0x%08x"),
        reinterpret_cast<TUint32>(etherFrameBeginning) );

    // start of dot11 frame
    SDataFrameHeader* dot11_dataframe_hdr ( NULL );

    // start of ethernet frame
    const SEthernetHeader* ether_hdr 
        = reinterpret_cast<const SEthernetHeader*>(etherFrameBeginning);

    // determine Ethernet type
    aEtherType = ether_hdr->Type();

    OsTracePrint( KWsaTxDetails, (TUint8*)
        ("UMAC: ether type: 0x%04x"),
        aEtherType );    

    // compute space required for encryption header
    // after dot11 radio header and before data payload
    const TUint encryption_offset = ( 
        ComputeEncryptionOffsetAmount( aCtxImpl, aDataBuffer ) );

    OsTracePrint( KWsaTxDetails, (TUint8*)
        ("UMAC: encryption_offset: %d"),
        encryption_offset );    

    const TUint32 ether_offset = (KMacAddressLength << 1);
    
    // take a backup copy of the destination and source addresses from the
    // ethernet frame as they will get overwritten
    
    TMacAddress da;
    os_memcpy( 
        reinterpret_cast<TUint8*>(&da), 
        etherFrameBeginning, 
        sizeof( TMacAddress ) );
    TMacAddress sa;
    os_memcpy( 
        reinterpret_cast<TUint8*>(&sa), 
        etherFrameBeginning + sizeof( TMacAddress ), 
        sizeof( TMacAddress ) );

    // compute space possibly required at the end of the dot11 mac
    // header for the QoS control field, which is required for QoS data frames
    // This will be needed (i.e. the qosOffset will be > 0) if QoS is enabled
    const TUint qosOffset = ComputeQosOffsetAmount( aCtxImpl );
    // the mac header of HT QoS data frames also has an additional field,
    // so determine if that field needs to be present and hence has a non-zero
    // length
    const TUint KHtControlOffset ( aCtxImpl.HtSupportedByNw() ? 
            KHtControlFieldLength : 0 );
    
    if ( qosOffset )
        {
        // a QoS data frame
        
        OsTracePrint( KWsaTxDetails | KQos, (TUint8*)
            ("UMAC: qos data frame"));    

        SQosDataFrameHeader* dot11QosDataFrameHdr = 
            reinterpret_cast<SQosDataFrameHeader*>(
                etherFrameBeginning 
                + ether_offset
                - sizeof( KEncapsulatingRfc1042SnapHeader )
                - encryption_offset
                - KHtControlOffset
                - sizeof( SQosDataFrameHeader ) );

        // construct the MAC header
        new (dot11QosDataFrameHdr) SQosDataFrameHeader;
        
        // set the frame type
        dot11QosDataFrameHdr->iHdr.iFrameControl.iType = E802Dot11FrameTypeQosData;
        
        // reset the QoS control field
        // => ack policy == acknowledge && priority == best effort
        dot11QosDataFrameHdr->ResetQosControl();
        
        // set the user priority
        dot11QosDataFrameHdr->SetUserPriority( aDataBuffer.UserPriority() );

        dot11_dataframe_hdr = reinterpret_cast<SDataFrameHeader*>(
            dot11QosDataFrameHdr);

        if ( KHtControlOffset )
            {
            // HT control field is present => order bit needs to be set
            dot11QosDataFrameHdr->iHdr.SetOrderBit();

            // clear the HT Control field, too
            reinterpret_cast<SHtQosDataFrameHeader*>(
                dot11QosDataFrameHdr)->ResetHtControl();
            }
        else
            {
            // HT control field is not present => order bit needs to be cleared
            dot11QosDataFrameHdr->iHdr.ClearOrderBit();
            }        
        }
    else
        {
        // a non-QoS data frame

        OsTracePrint( KWsaTxDetails, (TUint8*)
            ("UMAC: non-qos data frame"));    

        dot11_dataframe_hdr = reinterpret_cast<SDataFrameHeader*>(
            etherFrameBeginning 
            + ether_offset
            - sizeof( KEncapsulatingRfc1042SnapHeader )
            - encryption_offset
            - sizeof( SDataFrameHeader ) );

        // construct the MAC header. In this case this also sets the frame type
        // correctly
        new (dot11_dataframe_hdr) SDataFrameHeader;
        }

    // set the source address
    dot11_dataframe_hdr->iAddress2 = sa;

    // set the destination address
    DoSetTxMpduDaAddress( *dot11_dataframe_hdr, da );
    
    // set the To DS bit
    if ( aCtxImpl.NetworkOperationMode() == WHA::EBSS )
        {
        dot11_dataframe_hdr->SetToDsBit();        
        // set the BSS ID
        dot11_dataframe_hdr->iAddress1 = aCtxImpl.GetBssId();
        }
    else
        {
        dot11_dataframe_hdr->ClearToDsBit();
        // set the BSS ID
        dot11_dataframe_hdr->iAddress3 = aCtxImpl.GetBssId();
        }
        
    // determine if the frame needs to be encrypted
    if ( EncryptTxFrames( aCtxImpl, aDataBuffer ) )
        {
        dot11_dataframe_hdr->SetWepBit();
        }
    else
        {
        dot11_dataframe_hdr->ClearWepBit();
        }
        
    // set the snap header to correct location. 
    os_memcpy( 
        reinterpret_cast<TUint8*>(dot11_dataframe_hdr)
        + sizeof( SDataFrameHeader )
        + qosOffset
        + KHtControlOffset
        // space occupied by encryption header(s)
        + encryption_offset,  
        &KEncapsulatingRfc1042SnapHeader,
        sizeof( KEncapsulatingRfc1042SnapHeader ) );        

    // clear the area reserved for IV etc, when necessary
    if ( encryption_offset )
        {
        os_memset( 
            reinterpret_cast<TUint8*>(dot11_dataframe_hdr) 
                + sizeof( SDataFrameHeader ) 
                + qosOffset
                + KHtControlOffset,
            0,
            encryption_offset );           
        }

    // compute padding required for encryption trailer (ICV, MIC etc)
    const TUint encryptTrailerLength = (
        EncryptTrailerLength( aCtxImpl, aDataBuffer ) );
    
    // clear the area reserved for encryption trailer, when necessary
    if ( encryptTrailerLength )
        {
        os_memset( 
            reinterpret_cast<TUint8*>(dot11_dataframe_hdr) 
            + sizeof( SDataFrameHeader )
            + qosOffset
            + KHtControlOffset
            + encryption_offset
            + sizeof( SSnapHeader )  
            + aDataBuffer.GetLength() - ether_offset,
            0,
            encryptTrailerLength );                   
        }
    
    // calculate frame length 
    const TUint length_of_frame = 
        // MAC header length
        sizeof( SDataFrameHeader )
        // length of possibly existing QoS control field 
        + qosOffset
        // length of possibly existing HT control field
        + KHtControlOffset
        // SNAP header length
        + sizeof( SSnapHeader ) 
        // length of ethernet payload (including ether type)
        + aDataBuffer.GetLength() - ether_offset
        // encryption header length
        + encryption_offset
        // encryption trailer length
        + encryptTrailerLength;

    // we now have a dot11 frame ready to be sent
    aDataCntx.Dot11FrameReady( 
        reinterpret_cast<TUint8*>(dot11_dataframe_hdr),
        length_of_frame );

    OsTracePrint( KWsaTxDetails, (TUint8*)
        ("UMAC: length_of_frame (excl. fcs): %d"),
        length_of_frame );
    OsTracePrint( KWsaTxDetails, (TUint8*)
        ("UMAC: frame start address: 0x%08x"),
        reinterpret_cast<TUint32>(dot11_dataframe_hdr) );
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::EncapsulateSnapFrame(
    WlanContextImpl& aCtxImpl,
    TWlanUserTxDataCntx& aDataCntx,
    TDataBuffer& aDataBuffer,
    TBool aEncrypt,
    TUint aEncryptionOffset,
    TUint aEncryptTrailerLength,
    TUint aQosOffset,
    TUint aHtControlOffset ) const
    {
    // extract start of frame. The frame starts with a SNAP header
    TUint8* snapFrameBeginning ( aDataBuffer.GetBuffer() );

    // start of dot11 frame
    SDataFrameHeader* dot11_dataframe_hdr( NULL );
    
    if ( aQosOffset )
        {
        OsTracePrint( KWsaTxDetails | KQos, (TUint8*)
            ("UMAC: qos data frame"));    

        SQosDataFrameHeader* dot11QosDataFrameHdr( 
            reinterpret_cast<SQosDataFrameHeader*>(
                snapFrameBeginning 
                - aEncryptionOffset
                - aHtControlOffset
                - sizeof( SQosDataFrameHeader )) );

        // construct the MAC header
        new (dot11QosDataFrameHdr) SQosDataFrameHeader;
        
        // set the frame type
        dot11QosDataFrameHdr->iHdr.iFrameControl.iType = E802Dot11FrameTypeQosData;
        
        // reset the QoS control field
        // => ack policy == acknowledge && priority == best effort
        dot11QosDataFrameHdr->ResetQosControl();        

        // set the user priority
        dot11QosDataFrameHdr->SetUserPriority( aDataBuffer.UserPriority() );

        dot11_dataframe_hdr = reinterpret_cast<SDataFrameHeader*>(
            dot11QosDataFrameHdr);

        if ( aHtControlOffset )
            {
            // HT control field is present => order bit needs to be set
            dot11QosDataFrameHdr->iHdr.SetOrderBit();
            // clear the HT Control field, too
            reinterpret_cast<SHtQosDataFrameHeader*>(
                dot11QosDataFrameHdr)->ResetHtControl();
            }
        else
            {
            // HT control field is not present => order bit needs to be cleared
            dot11QosDataFrameHdr->iHdr.ClearOrderBit();
            }        
        }
    else
        {
        OsTracePrint( KWsaTxDetails | KQos, (TUint8*)
            ("UMAC: non-qos data frame"));    

        dot11_dataframe_hdr = reinterpret_cast<SDataFrameHeader*>(
            snapFrameBeginning 
            - aEncryptionOffset
            - sizeof( SDataFrameHeader ) );

        // construct the MAC header. In this case this also sets the frame type
        // correctly
        new (dot11_dataframe_hdr) SDataFrameHeader;
        }

    // set the source address
    dot11_dataframe_hdr->iAddress2 = aCtxImpl.iWlanMib.dot11StationId;

    // set the destination address
    DoSetTxMpduDaAddress( *dot11_dataframe_hdr, 
        aDataBuffer.KeDestinationAddress() );
    
    // set the To DS bit
    if ( aCtxImpl.NetworkOperationMode() == WHA::EBSS )
        {
        dot11_dataframe_hdr->SetToDsBit();        
        // set the BSS ID
        dot11_dataframe_hdr->iAddress1 = aCtxImpl.GetBssId();
        }
    else
        {
        dot11_dataframe_hdr->ClearToDsBit();
        // set the BSS ID
        dot11_dataframe_hdr->iAddress3 = aCtxImpl.GetBssId();
        }
        
    // determine if the frame needs to be encrypted
    if ( aEncrypt )
        {
        dot11_dataframe_hdr->SetWepBit();
        }
    else
        {
        dot11_dataframe_hdr->ClearWepBit();
        }

    // clear the area reserved for IV etc, when necessary
    if ( aEncryptionOffset )
        {
        os_memset( 
            reinterpret_cast<TUint8*>(dot11_dataframe_hdr) 
                + sizeof( SDataFrameHeader ) 
                + aQosOffset
                + aHtControlOffset,
            0,
            aEncryptionOffset );           
        }
    
    // clear the area reserved for encryption trailer, when necessary
    if ( aEncryptTrailerLength )
        {
        os_memset( 
            reinterpret_cast<TUint8*>(dot11_dataframe_hdr) 
            + sizeof( SDataFrameHeader )
            + aQosOffset
            + aHtControlOffset
            + aEncryptionOffset
            + aDataBuffer.GetLength(),
            0,
            aEncryptTrailerLength );                   
        }

    // calculate frame length
    const TUint length_of_frame = 
        // MAC header length
        sizeof( SDataFrameHeader ) +
        // length of possibly existing QoS control field 
        aQosOffset + 
        // length of possibly existing HT control field
        aHtControlOffset +
        // payload (including SNAP header)
        aDataBuffer.GetLength()
        // encryption header length
        + aEncryptionOffset
        // encryption trailer length
        + aEncryptTrailerLength;

    // we now have a dot11 frame ready to be sent
    aDataCntx.Dot11FrameReady( 
        reinterpret_cast<TUint8*>(dot11_dataframe_hdr),
        length_of_frame );

    OsTracePrint( KWsaTxDetails, (TUint8*)
        ("UMAC: length_of_frame: %d"),
        length_of_frame );
    OsTracePrint( KWsaTxDetails, (TUint8*)
        ("UMAC: frame start address: 0x%08x"),
        reinterpret_cast<TUint32>(dot11_dataframe_hdr) );
    // trace the dot11 frame header
    OsTracePrint( KWsaTxDetails, (TUint8*)
        ("UMAC: Encapsulated prorietary SNAP Tx frame:"), 
        *(reinterpret_cast<Sdot11MacHeader*>(dot11_dataframe_hdr))); 
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::SetDot11FrameToTxBuffer(
    const WlanContextImpl& aCtxImpl,
    TWlanUserTxDataCntx& aDataCntx,
    TDataBuffer& aDataBuffer ) const
    {
    OsTracePrint( KWsaTxDetails, (TUint8*)
        ("UMAC: WlanDot11Associated::SetDot11FrameToTxBuffer") );    

    if ( aCtxImpl.HtSupportedByNw() && aCtxImpl.QosEnabled() )
        {
        // in this case we need to insert the HT Control field to the
        // otherwise ready 802.11 MAC frame

        const TUint KOrigLengthOfFrame = aDataBuffer.GetLength();
        const TUint KMgmtFrameMacHdrLen = sizeof( SManagementFrameHeader );
        TUint8* KFrameStart = aDataBuffer.GetBuffer();

        os_memcpy( 
            KFrameStart + 
            KMgmtFrameMacHdrLen +
            KHtControlFieldLength,
            KFrameStart + 
            KMgmtFrameMacHdrLen,
            KOrigLengthOfFrame - KMgmtFrameMacHdrLen );

        // clear the added HT Control field
        reinterpret_cast<SHtManagementFrameHeader*>( 
            KFrameStart)->ResetHtControl();
        // update frame length
        aDataBuffer.KeSetLength( KOrigLengthOfFrame + KHtControlFieldLength );
        // as the HT control field is present the order bit needs to be set
        reinterpret_cast<SHtManagementFrameHeader*>( 
            KFrameStart)->iMgmtFrameHdr.SetOrderBit();        
        }
    
    // we now have a dot11 frame ready to be sent
    aDataCntx.Dot11FrameReady( 
        aDataBuffer.GetBuffer(), 
        aDataBuffer.GetLength() );
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::EncapsulateFrame(
    WlanContextImpl& aCtxImpl,
    TWlanUserTxDataCntx& aDataCntx,
    TDataBuffer& aDataBuffer,
    TUint16& aEtherType )
    {
    const TDataBuffer::TFrameType KFrameType( aDataBuffer.FrameType() );
    
    OsTracePrint( KWsaTx, (TUint8*)
        ("UMAC: WlanDot11Associated::EncapsulateFrame: frame type: %d"),
        KFrameType );
    
    if ( KFrameType == TDataBuffer::KEthernetFrame || 
         KFrameType == TDataBuffer::KEthernetTestFrame )
        {
        // ethernet II frame in the buffer for tx

        EncapsulateEthernetFrame( 
            aCtxImpl, 
            aDataCntx, 
            aDataBuffer, 
            aEtherType );
        }
    else if ( KFrameType == TDataBuffer::KSnapFrame )
        {
        // frame beginning with a SNAP header in the buffer for tx

        EncapsulateSnapFrame( 
            aCtxImpl, 
            aDataCntx, 
            aDataBuffer,
            EncryptTxFrames( aCtxImpl, aDataBuffer ),
            ComputeEncryptionOffsetAmount( aCtxImpl, aDataBuffer ),
            EncryptTrailerLength( aCtxImpl, aDataBuffer ),
            ComputeQosOffsetAmount( aCtxImpl ),
            aCtxImpl.HtSupportedByNw() ? 
                KHtControlFieldLength : 
                0 );
        }
    else if ( KFrameType == TDataBuffer::KDot11Frame )
        {
        // ready made 802.11 frame in the buffer for tx

        SetDot11FrameToTxBuffer( aCtxImpl, aDataCntx, aDataBuffer );
        }
    else
        {
        // not supported
        OsTracePrint( KErrorLevel, (TUint8*)("UMAC: frame_type: %d"),
            KFrameType );
        OsAssert( (TUint8*)("UMAC: panic"),(TUint8*)(WLAN_FILE), __LINE__ );                
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11Associated::TxData( 
    WlanContextImpl& aCtxImpl,
    TDataBuffer& aDataBuffer,
    TBool aMore )
    {
    TWlanUserTxDataCntx& data_cntx( aCtxImpl.GetTxDataCntx() );
    
    TBool stateChange( EFalse );

    if ( (aCtxImpl.ProtocolStackTxDataAllowed()) )
        {
        // protocol stack tx data allowed
        // now construct a dot11 frame from databuffer to storage

        TUint16 etherType( 0 ); // initial value: not relevant
        
        // construct the frame
        EncapsulateFrame( aCtxImpl, data_cntx, aDataBuffer, etherType );

        // dot11 frame ready to be sent so push it to the packet sceduler

        // start of dot11 frame to send
        const TUint8* start_of_frame( 
            data_cntx.StartOfFrame() );

        // select correct tx queue
        const WHA::TQueueId queue_id( 
            QueueId( aCtxImpl, start_of_frame ) );

        // push the frame to packet scheduler for transmission
        aCtxImpl.PushPacketToPacketScheduler(
            start_of_frame,
            data_cntx.LengthOfFrame(),
            queue_id,
            E802Dot11FrameTypeData,
            &aDataBuffer,
            aMore,
            OutgoingMulticastDataFrame( 
                reinterpret_cast<const SDataFrameHeader*>( start_of_frame ) ) );
            // now just wait for the scheduler to call completion methods
            
        // check if we need to change power mgmt mode because of frame Tx
        const TPowerMgmtModeChange KPowerMgmtModeChange ( 
            aCtxImpl.OnFrameTx( queue_id, etherType ) );
        
        // if any change change is needed regarding our power mgmt mode,
        // proceed with it
        stateChange = PowerMgmtModeChange( aCtxImpl, KPowerMgmtModeChange );        
        }
    else
        {
        // protocol stack tx data not allowed

#ifndef NDEBUG
        // programming error
        OsTracePrint( KErrorLevel, (TUint8*)
            ("UMAC: Tx attempted when it's not allowed") );
        OsAssert( (TUint8*)("UMAC: panic"), (TUint8*)(WLAN_FILE), __LINE__ );
#else
        aCtxImpl.iUmac.OnTxProtocolStackDataComplete( 
            KErrNone,
            &aDataBuffer );        
#endif        
        }

    return stateChange;        
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::OnPacketSendComplete(
    WlanContextImpl& aCtxImpl, 
    WHA::TStatus aStatus,
    TUint32 aPacketId,
    WHA::TRate aRate,
    TUint32 /*aPacketQueueDelay*/,
    TUint32 aMediaDelay,
    TUint aTotalTxDelay,
    TUint8 aAckFailures,
    WHA::TQueueId aQueueId,
    WHA::TRate aRequestedRate,
    TBool aMulticastData )
    {
    if ( aPacketId == E802Dot11FrameTypeData ||
         aPacketId == E802Dot11FrameTypeDataEapol )
        {
        // update data frame statistics
        UpdateTxDataFrameStatistics( 
            aCtxImpl,
            aQueueId, 
            aStatus, 
            aMulticastData, 
            aAckFailures,
            aMediaDelay,
            aTotalTxDelay );
        }
    else if ( aPacketId == E802Dot11FrameTypeQosDataNull )        
        {
        // inform Null Data frame sending controller of QoS Null data Tx 
        // completion; successful or not
        aCtxImpl.OnQosNullDataTxCompleted();
        }        
    else if ( aPacketId == E802Dot11FrameTypeDataNull )        
        {
        // inform Null Data frame sending controller of Null data Tx 
        // completion; successful or not
        aCtxImpl.OnNullDataTxCompleted();
        }        

    if ( aStatus == WHA::KSuccess )
        {
        aCtxImpl.OnTxCompleted( aRate, ETrue, aQueueId, aRequestedRate );
    
        aCtxImpl.ResetFailedTxPacketCount();
        DoRegainedBSSIndication( aCtxImpl );

        if ( aPacketId == E802Dot11FrameTypeData ||
             aPacketId == E802Dot11FrameTypeDataEapol ||
             aPacketId == E802Dot11FrameTypeTestFrame )
            {
            // inform Null Data frame sending controller of successful
            // data frame Tx completion
            aCtxImpl.OnDataTxCompleted( 
                aPacketId == E802Dot11FrameTypeDataEapol ?
                // as EAPOL and WAI frames are not really Voice data (we just
                // send them with Voice priority in WMM nw), handle them
                // here as Best Effort (Legacy)
                WHA::ELegacy : 
                aQueueId );
            }
        }
    else if ( aStatus == WHA::KErrorLifetimeExceeded && !aAckFailures )
        {
        // the packet was discarded by WLAN PDD without any Tx attempts
        // So this is not a Tx failure and we don't need to take
        // any further actions
        OsTracePrint( KWsaTxDetails, (TUint8*)
            ("UMAC: WlanDot11Associated::OnPacketSendComplete: packet expired in PDD without any Tx attempts)"));
        }
    else
        {
        // an actual Tx failure has occurred
        
        aCtxImpl.OnTxCompleted( aRate, EFalse, aQueueId, aRequestedRate );
    
        aCtxImpl.IncrementFailedTxPacketCount();

        // if we have failed to send more than threshold number of 
        // consecutive packets, send Consecutive Tx Failures indication to 
        // WLAN Mgmt Client - unless already sent
        if ( aCtxImpl.FailedTxPacketCount() > 
             aCtxImpl.iWlanMib.iFailedTxPacketCountThreshold )
            {
            DoConsecutiveTxFailuresIndication( aCtxImpl );
            aCtxImpl.ResetFailedTxPacketCount();
            }        
        }
    
    aCtxImpl.iUmac.OnTxDataSent();
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::DoConsecutiveBeaconsLostIndication( 
    WlanContextImpl& aCtxImpl )
    {
    if ( aCtxImpl.OnConsecutiveBeaconsLost() )
        {
        OnInDicationEvent( aCtxImpl, EConsecutiveBeaconsLost );
        }
    }

// -----------------------------------------------------------------------------
// 
// -----------------------------------------------------------------------------
//
void WlanDot11Associated::DoConsecutiveTxFailuresIndication( 
    WlanContextImpl& aCtxImpl )
    {
    if ( aCtxImpl.OnConsecutiveTxFailures() )
        {
        OnInDicationEvent( aCtxImpl, EConsecutiveTxFailures );
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::DoRegainedBSSIndication( 
    WlanContextImpl& aCtxImpl )
    {
    if ( aCtxImpl.OnBssRegained() )
        {
        OnInDicationEvent( aCtxImpl, EBSSRegained );
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11Associated::AddMulticastAddr(
    WlanContextImpl& aCtxImpl,
    const TMacAddress& aMacAddr )
    {
    TBool stateTransitionOccurred( EFalse );
    
    OsTracePrint( 
        KWlmCmdDetails, 
        (TUint8*)
        ("UMAC: WlanDot11Associated::AddMulticastAddr(): addr to be added:"),
        aMacAddr);

    if ( aCtxImpl.MulticastFilteringDisAllowed() )
        {
        OsTracePrint( 
            KWlmCmdDetails, 
            (TUint8*)
            ("UMAC: WlanDot11Associated::AddMulticastAddr(): Multicast filtering disallowed"));
            
        OnOidComplete( aCtxImpl, KErrGeneral );        
        }
    else
        {        
        if ( aCtxImpl.WHASettings().iNumOfGroupTableEntrys > 
             aCtxImpl.MulticastAddressCount() )
            {
            // wha layer is able to take in an address
            
            // 1st try to add the address to our own internal bookkeeping
            WlanContextImpl::TGroupAddStatus addStatus = 
                aCtxImpl.AddMulticastAddress( aMacAddr );

            switch ( addStatus )
                {
                case WlanContextImpl::EOk:
                    OsTracePrint( 
                        KWlmCmdDetails, 
                        (TUint8*)
                        ("UMAC: WlanDot11Associated::AddMulticastAddr(): Address will be added to the MIB"));
                    // the address needed to be added and adding went ok.
                    // Now update the group addresses MIB
                    stateTransitionOccurred = SetGroupAddressesTableMib( aCtxImpl ); 
                    break;
                case WlanContextImpl::EAlreadyExists: 
                    OsTracePrint( 
                        KWlmCmdDetails, 
                        (TUint8*)
                        ("UMAC: WlanDot11Associated::AddMulticastAddr(): Address already exists"));
                    // the specified address already exists so there's no need
                    // to update the group addresses MIB
                    // just complete the request with OK status
                    OnOidComplete( aCtxImpl );
                    stateTransitionOccurred = EFalse;           
                    break;
                case WlanContextImpl::EFull:
                    OsTracePrint( 
                        KWlmCmdDetails, 
                        (TUint8*)
                        ("UMAC: WlanDot11Associated::AddMulticastAddr(): Internal address table full; disallow multicast filtering"));
                    // we are not able to take in any more addresses.
                    // We will totally disable the multicast filtering
                    // and we won't allow it to be enabled any more during 
                    // the current nw connection
                    //
                    aCtxImpl.ResetMulticastAddresses();               
                    aCtxImpl.MulticastFilteringDisAllowed( ETrue );
                    stateTransitionOccurred = 
                        SetGroupAddressesTableMib( aCtxImpl );
                    break;
                default:
                    // programming error
                    OsTracePrint( KErrorLevel, (TUint8*)
                        ("UMAC: addStatus: %d"), addStatus );
                    OsAssert( (TUint8*)("UMAC: panic"), 
                        (TUint8*)(WLAN_FILE), __LINE__ );
                }
            }
        else
            {
            OsTracePrint( 
                KWlmCmdDetails, 
                (TUint8*)
                ("UMAC: WlanDot11Associated::AddMulticastAddr(): WHA not able to accept address; disallow multicast filtering"));
            // wha layer is not able to take in an address. Either this is one 
            // address too many, or it doesn't support even a single address.
            // In either case we will totally disable the multicast filtering
            // and we won't allow it to be enabled any more during the current 
            // nw connection
            aCtxImpl.ResetMulticastAddresses();               
            aCtxImpl.MulticastFilteringDisAllowed( ETrue );
            stateTransitionOccurred = SetGroupAddressesTableMib( aCtxImpl );
            }
        }

    // signal caller whether a state transition occurred or not
    return stateTransitionOccurred;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11Associated::RemoveMulticastAddr(
    WlanContextImpl& aCtxImpl,
    TBool aRemoveAll,
    const TMacAddress& aMacAddr )
    {
    TBool stateTransitionOccurred( EFalse );
    
    OsTracePrint( 
        KWlmCmdDetails, 
        (TUint8*)
        ("UMAC: WlanDot11Associated::RemoveMulticastAddr(): addr to be removed:"),
        aMacAddr);

    if ( aCtxImpl.MulticastFilteringDisAllowed() )
        {
        OsTracePrint( 
            KWlmCmdDetails, 
            (TUint8*)
            ("UMAC: WlanDot11Associated::RemoveMulticastAddr(): Multicast filtering disallowed"));
        // filtering is not allowed currently so there can't be any addresses
        // to remove. Just complete the request with OK status            
        OnOidComplete( aCtxImpl );        
        }
    else
        {
        if ( aRemoveAll )        
            {
            OsTracePrint( 
                KWlmCmdDetails, 
                (TUint8*)
                ("UMAC: WlanDot11Associated::RemoveMulticastAddr(): remove all"));
            // remove all addresses; naturally will also disable filtering
            aCtxImpl.ResetMulticastAddresses();
            stateTransitionOccurred = SetGroupAddressesTableMib( aCtxImpl );            
            }
        else
            {            
            // 1st remove the specified address from our own internal 
            // bookkeeping, if it exists
            if ( aCtxImpl.RemoveMulticastAddress( aMacAddr ) )
                {
                OsTracePrint( 
                    KWlmCmdDetails, 
                    (TUint8*)
                    ("UMAC: WlanDot11Associated::RemoveMulticastAddr(): removing the specified address"));
                // it existed, so update the group addresses MIB, too
                stateTransitionOccurred = SetGroupAddressesTableMib( aCtxImpl );                 
                }
            else
                {
                OsTracePrint( 
                    KWlmCmdDetails, 
                    (TUint8*)
                    ("UMAC: WlanDot11Associated::RemoveMulticastAddr(): specified address doesn't exist, nothing to do"));
                // it did't exist, so there's nothing to remove
                // Just complete the request with OK status            
                OnOidComplete( aCtxImpl );                    
                }
            }
        }

    // signal caller whether a state transition occurred or not
    return stateTransitionOccurred;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11Associated::AddBroadcastWepKey(
    WlanContextImpl& aCtxImpl,
    TUint32 aKeyIndex,             
    TBool aUseAsDefaulKey,                
    TUint32 aKeyLength,                      
    const TUint8 aKey[KMaxWEPKeyLength],
    const TMacAddress& aMac )
    {
    return OnAddBroadcastWepKey( aCtxImpl, aKeyIndex, aUseAsDefaulKey, 
        EFalse, // do NOT set as PTK
        aKeyLength, aKey, aMac );
    }

// -----------------------------------------------------------------------------
// 
// -----------------------------------------------------------------------------
//
TBool WlanDot11Associated::ConfigureTxQueueIfNecessary( 
    WlanContextImpl& aCtxImpl,
    TQueueId aQueueId,
    TUint16 aMediumTime,
    TUint32 aMaxTxMSDULifetime )
    {
    // this cast is safe as the types are effectively the same
    const WHA::TQueueId whaQueueId = static_cast<WHA::TQueueId>(aQueueId);
    
    if ( aMediumTime != aCtxImpl.iWlanMib.iMediumTime[whaQueueId] ||
         aMaxTxMSDULifetime != 
            aCtxImpl.iWlanMib.dot11MaxTransmitMSDULifetime[whaQueueId] )
        {
        // at least one of the parameters for this queue is changed => a
        // reconfiguration is needed
        
        // update the queue parameters. These values will be used in the
        // queue reconfiguration

        aCtxImpl.iWlanMib.iMediumTime[aQueueId] = aMediumTime;
        aCtxImpl.iWlanMib.dot11MaxTransmitMSDULifetime[whaQueueId] = 
            aMaxTxMSDULifetime;

        OsTracePrint( KUmacDetails, (TUint8*)
            ("UMAC: WlanDot11Associated::ConfigureTxQueueIfNecessary: reconfiguring the queue is necessary") );

        // reconfigure the queue. Also request the WLAN mgmt client request
        // to be completed
        return ConfigureTxQueue( aCtxImpl, whaQueueId, ETrue );
        }
    else
        {
        // the provided queue parameters have not changed, so no need to
        // reconfigure the queue
        
        OsTracePrint( KUmacDetails, (TUint8*)
            ("UMAC: WlanDot11Associated::ConfigureTxQueueIfNecessary: no queue reconfigure is necessary") );

        // complete the WLAN Mgmt Client request
        OnOidComplete( aCtxImpl, KErrNone );

        // signal caller that no state transition occurred
        return EFalse;
        }
    }
 
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11Associated::SetGroupAddressesTableMib(
    WlanContextImpl& aCtxImpl )
    {
    const TMacAddress* multicastAddresses( NULL );
    const TUint32 nbrOfAddrs( 
        aCtxImpl.GetMulticastAddresses( multicastAddresses ) );

    TUint32 mibLength(  
        // mib header length
        WHA::Sdot11GroupAddressesTable::KHeaderSize
        // + mib data length
        + ( sizeof( TMacAddress ) * nbrOfAddrs ) );

    // align length of MIB to 4-byte boundary
    mibLength = Align4( mibLength );
    
    OsTracePrint( 
        KWlmCmdDetails, 
        (TUint8*)
        ("UMAC: WlanDot11Associated::SetGroupAddressesTableMib(): mibLength: %d"), 
        mibLength );        

    // allocate memory for the mib to write
    WHA::Sdot11GroupAddressesTable* mib 
        = static_cast<WHA::Sdot11GroupAddressesTable*>
        (os_alloc( mibLength )); 

    if ( !mib )
        {
        // allocation failed
        // simulate macnotresponding error
        OsTracePrint( KWarningLevel, (TUint8*)
            ("UMAC: WlanDot11Associated::SetGroupAddressesTableMib(): memory allocating failed") );
        return DoErrorIndication( aCtxImpl, WHA::KErrorMacNotResponding );
        }
    
    if ( nbrOfAddrs )
        {
        // at least one address exists, so enable multicast address filtering
        mib->iEnable = ETrue;
        }
    else
        {
        // no addresses, so disable filtering
        mib->iEnable = EFalse;
        OsTracePrint( KWlmCmdDetails, (TUint8*)
            ("UMAC: WlanDot11Associated::SetGroupAddressesTableMib(): no addresses; disable filtering") );
        }

    mib->iNumOfAddrs = nbrOfAddrs;
    
    // copy the multicast addresses after the mib header
    os_memcpy( mib->iAddrData,
               reinterpret_cast<TUint8*>(const_cast<TMacAddress*>(
                    multicastAddresses)),
               ( sizeof( TMacAddress ) * nbrOfAddrs ) );
        
    WlanWsaWriteMib& wha_cmd = aCtxImpl.WsaWriteMib();
        
    wha_cmd.Set( 
        aCtxImpl, 
        WHA::KMibDot11GroupAddressesTable, 
        mibLength, 
        mib );
        
    // change global state: entry procedure triggers action
    ChangeState( aCtxImpl, 
        *this,              // prev state
        wha_cmd,            // next state
        // the ACT
        KCompleteManagementRequest
        );   

    os_free( mib ); // release the allocated memory

    // signal caller that a state transition occurred
    return ETrue;
    }         

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11Associated::PowerMgmtModeChange(
    WlanContextImpl& aCtxImpl,
    TPowerMgmtModeChange aPowerMgmtModeChange )
    {
    TBool stateChange ( EFalse );
    
    if ( aPowerMgmtModeChange != ENoChange )
        {
        // power mgmt mode change needed
        
        if ( aPowerMgmtModeChange == EToActive )
            {
            aCtxImpl.DesiredDot11PwrMgmtMode( WHA::KPsDisable );                
            }
        else if ( aPowerMgmtModeChange == EToLightPs )
            {
            aCtxImpl.DesiredDot11PwrMgmtMode( WHA::KPsEnable );
            aCtxImpl.SetDesiredPsModeConfig( 
                aCtxImpl.ClientLightPsModeConfig() );                
            }
        else // aPowerMgmtModeChange == EToDeepPs
            {
            aCtxImpl.DesiredDot11PwrMgmtMode( WHA::KPsEnable );
            aCtxImpl.SetDesiredPsModeConfig( 
                aCtxImpl.ClientDeepPsModeConfig() );                
            }
        
        if ( !(aCtxImpl.WsaCmdActive()) )
            {
            // proceed with the power mgmt mode change
            stateChange = ChangePowerMgmtMode( aCtxImpl );
            }
        else
            {
            // WHA command is in progress so we must defer this access
            aCtxImpl.RegisterEvent( KPowerMgmtTransition );

            OsTracePrint( KEventDispatcher | KPwrStateTransition, 
                (TUint8*)("UMAC: WlanDot11Associated::PowerMgmtModeChange: power mgmt mode change event registered"));                
            }
        }
    
    return stateChange;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11Associated::HandleProprietarySnapRxFrame( 
    TDataBuffer& aBuffer,
    TBool aQosData, 
    const TAny* const aFrame, 
    const SAmsduSubframeHeader* aSubFrameHeader,
    TUint aLength,
    TUint aDecryptHeaderLen,
    TUint aDecryptTrailerLen,
    TUint aHtControlLen,
    TUint8* aCopyBuffer ) const
    {
    OsTracePrint( KRxFrame, (TUint8*)
        ("UMAC: WlanDot11Associated::HandleProprietarySnapRxFrame"));

    // prepare received frame with proprietary SNAP header for wlan mgmt client
    // Remove the possibly existing security header & trailer before
    // passing the frame up
    
    const TUint8* frameBeginning = reinterpret_cast<const TUint8*>(aFrame);
    const TUint KMacHdrLen( aQosData ? 
        sizeof( SQosDataFrameHeader ) + aHtControlLen : 
        sizeof( SDataFrameHeader ) );
    // subframe header length is non-zero only if the frame is part of an 
    // A-MSDU, i.e. if aCopyBuffer is not NULL
    const TUint KSubframeHdrLen ( 
        aCopyBuffer ? sizeof( SAmsduSubframeHeader ) : 0 );    
    // determine subframe length
    const TUint KSubframeLen ( 
        aSubFrameHeader ? aSubFrameHeader->Length() : 0 );    

    if ( aCopyBuffer )
        {
        // the frame needs to be copied to the copy buffer, which means
        // that it is part of an A-MSDU

        // 1st copy the MAC header
        os_memcpy( aCopyBuffer, frameBeginning, KMacHdrLen );
                
        // then copy the subframe body following the subframe header
        os_memcpy( 
            aCopyBuffer 
            + KMacHdrLen, 
            reinterpret_cast<const TUint8*>(aSubFrameHeader) + KSubframeHdrLen,
            KSubframeLen );

        // update to point to the new location
        frameBeginning = aCopyBuffer;
        }
    else
        {
        // no copying to the copy buffer is required
        
        if ( aDecryptHeaderLen )
            {
            // decrypt header exists. Shift the MAC header so that it
            // overwrites the decrypt header, thus removing it
            
            TUint8* dest( const_cast<TUint8*>(frameBeginning) 
                          + aDecryptHeaderLen );
    
            TUint copyBlockSize( aQosData ? 
                sizeof( SQosDataFrameHeader ) + aHtControlLen : 
                sizeof( SDataFrameHeader ) );
    
            os_memcpy( dest, frameBeginning, copyBlockSize );

            // update to point to the new location
            frameBeginning = dest;            
            }
        }

#ifndef NDEBUG
    OsTracePrint( KRxFrame, (TUint8*)
        ("UMAC: WlanDot11Associated::HandleProprietarySnapRxFrame: MPDU header:"), 
        *(reinterpret_cast<const Sdot11MacHeader*>(
            frameBeginning)));
#endif

    // set the frame length
    if ( aCopyBuffer )
        {
        aBuffer.KeSetLength( KMacHdrLen + KSubframeLen );        
        }
    else
        {
        aBuffer.KeSetLength( 
            aLength - aDecryptHeaderLen - aDecryptTrailerLen );
        }
    // set the frame type
    aBuffer.FrameType( TDataBuffer::KDot11Frame );
    // set the offset to the beginning of the actual frame within the
    // Rx buffer
    aBuffer.KeSetOffsetToFrameBeginning(
        frameBeginning                  // frame beginning
        - aBuffer.KeGetBufferStart() ); // buffer beginning
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11Associated::ConfigureTxRatePolicies( 
    WlanContextImpl& aCtxImpl,
    const TTxRatePolicy& aRatePolicy,
    const TQueue2RateClass& aQueue2RateClass,
    const TInitialMaxTxRate4RateClass& aInitialMaxTxRate4RateClass,
    const TTxAutoRatePolicy& aAutoRatePolicy,
    const THtMcsPolicy& aHtMcsPolicy )
    {
    OsTracePrint( KTxRateAdapt, (TUint8*)
        ("UMAC: WlanDot11Associated::ConfigureTxRatePolicies"));

    TBool stateChange( EFalse );

    if ( aCtxImpl.ProtocolStackTxDataAllowed() )
        {
        // store the provided information ...
        StoreTxRatePolicyInfo( 
            aCtxImpl,
            aRatePolicy,
            aQueue2RateClass,
            aInitialMaxTxRate4RateClass,
            aAutoRatePolicy,
            aHtMcsPolicy );

        // ... take it into use; and specify that the mgmt client request needs
        // to be completed when doing it
        stateChange = WlanDot11State::ConfigureTxRatePolicies( aCtxImpl, 
                                                               ETrue );
        if ( !stateChange )
            {
            // a fatal error occurred. Simulate MAC Not Responding error
            // Note that the Mgmt Client request will be completed when 
            // entering the dot11error state
            stateChange = DoErrorIndication( 
                aCtxImpl, 
                WHA::KErrorMacNotResponding );            
            }
        }
    else
        {
        // as user data is not allowed currently, it means that WLAN Mgmt client
        // will request us to connect to a new nw shortly and this policy is for
        // that new nw. So we shouldn't take the new rate policy into use yet, 
        // as we don't know which rates the new nw will be supporting. We just 
        // store the provided information for later use
        stateChange = WlanDot11State::ConfigureTxRatePolicies( 
            aCtxImpl,
            aRatePolicy,
            aQueue2RateClass,
            aInitialMaxTxRate4RateClass,
            aAutoRatePolicy,
            aHtMcsPolicy );
        }
        
    return stateChange;
    }

// -----------------------------------------------------------------------------
// 
// -----------------------------------------------------------------------------
//
TBool WlanDot11Associated::ConfigurePwrModeMgmtTrafficOverride( 
    WlanContextImpl& aCtxImpl,
    TBool aStayInPsDespiteUapsdVoiceTraffic,
    TBool aStayInPsDespiteUapsdVideoTraffic,
    TBool aStayInPsDespiteUapsdBestEffortTraffic, 
    TBool aStayInPsDespiteUapsdBackgroundTraffic,
    TBool aStayInPsDespiteLegacyVoiceTraffic,
    TBool aStayInPsDespiteLegacyVideoTraffic,
    TBool aStayInPsDespiteLegacyBestEffortTraffic,
    TBool aStayInPsDespiteLegacyBackgroundTraffic )
    {
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanDot11Associated::ConfigurePwrModeMgmtTrafficOverride"));

    aCtxImpl.ConfigurePwrModeMgmtTrafficOverride( 
        aStayInPsDespiteUapsdVoiceTraffic,
        aStayInPsDespiteUapsdVideoTraffic,
        aStayInPsDespiteUapsdBestEffortTraffic, 
        aStayInPsDespiteUapsdBackgroundTraffic,
        aStayInPsDespiteLegacyVoiceTraffic,
        aStayInPsDespiteLegacyVideoTraffic,
        aStayInPsDespiteLegacyBestEffortTraffic,
        aStayInPsDespiteLegacyBackgroundTraffic );

    // as we are already connected and aware of the network capabilities, we 
    // also need to freeze the dynamic power mode mgmt traffic 
    // override/ignoration settings so that they become immediately effective
    aCtxImpl.FreezePwrModeMgmtTrafficOverride();

    OnOidComplete( aCtxImpl, KErrNone );

    // signal caller that no state transition occurred
    return EFalse;    
    }
