bluetooth/btstack/rfcomm/rfcommflow.cpp
changeset 0 29b1cd4cb562
child 13 16aa830c86c8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/rfcomm/rfcommflow.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,668 @@
+// Copyright (c) 2001-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:
+//
+
+#include <bluetooth/logger.h>
+#include <e32base.h>
+#include "rfcommutil.h"
+#include "rfcommmuxer.h"
+#include "rfcommflow.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_RFCOMM);
+#endif
+
+/*************************************************************************/
+
+
+CRfcommFlowStrategyFactory::CRfcommFlowStrategyFactory()
+	{
+	LOG_FUNC
+//	iFlowStrategies.Reset();  // Null all the pointers -- not necessary as array is in CBase class.
+	}
+
+CRfcommFlowStrategyFactory::~CRfcommFlowStrategyFactory()
+	{
+	LOG_FUNC
+	// Destroy all the state objects
+	iFlowStrategies.DeleteAll();
+	}
+	
+CRfcommFlowStrategyFactory* CRfcommFlowStrategyFactory::NewL()
+	{
+	LOG_STATIC_FUNC
+	CRfcommFlowStrategyFactory* factory  = new (ELeave) CRfcommFlowStrategyFactory();
+	CleanupStack::PushL(factory);
+	// Create all the new states
+	factory->iFlowStrategies[EFlowInitial]  = new (ELeave) TRfcommFlowStrategyInitial(factory);
+	factory->iFlowStrategies[EFlowCreditBased] = new (ELeave) TRfcommFlowStrategyCreditBased(factory);
+	factory->iFlowStrategies[EFlowNonCreditBased] = new (ELeave) TRfcommFlowStrategyNonCreditBased(factory);
+	// etc...
+	CleanupStack::Pop();
+	return factory;
+	}
+
+TRfcommFlowStrategy& CRfcommFlowStrategyFactory::GetFlowStrategy(const TFlowStrategies aFlowStrategy) const
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(aFlowStrategy != EMaxFlowStrategy, Panic(ERfCommFlowStrategyOutOfBounds));
+	return *iFlowStrategies[aFlowStrategy];
+	}
+
+
+
+
+/*************************************************************************/
+
+
+
+TRfcommFlowStrategy::TRfcommFlowStrategy(CRfcommFlowStrategyFactory* aFactory)
+:iFactory(aFactory)
+	{
+	LOG_FUNC
+	//iFactory = aFactory;
+	}
+
+
+TRfcommFlowStrategy::~TRfcommFlowStrategy()
+	{
+	LOG_FUNC
+	}
+
+
+
+
+
+TRfcommFlowStrategyInitial::TRfcommFlowStrategyInitial(CRfcommFlowStrategyFactory* aFactory)
+:TRfcommFlowStrategy(aFactory) 
+	{
+	LOG_FUNC
+	}
+
+TRfcommFlowStrategyInitial::~TRfcommFlowStrategyInitial()
+	{
+	LOG_FUNC
+	}
+
+TInt TRfcommFlowStrategyInitial::DataBufferMultiple()
+	{
+	LOG_FUNC
+	return KRfcommSAPBufferMultiple;
+	};
+
+TUint16 TRfcommFlowStrategyInitial::UsableMTU(const CRfcommSAP& aSAP, TUint8 /*aCredit*/) const
+	/**
+	  Available frame space for data
+	**/
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(aSAP.NegotiatedMTU(), Panic(ERfcommBadCalculatedMTU));
+
+	return aSAP.NegotiatedMTU();
+	}
+
+TBool TRfcommFlowStrategyInitial::AllowWrite(CRfcommSAP& /*aSAP*/)
+	/**
+	  Ignore CBFC Credits
+	**/
+	{
+	LOG_FUNC
+	return ETrue;
+	}
+
+TBool TRfcommFlowStrategyInitial::AllowFCOnOff(TBool /*aCommand*/)
+	/**
+	  Always allow 'FC On/Off' commands (not be confused with FC bit in an MSC command!).
+	**/
+	{
+	LOG_FUNC
+	return ETrue;
+	}
+
+TInt TRfcommFlowStrategyInitial::PNConvergenceLayer(TBool aCommand)
+	/**
+	  We are negotiating so keep trying for CBFC.
+	**/
+	{
+	LOG_FUNC
+	return aCommand?KCBFCCommandFlag:KCBFCResponseFlag;
+	}
+
+TUint8 TRfcommFlowStrategyInitial::PNFinalOctet() // FIXME: Rename to InitialCredit()
+	/**
+	  We are negotiating so keep trying for CBFC.
+	**/
+	{
+	LOG_FUNC
+	return KInitialCredit;
+	}
+
+TUint8 TRfcommFlowStrategyInitial::WriteCredit(CRfcommSAP& /*aSAP*/)
+	/**
+	  Return number of CBFC credits to send in UIH frame.
+	**/
+	{
+	LOG_FUNC
+	return 0;
+	}
+
+
+void TRfcommFlowStrategyInitial::OutgoingMSCReviseSignals(TBool /*aCommand*/, TUint8& /*aSignals*/)
+	/**
+	  Only requires action for CBFC.
+	**/
+	{
+	LOG_FUNC
+	}
+
+void TRfcommFlowStrategyInitial::ReviseDonatedCredits(CRfcommSAP& /*aSAP*/, TUint8 /*aCredit*/)
+	/**
+	  Only requires action for CBFC.
+	**/
+	{
+	LOG_FUNC
+	}
+
+void TRfcommFlowStrategyInitial::ReviseTransmittedCredits(CRfcommSAP& /*aSAP*/)
+	{
+	LOG_FUNC
+	/**
+	  Only requires action for CBFC.
+	**/
+	}
+
+CRfcommDataFrame* TRfcommFlowStrategyInitial::NewDataFrame(CRfcommMuxer& aMuxer, 
+													 TUint8 aAddr, 
+												     TInt aLen, 
+#ifdef _DEBUG
+												     TUint8 aCredit,
+#else
+												     TUint8 /*aCredit*/,
+#endif
+
+												     CRfcommSAP* aSAP)
+	/**
+	   Returns a new data (UIH) frame for the specified DLCI
+
+	   The C/R bit for the UIH frame is always 1 for the initiator and
+	   0 for the responder, which is the same as for commands.
+	**/
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(!aCredit, Panic(ERfcommUnexpectedCBFCCredit));
+	CRfcommDataFrame* frm=NULL;
+	TRAPD(err, frm=CRfcommDataFrame::NewL(aLen, aMuxer, aSAP));
+	if(!err)
+		{
+		frm->SetAddress(aAddr);
+		frm->SetControl(KUIHCtrlField);
+		}
+	return frm;	//	NB should be NULL if CRfcommDataFrame was not created
+	}
+
+TBool TRfcommFlowStrategyInitial::CanProcessNewData(const TBool aAllowProcessNewData) const
+	/**
+	  If a SAP's buffer becomes too full, then that SAP will cause the muxer
+	  to block any further intake of data. 'aSAPNoFreeSpaceMask' points to the
+	  relevant muxer flag.
+	**/
+	{
+	LOG_FUNC
+	return aAllowProcessNewData;
+	}
+
+void TRfcommFlowStrategyInitial::DecodeLength(TBool /*aCBFC*/, 
+										 	  TDesC8& /*aPacket*/, 
+											  TInt& /*aCreditBuffer*/, 
+											  TInt& /*aHeaderLengthBuffer*/)
+	/**
+	  Do nothing - not CBFC.
+	**/
+	{
+	LOG_FUNC
+	}
+
+TBool TRfcommFlowStrategyInitial::ProcessDataFrameReviseCredits(CRfcommSAP& /*aSAP*/, 
+													   TBool /*aPoll*/,
+													   TUint8 /*aCredit*/)
+	/**
+	  Not CBFC - return false => do nothing.
+	**/
+	{
+	LOG_FUNC
+	return EFalse;
+	}
+
+TBool TRfcommFlowStrategyInitial::MSC(CRfcommSAP& aSAP, TUint8 aSignals)
+	/**
+	  Respond to flow control aspects of an MSC signal.
+	**/
+	{
+	LOG_FUNC
+	// Set CTS. We're clear to send if flow control is off so this 
+	// is the inverse of the Flow Control bit
+	aSAP.CTS(!(aSignals & KModemSignalFC));
+	if(aSAP.CTS())
+		{
+		// Unblock ESOCK to try again (which might block again!)
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+void TRfcommFlowStrategyInitial::UpdateRxFlowControlState(CRfcommSAP& aSAP)
+	/**
+		Do non-CBFC flow control.
+	**/
+	{
+	LOG_FUNC
+	if(aSAP.DataBuffer().Count() + aSAP.Mux()->GetMaxDataSize() <=
+	   aSAP.DataBuffer().Length())
+		{
+		// Let the mux know we can take more data
+		//FC
+		aSAP.Mux()->SetCanHandleData(aSAP, ETrue);
+		}
+
+	if(aSAP.DataBuffer().Count() <= aSAP.LowTideMark())
+		{
+		if(!aSAP.CTR())
+			{
+			// We should send a flow on since we've sent an off
+			//FC
+			TUint8 signals=aSAP.Signals();
+			signals &= (~KModemSignalFC);
+			TInt result=aSAP.Mux()->SendMSC(aSAP, signals);
+			if(result == KErrNone)
+				{
+				// Update state if we sent it, otherwise don't
+				aSAP.SetSignals(signals);
+				aSAP.CTR(ETrue);
+				}
+			//
+			}
+		}
+	}
+
+TBool TRfcommFlowStrategyInitial::NewDataReviseCredits(CRfcommSAP& /*aSAP*/, const TDesC8& /*aData*/)
+	/**
+		Do nothing - SAPWise (CBFC) only.
+	**/
+	{
+	LOG_FUNC
+	return ETrue;
+	}
+
+void TRfcommFlowStrategyInitial::NewData(CRfcommSAP& aSAP)
+	/**
+		Deal with flow control when new data is coming in and CBFC is not on.
+	**/
+	{
+	LOG_FUNC
+	if(aSAP.DataBuffer().Count() + aSAP.Mux()->GetMaxDataSize() >=
+	   aSAP.DataBuffer().Length())
+		/**
+			N.B. CircularBuffers: Length() is MaxLength() and Count() is Length()!
+		**/
+		{
+		//Tell the mux we canna take any more, capt'n
+		aSAP.Mux()->SetCanHandleData(aSAP, EFalse);
+		}
+		
+	if(aSAP.DataBuffer().Count() >= aSAP.HighTideMark())
+		{
+		// Now try to send a flow off, if we haven't already
+		if(aSAP.CTR())
+			{
+			//FC
+			TUint8 signals=aSAP.Signals();
+			signals |= KModemSignalFC;
+			TInt result=aSAP.Mux()->SendMSC(aSAP, signals);
+			if(result == KErrNone)
+				{
+				// we sent it, so update state
+				aSAP.SetSignals(signals);
+				aSAP.CTR(EFalse);
+				}
+			//
+			}
+		}
+	return;
+	}
+
+CRfcommFlowStrategyFactory::TFlowStrategies TRfcommFlowStrategyInitial::FlowType()
+	{
+	LOG_FUNC
+	return CRfcommFlowStrategyFactory::EFlowInitial;
+	}
+
+
+TRfcommFlowStrategyCreditBased::TRfcommFlowStrategyCreditBased(CRfcommFlowStrategyFactory* aFactory)
+:TRfcommFlowStrategyInitial(aFactory) 
+	{
+	LOG_FUNC
+	}
+
+TRfcommFlowStrategyCreditBased::~TRfcommFlowStrategyCreditBased()
+	{
+	LOG_FUNC
+	}
+
+
+TInt TRfcommFlowStrategyCreditBased::DataBufferMultiple()
+	{
+	LOG_FUNC
+	return KRfcommSAPCBFCBufferMultiple;
+	};
+
+TUint16 TRfcommFlowStrategyCreditBased::UsableMTU(const CRfcommSAP& aSAP, TUint8 aCredit) const
+	/**
+	  Available frame space for data
+	**/
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(aSAP.NegotiatedMTU(), Panic(ERfcommBadCalculatedMTU));
+
+	//Allow for possible credit in header.
+	return (STATIC_CAST(TUint16, (aSAP.NegotiatedMTU() - (aCredit?1:0))));
+	}
+
+TBool TRfcommFlowStrategyCreditBased::AllowWrite(CRfcommSAP& aSAP)
+	/**
+	**/
+	{
+	LOG_FUNC
+	return aSAP.LocalCredit();
+	}
+
+TBool TRfcommFlowStrategyCreditBased::AllowFCOnOff(TBool aCommand)
+	/**
+	  Allow responses only. Do not transmit an 'FC On/Off' command.
+	  (not be confused with FC bit in an MSC command!)
+	**/
+	{
+	LOG_FUNC
+	return !aCommand; //N.B. Reverse logic - if command then return 'EFalse'
+	}
+
+TUint8 TRfcommFlowStrategyCreditBased::WriteCredit(CRfcommSAP& aSAP)
+	/**
+	  Return number of CBFC credits to send in UIH frame
+	**/
+	{
+	LOG_FUNC
+	return aSAP.FreeCredit();
+	}
+
+void TRfcommFlowStrategyCreditBased::OutgoingMSCReviseSignals(TBool aCommand, TUint8& aSignals)
+	/**
+	  There is a bit that when zero => CTS should be set to true. 
+	  For CBFC this bit must always be zero if we are sending a command. 
+	  Thus we are masking that part of an MSC command which tells the remote 
+	  to switch its CTS off (and so stop sending). 
+	  Required by SPEC.
+	**/
+	{
+	LOG_FUNC
+	if(aCommand)
+		aSignals &= (~KModemSignalFC);
+	}
+
+void TRfcommFlowStrategyCreditBased::ReviseDonatedCredits(CRfcommSAP& aSAP, TUint8 aCredit)
+	/**
+	  Revise proxy credit (our knowledge of remote credit) upwards by 'aCredit'.
+	**/
+	{
+	LOG_FUNC
+	//credit change
+	//Increase proxy credit by 'aCredit'.
+	aSAP.ProxyForRemoteCreditAddCredit(aCredit);
+	}
+
+void TRfcommFlowStrategyCreditBased::ReviseTransmittedCredits(CRfcommSAP& aSAP)
+	/**
+	  Revise our own credits downward by one.
+	  Revise proxy credit (our knowledge of remote credit) upwards by 'aCredit'.
+	**/
+	{
+	LOG_FUNC
+	//Decrement allowed transmission credits.
+	aSAP.LocalCreditDecrement();
+	}
+
+CRfcommDataFrame* TRfcommFlowStrategyCreditBased::NewDataFrame(CRfcommMuxer& aMuxer, 
+												   TUint8 aAddr, 
+												   TInt aLen, 
+												   TUint8 aCredit,
+												   CRfcommSAP* aSAP)
+	/**
+	   Returns a new data (UIH) frame for the specified DLCI
+
+	   The C/R bit for the UIH frame is always 1 for the initiator and
+	   0 for the responder, which is the same as for commands.
+	**/
+	{
+	LOG_FUNC
+	CRfcommDataFrame* frm=NULL;
+	TInt err = 0;
+	if(aCredit)
+		{
+		TRAP(err, frm=CRfcommCreditDataFrame::NewL(aLen, aMuxer, aSAP));
+		if(!err)
+			{
+			frm->SetAddress(aAddr);
+			frm->SetControl(KUIHCBFCCtrlField); //FIXME - redone later
+			((CRfcommCreditDataFrame*)frm)->SetCredit(aCredit);
+			}
+		}
+	else
+		{
+		TRAP(err, frm=CRfcommDataFrame::NewL(aLen, aMuxer, aSAP));
+		if(!err)
+			{
+			frm->SetAddress(aAddr);
+			frm->SetControl(KUIHCtrlField); //FIXME - redone later ... Poll bit always 0 for UIH
+			}
+		}
+	return frm;	//	NB should be NULL if CRfcommDataFrame was not created
+	}
+
+TBool TRfcommFlowStrategyCreditBased::CanProcessNewData(const TBool /*aAllowProcessNewData*/) const
+	/**
+	  CBFC is on (=>SAP wise flow control). This function is for MUX wide 
+	  flow control, and so should ignore all flags and never block here.
+	**/
+	{
+	LOG_FUNC
+	return ETrue;
+	}
+
+void TRfcommFlowStrategyCreditBased::DecodeLength(TBool aCBFC, 
+											      TDesC8& aPacket, 
+											      TInt& aCreditBuffer, 
+											      TInt& aHeaderLengthBuffer)
+	/**
+		Provides the credit buffer with the sent credit, and revises the
+		header length buffer to take credit byte into account.
+	**/
+	{
+	LOG_FUNC
+	TInt cIndex = ((aPacket)[2] & 1)?3:4;
+	if (aCBFC)
+		{
+		aCreditBuffer = aPacket[cIndex];
+		aHeaderLengthBuffer++;	// data is 1 byte smaller
+		LOG1(_L("RFCOMM: Incoming packet: value in credit field is %d"), aCreditBuffer);
+		}
+	else
+		{
+		LOG(_L("RFCOMM: No credit field in incoming packet"));
+		}
+	}
+
+TBool TRfcommFlowStrategyCreditBased::ProcessDataFrameReviseCredits(CRfcommSAP& aSAP, 
+													   TBool aPoll,
+													   TUint8 aCredit)
+	/**
+	   Add 'aCredit' (discovered in incoming data frame)to our credit.
+	   If as a result we have credits when non had previously existed, 
+	   return true to unblock SAP.
+	**/
+	//credit change
+	{
+	LOG_FUNC
+	LOG(_L("CRfcommFlowSAPWise::ProcessDataFrameReviseCredits"));
+	if (aPoll)
+		{
+		TBool isUnblock = (!(aSAP.LocalCredit()) && aCredit)?ETrue:EFalse;
+		aSAP.LocalCreditAddCredit(aCredit);
+		if (isUnblock)
+			{
+			LOG(_L("RFCOMM: Transmit credit was zero."));
+			if (aSAP.SendBlocked() & CRfcommSAP::EBlockedByMux)
+				{
+				return ETrue;
+				}
+			}
+		}
+	else
+		{
+		LOG(_L("RFCOMM: Data with no Tx Credit"));
+		}
+	return EFalse;
+	}
+
+TBool TRfcommFlowStrategyCreditBased::MSC(CRfcommSAP& aSAP, TUint8 /*aSignals*/)
+	/**
+	  "Clear To Send" must be set to true. Flow control is handled
+	  only by credits. See "AllowWrite()".
+	**/
+	{
+	LOG_FUNC
+	aSAP.CTS(ETrue);
+	return ETrue;
+	}
+
+void TRfcommFlowStrategyCreditBased::UpdateRxFlowControlState(CRfcommSAP& aSAP)
+	/**
+	  Donate credits if possible when data is read off SAPs ring buffer.
+	  Note: Function was called Read() originally, but actually is independent
+	  of anything to do with reading.
+	**/
+	{
+	LOG_FUNC
+	if(aSAP.ProxyForRemoteCredit() <= KRfcommCreditsLowWaterMark)
+		{
+		TUint8 spareCredits = aSAP.FreeCredit();
+		if (spareCredits && spareCredits>=aSAP.ProxyForRemoteCredit())
+			{// We've got a worth while no. of credits to forward. Do a donation.
+			aSAP.Mux()->Donate(aSAP, spareCredits);	// send an empty frame with credits
+			}
+		}
+	LOG2(_L("RFCOMM: After: RxCr %d, ReUsed (or Free) %d"), aSAP.ProxyForRemoteCredit(), aSAP.FreeCreditCalculation());
+	}
+
+TBool TRfcommFlowStrategyCreditBased::NewDataReviseCredits(CRfcommSAP& aSAP, const TDesC8& aData)
+	/**
+		Revise proxy credit downwards by one if a frame has arrived containing 
+		new data.
+	**/
+	//credit change
+	{
+	LOG_FUNC
+	if (aData.Length() > 0)
+		{
+		if (aSAP.ProxyForRemoteCredit() == 0)
+			{
+			// got to disconnect cos they're ignoring our credits
+			LOG(_L("RFCOMM: Data received with 0 receive credit"));
+			return EFalse;
+			}
+		else
+			{
+			// update remote transmit credit proxy
+			aSAP.ProxyForRemoteCreditDecrement();
+			}
+		}
+	return ETrue;
+	}
+
+void TRfcommFlowStrategyCreditBased::NewData(CRfcommSAP& aSAP)
+	/**
+		If other side have run out of credits, check that we really
+		don't have any to send them. This should rarely happen, as
+		NotifyNewData tries to forward credits before they run out.
+	**/
+	{
+	LOG_FUNC
+	if (aSAP.ProxyForRemoteCredit() == 0)
+		{// they have run out! See if we have any credits
+		TUint8 spareCredits = aSAP.FreeCredit();
+		if(spareCredits)
+			{// got some credits! Send them.
+			aSAP.Mux()->Donate(aSAP, spareCredits);	// an empty frame with credits
+			}
+		}
+	return;
+	}
+
+CRfcommFlowStrategyFactory::TFlowStrategies TRfcommFlowStrategyCreditBased::FlowType()
+	{
+	LOG_FUNC
+	return CRfcommFlowStrategyFactory::EFlowCreditBased;
+	}
+
+
+
+
+
+
+TRfcommFlowStrategyNonCreditBased::TRfcommFlowStrategyNonCreditBased(CRfcommFlowStrategyFactory* aFactory)
+:TRfcommFlowStrategyInitial(aFactory) 
+	{
+	LOG_FUNC
+	}
+
+TRfcommFlowStrategyNonCreditBased::~TRfcommFlowStrategyNonCreditBased()
+	{
+	LOG_FUNC
+	}
+
+
+
+TInt TRfcommFlowStrategyNonCreditBased::PNConvergenceLayer(TBool /*aCommand*/)
+	/**
+	  Always allow 'FC On/Off'.
+	**/
+	{
+	LOG_FUNC
+	return 0x00;
+	}
+
+TUint8 TRfcommFlowStrategyNonCreditBased::PNFinalOctet()
+	/**
+	  Return 0.
+	**/
+	{
+	LOG_FUNC
+	return 0x00;
+	}
+
+
+CRfcommFlowStrategyFactory::TFlowStrategies TRfcommFlowStrategyNonCreditBased::FlowType()
+	{
+	LOG_FUNC
+	return CRfcommFlowStrategyFactory::EFlowNonCreditBased;
+	}
+