--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/rfcomm/rfcommstates.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,2465 @@
+// Copyright (c) 1997-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 <bt_sock.h>
+#include "rfcommstates.h"
+#include "rfcomm.h"
+#include "rfcommmuxer.h"
+#include "rfcommflow.h"
+#include "rfcommconsts.h"
+#include "AsyncErrorKicker.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_RFCOMM);
+#endif
+
+// Together, these classes and the SAP implement the State pattern
+// (GOF). The states themselves are implemented using the Flyweight
+// pattern. Each state is a Flyweight object, and CRfcommStateFactory
+// is manager of these objects. As a result of being a flyweight, no
+// state object may have state that can't be shared between all
+// possible users of the state (ie no per-SAP state)
+
+
+CRfcommStateFactory* CRfcommStateFactory::NewL()
+ /**
+ Create all the state singletons.
+ **/
+ {
+ CRfcommStateFactory* factory= new (ELeave) CRfcommStateFactory();
+ CleanupStack::PushL(factory);
+ // Create all the new states
+ factory->ConstructL();
+ CleanupStack::Pop();
+ return factory;
+ }
+
+void CRfcommStateFactory::ConstructL()
+ {
+ iStates[EError] = new (ELeave) TRfcommStateError(*this);
+ iStates[EClosed] = new (ELeave) TRfcommStateClosed(*this);
+ iStates[EWaitForMux]= new (ELeave) TRfcommStateWaitForMux(*this);
+ iStates[EWaitForPNResp]= new (ELeave) TRfcommStateWaitForPNResp(*this);
+ iStates[EWaitForUA]= new (ELeave) TRfcommStateWaitForUA(*this);
+ iStates[EOpen] = new (ELeave) TRfcommStateOpen(*this);
+ iStates[EDisconnect] = new (ELeave) TRfcommStateDisconnect(*this);
+ iStates[EListening] = new (ELeave) TRfcommStateListening(*this);
+ iStates[EWaitForSABM] = new (ELeave) TRfcommStateWaitForSABM(*this);
+ iStates[EWaitForIncomingSecurityCheck] = new (ELeave) TRfcommStateIncomingSecurityCheck(*this);
+ iStates[EWaitForOutgoingSecurityCheck] = new (ELeave) TRfcommStateOutgoingSecurityCheck(*this);
+ iStates[EWaitForStart] = new (ELeave) TRfcommStateWaitForStart(*this);
+ iStates[ECloseOnStart] = new (ELeave) TRfcommStateCloseOnStart(*this);
+ iStates[EDisconnecting] = new (ELeave) TRfcommStateDisconnecting(*this);
+ // etc...
+ }
+
+TInt CRfcommStateFactory::StateIndex(const TRfcommState* aState) const
+ {
+ TInt state;
+ for (state = 0; state < ERfcommMaxState; state++)
+ {
+ if (iStates[state] == aState)
+ {
+ return state;
+ }
+ }
+
+ return KUnknownState;
+ }
+
+/*
+
+TRfcommState base class implementation
+
+*/
+
+TInt TRfcommState::SignalError(CRfcommSAP& aSAP, TInt aErr, MSocketNotify::TOperationBitmasks aType)
+ /**
+ Signal an error, with appropriate types to tell socket what's
+ been affected.
+ **/
+ {
+ if (aSAP.iSocket)
+ {
+ aSAP.iSocket->Error(aErr, aType);
+ }
+
+ if(aType & MSocketNotify::EErrorFatal)
+ {
+ aSAP.iProtocol.ControlPlane().ModifyPhysicalLink(EUndoOverridePark, aSAP.iRemoteDev);
+ ChangeState(aSAP, CRfcommStateFactory::EError);
+ }
+ return aErr;
+ }
+
+// State change utility function
+
+void TRfcommState::ChangeState(CRfcommSAP& aSAP, CRfcommStateFactory::TRfcommStates aNewState) const
+ {
+ aSAP.iState->Exit(aSAP);
+#ifdef __FLOG_ACTIVE
+ TRfcommState* state= iFactory.GetState(aNewState);
+ LOG3(_L("RFCOMM : Sap %08x : State %S -> %S"),
+ &aSAP, &aSAP.iState->iName, &state->iName);
+#endif //__FLOG_ACTIVE
+ aSAP.iState=iFactory.GetState(aNewState);
+ aSAP.iState->Enter(aSAP);
+ }
+
+void TRfcommState::PanicInState(TRFCOMMPanic aPanic) const
+ {
+ Panic(aPanic, iFactory.StateIndex(this));
+ }
+
+#ifdef _DEBUG
+void TRfcommState::DebugPanicInState(TRFCOMMPanic aPanic) const
+#else
+void TRfcommState::DebugPanicInState(TRFCOMMPanic /*aPanic*/) const
+#endif
+ {
+ #ifdef _DEBUG
+ PanicInState(aPanic);
+ #endif
+ }
+
+TRfcommState::TRfcommState(CRfcommStateFactory& aFactory)
+ : iFactory(aFactory)
+ {
+ }
+
+/************************************************************************/
+/*
+ The default state operations.
+*/
+
+TRfcommStateDefault::TRfcommStateDefault(CRfcommStateFactory& aFactory)
+ : TRfcommState(aFactory)
+ {
+ }
+
+void TRfcommStateDefault::Enter(CRfcommSAP& /*aSAP*/)
+ {
+ // Do nothing
+ }
+
+void TRfcommStateDefault::Exit(CRfcommSAP& /*aSAP*/)
+ {
+ // Do nothing
+ }
+
+void TRfcommStateDefault::ActiveOpen(CRfcommSAP& /* aSAP */ )
+ {
+ PanicInState(ERfcommUnexpectedEvent);
+ }
+
+TInt TRfcommStateDefault::PassiveOpen(CRfcommSAP& /* aSAP */,
+ TUint /*aQueSize*/)
+ {
+ PanicInState(ERfcommUnexpectedEvent);
+ return KErrGeneral;
+ }
+
+
+void TRfcommStateDefault::Shutdown(CRfcommSAP& aSAP)
+ {
+ SignalError(aSAP, KErrRfcommSAPUnexpectedEvent, MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateDefault::FastShutdown(CRfcommSAP& aSAP)
+ {
+ SignalError(aSAP, KErrRfcommSAPUnexpectedEvent, MSocketNotify::EErrorAllOperations);
+ }
+
+
+TInt TRfcommStateDefault::Send(CRfcommSAP& /*aSAP*/, const TDesC8& /*aData*/ )
+ {
+ PanicInState(ERfcommUnexpectedEvent);
+ return 0;
+ }
+
+void TRfcommStateDefault::Read(CRfcommSAP& /*aSAP*/, TDesC8& /*aData*/)
+ {
+ PanicInState(ERfcommUnexpectedEvent);
+ }
+
+void TRfcommStateDefault::Ioctl(CRfcommSAP& aSAP, TUint /*aLevel*/,
+ TUint /*aName*/, TDes8* /*aOption*/)
+ {
+ SignalError(aSAP, KErrNotSupported, MSocketNotify::EErrorIoctl);
+ }
+
+void TRfcommStateDefault::CancelIoctl(CRfcommSAP& aSAP, TUint /*aLevel*/, TUint /*aName*/)
+ /**
+ A request to cancel an Ioctl.
+
+ Since the user has already been completed, and ESOCK prevents
+ the completion occuring twice, there's very little we need to
+ do (in fact we could ignore this entirely).
+
+ Instead we will simply reset out IoctlLevel and IoctlName,
+ since this will now mean that we will not complete the ioctl
+ when the complete signal comes in. If the ioctl is going on
+ below us, we will ignore the ioctl complete from them as well.
+ **/
+ {
+ aSAP.iIoctlLevel=0;
+ aSAP.iIoctlName=0;
+ }
+
+void TRfcommStateDefault::Start(CRfcommSAP& /*aSAP*/)
+ {
+ PanicInState(ERfcommUnexpectedEvent);
+ }
+
+TInt TRfcommStateDefault::SetOption(CRfcommSAP &aSAP, TUint aLevel,
+ TUint aName, const TDesC8& aOption)
+ /**
+ Set Option from user - works the same for SAPs in all states.
+ **/
+ {
+ if(aLevel == KSolBtRFCOMM)
+ {
+ switch(aName)
+ {
+ case KRFCOMMLocalPortParameter:
+ // Just do a straight copy into the local port params
+ aSAP.iLocalPortParams = *(TRfcommRemotePortParams*)aOption.Ptr();
+ break;
+ case KRFCOMMMaximumSupportedMTU:
+ {
+ if ( aOption.Length() != sizeof(TUint16) )
+ {
+ return KErrArgument;
+ }
+ else
+ {
+ TPckgBuf<TUint16> maxMTU;
+ maxMTU.Copy(aOption);
+ aSAP.iUserDefinedMTU = maxMTU();
+ }
+ };
+ break;
+
+ case KRFCOMMFlowTypeCBFC:
+ {
+ if ( aOption.Length() != sizeof(TBool) )
+ {
+ return KErrArgument;
+ }
+ else
+ {
+ TPckgBuf<TBool> aCBFC;
+ aCBFC.Copy(aOption);
+ if(!aCBFC())
+ {
+ //Switch off CBFC facility by setting flow strategy to
+ //non-CBFC and setting the flow negotiated flag to 'ETrue'.
+ if(aSAP.iMux)
+ {
+ if(!aSAP.iMux->SetFlowType(CRfcommFlowStrategyFactory::EFlowNonCreditBased))
+ {
+ return KErrNotSupported;
+ }
+ }
+ else
+ {
+ aSAP.DisallowCBFC(); //will cause muxer to use muxwise CBFC when created
+ }
+ }
+ else
+ {
+ //Switch on CBFC facility by setting flow strategy to
+ //CBFC and setting the flow negotiated flag to 'ETrue'.
+ if(aSAP.iMux)
+ {
+ if(!aSAP.iMux->SetFlowType(CRfcommFlowStrategyFactory::EFlowCreditBased))
+ {
+ return KErrNotSupported;
+ }
+ }
+ else
+ {
+ aSAP.AllowCBFC(); //will cause muxer to use muxwise CBFC when created
+ }
+ }
+ }
+ }
+ break;
+ case KRFCOMMErrOnMSC:
+ {
+ if ( aOption.Length() != sizeof(TUint8) )
+ {
+ return KErrArgument;
+ }
+ else
+ {
+ TPckgBuf<TUint8> aSignal;
+ aSignal.Copy(aOption);
+ aSAP.iDisconnectMSCSignal = aSignal();
+ }
+ }
+ break;
+ case KBTSecurityDeviceOverride:
+ {
+ // let the security object handle this
+ return aSAP.SetDeviceOverride(aOption); // just pass the option
+ }
+ case KBTRegisterCodService:
+ {
+ TInt err = KErrNone;
+ TUint16 newServiceBits = *reinterpret_cast<const TUint16*>(aOption.Ptr());
+ err = aSAP.SetCodServiceBits(newServiceBits); // The service bits are saved and then registered when SAP becomes live
+ return err;
+ }
+
+ case KRFCOMMForgiveCBFCOverflow:
+ {
+ if ( aOption.Length() != sizeof(TBool) )
+ {
+ return KErrArgument;
+ }
+ else
+ {
+ aSAP.iForgiveCBFCOverflow = *reinterpret_cast<const TBool*>(aOption.Ptr());
+ }
+ };
+ break;
+
+ default:
+ return KErrNotSupported;
+ }
+ return KErrNone;
+ }
+
+ // Not a RFCOMM setopt so error. We don't support passdown (yet).
+ return KErrNotSupported;
+ }
+
+void TRfcommStateDefault::MuxUp(CRfcommSAP& aSAP)
+ {
+ SignalError(aSAP, KErrRfcommSAPUnexpectedEvent,
+ MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateDefault::LinkDown(CRfcommSAP& aSAP)
+ {
+ SignalError(aSAP, KErrRfcommSAPUnexpectedEvent,
+ MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateDefault::SABM(CRfcommSAP& /*aSAP*/, CRfcommMuxer& aMux, TUint8 aDLCI)
+ /**
+ Send a DM, since we are not expecting this
+
+ **/
+ {
+ aMux.SendDM(aDLCI);
+ }
+
+void TRfcommStateDefault::DISC(CRfcommSAP& aSAP)
+ /**
+ Send a DM as we were not expecting this
+ **/
+ {
+ aSAP.iMux->SendDM(aSAP.iDLCI);
+ }
+
+void TRfcommStateDefault::UA(CRfcommSAP& /*aSAP*/)
+ /**
+ Do nothing - unexpected response
+ **/
+ {
+ }
+
+void TRfcommStateDefault::DM(CRfcommSAP& /*aSAP*/)
+ /**
+ Do nothing - unexpected response
+ **/
+ {
+ }
+
+void TRfcommStateDefault::RPN(CRfcommSAP& aSAP, const TRfcommRPNTransaction& /*aRPNTransaction*/, CRfcommMuxer& /*aMuxer*/, TUint8 /*aDLCI*/)
+ /**
+ Send a DM as we didn't expect this
+ **/
+ {
+ aSAP.iMux->SendDM(aSAP.iDLCI);
+ }
+
+void TRfcommStateDefault::RPNRsp(CRfcommSAP& /*aSAP*/,
+ const TRfcommRPNTransaction&
+ /*aRPNTransaction*/)
+ /**
+ Do nothing - unwanted response
+ **/
+ {
+ }
+
+
+void TRfcommStateDefault::PN(CRfcommSAP& aSAP, TRfcommPortParams& /*aParams*/, CRfcommMuxer& /*aMux*/, TUint8 aDLCI)
+ /**
+ Send a DM as we aren't handling this.
+ **/
+ {
+ __ASSERT_DEBUG(aDLCI==aSAP.iDLCI, PanicInState(ERfcommPNMismatchedDLCI));
+ aSAP.iMux->SendDM(aDLCI);
+ }
+
+void TRfcommStateDefault::PNResp(CRfcommSAP& /*aSAP*/, TRfcommPortParams& /*aParams*/)
+ /**
+ Do nothing - unwanted response
+ **/
+ {
+ }
+
+void TRfcommStateDefault::MSC(CRfcommSAP& /*aSAP*/, TUint8 /*aSignals*/)
+ /**
+ Do nothing - the response is autogenerated
+ **/
+ {
+ }
+
+void TRfcommStateDefault::RLS(CRfcommSAP& /*aSAP*/, TUint8 /*aStatus*/)
+ /**
+ Do nothing - the response is autogenerated
+ **/
+ {
+ }
+
+void TRfcommStateDefault::NewData(CRfcommSAP& /*aSAP*/, const TDesC8& /*aData*/)
+ /**
+ Do nothing - we're throwing this data away
+ **/
+ {
+ }
+
+void TRfcommStateDefault::NotifyNewDataCallback(CRfcommSAP& aSAP)
+ {
+ SignalError(aSAP, KErrRfcommSAPUnexpectedEvent, MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateDefault::CanSend(CRfcommSAP& aSAP)
+ {
+ SignalError(aSAP, KErrRfcommSAPUnexpectedEvent, MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateDefault::IoctlComplete(CRfcommSAP& /*aSAP*/, TInt /*aErr*/, TUint /*aLevel*/, TUint /*aName*/, TDesC8* /*aBuf*/)
+ {
+ // Do nothing, this isn't for us
+ }
+
+void TRfcommStateDefault::AccessRequestComplete(CRfcommSAP& /*aSAP*/, TInt /*aResult*/)
+ {
+ PanicInState(ERfcommUnexpectedSecurityRequesterCallback);
+ }
+
+void TRfcommStateDefault::Error(CRfcommSAP& aSAP, TInt aErr,
+ CRfcommSAP::TErrorTypes /*aType*/)
+ {
+ SignalError(aSAP, aErr,
+ MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateDefault::ParentClosed(CRfcommSAP& aSAP)
+ /**
+ Our parent SAP has been deleted - NULL our pointer to it.
+
+ This function only really applies to cloned child SAPs, but we
+ handle it here anyway.
+ **/
+ {
+ aSAP.iParentSAP=NULL;
+ }
+
+TBool TRfcommStateDefault::HandleFrameResponseTimeout(CRfcommSAP& /*aSAP*/)
+ {
+ // By default, we refuse to handle the frame timeout, indicating this by
+ // returning EFalse.
+ return EFalse;
+ }
+
+//TRY_CBFC
+TUint8 TRfcommStateDefault::FreeCredit(CRfcommSAP& /*aSAP*/)
+ {
+ // return the max initial credit
+ return (TUint8)KInitialCredit;
+ }
+
+TInt TRfcommStateDefault::ProxyForRemoteCredit(const CRfcommSAP& /*aSAP*/) const
+ {
+ // return the max initial credit
+ return (TInt)KInitialCredit;
+ }
+
+void TRfcommStateDefault::SetProxyForRemoteCredit(CRfcommSAP& aSAP, TInt aCredit)
+ {
+ aSAP.iProxyForRemoteCredit = aCredit;
+ LOG1(_L("RFCOMM: Proxy for credits available on REMOTE (...NEW SETUP...) -> %d"), aSAP.iProxyForRemoteCredit);
+ }
+
+void TRfcommStateDefault::ProxyForRemoteCreditDecrement(CRfcommSAP& aSAP)
+ {
+ __ASSERT_DEBUG(aSAP.iProxyForRemoteCredit > 0,PanicInState(ERfcommCBFCProxyForRemoteCreditGoingNegative));
+
+ aSAP.iProxyForRemoteCredit--;
+ LOG(_L("RFCOMM: Proxies for CREDITS on REMOTE:-"));
+ LOG2(_L("RFCOMM: Available for use (...DECREMENT...) %d -> %d: -1"),
+ aSAP.iProxyForRemoteCredit+1,
+ aSAP.iProxyForRemoteCredit);
+ #ifdef _DEBUG
+ aSAP.iProxyForRemoteCreditsUsed++;
+ LOG1(_L("RFCOMM: Total USED so far during this session: %d"),
+ aSAP.iProxyForRemoteCreditsUsed);
+ #endif
+ return;
+ }
+
+void TRfcommStateDefault::ProxyForRemoteCreditAddCredit(CRfcommSAP& aSAP, TUint8 aCredit)
+ {
+ aSAP.iProxyForRemoteCredit += aCredit;
+ LOG(_L("RFCOMM: Proxies for CREDITS on REMOTE:-"));
+ LOG3(_L("RFCOMM: Available for use (...ADD...) %d -> %d: +%d"),
+ aSAP.iProxyForRemoteCredit-aCredit,
+ aSAP.iProxyForRemoteCredit,
+ aCredit);
+ #ifdef _DEBUG
+ aSAP.iProxyForRemoteCreditsSupplied += aCredit;
+ LOG1(_L("RFCOMM: Total SUPPLIED so far during this session: %d"),
+ aSAP.iProxyForRemoteCreditsSupplied);
+ #endif
+ }
+
+
+TInt TRfcommStateDefault::LocalCredit(const CRfcommSAP& /*aSAP*/) const
+ {
+ // return a zero credit - so cannot send
+ return (TInt)0;
+ }
+
+void TRfcommStateDefault::SetInitialLocalCredit(CRfcommSAP& aSAP, TInt aCredit)
+ {
+ aSAP.iLocalCredit = aCredit;
+ LOG1(_L("RFCOMM: LOCAL credits available (...NEW SETUP...) -> %d"), aSAP.iLocalCredit);
+ }
+
+void TRfcommStateDefault::LocalCreditDecrement(CRfcommSAP& aSAP)
+ {
+ __ASSERT_DEBUG(aSAP.iLocalCredit > 0,PanicInState(ERfcommCBFCLocalCreditGoingNegative));
+
+ aSAP.iLocalCredit--;
+ LOG(_L("RFCOMM: LOCAL CREDITS:-"));
+ LOG2(_L("RFCOMM: Available for use (...DECREMENT...) %d -> %d: -1"),
+ aSAP.iLocalCredit+1,
+ aSAP.iLocalCredit);
+ #ifdef _DEBUG
+ aSAP.iLocalCreditsUsed++;
+ LOG1(_L("RFCOMM: Total USED so far during this session: %d"),
+ aSAP.iLocalCreditsUsed);
+ #endif
+ return;
+ }
+
+void TRfcommStateDefault::LocalCreditAddCredit(CRfcommSAP& aSAP, TUint8 aCredit)
+ {
+ aSAP.iLocalCredit += aCredit;
+ LOG(_L("RFCOMM: LOCAL CREDITS:-"));
+ LOG3(_L("RFCOMM: Available for use (...ADD...) %d -> %d: +%d"),
+ aSAP.iLocalCredit-aCredit,
+ aSAP.iLocalCredit,
+ aCredit);
+ #ifdef _DEBUG
+ aSAP.iLocalCreditsSupplied += aCredit;
+ LOG1(_L("RFCOMM: Total SUPPLIED so far during this session: %d"),
+ aSAP.iLocalCreditsSupplied);
+ #endif
+ }
+
+
+/*************************************************************************/
+
+// TRfcommStateError
+
+/*************************************************************************/
+
+TRfcommStateError::TRfcommStateError(CRfcommStateFactory& aFactory)
+ : TRfcommStateDefault(aFactory)
+ {
+ STATENAME("Error");
+ }
+
+void TRfcommStateError::ActiveOpen(CRfcommSAP& aSAP)
+ {
+ SignalError(aSAP, KErrGeneral, MSocketNotify::EErrorAllOperations);
+ }
+
+TInt TRfcommStateError::PassiveOpen(CRfcommSAP& aSAP, TUint /*aQueSize*/)
+ {
+ return SignalError(aSAP,KErrGeneral, MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateError::Shutdown(CRfcommSAP& aSAP)
+ {
+ SignalError(aSAP,KErrGeneral, MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateError::FastShutdown(CRfcommSAP& aSAP)
+ {
+ SignalError(aSAP,KErrGeneral, MSocketNotify::EErrorAllOperations);
+ }
+
+TInt TRfcommStateError::Send(CRfcommSAP& aSAP, const TDesC8&)
+ {
+ SignalError(aSAP,KErrGeneral, MSocketNotify::EErrorAllOperations);
+ return 0;
+ }
+
+void TRfcommStateError::Read(CRfcommSAP& aSAP, TDesC8&)
+ {
+ SignalError(aSAP,KErrGeneral, MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateError::Ioctl(CRfcommSAP& aSAP, TUint, TUint, TDes8*)
+ {
+ SignalError(aSAP,KErrGeneral, MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateError::CancelIoctl(CRfcommSAP& /*aSAP*/, TUint, TUint)
+ /**
+ Ignore the cancel.
+
+ This is because cancelling in the error state may even be
+ valid, and it doesn't hurt to ignore cancels.
+ **/
+ {
+ }
+
+void TRfcommStateError::Start(CRfcommSAP& aSAP)
+ {
+ SignalError(aSAP,KErrGeneral, MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateError::Enter(CRfcommSAP& aSAP)
+ /**
+ Take this SAP off the mux, since once it's in error state we
+ don't want to get any more notifications from the mux
+ **/
+ {
+
+ aSAP.DeregisterCodService(); // See if there is a Service to remove for CodMan
+
+ if(aSAP.iMux)
+ {
+ aSAP.iMux->DetachSAP(aSAP);
+ }
+ }
+
+
+/*************************************************************************/
+
+// Implementation of TRfcommStateClosed
+//
+// This is the intial state for a SAP and represents it before Connect() has
+// been called, and it is also the end state when closing down
+
+TRfcommStateClosed::TRfcommStateClosed(CRfcommStateFactory& aFactory)
+ : TRfcommStateDefault(aFactory)
+ {
+ STATENAME("Closed");
+ }
+
+void TRfcommStateClosed::Enter(CRfcommSAP& aSAP)
+ /**
+ Entering the closed state.
+
+ We need to remove ourselves from the muxer (if we have one)
+ **/
+ {
+ aSAP.iClosePending=EFalse;
+ if(aSAP.iNotifyNewDataCallback->IsActive())
+ {
+ aSAP.iNotifyNewDataCallback->Cancel();
+ }
+ aSAP.iNewDataToNotify=0;
+ aSAP.iDataBuffer.Reset();
+ aSAP.iProtocol.ControlPlane().ModifyPhysicalLink(EUndoOverridePark, aSAP.iRemoteDev);
+ if(aSAP.iMux)
+ {
+ aSAP.iMux->DetachSAP(aSAP);
+ }
+ aSAP.DeregisterCodService(); // See if there is a Service to remove for CodMan
+
+ }
+
+void TRfcommStateClosed::Error(CRfcommSAP& /*aSAP*/, TInt /*aErr*/,
+ CRfcommSAP::TErrorTypes /*aType*/)
+ /**
+ Panic, we'd never get here since state closed never has a mux associated.
+ **/
+ {
+ DebugPanicInState(ERfcommUnexpectedEvent);
+ }
+
+void TRfcommStateClosed::ActiveOpen(CRfcommSAP& aSAP)
+ /**
+ Called by Socket to cause a connection to be established.
+
+ This causes a sequence of actions which ends with
+ ConnectComplete() being called on the socket notifier. Since a
+ SAP in the closed state may not be fully initialised, all the
+ parameters must be checked before starting the connect.
+ **/
+ {
+ LOG1(_L("RFCOMM: ActiveOpen while state closed, sap %08x"), &aSAP);
+ if(!aSAP.ServerChannelValid() )
+ {
+ LOG(_L("SAP DLCI error"));
+ SignalError(aSAP, KErrRfcommBadAddress, MSocketNotify::EErrorConnect);
+ return;
+ }
+
+ aSAP.RegisterCodService(); // See if there is a Service set for CodMan
+
+ // First get a Mux.
+ ChangeState(aSAP, CRfcommStateFactory::EWaitForMux);
+ }
+
+TInt TRfcommStateClosed::PassiveOpen(CRfcommSAP& aSAP,
+ TUint aQueSize)
+ /**
+ We've just asked to turn into a listening SAP.
+
+ NB. We need to check with the protocol that no other listening SAPs
+ are currently listening on our specified server channel.
+ We should also return an error if the protocol has no listener
+ **/
+ {
+ // First, check that the protocol is listening
+
+ if(aSAP.iProtocol.IncrementListeners() != KErrNone) // get protocol to attempt to listen
+ return KErrRfcommNotListening;
+
+ // Second, the check for other listening SAPs on our specified local address.
+
+ TBTSockAddr listeningName;
+ aSAP.LocalName(listeningName);
+ CRfcommSAP* listener=aSAP.iProtocol.FindIdleSAP(listeningName);
+
+ __ASSERT_DEBUG((listener==NULL), PanicInState(ERfcommSAPAllocatedBetweenBindAndListen));
+
+ if(listener!=NULL)
+ return KErrInUse;
+
+ TBTSockAddr remoteAddr;
+ remoteAddr.SetBTAddr(aSAP.iRemoteDev);
+ remoteAddr.SetPort(aSAP.iServerChannel);
+ if(aSAP.iProtocol.FindIdleSAP(remoteAddr))
+ return KErrAlreadyExists; // RFCOMM already has a listener which would field this address...
+
+ aSAP.iMaxClonesWaitingForStart=aQueSize;
+ aSAP.RegisterCodService(); // See if there is a Service set for CodMan
+ ChangeState(aSAP, CRfcommStateFactory::EListening);
+ return KErrNone;
+ }
+
+void TRfcommStateClosed::Start(CRfcommSAP& /*aSAP*/)
+ {
+ // Do nothing - this is quite a valid thing to happen, but the base class panics!
+ }
+
+void TRfcommStateClosed::Shutdown(CRfcommSAP& aSAP)
+ /**
+ The ESOCK would like us to close down. We can.
+ **/
+ {
+ aSAP.iSocket->CanClose();
+ }
+
+TInt TRfcommStateClosed::SetOption(CRfcommSAP &aSAP, TUint aLevel,
+ TUint aName, const TDesC8& aOption)
+ /**
+ Allow modem status to be modified whilst we are closed.
+ **/
+ {
+ if(aLevel == KSolBtRFCOMM && aName == KRFCOMMLocalModemStatus)
+ {
+ if ( aOption.Length() != sizeof(TUint8) )
+ {
+ return KErrArgument;
+ }
+ else
+ {
+ TPckgBuf<TUint8> signals;
+ signals.Copy(aOption);
+ aSAP.iSignals = signals();
+ return KErrNone;
+ }
+ }
+
+ return TRfcommStateDefault::SetOption(aSAP, aLevel, aName, aOption);
+ }
+
+/*************************************************************************/
+
+// Implementation of TRfcommStateConnecting
+
+TRfcommStateConnecting::TRfcommStateConnecting(CRfcommStateFactory& aFactory)
+ : TRfcommStateDefault(aFactory)
+ {
+ }
+
+
+void TRfcommStateConnecting::Shutdown(CRfcommSAP& aSAP)
+/**
+ We've been asked to do a controlled shutdown during connection.
+ Record the fact that we want to send a disconnect command upon
+ reception of the UA command.
+**/
+ {
+ aSAP.iClosePending=ETrue;
+ }
+
+void TRfcommStateConnecting::FastShutdown(CRfcommSAP& aSAP)
+/**
+ We've been asked to shutdown with extreme prejudice.
+ Move back to the Closed state - there is nothing we can do
+ to make this clean
+**/
+ {
+ ChangeState(aSAP, CRfcommStateFactory::EClosed);
+ };
+
+
+void TRfcommStateConnecting::DM(CRfcommSAP& aSAP)
+/**
+ We've received a DM (Disconnect Mode) response.
+ If we've been asked to close down anyway, tell the SAP that we can be closed.
+ If we haven't, signal an error to the socket.
+**/
+ {
+ FailureWhileConnecting(aSAP, KErrCouldNotConnect);
+ }
+
+void TRfcommStateConnecting::FailureWhileConnecting(CRfcommSAP& aSAP, TInt aErrorCode)
+/**
+ Send appropriate notification of failure back to SAP socket if failure occurs while connecting.
+**/
+ {
+ if(!aSAP.iClosePending)
+ {
+ ChangeState(aSAP, CRfcommStateFactory::EClosed);
+ SignalError(aSAP, aErrorCode, MSocketNotify::EErrorConnect);
+ }
+ else
+ {
+ aSAP.iSocket->CanClose(); // Will delete us
+ }
+ }
+
+void TRfcommStateConnecting::LinkDown(CRfcommSAP& aSAP)
+ /**
+ The link has gone down before our connection came up.
+
+ This can even (counter-intuitively) occur in WaitForMux because
+ there is a lag between the l2cap link coming up and the mux
+ channel itself coming up. In the interval, the l2cap link can
+ drop again and thus we get a link down notification (the mux
+ will not attempt to reconnect).
+
+ Handle this by simply erroring the connection attempt.
+ **/
+ {
+ // tell secman not to bother
+ aSAP.CancelAccessRequest();
+
+ FailureWhileConnecting(aSAP, KErrDisconnected);
+ }
+
+
+/*******************************************************************/
+/*
+ Implementation of WaitForMux state.
+ When a mux has been found for this SAP, then MuxUp is called.
+*/
+
+TRfcommStateWaitForMux::TRfcommStateWaitForMux(CRfcommStateFactory& aFactory)
+ : TRfcommStateConnecting(aFactory)
+ {
+ STATENAME("WaitForMux");
+ }
+
+void TRfcommStateWaitForMux::Enter(CRfcommSAP& aSAP)
+ /**
+ Entry to the state.
+
+ Ask the protocol to find a mux for us. We can then
+ add our context to it.
+ **/
+ {
+ CRfcommMuxer* mux=NULL;
+ TBTSockAddr remoteAddr;
+ aSAP.RemName(remoteAddr);
+ TInt error=aSAP.iProtocol.GetMux(remoteAddr,mux);
+ if(error)
+ Error(aSAP, error, CRfcommSAP::EErrorGeneral);
+ else
+ {
+ // Calculate the DLCI that this SAP is trying to connect to
+ aSAP.iDLCI=mux->MakeOutboundDLCI(TUint8(remoteAddr.Port()));
+ // Ensure that we don't already have a connection for the DLCI
+ // we're trying to connect to on this device...
+ if(mux->FindSAP(aSAP.DLCI())!=NULL)
+ {
+ // Problems! We've already got a SAP attached to this DLCI
+ // for this same device...we need to error this SAP.
+ LOG1(_L("RFCOMM: Attempt to connect to connected SAP, DLCI %d"), aSAP.DLCI());
+ Error(aSAP, KErrAlreadyExists, CRfcommSAP::EErrorGeneral);
+ }
+ else
+ {
+ // Transfer the sap to the mux
+ if(aSAP.iMux)
+ aSAP.iLink.Deque();
+ aSAP.iMux=mux;
+
+ // The next line will call back into MuxUp, either synchronously
+ // (when mux channel is already up) or asynchronously (when the
+ // mux channel needs to be opened).
+ mux->AddSAP(aSAP);
+ }
+ }
+ }
+
+void TRfcommStateWaitForMux::MuxUp(CRfcommSAP& aSAP)
+ /**
+ A muxer has been found for this SAP.
+ On entry, the sap has had a muxer associated with it.
+ Change state to connecting, so it can do any config and the SABM.
+ **/
+ {
+ ChangeState(aSAP, CRfcommStateFactory::EWaitForPNResp); // Enter the new state
+ }
+
+void TRfcommStateWaitForMux::Shutdown(CRfcommSAP& aSAP)
+ /**
+ We've been asked to shutdown while connecting.
+
+ Since the mux isn't up yet, we must detach from the mux and
+ also signal that we can be deleted.
+ **/
+ {
+ ChangeState(aSAP, CRfcommStateFactory::EClosed);// Enter this state, which detaches us
+ aSAP.iSocket->CanClose(); // This deletes us
+ }
+
+void TRfcommStateWaitForMux::SABM(CRfcommSAP& aSAP, CRfcommMuxer& /*aMux*/, TUint8 /*aDLCI*/)
+ {
+ DebugPanicInState(ERfcommFrameBeforeMuxUp);
+ SignalError(aSAP, KErrRfcommSAPUnexpectedEvent, MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateWaitForMux::UA(CRfcommSAP& /*aSAP*/)
+ {
+ DebugPanicInState(ERfcommFrameBeforeMuxUp);
+ /*ignore and hope for the best*/
+ }
+
+void TRfcommStateWaitForMux::DISC(CRfcommSAP& aSAP)
+ {
+ DebugPanicInState(ERfcommFrameBeforeMuxUp);
+ SignalError(aSAP, KErrRfcommSAPUnexpectedEvent, MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateWaitForMux::DM(CRfcommSAP& /*aSAP*/)
+ {
+ DebugPanicInState(ERfcommFrameBeforeMuxUp);
+ /*ignore and hope for the best*/
+ }
+
+void TRfcommStateWaitForMux::RPN(CRfcommSAP& aSAP, const TRfcommRPNTransaction& /*aRPNTransaction*/, CRfcommMuxer& /*aMux*/, TUint8 /*aDLCI*/)
+ {
+ DebugPanicInState(ERfcommFrameBeforeMuxUp);
+ SignalError(aSAP, KErrRfcommSAPUnexpectedEvent, MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateWaitForMux::RPNRsp(CRfcommSAP& /*aSAP*/, const TRfcommRPNTransaction& /*aRPNTransaction*/)
+ {
+ DebugPanicInState(ERfcommFrameBeforeMuxUp);
+ /*ignore and hope for the best*/
+ }
+
+// Parameter negotiation
+void TRfcommStateWaitForMux::PN(CRfcommSAP& aSAP, TRfcommPortParams& /*aParams*/, CRfcommMuxer& /*aMux*/, TUint8 /*aDLCI*/)
+ {
+ DebugPanicInState(ERfcommFrameBeforeMuxUp);
+ SignalError(aSAP, KErrRfcommSAPUnexpectedEvent, MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateWaitForMux::PNResp(CRfcommSAP& /*aSAP*/, TRfcommPortParams& /*aParams*/)
+ {
+ DebugPanicInState(ERfcommFrameBeforeMuxUp);
+ /*ignore and hope for the best*/
+ }
+
+void TRfcommStateWaitForMux::MSC(CRfcommSAP& /*aSAP*/, TUint8 /*aSignals*/)
+ {
+ DebugPanicInState(ERfcommFrameBeforeMuxUp);
+ /*ignore and hope for the best*/
+ }
+
+void TRfcommStateWaitForMux::RLS(CRfcommSAP& /*aSAP*/, TUint8 /*aStatus*/)
+ {
+ DebugPanicInState(ERfcommFrameBeforeMuxUp);
+ /*ignore and hope for the best*/
+ }
+
+void TRfcommStateWaitForMux::NewData(CRfcommSAP& /*aSAP*/, const TDesC8& /*aData*/)
+ {
+ DebugPanicInState(ERfcommFrameBeforeMuxUp);
+ /*ignore and hope for the best*/
+ }
+
+/*****************************************************************
+
+State Wait for PN Response
+
+******************************************************************/
+
+TRfcommStateWaitForPNResp::TRfcommStateWaitForPNResp(CRfcommStateFactory& aFactory)
+ : TRfcommStateConnecting(aFactory)
+ {
+ STATENAME("WaitForPNResp");
+ }
+
+void TRfcommStateWaitForPNResp::Enter(CRfcommSAP& aSAP)
+ /**
+ The Mux is up - it's time to initiate Parameter Negotiations
+
+ Send a PN packet with our preferred parameters.
+ **/
+ {
+ __ASSERT_DEBUG(aSAP.iMux!=NULL,PanicInState(ERfcommNullMux));
+ TRfcommPortParams params;
+ params.iMaxFrameSize=aSAP.MaximumMTU();
+ params.iInitialCredit = aSAP.iMux->FlowStrategy()->PNFinalOctet();
+
+ aSAP.SetProxyForRemoteCredit(params.iInitialCredit); // set up our proxy
+#ifdef _DEBUG
+ aSAP.iProxyForRemoteCreditsSupplied = params.iInitialCredit;
+#endif
+ aSAP.iMTU=params.iMaxFrameSize; // Remember the newly calculated MTU
+ LOG2(_L("RFCOMM: Sending PN (Calculated MTU %d from frame size %d)"), aSAP.iMTU, aSAP.iMux->GetMaxDataSize());
+
+ TInt error=aSAP.iMux->SendPN(aSAP, params);
+
+ if(error!=KErrNone)
+ {
+ // Error occurred while sending parameter negotiation command
+ FailureWhileConnecting(aSAP, error);
+ }
+ }
+
+void TRfcommStateWaitForPNResp::PNResp(CRfcommSAP& aSAP, TRfcommPortParams& aParams)
+ /**
+ We've received a PN response, which may or may not mean our
+ datasize has been accepted
+
+ If everything is OK (ie. the PN response accepts a MTU size
+ between 1 and our original maximum MTU), send a SABM and begin
+ waiting for a UA. If things are not OK, we either signal an error
+ or inform the socket that we CanClose (depending on whether we
+ were trying to disconnect anyway).
+
+ **/
+ {
+ __ASSERT_DEBUG(aSAP.iMux!=NULL,PanicInState(ERfcommNullMux));
+ if(aParams.iCreditIndicator == (KCBFCResponseFlag >> 4))
+ {
+ aSAP.SetInitialLocalCredit(aParams.iInitialCredit);
+ #ifdef _DEBUG
+ aSAP.iLocalCreditsSupplied = aParams.iInitialCredit;
+ #endif
+ }
+ TInt error=KErrNone;
+ if(aParams.iMaxFrameSize<=aSAP.NegotiatedMTU() && aParams.iMaxFrameSize>0)
+ {
+ LOG1(_L("RFCOMM: Received acceptable PN Response with MTU %d"),aParams.iMaxFrameSize);
+
+ // We have an MTU value so ask L2Cap to work out the optimal MTU value based on the one
+ // received from the remote. Note that the Max Frame Size is the max data size for Rfcomm
+ // so we need to add on the max Rfcomm header size to create the restriction on L2Cap
+ // MTU.
+ TPckgBuf<TInt> restrictedMtu(aParams.iMaxFrameSize + KMaxFrameOverhead);
+ error = aSAP.GetOption(KSolBtL2CAP, KL2CAPOutboundMTUForBestPerformanceWithRestriction, restrictedMtu);
+
+ // If we get an error continue but with a possible non-optimal MTU value. iMTU is the max
+ // data size so remove the max Rfcomm header size as these will get added later.
+ aSAP.iMTU = (error == KErrNone) ? restrictedMtu() - KMaxFrameOverhead : aParams.iMaxFrameSize;
+ error = KErrNone;
+
+ }
+ else
+ {
+ LOG1(_L("RFCOMM: Received unacceptable PN Response with MTU %d"),aParams.iMaxFrameSize);
+ error=KErrRfcommParameterNegotiationFailure;
+ };
+
+ if(error!=KErrNone)
+ {
+ FailureWhileConnecting(aSAP,error);
+ }
+ else
+ {
+ ChangeState(aSAP, CRfcommStateFactory::EWaitForOutgoingSecurityCheck);
+ }
+ }
+
+/*********************************************************************/
+/*
+ We've received a good PN configuration response and we're waiting for the connection to complete.
+
+*/
+
+TRfcommStateWaitForUA::TRfcommStateWaitForUA(CRfcommStateFactory& aFactory)
+ : TRfcommStateConnecting(aFactory)
+ {
+ STATENAME("WaitForUA");
+ }
+
+void TRfcommStateWaitForUA::UA(CRfcommSAP& aSAP)
+ /**
+ The SABM command has succeeded.
+
+ If there is no close pending we go to open, and
+ also construct the SAPs buffers. If we need to close, we go to
+ the disconnect state.
+ **/
+ {
+ __ASSERT_DEBUG(aSAP.iMux!=NULL,PanicInState(ERfcommNullMux));
+ if(aSAP.iClosePending)
+ {
+ ChangeState(aSAP, CRfcommStateFactory::EDisconnect);
+ }
+ else
+ {
+ TRAPD(err,
+ aSAP.iDataBuffer.SetLengthL(aSAP.iMux->FlowStrategy()->DataBufferMultiple()*
+ aSAP.iMTU));
+ if(err != KErrNone)
+ {
+ // Failed to alloc, so fail this connect and error
+ SignalError(aSAP, err, MSocketNotify::EErrorConnect);
+ ChangeState(aSAP, CRfcommStateFactory::EClosed);
+ return;
+ }
+
+ aSAP.iHighTideMark=KRfcommSAPBufferHighMultiple*(aSAP.iMTU);
+ aSAP.iLowTideMark=KRfcommSAPBufferLowMultiple*(aSAP.iMTU);
+
+
+ ChangeState(aSAP, CRfcommStateFactory::EOpen);
+ }
+ }
+
+TBool TRfcommStateWaitForUA::HandleFrameResponseTimeout(CRfcommSAP& aSAP)
+ /**
+ We've waited too long for a UA to come back to us, so we start disconnecting.
+
+ We also need to signal
+ **/
+ {
+ if(!aSAP.iClosePending)
+ SignalError(aSAP, KErrTimedOut, MSocketNotify::EErrorConnect);
+
+ ChangeState(aSAP, CRfcommStateFactory::EDisconnect);
+ return ETrue;
+ }
+
+/******************************************************************/
+/*
+ The Listening state.
+
+ Deals with incoming connection requests
+*/
+
+TRfcommStateListening::TRfcommStateListening(CRfcommStateFactory& aFactory)
+ : TRfcommStateDefault(aFactory)
+ {
+ STATENAME("Listening");
+ }
+
+void TRfcommStateListening::Enter(CRfcommSAP& aSAP)
+ /**
+ The SAP is entering the listening state. The protocol needs to know.
+ **/
+ {
+ aSAP.iProtocol.AddIdleSAP(aSAP);
+ aSAP.iProtocol.RemoveBoundSAP(aSAP);
+ }
+
+void TRfcommStateListening::Exit(CRfcommSAP& aSAP)
+ {
+ aSAP.DeregisterCodService(); // See if there is a Service to remove for CodMan
+ aSAP.StopListening();
+ }
+
+void TRfcommStateListening::SABM(CRfcommSAP& aSAP, CRfcommMuxer& aMux, TUint8 aDLCI)
+ /**
+ A SABM sent to a listening SAP means we need create a clone SAP
+ **/
+ {
+ // NB. The criteria of the following assert will vary for the different states
+ // that this function gets called in.
+ __ASSERT_DEBUG(aDLCI!=0, PanicInState(ERfcommBadDLCIForClone));
+
+ CRfcommSAP* newSAP=AttemptToClone(aSAP, aMux, aDLCI);
+ if(newSAP)
+ newSAP->SABM(aMux, aDLCI);
+ }
+
+void TRfcommStateListening::PN(CRfcommSAP& aSAP, TRfcommPortParams& aParams, CRfcommMuxer& aMux, TUint8 aDLCI)
+ /**
+ A PN sent to a listening SAP means we need create a clone SAP
+ **/
+ {
+ // NB. The criteria of the following assert will vary for the different states
+ // that this function gets called in.
+ __ASSERT_DEBUG(aDLCI!=0, PanicInState(ERfcommBadDLCIForClone));
+
+ CRfcommSAP* newSAP=AttemptToClone(aSAP, aMux, aDLCI);
+ if(newSAP)
+ {
+ newSAP->PN(aParams, aMux, aDLCI);
+ }
+ else
+ {
+ // Uh Oh! Couldn't get a SAP, don't worry too much - a DM has been sent
+ LOG(_L("[rfcommstates.cpp]: Couldn't clone a SAP after receiving a PN\n"));
+ }
+ }
+
+void TRfcommStateListening::RPN(CRfcommSAP& aSAP, const TRfcommRPNTransaction& aRPNTransaction, CRfcommMuxer& aMux, TUint8 aDLCI)
+ /**
+ A RPN sent to a listening SAP means we need create a clone SAP
+ **/
+ {
+ __ASSERT_DEBUG(aDLCI!=0, PanicInState(ERfcommBadDLCIForClone));
+
+ CRfcommSAP* newSAP=AttemptToClone(aSAP, aMux, aDLCI);
+ if(newSAP)
+ {
+ newSAP->RPN(&aRPNTransaction, aMux, aDLCI);
+ }
+ else
+ {
+ // Uh Oh! Couldn't get a SAP, don't worry too much - a DM has been sent
+ LOG(_L("[rfcommstates.cpp]: Couldn't clone a SAP after receiving a RPN\n"));
+ }
+ }
+
+void TRfcommStateListening::Shutdown(CRfcommSAP& aSAP)
+ {
+ ChangeState(aSAP, CRfcommStateFactory::EClosed);
+ aSAP.iSocket->CanClose();
+ }
+
+void TRfcommStateListening::FastShutdown(CRfcommSAP& aSAP)
+ {
+ ChangeState(aSAP, CRfcommStateFactory::EClosed);
+ }
+
+CRfcommSAP* TRfcommStateListening::AttemptToClone(CRfcommSAP& aSAP, CRfcommMuxer& aMux, TUint8 aDLCI)
+ /**
+ Utility function private to TRfcommStateListening which attempts to create a cloned SAP
+
+ @param aSAP The SAP to be cloned
+ @param aMux The Muxer to attach it to
+ @param aDLCI The DLCI of the cloned SAP
+ **/
+ {
+ CRfcommSAP* newSAP=aSAP.CloneMe();
+
+ if(!newSAP)
+ {
+ // A new clone could not be created - send a DM, game over
+ aMux.SendDM(aDLCI);
+ }
+ else
+ {
+ // Set up details of the new identity of the clone
+
+ // Put it in WaitForSABM state
+ newSAP->iState=iFactory.GetState(CRfcommStateFactory::EWaitForSABM);
+ newSAP->iState->Enter(*newSAP);
+
+ newSAP->iSignals=aSAP.iSignals;
+ newSAP->iDLCI=aDLCI;
+ newSAP->iServerChannel=aMux.MakeServerChannel(aDLCI);
+ newSAP->iRemoteDev=aMux.RemoteBTAddr();
+ // Must come after registering the remote address with the new SAP because we will need
+ // the remote address to find a link in LinkMgrProtocol to override LPM on
+ newSAP->iProtocol.ControlPlane().ModifyPhysicalLink(EOverrideLPMWithTimeout, newSAP->iRemoteDev);
+ newSAP->iUserDefinedMTU=aSAP.iUserDefinedMTU; // We take our cues as
+ // regards max MTU from
+ // the listening SAP.
+
+ newSAP->iMux=&aMux;
+ // NB. The listening SAP retains ownership of the SAP until it sends a
+ // ConnectComplete up for it.
+ aMux.AddSAP(*newSAP);
+ }
+
+ return newSAP;
+ }
+
+TInt TRfcommStateListening::SetOption(CRfcommSAP &aSAP, TUint aLevel,
+ TUint aName, const TDesC8& aOption)
+ /**
+ Allow modem status to be modified whilst we are listening.
+ **/
+ {
+ if(aLevel == KSolBtRFCOMM && aName == KRFCOMMLocalModemStatus)
+ {
+ if ( aOption.Length() != sizeof(TUint8) )
+ {
+ return KErrArgument;
+ }
+ else
+ {
+ TPckgBuf<TUint8> signals;
+ signals.Copy(aOption);
+ aSAP.iSignals = signals();
+ return KErrNone;
+ }
+ }
+
+ return TRfcommStateDefault::SetOption(aSAP, aLevel, aName, aOption);
+ }
+
+/*****************************************************************/
+/*
+ The cloned base state
+*/
+
+TRfcommStateCloned::TRfcommStateCloned(CRfcommStateFactory& aFactory)
+ : TRfcommStateDefault(aFactory)
+ {
+ }
+
+void TRfcommStateCloned::LinkDown(CRfcommSAP& aSAP)
+ {
+ aSAP.iParentSAP->ChildConnectFailed(aSAP);
+ }
+
+void TRfcommStateCloned::Error(CRfcommSAP& aSAP, TInt /*aErr*/,
+ CRfcommSAP::TErrorTypes /*aType*/)
+ {
+ aSAP.iParentSAP->ChildConnectFailed(aSAP); // Will delete us...
+ }
+
+void TRfcommStateCloned::DISC(CRfcommSAP& aSAP)
+ /**
+ DISC, so the other end wants us to go away.
+
+ Kill ourselves after acknowledging it.
+ **/
+ {
+ aSAP.iMux->SendUA(aSAP.DLCI()); // Send a UA which will outlive this SAP
+ aSAP.iParentSAP->ChildConnectFailed(aSAP);
+ }
+
+void TRfcommStateCloned::DM(CRfcommSAP& aSAP)
+ /**
+ We've got a DM in, which means that something's wrong.
+
+ We'll junk this connection, since we should not get a DM as we
+ haven't sent any commands. The other end is obviously very
+ confused.
+ **/
+ {
+ aSAP.iParentSAP->ChildConnectFailed(aSAP);
+ }
+
+/******************************************************************/
+/*
+ The Wait For SABM state.
+
+ (The intial state of a cloned SAP)
+*/
+
+TRfcommStateWaitForSABM::TRfcommStateWaitForSABM(CRfcommStateFactory& aFactory)
+ : TRfcommStateCloned(aFactory)
+ {
+ STATENAME("WaitForSABM");
+ }
+
+void TRfcommStateWaitForSABM::SABM(CRfcommSAP& aSAP, CRfcommMuxer& aMux, TUint8 aDLCI)
+ /**
+ We received a SABM - Tell our parent SAP that we're connected
+ **/
+ {
+ if(aSAP.iMTU==0) // i.e. We've not entered into any negotiations...
+ aSAP.iMTU=KRfcommDefaultMTU; // ...so set to default.
+
+ // We can set up the Data Buffer since MTU negotiations are now over.
+ //TRAPD(err, aSAP.iDataBuffer.SetLengthL(KRfcommSAPBufferMultiple*aSAP.iMTU));
+ TRAPD(err,
+ aSAP.iDataBuffer.SetLengthL(aMux.FlowStrategy()->DataBufferMultiple()*
+ aSAP.iMTU));
+ if(err != KErrNone)
+ {
+ // Failed to alloc, so fail this connect and error
+ aMux.SendDM(aDLCI);
+ aSAP.iParentSAP->ChildConnectFailed(aSAP); // Will delete us...
+ return;
+ }
+
+ aSAP.iHighTideMark=KRfcommSAPBufferHighMultiple*(aSAP.iMTU);
+ aSAP.iLowTideMark=KRfcommSAPBufferLowMultiple*(aSAP.iMTU);
+
+ ChangeState(aSAP,CRfcommStateFactory::EWaitForIncomingSecurityCheck);
+ }
+
+#ifndef _DEBUG
+void TRfcommStateWaitForSABM::PN(CRfcommSAP& aSAP, TRfcommPortParams& aParams,
+ CRfcommMuxer& /*aMux*/, TUint8 /*aDLCI*/)
+#else
+void TRfcommStateWaitForSABM::PN(CRfcommSAP& aSAP, TRfcommPortParams& aParams,
+ CRfcommMuxer& aMux, TUint8 /*aDLCI*/)
+#endif
+ {
+ __ASSERT_DEBUG(aSAP.iMux==&aMux,PanicInState(ERfcommInvalidMuxInSAP));
+ if(aSAP.iMTU==0)
+ {
+ // We have no MTU set for the SAP at the moment. Try to determine
+ // the upper limit of what we can handle
+ aSAP.iMTU=aSAP.MaximumMTU();
+ }
+
+ if(aParams.iMaxFrameSize>aSAP.iMTU || aParams.iMaxFrameSize<=0)
+ {
+ // Either the remote device wants a larger MTU or has provided us
+ // with a nonsensical <=0 one.
+ // Either way, try to negotiate the MTU to our maximum
+ aParams.iMaxFrameSize=aSAP.iMTU;
+ }
+ else
+ {
+
+ // We have an MTU value so ask L2Cap to work out the optimal MTU value based on the one
+ // received from the remote. Note that the Max Frame Size is the max data size for Rfcomm
+ // so we need to add on the max Rfcomm header size to create the restriction on L2Cap
+ // MTU.
+ TPckgBuf<TInt> restrictedMtu(aParams.iMaxFrameSize + KMaxFrameOverhead);
+ TInt err = aSAP.GetOption(KSolBtL2CAP, KL2CAPOutboundMTUForBestPerformanceWithRestriction, restrictedMtu);
+
+
+ // If we get an error continue but with a possible non-optimal MTU value. iMTU is the max
+ // data size so remove the max Rfcomm header size as these will get added later.
+ aSAP.iMTU = (err == KErrNone) ? restrictedMtu() - KMaxFrameOverhead : aParams.iMaxFrameSize;
+ }
+
+ if(aParams.iCreditIndicator == (KCBFCCommandFlag >> 4))
+ {
+ // Remote device is using CBFC. Set up the credit counts
+ aSAP.SetInitialLocalCredit(aParams.iInitialCredit); // Credits we've just been given
+
+ // now set to the credits we will give them
+ aParams.iInitialCredit = aSAP.iMux->FlowStrategy()->PNFinalOctet();
+ aSAP.SetProxyForRemoteCredit(aParams.iInitialCredit); // and record it
+ }
+
+ aSAP.iMux->SendPNResponse(aSAP, aParams);
+ }
+
+void TRfcommStateWaitForSABM::RPN(CRfcommSAP& aSAP, const TRfcommRPNTransaction& aRPNTransaction,
+ CRfcommMuxer& /*aMux*/, TUint8 /*aDLCI*/)
+ {
+ // Write whatever's changed into our local port params.
+ aSAP.iLocalPortParams.UpdateFromRPNTransaction(aRPNTransaction);
+
+ // Send L2CAP exactly what they sent us, except this is a response
+ aSAP.iMux->SendRPN(aSAP, EFalse, KRPNResponseLength, aRPNTransaction);
+
+ // Don't bother telling the user. If they're interested they'll just have
+ // to keep doing getOpts whenever they feel like it!
+ }
+
+void TRfcommStateWaitForSABM::MuxUp(CRfcommSAP& /*aSAP*/)
+ {
+ // Do nothing - included because the default class will panic.
+ }
+
+
+/******************************************************************/
+/*
+ The Incoming Security Check state.
+
+ Handles making and responding to a security check on the security manager.
+
+*/
+
+TRfcommStateIncomingSecurityCheck::TRfcommStateIncomingSecurityCheck(CRfcommStateFactory& aFactory)
+ : TRfcommStateCloned(aFactory)
+ {
+ STATENAME("IncomingSecurityCheck");
+ }
+
+void TRfcommStateIncomingSecurityCheck::Enter(CRfcommSAP& aSAP)
+ {
+ // INCOMING security
+ // The listening SAP has the security settings, and possible device overrides
+ aSAP.StartAccessRequest(aSAP.ListeningSAP());
+ }
+
+void TRfcommStateIncomingSecurityCheck::AccessRequestComplete(CRfcommSAP& aSAP, TInt aResult)
+ {
+ if(aResult==KErrNone)
+ {
+ LOG1(_L("RFCOMM: Access Request Successful (DLCI %d)"), aSAP.DLCI());
+ ChangeState(aSAP, CRfcommStateFactory::EWaitForStart);
+ }
+ else
+ {// Something is not well - inform our parent.
+ // This will result in us being deleted, so send DM for the DLCI
+ LOG2(_L("RFCOMM: Access Request Failed (DLCI %d, Error %d)"), aSAP.DLCI(),aResult);
+ aSAP.iMux->SendDM(aSAP.DLCI());
+ aSAP.iParentSAP->ChildConnectFailed(aSAP);
+ }
+ }
+
+void TRfcommStateIncomingSecurityCheck::Exit(CRfcommSAP& aSAP)
+ {
+ aSAP.CancelAccessRequest();
+ }
+
+/******************************************************************/
+/*
+ The Outgoing Security Check state.
+
+ Handles making and responding to a security check on the security manager.
+
+*/
+
+TRfcommStateOutgoingSecurityCheck::TRfcommStateOutgoingSecurityCheck(CRfcommStateFactory& aFactory)
+ : TRfcommStateConnecting(aFactory)
+ {
+ STATENAME("OutgoingSecurityCheck");
+ }
+
+void TRfcommStateOutgoingSecurityCheck::Enter(CRfcommSAP& aSAP)
+ {
+ // Outgoing security
+ // The listening SAP has the security settings, and possible device overrides
+ aSAP.StartAccessRequest(aSAP);
+ }
+
+void TRfcommStateOutgoingSecurityCheck::AccessRequestComplete(CRfcommSAP& aSAP, TInt aResult)
+ {
+ if(aResult==EBTSecManAccessGranted)
+ {
+ LOG1(_L("RFCOMM: Access Request Successful (DLCI %d)"), aSAP.DLCI());
+ aResult = aSAP.iMux->SendSABM(aSAP);
+ }
+
+ if (aResult < KErrNone || aResult == EBTSecManAccessDenied)
+ {// Something is not well - inform our parent.
+ // This will result in us being deleted, so send DM for the DLCI
+ LOG2(_L("RFCOMM: Outgoing security Failed (DLCI %d, Error %d)"), aSAP.DLCI(),aResult);
+ FailureWhileConnecting(aSAP, KErrCouldNotConnect);
+ }
+ else
+ {
+ ChangeState(aSAP, CRfcommStateFactory::EWaitForUA);
+ }
+ }
+
+void TRfcommStateOutgoingSecurityCheck::Exit(CRfcommSAP& aSAP)
+ {
+ aSAP.CancelAccessRequest();
+ }
+
+
+/******************************************************************/
+/*
+ The Wait For Start state.
+
+*/
+
+TRfcommStateWaitForStart::TRfcommStateWaitForStart(CRfcommStateFactory& aFactory)
+ : TRfcommStateDefault(aFactory)
+ {
+ STATENAME("WaitForStart");
+ }
+
+void TRfcommStateWaitForStart::Enter(CRfcommSAP &aSAP)
+ {
+ // NB. The following line effectively passes ownership of aSAP away from
+ // the parent SAP to ESOCK
+ aSAP.iParentSAP->ChildConnected(aSAP);
+ }
+
+void TRfcommStateWaitForStart::Start(CRfcommSAP& aSAP)
+ /**
+ Start has been called on us. Move to the open state.
+ **/
+ {
+ __ASSERT_DEBUG(aSAP.iMux!=NULL,PanicInState(ERfcommNullMux));
+ if(aSAP.iParentSAP!=NULL)
+ {
+ // We still have an extant parent listening SAP.
+ // Let them know that we have been started so that they can
+ // accept more cloned SAPs into the WaitForStart state.
+ aSAP.iParentSAP->ChildStarted(aSAP);
+ }
+ aSAP.iMux->SendUA(aSAP);
+
+ ChangeState(aSAP, CRfcommStateFactory::EOpen);
+ }
+
+void TRfcommStateWaitForStart::DISC(CRfcommSAP& aSAP)
+ /**
+ We are waiting for a Start, but the remote device has decided to disconnect us.
+
+ Send a UA, and enter the CloseOnStart state.
+ **/
+ {
+ __ASSERT_DEBUG(aSAP.iMux!=NULL,PanicInState(ERfcommNullMux));
+ aSAP.iMux->SendUA(aSAP.DLCI());
+ aSAP.iMux->DetachSAP(aSAP); // Because we're just waiting about for a Start
+ // from above - we're not interested in what is
+ // coming in from below (esp. LinkDown notification).
+ ChangeState(aSAP,CRfcommStateFactory::ECloseOnStart);
+ }
+
+void TRfcommStateWaitForStart::Error(CRfcommSAP& aSAP, TInt /*aErr*/,
+ CRfcommSAP::TErrorTypes /*aType*/)
+ {
+ // We can't really do anything at this point with an error,
+ // because ESOCK knows about us. Therefore, the best we can
+ // do is to move into CloseOnStart state.
+ ChangeState(aSAP,CRfcommStateFactory::ECloseOnStart);
+ }
+
+/******************************************************************/
+/*
+ The Close On Start state.
+
+ For when a SAP triggers a ConnectComplete, but receives a remote DISC
+ before it receives a local Start.
+*/
+
+TRfcommStateCloseOnStart::TRfcommStateCloseOnStart(CRfcommStateFactory& aFactory)
+ : TRfcommStateDefault(aFactory)
+ {
+ STATENAME("CloseOnStart");
+ }
+
+void TRfcommStateCloseOnStart::Start(CRfcommSAP& aSAP)
+ {
+ // NB. The Disconnect() invalidates the cloned SAP on ESOCKs
+ // accept queue - however, a client side Accept() will still be
+ // completed with KErrNone. The first read or write will be
+ // completed with KErrDisconnected.
+ //
+ aSAP.iSocket->Disconnect();
+
+ if(aSAP.iParentSAP!=NULL)
+ {
+ // We still have an extant parent listening SAP.
+ // Let them know that we have been started so that they can
+ // accept more cloned SAPs into the WaitForStart state.
+ aSAP.iParentSAP->ChildStarted(aSAP);
+ }
+
+ ChangeState(aSAP, CRfcommStateFactory::EClosed);
+ }
+
+void TRfcommStateCloseOnStart::Error(CRfcommSAP& /*aSAP*/, TInt /*aErr*/,
+ CRfcommSAP::TErrorTypes /*aType*/)
+ {
+ // Nothing we can really do with this error, since we're already
+ // going to close on start.
+ }
+
+
+/******************************************************************/
+/*
+ The open state.
+
+ Here all the data xfer and flow control is handled.
+*/
+
+TRfcommStateOpen::TRfcommStateOpen(CRfcommStateFactory& aFactory)
+ : TRfcommStateDefault(aFactory)
+ {
+ STATENAME("Open");
+ }
+
+void TRfcommStateOpen::Enter(CRfcommSAP& aSAP)
+ /**
+ We've entered the open state, so tell the SAP.
+ **/
+ {
+ // If this fails what happens?
+ TInt err=aSAP.iMux->SendMSC(aSAP, aSAP.iSignals);
+ if(err != KErrNone)
+ {
+ // Failed to alloc, so fail this connect and error
+ SignalError(aSAP, err, MSocketNotify::EErrorConnect);
+ ChangeState(aSAP, CRfcommStateFactory::EClosed);
+ return;
+ }
+
+ aSAP.iProtocol.ControlPlane().ModifyPhysicalLink(EUndoOverridePark, aSAP.iRemoteDev);
+ aSAP.iSocket->ConnectComplete();
+ aSAP.CTS(EFalse); // So that we block should anyone try to write
+ // anything through this SAP before we get MSC
+ // from remote side.
+ aSAP.iReceivedMSC=EFalse; // Until we receive an MSC, we consider any
+ // user data we receive as being a bit naughty
+ // (other device is non 7.10 compliant)
+ }
+
+void TRfcommStateOpen::PerformDataAwareStateChange(CRfcommSAP& aSAP)
+ {
+ if(aSAP.iNewDataToNotify)
+ {
+ // We have outstanding data, so move to a half-closed state
+ // where stuff can be read out but not sent
+ ChangeState(aSAP, CRfcommStateFactory::EDisconnecting);
+ }
+ else
+ {
+ // Now call DisConnect(). Eventually this sap will be cleared up.
+ ChangeState(aSAP, CRfcommStateFactory::EClosed);
+ aSAP.iSocket->Disconnect();
+ }
+ }
+
+
+void TRfcommStateOpen::Exit(CRfcommSAP& aSAP)
+ /**
+ Exit the open channel
+ **/
+ {
+ aSAP.DeregisterCodService(); // See if there is a Service to remove for CodMan
+ }
+
+
+void TRfcommStateOpen::Shutdown(CRfcommSAP& aSAP)
+ /**
+ Shutdown the open channel gracefully
+ **/
+ {
+ aSAP.iProtocol.ControlPlane().ModifyPhysicalLink(EOverridePark, aSAP.iRemoteDev);
+ aSAP.iClosePending=ETrue;
+ ChangeState(aSAP, CRfcommStateFactory::EDisconnect);
+ }
+
+void TRfcommStateOpen::FastShutdown(CRfcommSAP& aSAP)
+ /**
+ Terminate the session with extreme prejudice :-)
+
+ Send a disconnect, but don't bother to wait around since we'll
+ be deleted by our owner when this call returns.
+ **/
+ {
+ // Send a DISC using the DLCI based overload which ensures that
+ // the queued DISC frame does not get deleted when the SAP gets
+ // removed from the muxer
+ __ASSERT_DEBUG(aSAP.iMux!=NULL,PanicInState(ERfcommNullMux));
+ aSAP.iProtocol.ControlPlane().ModifyPhysicalLink(EOverridePark, aSAP.iRemoteDev);
+ aSAP.iMux->SendDISC(aSAP.DLCI()); // Disassociated from the SAP
+ ChangeState(aSAP, CRfcommStateFactory::EClosed);
+ }
+
+TInt TRfcommStateOpen::Send(CRfcommSAP& aSAP, const TDesC8& aData)
+ /**
+ Attempt to send some data.
+
+ We fragment the data and issue writes to the mux. Keep going
+ until either the mux tells us to back off, or until all the
+ data has been sent.
+
+ We also obey our flow control setting, so we only send if we
+ are clear to send
+
+ Note that when we fail to send all the data, ESOCK will keep
+ calling us until we return zero before marking us as blocked.
+ Therefore we have to check if we're blocked before trying to
+ send anything.
+
+ @return Length of data actually sent
+ **/
+ {
+ __ASSERT_DEBUG(aSAP.iMux!=NULL,PanicInState(ERfcommNullMux));
+
+ TInt len=0; // Current amount we've sent.
+ TInt fraglen;
+ TInt sentlen=0;
+
+ if(!aSAP.SendBlocked())
+ {
+ if(aSAP.CTS()) //FIXME???? make general to accommodate CBFC as well
+ {
+ while(len < aData.Length())
+ {
+ //NB1 0 if !CBFC
+ //NB2 A successful 'Write' will update this value for next loop.
+ TUint8 credit = aSAP.Mux()->FlowStrategy()->WriteCredit(aSAP);
+ fraglen=Min(aData.Length()-len, STATIC_CAST(TInt, aSAP.UsableMTU(credit)));
+ if(aSAP.iMux->FlowStrategy()->AllowWrite(aSAP))
+ sentlen=aSAP.iMux->Write(aSAP, credit, aData.Mid(len,fraglen));
+ else
+ sentlen=0;
+ len+=sentlen;
+ if(sentlen != fraglen)
+ {
+ // We failed to send all the data we wanted to, so
+ // we're now blocked from sending, and must wait for a
+ // notification that we can send again.
+ aSAP.SendBlocked(aSAP.SendBlocked() | CRfcommSAP::EBlockedByMux);
+ break;
+ }
+ }
+ }
+ else
+ {
+ // We're not clear to send to the other end, so back off ESOCK
+ aSAP.SendBlocked(aSAP.SendBlocked() | CRfcommSAP::EBlockedByRemote);
+ }
+ }
+ return len;
+ }
+
+void TRfcommStateOpen::Read(CRfcommSAP& aSAP, TDesC8& aData)
+ /**
+ Read some data.
+
+ This is filled from the circular buffer of incoming data.
+ **/
+ {
+ __ASSERT_DEBUG(aSAP.DataBuffer().Count() >= aData.Length(),
+ PanicInState(ERfcommReadForTooMuchData));
+ __ASSERT_DEBUG(aSAP.iMux!=NULL,PanicInState(ERfcommNullMux));
+ LOG1(_L("RFCOMM: Read of %d bytes"), aData.Length());
+
+ aSAP.DataBuffer().Remove(const_cast<TUint8*>(aData.Ptr()), aData.Length());
+ //TRY_CBFC
+ aSAP.iMux->FlowStrategy()->UpdateRxFlowControlState(aSAP);
+ //FIXED_ME removed flow control code to flow strategy
+ }
+
+void TRfcommStateOpen::NewData(CRfcommSAP& aSAP, const TDesC8& aData)
+ /**
+ New data has come in.
+
+ Store it in a buffer, and signal the socket to get it.
+ **/
+ {
+ LOG1(_L("RFCOMM: New data length %d"), aData.Length());
+ __ASSERT_DEBUG(aSAP.iMux!=NULL,PanicInState(ERfcommNullMux));
+ __ASSERT_DEBUG(aData.Length() <= aSAP.iMux->GetMaxDataSize(), PanicInState(ERfcommDataPacketTooLong));
+
+ if(!aSAP.iReceivedMSC)
+ {
+ // We're dealing with a rather naughty non-7.10-compliant
+ // RFCOMM implementation which doesn't send an MSC before
+ // sending it's data. Note this fact and continue.
+ LOG(_L("RFCOMM: Warning received data before MSC on new connection!"));
+ }
+ //TRY_CBFC
+ if (!aSAP.iMux->FlowStrategy()->NewDataReviseCredits(aSAP, aData)) // ...the remote has overflowed us...
+ {
+ // remote is ignoring flow control decide whether to disconnect or drop packets (if using CBFC)
+ if (aSAP.iMux->FlowStrategy()->FlowType() == CRfcommFlowStrategyFactory::EFlowCreditBased &&
+ aSAP.iForgiveCBFCOverflow)
+ {
+ //drop packet - l2cap frees the underlying packet storage
+ return;
+ }
+ else
+ {
+ aSAP.iSocket->Disconnect();
+ ChangeState(aSAP, CRfcommStateFactory::EClosed);
+ return;
+ }
+ }
+
+ TInt storelen=aSAP.DataBuffer().Add(aData.Ptr(), aData.Length());
+ if(storelen != aData.Length())
+ {
+ // We've not got room for some of the data
+ LOG(_L("RFCOMM: Warning received data loss"));
+ }
+
+ // Check to see if we need to quench the source
+ LOG1(_L("RFCOMM: Data buffer level %d"), aSAP.DataBuffer().Count());
+
+ aSAP.iMux->FlowStrategy()->NewData(aSAP);
+ //FIXED-ME flow code block removed to strategy object (applies non CBFC flow only).
+
+ if(storelen)
+ {
+ aSAP.iNewDataToNotify += storelen;
+ aSAP.iNotifyNewDataCallback->CallBack();
+ }
+ }
+
+void TRfcommStateOpen::NotifyNewDataCallback(CRfcommSAP& aSAP)
+ {
+ TInt newData = aSAP.iNewDataToNotify;
+ if (newData)
+ {
+ aSAP.iNewDataToNotify = 0;
+ LOG1(_L("RFCOMM: Actually telling Socket of newdata %d bytes"), newData);
+ aSAP.iSocket->NewData(newData);
+ }
+ aSAP.iMux->FlowStrategy()->UpdateRxFlowControlState(aSAP);
+ }
+
+void TRfcommStateOpen::CanSend(CRfcommSAP& aSAP)
+ /**
+ SAP is now clear to try to send data again (maybe) since these
+ can be sent any time.
+
+ Notify the socket.
+ **/
+ {
+
+ __ASSERT_DEBUG(aSAP.SendBlocked() & CRfcommSAP::EBlockedByMux, PanicInState(ERfcommUnexpectedCanSend));
+
+ aSAP.SendBlocked(aSAP.SendBlocked() & ~CRfcommSAP::EBlockedByMux);
+ aSAP.iSocket->CanSend();
+ }
+
+void TRfcommStateOpen::MSC(CRfcommSAP& aSAP, TUint8 aSignals)
+/**
+ Modem signals received from the remote end.
+ The only bit we're really interested in is the FC bit but we store
+ the rest anyway in case anyone above wants to read it (see GetOpt)
+**/
+ {
+ aSAP.iRemoteModemStatus = aSignals;
+
+ aSAP.iReceivedMSC=ETrue;
+
+ //FC
+ if(aSAP.iMux->FlowStrategy()->MSC(aSAP, aSignals))
+ {
+ aSAP.SendBlocked(aSAP.SendBlocked() & ~CRfcommSAP::EBlockedByRemote);
+ aSAP.iSocket->CanSend();
+ }
+ //
+
+ // Indication of config change
+ IoctlComplete(aSAP, KErrNone, KSolBtRFCOMM, KRFCOMMConfigChangeIndicationIoctl, NULL);
+
+ // If there has been a signal status to watch for set, and this is it, then we send a disconnect error to the client reader
+ if(aSAP.iDisconnectMSCSignal && aSAP.iDisconnectMSCSignal == aSignals)
+ {
+ SignalError(aSAP, KErrDisconnected, MSocketNotify::EErrorRecv);
+ }
+ }
+
+void TRfcommStateOpen::RLS(CRfcommSAP& aSAP, TUint8 aStatus)
+// Remote Line Status command received from remote end
+// Just set the SAP's local copy
+ {
+ aSAP.iRLSstatus = aStatus;
+ // Indication of config change
+ IoctlComplete(aSAP, KErrNone, KSolBtRFCOMM, KRFCOMMConfigChangeIndicationIoctl, NULL);
+ }
+
+
+void TRfcommStateOpen::DISC(CRfcommSAP& aSAP)
+ /**
+ The remote end has sent a DISC. We need to respond.
+
+ Q: What do we do about any data in our buffers?
+ A: Q the UA in order, so that all data in our buffers is
+ flushed before the UA is sent.
+ **/
+ {
+ // Use the DLCI based overload of SendUA, which ensures that the queued
+ // frame has no SAP set.
+ // This ensures that the frame does not get deleted when this SAP is detached
+ // from the muxer, when we enter the Closed state.
+ __ASSERT_DEBUG(aSAP.iMux!=NULL,PanicInState(ERfcommNullMux));
+ aSAP.iMux->SendUA(aSAP.DLCI()); // If this fails there's not much we can do
+ PerformDataAwareStateChange(aSAP);
+ }
+
+
+void TRfcommStateOpen::DM(CRfcommSAP& aSAP)
+ /**
+ A DM, which means the other end has gone away.
+
+ Time to error everything.
+ **/
+ {
+ SignalError(aSAP, KErrDisconnected, MSocketNotify::EErrorAllOperations);
+ }
+
+void TRfcommStateOpen::RPN(CRfcommSAP& aSAP, const TRfcommRPNTransaction& aRPNTransaction, CRfcommMuxer& /*aMuxer*/, TUint8 /*aDLCI*/)
+// The remote end has sent us an RPN negotiate command
+// Just accept whatever they suggested then tell them we've accepted
+ {
+ // Write whatever's changed into our local port params.
+ aSAP.iLocalPortParams.UpdateFromRPNTransaction(aRPNTransaction);
+
+ // Send L2CAP exactly what they sent us, except this is a response
+ aSAP.iMux->SendRPN(aSAP, EFalse, KRPNResponseLength, aRPNTransaction);
+
+ // Indication of config change
+ IoctlComplete(aSAP, KErrNone, KSolBtRFCOMM, KRFCOMMConfigChangeIndicationIoctl, NULL);
+ }
+
+void TRfcommStateOpen::RPNRsp(CRfcommSAP& aSAP,
+ const TRfcommRPNTransaction& aRPNTransaction)
+// The remote end has sent us an RPN response
+ {
+ // Make sure this is the response we are waiting for
+ switch(aSAP.iIoctlName)
+ {
+ case KRFCOMMRemotePortNegCmdIoctl:
+ LOG1(_L("RFCOMM: Received Negotiation response for sap %08x"), &aSAP);
+ // Update the local port parameters with what was agreed by the remote end.
+ aSAP.iLocalPortParams.UpdateFromRPNTransaction(aRPNTransaction);
+ break;
+ case KRFCOMMRemotePortNegRequestIoctl:
+ LOG1(_L("RFCOMM: Received Query response for sap %08x"), &aSAP);
+ break;
+ default:
+ __DEBUGGER();
+ LOG(_L("Unexpected RPN response\n"));
+ return;
+ }
+
+ // Convert the RPN transaction structure into a descriptor
+ TPckgC<TRfcommRPNTransaction> RPNTransaction(aRPNTransaction);
+
+ // Send the package up to the user
+ IoctlComplete(aSAP, KErrNone, KSolBtRFCOMM, aSAP.iIoctlName, &RPNTransaction);
+ }
+
+void TRfcommStateOpen::PN(CRfcommSAP& aSAP, TRfcommPortParams& /*aParams*/, CRfcommMuxer& /*aMux*/, TUint8 /*aDLCI*/)
+ /**
+ We received a PN in the open state - no problem, but for simplicity
+ we just respond with our current settings (i.e. MTU).
+ NB. Importantly, the initial credit field should be set to 0...
+ **/
+ {
+ TRfcommPortParams params;
+ params.iMaxFrameSize=aSAP.NegotiatedMTU();
+ params.iInitialCredit=0; // See F:1 5.5.3 Core spec 1.1
+ aSAP.iMux->SendPNResponse(aSAP, params);
+ }
+
+void TRfcommStateOpen::LinkDown(CRfcommSAP& aSAP)
+ /**
+ The link has gone down, which means our Mux will be deleting itself.
+
+ Go to closed state, which removes us from the mux
+ **/
+ {
+ PerformDataAwareStateChange(aSAP);
+ }
+
+
+void TRfcommStateOpen::Ioctl(CRfcommSAP& aSAP,TUint aLevel,TUint aName,TDes8* aOption)
+ /**
+ Perform an RFCOMM ioctl that applies to an RFCOMM connection.
+
+ If it is not valid, an error occurs.
+ **/
+ {
+ TInt ret = KErrNone;
+
+ switch(aName)
+ {
+ case KRFCOMMModemStatusCmdIoctl:
+ {
+ // aOption stores modem status
+ if(!aOption)
+ {
+ ret=KErrArgument;
+ }
+ else
+ {
+ TUint8 flags = TUint8(*aOption->Ptr());
+ // Make sure we send our real flow control status,
+ // not just whatever was in the data received from the user
+ if(aSAP.CTR())
+ {
+ flags &= ~KModemSignalFC;
+ }
+ else
+ {
+ flags |= KModemSignalFC;
+ }
+
+ aSAP.SetSignals(flags);
+ ret = aSAP.iMux->SendMSC(aSAP, flags);
+ }
+ }
+ // Complete Ioctl with what was sent to us
+ IoctlComplete(aSAP, ret, aLevel, aName, aOption);
+ break;
+
+ case KRFCOMMRemoteLineStatusCmdIoctl:
+ // aOption stores status of remote line
+ ret = aOption ? aSAP.iMux->SendRLS(aSAP, TUint8(*aOption->Ptr())) : KErrArgument;
+ // Complete Ioctl with what was sent to us
+ IoctlComplete(aSAP, ret, aLevel, aName, aOption);
+ break;
+
+ case KRFCOMMRemotePortNegCmdIoctl:
+ LOG1(_L("RFCOMM: Sending RPN negotiate for sap %08x"),
+ &aSAP);
+ // aOption is really an RPN transaction structure
+ ret = aOption ? aSAP.iMux->SendRPN(aSAP, ETrue, KRPNCommandLength,
+ *(TRfcommRPNTransaction*)aOption->Ptr()) : KErrArgument;
+ break;
+
+ case KRFCOMMRemotePortNegRequestIoctl:
+ LOG1(_L("RFCOMM: Sending RPN query for sap %08x"),
+ &aSAP);
+ // aOption is really an RPN transaction structure
+ ret = aOption ? aSAP.iMux->SendRPN(aSAP, ETrue, KRPNRequestLength,
+ *(TRfcommRPNTransaction*)aOption->Ptr()) : KErrArgument;
+ break;
+
+ case KRFCOMMFConIoctl:
+ LOG2(_L("RFCOMM: Sending FCon command for device 0x%04x%08x"),
+ TUint(TUint16((aSAP.iMux->RemoteBTAddr())[0])),
+ TUint((aSAP.iMux->RemoteBTAddr())[2]));
+ //FC
+ ret=aSAP.iMux->SendFCon();
+ //
+ IoctlComplete(aSAP, ret, aLevel, aName, aOption);
+ break;
+
+ case KRFCOMMFCoffIoctl:
+ LOG2(_L("RFCOMM: Sending FCoff command for device 0x%04x%08x"),
+ TUint(TUint16((aSAP.iMux->RemoteBTAddr())[0])),
+ TUint((aSAP.iMux->RemoteBTAddr())[2]));
+ //FC
+ ret=aSAP.iMux->SendFCoff();
+ //
+ IoctlComplete(aSAP, ret, aLevel, aName, aOption);
+ break;
+
+ case KRFCOMMConfigChangeIndicationIoctl:
+ LOG(_L("RFCOMM: Registering KRFCOMMConfigChangeIndicationIoctl"));
+ // Do nothing -- async completion on remote MSC, RPN, RLS
+ break;
+
+ default:
+ ret = KErrNotSupported;
+ }
+
+ if(ret != KErrNone)
+ {
+ aSAP.iIoctlName=0;
+ aSAP.iIoctlLevel=0;
+ SignalError(aSAP, ret, MSocketNotify::EErrorIoctl);
+ }
+ }
+
+void TRfcommStateOpen::IoctlComplete(CRfcommSAP& aSAP, TInt aErr, TUint aLevel,TUint aName, TDesC8* aBuf)
+ /**
+ An ioctl has completed.
+
+ If it's for us, signal the socket
+ **/
+ {
+ if (aSAP.iIoctlLevel==aLevel &&
+ aSAP.iIoctlName ==aName)
+ {
+ if(aErr == KErrNone)
+ {
+ aSAP.iSocket->IoctlComplete(aBuf);
+ }
+ else
+ {
+ SignalError(aSAP, aErr, MSocketNotify::EErrorIoctl);
+ }
+ aSAP.iIoctlName=0;
+ aSAP.iIoctlLevel=0;
+ }
+ }
+
+TInt TRfcommStateOpen::SetOption(CRfcommSAP &aSAP, TUint aLevel,
+ TUint aName, const TDesC8& aOption)
+ /**
+ Allow modem status to be modified whilst open.
+ **/
+ {
+ TInt ret;
+ if(aLevel == KSolBtRFCOMM && aName == KRFCOMMLocalModemStatus)
+ {
+ if ( aOption.Length() != sizeof(TUint8) )
+ {
+ ret = KErrArgument;
+ }
+ else
+ {
+ TUint8 flags = TUint8(*aOption.Ptr());
+ if(aSAP.CTR())
+ {
+ flags &= ~KModemSignalFC;
+ }
+ else
+ {
+ flags |= KModemSignalFC;
+ }
+
+ ret = aSAP.iMux->SendMSC(aSAP, flags);
+ if(ret==KErrNone)
+ {
+ // Oddly, ioctl KRFCOMMModemStatusCmdIoctl updates
+ // local signals regardless of an error sending them
+ aSAP.SetSignals(flags);
+ }
+ }
+ }
+ else
+ {
+ ret = TRfcommStateDefault::SetOption(aSAP, aLevel, aName, aOption);
+ }
+
+
+ return ret;
+ }
+
+
+//TRY_CBFC
+TUint8 TRfcommStateOpen::FreeCredit(CRfcommSAP& aSAP)
+ {
+ TUint8 creds = aSAP.FreeCreditCalculation();
+ return creds;
+ }
+
+TInt TRfcommStateOpen::ProxyForRemoteCredit(const CRfcommSAP& aSAP) const
+ {
+ return aSAP.iProxyForRemoteCredit;
+ }
+
+TInt TRfcommStateOpen::LocalCredit(const CRfcommSAP& aSAP) const
+ {
+ return aSAP.iLocalCredit;
+ }
+
+
+
+/*********************************************************************/
+/*
+ Disconnect state
+*/
+
+TRfcommStateDisconnect::TRfcommStateDisconnect(CRfcommStateFactory& aFactory)
+ : TRfcommStateDefault(aFactory)
+ {
+ STATENAME("Disconnect");
+ }
+
+void TRfcommStateDisconnect::Enter(CRfcommSAP& aSAP)
+ /**
+ Send out a disconnect via the mux
+ **/
+ {
+ __ASSERT_DEBUG(aSAP.iMux!=NULL,PanicInState(ERfcommNullMux));
+ // Need to kill off any data callback since we don't want it
+ aSAP.iNotifyNewDataCallback->Cancel();
+ aSAP.iNewDataToNotify = 0;
+ aSAP.iDataBuffer.Reset();
+ aSAP.iMux->SendDISC(aSAP);
+ }
+
+void TRfcommStateDisconnect::DISC(CRfcommSAP& aSAP)
+ /**
+ We're attempting to DISC, and apparently so is our peer SAP.
+
+ Send them a UA, and move to the closed state.
+ **/
+ {
+ aSAP.iMux->SendUA(aSAP.DLCI()); // Send to DLCI, or we may lose this when the SAP gets deleted.
+ DisconnectComplete(aSAP);
+ }
+
+void TRfcommStateDisconnect::UA(CRfcommSAP& aSAP)
+ /**
+ An answer to our DISC.
+
+ Disconnect from the Mux now, so it can go away if needs be.
+ **/
+ {
+ DisconnectComplete(aSAP);
+ }
+
+void TRfcommStateDisconnect::DM(CRfcommSAP& aSAP)
+ /**
+ Strange, the remote end has sent an error for our disconnect.
+ Oh well, better close anyway.
+ **/
+ {
+ DisconnectComplete(aSAP);
+ }
+
+void TRfcommStateDisconnect::DisconnectComplete(CRfcommSAP& aSAP)
+ {
+ // Record whether there is a CanClose pending - entering the closed
+ // state will overwrite this data.
+ TBool canCloseRequired=aSAP.iClosePending;
+
+ ChangeState(aSAP, CRfcommStateFactory::EClosed); // Must enter before CanClose, since
+ // this may delete us
+ if(canCloseRequired)
+ aSAP.iSocket->CanClose();
+ }
+
+void TRfcommStateDisconnect::LinkDown(CRfcommSAP& aSAP)
+ /**
+ The link has gone down during our close.
+ **/
+ {
+ ChangeState(aSAP, CRfcommStateFactory::EClosed); // Enter before we get deleted...
+ aSAP.iSocket->CanClose();
+ }
+
+/***************************************************************
+
+The disconnecting state
+
+***************************************************************/
+
+
+TRfcommStateDisconnecting::TRfcommStateDisconnecting(CRfcommStateFactory& aFactory)
+ : TRfcommStateDefault(aFactory)
+ {
+ STATENAME("Disconnecting");
+ }
+
+
+void TRfcommStateDisconnecting::Enter(CRfcommSAP& aSAP)
+ /**
+ Enter the disconnecting state.
+
+ This state is entered if we have data for the client, but the
+ other end has disconnected. Thus we are in a half-disconnected
+ state, where the remote end thinks we're closed and the local
+ end thinks we're open. Since we have data to notify, we'll send
+ it to ESOCK immediately.
+
+ We disconnect from the mux as we have no further need for
+ incoming events.
+ **/
+ {
+ __ASSERT_DEBUG(aSAP.iNewDataToNotify, PanicInState(ERfcommStateDisconnectingWithNoData));
+ __ASSERT_DEBUG(aSAP.iNotifyNewDataCallback->IsActive(), PanicInState(ERfcommStateDisconnectingWithNoData));
+// aSAP.iNotifyNewDataCallback->Cancel(); // Cancel the outstanding callback
+ aSAP.iMux->DetachSAP(aSAP);
+ TInt newData = aSAP.iNewDataToNotify;
+ if (newData)
+ {
+ aSAP.iNewDataToNotify = 0;
+ aSAP.iSocket->NewData(newData);
+ }
+ }
+
+TInt TRfcommStateDisconnecting::Send(CRfcommSAP& aSAP, const TDesC8& /*aData*/)
+ /**
+ Attempt to send data, but we can't.
+
+ We will signal an error on the send.
+ **/
+ {
+ aSAP.iErrorKicker->SetError(KErrDisconnected, CRfcommSAP::EErrorOperation);
+ aSAP.iErrorKicker->Call(); // FIXME: Note this currently errors everything
+ return 0;
+ }
+
+void TRfcommStateDisconnecting::Read(CRfcommSAP& aSAP, TDesC8& aData)
+ /**
+ Read out some of the data from the buffer.
+
+ Note that there is no need to deal with flow control, as the
+ link is dead. Also, there is no need to deal with notifying
+ the mux for more data, since we assume that only data that is
+ notified to us before the DISC is valid.
+ **/
+ {
+ __ASSERT_DEBUG(aSAP.DataBuffer().Count() >= aData.Length(),
+ PanicInState(ERfcommReadForTooMuchData));
+ LOG1(_L("RFCOMM: Read of (disconnecting) %d bytes"), aData.Length());
+
+ aSAP.DataBuffer().Remove(const_cast<TUint8*>(aData.Ptr()), aData.Length());
+ }
+
+void TRfcommStateDisconnecting::NotifyNewDataCallback(CRfcommSAP& aSAP)
+/**
+ Method name is a bit dud in this state
+ We notify the socket that it is Disconnected
+ Provides better behaviour for the MSocketNotify - it doesn't end up
+ with a Disconnect upcall whilst calling Read
+**/
+ {
+ __ASSERT_DEBUG(aSAP.iNewDataToNotify == 0, PanicInState(ERfcommStateClosedWithData));
+
+ LOG(_L("SAP has no more data - socket now Disconnected"));
+
+ aSAP.iSocket->Disconnect();
+ ChangeState(aSAP, CRfcommStateFactory::EClosed);
+ }
+
+void TRfcommStateDisconnecting::Ioctl(CRfcommSAP& aSAP,TUint /*aLevel*/,TUint /*aName*/,TDes8* /*aOption*/)
+ /**
+ An ioctl has been requested while we are in state disconnected.
+
+ We fail all ioctls in this state.
+ **/
+ {
+ __ASSERT_DEBUG(aSAP.iIoctlLevel == 0, PanicInState(ERfcommSAPTwoIoctls));
+ __ASSERT_DEBUG(aSAP.iIoctlName == 0, PanicInState(ERfcommSAPTwoIoctls));
+
+ SignalError(aSAP, KErrDisconnected, MSocketNotify::EErrorIoctl);
+ }
+
+void TRfcommStateDisconnecting::Shutdown(CRfcommSAP& aSAP)
+ /**
+ We've been asked to close this side.
+
+ Since the other end has already gone away, there's no need to
+ send anything, we can go straight to closed.
+ **/
+ {
+ ChangeState(aSAP, CRfcommStateFactory::EClosed); // Must enter before CanClose, since
+ // this may delete us
+ aSAP.iSocket->CanClose();
+ }
+
+void TRfcommStateDisconnecting::FastShutdown(CRfcommSAP& aSAP)
+ /**
+ We've been asked to close this side with extreme prejudice.
+
+ Since the other end has already gone away, there's no need to
+ send anything, we can go straight to closed. Note we will be
+ deleted when this returns.
+ **/
+ {
+ ChangeState(aSAP, CRfcommStateFactory::EClosed);
+ }