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

