diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/rfcomm/rfcommstates.cpp --- /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 +#include +#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 maxMTU; + maxMTU.Copy(aOption); + aSAP.iUserDefinedMTU = maxMTU(); + } + }; + break; + + case KRFCOMMFlowTypeCBFC: + { + if ( aOption.Length() != sizeof(TBool) ) + { + return KErrArgument; + } + else + { + TPckgBuf 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 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(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(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 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 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 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 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(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 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(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); + }