--- /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;
+ }
+