// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "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:
//
/**
@file
@internalComponent
*/
#include "hctlbcspsequencer.h"
#include "hctlbcspFrameQueue.h"
#include "hctlbcsp.h"
#include "hctlbcspframe.h"
#include "hctlbcspconsts.h"
#include "hctlbcspsender.h"
#include "debug.h"
#include <bluetooth/hci/delay.h>
#define MAX_RX_TIMEOUT_ALLOWED 5
/**
Implementation of Class CHCTLBcspSequencer
*/
CHCTLBcspSequencer::CHCTLBcspSequencer(CHCTLBcsp& aHctlBcsp, CHCTLBcspFrameQueue& aBcspFrameQueue):
iRelRxState(EIdle),
iHctlBcsp(aHctlBcsp),
iFrameQueue(aBcspFrameQueue)
{
LOG_FUNC
}
CHCTLBcspSequencer* CHCTLBcspSequencer::NewL(CHCTLBcsp &aHCTLBcsp, RBusDevComm& aPort, CHCTLBcspFrameQueue& aBcspFrameQueue)
{
LOG_STATIC_FUNC
CHCTLBcspSequencer* self=new(ELeave) CHCTLBcspSequencer(aHCTLBcsp, aBcspFrameQueue);
CleanupStack::PushL(self);
self->ConstructL(aPort);
CleanupStack::Pop();
return self;
}
void CHCTLBcspSequencer::ConstructL(RBusDevComm& aPort)
{
LOG_FUNC
TCallBack cb = TCallBack(CHCTLBcspSequencer::RxTimeout, this);
iRxAckTimer = CDelay::NewL(cb, CActive::EPriorityStandard);
cb = TCallBack(CHCTLBcspSequencer::TxTimeout, this);
iTxAckTimer = CDelay::NewL(cb, CActive::EPriorityStandard);
iSender = CHCTLBcspSender::NewL(*this,aPort);
}
CHCTLBcspSequencer::~CHCTLBcspSequencer()
{
LOG_FUNC
delete iRxAckTimer;
delete iTxAckTimer;
delete iSender;
}
/**
Resets the BCSP sequencer to default state.
*/
void CHCTLBcspSequencer::Reset()
{
LOG_FUNC
iSender->Cancel();
//reset rx variables
iRxAck = 0;
iRxSeq = 0;
iRxSeqExpected = 0;
iRxSeqLast = 0;
}
/**
Called when a peer reset of BCSP is detected
Passes the notification up the BCSP stack for handling
*/
void CHCTLBcspSequencer::HandlePeerReset()
{
LOG_FUNC
iHctlBcsp.HandlePeerReset();
}
void CHCTLBcspSequencer::HandleRx(CRxHctlBcspFrame* aFrame)
{
LOG_FUNC
iRelRxState = EReceiving;
iRxSeqLast = iRxSeq;
iRxAck = aFrame->Ack();
if (aFrame->IsReliableProtcolType())
{
iRxSeq=aFrame->Sequence();
}
#ifdef __DEBUG_SEQ_Values__
LOG1(_L8("\tiRxAck value = %d"),iRxAck);
LOG1(_L8("\tiRxSeq value = %d"),iRxSeq);
LOG1(_L8("\tiRxSeqLast value = %d"),iRxSeqLast);
LOG1(_L8("\tiRxSeqExpected value = %d"),iRxSeqExpected);
LOG1(_L8("\tiTxAck value = %d"),iTxAck);
#endif
// Call HandleRxAck() to process the Ack Value conveyed in the received frame header
HandleRxAck();
// Check if the received frame is unreliable and on the BCSP Link Establihsment channel
// If it is then route directly to the iHctlBcsp.PacketRouter()
if(!aFrame->IsReliableProtcolType() && aFrame->ProtocolId() == KBcspLinkChnl) //Route LE packets
{
#ifdef __DEBUG_SEQ_VERBOSE__ //Enable flogging for Sequencer
LOG(_L8("HCTLBCSP: CHCTLBcspSequencer::HandleRx() Pass LE Packet to LE Entity\n\n"));
#endif
iHctlBcsp.PacketRouter();
return;
}
// Otherwise the packet is reliable and passes into this switch statement
switch (iRelRxState) //Verify ProtocolId is valid and permissable
{
case EIdle: // Fall through.
case EReceiving:
{
if(iRxSeq == iRxSeqExpected && (iHctlBcsp.CheckIsAckPacket()==EFalse))
{
iTxAck = static_cast <TUint8>((iRxSeq+1)%8);
iRxSeqExpected = iTxAck;
iHctlBcsp.PacketRouter(); // Forward RxFrame to PacketRouter
}
//else drop the packet
StartTxAckTimer();
}
break;
default:
break;
};
}
void CHCTLBcspSequencer::SendNextPacket()
/**
This method attempts to send the next frame on the queue.
*/
{
LOG_FUNC
if(SendQueuedFrame() == KErrBcspMaxRetries)
{
HandlePeerReset();
}
}
TUint8 CHCTLBcspSequencer::TxAck() const
{
LOG_FUNC
return iTxAck;
}
void CHCTLBcspSequencer::HandleRxAck()
{
LOG_FUNC
// An ACK has been received, so reset the Tx timeout counter
iRxTimeoutCount = 0;
// Returns ETrue if there are unacknowledged packets remaining
// in the window
if(iFrameQueue.AckReceived(iRxAck))
{
StartRxAckTimer();
}
else
{
iRxAckTimer->Cancel();
}
}
/*static*/ TInt CHCTLBcspSequencer::RxTimeout(TAny* aThis)
{
LOG_STATIC_FUNC
reinterpret_cast<CHCTLBcspSequencer*>(aThis)->HandleRxAckTimeout();
return KErrNone;
}
void CHCTLBcspSequencer::SendPacket(TDesC8* aFrame, TBool aIsReliable)
/**
This method writes @param aFrame and starts the RxAckTimer if @param aIsReliable
is ETrue
*/
{
LOG_FUNC
iSender->Write(*aFrame);
if (aIsReliable)
{
StartRxAckTimer();
}
}
TInt CHCTLBcspSequencer::TxTimeout(TAny* aThis)
{
LOG_STATIC_FUNC
reinterpret_cast<CHCTLBcspSequencer*>(aThis)->TxAckMsg();
return KErrNone;
}
void CHCTLBcspSequencer::TxAckMsg()
{
LOG_FUNC
iHctlBcsp.TxAckMsg();
}
void CHCTLBcspSequencer::StartRxAckTimer()
{
LOG_FUNC
iRxAckTimer->Cancel();
iRxAckTimer->After(KRxAckTimeout);
}
void CHCTLBcspSequencer::StartTxAckTimer()
{
LOG_FUNC
if ( !iTxAckTimer->IsActive() )
{
iTxAckTimer->After(KRxAckTimeout);
}
}
void CHCTLBcspSequencer::HandleRxAckTimeout()
{
LOG_FUNC
// Increment the Rx ACK Timeout counter
iRxTimeoutCount++;
iFrameQueue.AckTimeout();
// While the maximum rx timeout allowed is not reached, rearm the timer
// waiting for the ACK.
// If the max is reached, we consider that the peer will not respond and
// has been poweroff. So, do not rearm the timer and reset the counter.
// Notice that the BC4 ship implements the same mechanism.
if (iRxTimeoutCount < MAX_RX_TIMEOUT_ALLOWED)
{
StartRxAckTimer();
}
else
{
iRxTimeoutCount = 0;
}
}
void CHCTLBcspSequencer::WakeUp()
/**
Method made available to trigger the TxStateMachine to do something
depending upon its current state
*/
{
LOG_FUNC
if(!iSender->IsActive())
{
SendNextPacket();
}
}
TInt CHCTLBcspSequencer::SendQueuedFrame()
/**
Method to get the Next Frame from the frame queue and then write it via
the SendPacket method
*/
{
LOG_FUNC
TDesC8* frame = NULL;
TBool isReliable = EFalse;
TInt err = iFrameQueue.GetNextFrame(frame,isReliable);
if (!err)
{
SendPacket(frame,isReliable);
}
return err;
}