bluetooth/btstack/linkmgr/ACLSAP.cpp
changeset 0 29b1cd4cb562
child 8 2b6718f05bdb
--- /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.
+	}
+