bthci/hci2implementations/hctls/bcsp/src/hctlbcspFrameQueue.cpp
changeset 0 29b1cd4cb562
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bthci/hci2implementations/hctls/bcsp/src/hctlbcspFrameQueue.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,727 @@
+// 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 "hctlbcspFrameQueue.h"
+
+#include "hctlbcsp.h"
+#include "hctlbcspconsts.h"
+#include "hctlbcspsequencer.h"
+#include "bcsputils.h"
+#include "debug.h"
+#include "hctlbcspframe.h"
+
+CHCTLBcspWindow::CHCTLBcspWindow(TInt aWindowSize, TInt aNumberOfSeqIds)
+  : iEndOfWindow(aWindowSize - 1),
+    iWindowSize(aWindowSize),
+    iNumberOfSeqIds(aNumberOfSeqIds),
+	iFrameQue(_FOFF(CTxHctlBcspFrame,iLink))
+	{
+	LOG_FUNC
+	}
+
+CHCTLBcspWindow* CHCTLBcspWindow::NewL(TInt aWindowSize, TInt aFrameSize, TInt aNumberOfSeqIds)
+	{
+	LOG_STATIC_FUNC
+
+	CHCTLBcspWindow *self = new (ELeave) CHCTLBcspWindow(aWindowSize, aNumberOfSeqIds);
+	CleanupStack::PushL(self);
+	self->ConstructL(aFrameSize);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+void CHCTLBcspWindow::ConstructL(TInt aFrameSize)
+	{
+	LOG_FUNC
+
+	for(TInt i=0;i<iWindowSize;i++)
+		{
+		CTxHctlBcspFrame *frame = CTxHctlBcspFrame::NewL(aFrameSize);
+		frame->Reset();
+		iFrameQue.AddLast(*frame);
+
+#ifdef _DEBUG_WINDOW
+		LOG1(_L8("frame added 0x%08x"),frame);
+#endif
+		}
+#ifdef _DEBUG_WINDOW
+	TraceWindowVars();
+	TraceQueue();
+#endif
+	}
+
+CHCTLBcspWindow::~CHCTLBcspWindow()
+	{
+	LOG_FUNC
+
+	for (TInt i=0;i<iWindowSize;i++)
+		{
+		CTxHctlBcspFrame* frame = iFrameQue.First();
+		iFrameQue.Remove(*frame);
+		delete frame;
+		}
+	}
+
+TInt CHCTLBcspWindow::StartOfWindow() const
+	{
+	LOG_FUNC
+	 
+	return iStartOfWindow; 
+	}
+	
+TInt CHCTLBcspWindow::ToSeqId(TInt aIndex) const
+	{ 
+	LOG_FUNC
+
+	return (aIndex + iStartOfWindow)%iNumberOfSeqIds; 
+	}
+	
+TInt CHCTLBcspWindow::ToWindowIndex(TInt aSeqId) const 
+	{
+	LOG_FUNC
+	
+	TInt index = aSeqId - iStartOfWindow;
+	if ( index < 0 )
+		{
+		index += iNumberOfSeqIds;
+		}
+	return index; 
+	}
+	
+void CHCTLBcspWindow::IncrementSeqId(TInt &aSeqId) const
+	{ 
+	aSeqId++; 
+	aSeqId %= iNumberOfSeqIds;
+	}
+
+
+CTxHctlBcspFrame* CHCTLBcspWindow::GetFrame(TInt aIndex)
+/**
+	Method to retrieve a frame for transmission
+*/
+	{
+	LOG_FUNC
+
+	__TEST_INVARIANT;
+
+	TInt index = ToWindowIndex(aIndex);
+	__ASSERT_DEBUG((index>=0) && (index<iWindowSize), PANIC(KBcspPanicCat, EBadBcspFrame));
+
+	TSglQueIter<CTxHctlBcspFrame> iter(iFrameQue);
+	CTxHctlBcspFrame* frame = iter++;
+
+	while ( index > 0 )
+		{
+		frame = iter++;
+		index--;
+		}
+
+#ifdef _DEBUG_WINDOW
+	LOG2(_L8("frame got 0x%08x (index:%d)"), frame, aIndex);
+#endif
+
+	return frame;
+	}
+
+TInt CHCTLBcspWindow::FreeFrames()
+/**
+	Method to free up space in transmit queue for sending more BCSP frames
+*/
+	{
+	LOG_FUNC
+
+	__TEST_INVARIANT;
+
+	return ToWindowIndex(iEndOfWindow) - ToWindowIndex(iWritePosition);
+	}
+
+CTxHctlBcspFrame *CHCTLBcspWindow::WriteFrame()
+/**
+	Method to Write a frame to the frame queue
+*/	
+	{
+	LOG_FUNC
+
+	CTxHctlBcspFrame *frame = NULL;
+
+	__TEST_INVARIANT;
+
+	if ( iWritePosition != iEndOfWindow )
+		{
+		frame = GetFrame(iWritePosition);
+		
+		if(frame)
+			{
+			IncrementSeqId(iWritePosition);
+			// Make sure we're not returning a valid frame to be overwritten
+			__ASSERT_DEBUG(!frame->IsValid(), PANIC(KBcspPanicCat, EBadBcspFrame));
+			}
+		else
+			{
+	#ifdef _DEBUG_WINDOW
+	FTRACE(FPrintBCSP(_L("Bcsp frame queue blocked (panic now deprecated)")));
+	#endif
+			}
+
+#ifdef _DEBUG_WINDOW
+		LOG(_L8("Post Write"));
+		TraceWindowVars();
+		TraceQueue();
+#endif
+		}
+
+	__TEST_INVARIANT;
+
+	return frame;
+	}
+
+CTxHctlBcspFrame *CHCTLBcspWindow::ReadFrame()
+/**
+	Method to read a frame from the Transmit Queue
+
+	@return CTxHctlBcspFrame * frame
+*/	
+	{
+	LOG_FUNC
+
+	CTxHctlBcspFrame *frame = NULL;
+
+	__TEST_INVARIANT;
+
+	if ( iReadPosition != iWritePosition )
+		{
+		frame = GetFrame(iReadPosition);
+		frame->SetSequence(static_cast<TUint8>(iReadPosition));
+
+		if (iReadPosition == iHighReadPosition)
+			{
+			IncrementSeqId(iHighReadPosition);
+			}
+		IncrementSeqId(iReadPosition);
+
+#ifdef _DEBUG_WINDOW
+		LOG(_L8("Post Read"));
+		TraceWindowVars();
+		TraceQueue();
+#endif
+		// Make sure we're returning a valid frame to be used
+		__ASSERT_DEBUG(frame->IsValid(), PANIC(KBcspPanicCat, EBadBcspFrame));
+		}
+
+	__TEST_INVARIANT;
+
+	return frame;
+}
+
+TBool CHCTLBcspWindow::Acknowledged(TInt aAckNo)
+/**
+	Method to handle TxFrame acknowledgement
+	Remove the frame that is acknowledged
+	@param current Rx Ack value, KErrTimedOut if ack timed out
+*/
+	{
+	LOG_FUNC
+
+	TBool framesRemainingInWindow = EFalse;
+
+#ifdef _DEBUG_WINDOW
+	LOG(_L8("Pre Ack"));
+	TraceWindowVars();
+#endif
+
+	__TEST_INVARIANT;
+
+	const TBool resetWindow = ((aAckNo >= 0) && (ToWindowIndex(aAckNo) > ToWindowIndex(iHighReadPosition))) ? ETrue : EFalse;
+
+	if (resetWindow)
+		{
+#ifdef _DEBUG_WINDOW
+		LOG1(_L8("Ack for unsent packet (%d), resetting entire window!!!"), aAckNo);
+#endif
+		// reset frames
+		TSglQueIter<CTxHctlBcspFrame> iter(iFrameQue);
+		CTxHctlBcspFrame *frame = iter++;
+
+		while (iter)
+			{
+			frame->Reset();
+			frame = iter++;
+			}
+
+		//reset window variables
+		iStartOfWindow = aAckNo;
+		iStartOfWindow %= iNumberOfSeqIds;
+
+		iEndOfWindow  = iStartOfWindow + iWindowSize-1;
+		iEndOfWindow %= iNumberOfSeqIds;
+
+		iWritePosition = iHighReadPosition = iReadPosition = iStartOfWindow;
+
+		framesRemainingInWindow = ETrue;
+		}
+	else if (iReadPosition != iStartOfWindow) // frames waiting to be acknowledged
+		{
+		LOG1(_L("Ack(%d)"), aAckNo);
+
+		if ( aAckNo >= 0 )
+			{
+			aAckNo %= iNumberOfSeqIds; // Make sure we wrap around at the end of the window
+			LOG1(_L8("Ack(%d)"), aAckNo);
+
+			// TInt  readIndex = ackWindowIndex;
+			TBool found = EFalse;
+			TInt i = iStartOfWindow;
+
+			while(!found && (i != iEndOfWindow))
+				{
+				if((i == aAckNo))
+					{
+					found = ETrue;
+					}
+				else if(!found)
+					{
+					// This frame is before the ackWindowIndex therefore we should reset it
+					CTxHctlBcspFrame* frame = iFrameQue.First();
+					frame->Reset();
+					iFrameQue.Remove(*frame);
+					iFrameQue.AddLast(*frame);
+					}
+				IncrementSeqId(i);
+				}
+
+			iStartOfWindow = aAckNo;
+			iStartOfWindow %= iNumberOfSeqIds;
+
+			iEndOfWindow = iStartOfWindow + iWindowSize - 1;
+			iEndOfWindow %= iNumberOfSeqIds;
+			}
+
+		// We've just had an acknowledgement so we need to resend from the start of the buffer
+		iReadPosition = iStartOfWindow;
+
+		// We've just rationalised the window. The write position can remain where it was.
+		framesRemainingInWindow = ETrue;
+		}
+
+#ifdef _DEBUG_WINDOW
+	LOG(_L("Post Ack"));
+	TraceWindowVars();
+	TraceQueue();
+#endif
+
+	__TEST_INVARIANT;
+
+	return framesRemainingInWindow;
+	}
+
+void CHCTLBcspWindow::__DbgTestInvariant(void) const
+/**
+	Debug method
+*/
+	{
+	// Check that the class is internally consistent
+	__ASSERT_DEBUG(iNumberOfSeqIds>0,                                              User::Invariant());
+	__ASSERT_DEBUG((iWindowSize>0) && (iWindowSize <= iNumberOfSeqIds),            User::Invariant());
+
+	__ASSERT_DEBUG((iStartOfWindow>=0) && (iStartOfWindow<=iNumberOfSeqIds),       User::Invariant());
+	__ASSERT_DEBUG((iEndOfWindow>=0)   && (iEndOfWindow<=iNumberOfSeqIds),         User::Invariant());
+	__ASSERT_DEBUG((iReadPosition>=0 ) && (iReadPosition<=iNumberOfSeqIds),        User::Invariant());
+	__ASSERT_DEBUG((iHighReadPosition>=0 ) && (iHighReadPosition<=iNumberOfSeqIds),User::Invariant());
+	__ASSERT_DEBUG((iWritePosition>=0) && (iWritePosition<=iNumberOfSeqIds),       User::Invariant());
+
+	TInt winSize = iEndOfWindow - iStartOfWindow + 1;
+	if ( winSize < 0 )
+		{
+		winSize += iNumberOfSeqIds;
+		}
+	__ASSERT_DEBUG(winSize == iWindowSize,   User::Invariant());
+	if ( iWritePosition != iEndOfWindow )
+		{
+		__ASSERT_DEBUG(ToWindowIndex(iReadPosition) <= ToWindowIndex(iHighReadPosition),  User::Invariant());
+		__ASSERT_DEBUG(ToWindowIndex(iHighReadPosition) <= ToWindowIndex(iWritePosition), User::Invariant());
+		}
+	}
+
+#ifdef _DEBUG_WINDOW
+void CHCTLBcspWindow::TraceWindowVars() const
+/**
+	Debug method
+*/
+	{
+	LOG_FUNC
+	RProcess process;
+	RThread thread;
+	TName pName = process.Name();
+	TName tName = thread.Name();
+	LOG1(_L8("Process : %S"), &pName);
+	LOG1(_L8("Thread : %S"), &tName);
+	LOG6(_L8("this : 0x%08x    SOW : %d, RP : %d, HRP: %d, WP : %d, EOW :%d"),this, iStartOfWindow, iReadPosition, iHighReadPosition, iWritePosition, iEndOfWindow);
+	}
+
+void CHCTLBcspWindow::TraceQueue()
+/**
+	Debug method
+*/
+	{
+	LOG_FUNC
+	LOG1(_L8("Queue Dumping this 0x%08x"), this);
+	TSglQueIter<CTxHctlBcspFrame> iter(iFrameQue);
+	CTxHctlBcspFrame *frame = iter++;
+
+	_LIT(KSpace, " ");
+	_LIT(KWriteMarker, "W");
+	_LIT(KHighMarker, "H");
+	_LIT(KReadMarker, "R");
+
+	TInt index = 0;
+	while ( index < iWindowSize )	
+		{
+	LOG6(_L8("%S %S %S this: 0x%08x, frame : 0x%08x, %d"),
+			(index == ToWindowIndex(iWritePosition)) ? &KWriteMarker : &KSpace,
+			(index == ToWindowIndex(iHighReadPosition)) ? &KHighMarker : &KSpace,
+			(index == ToWindowIndex(iReadPosition)) ? &KReadMarker : &KSpace,
+			this,
+			frame,
+			frame->IsValid()
+			);
+		frame = iter++;
+		index++;
+		}
+	}
+#endif
+
+void CHCTLBcspWindow::Reset()
+	{
+	LOG_FUNC
+
+	for(TInt i=0;i<iWindowSize;i++)
+		{
+		CTxHctlBcspFrame* frame = iFrameQue.First();
+		frame->Reset();
+		iFrameQue.Remove(*frame);
+		iFrameQue.AddLast(*frame);
+
+#ifdef _DEBUG_WINDOW
+		LOG1(_L8("frame reset 0x%08x"),frame);
+#endif
+		}
+
+	iStartOfWindow = 0;
+	iReadPosition  = 0;
+	iHighReadPosition = 0;
+	iWritePosition = 0;
+	iEndOfWindow = iWindowSize - 1;
+
+#ifdef _DEBUG_WINDOW
+	LOG(_L8("Reset"));
+
+	TraceWindowVars();
+	TraceQueue();
+#endif
+	}
+	
+/* *********************************************************************************************
+ * Frame Queue - Manages the frame queues for both Reliable and Unreliable data transfer
+ * ********************************************************************************************* */
+CHCTLBcspFrameQueue* CHCTLBcspFrameQueue::NewL(CHCTLBcsp &aBcsp)
+	{
+	LOG_STATIC_FUNC
+
+	CHCTLBcspFrameQueue *self = new (ELeave) CHCTLBcspFrameQueue(aBcsp);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+void CHCTLBcspFrameQueue::ConstructL()
+	{
+	LOG_FUNC
+
+	// Create the queues
+	iReliableQueue   = CHCTLBcspWindow::NewL(KBcspReliableWindowSize, 
+		                                     KMaxReliablePayloadSize,
+											 KMaxBcspWindowSize);
+
+	iUnreliableQueue = CHCTLBcspWindow::NewL(KBcspUnreliableQueueSize,
+		                                     KMaxUnreliablePayloadSize,
+											 KBcspUnreliableQueueSize+1);
+
+	// Room for Slip encoded packet is twice the size of a full BCSP Packet + 2 for the 0xC0 bytes
+	iSlipEncodedFrame = HBufC8::NewL(2 + ((KMaxReliablePayloadSize + KBcspHeaderBytes + KBcspCrcBytes) * 2));
+	}
+
+CHCTLBcspFrameQueue::CHCTLBcspFrameQueue(CHCTLBcsp& aBcsp)
+  : iBcsp(aBcsp)
+	{
+	LOG_FUNC
+	}
+
+CHCTLBcspFrameQueue::~CHCTLBcspFrameQueue()
+
+	{
+	LOG_FUNC
+
+	delete iUnreliableQueue;
+	delete iReliableQueue;
+	delete iSlipEncodedFrame;
+	}
+
+
+void CHCTLBcspFrameQueue::SetSequencer(CHCTLBcspSequencer& aSequencer)
+/**
+	Simple method to initialise iSequencer with @param &aSequencer
+*/
+	{
+	LOG_FUNC
+
+	iSequencer = &aSequencer;
+	}
+
+TInt CHCTLBcspFrameQueue::GetNextFrame(TDesC8* &aFrame, TBool& aIsReliable)
+/**
+	Method to get the next frame from the frame queue and then to build and slip encode it
+
+	@param &aFrame
+	@param aIsReliable a reference to a TBool which will be set to ETrue if the
+	       Frame is from the reliable queue.
+	@return err
+*/
+	{
+	LOG_FUNC
+
+	TInt err = KErrNone;
+
+	CTxHctlBcspFrame *frame = iUnreliableQueue->ReadFrame();
+
+	if ( frame )
+		{
+		// Got unreliable frame
+		iUnreliableQueue->Acknowledged(iUnreliableQueue->StartOfWindow()+1); // Remove the first frame from the window
+		frame->SetSequence(0);
+		aIsReliable = EFalse;
+		}
+	else
+		{
+		// Couldn't get an unreliable frame, so ... get a reliable frame
+		frame = iReliableQueue->ReadFrame();
+
+		if ( !frame )
+			{
+			// We can't get a reliable frame either
+			err = KErrBcspNothingToSend;
+			aIsReliable = EFalse;
+			}
+		else
+			{
+			// We've got a reliable frame - increment the retry count
+			frame->IncrementRetries();
+			frame->SetFlags(iSequencer->TxAck(), ETrue, ETrue);
+			if ( frame->Retries() >= KTxRetryLimit )
+				{
+				err = KErrBcspMaxRetries;
+				}
+
+			aIsReliable = ETrue;
+			}
+		}
+
+	if ( !err )
+		{
+		// No errors so far, so SlipEncode the frame into iSlipEncodedFrame
+		frame->BuildFrame();
+		frame->SlipEncodeFrame(iSlipEncodedFrame->Des());
+		aFrame = iSlipEncodedFrame;
+
+#ifdef _DEBUG_WINDOW
+		LOG(_L8("Got Frame"));
+		LOGHEXRAW(frame->Payload().Ptr(), frame->PayloadLength());
+
+		LOG(_L8("Slip Encoded Frame"));
+		LOGHEXRAW(aFrame->Ptr(), aFrame->Length());
+#endif
+		}
+	else
+		{
+		// Just in case our return code is ignored :->
+		aFrame = NULL;
+		}
+
+	return err;
+	}
+
+TBool CHCTLBcspFrameQueue::AckReceived(TInt aAckValue)
+/**
+	Handle AckReceived @param aAckValue
+
+	call Acknowledged method on relaible queue
+	call CanSend()
+
+	@return ETrue if there are unacknowledged packets remaining
+	              in the tx window.
+*/
+	{
+	LOG_FUNC
+
+	TBool ret=iReliableQueue->Acknowledged(aAckValue);
+	CanSend();
+	return ret;
+	}
+
+void CHCTLBcspFrameQueue::AckTimeout()
+/**
+	Handle Rx Ack timeout
+	
+	Inform reliable queue to resend frame
+	CanSend
+	Call iSequencer->WakeUp() to trigger the sequencer to pull frames for resending from the
+	reliable frame queue
+*/
+	{
+	LOG_FUNC
+
+	iReliableQueue->Acknowledged(KErrTimedOut);
+	CanSend();
+	iSequencer->WakeUp();
+	}
+
+TInt CHCTLBcspFrameQueue::AddReliableFrame(const TDesC8 &aData, TUint8 aProtocolId)
+/**
+	Method to add a frame to the Tx Reliable queue
+	@param aData - HCI payload
+	@param aProtocolId - ProtocolId eg. ACL, Command
+*/
+	{
+	TInt err = KErrNone;
+	LOG_FUNC
+
+#ifdef _DEBUG_WINDOW
+	LOG(_L8("Adding Frame to Reliable Queue"));
+	LOGHEXDESC(aData);
+#endif
+
+	CTxHctlBcspFrame *frame=iReliableQueue->WriteFrame();
+	
+	// A NULL frame is now returned if (on the rare occasion) the frame queue has become blocked.
+	// The original panic has been replaced by an upward error handling path. 
+	if (!frame)
+	 	{
+	 	err = KErrOverflow;	
+	 	}
+	else
+		{
+		frame->SetProtocolId(aProtocolId);
+		frame->SetPayload(aData);
+		iSequencer->WakeUp();
+		}
+	
+	CanSend();
+	return err;
+	
+	}
+
+void CHCTLBcspFrameQueue::AddUnreliableFrame(const TDesC8 &aData, TUint8 aProtocolId, 
+											 TUint8 aAck,
+											 TBool aCRCEnabled)
+/**
+	Method to add an Unreliable frame to the unreliable frame queue
+
+	@param aData - HCI payload
+	@param aProtocolId
+	@param aAck
+	@param aCRCState
+
+	Calls wakeup method on the sequencer to trigger frame sending
+*/
+	{
+	LOG_FUNC
+
+	CTxHctlBcspFrame *frame=iUnreliableQueue->WriteFrame();
+
+#ifdef _DEBUG_WINDOW
+	LOG(_L8("Adding Frame to Unreliable Queue"));
+#endif
+
+	LOGHEXDESC(aData);
+	if ( frame )
+		{
+		frame->SetProtocolId(aProtocolId);
+		frame->SetPayload(aData);
+		frame->SetFlags(aAck, aCRCEnabled, EFalse);
+		}
+	// else we've got no free slots - therefore since this is an unreliable Frame we can
+	// just throw it away
+
+	iSequencer->WakeUp();
+	}
+
+void CHCTLBcspFrameQueue::AddUnreliableFrame(TUint8 aProtocolId,
+											 TUint8 aAck,
+											 TBool aCRCEnabled)
+/**
+	Method to add an empty Unreliable frame to the unreliable frame queue
+	eg. an AckFrame
+
+	@param aProtocolId
+	@param aAck
+	@param aCRCState
+
+*/
+	{
+	LOG_FUNC
+
+	CTxHctlBcspFrame *frame=iUnreliableQueue->WriteFrame();
+
+#ifdef _DEBUG_WINDOW
+	LOG(_L8("Adding Empty Frame to Unreliable Queue"));
+#endif
+
+	if ( frame )
+		{
+		frame->SetProtocolId(aProtocolId);
+		frame->SetPayload();
+		frame->SetFlags(aAck, aCRCEnabled, EFalse);
+		}
+	// else we've got no free slots - therefore since this is an unreliable Frame we can
+	// just throw it away
+
+	iSequencer->WakeUp();
+
+	}
+	
+void CHCTLBcspFrameQueue::Reset()
+ 	{
+	LOG_FUNC
+
+ 	iReliableQueue->Reset();
+ 	iUnreliableQueue->Reset();
+ 	}
+ 	
+void CHCTLBcspFrameQueue::CanSend()
+/**
+	Kicks packet router
+	Only reliable queue is checked here for availability because we don't want to flow control 
+	the unreliable packets. Unreliable packets are simply dropped when the queue is full.
+*/
+	{
+	LOG_FUNC
+
+	iBcsp.CanSend(iReliableQueue->FreeFrames()>0);
+	}