diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/linkmgr/ACLSAP.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/linkmgr/ACLSAP.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,1566 @@ +// 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: +// ACL.cpp +// Implementation of ACL Logical links +// +// + +#include +#include +#include + +#include "ACLSAP.h" +#include "physicallinks.h" +#include "physicallinksmanager.h" +#include "Basebandmodel.h" +#include "linkmgr.h" +#include "AclDataQController.h" +#include "linkconsts.h" +#include "linkutil.h" + +#include "hcifacade.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR); +#endif + +#pragma warning (disable: 4100) //unreference formal param + +CACLLinkStateFactory* CACLLinkStateFactory::NewL() + { + LOG_STATIC_FUNC + CACLLinkStateFactory* ret=new (ELeave) CACLLinkStateFactory(); + CleanupStack::PushL(ret); + ret->ConstructL(); + CleanupStack::Pop(ret); + return ret; + } + +void CACLLinkStateFactory::ConstructL() + { + LOG_FUNC + iStates[EClosed] =new (ELeave) TACLLinkStateClosed(*this); + iStates[EListening] =new (ELeave) TACLLinkStateListening(*this); + iStates[EAccepting] =new (ELeave) TACLLinkStateAccepting(*this); + iStates[EWaitForLink] =new (ELeave) TACLLinkStateWaitForLink(*this); + iStates[EWaitForStart] =new (ELeave) TACLLinkStateWaitForStart(*this); + iStates[EWaitForStartError] =new (ELeave) TACLLinkStateWaitForStartError(*this); + iStates[EWaitForStartParked] =new (ELeave) TACLLinkStateWaitForStartParked(*this); + iStates[EOpen] =new (ELeave) TACLLinkStateOpen(*this); + iStates[EOpenParked] =new (ELeave) TACLLinkStateOpenParked(*this); + iStates[EClosing] =new (ELeave) TACLLinkStateClosing(*this); + } + +CACLLinkStateFactory::~CACLLinkStateFactory() + { + LOG_FUNC + iStates.DeleteAll(); + } + +TACLLinkState& CACLLinkStateFactory::GetState(CACLLinkStateFactory::TACLLinkStates aState) + { + LOG_FUNC + __ASSERT_DEBUG(aState != EACLLinkMaxState, Panic(EACLLinkStateOutOfBounds)); + return *iStates[aState]; + } + +TInt CACLLinkStateFactory::StateIndex(const TACLLinkState* aState) const + { + LOG_FUNC + TInt state; + for (state = 0; state < EACLLinkMaxState; state++) + { + if (iStates[state] == aState) + { + return state; + } + } + + return KUnknownState; + } + +TACLLinkState::TACLLinkState(CACLLinkStateFactory& aFactory) +: iFactory(aFactory) + { + LOG_FUNC + } + +void TACLLinkState::PanicInState(TLinkPanic aPanic) const + { + LOG_FUNC + Panic(aPanic, iFactory.StateIndex(this)); + } + +void TACLLinkState::ChangeState(CACLLink& aContext, CACLLinkStateFactory::TACLLinkStates aState) const + { + LOG_FUNC + aContext.iState->Exit(aContext); + +#ifdef __FLOG_ACTIVE + TACLLinkState* state=&iFactory.GetState(aState); + LOG2(_L("ACLLink: State %S -> %S"), &aContext.iState->iName, &state->iName); +#endif //__FLOG_ACTIVE + aContext.iState=&iFactory.GetState(aState); + + aContext.iState->Enter(aContext); + } + + +void TACLLinkState::Enter(CACLLink& /*aContext*/) const + { + LOG_FUNC + // do nothing + } + +void TACLLinkState::Exit(CACLLink& /*aContext*/) const + { + LOG_FUNC + // do nothing + } + +void TACLLinkState::Deletion(CACLLink& /*aContext*/) const + { + LOG_FUNC + PanicInState(EBTACLSAPUnexpectedEvent); + } + +void TACLLinkState::ActiveOpen(CACLLink& /*aContext*/) const + { + LOG_FUNC + PanicInState(EBTACLSAPUnexpectedEvent); + } + +void TACLLinkState::DataReceived(CACLLink& /*aContext*/, TUint8 /*aFlag*/, const TDesC8& /*aData*/) const + { + LOG_FUNC + __DEBUGGER(); // drop, but see why went wrong + } + +void TACLLinkState::Start(CACLLink& /*aContext*/) const + { + LOG_FUNC + PanicInState(EBTACLSAPUnexpectedEvent); //Panics since ESOCK and our state machine might be out of sync + } + +void TACLLinkState::ConnectComplete(CACLLink& /*aContext*/, const TBTConnect& /*aConnect*/) const + { + LOG_FUNC + //Drop the event silently in UREL + #ifdef _DEBUG + PanicInState(EBTACLSAPUnexpectedEvent); + #endif + } + +TBool TACLLinkState::ConnectRequest(CACLLink& /*aContext*/, + const TBTConnect& /*aConnectDetails*/, + const CPhysicalLink& /*aPhysicalLink*/) const + { + LOG_FUNC + PanicInState(EBTACLSAPUnexpectedEvent); + return EFalse; + } + +void TACLLinkState::Disconnection(CACLLink& /*aContext*/) const + { + LOG_FUNC + PanicInState(EBTACLSAPUnexpectedEvent); + } + +void TACLLinkState::Error(CACLLink& /*aContext*/, TInt /*aError*/) const + { + LOG_FUNC + PanicInState(EBTACLSAPUnexpectedEvent); + } + +void TACLLinkState::Timeout(CACLLink& /*aContext*/, TBasebandTimeout /*aTimeout*/) const + { + LOG_FUNC + PanicInState(EBTACLSAPUnexpectedEvent); + } + +void TACLLinkState::Shutdown(CACLLink& /*aContext*/, CServProviderBase::TCloseType /*aCloseType*/) const + { + LOG_FUNC + PanicInState(EBTACLSAPUnexpectedEvent); + } + +TInt TACLLinkState::PassiveOpen(CACLLink& /*aContext*/, TUint /*aQueSize*/) const + { + LOG_FUNC + PanicInState(EBTACLSAPUnexpectedEvent); + return KErrGeneral; + } + +TUint TACLLinkState::Write(CACLLink& /*aContext*/, const TDesC8& /*aData*/, TUint /*aOptions*/, TSockAddr* /*aAddr*/) const + { + LOG_FUNC + LOG(_L("CACLLink: Write in base state - Dropping data...")); + return 0; + } + +TBool TACLLinkState::IsIdle() const + { + LOG_FUNC + return EFalse; + } + +void TACLLinkState::Park(CACLLink& /*aContext*/) const + { + LOG_FUNC + // ignore + } + +void TACLLinkState::Unpark(CACLLink& /*aContext*/) const + { + LOG_FUNC + // ignore + } + +//---------------------------------------------------------------------------------- + +TACLLinkStateListening::TACLLinkStateListening(CACLLinkStateFactory& aFactory) +: TACLLinkState(aFactory) + { + LOG_FUNC + STATENAME("Listening"); + } + +void TACLLinkStateListening::Enter(CACLLink& aContext) const + { + LOG_FUNC + TInt err = aContext.iLinksMan.AddListener(aContext, EACLLink); + if (err) + { + __ASSERT_DEBUG(err!=KErrInUse, PanicInState(EBTACLAlreadyBoundToPhysicalLink)); + aContext.iSocket->Error(err, MSocketNotify::EErrorFatal); + ChangeState(aContext, CACLLinkStateFactory::EClosed); + } + } + + +TBool TACLLinkStateListening::ConnectRequest(CACLLink& aContext, + const TBTConnect& /*aConnectDetails*/, + const CPhysicalLink& aPhysicalLink) const + { + LOG_FUNC + // ah! we have a connection + /* super - now BT1.1/1.2 says we don't need to do anything - + ACL is here for free: with same handle as PhysicalLink. + We do however need to introduce ourselves to ACL Q + */ + LOG(_L("CACLLink: Physical Channel Up - Spawning...")); + + // clone the sap - we only support *one* child at present since + // we dont expose ACL sockets to ESOCK - only to L2CAP who will + // only attempt to Start one child at a time + TRAPD(err, SpawnL(aContext, const_cast(aPhysicalLink))); + + return (err==KErrNone); + } + + +void TACLLinkStateListening::SpawnL(CACLLink& aContext, CPhysicalLink& aPhysicalLink) const +/** + Called when a PassiveSAP wants to create a (connected) child of itself +**/ + { + LOG_FUNC + //Listening SAP only keeps one pointer to accepting SAP so we + //can't have more than one outstanding accepting SAP + if(aContext.iChild) + { + LOG(_L("TACLLinkStateListening::SpawnL: there is already an outstanding accepting SAP, can't spawn now")); + User::Leave(KErrInUse); + } + + CACLLink* child = CACLLink::NewLC(aContext.iLinksMan, + &aPhysicalLink, + aContext.iProtocol); + + User::LeaveIfError(child->BindLink(EACLLink, aPhysicalLink)); + CleanupStack::Pop(child); + + // tell child about parent + child->ListeningSAP() = &aContext; + + // and parent about child + aContext.iChild = child; + + // configure the SAP + child->iRemoteDev = aPhysicalLink.BDAddr(); + child->iLocalPort = aContext.iLocalPort; + + // transition for child is Closed->WaitForStart + ChangeState(*child, CACLLinkStateFactory::EAccepting); + } + +void TACLLinkStateListening::Shutdown(CACLLink& aContext, CServProviderBase::TCloseType aCloseType) const + { + LOG_FUNC + ChangeState(aContext, CACLLinkStateFactory::EClosed); + + if(aContext.iChild) + { + //As soon as this method returns, this SAP could be deleted. + //Notify the child. + aContext.iChild->Shutdown(CServProviderBase::EImmediate); + + //If the child was in EAccepting state, the shutdown will move it to EClosing. It + //will still be bound to PHY, and when the ConnectComplete arrives the PHY will + //still route it to the child. If the parent has been deleted by then, the child + //will cause a panic. The best thing to do is delete the child now. + //N.B. It is important to Shutdown the child before deleting it, because if it + //was in EAccepting state calling Shutdown stops its watchdog. + aContext.DeleteChild(aContext.iChild); + aContext.iChild=NULL; //Not sure if this is needed, but it'll do no harm. + } + + if (aCloseType == CServProviderBase::ENormal) + { + aContext.Socket()->CanClose(); + } + } + +void TACLLinkStateListening::Deletion(CACLLink& aContext) const + { + LOG_FUNC + delete aContext.iChild; + } + +void TACLLinkStateListening::Exit(CACLLink& aContext) const + { + LOG_FUNC + aContext.iLinksMan.RemoveListener(aContext); + } + +void TACLLinkStateListening::Error(CACLLink& /*aContext*/, TInt __DEBUG_ONLY(aError)) const + { + LOG_FUNC + // Don't propagate the error as listeners don't care if the hardware has gone down + __ASSERT_DEBUG(aError==KErrHardwareNotAvailable, PanicInState(EBTACLSAPUnexpectedEvent)); + } + +//---------------------------------------------------------------------------------- + +TACLLinkStateAccepting::TACLLinkStateAccepting(CACLLinkStateFactory& aFactory) +: TACLLinkState(aFactory) + { + LOG_FUNC + STATENAME("Accepting"); + } + +void TACLLinkStateAccepting::Enter(CACLLink& aContext) const + { + LOG_FUNC + // start watchdog + aContext.iAcceptWatchdog.Start(); + // If the link is accepting then it must be being remotely initiated. + aContext.SetInitiationState(CACLLink::ERemotelyInitiated); + if(aContext.iLinksMan.IsAcceptPairedOnlyMode()) + { + // Start an access request for appropriate security - this will start as + // the physical link is established. + aContext.StartAccessRequest(*(aContext.ListeningSAP())); + } + } + +void TACLLinkStateAccepting::Exit(CACLLink& aContext) const + { + LOG_FUNC + //Ensure watchdog has been cancelled (safe to call Cancel multiple times) + aContext.iAcceptWatchdog.Cancel(); + } + +void TACLLinkStateAccepting::ConnectComplete(CACLLink& aContext, const TBTConnect& /*aConnect*/) const + { + LOG_FUNC + // cancel watchdog + aContext.iAcceptWatchdog.Cancel(); + + // must wait until started + ChangeState(aContext, CACLLinkStateFactory::EWaitForStart); + // let the listening Socket know... + __ASSERT_DEBUG(aContext.ListeningSAP()->Socket(), PanicInState(EBTACLSAPNullSocket)); + aContext.ListeningSAP()->Socket()->ConnectComplete(aContext); // tell listening socket about its new SAP + } + +void TACLLinkStateAccepting::Timeout(CACLLink& aContext, TBasebandTimeout aTimeout) const + { + LOG_FUNC + if (aTimeout == EAccept) + { + // the accept never worked - give up + Error(aContext, KErrTimedOut); + } + } + +void TACLLinkStateAccepting::Deletion(CACLLink& aContext) const + { + LOG_FUNC + aContext.ListeningSAP()->RemoveChild(&aContext); + } + +void TACLLinkStateAccepting::Disconnection(CACLLink& aContext) const + { + LOG_FUNC + // This will be called if there is a hardware error - give up + Error(aContext, KErrDisconnected); + } + +void TACLLinkStateAccepting::Error(CACLLink& aContext, TInt /*aError*/) const + { + LOG_FUNC + // unbind from physical link + aContext.UnbindLink(EACLLink); + // cancel watchdog + aContext.iAcceptWatchdog.Cancel(); + aContext.ListeningSAP()->DeleteChild(&aContext); + } + +void TACLLinkStateAccepting::Shutdown(CACLLink& aContext, CServProviderBase::TCloseType /*aCloseType*/) const + { + LOG_FUNC + // we're not wanted - close + ChangeState(aContext, CACLLinkStateFactory::EClosing); + } + +//---------------------------------------------------------------------------------- + + +TACLLinkStateWaitForStart::TACLLinkStateWaitForStart(CACLLinkStateFactory& aFactory) +: TACLLinkState(aFactory) + { + LOG_FUNC + STATENAME("WaitForStart"); + } + +void TACLLinkStateWaitForStart::Enter(CACLLink& __DEBUG_ONLY(aContext)) const + { + LOG_FUNC + __ASSERT_DEBUG(aContext.ListeningSAP(), PanicInState(EBTACLSAPParentlessChild)); + } + +void TACLLinkStateWaitForStart::Start(CACLLink& aContext) const + { + LOG_FUNC + // transfer ownership to higher entity + aContext.ListeningSAP()->RemoveChild(&aContext); + aContext.ListeningSAP() = NULL; + TInt err = aContext.OpenLogicalLink(); + if (err == KErrNone) + { + ChangeState(aContext, CACLLinkStateFactory::EOpen); + } + else + { + Error(aContext, err); + } + } + +void TACLLinkStateWaitForStart::Deletion(CACLLink& aContext) const + { + LOG_FUNC + aContext.ListeningSAP()->RemoveChild(&aContext); + } + +void TACLLinkStateWaitForStart::Error(CACLLink& aContext, TInt /*aError*/) const + { + LOG_FUNC + ChangeState(aContext, CACLLinkStateFactory::EWaitForStartError); + } + +void TACLLinkStateWaitForStart::Park(CACLLink& aContext) const + { + LOG_FUNC + // blimey! already gone into park - dont tell socket anything + ChangeState(aContext, CACLLinkStateFactory::EWaitForStartParked); + } + +void TACLLinkStateWaitForStart::Disconnection(CACLLink& aContext) const + { + LOG_FUNC + aContext.ListeningSAP()->DeleteChild(&aContext); + } + +void TACLLinkStateWaitForStart::Shutdown(CACLLink& aContext, CServProviderBase::TCloseType /*aCloseType*/) const + { + LOG_FUNC + // just go + ChangeState(aContext, CACLLinkStateFactory::EClosing); + } + +//---------------------------------------------------------------------------------- + +TACLLinkStateWaitForStartError::TACLLinkStateWaitForStartError(CACLLinkStateFactory& aFactory) +: TACLLinkState(aFactory) + { + LOG_FUNC + STATENAME("Error"); + } + +void TACLLinkStateWaitForStartError::Start(CACLLink& aContext) const + { + LOG_FUNC + // summat went wrong - close and remove this acceptor + ChangeState(aContext, CACLLinkStateFactory::EClosed); + aContext.Socket()->Disconnect(); + + aContext.ListeningSAP()->RemoveChild(&aContext); + } + +void TACLLinkStateWaitForStartError::Disconnection(CACLLink& /*aContext*/) const + { + LOG_FUNC + //Release build should silently ignore the event + __ASSERT_DEBUG(EFalse,PanicInState(EBTACLSAPUnexpectedEvent)); + } + +void TACLLinkStateWaitForStartError::Error(CACLLink& /*aContext*/, TInt /*aError*/) const + { + LOG_FUNC + //Release build should silently ignore the event + __ASSERT_DEBUG(EFalse,PanicInState(EBTACLSAPUnexpectedEvent)); + } + +void TACLLinkStateWaitForStartError::Deletion(CACLLink& aContext) const +/* +In WaitForStartError state we expect ESock to send a Start. But deletion could occur +if ESock deletes the parent SAP. +*/ + { + LOG_FUNC + aContext.ListeningSAP()->RemoveChild(&aContext); + } + +//---------------------------------------------------------------------------------- + +TACLLinkStateWaitForStartParked::TACLLinkStateWaitForStartParked(CACLLinkStateFactory& aFactory) +: TACLLinkStateWaitForStart(aFactory) + { + LOG_FUNC + STATENAME("WaitForStartParked"); + } + +void TACLLinkStateWaitForStartParked::Start(CACLLink& aContext) const + { + LOG_FUNC + // transfer ownership to higher entity + aContext.ListeningSAP()->RemoveChild(&aContext); + aContext.ListeningSAP() = NULL; + ChangeState(aContext, CACLLinkStateFactory::EOpenParked); + } + +void TACLLinkStateWaitForStartParked::Unpark(CACLLink& aContext) const + { + LOG_FUNC + // that's nice - un-parked! + ChangeState(aContext, CACLLinkStateFactory::EWaitForStart); + } + +//---------------------------------------------------------------------------------- + +TACLLinkStateWaitForLink::TACLLinkStateWaitForLink(CACLLinkStateFactory& aFactory) +: TACLLinkState(aFactory) + { + LOG_FUNC + STATENAME("WaitForLink"); + } + +void TACLLinkStateWaitForLink::ConnectComplete(CACLLink& aContext, const TBTConnect& aConnectDetails) const + { + LOG_FUNC + /* super - now BT1.1/1.2 says we don't need to do anything - + ACL is here for free: with same handle as PhysicalLink. + We do however need to introduce ourselves to ACL Q + */ + LOG(_L("CACLLink: Physical Channel Up")) + __ASSERT_DEBUG(aConnectDetails.iBdaddr == aContext.iPhysicalLink->BDAddr(), PanicInState(EBTACLSAPWrongPhysicalLink)); + aContext.iRemoteDev = aConnectDetails.iBdaddr; + TInt err = aContext.OpenLogicalLink(); + if (err == KErrNone) + { + ChangeState(aContext, CACLLinkStateFactory::EOpen); + aContext.Socket()->ConnectComplete(); + } + else + { + Error(aContext, err); + } + } + +void TACLLinkStateWaitForLink::Error(CACLLink& aContext, TInt aError) const + { + LOG_FUNC + ChangeState(aContext, CACLLinkStateFactory::EClosed); + aContext.Socket()->Error(aError, MSocketNotify::EErrorConnect); + } + +void TACLLinkStateWaitForLink::Exit(CACLLink& /*aContext*/) const + { + LOG_FUNC + } + +void TACLLinkStateWaitForLink::Shutdown(CACLLink& aContext, CServProviderBase::TCloseType /*aCloseType*/) const + { + LOG_FUNC + // just change to closing - we'll get notified of PhyUp, but that'll get swallowed + // and the connection will timeout and die. + ChangeState(aContext, CACLLinkStateFactory::EClosing); + } + +void TACLLinkStateWaitForLink::Disconnection(CACLLink& aContext) const + { + LOG_FUNC + //Physical link is down + Error(aContext, KErrCouldNotConnect); + } + +//---------------------------------------------------------------------------------- + +TACLLinkStateOpen::TACLLinkStateOpen(CACLLinkStateFactory& aFactory) +: TACLLinkState(aFactory) + { + LOG_FUNC + STATENAME("Open"); + } + +void TACLLinkStateOpen::Enter(CACLLink& /*aContext*/) const + { + LOG_FUNC + } + +void TACLLinkStateOpen::Disconnection(CACLLink& aContext) const + { + LOG_FUNC + // an unexpected phy loss + LOG(_L("CACLLink: Physical Channel Down")) + + // No more data will appear so close + aContext.CloseLogicalLink(); + ChangeState(aContext, CACLLinkStateFactory::EClosed); + aContext.Socket()->Disconnect(); + } + +void TACLLinkStateOpen::Error(CACLLink& aContext, TInt aError) const + { + LOG_FUNC + // eg for weird times when we get another (PHY) connect complete with error! + ChangeState(aContext, CACLLinkStateFactory::EClosed); + aContext.Socket()->Error(aError, MSocketNotify::EErrorAllOperations); + } + +TUint TACLLinkStateOpen::Write(CACLLink& aContext, const TDesC8& aData, TUint aOptions, TSockAddr* aAddr) const + { + LOG_FUNC + // Note: This method returns the number of data + // fragments written (normally 1), NOT the number of bytes. + LOG(_L("TACLLinkStateOpen::Write")); + + // we only allow 8 bit flags + __ASSERT_DEBUG(aOptions < KMaxTUint8, PanicInState(EBTACLSAPBadFlagsOnWrite)); + + TUint8 flags = static_cast(aOptions); + + // check not trying to do piconet broadcast - active bc is OK + __ASSERT_DEBUG(!((flags >> KPacketPBBCFlagShift) & KPacketBCFlagMask), + PanicInState(EBTACLSAPDoesNotSupportPiconetBroadcast)); + + // check that L2CAP isn't trying to broadcast + __ASSERT_DEBUG(!(((flags >> KPacketPBBCFlagShift) & KPacketBCFlagMask) + && aContext.iLocalPort==EACLPortL2CAP), PanicInState(EBTACLSAPDoesNotSupportBroadcastL2CAP)); + + + CACLDataQController& aclQctrl = aContext.iProtocol.ACLController(); + CACLDataItem* dataItem = aclQctrl.GetFreeItem(); // we don't own it - but might be NULL + + TUint retVal; + + if (!dataItem) + { + // no space on pool + LOG(_L("\tno space in ACL data pool- blocking socket")) + aContext.iSocketBlocked = ETrue; + retVal = 0; // could not write anything - notify later + } + else + { + // space on pool...so format get the HCIFacade to format and queue the frame + dataItem->SetElementHandle(aAddr); + retVal = aContext.DoWrite(*dataItem, aclQctrl, aData, flags); + } + + LOG1(_L("TACLLinkStateOpen::Write retVal = %d"), retVal); + return retVal; + } + + +void TACLLinkStateOpen::DataReceived(CACLLink& aContext, TUint8 aFlag, const TDesC8& aData) const + { + LOG_FUNC + aContext.NotifyDataToSocket(aFlag, aData); + } + +void TACLLinkStateOpen::Park(CACLLink& aContext) const + { + LOG_FUNC + ChangeState(aContext, CACLLinkStateFactory::EOpenParked); + } + +void TACLLinkStateOpen::Shutdown(CACLLink& aContext, CServProviderBase::TCloseType /*aCloseType*/) const + { + LOG_FUNC + // an ACL link cannot be closed with the underlying PHY going. + // to congrue with the spec we keep this object all the time the PHY + // is around. +// __ASSERT_DEBUG(aCloseType == CServProviderBase::ENormal, PanicInState(EBTACLSAPNotSupportedFeature)); + + // Change state first in case the ACL is currently in Park mode. + // This will allow the logic in the Exit of the Open Park state + // to un-park the link before it is disconnected. + ChangeState(aContext, CACLLinkStateFactory::EClosing); // we close when the PHY says + } + + +void TACLLinkStateOpen::Exit(CACLLink& /*aContext*/) const + { + LOG_FUNC + } + +//---------------------------------------------------------------------------------- + +TACLLinkStateOpenParked::TACLLinkStateOpenParked(CACLLinkStateFactory& aFactory) +: TACLLinkStateOpen(aFactory) + { + LOG_FUNC + STATENAME("OpenParked"); + } + +void TACLLinkStateOpenParked::Enter(CACLLink& aContext) const + { + LOG_FUNC + // signal ACL Data Q controller that we've parked up + aContext.ParkLogicalLink(); + } + +TUint TACLLinkStateOpenParked::Write(CACLLink& /*aContext*/, const TDesC8& /*aData*/, TUint /*aOptions*/, TSockAddr* /*aAddr*/) const + { + LOG_FUNC + // can't - parked; and don't support piconet broadcast yet... + // for active broadcast support that in open state + return 0; + } + +void TACLLinkStateOpenParked::Unpark(CACLLink& aContext) const + { + LOG_FUNC + ChangeState(aContext, CACLLinkStateFactory::EOpen); + + // flow control on socket + aContext.Socket()->CanSend(); + } + +void TACLLinkStateOpenParked::DataReceived(CACLLink& aContext, TUint8 aFlag, const TDesC8& aData) const + { + LOG_FUNC + // We handle data in this state to allow for the race condition between + // the Mode Change [Park] event and data that was received at the HC just + // prior to Park mode being entered. + aContext.NotifyDataToSocket(aFlag, aData); + } + +void TACLLinkStateOpenParked::Exit(CACLLink& aContext) const + { + LOG_FUNC + // signal ACL Data Q controller that we're un-parking + aContext.UnparkLogicalLink(); + } + +//---------------------------------------------------------------------------------- + +TACLLinkStateClosing::TACLLinkStateClosing(CACLLinkStateFactory& aFactory) +: TACLLinkState(aFactory) + { + LOG_FUNC + STATENAME("Closing"); + } + +void TACLLinkStateClosing::ActiveOpen(CACLLink& aContext) const + { + LOG_FUNC + // erk - have been asked to Open as we're closing down + // the link hasn't gone yet, so just say it's there! + + // Set as locally initiated (even if previously it was remotely initiated) + aContext.SetInitiationState(CACLLink::ELocallyInitiated); + ChangeState(aContext, CACLLinkStateFactory::EOpen); + aContext.Socket()->ConnectComplete(); + } + +void TACLLinkStateClosing::Shutdown(CACLLink& /*aContext*/, CServProviderBase::TCloseType /*aCloseType*/) const + { + LOG_FUNC + // we are! just ignore and continue + } + +void TACLLinkStateClosing::Disconnection(CACLLink& aContext) const + { + LOG_FUNC + // an expected phy down! + + // No more data will appear, so close + aContext.CloseLogicalLink(); + + ChangeState(aContext, CACLLinkStateFactory::EClosed); + aContext.Socket()->CanClose(); + } + +void TACLLinkStateClosing::Enter(CACLLink& aContext) const + { + LOG_FUNC + // Inform the physical link that this link has become idle. + aContext.LinkStateIdle(); + } + +void TACLLinkStateClosing::Deletion(CACLLink& /*aContext*/) const + { + LOG_FUNC + // allowed + } + +TBool TACLLinkStateClosing::IsIdle() const + { + LOG_FUNC + return ETrue; + } + +void TACLLinkStateClosing::DataReceived(CACLLink& aContext, TUint8 aFlag, const TDesC8& aData) const + { + LOG_FUNC + // got data for socket - that's fine: deliver it to decide + aContext.NotifyDataToSocket(aFlag, aData); + } + +TUint TACLLinkStateClosing::Write(CACLLink& /*aContext*/, const TDesC8& /*aData*/, TUint /*aOptions*/, TSockAddr* /*aAddr*/) const + { + LOG_FUNC + //Drop, we are not interested in data anymore. + return 0; + } + +void TACLLinkStateClosing::Error(CACLLink& aContext, TInt /*aError*/) const + { + LOG_FUNC + // This method is reached by receiving a Disconnection Complete Event with Status field != EOk, + // in this case we don't want to error the socket - so instead it is just closed + ChangeState(aContext, CACLLinkStateFactory::EClosed); + aContext.Socket()->CanClose(); + } + +//---------------------------------------------------------------------------------- + +TACLLinkStateClosed::TACLLinkStateClosed(CACLLinkStateFactory& aFactory) +: TACLLinkState(aFactory) + { + LOG_FUNC + STATENAME("Closed"); + } + +void TACLLinkStateClosed::Enter(CACLLink& aContext) const + { + LOG_FUNC + aContext.ClearInitiationState(); // once closed, there is no direction + aContext.ClearPhysicalLink(); + } + +void TACLLinkStateClosed::Start(CACLLink& /*aContext*/) const + { + LOG_FUNC + // gulp this - don't need to transition + } + +void TACLLinkStateClosed::Shutdown(CACLLink& aContext, CServProviderBase::TCloseType aCloseType) const + { + LOG_FUNC + if (aCloseType != CServProviderBase::EImmediate) + { + aContext.Socket()->CanClose(); + } + } + +void TACLLinkStateClosed::ActiveOpen(CACLLink& aContext) const + { + LOG_FUNC + // create ACL link + ChangeState(aContext, CACLLinkStateFactory::EWaitForLink); + TRAPD(err, aContext.BindAndConnectPhysicalLinkL()); + + if (err != KErrNone) + { + aContext.Error(err); + } + else + { + // Successful active open means that we've been locally initiated. + aContext.SetInitiationState(CACLLink::ELocallyInitiated); + } + } + +TInt TACLLinkStateClosed::PassiveOpen(CACLLink& aContext, TUint __DEBUG_ONLY(aQueSize)) const + { + LOG_FUNC + __ASSERT_DEBUG(aQueSize == 1, PanicInState(EBTACLSAPUnsupportedQueSize)); + // only support a QueSize of 1 - we don't expose the ACL Links to ESOCK + // for use by RSockets; furhter we enforce L2CAP to *start* us synchronously + // upon ACL link up + + ChangeState(aContext, CACLLinkStateFactory::EListening); + + return KErrNone; + } + +void TACLLinkStateClosed::Deletion(CACLLink& /*aContext*/) const + { + LOG_FUNC + // allowed deletion + } + +TBool TACLLinkStateClosed::IsIdle() const + { + LOG_FUNC + return ETrue; + } + +//---------------------------------------------------------------------------------- + +CACLLink* CACLLink::NewLC(CPhysicalLinksManager& aLinksMan, CPhysicalLink* aConnection, CLinkMgrProtocol& aProtocol) + { + LOG_STATIC_FUNC + CACLLink* s = new(ELeave) CACLLink(aLinksMan, aConnection, aProtocol); + CleanupStack::PushL(s); + s->ConstructL(); + return s; + } + + +CACLLink* CACLLink::NewL(CPhysicalLinksManager& aLinksMan, CPhysicalLink* aConnection, CLinkMgrProtocol& aProtocol) + { + LOG_STATIC_FUNC + CACLLink* s = NewLC(aLinksMan, aConnection, aProtocol); + CleanupStack::Pop(s); + return s; + } + + +CACLLink::CACLLink(CPhysicalLinksManager& aManager, + CPhysicalLink* aConnection, + CLinkMgrProtocol& aProtocol) +: CBTBasebandSAP(aManager, aConnection), + iProtocol(aProtocol), + iSocketBlocked(EFalse) + { + LOG_FUNC + iState = &iProtocol.ACLStateFactory().GetState(CACLLinkStateFactory::EClosed); + + // Hard-coded security for ACL level security (not service level). + // MITM desired is applied to ensure authentication takes place (if + // since authentication is normally eq. MITM protection). Pairing + // is prevented where appropriate. + // (See Paired Only Connections Mode for why this is used). + iSecurity.SetAuthentication(EMitmDesired); + iSecurity.SetEncryption(EFalse); + iSecurity.SetAuthorisation(EFalse); + } + +CACLLink::~CACLLink() + { + LOG_FUNC + // CloseLogicalLink() should have been called by now, but just in case... + if ( iHandle != KInvalidConnectionHandle ) + { + CloseLogicalLink(); + } + + // There could be an access request outstanding + CancelAccessRequest(); + + iState->Deletion(*this); + + UnbindLink(EACLLink); + + __ASSERT_DEBUG(!iChild, Panic(EAclSapChildStillPresentAtDestruction)); + } + +void CACLLink::ConstructL() + { + LOG_FUNC + CBTBasebandSAP::ConstructL(); + } + +void CACLLink::ActiveOpen() + { + LOG_FUNC + iState->ActiveOpen(*this); + } + +void CACLLink::BindAndConnectPhysicalLinkL() + { + LOG_FUNC + if(!(Baseband().IsACLPossible())) + { + User::Leave(KErrInsufficientBasebandResources); + } + + iPhysicalLink = iLinksMan.FindPhysicalLink(iRemoteDev); + + if (iPhysicalLink) + { + TBTBasebandLinkState::TLinkState linkState = iPhysicalLink->LinkState().LinkState(); + if (linkState == TBTBasebandLinkState::ELinkDown) + { + // Incoming pending physical link connection. We must error here as we cannot + // know if the incoming connection will be successful or not. + iPhysicalLink = NULL; + User::Leave(KErrPendingPhysicalLink); + } + + User::LeaveIfError(BindLink(EACLLink, *iPhysicalLink)); + + if (linkState == TBTBasebandLinkState::ELinkUp) + { + // We have an existing, connected, physical link so we can complete this request + TBTConnect connect; + connect.iBdaddr = iPhysicalLink->BDAddr(); + ConnectComplete(connect); + return; + } + + // If we get here then we must have a pending active physical link connection. + // As we bind to it above then this connection request we be completed when + // the physical link is completed. + __ASSERT_ALWAYS(linkState == TBTBasebandLinkState::ELinkPending, Panic(EBTBasebandInvalidLinkState)); + } + else + { + // New physical link connection request + CPhysicalLink* physicalLink = &iLinksMan.NewPhysicalLinkL(iRemoteDev); + // physicalLink is owned by the physical links manager. + User::LeaveIfError(BindLink(EACLLink, *physicalLink)); + TInt err = iPhysicalLink->Connect(); + if(err != KErrNone) + { + // If we failed to connect then we should roll back the attachment + ClearPhysicalLink(); + User::Leave(err); + } + } + } + +TInt CACLLink::SetLocalName(TSockAddr& aAddr) + { + LOG_FUNC + // Copy this address into iLocal* + // Overwrite their BTAddr if it's wrong + // Must check that the endpoint required is free + TACLSockAddr aclAddr = TACLSockAddr::Cast(aAddr); + TACLPort port = aclAddr.Port(); + + iLocalPort=port; + return KErrNone; + } + +TInt CACLLink::SetRemName(TSockAddr& aAddr) + { + LOG_FUNC + // Copy this over + __ASSERT_DEBUG(!ListeningSAP(), Panic(EBTACLSAPListenerSettingName)); // not applicable to listeners + + TACLSockAddr bbAddr(aAddr); // convert + iRemoteDev = bbAddr.BTAddr(); + + //try to get the details about the intended remote now + return KErrNone; + } + +TInt CACLLink::PassiveOpen(TUint aQueSize) + { + LOG_FUNC + return iState->PassiveOpen(*this, aQueSize); + } + + +TInt CACLLink::GetOption(TUint aLevel,TUint aName,TDes8& aOption) const + { + LOG_FUNC + TInt ret = KErrNone; + + if (aLevel != KSolBtLM && aLevel != KSolBtACL) + ret = KErrNotSupported; + else + { + if (aLevel == KSolBtACL) + { + ret = DoACLGetOption(aName, aOption); + } + else + { + if (iPhysicalLink) + { + // forward to PHY + ret = iPhysicalLink->GetOption(aLevel, aName, aOption); + } + else + { + // ho-hum! + ret = KErrNotReady; + } + } + } + return ret; + } + + +TInt CACLLink::DoACLGetOption(TUint aName,TDes8& aOption) const + { + LOG_FUNC + TInt ret = KErrNone; + + switch (aName) + { + case ELMOutboundACLSize: + { + if (aOption.Length() != sizeof(TInt)) + { + ret = KErrArgument; + } + TInt val = iProtocol.ACLPacketMTU(); + aOption = TPtrC8(reinterpret_cast(&val), sizeof(TInt)); + break; + } + case ELMInboundACLSize: + { + if (aOption.Length() != sizeof(TInt)) + { + ret = KErrArgument; + } + TInt val = KLinkMgrIncomingBufferSize; // for now... + aOption = TPtrC8(reinterpret_cast(&val), sizeof(TInt)); + break; + } + case KLMGetACLHandle: + { + if (aOption.Length() != sizeof(THCIConnHandle)) + { + ret = KErrArgument; + } + aOption = TPtrC8(reinterpret_cast(&iHandle), sizeof(THCIConnHandle)); + break; + } + default: + __DEBUGGER(); + ret = KErrNotSupported; + } + return ret; + } + + +TUint CACLLink::DoWrite(CACLDataItem& aItem, CACLDataQController& aQctrl, + const TDesC8& aData, TUint8 aOptions) +/** + aFrame has been obtained by someone else (the state) + - we can go and complete the write +**/ + { + LOG_FUNC + LOG1(_L("CACLLink::DoWrite aItem = 0x%08x"), &aItem); + + __ASSERT_ALWAYS(aItem.Frame() != NULL, Panic(EBTACLSAPWriteDataItemWithNullFrame)); + + // Fill the item with data... + iLinksMan.HCIFacade().FormatACLData(*aItem.Frame(), Handle(), aOptions, aData); + //.. and add it to the pool + aQctrl.AddItem(aItem); + TInt ret = 1; //one data 'item' + + LOG1(_L("CACLLink::DoWrite ret = %d"), ret); + return ret; + } + +void CACLLink::Ioctl(TUint /*aLevel*/,TUint /*aName*/,TDes8* /*aOption*/) + { + LOG_FUNC + // good stuff here! + } + +void CACLLink::CancelIoctl(TUint /*aLevel*/,TUint /*aName*/) + { + LOG_FUNC + // good stuff here! + } + +TInt CACLLink::SAPSetOption(TUint aLevel,TUint aName,const TDesC8& aOption) + { + LOG_FUNC + TInt rerr = KErrNone; + + switch(aLevel) + { + case KSolBtLM: + rerr = KErrNotSupported; + break; + + case KSolBtACL: + rerr = DoACLSetOption(aName, aOption); + break; + + default: + rerr = KErrNotSupported; + break; + }; + + return rerr; + } + +TInt CACLLink::DoACLSetOption(TUint aName, const TDesC8& /*aOption*/) + { + LOG_FUNC + TInt rerr = KErrNone; + + switch(aName) + { + case KSolBtACLFlushOccured: + rerr = ProcessFlush(); + break; + + default: + rerr = KErrNotSupported; + break; + }; + + return rerr; + } + +void CACLLink::RemName(TSockAddr& aAddr) const + { + LOG_FUNC + //Return the remote name + // Copy iRemoteDev and iRemotePort into TSockAddr and return + TACLSockAddr bbAddr(aAddr); + bbAddr.SetBTAddr(iRemoteDev); + aAddr=bbAddr; // Convert back + } + +void CACLLink::LocalName(TSockAddr& aAddr) const + { + LOG_FUNC + // Copy iLocalPort into TSockAddr and return + TACLSockAddr bbAddr(aAddr); + bbAddr.SetBTAddr(iProtocol.LocalBTAddress()); + aAddr=bbAddr; + } + +void CACLLink::Start() + { + LOG_FUNC + iState->Start(*this); + } + +void CACLLink::Timeout(TBasebandTimeout aTimeout) + { + LOG_FUNC + iState->Timeout(*this, aTimeout); + } + +void CACLLink::AutoBind() + { + LOG_FUNC + // at present no state bothers with this, so just process here + // do nothing + } + +void CACLLink::Shutdown(TCloseType aOption) + { + LOG_FUNC + iState->Shutdown(*this, aOption); + } + +void CACLLink::Shutdown(TCloseType aOption, const TDesC8& /*aDisconnectionData*/) + { + LOG_FUNC + iState->Shutdown(*this, aOption); + } + + +void CACLLink::NotifyDataToSocket(TUint8 aFlag, const TDesC8& aData) + { + LOG_FUNC + const TUint8 KFlagHeaderSize =1; +/* + The design of the protocol specification for L2CAP + means that both we and L2CAP need to know the flag parameter + for now we just signal one datagram (*could* signal two - one for flag: but that's just as grubby) +*/ + + + // make a new chain consisting of Flag(1st octet) followed by Data. + RMBufChain aclData; + #ifdef HOSTCONTROLLER_TO_HOST_FLOW_CONTROL + THCIConnHandle connH=iHandle; + aclData = const_cast(iLinksMan.HCIFacade()).TakeInboundACLDataBufferFromPool(connH); + aclData.CopyIn(aData,KFlagHeaderSize); + aclData.TrimEnd(aData.Length()+KFlagHeaderSize); //return the reserved MBufs we didn't need + //to the global pool + #else + TRAPD(err, aclData.CreateL(aData, KFlagHeaderSize)); + + if (err) + { + //Since HC->H flow control is off, and we have run out of MBufs + //there is nothing we can do here but drop or disconnect the link + //due to limited resources. We drop. + return; + } + #endif + + aclData.First()->Ptr()[0] = aFlag; // aData is already in the chain + + + // slap onto the RMBufPacketQ + iInboundBuffer.Append(aclData); // transfers + + + #ifndef HOSTCONTROLLER_TO_HOST_FLOW_CONTROL + if (!err) + { + #endif + iSocket->NewData(1); // datagrams: could async notify - or get l2cap to drain async + #ifndef HOSTCONTROLLER_TO_HOST_FLOW_CONTROL + } + #endif + } + + +void CACLLink::GetData(TDes8& aDesc,TUint aOptions,TSockAddr* aAddr) + { + LOG_FUNC +// see note in call to NewData - we just give a single descriptor +// could get L2CAP to call down with options/addr: it could say whether +// it's expecting flags or not etc + +// this overload is for descriptors - will be deprecated +// for now use a dummy mbufchain + RMBufChain dummy; + + GetData(dummy, aDesc.Length(), aOptions, aAddr); + + aDesc.SetMax(); + dummy.CopyOut(aDesc); + dummy.Free(); // release the actual mBuf - it's also gone from the inbound buffer collection + } + +TInt CACLLink::GetData(RMBufChain& aData, TUint /*aLength*/, TUint /*aOptions*/, TSockAddr* /*aAddr*/) + { + LOG_FUNC + static_cast(iInboundBuffer.Remove(aData)); // take off Q + + return 1; // datagram + } + +TUint CACLLink::Write(const TDesC8& aDesc,TUint aOptions, TSockAddr* aAddr) + { + LOG_FUNC + return iState->Write(*this, aDesc, aOptions, aAddr); + } + +void CACLLink::CloseLogicalLink() + { + LOG_FUNC + // tell our Q controller about ACL death + iProtocol.ACLController().ACLLogicalLinkDown(iHandle); + iHandle = KInvalidConnectionHandle; + } + +void CACLLink::ClearPhysicalLink() + { + LOG_FUNC + CBTBasebandSAP::UnbindLink(EACLLink); + } + +TInt CACLLink::OpenLogicalLink() + { + LOG_FUNC + // spec says our ACL handle = that of PHY + iHandle = iPhysicalLink->Handle(); + TInt err = iProtocol.ACLController().ACLLogicalLinkUp(iHandle, iPhysicalLink->IsParked()); + + return err; + } + +void CACLLink::Error(TInt aErr) + { + LOG_FUNC + iState->Error(*this, aErr); + } + +void CACLLink::Disconnection() + { + LOG_FUNC + iState->Disconnection(*this); + } + +void CACLLink::ConnectComplete(const TBTConnect& aConnect) + { + LOG_FUNC + iState->ConnectComplete(*this, aConnect); + } + +TBool CACLLink::ConnectRequest(const TBTConnect& aConnect, const CPhysicalLink& aPhysicalLink) + { + LOG_FUNC + return iState->ConnectRequest(*this, aConnect, aPhysicalLink); + } + +void CACLLink::DataReceived(THCIConnHandle __DEBUG_ONLY(aConnH), TUint8 aFlag, const TDesC8& aData) + { + LOG_FUNC + // even if this is broadcast, the data *FROM* the HC to the Host should have + // the real connection handle of the physical link to the master (who sent the data!) + __ASSERT_DEBUG(aConnH == Handle(), Panic(EBTACLSAPDataForWrongSAP)); + + if (!CanHandleFlags(aFlag)) + { + // we can't handle these flags - other ACL SAPs might though! + return; + } + + iState->DataReceived(*this, aFlag, aData); + } + +void CACLLink::PacketsSent(THCIConnHandle /*aHandle*/, TUint16 /*aNumPackets*/) + { + LOG_FUNC + TryToSend(); + } + +TBool CACLLink::CanHandleFlags(TUint8 aFlag) const + { + LOG_FUNC + // map packet flags to port + TUint8 pbFlags = static_cast((aFlag >> KPacketPBBCFlagShift) & KPacketPBFlagMask); + if ((pbFlags == KFirstHLFragment || pbFlags == KContinuingHLFragment) && iLocalPort == EACLPortL2CAP) + return ETrue; + if (pbFlags == 0 && iLocalPort == EACLPortRaw) + return ETrue; + return EFalse; + } + +TInt CACLLink::ProcessFlush() + { + LOG_FUNC + CACLDataQController& aclQCtrl = iProtocol.ACLController(); + return aclQCtrl.SetFlushInProgress(Handle()); + } + +TBool CACLLink::IsIdle() const + { + LOG_FUNC + return iState->IsIdle(); + } + +void CACLLink::PhysicalLinkChange(const TBTBasebandEventNotification& aEvent, CPhysicalLink& __DEBUG_ONLY(aPhysicalLink)) + { + LOG_FUNC + // ACL logical links only really interested in parking of PHY + __ASSERT_DEBUG(&aPhysicalLink == iPhysicalLink, Panic(EBTACLSAPWrongPhysicalLink)); + + // test for a park event + if (aEvent.EventType() & ENotifyParkMode) + { + // gone into park, but check phy role: could do broadcast + iState->Park(*this); + } + + else if (aEvent.EventType() & ENotifyActiveMode) + { + //un-parked + iState->Unpark(*this); + } + + else + { + // uninteresting event + } + } + +void CACLLink::TryToSend() + { + LOG_FUNC + // no need to tell the buffer management entity (acl pool) - already done + // if socket blocked - unblock + if (iSocketBlocked) + { + iSocketBlocked = EFalse; + Socket()->CanSend(); + } + } + +void CACLLink::ParkLogicalLink() + { + LOG_FUNC + iProtocol.ACLController().SetParked(Handle(), ETrue); + } + +void CACLLink::UnparkLogicalLink() + { + LOG_FUNC + iProtocol.ACLController().SetParked(Handle(), EFalse); + } + +void CACLLink::SetInitiationState(TInitiationState aState) + { + __ASSERT_DEBUG(aState != EInvalidInitiationState, Panic(EBTACLSAPIndeterminateInitiator)); + iInitiationState = aState; + } + +void CACLLink::ClearInitiationState() + { + iInitiationState = EInvalidInitiationState; + } + +TBool CACLLink::IsLocallyInitiated() const + { + __ASSERT_DEBUG(iInitiationState != EInvalidInitiationState, Panic(EBTACLSAPIndeterminateInitiator)); + return iInitiationState == ELocallyInitiated; + } + +void CACLLink::AccessRequestComplete(TInt aResult) + { + // We don't expect the deferred result, if we get this then the state machine + // for the logical link needs to be updated. + __ASSERT_DEBUG(aResult != EBTSecManAccessDeferred, Panic(EBTACLSAPUnexpectedSecurityResult)); + if(aResult == EBTSecManAccessDenied) // convert to a symbian error + { + aResult = KErrAccessDenied; + } + if(aResult != KErrNone) + { + // Kill the physical link, this should result in the ACL link being + // pulled down. + if(iPhysicalLink) + { + iPhysicalLink->Terminate(ERemoteUserEndedConnection); + } + } + // else success from the access requester so we're done. + } +