/*
* Copyright (c) 2007-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 WlanNullSendController class
*
*/

/*
* %version: 15 %
*/

#include "config.h"
#include "umacnullsendcontroller.h"
#include "umacnullsender.h"
#include "UmacContextImpl.h"


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


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::StartVoiceOverWlanCallMaintenance()
    {
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::StartVoiceOverWlanCallMaintenance") );

    iFlags |= KVoiceOverWlanCallMaintenanceStarted;
    
    if ( iFlags & KInVoiceCallState )
        {
        RegisterNoVoiceTimeout( iNoVoiceTimeout );
        
        if ( ( iWlanContextImpl.UapsdUsedForVoice() ) &&
             ( iWlanContextImpl.CurrentDot11PwrMgmtMode() == 
               WHA::KPsEnable ) )
            {
            // U-APSD is used for Voice and we are also in PS mode,
            // so initiate QoS Null Frame sending by arming the timer
            RegisterNullTimeout( iNullTimeout );
            }            
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::ResumeQosNullSending()
    {
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::ResumeQosNullSending") );                

    if ( ( iWlanContextImpl.UapsdUsedForVoice() )&&
         ( iFlags & KInVoiceCallState ) &&
         ( iWlanContextImpl.CurrentDot11PwrMgmtMode() == WHA::KPsEnable ) )
        {
        // U-APSD is used for Voice, we are in Voice Call state and in PS
        // mode, so resume QoS null frame sending by re-arming the timer
        RegisterNullTimeout( iNullTimeout );
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::OnFrameRx( 
    WHA::TQueueId aQueueId,
    TUint aPayloadLength )
    {
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::OnFrameRx entry: iFlags: 0x%08x"),
        iFlags );        

    if ( iFlags & KVoiceOverWlanCallMaintenanceStarted )
        {
        if ( iFlags & KInVoiceCallState )
            {
            // we are already in Voice Call state

            const TInt64 KTimeNow( os_systemTime() );
            
            if ( aQueueId == WHA::EVoice )
                {
                // and this is a Voice priority frame.
                
                iLatestVoiceRxOrTxInVoiceCallState = KTimeNow;
                }
            else if ( ( aQueueId == WHA::ELegacy ) && 
                      ( aPayloadLength < 
                        iBestEffortVoiceRxLengthThreshold ) )
                {
                iBestEffortVoiceRxTimeStamp[iBestEffortVoiceRxInd] = KTimeNow;
                iBestEffortVoiceRxInd = 
                    ( iBestEffortVoiceRxInd == 
                      KBestEffortVoiceRxTimeStampCnt - 1 ) ? 0 :
                      ++iBestEffortVoiceRxInd;
                }
            else
                {
                // no action
                }
            }
        else if ( iFlags & KVoiceCallEntryPending )
            {
            // we are in Voice Call Entry Pending state

            if ( aQueueId == WHA::EVoice )
                {
                // this is a Voice priority frame
                
                ++iVoiceCallEntryRxCount;
                
                OsTracePrint( KUmacDetails, (TUint8*)
                    ("UMAC: WlanNullSendController::OnFrameRx: iVoiceCallEntryRxCount is now: %d"),
                    iVoiceCallEntryRxCount );
    
                if ( iVoiceCallEntryRxCount >= iVoiceCallEntryThreshold )
                    {
                    // We will enter the Voice Call state
                    EnterVoiceCallState();
                    }
                }            
            }
        else
            {
            // we are neither in Voice Call Entry Pending nor Voice Call state
    
            if ( aQueueId == WHA::EVoice )
                {
                // this is a Voice priority frame. 
                
                // It is the 1st Voice priority frame in the period starting
                // now
                iVoiceCallEntryRxCount = 1;
                iVoiceCallEntryTxCount = 0;
                
                // We enter the Voice Call Entry Pending state by arming
                // the relevant timer
    
                OsTracePrint( KUmacDetails, (TUint8*)
                    ("UMAC: WlanNullSendController::OnFrameRx: Enter Voice Call Entry pending state") );
    
                RegisterVoiceCallEntryTimeout();
                }
            }
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::OnFrameTx( WHA::TQueueId aQueueId )
    {
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::OnFrameTx entry: iFlags: 0x%08x"),
        iFlags );        

    iLatestTx = os_systemTime();
    
    if ( iFlags & KVoiceOverWlanCallMaintenanceStarted )
        {
        if ( iFlags & KInVoiceCallState )
            {
            // we are already in Voice Call state

            if ( aQueueId == WHA::EVoice )
                {
                // and this is a Voice priority frame.
                
                iLatestVoiceRxOrTxInVoiceCallState = iLatestTx;
                }
            }
        else if ( iFlags & KVoiceCallEntryPending )
            {
            // we are in Voice Call Entry Pending state

            if ( aQueueId == WHA::EVoice )
                {
                // this is a Voice priority frame
                
                ++iVoiceCallEntryTxCount;
                
                OsTracePrint( KUmacDetails, (TUint8*)
                    ("UMAC: WlanNullSendController::OnFrameTx: iVoiceCallEntryTxCount is now: %d"),
                    iVoiceCallEntryTxCount );

                if ( iVoiceCallEntryTxCount >= iVoiceCallEntryThreshold )
                    {
                    // We will enter the Voice Call state
                    EnterVoiceCallState();
                    }
                }            
            }
        else
            {
            // we are neither in Voice Call Entry Pending nor Voice Call state

            if ( aQueueId == WHA::EVoice )
                {
                // this is a Voice priority frame. 
                
                // It is the 1st Voice priority frame in the period starting
                // now
                iVoiceCallEntryTxCount = 1;
                iVoiceCallEntryRxCount = 0;
                                
                // We enter the Voice Call Entry Pending state by arming
                // the relevant timer

                OsTracePrint( KUmacDetails, (TUint8*)
                    ("UMAC: WlanNullSendController::OnFrameTx: Enter Voice Call Entry pending state") );

                RegisterVoiceCallEntryTimeout();
                }
            }
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::OnVoiceCallEntryTimerTimeout()
    {
    // as this timer expired we are no more in Voice Call Entry Pending state
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::OnVoiceCallEntryTimerTimeout: exit entry pending (to idle) state") );
    
    iFlags &= ~KVoiceCallEntryPending;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::OnNullTimerTimeout()
    {
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::OnNullTimerTimeout") );
    
    iFlags &= ~KNullTimerArmed;

    if ( iWlanContextImpl.CurrentDot11PwrMgmtMode() == WHA::KPsEnable )
        {        
        // we are still in PS mode (as we were when this timer was 
        // armed)
        
        TUint32 KTimeAfterLatestTx ( 
            static_cast<TUint32>(os_systemTime() - iLatestTx) );
        
        if ( KTimeAfterLatestTx >= iNullTimeout )
            {
            // request a QoS Null Data frame to be sent.
            // However, if there is a frame Tx (of any frame) already pending, 
            // we won't request a new frame to be sent. Note that it can really
            // be any frame, i.e. for any AC, as we currently always set all the
            // ACs as trigger enabled if U-APSD is used
            if ( !iWlanContextImpl.UnsentTxPackets() )
                {
                if ( !iNullSender.TxNullDataFrame( iWlanContextImpl, ETrue ) )
                    {
                    // frame was not sent because we didn't get a Tx buffer.
                    // In this case we'll skip the sending
                    // However, we need to re-arm the timer to trigger the next
                    // QoS Null Data frame sending
                    RegisterNullTimeout( iNullTimeout );
                    }
                }
            else
                {
                // frame Tx already pending, so we don't ask a QoS Null to be sent
                OsTracePrint( KUmacDetails, (TUint8*)
                    ("UMAC: WlanNullSendController::OnNullTimerTimeout: frame Tx already pending. New QoS Null Tx request skipped") );
                
                // However, we need to re-arm the timer to trigger the next
                // QoS Null Data frame sending
                RegisterNullTimeout( iNullTimeout );
                }
            }
        else
            {
            // No need to send QoS Null; yet. Re-arm the timer with 
            // a suitable timeout relative to the time of the latest Tx
            // in QoS Null frame sending state
            RegisterNullTimeout( iNullTimeout - KTimeAfterLatestTx );
            }
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::OnQosNullDataTxCompleted()
    {
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::OnQosNullDataTxCompleted entry: iFlags: 0x%08x"),
        iFlags );

    if ( iFlags & KVoiceOverWlanCallMaintenanceStarted &&
         iFlags & KInVoiceCallState &&
         iWlanContextImpl.CurrentDot11PwrMgmtMode() == WHA::KPsEnable )
        {
        // we are doing Voice over WLAN Call maintenance, in Voice Call 
        // state and also in PS mode 

        // as the previous QoS Null Data, i.e. U-APSD Trigger, Frame has been
        // transmitted, re-arm the timer for the next round
        RegisterNullTimeout( iNullTimeout );
        }

    iLatestTx = os_systemTime();
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::OnNoVoiceTimerTimeout()
    {
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::OnNoVoiceTimerTimeout entry: iFlags: 0x%08x"),
        iFlags );    

    iFlags &= ~KNoVoiceTimerArmed;
    const TInt64 KTimeNow( os_systemTime() );
    const TUint32 KTimeAfterLatestVoiceRxOrTxInVoiceCallState ( 
        static_cast<TUint32>(KTimeNow - 
                             iLatestVoiceRxOrTxInVoiceCallState) );
    
    if ( KTimeAfterLatestVoiceRxOrTxInVoiceCallState >= iNoVoiceTimeout )
        {
        // no real Voice priority traffic any more.
        
        // check if there still is traffic which may be Voice traffic
        // erroneously tagged as Best Effort priority 
        if ( KTimeNow -
             // the oldest time stamp of these frames that we have in our
             // records - or zero if such a time stamp doesn't exist
             iBestEffortVoiceRxTimeStamp[iBestEffortVoiceRxInd] > 
             iBestEffortVoiceRxTimeWindow )
            {
            // exit Voice Call state
            iFlags &= ~KInVoiceCallState;
        
            // Send voice call state change indication to engine
            iWlanContextImpl.iUmac.OnInDicationEvent( EVoiceCallOff );
    
            OsTracePrint( KUmacDetails, (TUint8*)
                ("UMAC: WlanNullSendController::OnNoVoiceTimerTimeout: Exit Voice Call state") );
            
            // stop the possibly ongoing Null frame sending by canceling the 
            // Null timer
            CancelNullTimeout();
            }
        else
            {
            OsTracePrint( KUmacDetails, (TUint8*)
                ("UMAC: WlanNullSendController::OnNoVoiceTimerTimeout: Continue in Voice Call state") );
            
            RegisterNoVoiceTimeout( iNoVoiceTimeout );
            }
        }
    else
        {
        // don't exit Voice Call state. Re-arm the timer instead with a 
        // suitable timeout relative to the latest Voice priority frame 
        // Rx or Tx in Voice Call state 
        RegisterNoVoiceTimeout( 
            iNoVoiceTimeout - KTimeAfterLatestVoiceRxOrTxInVoiceCallState );        
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::OnKeepAliveTimerTimeout()
    {
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::OnKeepAliveTimerTimeout") );
    
    iFlags &= ~KKeepAliveTimerArmed;

    TUint32 KTimeAfterLatestTx ( 
        static_cast<TUint32>(os_systemTime() - iLatestTx) );
    
    if ( KTimeAfterLatestTx >= iKeepAliveTimeout )
        {
        // request a regular Null Data frame to be sent
    
        // however, if there is a frame Tx (of any frame) already pending, 
        // we won't request a new frame to be sent. 
        if ( !iWlanContextImpl.UnsentTxPackets() )
            {
            if ( !iNullSender.TxNullDataFrame( iWlanContextImpl, EFalse ) )
                {
                // frame was not sent because we didn't get a Tx buffer.
                // In this case we'll skip the sending
                // However, we need to re-arm the timer to trigger the next
                // Null Data frame sending
                RegisterKeepAliveTimeout( iKeepAliveTimeout );
                }
            }
        else
            {
            // frame Tx already pending, so we don't ask a Null Data to be sent
            OsTracePrint( KUmacDetails, (TUint8*)
                ("UMAC: WlanNullSendController::OnKeepAliveTimerTimeout: frame Tx already pending. New Null Data Tx request skipped") );
            
            // However, we need to re-arm the timer to trigger the next
            // Null Data frame sending
            RegisterKeepAliveTimeout( iKeepAliveTimeout );
            }
        }
    else
        {
        // No need to send keep alive; yet. Re-arm the timer with 
        // a suitable timeout relative to the time of the latest frame Tx
        RegisterKeepAliveTimeout( iKeepAliveTimeout - KTimeAfterLatestTx );        
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::RegisterVoiceCallEntryTimeout()
    {
    CancelVoiceCallEntryTimeout();

    iWlanContextImpl.iUmac.RegisterTimeout( 
        iVoiceCallEntryTimeout, 
        EWlanVoiceCallEntryTimer );

    iFlags |= KVoiceCallEntryPending;

    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::RegisterVoiceCallEntryTimeout: timer armed") );
    }


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::RegisterNullTimeout( 
    TUint32 aTimeoutInMicroSeconds )
    {
    CancelNullTimeout();

    iWlanContextImpl.iUmac.RegisterTimeout( 
        aTimeoutInMicroSeconds, 
        EWlanNullTimer );

    iFlags |= KNullTimerArmed;

    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::RegisterNullTimeout: timer armed") );
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::RegisterNoVoiceTimeout( 
    TUint32 aTimeoutInMicroSeconds )
    {
    CancelNoVoiceTimeout();
    
    iWlanContextImpl.iUmac.RegisterTimeout( aTimeoutInMicroSeconds, 
                                            EWlanNoVoiceTimer );
    iFlags |= KNoVoiceTimerArmed;                                            

    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::RegisterNoVoiceTimeout: timer armed") );
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::RegisterKeepAliveTimeout( 
    TUint32 aTimeoutInMicroSeconds )
    {
    CancelKeepAliveTimeout();

    iWlanContextImpl.iUmac.RegisterTimeout( aTimeoutInMicroSeconds, 
                                            EWlanKeepAliveTimer );

    iFlags |= KKeepAliveTimerArmed;

    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::RegisterKeepAliveTimeout: timer armed") );
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::CancelVoiceCallEntryTimeout()
    {
    if ( iFlags & KVoiceCallEntryPending )
        {
        iWlanContextImpl.iUmac.CancelTimeout( EWlanVoiceCallEntryTimer );        
        
        iFlags &= ~KVoiceCallEntryPending;

        OsTracePrint( KUmacDetails, (TUint8*)
            ("UMAC: WlanNullSendController::CancelVoiceCallEntryTimeout: timer cancelled") );
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::CancelNullTimeout()
    {
    if ( iFlags & KNullTimerArmed )
        {
        iWlanContextImpl.iUmac.CancelTimeout( EWlanNullTimer );        
        
        iFlags &= ~KNullTimerArmed;

        OsTracePrint( KUmacDetails, (TUint8*)
            ("UMAC: WlanNullSendController::CancelNullTimeout: timer cancelled") );
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::CancelNoVoiceTimeout()
    {
    if ( iFlags & KNoVoiceTimerArmed )
        {
        iWlanContextImpl.iUmac.CancelTimeout( EWlanNoVoiceTimer );    
        
        iFlags &= ~KNoVoiceTimerArmed;

        OsTracePrint( KUmacDetails, (TUint8*)
            ("UMAC: WlanNullSendController::CancelNoVoiceTimeout: timer cancelled") );
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::CancelKeepAliveTimeout()
    {
    if ( iFlags & KKeepAliveTimerArmed )
        {
        iWlanContextImpl.iUmac.CancelTimeout( EWlanKeepAliveTimer );        
        
        iFlags &= ~KKeepAliveTimerArmed;

        OsTracePrint( KUmacDetails, (TUint8*)
            ("UMAC: WlanNullSendController::CancelKeepAliveTimeout: timer cancelled") );
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void WlanNullSendController::EnterVoiceCallState()
    {
    OsTracePrint( KUmacDetails, (TUint8*)
        ("UMAC: WlanNullSendController::EnterVoiceCallState") );

    CancelVoiceCallEntryTimeout();

    iFlags |= KInVoiceCallState;

    // Send voice call state change indication to engine
    iWlanContextImpl.iUmac.OnInDicationEvent( EVoiceCallOn );

    // arm the No Voice timer so that we also exit the Voice Call
    // state at some point
    iLatestVoiceRxOrTxInVoiceCallState = os_systemTime();
    RegisterNoVoiceTimeout( iNoVoiceTimeout );
    
    if ( ( iWlanContextImpl.UapsdUsedForVoice() ) &&
         ( iWlanContextImpl.CurrentDot11PwrMgmtMode() == WHA::KPsEnable ) )
        {
        // U-APSD is used for Voice and we are also in PS mode,
        // so initiate QoS Null Frame sending by arming the timer
        RegisterNullTimeout( iNullTimeout );                        
        }
    }
