diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/linkmgr/SyncSap.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/linkmgr/SyncSap.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,1048 @@ +// Copyright (c) 2003-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: +// Implements the Synchronous SAP class +// +// + +#include +#include +#include "SCOSAP.h" +#include "physicallinksmanager.h" +#include "physicallinks.h" +#include "hcifacade.h" +#include "linkutil.h" +#include "linkmuxer.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR); +#endif + + +CBTSynchronousLink::CBTSynchronousLink(CPhysicalLinksManager& aLinksMan, CPhysicalLink* aPhysicalLink, TLinkType aLinkType) +: CBTBasebandSAP(aLinksMan, aPhysicalLink), iLinkType(aLinkType) + { + LOG2(_L("Creating sync SAP 0x%08x, type %d"), this, aLinkType); + iState = &aLinksMan.LinkManagerProtocol(). + SyncStateFactory().GetState(CSyncLinkStateFactory::EClosed); + } + +void CBTSynchronousLink::ConstructL() + { + CBTBasebandSAP::ConstructL(); + } + +CBTSynchronousLink::~CBTSynchronousLink() + { + LOG2(_L("Destroying sync SAP 0x%08x, iPhysicalLink 0x%08x"), this, iPhysicalLink); + + /* + The ASSERT below has been commented due to an issue with ESock. + + During a normal shutdown, a CBTSynchronousLink will always unbind from the physical link before + calling CanClose(). However automated tests have shown that there is no guarantee that during a + normal shutdown ESock will wait until the SAP has called CanClose() before deleting it. If + the socket is closed whilst ESock is waiting for the CanClose() it will immediately delete the + SAP (and complete the Shutdown request with KErrCancel). In this case the SAP will still be + bound to a physical link. + + If the ESock issue is rectified, the ASSERT should be reinstated. + + + ASSERT(!iPhysicalLink); + */ + + iState->Deletion(*this); + + ReleaseAndUnbind(); + } + +void CBTSynchronousLink::ReleaseAndUnbind() +/** + NB If the physical link is unbound, and the SCO packet type is + already ENoSCOLink, this function will do nothing! +*/ + { + LOG1(_L("CBTSyncLink (0x%08x): Release and unbind"), this); + + if(iPhysicalLink) + { + iPhysicalLink->UndoOverridePark(); + } + UnbindLink(iLinkType); //sets iPhysicalLink to NULL + } + +void CBTSynchronousLink::PhysicalLinkChange(const TBTBasebandEventNotification& IF_FLOGGING(aEvent), CPhysicalLink& __DEBUG_ONLY(aPhysicalLink)) + { + __ASSERT_DEBUG(&aPhysicalLink == iPhysicalLink, Panic(EBTSCOSAPWrongPhysicalLink)); + + // spec says should never have got parked with sco link + //__ASSERT_DEBUG(!(aEvent.EventType() & ENotifyParkMode), Panic(EBTSCOSAPExistsWhenParking)); + // + // Above ASSERT too strong as races are occuring. Log, but 'ignore'. + + #ifdef __FLOG_ACTIVE + if (!(aEvent.EventType() & ENotifyParkMode)) + { + LOG(_L("Sync SAP: Park request received with an active eSCO link. Likely to be a race.")); + } + #endif + } + +void CBTSynchronousLink::AutoBind() + /** + Pick an appropriate local port. + **/ + { + } + +TInt CBTSynchronousLink::SetLocalName(TSockAddr& /*aAddr*/) +/** + Override KErrNotSupported in base class. +*/ + { + return KErrNone; + } + +void CBTSynchronousLink::Start() + { + iState->Start(*this); + } + +void CBTSynchronousLink::Timeout(TBasebandTimeout aTimeout) + { + iState->Timeout(*this, aTimeout); + } + +TInt CBTSynchronousLink::SetRemName(TSockAddr& aAddr) + { + TBTSockAddr bbAddr(aAddr); // convert + iRemoteDev=bbAddr.BTAddr(); + + return KErrNone; // to avoid race conditions we check for ACL later + } + +void CBTSynchronousLink::ActiveOpen() +/** + Attach SCO link - the packet type should have been set by this stage +**/ + { + iState->ActiveOpen(*this); + } + +void CBTSynchronousLink::DoActiveOpenL() + { + // proceed with creating the SCO link, but first check that phy exists + CPhysicalLink* foundPhysicalLink = iLinksMan.FindPhysicalLink(iRemoteDev); + + if (!foundPhysicalLink) + { + // cannot do this until baseband is up...SCO Transports aren't allowed to instantiate + // PHYs + User::Leave(KErrNotReady); + } + else + { + if (foundPhysicalLink->HasSyncLink()) + { + User::Leave(KErrInUse); + } + + // go and actually make the link + if(!IsLinkProbablyPossible()) + { + User::Leave(KErrOverflow); + } + foundPhysicalLink->OverridePark(); + BindLink(iLinkType, *foundPhysicalLink); + + //Initiate a SCO connection with user specified packet types. all types will be allowed + //if SetOption has not been called. + TInt err = MakeConnection(foundPhysicalLink); + if (err) + { + UnbindLink(iLinkType); + User::Leave(err); + } + } + } + +TInt CBTSynchronousLink::PassiveOpen(TUint aQueSize) +/** + Record ourselves as willing to accept a SCO link (possibly of the CoD we want) +**/ + { + LOG1(_L("CBTSyncLink (0x%08x): PassiveOpen"), this); + return iState->PassiveOpen(*this, aQueSize); + } + +TUint CBTSynchronousLink::Write(const TDesC8& aData, TUint aOptions, TSockAddr* aAddr) + { + return iState->Write(*this, aData, aOptions, aAddr); + } + +void CBTSynchronousLink::PacketsSent(THCIConnHandle /*aHandle*/, TUint16 /*aNumPackets*/) + { + // ignore - no need to unblock socket + } + +TBool CBTSynchronousLink::ConnectRequest(const TBTConnect& aSCOLink, const CPhysicalLink& aPhysicalLink) + { + return iState->ConnectRequest(*this, aSCOLink, aPhysicalLink); + } + +void CBTSynchronousLink::GetData(TDes8& aDesc,TUint /*aOptions*/,TSockAddr* /*aAddr*/) + { + // return the packet - we only have one - if other stuff has got overwritten + // so be it - we're going for synchronicity... + aDesc = InboundFrame(); + } + +void CBTSynchronousLink::ConnectComplete(const TBTConnect& aSCOLink) + { + TBTSyncConnectOpts SCODefaultOpts(KSCODefaultTransmissionInterval, KSCODefaultRetransmissionWindow, + KSCODefaultRxPacketLength, KSCODefaultTxPacketLength, KSCODefaultAirMode); + + SyncConnectComplete(aSCOLink, SCODefaultOpts); + } + +void CBTSynchronousLink::SyncConnectComplete(const TBTConnect& aSCOLink, const TBTSyncConnectOpts& aSyncOpts) + { + iState->ConnectComplete(*this, aSCOLink, aSyncOpts); + } + +void CBTSynchronousLink::Disconnection() + { + LOG2(_L("CBTSyncLink (0x%08x): disconnection on handle %d"), this, iHandle); + iState->Disconnection(*this); + } + +void CBTSynchronousLink::ParentClosing() + { + iState->ParentClosing(*this); + } + +void CBTSynchronousLink::DataReceived(THCIConnHandle __DEBUG_ONLY(aConnH), TUint8 /*aIgnore*/, const TDesC8& aData) + { + __ASSERT_DEBUG(aConnH == Handle(), Panic(EBTSCOSAPWrongSCOSAP)); + iState->DataReceived(*this, aData); + } + +void CBTSynchronousLink::Error(TInt aErr) + { + iState->Error(*this, aErr); + } + +void CBTSynchronousLink::Shutdown(TCloseType aClose) +/** + Detach SCO +**/ + { + LOG2(_L("CBTSyncLink (0x%08x): shutdown no data on handle %d"), this, iHandle); + iState->Shutdown(*this, aClose); + } + +void CBTSynchronousLink::Shutdown(TCloseType aClose, const TDesC8& /*aDisconnectionData*/) +/** + Detach SCO - ESock seems to send us through this way + whatever the client request has been. The overload above probably does + not need implementing. +**/ + { + LOG2(_L("CBTSyncLink (0x%08x): shutdown on handle %d"), this, iHandle); + iState->Shutdown(*this, aClose); + } + +TBool CBTSynchronousLink::IsIdle() const + { + return iState->IsIdle(); + } + +TBool CBTSynchronousLink::IsOpen() const + { + return iState->IsOpen(); + } + +TInt CBTSynchronousLink::CommonShutdown() + { + // not common to all baseband SAPs - only for closing SCO links at moment + // since PhysicalLink object looks after the PHY, and ACL goes up/down with that + TRAPD(err, iLinksMan.HCIFacade().DisconnectL(Handle(), static_cast(KActiveDisconnectReason))); + return err; + } + +TBool CBTSynchronousLink::ConnectPending() const + { + return iState->IsConnectPending(); + } + + + +//---------------------------------------------------------------------------------- +// STATE FACTORY +//---------------------------------------------------------------------------------- + +CSyncLinkStateFactory* CSyncLinkStateFactory::NewL() + { + CSyncLinkStateFactory* ret=new (ELeave) CSyncLinkStateFactory(); + CleanupStack::PushL(ret); + ret->ConstructL(); + CleanupStack::Pop(); + return ret; + } + +void CSyncLinkStateFactory::ConstructL() + { + iStates[EClosed] =new (ELeave) TSCOLinkStateClosed(*this); + iStates[EWaitForSCO] =new (ELeave) TSCOLinkStateWaitForSCO(*this); + iStates[EListening] =new (ELeave) TSCOLinkStateListening(*this); + iStates[EAccepting] =new (ELeave) TSCOLinkStateAccepting(*this); + iStates[EWaitForStart] =new (ELeave) TSCOLinkStateWaitForStart(*this); + iStates[EWaitForStartError] =new (ELeave) TSCOLinkStateWaitForStartError(*this); + iStates[EOpen] =new (ELeave) TSCOLinkStateOpen(*this); + iStates[EClosing] =new (ELeave) TSCOLinkStateClosing(*this); + } + +CSyncLinkStateFactory::~CSyncLinkStateFactory() + { + iStates.DeleteAll(); + } + +TSyncLinkState& CSyncLinkStateFactory::GetState(CSyncLinkStateFactory::TSyncLinkStates aState) + { + __ASSERT_DEBUG(iStates[aState], Panic(ELinkMgrBadSCOState)); + return *iStates[aState]; + } + +TInt CSyncLinkStateFactory::StateIndex(const TSyncLinkState* aState) const + { + TInt state; + for (state = 0; state < ESyncLinkMaxState; state++) + { + if (iStates[state] == aState) + { + return state; + } + } + + return KUnknownState; + } + + + + +//---------------------------------------------------------------------------------- +// STATES +//---------------------------------------------------------------------------------- + +TSyncLinkState::TSyncLinkState(CSyncLinkStateFactory& aFactory) +: iFactory(aFactory) + { + } + +void TSyncLinkState::PanicInState(TLinkPanic aPanic) const + { + Panic(aPanic, iFactory.StateIndex(this)); + } + +void TSyncLinkState::ChangeState(CBTSynchronousLink& aContext, CSyncLinkStateFactory::TSyncLinkStates aState) const + { + aContext.iState->Exit(aContext); + +#ifdef __FLOG_ACTIVE + TSyncLinkState* state=&iFactory.GetState(aState); + LOG2(_L("SCOLink: State %S -> %S"), &aContext.iState->iName, &state->iName); +#endif //__FLOG_ACTIVE + aContext.iState=&iFactory.GetState(aState); + + aContext.iState->Enter(aContext); + } + +void TSyncLinkState::Enter(CBTSynchronousLink& /*aContext*/) const + { + // do nothing + } + +void TSyncLinkState::Deletion(CBTSynchronousLink& /*aContext*/) const + { + PanicInState(EBTSCOSAPUnexpectedEvent); + } + +void TSyncLinkState::Exit(CBTSynchronousLink& /*aContext*/) const + { + // do nothing + } + +void TSyncLinkState::Timeout(CBTSynchronousLink& /*aContext*/, TBasebandTimeout /*aTimeout*/) const + { + PanicInState(EBTSCOSAPUnexpectedEvent); + } + +void TSyncLinkState::Shutdown(CBTSynchronousLink& /*aContext*/, CServProviderBase::TCloseType /*aCloseType*/) const + { + PanicInState(EBTSCOSAPUnexpectedEvent); + } + +TInt TSyncLinkState::PassiveOpen(CBTSynchronousLink& /*aContext*/, TUint /*aQueSize*/) const + { + PanicInState(EBTSCOSAPUnexpectedEvent); + return KErrNotSupported; + } + +void TSyncLinkState::ActiveOpen(CBTSynchronousLink& /*aContext*/) const + { + PanicInState(EBTSCOSAPUnexpectedEvent); + } + +TBool TSyncLinkState::ConnectRequest(CBTSynchronousLink& /*aContext*/, const TBTConnect& /*aSCOLink*/, const CPhysicalLink& /*aPhysicalLink*/) const + { +#ifdef _DEBUG + PanicInState(EBTSCOSAPUnexpectedEvent); //listener code could be suspect +#endif + return EFalse; + } + +void TSyncLinkState::Error(CBTSynchronousLink& /*aContext*/, TInt __DEBUG_ONLY(aErr)) const + { + // Don't propogate the error as listeners don't care if the hardware has gone down + __ASSERT_DEBUG(aErr==KErrHardwareNotAvailable||aErr==EHostSecurityRejection, PanicInState(EBTSCOSAPUnexpectedEvent)); + } + +void TSyncLinkState::ConnectComplete(CBTSynchronousLink& /*aContext*/, const TBTConnect& /*aConnect*/, const TBTSyncConnectOpts& /*aSyncOpts*/) const + { + // eat unexpected HW event + } + +void TSyncLinkState::Disconnection(CBTSynchronousLink& /*aContext*/) const + { + // eat unexpected HW event + } + +void TSyncLinkState::DataReceived(CBTSynchronousLink& /*aContext*/, const TDesC8& /*aData*/) const + { + // dump data + } + +void TSyncLinkState::PacketTypeChange(CBTSynchronousLink& /*aContext*/, THCIErrorCode /*aErr*/, THCIConnHandle /*aConnH*/, TUint16 /*aNewPacket*/) const + { + PanicInState(EBTSCOSAPUnexpectedEvent); + } + +void TSyncLinkState::Start(CBTSynchronousLink& /*aContext*/) const + { + PanicInState(EBTSCOSAPUnexpectedEvent); + } + +TUint TSyncLinkState::Write(CBTSynchronousLink& /*aContext*/, const TDesC8& /*aData*/, TUint /*aOptions*/, TSockAddr* /*aAddr*/) const + { + PanicInState(EBTSCOSAPUnexpectedEvent); + return 0; + } + +TBool TSyncLinkState::IsIdle() const + { + return EFalse; + } + +TBool TSyncLinkState::IsOpen() const + { + return EFalse; + } + +void TSyncLinkState::ParentClosing(CBTSynchronousLink& /*aContext*/) const + { + // Swallow the event. + } + +TBool TSyncLinkState::IsConnectPending() const + { + return EFalse; + } + +//---------------------------------------------------------------------------------- + +TSCOLinkStateClosed::TSCOLinkStateClosed(CSyncLinkStateFactory& aFactory) +: TSyncLinkState(aFactory) + { + STATENAME("TSCOLinkStateClosed"); + } + +void TSCOLinkStateClosed::Enter(CBTSynchronousLink& aContext) const + { + aContext.ReleaseAndUnbind(); + } + +void TSCOLinkStateClosed::Start(CBTSynchronousLink& /*aContext*/) const + { + //gulp + } + +void TSCOLinkStateClosed::ActiveOpen(CBTSynchronousLink& aContext) const + { + TRAPD(err, aContext.DoActiveOpenL()); + if (err) + { + aContext.Socket()->Error(err, MSocketNotify::EErrorConnect); + } + else + { + ChangeState(aContext, CSyncLinkStateFactory::EWaitForSCO); + } + } + +TBool TSCOLinkStateClosed::IsIdle() const + { + return ETrue; + } + + +TInt TSCOLinkStateClosed::PassiveOpen(CBTSynchronousLink& aContext, TUint /*aQueSize*/) const + { + // need to attach to the PHY + + TInt err = aContext.iLinksMan.AddListener(aContext, aContext.iLinkType); + + if (err == KErrNone) + { + ChangeState(aContext, CSyncLinkStateFactory::EListening); + } + + return err; + } + +void TSCOLinkStateClosed::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType aCloseType) const + { + LOG(_L("CBTSyncLink: closed state shutdown, immediate")); + if (aCloseType == CServProviderBase::ENormal) + { + aContext.Socket()->CanClose(); // tell socket it's fine to go + } + } + + +void TSCOLinkStateClosed::Deletion(CBTSynchronousLink& /*aContext*/) const + { + // allowed + } + +void TSCOLinkStateClosed::Disconnection(CBTSynchronousLink& /*aContext*/) const + { + //discard + } + +//---------------------------------------------------------------------------------- + +TSCOLinkStateWaitForSCO::TSCOLinkStateWaitForSCO(CSyncLinkStateFactory& aFactory) +: TSyncLinkState(aFactory) + { + STATENAME("TSCOLinkStateWaitForSCO"); + } + + +void TSCOLinkStateWaitForSCO::ConnectComplete(CBTSynchronousLink& aContext, const TBTConnect& aSCOInfo, const TBTSyncConnectOpts& aSyncOpts) const + { + aContext.iHandle = aSCOInfo.iConnH; + aContext.iRemoteDev = aSCOInfo.iBdaddr; + + aContext.SetExtOptions(aSyncOpts); + + aContext.Socket()->ConnectComplete(); + + ChangeState(aContext, CSyncLinkStateFactory::EOpen); + } + +void TSCOLinkStateWaitForSCO::Disconnection(CBTSynchronousLink& aContext) const + { + // The SCO is not currently connected, therefore this + // must be a PHY disconnection event. A ConnectionComplete event + // should not be received now so enter the closed state. + aContext.Socket()->Error(ENoConnection, MSocketNotify::EErrorConnect); + ChangeState(aContext, CSyncLinkStateFactory::EClosed); + } + + +void TSCOLinkStateWaitForSCO::Error(CBTSynchronousLink& aContext, TInt aErr) const + { + // SCO failed + aContext.Socket()->Error(aErr, MSocketNotify::EErrorConnect); + ChangeState(aContext, CSyncLinkStateFactory::EClosed); + } + + +void TSCOLinkStateWaitForSCO::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType aCloseType) const + { + // just change to closing - we'll get notified of PhyUp, but that'll get swallowed + // and the connection will timeout and die. + if (aCloseType == CServProviderBase::ENormal) + { + LOG(_L("CBTSyncLink: wait for SCO state shutdown, move to closing")); + ChangeState(aContext, CSyncLinkStateFactory::EClosing); + } + else + { + LOG(_L("CBTSyncLink: wait for SCO state shutdown, die immediately")); + aContext.ReleaseAndUnbind(); + ChangeState(aContext, CSyncLinkStateFactory::EClosed); + } + } + +TBool TSCOLinkStateWaitForSCO::IsConnectPending() const + { + return ETrue; + } + + + + +//---------------------------------------------------------------------------------- + +TSCOLinkStateWaitForStart::TSCOLinkStateWaitForStart(CSyncLinkStateFactory& aFactory) +: TSyncLinkState(aFactory) + { + STATENAME("TSCOLinkStateWaitForStart"); + } + + +void TSCOLinkStateWaitForStart::Start(CBTSynchronousLink& aContext) const +/** + Called as a result of a socket "ConnectComplete" +*/ + { + aContext.ListeningSAP()->RemoveChild(&aContext); + aContext.ListeningSAP() = NULL; + ChangeState(aContext, CSyncLinkStateFactory::EOpen); + } + +void TSCOLinkStateWaitForStart::Error(CBTSynchronousLink& aContext, TInt /*aError*/) const + { + aContext.ReleaseAndUnbind(); + // no socket to notify of error until Start - transition state + ChangeState(aContext, CSyncLinkStateFactory::EWaitForStartError); + } + +void TSCOLinkStateWaitForStart::Disconnection(CBTSynchronousLink& aContext) const + { + // the open - but unaccepted - link has disappeared + Error(aContext, KErrDisconnected); + } + +void TSCOLinkStateWaitForStart::Deletion(CBTSynchronousLink& aContext) const + { + // allowed deletion - should be listening SAP that is deleting us from + // an unwanted connection + aContext.ReleaseAndUnbind(); + aContext.ListeningSAP()->RemoveChild(&aContext); + } + +void TSCOLinkStateWaitForStart::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType /*aCloseType*/) const + { + LOG(_L("CBTSyncLink: wait for start state shutdown, tell peer and die")); + TInt err = aContext.CommonShutdown(); // tell peer + if (err) + { + Error(aContext, KErrCouldNotDisconnect); + } + + aContext.ReleaseAndUnbind(); + + // just go + aContext.ListeningSAP()->DeleteChild(&aContext); + } + + +//---------------------------------------------------------------------------------- + +TSCOLinkStateWaitForStartError::TSCOLinkStateWaitForStartError(CSyncLinkStateFactory& aFactory) +: TSyncLinkState(aFactory) + { + STATENAME("TSCOLinkStateWaitForStartError"); + } + +void TSCOLinkStateWaitForStartError::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType aCloseType) const + { + LOG(_L("CBTSyncLink: wait for start error state shutdown, immediate")); + ChangeState(aContext, CSyncLinkStateFactory::EClosed); + if (aCloseType == CServProviderBase::ENormal) + { + aContext.Socket()->CanClose(); + } + } + +void TSCOLinkStateWaitForStartError::Start(CBTSynchronousLink& aContext) const + { + // summat went wrong - close and remove this acceptor + ChangeState(aContext, CSyncLinkStateFactory::EClosed); + aContext.Socket()->Disconnect(); + + aContext.ListeningSAP()->RemoveChild(&aContext); + } + +void TSCOLinkStateWaitForStartError::Deletion(CBTSynchronousLink& aContext) const + { + // allowed deletion - should be listening SAP that is deleting us from + // an unwanted connection + aContext.ReleaseAndUnbind(); + aContext.ListeningSAP()->RemoveChild(&aContext); + } + +//---------------------------------------------------------------------------------- + +TSCOLinkStateListening::TSCOLinkStateListening(CSyncLinkStateFactory& aFactory) +: TSyncLinkState(aFactory) + { + STATENAME("TSCOLinkStateListening"); + } + +void TSCOLinkStateListening::Deletion(CBTSynchronousLink& aContext) const + { + delete aContext.iChild; + } + +void TSCOLinkStateListening::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType aCloseType) const + { + LOG(_L("CBTSyncLink: listening state shutdown, immediate")); + if (aContext.iChild) + { + aContext.iChild->ParentClosing(); + } + + ChangeState(aContext, CSyncLinkStateFactory::EClosed); + if (aCloseType == CServProviderBase::ENormal) + { + aContext.Socket()->CanClose(); + } + } + +TBool TSCOLinkStateListening::ConnectRequest(CBTSynchronousLink& aContext, const TBTConnect& /*aSCOLink*/, const CPhysicalLink& aPhysicalLink) const + { + // only support one 'link' (HCI-wise!) at moment + TRAPD(err, SpawnL(aContext, const_cast(aPhysicalLink))); + return (err==KErrNone); + // don't transition state yet - connection may fail. + }; + + +void TSCOLinkStateListening::SpawnL(CBTSynchronousLink& aContext, CPhysicalLink& aPhysicalLink) const + { + CBTSynchronousLink* child; + switch (aContext.iLinkType) + { + case ESCOLink: + child = CSCOLink::NewLC(aContext.iLinksMan, NULL); + break; + default: + child = CeSCOLink::NewLC(aContext.iLinksMan, NULL); + break; + } + + // configure the SAP and prepare PHY for SCO link + User::LeaveIfError(child->BindLink(child->iLinkType, aPhysicalLink)); + + // tell child about parent + child->ListeningSAP() = &aContext; + + // and tell parent about child + aContext.iChild = child; + CleanupStack::Pop(child); + + aPhysicalLink.OverridePark(); + + child->iRemoteDev = aPhysicalLink.BDAddr(); + + // transition for child is Closed->Accepting + ChangeState(*child, CSyncLinkStateFactory::EAccepting); + } + +void TSCOLinkStateListening::Exit(CBTSynchronousLink& aContext) const + { + aContext.iLinksMan.RemoveListener(aContext); + } + + +//---------------------------------------------------------------------------------- + +TSCOLinkStateAccepting::TSCOLinkStateAccepting(CSyncLinkStateFactory& aFactory) +: TSyncLinkState(aFactory) + { + STATENAME("TSCOLinkStateAccepting"); + } + +void TSCOLinkStateAccepting::Enter(CBTSynchronousLink& aContext) const + { + // start watchdog + aContext.iAcceptWatchdog.Start(); + } + +void TSCOLinkStateAccepting::Exit(CBTSynchronousLink& aContext) const + { + // Ensure watchdog is cancelled (calling Cancel twice is OK) + aContext.iAcceptWatchdog.Cancel(); + } + +void TSCOLinkStateAccepting::ConnectComplete(CBTSynchronousLink& aContext, const TBTConnect& aSCOInfo, const TBTSyncConnectOpts& aSyncOpts) const + { + // cancel watchdog + aContext.iAcceptWatchdog.Cancel(); + + aContext.iHandle = aSCOInfo.iConnH; + aContext.iRemoteDev = aSCOInfo.iBdaddr; + aContext.SetExtOptions(aSyncOpts); + + if (aContext.iClosePending) + { + //aContext.Shutdown(CServProviderBase::EImmediate);//this just resets iClosePending to ETrue + TInt err = aContext.CommonShutdown(); + + if (err) + { + aContext.Socket()->Error(KErrCouldNotDisconnect, MSocketNotify::EErrorClose); + ChangeState(aContext, CSyncLinkStateFactory::EOpen); + } + else + { + // wait for disconnection complete before anything else + ChangeState(aContext, CSyncLinkStateFactory::EClosing); + } + } + else + { + // let the listening Socket know... + __ASSERT_DEBUG(aContext.iParent, PanicInState(EBTSCOSAPParentlessChild)); + __ASSERT_DEBUG(aContext.iParent->Socket(), PanicInState(EBTSCOSAPNullSocket)); + ChangeState(aContext, CSyncLinkStateFactory::EWaitForStart); + aContext.iParent->Socket()->ConnectComplete(aContext); // tell listening socket about its new SAP + } + } + +void TSCOLinkStateAccepting::Error(CBTSynchronousLink& aContext, TInt /*aError*/) const + { + // cancel watchdog + aContext.iAcceptWatchdog.Cancel(); + + aContext.ReleaseAndUnbind(); + + // aContext will be deleted + aContext.ListeningSAP()->DeleteChild(&aContext); + } + +void TSCOLinkStateAccepting::Deletion(CBTSynchronousLink& aContext) const + { + // cancel watchdog + aContext.iAcceptWatchdog.Cancel(); + + // allowed deletion + aContext.ReleaseAndUnbind(); + aContext.ListeningSAP()->RemoveChild(&aContext); + } + +void TSCOLinkStateAccepting::Timeout(CBTSynchronousLink& aContext, TBasebandTimeout aTimeout) const + { + if (aTimeout == EAccept) + { + // the accept never worked - give up + Error(aContext, KErrTimedOut); + } + } + +void TSCOLinkStateAccepting::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType IF_FLOGGING(aCloseType)) const + { + LOG1(_L("CBTSyncLink: accepting state shutdown, just take a note. Type: %d"), aCloseType); + aContext.iClosePending = ETrue; + } + +void TSCOLinkStateAccepting::Disconnection(CBTSynchronousLink& aContext) const + { + LOG(_L("CBTSyncLink: accepting state and PHY has disconnected.")); + Error(aContext, KErrDisconnected); + } + +void TSCOLinkStateAccepting::ParentClosing(CBTSynchronousLink& aContext) const + { + aContext.iClosePending = ETrue; + } + + +//---------------------------------------------------------------------------------- + +TSCOLinkStateOpen::TSCOLinkStateOpen(CSyncLinkStateFactory& aFactory) +: TSyncLinkState(aFactory) + { + STATENAME("TSCOLinkStateOpen"); + } + +void TSCOLinkStateOpen::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType aCloseType) const + { + TInt err = aContext.CommonShutdown(); + + if (err) + { + aContext.Socket()->Error(KErrCouldNotDisconnect, MSocketNotify::EErrorClose); + } + else + { + if (aCloseType == CServProviderBase::ENormal) + { + LOG(_L("CBTSyncLink: open state shutdown, wait for link down")); + + // wait for disconnection complete before anything else + ChangeState(aContext, CSyncLinkStateFactory::EClosing); + } + else + { + LOG(_L("CBTSyncLink: open state shutdown, immediate")); + + aContext.ReleaseAndUnbind(); + aContext.iLinksMan.Baseband().UpdateModelForDisconnection(aContext.iHandle, aContext.LinkType()); + ChangeState(aContext, CSyncLinkStateFactory::EClosed); + } + } + } + +TUint TSCOLinkStateOpen::Write(CBTSynchronousLink& aContext, const TDesC8& aData, TUint aOptions, TSockAddr* aAddr) const + { + TUint retVal = aContext.DoWrite(aData, aOptions, aAddr); + return retVal; + } + +void TSCOLinkStateOpen::Disconnection(CBTSynchronousLink& aContext) const + { + // we were open, go to closed + ChangeState(aContext, CSyncLinkStateFactory::EClosed); + aContext.Socket()->Disconnect(); + } + +void TSCOLinkStateOpen::Error(CBTSynchronousLink& aContext, TInt /*aErr*/) const + { + // the link has gone - go through disconnect path + aContext.Disconnection(); + } + +void TSCOLinkStateOpen::PacketTypeChange(CBTSynchronousLink& __DEBUG_ONLY(aContext), THCIErrorCode aErr, THCIConnHandle __DEBUG_ONLY(aConnH), TUint16 /*aNewPacketMask*/) const + { + __ASSERT_DEBUG(aConnH == aContext.Handle(), PanicInState(EBTSCOSAPWrongSCOSAP)); + + if (aErr == KErrNone) + { + // in 1.1 this won't happen - for 1.2 eSCO do something here + } + else + { + // dump + } + } + +void TSCOLinkStateOpen::DataReceived(CBTSynchronousLink& aContext, const TDesC8& aData) const + { + __ASSERT_DEBUG(aData.Size() <= KInboundSCODataSize, Panic(ESCOInboundPacketTooLarge)); + aContext.InboundFrame() = aData; + + // signal (stream) socket of number of bytes we have + aContext.Socket()->NewData(aData.Length()); + } + +TBool TSCOLinkStateOpen::IsOpen() const + { + return ETrue; + } + +void TSCOLinkStateOpen::Exit(CBTSynchronousLink& /*aContext*/) const + { + } + + +//---------------------------------------------------------------------------------- + +TSCOLinkStateClosing::TSCOLinkStateClosing(CSyncLinkStateFactory& aFactory) +: TSyncLinkState(aFactory) + { + STATENAME("TSCOLinkStateClosing"); + } + +void TSCOLinkStateClosing::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType aCloseType) const + { + LOG(_L("CBTSyncLink: closing state shutdown, immediate")); + + ChangeState(aContext, CSyncLinkStateFactory::EClosed); + if ((aCloseType == CServProviderBase::ENormal) && (aContext.iSocket)) + { + aContext.Socket()->CanClose(); + } + } + +void TSCOLinkStateClosing::ActiveOpen(CBTSynchronousLink& aContext) const + { + // erk - have been asked to Open as we're closing down + // the link hasn't gone yet, so just say it's there! + ChangeState(aContext, CSyncLinkStateFactory::EOpen); + aContext.Socket()->ConnectComplete(); + } + + +void TSCOLinkStateClosing::Error(CBTSynchronousLink& aContext, TInt /*aError*/) const + { + ChangeState(aContext, CSyncLinkStateFactory::EClosed); + } + +void TSCOLinkStateClosing::Disconnection(CBTSynchronousLink& aContext) const + { + LOG(_L("CBTSyncLink: disconnection notification in closing state")); + + // as expected + ChangeState(aContext, CSyncLinkStateFactory::EClosed); + if (aContext.iSocket) + { + aContext.Socket()->CanClose(); + } + // Otherwise just swallow the event... + } + +TBool TSCOLinkStateClosing::IsIdle() const + { + return ETrue; + } + +void TSCOLinkStateClosing::ConnectComplete(CBTSynchronousLink& aContext, const TBTConnect& aSCOInfo, const TBTSyncConnectOpts& /*aSyncOpts*/) const + { + LOG(_L("CBTSyncLink: connection notification in closing state")); + + // This connection is no longer required, so close the socket + ChangeState(aContext, CSyncLinkStateFactory::EClosed); + + + //The call path to this ConnectComplete will have included CPhysicalLink::ConnectionComplete. + //CPhysicalLink::ConnectionComplete updates the baseband model to show a new SCO link is in place + //But if the SCO link is in state TSCOLinkStateClosing, setup of the new link won't occur. So the model + //should be updated again. If it isn't updated, it won't be possible to add a new SCO link, because the + //model will indicate that one already exists. + aContext.iLinksMan.Baseband().UpdateModelForDisconnection(aSCOInfo.iConnH, aSCOInfo.iLinkType); + + //Even though the SAP is now EClosed, the baseband might have a SCO link to the remote device. + //The only way to be sure is to send a Disconnect. The transition to EClosed at the will have + //unbound this SAP from the PHY, so if the remote device responds to the Disconnect, the response + //will be thrown away. + TRAP_IGNORE(aContext.iLinksMan.HCIFacade().DisconnectL(aSCOInfo.iConnH, ERemoteUserEndedConnection)); + + if (aContext.iSocket) + { + aContext.Socket()->CanClose(); + } + // socket could be deleted now so be careful + } + +void TSCOLinkStateClosing::Deletion(CBTSynchronousLink& /*aContext*/) const + { + // allowed + } + +//----------------------------------------------------------------------------------- +