/*
* Copyright (c) 2002-2009 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 WlanDot11InfrastructureMode class
*
*/

/*
* %version: 63 %
*/

#include "config.h"
#include "UmacDot11InfrastructureMode.h"
#include "UmacWsaAddKey.h"
#include "umacaddbroadcastwepkey.h"
#include "UmacContextImpl.h"
#include "UmacWsaKeyIndexMapper.h"
#include "UmacWsaWriteMib.h"
#include "umacelementlocator.h"
#include "umacwhaconfigureac.h"
#include "FrameXferBlock.h"


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


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::Connect(
    WlanContextImpl& aCtxImpl,
    const TSSID& aSSID,                 
    const TMacAddress& aBSSID,          
    TUint16 aAuthAlgorithmNbr,      
    TEncryptionStatus aEncryptionStatus,
    TBool /*aIsInfra*/,
    TUint16 aScanResponseFrameBodyLength,
    const TUint8* aScanResponseFrameBody,
    const TUint8* aIeData,
    TUint16 aIeDataLength )
    {
    // store data for later access.
    // Pointers supplied are valid to the point the
    // corresponding completion method is called

    aCtxImpl.SetScanResponseFrameBody( aScanResponseFrameBody );
    aCtxImpl.SetScanResponseFrameBodyLength( aScanResponseFrameBodyLength );            
    aCtxImpl.IeData( aIeData );
    aCtxImpl.IeDataLength( aIeDataLength );

    return Connect( 
        aCtxImpl, 
        aSSID, 
        aBSSID, 
        aAuthAlgorithmNbr, 
        aEncryptionStatus
        );
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::Connect(
    WlanContextImpl& aCtxImpl,
    const TSSID& aSSID,                 
    const TMacAddress& aBSSID,          
    TUint16 aAuthAlgorithmNbr,      
    TEncryptionStatus aEncryptionStatus )
    {
    // construct disassociation frame
    // note that we don't have to set SA because we have already set it
    // in the initialize phase of the dot11 state machine
    //

    // set the BSSID of the existing network
    (aCtxImpl.GetDisassociationFrame()).iHeader.iBSSID = aCtxImpl.GetBssId();
    (aCtxImpl.GetHtDisassociationFrame()).iHeader.iBSSID = aCtxImpl.GetBssId();
    // set the DA
    (aCtxImpl.GetDisassociationFrame()).iHeader.iDA = aCtxImpl.GetBssId();
    (aCtxImpl.GetHtDisassociationFrame()).iHeader.iDA = aCtxImpl.GetBssId();
    
    // set current BSSID in reassociation request frame
    (aCtxImpl.GetReassociationRequestFrame()).iFixedFields.iCurrentApAddress = 
        aCtxImpl.GetBssId();
    (aCtxImpl.GetHtReassociationRequestFrame()).iFixedFields.iCurrentApAddress = 
        aCtxImpl.GetBssId();

    // ... and make a note that we need to perform reassociation 
    // instead of association later on towards the new AP
    aCtxImpl.Reassociate( ETrue );

    
    // Store parameters of the new BSS to connect to
    //
    (aCtxImpl.GetBssId())= aBSSID;   
    (aCtxImpl.GetSsId()) = aSSID;   
    (aCtxImpl.EncryptionStatus()) = aEncryptionStatus;   
    
    // set values in authentication frame
    //
    (aCtxImpl.AuthenticationAlgorithmNumber()) = aAuthAlgorithmNbr;
    // set the BSSID field
    (aCtxImpl.GetAuthenticationFrame()).iHeader.iBSSID = aBSSID;
    (aCtxImpl.GetHtAuthenticationFrame()).iHeader.iBSSID = aBSSID;
    // set the DA field
    (aCtxImpl.GetAuthenticationFrame()).iHeader.iDA = aBSSID;
    (aCtxImpl.GetHtAuthenticationFrame()).iHeader.iDA = aBSSID;
    // set the SA field
    (aCtxImpl.GetAuthenticationFrame()).iHeader.iSA 
        = aCtxImpl.iWlanMib.dot11StationId;
    (aCtxImpl.GetHtAuthenticationFrame()).iHeader.iSA 
        = aCtxImpl.iWlanMib.dot11StationId;
    aCtxImpl.NetworkOperationMode( WHA::EBSS );
    
    if ( aCtxImpl.DisassociatedByAp() )
        {
        // if the AP has already sent us a disassociation or deauthentication
        // frame, we won't send it a disassociation frame any more. So in this
        // case we skip the dot11DisassociationPending state and go directly to 
        // dot11Synchronize state.        

        aCtxImpl.DisassociatedByAp( EFalse ); // also reset the flag
        
        ChangeState( aCtxImpl, 
            *this,                                          // prev state
            aCtxImpl.iStates.iSynchronizeState              // next state
            );        
        }
    else
        {
        // the most common case, i.e. we are still associated with the current
        // AP. So make a state change to dot11DisassociationPending
        ChangeState( aCtxImpl, 
            *this,                                          // prev state
            aCtxImpl.iStates.iDisassociationPendingState    // next state
            );
        }

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

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::SetRcpiTriggerLevel(
    WlanContextImpl& aCtxImpl,
    TUint32 aRcpiTrigger)
    {
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanDot11InfrastructureMode::SetRcpiTriggerLevel: aRcpiTrigger: %d"), 
        aRcpiTrigger );
 
    // update the MIB. Also request the WLAN mgmt client request
    // to be completed
    return SetRcpiTriggerLevelMib(aCtxImpl, aRcpiTrigger, ETrue );
    }

// -----------------------------------------------------------------------------
// 
// -----------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::SetRcpiTriggerLevelMib(
    WlanContextImpl& aCtxImpl,
    TUint32 aRcpiTrigger,
    TBool aCompleteManagementRequest )
    {
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanDot11InfrastructureMode::SetRcpiTriggerLevelMib: aRcpiTrigger: %d"), 
        aRcpiTrigger );
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanDot11InfrastructureMode::SetRcpiTriggerLevelMib: aCompleteManagementRequest: %d"), 
        aCompleteManagementRequest );

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

    if ( !mib )
        {
        // allocation failed
        // simulate macnotresponding error
        OsTracePrint( KWarningLevel, (TUint8*)
            ("UMAC: WlanDot11InfrastructureMode::SetRcpiTriggerLevelMib: abort") );
        return DoErrorIndication( aCtxImpl, WHA::KErrorMacNotResponding );
        }

    mib->iThreshold = aRcpiTrigger;

    WlanWsaWriteMib& wha_cmd = aCtxImpl.WsaWriteMib();
        
    wha_cmd.Set( aCtxImpl, WHA::KMibRcpiThreshold, sizeof(*mib), mib );

    const TUint32 KNoNeedToCompleteManagementRequest = 0;
        
    // change global state: entry procedure triggers action
    ChangeState( aCtxImpl, 
        *this,              // prev state
        wha_cmd,            // next state
        aCompleteManagementRequest ? KCompleteManagementRequest : 
                                     KNoNeedToCompleteManagementRequest
        );   

    os_free( mib ); // release the memory

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

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11InfrastructureMode::OnDeauthenticateFrameRx( 
    WlanContextImpl& aCtxImpl,
    TUint8* aBuffer )
    {
    // note that the AP has disassociated us (as the AP deauthenticated us
    // we are also disassociated)
    aCtxImpl.DisassociatedByAp( ETrue );
    aCtxImpl.StopVoiceOverWlanCallMaintenance();
    aCtxImpl.StopKeepAlive();
    OnInDicationEvent( aCtxImpl, EMediaDisconnect );

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

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11InfrastructureMode::OnDisassociateFrameRx( 
    WlanContextImpl& aCtxImpl,
    TUint8* aBuffer )
    {
    // note that the AP has disassociated us
    aCtxImpl.DisassociatedByAp( ETrue );
    aCtxImpl.StopVoiceOverWlanCallMaintenance();
    aCtxImpl.StopKeepAlive();
    OnInDicationEvent( aCtxImpl, EMediaDisconnect );

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

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

    // buffer begin
    const TUint8* ptr = reinterpret_cast<const TUint8*>(aFrame);
    
    // bypass mac header, timestamp fixed field, beacon interval fixed field
    // and capability fixed field
    const TUint8 offset = 
        KMacHeaderLength 
        + KTimeStampFixedFieldLength 
        + KBeaconIntervalFixedFieldLength
        + KCapabilityInformationFixedFieldLength;        
    ptr += offset; // we now point to the beginning of IEs

    if ( aLength > offset )
        {
        //=================================================================
        // Check if any dynamic nw parameters, that we are monitoring, have 
        // changed. If they have, take the new parameters into use
        //=================================================================

        WlanElementLocator elementLocator( ptr, aLength - offset );
        
        ValidateErpParams( aCtxImpl, elementLocator );

        if ( aCtxImpl.QosEnabled() )
            {
            ValidateAcParams( aCtxImpl, elementLocator );
            }
        else
            {
            // this is not a QoS connection => not relevant to check
            // for AC parameter changes
            }

        if ( aCtxImpl.HtSupportedByNw() )
            {
            ValidateHtBssOperationParams( aCtxImpl, elementLocator );
            }
        else
            {
            // this is not a HT connection => not relevant to check
            // for HT operation parameter changes
            }
        }
    else
        {
        // frame too short to contain any IEs => ignore it
        OsTracePrint( KRxFrame | KWarningLevel, (TUint8*)
            ("UMAC: WlanDot11InfrastructureMode::OnBeaconFrameRx: WARNING: frame too short to contain any IEs => ignored") );        
        }

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

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11InfrastructureMode::ValidateErpParams( 
    WlanContextImpl& aCtxImpl,
    WlanElementLocator& aElementLocator )
    {
    TUint8 length( 0 );
    const TUint8* elementData( NULL );

    if ( WlanElementLocator::EWlanLocateOk == 
         aElementLocator.InformationElement( 
            E802Dot11ErpInformationIE, 
            length, 
            &elementData ) )
        {
        // ERP IE found
        OsTracePrint( KInfoLevel, (TUint8*)
            ("UMAC: WlanDot11InfrastructureMode::ValidateErpParams(): ERP IE present"));
            
        if ( ( *elementData & KUseProtectionMask ) != aCtxImpl.ProtectionBitSet() )            
            {
            OsTracePrint( KInfoLevel, (TUint8*)
                ("UMAC: WlanDot11InfrastructureMode::ValidateErpParams(): use protection setting changed, is now: %d"),
                *elementData & KUseProtectionMask );

            // use protection setting has changed, update the setting & 
            // re-set the MIB
            //
            aCtxImpl.ProtectionBitSet( *elementData & KUseProtectionMask );

            if ( !(aCtxImpl.WsaCmdActive()) )
                {
                SetCtsToSelfMib( aCtxImpl );
                }
            else
                {
                // WHA command is in progress so we must defer this access
                aCtxImpl.RegisterEvent( KSetCtsToSelf );
                }
            }
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11InfrastructureMode::ValidateAcParams( 
    WlanContextImpl& aCtxImpl,
    WlanElementLocator& aElementLocator )
    {
    TUint8 length( 0 );
    const TUint8* elementData( NULL );

    // is WMM Parameter Element present                        
    if ( aElementLocator.InformationElement( 
            E802Dot11VendorSpecificIE,
            KWmmElemOui,
            KWmmElemOuiType,
            KWmmParamElemOuiSubtype,
            length, 
            &elementData ) 
        == WlanElementLocator::EWlanLocateOk )    
        {
        // element found
        OsTracePrint( KQos, (TUint8*)
            ("UMAC: WlanDot11InfrastructureMode::ValidateAcParams(): WMM param elem present"));
        
        if ( (reinterpret_cast<const SWmmParamElemData*>(elementData))->ParameterSetCount() 
            != aCtxImpl.WmmParameterSetCount() )
            {
            // AC parameters have changed => parse them again
            //
            OsTracePrint( KQos, (TUint8*)
                ("UMAC: WlanDot11InfrastructureMode::ValidateAcParams(): WMM param set count changed (value: %d) => params changed"),
                (reinterpret_cast<const SWmmParamElemData*>(elementData))->ParameterSetCount() );

            ParseAcParameters( aCtxImpl,
                reinterpret_cast<const SWmmParamElemData&>(*elementData ) );

            // we also need to configure the ACs again
            //
            if ( !(aCtxImpl.WsaCmdActive()) )
                {
                ConfigureAcParams( aCtxImpl );
                }
            else
                {
                // WHA command is in progress so we must defer this access
                aCtxImpl.RegisterEvent( KAcParamUpdate );
                }
            }
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11InfrastructureMode::ValidateHtBssOperationParams( 
    WlanContextImpl& aCtxImpl,
    WlanElementLocator& aElementLocator )
    {
    TUint8 length( 0 );
    const TUint8* elementData( NULL );

    if ( WlanElementLocator::EWlanLocateOk == 
         aElementLocator.InformationElement( 
            E802Dot11HtOperationIE, 
            length, 
            &elementData ) )
        {
        // HT Operation element found
        OsTracePrint( KInfoLevel, (TUint8*)
            ("UMAC: WlanDot11InfrastructureMode::ValidateHtBssOperationParams: element present"));
            
        if ( os_memcmp( 
                 elementData, 
                 &(aCtxImpl.GetNwHtOperationIe().iData), 
                 sizeof( SHtOperationIeData ) ) )            
            {
            // content of the element has changed 
            OsTracePrint( KInfoLevel, (TUint8*)
                ("UMAC: element changed"));

            // store the changed element content to our context
            aCtxImpl.GetNwHtOperationIe().SetIeData( 
                elementData, 
                length );
            
            // inform the lower layers about the new HT operation 
            // configuration 
            if ( !(aCtxImpl.WsaCmdActive()) )
                {
                ConfigureHtBssOperation( aCtxImpl );
                }
            else
                {
                // WHA command is in progress so we must defer this access
                aCtxImpl.RegisterEvent( KSetHtBssOperation );
                }
            }
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11InfrastructureMode::DoSetTxMpduDaAddress( 
    SDataFrameHeader& aDataFrameHeader, 
    const TMacAddress& aMac ) const
    {
    aDataFrameHeader.iAddress3 = aMac;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::DoIsRxFrameSAourAddress( 
    WlanContextImpl& aCtxImpl,
    const SDataFrameHeader& aFrameHeader,
    const SAmsduSubframeHeader* aSubFrameHeader ) const
    {
    if ( aSubFrameHeader )
        {
        // the MSDU is part of an A-MSDU and the caller wants to use the
        // SA in the subframe header for this check
        return aSubFrameHeader->iSa == aCtxImpl.iWlanMib.dot11StationId;
        }
    else
        {
        // the caller wants to use the SA in the MAC header for this check
        return aFrameHeader.iAddress3 == aCtxImpl.iWlanMib.dot11StationId;
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::DoIsValidAddressBitCombination(
    const SDataFrameHeader& aFrameHeader ) const
    {
    return (aFrameHeader.IsFromDsBitSet() && !(aFrameHeader.IsToDsBitSet()));
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11InfrastructureMode::DoBuildEthernetFrame(
    TDataBuffer& aBuffer,
    const SDataMpduHeader& aDot11DataMpdu,
    const TUint8* aStartOfEtherPayload,
    TUint aEtherPayloadLength,
    TBool aAmsdu,
    TUint8* aCopyBuffer )
    {
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanDot11InfrastructureMode::DoBuildEthernetFrame"));

    TUint8* realEtherPayloadStart ( const_cast<TUint8*>(aStartOfEtherPayload) );
    
    if ( aCopyBuffer )
        {
        // the frame needs to be copied to the copy buffer 

        // update to point to the new location
        realEtherPayloadStart = aCopyBuffer + ( 2 * sizeof( TMacAddress ) );
        
        os_memcpy( 
            realEtherPayloadStart,
            aStartOfEtherPayload, 
            aEtherPayloadLength );
        }

    if ( aAmsdu )
        {
        // this MSDU is part of an A-MSDU. Assign SA and DA from subframe
        // header
        const SAmsduSubframeHeader* KSubframeHeader ( 
            reinterpret_cast<const SAmsduSubframeHeader*>( 
                aStartOfEtherPayload 
                - sizeof( SSnapHeader ) 
                - sizeof( SAmsduSubframeHeader ) ) );

        // copy SA to the correct location.
        // We do that first so that it doesn't get overwritten by DA
        os_memcpy( 
            const_cast<TUint8*>(realEtherPayloadStart) - sizeof( TMacAddress ),
            reinterpret_cast<const TUint8*>(KSubframeHeader->iSa.iMacAddress),
            sizeof( TMacAddress ) );
    
        // copy DA to the correct location.
        os_memcpy( 
            const_cast<TUint8*>(realEtherPayloadStart) 
                - ( 2 * sizeof( TMacAddress ) ), 
            reinterpret_cast<const TUint8*>(KSubframeHeader->iDa.iMacAddress ),
            sizeof( TMacAddress ) );
        
        }
    else
        {
        // assign SA and DA from MAC header 
        
        // copy SA to the correct location.
        // We do that first so that it doesn't get overwritten by DA
        os_memcpy( 
            const_cast<TUint8*>(realEtherPayloadStart) - sizeof( TMacAddress ),
            reinterpret_cast<const TUint8*>(
                aDot11DataMpdu.iHdr.iAddress3.iMacAddress ),
            sizeof( TMacAddress ) );
    
        // copy DA to the correct location.
        os_memcpy( 
            const_cast<TUint8*>(realEtherPayloadStart) 
                - ( 2 * sizeof( TMacAddress ) ), 
            reinterpret_cast<const TUint8*>(
                aDot11DataMpdu.iHdr.iAddress1.iMacAddress ),
            sizeof( TMacAddress ) );
        }
    
    // set the length
    aBuffer.KeSetLength( 
        aEtherPayloadLength 
        + ( sizeof( TMacAddress ) * 2 ) );

    // set the frame type
    aBuffer.FrameType( TDataBuffer::KEthernetFrame );
    
    // set the offset to the beginning of the ready ethernet frame 
    // from the beginning of the Rx buf
    aBuffer.KeSetOffsetToFrameBeginning( 
        realEtherPayloadStart - ( 2 * sizeof( TMacAddress ) ) // frame beginning
        - aBuffer.KeGetBufferStart() );                      // buffer beginning

    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanDot11InfrastructureMode::DoBuildEthernetFrame: offset to frame beginning: %d"),
        aBuffer.KeOffsetToFrameBeginning());
    }
    
// -----------------------------------------------------------------------------
// 
// -----------------------------------------------------------------------------
//
void WlanDot11InfrastructureMode::DoConsecutivePwrModeSetFailuresIndication( 
    WlanContextImpl& aCtxImpl )
    {
    if ( aCtxImpl.OnConsecutivePwrModeSetFailures() )
        {
        OnInDicationEvent( aCtxImpl, EConsecutivePwrModeSetFailures );
        }
    }
    
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::Disconnect( WlanContextImpl& aCtxImpl )
    {
    OsTracePrint( KInfoLevel, (TUint8*)("UMAC: WlanDot11InfrastructureMode::Disconnect():"));

    // set completion code to idle state
    // as it does the request completion

    aCtxImpl.iStates.iIdleState.Set( KErrNone );
    ChangeState( aCtxImpl, 
        *this,                                  // prev state
        aCtxImpl.iStates.iDeauthPendingState    // next state
        );                      

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

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::RealScan(
    WlanContextImpl& aCtxImpl,
    TScanMode aMode,                    
    const TSSID& aSSID,                 
    TUint32 aScanRate,                    
    SChannels& aChannels,
    TUint32 aMinChannelTime,            
    TUint32 aMaxChannelTime,
    TBool aSplitScan )
    {
    // scanning mode requested
    // set parameters
    // NOTE: OID command parameters are guaranteed to be valid
    // to the point a correcponding completion method is called

    aCtxImpl.iStates.iInfrastructureScanningMode.Set( 
        aMode, aSSID, aScanRate, aChannels,
        aMinChannelTime, aMaxChannelTime, aSplitScan );

    ChangeState( aCtxImpl, 
        *this,                                          // prev state
        aCtxImpl.iStates.iInfrastructureScanningMode    // next state
        );                      

    return ETrue; // global statemachine transition will occur 
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11InfrastructureMode::TxMgmtData( 
    WlanContextImpl& aCtxImpl,
    TDataBuffer& aDataBuffer )
    {
    TWlanUserTxDataCntx& data_cntx( aCtxImpl.GetMgmtTxDataCntx() );

    // make a note of the frame type
    const TDataBuffer::TFrameType frameType( aDataBuffer.FrameType() );

    TUint16 etherType( 0 ); // initial value: not relevant
    
    // construct a dot11 frame from databuffer to storage
    EncapsulateFrame( aCtxImpl, data_cntx, aDataBuffer, etherType );
    
    // 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 ) );

    T802Dot11FrameControlTypeMask dot11FrameType ( E802Dot11FrameTypeDataEapol );
    TBool useSpecialTxAutoRatePolicy( EFalse );
    
    switch ( frameType )
        {
        case TDataBuffer::KEthernetFrame:
            // dot11FrameType already correct
            
            // request special Tx autorate policy use for EAPOL and WAI frames
            useSpecialTxAutoRatePolicy = ETrue;
            break;
        case TDataBuffer::KDot11Frame:
            dot11FrameType = E802Dot11FrameTypeManagementAction;
            break;
        case TDataBuffer::KSnapFrame:
            // these frames have the same Tx completion handling as Eapol 
            // frames so we use the same dot11FrameType value  - which is
            // already correct - for them, too
            break;
        case TDataBuffer::KEthernetTestFrame:
            dot11FrameType = E802Dot11FrameTypeTestFrame;
            break;
        default:
            // programming error
            OsTracePrint( KErrorLevel, (TUint8*)
                ("UMAC: unsupported frame type: %d"), frameType );
            OsAssert( (TUint8*)("UMAC: panic"), 
                (TUint8*)(WLAN_FILE), __LINE__ );            
        }
    
    // push the frame to packet scheduler for transmission
    aCtxImpl.PushPacketToPacketScheduler(
        start_of_frame,
        data_cntx.LengthOfFrame(),
        queue_id,
        dot11FrameType,
        &aDataBuffer,
        EFalse,
        EFalse,
        useSpecialTxAutoRatePolicy );
            
    // now just wait for the scheduler to call completion methods

    if ( frameType == TDataBuffer::KEthernetFrame )
        {
        // check if we need to change power mgmt mode because of the frame Tx
    
        const TPowerMgmtModeChange KPowerMgmtModeChange ( 
            aCtxImpl.OnFrameTx( queue_id, etherType ) );
        
        // if any change change is needed regarding our power mgmt mode,
        // proceed with it
        PowerMgmtModeChange( aCtxImpl, KPowerMgmtModeChange );
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11InfrastructureMode::DoRcpiIndication( 
    WlanContextImpl& aCtxImpl,
    WHA::TRcpi aRcpi )
    {
    OsTracePrint( KWlmIndication, (TUint8*)
        ("UMAC: WlanDot11InfrastructureMode::DoRcpiIndication: rcpi: %d"),
        aRcpi ); 
        
    OnInDicationEvent( aCtxImpl, ERcpiTrigger );
    
    // restore the MIB back to its default value, i.e. zero. 
    // This means that no further RCPI indications should arrive from WHA 
    // layer until the RCPI threshold is set again by WLAN Mgmt Client
    if ( !(aCtxImpl.WsaCmdActive()) )
        {
        SetRcpiTriggerLevelMib( aCtxImpl, WHA::KRcpiThresholdDefault, EFalse );
        }
    else
        {
        // WHA command is in progress so we must defer this access
        aCtxImpl.RegisterEvent( KSetRcpiTriggerLevel );
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanDot11InfrastructureMode::DoPsModeErrorIndication( 
    WlanContextImpl& aCtxImpl )
    {
    OsTracePrint( KWlmIndication, (TUint8*)
        ("UMAC: WlanDot11InfrastructureMode::DoPsModeErrorIndication") ); 
    
    OnInDicationEvent( aCtxImpl, EPsModeError );    
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::OnVoiceCallEntryTimerTimeout( 
    WlanContextImpl& aCtxImpl )
    {
    aCtxImpl.OnVoiceCallEntryTimerTimeout();

    return EFalse;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::OnNullTimerTimeout( 
    WlanContextImpl& aCtxImpl )
    {
    aCtxImpl.OnNullTimerTimeout();

    return EFalse;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::OnNoVoiceTimerTimeout( 
    WlanContextImpl& aCtxImpl )
    {
    aCtxImpl.OnNoVoiceTimerTimeout();

    return EFalse;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::OnKeepAliveTimerTimeout( 
    WlanContextImpl& aCtxImpl )
    {
    aCtxImpl.OnKeepAliveTimerTimeout();

    return EFalse;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::OnWlanWakeUpIntervalChange( 
    WlanContextImpl& aCtxImpl )
    {
    OsTracePrint( KPwrStateTransition, (TUint8*)
        ("UMAC: : WlanDot11InfrastructureMode::OnWlanWakeUpIntervalChange"));

    WHA::SwlanWakeUpInterval* mib 
        = static_cast<WHA::SwlanWakeUpInterval*>
        (os_alloc( sizeof( WHA::SwlanWakeUpInterval ) )); 

    if ( !mib )
        {
        // allocation failed
        // simulate macnotresponding error
        OnOidComplete( aCtxImpl );  // complete also
        OsTracePrint( KWarningLevel, 
            (TUint8*)("UMAC: WlanDot11InfrastructureMode::OnWlanWakeUpIntervalChange: alloc failed, abort") );
        return DoErrorIndication( aCtxImpl, WHA::KErrorMacNotResponding );
        }

    // determine the desired new wake-up setting
    const TDot11PsModeWakeupSetting KDesiredPsModeConfig (
        aCtxImpl.DesiredPsModeConfig() );
    
    // take it into use

    mib->iMode = KDesiredPsModeConfig.iWakeupMode;
    mib->iListenInterval = KDesiredPsModeConfig.iListenInterval;
    
    WlanWsaWriteMib& wha_cmd = aCtxImpl.WsaWriteMib();

    wha_cmd.Set( 
        aCtxImpl, WHA::KMibWlanWakeUpInterval, sizeof(*mib), mib );

    OsTracePrint( KPwrStateTransition, 
        (TUint8*)("UMAC: WlanDot11InfrastructureMode::OnWlanWakeUpIntervalChange: desired mode: %d"), 
        mib->iMode );
    OsTracePrint( KPwrStateTransition, (TUint8*)
        ("UMAC: WlanDot11InfrastructureMode::OnWlanWakeUpIntervalChange: desired listen interval: %d"), 
        mib->iListenInterval );

    // store the new setting also locally
    aCtxImpl.iWlanMib.iWlanWakeupInterval = mib->iMode;
    aCtxImpl.iWlanMib.iWlanListenInterval = mib->iListenInterval;

    // change global state: entry procedure triggers action
    ChangeState( aCtxImpl, 
        *this,              // prev state
        wha_cmd,            // next state
        // the ACT signals that this operation should be completed to user 
        WlanDot11State::KCompleteManagementRequest
        );           

    // as the parameters have been supplied we can now deallocate
    os_free( mib );      
    
    // signal state transition change
    return ETrue;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool WlanDot11InfrastructureMode::TxNullDataFrame( 
    WlanContextImpl& aCtxImpl,
    TBool aQosNull )
    {
    OsTracePrint( KWsaTxDetails, (TUint8*)
        ("UMAC: WlanDot11InfrastructureMode::TxNullDataFrame: aQosNull: %d"),
        aQosNull );

    TBool status ( ETrue );
    
    TUint32 lengthOfFrame( 0 );
    T802Dot11FrameControlTypeMask frameType( E802Dot11FrameTypeDataNull );

    // copy Null Data frame to tx-buffer to correct offset
    // client doesn't need to take care of the tx buffer header space
    // as the method below does it by itself
    TUint8* start_of_frame = aCtxImpl.TxBuffer();

    if ( start_of_frame )
        {
        if ( aQosNull )
            {
            // transmit of QoS Null Data Frame requested
            
            lengthOfFrame = aCtxImpl.QosNullDataFrameLength();
            frameType = E802Dot11FrameTypeQosDataNull;
            
            os_memcpy( 
                start_of_frame,
                &(aCtxImpl.QosNullDataFrame()), 
                lengthOfFrame );

            // as this needs to be a trigger frame for Voice AC, set the User
            // Priority of the frame accordingly
                
            const TUint8 K802Dot1dPriorityVoice = 6;
            SQosDataFrameHeader* qosHdr = 
                reinterpret_cast<SQosDataFrameHeader*>(start_of_frame);
            qosHdr->SetUserPriority( K802Dot1dPriorityVoice );
            
            // note that the Order bit of the Frame Control field of the QoS 
            // Null data frame has already been given the correct value in 
            // WlanMacActionState::OnDot11InfrastructureModeStateSpaceEntry()
            // method
            }
        else
            {
            // transmit of Null Data Frame requested
            
            lengthOfFrame = sizeof( SNullDataFrame );
            // frame type is already correct for this case
            
            os_memcpy( 
                start_of_frame,
                &(aCtxImpl.NullDataFrame()), 
                lengthOfFrame );
            }

        // determine Tx queue
        const WHA::TQueueId queue_id( QueueId( aCtxImpl, start_of_frame ) );
        
        // send the Null Data frame by pushing it to packet scheduler
        aCtxImpl.PushPacketToPacketScheduler( 
                start_of_frame, 
                lengthOfFrame, 
                queue_id,
                frameType,
                NULL,
                EFalse,
                EFalse );
        }                
    else
        {
        // we didn't get a Tx buffer so we can't submit a frame send request.
        status = EFalse;
        }
        
    return status;                
    }
