--- /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 <bluetooth/logger.h>
+#include <bt_sock.h>
+#include <bluetooth/hci/aclpacketconsts.h>
+
+#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<CPhysicalLink&>(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<TUint8>(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<TUint8*>(&val), sizeof(TInt));
+ break;
+ }
+ case ELMInboundACLSize:
+ {
+ if (aOption.Length() != sizeof(TInt))
+ {
+ ret = KErrArgument;
+ }
+ TInt val = KLinkMgrIncomingBufferSize; // for now...
+ aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+ break;
+ }
+ case KLMGetACLHandle:
+ {
+ if (aOption.Length() != sizeof(THCIConnHandle))
+ {
+ ret = KErrArgument;
+ }
+ aOption = TPtrC8(reinterpret_cast<const TUint8*>(&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<CHCIFacade&>(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<void>(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<TUint8>((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.
+ }
+