bluetooth/btstack/linkmgr/physicallinks.cpp
changeset 0 29b1cd4cb562
child 3 4e39398d58ed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/linkmgr/physicallinks.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,4291 @@
+// Copyright (c) 2006-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:
+// Implementation of physical links
+// 
+//
+
+#include <bluetooth/logger.h>
+#include "physicallinksmanager.h"
+#include "physicallinks.h"
+#include "AclDataQController.h"
+#include "ACLSAP.h"
+#include "SCOSAP.h"
+#include "ProxySAP.h"
+#include "linkconsts.h"
+#include "hcifacade.h"
+#include "hostresolver.h"
+#include "PhysicalLinkHelper.h"
+#include "pairingscache.h"
+#include "oobdata.h"
+#include "pairingserver.h"
+
+#include <bt_sock.h>
+
+#include <bluetooth/hci/commandstatusevent.h>
+#include <bluetooth/hci/sniffmodecommand.h>
+#include <bluetooth/hci/exitsniffmodecommand.h>
+#include <bluetooth/hci/holdmodecommand.h>
+#include <bluetooth/hci/parkmodecommand.h>
+#include <bluetooth/hci/exitparkmodecommand.h>
+#include <bluetooth/hci/modechangeevent.h>
+#include <bluetooth/hci/disconnectcommand.h>
+#include <bluetooth/hci/disconnectioncompleteevent.h>
+#include <bluetooth/hci/hciconsts.h>
+
+#include <bluetooth/hci/linkkeyrequestreplycommand.h>
+#include <bluetooth/hci/linkkeyrequestreplynegativecommand.h>
+#include <btextnotifierspartner.h>
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR);
+#endif
+
+#ifdef _DEBUG
+PANICCATEGORY("plink");
+#endif
+
+static const THCIErrorCode KDefaultRejectReason = EHostSecurityRejection; // see spec Error Codes
+
+#ifdef _DEBUG
+#define __CHECK_CONNECTION_HANDLE(aHandle) __ASSERT_DEBUG(aHandle==iHandle, Panic(EBTLinkMgrConnectionEventInWrongSAP));
+#else
+#define __CHECK_CONNECTION_HANDLE(aHandle) aHandle=aHandle; // to suppress warnings
+#endif
+
+CPhysicalLink* CPhysicalLink::NewLC(CPhysicalLinksManager& aConnectionMan, CRegistrySession& aRegSess, const TBTNamelessDevice& aDevice)
+	{
+	LOG_STATIC_FUNC
+	CPhysicalLink* s = new(ELeave) CPhysicalLink(aConnectionMan, aRegSess, aDevice);
+	CleanupStack::PushL(s);
+	s->ConstructL();
+	return s;
+	}
+
+CPhysicalLink* CPhysicalLink::NewL(CPhysicalLinksManager& aConnectionMan, CRegistrySession& aRegSess, const TBTNamelessDevice& aDevice)
+	{
+	LOG_STATIC_FUNC
+	CPhysicalLink* s = NewLC(aConnectionMan, aRegSess, aDevice);
+	CleanupStack::Pop(s);
+	return s;
+	}
+
+CPhysicalLink::CPhysicalLink(CPhysicalLinksManager& aConnectionMan, CRegistrySession& aRegSess, const TBTNamelessDevice& aDevice)
+	: iLinksMan(aConnectionMan)
+	, iRegSess(aRegSess)
+	, iDevice(aDevice)
+	, iHandle(KHCIBroadcastHandle)
+	, iRemoteFeatures(KInvalidRemoteFeatures)
+	, iDeviceResult(KDeviceNotObtained)
+	, iRegistryHelpers(_FOFF(CBTRegistryHelperBase,iLink))
+	, iProxySAPs(_FOFF(CBTProxySAP, iQueueLink))
+	, iOverrideParkRequests(EFalse)
+	, iOverrideLPMRequests(EFalse)
+	, iLPMOverrideTimerQueued(EFalse)
+	, iConnectionPacketTypeChanged(EFalse)
+	, iLowPowModeCtrl(*this, iLinksMan.HCIFacade().CommandQController())
+	, iDisconnectCtrl(*this, iLinksMan.HCIFacade().CommandQController())
+	, iAuthenticationCtrl(*this, iLinksMan.HCIFacade().CommandQController())
+	, iLSTO(0)
+	, iAutoKeyRefreshQue(_FOFF(XAutoKeyRefreshToken, iQueLink))
+	, iRemoteDeviceRecordedByStack(ENull)
+	{
+	LOG_FUNC
+	// don't initialise physical link policy just yet - wait until connection completes
+	// by then we'll have best information to go on
+	LOG1(_L("New CPhysicalLink [0x%08x]"), this);
+	}
+
+CPhysicalLink::~CPhysicalLink()
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iACLLogicalLinks.Count()==0, Panic(EBTACLLogicalLinkBadDebind));
+	__ASSERT_DEBUG(iSyncLogicalLink == NULL, Panic(EBTSCOLogicalLinkBadDebind));
+
+	LOG1(_L("CPhysicalLink Destructing this = 0x%08x"), this);
+
+	// tell ConnectionsManager...
+	iLinksMan.RemovePhysicalLink(*this);
+	iACLLogicalLinks.Close();
+
+	RemoveIdleTimer();
+	if (iLPMOverrideTimerQueued)
+		{
+		BTSocketTimer::Remove(iOverrideLPMTimerEntry);
+		iLPMOverrideTimerQueued = EFalse;
+		}
+
+	LOG(_L("sec\tClosing subscribers..."))
+
+	LOG(_L("sec\tClosing helpers..."))
+	TSglQueIter<CBTRegistryHelperBase> iter(iRegistryHelpers);
+	CBTRegistryHelperBase* helper;
+	// Detach from any Registry Helpers for which this object
+	// is the parent.
+	while(iter)
+		{
+		helper = iter++;
+		helper->DetachParent();
+		}
+
+	iLowPowModeCtrl.Abort();
+	iDisconnectCtrl.Abort();
+	iAuthenticationCtrl.Abort();
+
+	delete iPhysicalLinkMetrics;
+	delete iPinRequester;
+	delete iNumericComparator;
+	delete iPasskeyEntry;
+	delete iArbitrationDelay;
+	delete iRoleSwitchCompleteCallBack;
+	delete iEncryptionEnforcer;
+
+	DeleteRoleSwitcher();
+	}
+
+void CPhysicalLink::ConstructL()
+	{
+	LOG_FUNC
+	LOG1(_L("CPhysicalLink::ConstructL() this = 0x%08x"), this);
+	GetDeviceFromRegistryL();
+	iArbitrationDelay = CArbitrationDelayTimer::NewL(this);
+
+	TCallBack cb1(RoleSwitchCompleteCallBack, this);
+	iRoleSwitchCompleteCallBack = new (ELeave)CAsyncCallBack(cb1, EActiveHighPriority);
+
+	TCallBack cb2(OverrideLPMTimeoutCallback, this);
+	iOverrideLPMTimerEntry.Set(cb2);
+
+	iPhysicalLinkMetrics = CPhysicalLinkMetrics::NewL(*this, iLinksMan.HCIFacade().CommandQController());
+	}
+
+TBool CPhysicalLink::HasHandle(THCIConnHandle aHandle) const
+	{
+	LOG_FUNC
+	if (iHandle == aHandle)
+		return ETrue;	// Only one ACL handle is available.
+	if (iSyncLogicalLink && iSyncLogicalLink->Handle() == aHandle)
+		return ETrue;
+	return EFalse;
+	}
+
+TBool CPhysicalLink::HasSyncLink() const
+	{
+	LOG_FUNC
+	return (iSyncLogicalLink != NULL);
+	}
+
+void CPhysicalLink::SubscribeLinkObserver(MPhysicalLinkObserver& aObserver)
+/**
+The subscribed object will be notified whenever a link state change occurs.
+**/
+	{
+	LOG_FUNC
+#ifdef _DEBUG
+	//ensure the subscriber isn't already subscribed...
+	TInt found = 0;
+	TDblQueIter<MPhysicalLinkObserver> iter(iBasebandSubscribers);
+	while (iter)
+		{
+		if (iter++ == &aObserver)
+			{
+			found++;
+			}
+		}
+
+	__ASSERT_DEBUG(found==0, Panic(ELinkMgrBadBasebandArray));
+#endif	//_DEBUG
+
+	iBasebandSubscribers.AddFirst(aObserver.ObserverQLink());
+	}
+
+void CPhysicalLink::UnsubscribeLinkObserver(MPhysicalLinkObserver& aObserver)
+/**
+aSubscriber will no longer be notified of state changes.
+**/
+	{
+	LOG_FUNC
+	aObserver.ObserverQLink().Deque();
+	}
+
+TInt CPhysicalLink::TryToAndThenPreventHostEncryptionKeyRefresh(TAny* aOutToken)
+	{
+	LOG_FUNC
+	TInt err = KErrNone;
+	// The handling of the TAny* parameter seems a bit wacky - but it makes sense as follows
+	// this call handles a call from the bluetooth control plane (which passes
+	// only a TAny* as a parameter).  We need to return a value back through as well, so we need
+	// a pointer to a pointer (so after using the input it can be modified to point to the
+	// output).  We need a Bluetooth device address so a pointer to a pointer to a TBTDevAddr 
+	// is passed down. Then the pointer to a pointer is used to update the pointer to a control
+	// plane token (which represents a handle preventing host encryption key refreshes).
+	if (!IsEncryptionPauseResumeSupported())
+		{
+		err = KErrNotSupported;
+		*reinterpret_cast<MBluetoothControlPlaneToken**>(aOutToken) = NULL;
+		}
+	else
+		{
+		if (iAutoKeyRefreshQue.IsEmpty())
+			{
+			TRAP_IGNORE(iLinksMan.HCIFacade().RefreshEncryptionKeyL(iHandle));
+			// If we can't refresh the encryption key, there's not much we can do
+			}
+		XAutoKeyRefreshToken* token = new XAutoKeyRefreshToken();
+		if (token)
+			{
+			iAutoKeyRefreshQue.AddLast(*token);
+			}
+		else
+			{
+			err = KErrNoMemory;
+			}
+		*reinterpret_cast<MBluetoothControlPlaneToken**>(aOutToken) = token;
+		}
+	return err;
+	}
+
+void CPhysicalLink::RegistryTaskComplete(CBTRegistryHelperBase* aHelper, TInt /*aResult*/)
+/**
+	A task has completed where we don't expect a response - just cleanup helper
+**/
+	{
+	LOG_FUNC
+	// remove from array
+	iRegistryHelpers.Remove(*aHelper);
+	// delete
+	delete aHelper;
+	}
+
+/**
+The completion function of a registry helper returning a remote device entry.
+*/
+void CPhysicalLink::RegistryTaskComplete(CBTRegistryHelperBase* aHelper,
+										 const TBTNamelessDevice& aDevice,
+										 TInt aResult)
+/**
+	A registry task has completed where we expect a device to be returned
+**/
+	{
+	LOG_FUNC
+	LOG1(_L("CPhysicalLink: Registry task complete; result %d"), aResult);
+
+	// The TBTNamelessDevice assignment operator only performs an update on valid
+	// entries, as we need a bitwise copy (since we want to mirror what is in the registry)
+	// we need to go to these extras steps.
+	TPckg<TBTNamelessDevice> cachedVersion(iRegistryDevice);
+	cachedVersion.Copy(TPckg<TBTNamelessDevice>(aDevice));
+
+	// We have to be careful about information flow from the registry to the actual representation (iDevice).
+	// Although the values in the returned entry are useful if the stack has not yet got the information,
+	// if the stack has already gathered values then they will be more up-to-date.  Therefore we need to
+	// assign the current values first if they are valid to build up the latest representation.
+	TBTNamelessDevice device = aDevice; // Take a local copy to work on.
+	if(iDevice.IsValidDeviceClass())
+		{
+		device.SetDeviceClass(iDevice.DeviceClass());
+		}
+	if(iDevice.IsValidPageScanRepMode())
+		{
+		device.SetPageScanRepMode(iDevice.PageScanRepMode());
+		}
+	if(iDevice.IsValidPageScanPeriodMode())
+		{
+		device.SetPageScanPeriodMode(iDevice.PageScanPeriodMode());
+		}
+	if(iDevice.IsValidPageScanMode())
+		{
+		device.SetPageScanMode(iDevice.PageScanMode());
+		}
+	if(iDevice.IsValidClockOffset())
+		{
+		device.SetClockOffset(iDevice.ClockOffset());
+		}
+	if(iDevice.IsValidUsed())
+		{
+		device.SetUsed(iDevice.Used());
+		}
+	if(iDevice.IsValidSeen())
+		{
+		device.SetSeen(iDevice.Seen());
+		}
+
+	// With the link key (and friends) we need to be even more careful, as there is effectively
+	// two-way flow of information:
+	// 1) Link key storage from the stack.
+	// 2) Unbonding from the clients of the registry.
+	// Further to this, if the registry entry does not have a link key, it can mean one of several
+	// things:
+	// a) No link key has been generated with the remote device yet.
+	// b) A client of the registry has unbonded the remote device.
+	// c) The current link key is not to form a bond.
+	// Taking this into account we make a suitable choice about what to do.
+	if(!iDevice.IsValidLinkKey())
+		{
+		// If there isn't any link key in the system yet - and so it is safe to update the representation
+		// to use the value from the registry.
+		// <NOP> - see the global update below.
+		}
+	else
+		{
+		// We currently have a link key....
+		if(device.IsValidLinkKey() || iRegistryDevBeingMod.InUse())
+			{
+			// Even if the linkkey in registry is valid, it must be older or same as the current
+			// link key (as the stack is the only one who should be adding keys). Also, if we are in the
+			// middle of updating the registry (for any purpose) then we can ignore the registry value as it
+			// may not be up-to date. So, for both the cases just update with the latest.
+			// There is an interesting case if client modifies the registry by unpairing/or deleting the device
+			// and stack also tries to updates the link key in registry at the same time. We will give priority
+			// to stack's updations by keeping the latest copy as it is.
+			device.SetLinkKey(iDevice.LinkKey(), iDevice.LinkKeyType());
+			if(iDevice.IsValidPassKey())
+				{
+				device.SetPassKey(iDevice.PassKey());
+				}
+			}
+		else
+			{
+			// This is the interesting case where a client has unbonded, but we already have a link
+			// key.  What we do here is interesting.
+			if(IsAuthenticationPending() || LinkState().Authenticated() || SimplePairingMode() == EPhySimplePairingEnabled)
+
+				{
+				// If we're already authenticated / authenticating then we're already using our link
+				// key.  As such we need to continue using it, but we don't want to update
+				// Also if the link is SSP enabled then we can re-authenticate - this removes any chance of a
+ 				// race condition with the registry.
+				iPreventLinkKeyUpdateReg = ETrue; // prevent locally cached link key being given back to the registry
+				}
+			else
+				{
+				// The link key hasn't been used yet - so we can update the representation to not
+				// have a link key (as requested by the user).
+				// Currently there is no chance of a race with the registry as combination link keys are
+ 				// always form a bond.
+				iDevice.DeleteLinkKey(); // delete locally cached link key
+				}
+			}
+		}
+
+	// Finally update the representation - we should have manipulated it correctly.
+	iDevice = device;
+
+	// Store the result of the retrieval for usage later.
+	iDeviceResult = aResult;
+
+	// the HW asked earlier for a link key - we can respond now
+	__ASSERT_DEBUG(iDevice.IsValidAddress(), Panic(EBTPhysicalLinksInvalidAddress));
+	if(iWaitingForLinkKeyFromRegistry)
+		{
+		if (aResult == KErrNone && iDevice.IsValidLinkKey())
+			{
+			if ( iDevice.LinkKeyType() != ELinkKeyCombination)
+				{
+				if (iLinksMan.SecMan().DebugMode() && iDevice.LinkKeyType() != ELinkKeyDebug)
+					{
+					LOG(_L("CPhysicalLink: Debug mode - Link to debug link key"))
+					iAuthenticationCtrl.LinkKeyRequestNegativeReply(iDevice.Address());
+					}
+				else
+				if (iRequireAuthenticatedLinkKey && iDevice.LinkKeyType() == ELinkKeyUnauthenticatedUpgradable && IsPairable())
+					{
+					LOG(_L("CPhysicalLink: Requiring Authenticated link key but currently only have unauthenticated"))
+					iAuthenticationCtrl.LinkKeyRequestNegativeReply(iDevice.Address());
+					}
+				else
+					{
+					LOG(_L("CPhysicalLink: Issuing link key to HC now"))
+					iAuthenticationCtrl.LinkKeyRequestReply(iDevice.Address(), iDevice.LinkKey());
+					}
+				}
+			else if(IsPasskeyMinLengthOK() && SimplePairingMode() != EPhySimplePairingEnabled)
+				{
+				LOG(_L("CPhysicalLink: Issuing link key to HC now"))
+				iAuthenticationCtrl.LinkKeyRequestReply(iDevice.Address(), iDevice.LinkKey());
+				}
+			else
+				{
+				LOG(_L("CPhysicalLink: Current PIN code too short!"))
+				iAuthenticationCtrl.LinkKeyRequestNegativeReply(iDevice.Address());
+				}
+			}
+		else
+			{
+			iAuthenticationCtrl.LinkKeyRequestNegativeReply(iDevice.Address());
+			}	
+		}
+	iRequireAuthenticatedLinkKey = EFalse;
+	iWaitingForLinkKeyFromRegistry = EFalse;
+
+	RegistryTaskComplete(aHelper, aResult);	 // cleans up our helper
+	}
+
+void CPhysicalLink::RegistryTaskComplete(CBTRegistryHelperBase* aHelper, TRegistryUpdateStatus aRegUpdateStatus, TInt aResult)
+	{
+/**
+	This method allows the RegistryHelpers to indicate the registry action taking place
+
+**/
+	LOG_FUNC
+	if(iRemoteDeviceRecordedByStack == EPending && aRegUpdateStatus != ENoChange)
+		{
+		if (aResult == KErrNone)
+			{
+			iRemoteDeviceRecordedByStack = EComplete;
+			}
+		else
+			{
+			iRemoteDeviceRecordedByStack = ENull;
+			}
+		}
+
+	iRegistryDevBeingMod.RequestCompleted();
+	RegistryTaskComplete(aHelper, aResult);	 // cleans up our helper
+	}
+
+void CPhysicalLink::CompletedPackets(THCIConnHandle aConnH, TUint16 aNumPackets)
+/**
+	In this domain we are happy that a valid event has come in telling us that
+	the buffers in the local controller have been freed
+
+	Since this reflects both local hardware features (buffers) and flow control
+	we treat them separately
+
+**/
+	{
+	LOG_FUNC
+	// firstly report that local hardware buffers free (via inlines!)
+	iLinksMan.LinkManagerProtocol().LinkMuxer().DataQController().CompletedPackets(aConnH, aNumPackets);
+
+	// we forward this to the thing that sends the packets so that they can do Flow Control
+
+	// could speed up - already done this handle test to get into *this* object
+	if (iSyncLogicalLink && iSyncLogicalLink->Handle() == aConnH)
+		{
+		iSyncLogicalLink->PacketsSent(aConnH, aNumPackets);
+		}
+	else
+		{
+		for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
+			{
+			iACLLogicalLinks[i]->PacketsSent(aConnH, aNumPackets);
+			}
+		}
+	}
+
+void CPhysicalLink::TryToSend()
+	{
+	LOG_FUNC
+	for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
+		{
+		iACLLogicalLinks[i]->TryToSend();
+		}
+	}
+
+void CPhysicalLink::NewLinkKey(const TBTDevAddr& /*aAddr*/, const TBTLinkKey& aLinkKey, THCILinkKeyType aLinkKeyType)
+	{
+	LOG_FUNC
+	if(iLinkKeyPending)
+		//user has entered a PIN and a new link key has come in.
+		{
+		iLinkKeyPending = EFalse;
+		iPreventLinkKeyUpdateReg = EFalse; //override previous unpair so that link key will be stored in the registry.
+		}
+
+	TBTLinkKeyType linkKeyType = ELinkKeyCombination;
+
+	switch (aLinkKeyType)
+		{
+	case ECombinationKey:
+		linkKeyType = ELinkKeyCombination;
+		break;
+	case EDebugCombinationKey:
+		linkKeyType = ELinkKeyDebug;
+		break;
+	case EAuthenticatedCombinationKey:
+		linkKeyType = ELinkKeyAuthenticated;
+		break;
+	case EUnauthenticatedCombinationKey:
+		if (iLocalMITM || (iAuthenticationRequirement & KAuthenticationMitmReqMask) ||
+			(iIOCapability == EIOCapsDisplayOnly) || (iIOCapability == EIOCapsNoInputNoOutput))
+			{
+			linkKeyType = ELinkKeyUnauthenticatedNonUpgradable;
+			}
+		else
+			{
+			linkKeyType = ELinkKeyUnauthenticatedUpgradable;
+			}
+		break;
+	case EChangedCombinationKey:
+		linkKeyType = iDevice.LinkKeyType(); // The link key type hasn't changed
+		break;
+
+	case ELocalUnitKey:
+	case ERemoteUnitKey:
+	default:
+		LOG1(_L("CPhysicalLink: Unexpected link key type (%d)"), aLinkKeyType);
+		__ASSERT_DEBUG(EFalse, Panic(EBTUnexpectedLinkKeyType));
+		break;
+		}
+
+	if (linkKeyType == ELinkKeyCombination && iNewPinCodeValid)
+		{
+		// Only bind a PIN code to the device if we receive a link key based on it.
+		iNewPinCodeValid = EFalse;
+		iDevice.SetPassKey(iNewPinCode);
+		}
+
+
+	SetLinkKey(aLinkKey, linkKeyType);	// keeps a copy in our 'cache', updates paired list in PHYs mananger
+
+	TRAP_IGNORE(StoreDeviceL(EFalse)); //EFalse: new meaning - do not prevent addition
+	// if that errored we just have to keep it here - but it won't be in registry
+	// so it'll be a transient pairing
+	}
+
+void CPhysicalLink::UpdateFromInquiryCache()
+	{
+	LOG_FUNC
+	CBTInqResultRecord* juice = NULL; //juice is data from remote which can help/speed up a connection
+	// see if we can speed up the connection - the inquiry cache may have some juice
+	juice = iLinksMan.LinkManagerProtocol().InquiryMgr().BasebandParametersFromCache(iDevice.Address());
+	if(!juice)
+	//no juice in inquiry manager
+		{
+		return;
+		}
+
+	TInquiryLogEntry& jle = juice->LogEntry();
+	// Inquiry cache may have useful info we can use: put them in our device
+	// so that we can use this, and update this into the Registry later.
+	// This will allow Apps to get this to initiate faster connections.
+	// The update to the Registry doesn't happen here, but at other interesting times
+	ASSERT_DEBUG(jle.iBdaddr == iDevice.Address());
+	//Only update with juice values from inquiry manager if they are have come from HCI
+	//(i.e. from remote) - records in the inquiry manager are filled with default
+	//values and then fields are updated as relevant HCI events occur.
+	// - iDevice may already contain values found in the registry which are
+	//not default and therefore should have come themselves from the HCI
+	if(juice->IsPageScanModeFromHCI())
+		{
+		iDevice.SetPageScanMode(jle.iPageScanMode);
+		}
+	if(juice->IsPageScanRepModeFromHCI())
+		{
+		iDevice.SetPageScanRepMode(jle.iPageScanRepetitionMode);
+		}
+	if(juice->IsClockOffsetFromHCI())
+		{
+		iDevice.SetClockOffset(jle.iClockOffset);
+		}
+	}
+
+void CPhysicalLink::StoreDeviceL( TBool aPreventDeviceAddition )
+	{
+	LOG_FUNC
+
+	UpdateFromInquiryCache(); //check juice has not just come in - update iDevice if so
+
+	//only write back the necessary attributes
+	TBTNamelessDevice device;
+	if(iDevice.IsValidAddress())
+		{
+		device.SetAddress(iDevice.Address());
+		}
+	if(iDevice.IsValidDeviceClass())
+		{
+		device.SetDeviceClass(iDevice.DeviceClass());
+		}
+	if(iDevice.IsValidLinkKey() && !iPreventLinkKeyUpdateReg)
+		{
+		if(!(iIOCapsReceived && (iAuthenticationRequirement == EMitmNotReqNoBonding || iAuthenticationRequirement == EMitmReqNoBonding)))
+			{
+			LOG(_L("!!! Storing Link Key in Registry"));
+			device.SetLinkKey(iDevice.LinkKey(), iDevice.LinkKeyType());
+			}
+		}
+	if(iDevice.IsValidPageScanRepMode())
+		{
+		device.SetPageScanRepMode(iDevice.PageScanRepMode());
+		}
+	if(iDevice.IsValidPageScanPeriodMode())
+		{
+		device.SetPageScanPeriodMode(iDevice.PageScanPeriodMode());
+		}
+	if(iDevice.IsValidPageScanMode())
+		{
+		device.SetPageScanMode(iDevice.PageScanMode());
+		}
+	if(iDevice.IsValidClockOffset())
+		{
+		device.SetClockOffset(iDevice.ClockOffset());
+		}
+	if(iDevice.IsValidUsed())
+		{
+		device.SetUsed(iDevice.Used());
+		}
+	if(iDevice.IsValidSeen())
+		{
+		device.SetSeen(iDevice.Seen());
+		}
+	if(iDevice.IsValidPassKey())
+		{
+		device.SetPassKey(iDevice.PassKey());
+		}
+
+	// if already exists an outstanding request with the same device info
+	// is useless to store it again. Just return doing nothing.
+	if (iRegistryDevBeingMod.InUse() && iRegistryDevBeingMod.IsEqual(device))
+		{
+		return;	// do nothing
+		}
+
+	CBTDeviceModifier* modifier = CBTDeviceModifier::NewL(iRegSess, *this, iLinksMan.LinkManagerProtocol().InquiryMgr());
+	iRegistryHelpers.AddLast(*modifier);
+	TBool allowAdd = EFalse;
+
+	if(!aPreventDeviceAddition && iRemoteDeviceRecordedByStack == ENull)
+		//Only allow the device to be added to the registry if
+		// 1) this StoreDeviceL function has not been called
+		//	  with its "prevent device addition" parameter set to true
+		// 2) we believe the cached remote device details have not yet
+		//    been recorded in the registry.
+		//    (If they have and if they are no longer there it is
+		// 	   because an app has deleted them from the registry...
+		//	   so we should not re-add them )
+		{
+		allowAdd = ETrue;
+		}
+	modifier->Start(device, allowAdd);
+	iRegistryDevBeingMod.Begin(device);
+
+	if(allowAdd)
+		//The physical link object has now made its attempt to add its cached
+		//remote device details to the registry.
+		{
+		iRemoteDeviceRecordedByStack = EPending;
+		}
+
+	}
+
+void CPhysicalLink::SetLinkKey(const TBTLinkKey& aLinkKey, TBTLinkKeyType aLinkKeyType)
+	{
+	LOG_FUNC
+	iDevice.SetLinkKey(aLinkKey, aLinkKeyType); // keeps a copy in our 'cache'
+	}
+
+void CPhysicalLink::ReadRemoteSupportedFeaturesComplete(THCIErrorCode aErr, THCIConnHandle aConnH, const TBTFeatures& aBitMask)
+	{
+	LOG_FUNC
+	__CHECK_CONNECTION_HANDLE(aConnH);
+
+	if (aErr == KErrNone)
+		{
+		iRemoteFeatures = aBitMask;
+
+		if(!iConnectionPacketTypeChanged)
+			{
+			//Send change connection packet type HCI command if application hasn't, we have to do this
+			//because some hardware defaults to using 1-slot packets unless told otherwise by the host.
+			ChangeConnectionPacketType(EAnyACLPacket);
+			}
+
+		//If remote host supports extended features, then send request to read these.
+		if (iRemoteFeatures[ESupportedExtendedFeaturesBit])
+			{
+			TRAP_IGNORE(iLinksMan.HCIFacade().ReadRemoteExtendedFeaturesL(iHandle, KRemoteExtendedFeaturesPage1));
+			}
+		else
+			{
+			// If the remote doesn't support extended features, then neither they support SSP
+			// (no way to indicate the host supported bit).  So set it as disabled.
+			TPhysicalLinkSimplePairingMode previousSetting = SimplePairingMode();
+			__ASSERT_DEBUG(((SimplePairingMode() == EPhySimplePairingDisabled) || (SimplePairingMode() == EPhySimplePairingUndefined)),Panic(EBTSSPModeChangedDuringConnection));
+			iSimplePairingMode = EPhySimplePairingDisabled;
+			if(SimplePairingMode() != previousSetting)
+				{
+				iLinksMan.SecMan().SimplePairingSupportDetermined(BDAddr());
+				}
+			}
+		}
+	else
+		{
+		iRemoteFeatures = TBTFeatures(KInvalidRemoteFeatures);
+		}
+	}
+
+void CPhysicalLink::ReadRemoteExtendedFeaturesComplete(THCIErrorCode aErr, THCIConnHandle aConnH, TUint64 aBitMask, TUint8 aPageNumber, TUint8 /* aMaximumPageNumber */)
+	{
+	LOG_FUNC
+	__CHECK_CONNECTION_HANDLE(aConnH);
+
+	LOG1(_L("CPhysicalLink: ReadRemoteExtendedFeaturesComplete; result %d"), aErr);
+
+	switch (aPageNumber)
+		{
+		case KRemoteExtendedFeaturesPage1:
+			{
+			TPhysicalLinkSimplePairingMode currentSetting = SimplePairingMode();
+
+			if (aErr == EOK && aBitMask & (1 << ESecureSimplePairingHostSupportBit) && iLinksMan.SecMan().LocalSimplePairingMode())
+				{
+				__ASSERT_DEBUG(((SimplePairingMode() == EPhySimplePairingEnabled) || (SimplePairingMode() == EPhySimplePairingUndefined)),Panic(EBTSSPModeChangedDuringConnection));
+				iSimplePairingMode = EPhySimplePairingEnabled;
+				}
+			else
+				{
+				__ASSERT_DEBUG(((SimplePairingMode() == EPhySimplePairingDisabled) || (SimplePairingMode() == EPhySimplePairingUndefined)),Panic(EBTSSPModeChangedDuringConnection));
+				iSimplePairingMode = EPhySimplePairingDisabled;
+				}
+			if(SimplePairingMode()!=currentSetting)
+				{
+				iLinksMan.SecMan().SimplePairingSupportDetermined(BDAddr());
+				}			
+			break;
+			}
+		default:
+			{
+			// Ignore
+			}
+		}
+	}
+
+void CPhysicalLink::ReadRemoteVersionInfoComplete(THCIErrorCode aErr, THCIConnHandle aConnH, const TBTDevRemoteHwVersion& aVer)
+	{
+	LOG_FUNC
+	__CHECK_CONNECTION_HANDLE(aConnH);
+	if (aErr == KErrNone)
+		{
+		iRemoteVersion = aVer;
+		}
+	else
+		{
+		iRemoteVersion = KInvalidRemoteHwVersion;
+		}
+	}
+
+void CPhysicalLink::AuthenticationComplete(THCIErrorCode aErr, THCIConnHandle aConnH)
+	{
+	LOG_FUNC
+	__CHECK_CONNECTION_HANDLE(aConnH);
+	iLinkState.SetAuthenticated(aErr == EOK);
+	//__ASSERT_DEBUG(iAuthStateMask == EAuthenticationRequestPending,Panic(EUnexpectedAuthenticationState));
+	AuthenticationComplete(EAuthenticationRequestPending);
+
+	if (aErr!=EOK)
+		{
+		if(aErr == ERemoteUserEndedConnection)
+			{
+			HandlePrefetch();
+			}
+		iNewPinCodeValid = EFalse;
+		delete iPinRequester;
+		iPinRequester = NULL;
+		iPinHandler = NULL;
+		}
+	else
+		{
+		__ASSERT_DEBUG(!iPinHandler, Panic(EBTPhysicalLinkPinHandlerStillPresent));
+		}
+
+	iLinksMan.SecMan().AuthenticationComplete(BDAddr(), aErr);
+
+	TBTBasebandEventNotification event(ENotifyAuthenticationComplete, aErr);
+	NotifyStateChange(event);
+	}
+
+void CPhysicalLink::HandlePrefetch()
+	{
+	// Here we determine whether we need to handle a pre-fetch case.  If there is an
+	// outbound authentication attempt and we have a PIN notifier we need to hand it
+	// over to the pre-fetch manager.
+	// We also note the fact that we've done this for any access requester to know whether
+	// it should indicate to defer.
+	if(iPinRequester && iLinksMan.SecMan().IsOutboundAccessRequest(BDAddr()) && iSimplePairingMode != EPhySimplePairingEnabled)
+		{
+		TInt err = iLinksMan.PrefetchMan().HandleOverPinRequester(BDAddr(), iPinRequester);
+		if(err == KErrNone)
+			{
+			iPinRequester = NULL; // As far as phys links is concerned the pin requester doesn't exist anymore.
+			iPinHandler = NULL; // So the handler is also not needed.
+			}
+		}
+	}
+
+void CPhysicalLink::EncryptionChange(THCIErrorCode aErr, THCIConnHandle aConnH, TBool aEncrypted)
+	{
+	LOG_FUNC
+	__CHECK_CONNECTION_HANDLE(aConnH);
+
+	if(aErr == EOK)
+		{
+		// Only change state if it's an actual encryption change - not an error (because if an error
+		// we should presumably stay the same).
+		if(aEncrypted)
+			{
+			iLinkState.SetEncrypted(ETrue);
+			if(!iLinkState.Authenticated())
+				{
+				// If a link is encrypted then we know that the link is also authenticated.
+				iLinkState.SetAuthenticated(ETrue);
+				}
+			}
+		else
+			{
+			iLinkState.SetEncrypted(EFalse);
+			}
+		}
+
+	TBTBasebandEventNotification event(aEncrypted ? ENotifyEncryptionChangeOn : ENotifyEncryptionChangeOff, aErr);
+	NotifyStateChange(event);
+
+	// Having the encryption enforcer present means that we have asked for encryption.
+	// But we should only act if this is a valid encryption change event.
+	if (iEncryptionEnforcer && aErr == EOK)
+		{
+		// if the roleswitch has been requested by us then CRoleSwitcher has already parked / unparked
+		// the ACL controller. That means that the request is submitted twice.
+		// This is not supposed to be a problem, but we need to be aware of it.
+		if (!aEncrypted)
+			{
+			iLinksMan.LinkManagerProtocol().ACLController().SetParked(iHandle, ETrue);
+			iEncryptionEnforcer->EncryptionDisabled(SimplePairingMode()== EPhySimplePairingEnabled);
+			}
+		else
+			{
+			iLinksMan.LinkManagerProtocol().ACLController().SetParked(iHandle, EFalse);
+			iEncryptionEnforcer->EncryptionEnabled();
+			}
+		}
+	}
+
+TInt CPhysicalLink::ChangeEncryption(THCIEncryptModeFlag aEnable)
+	{
+	LOG_FUNC
+	// no policy to adjust, but test local & remote features
+	if (!IsEncryptionSupported())
+		{
+		return KErrNotSupported;
+		}
+
+	TRAPD(err, iLinksMan.HCIFacade().SetEncryptL(Handle(), aEnable));
+	if (err == KErrNone)
+		{
+		err = ManageEncryptionEnforcement(aEnable);
+		}
+
+	return err;
+	}
+
+TInt CPhysicalLink::Authenticate(TBool aRequireAuthenticatedLinkKey)
+	{
+	LOG_FUNC
+	// no policy to adjust, just check to see if the link is already authenticated
+	TInt err = KErrNone;
+	__ASSERT_DEBUG(iSimplePairingMode != EPhySimplePairingUndefined, Panic(EBTPhysicalLinksInvalidArgument));
+
+	/* If its a dedicated bonding attempt, then we always want to authenticate again in order to generate
+	 * a stronger linkkey if possible
+	 */
+	if (!iLinksMan.SecMan().IsDedicatedBondingAttempted(iDevice.Address()) && iLinkState.Authenticated() && !iLinksMan.LinkManagerProtocol().IsSecureSimplePairingSupportedLocally())
+		{
+		err = KErrAlreadyExists;
+		}
+	else
+		{
+		iRequireAuthenticatedLinkKey = aRequireAuthenticatedLinkKey;
+		TRAP(err, iLinksMan.HCIFacade().AuthenticateL(Handle()));
+		if(err == KErrNone)
+			{
+			SetAuthenticationPending(EAuthenticationRequestPending);
+			}
+		}
+	return err;
+	}
+
+TInt CPhysicalLink::ChangeLinkKey()
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iLinkState.Authenticated(), Panic(EBTPhysicalLinkNotAuthenticated));
+
+	// Check if the peer device can support the link key regeneration procedure.
+	if(PeerSupportsLinkKeyRegeneration())
+		{
+		return iLinksMan.HCIFacade().ChangeConnectionLinkKey(Handle());
+		}
+	return KErrNone;
+	}
+
+TBool CPhysicalLink::PeerSupportsLinkKeyRegeneration() const
+	{
+	LOG_FUNC
+	// Any future manufacturers or manufacturer versions that do not support
+	// link key re-generation should be added to this method.
+
+	// See Bluetooth assigned numbers for an explanation of this value.
+	return (iRemoteVersion.iManufacturerID != 0x000a);
+	}
+
+void CPhysicalLink::ACLDataReceived(THCIConnHandle aConnH, TUint8 aFlag, const TDesC8& aData)
+	{
+	LOG_FUNC
+	__CHECK_CONNECTION_HANDLE(aConnH);
+
+	// there may be a race condition in that we have got ACL data but just started
+	// shutdown procedures...
+
+	// find the ACL wanting this type of data...
+	for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
+		{
+		iACLLogicalLinks[i]->DataReceived(aConnH, aFlag, aData);
+		}
+	// else just dump
+	}
+
+void CPhysicalLink::SCODataReceived(THCIConnHandle aConnH, const TDesC8& aData)
+	{
+	LOG_FUNC
+	if (iSyncLogicalLink)
+		iSyncLogicalLink->DataReceived(aConnH, 0, aData);	// flags ignored
+
+	}
+
+void CPhysicalLink::ConnectionComplete(THCIErrorCode aErr, const TBTConnect& aConn)
+	{
+	LOG_FUNC
+	if (aErr == KErrNone)
+		{
+		if(aConn.iLinkType == ESCOLink && !iSyncLogicalLink)
+			{
+			LOG(_L("Got a ConnectionComplete for a non-existant SCO link"))
+			//This situation can occur if ESock deletes the iSyncLogicalLink whilst it is waiting for
+			//a remote device to respond to a connection request.
+			iLinksMan.Baseband().UpdateModelForConnectionError(aConn.iBdaddr, aConn.iLinkType);
+
+			if(aErr==EOK) // if error, aConn.iConnH will refer to the ACL link used to initialise the SCO link, so dont disconnect that
+				{
+				//The baseband might actually have established a SCO link, so send a Disconnect.
+				//If no SCO link exists the command will fail gracefully.
+				TRAP_IGNORE(iLinksMan.HCIFacade().DisconnectL(aConn.iConnH, ERemoteUserEndedConnection));
+				}
+
+			return;
+			}
+
+		// update bb model
+		iLinksMan.Baseband().UpdateModel(aConn);
+		if (aConn.iLinkType == EACLLink)
+			{
+			// we need to store the physical handle we're on - in BT1.2 this is also taken to be
+			// the ACL Link handle too...
+			// do the PHY stuff first
+			LOG(_L("CPhysicalLink: PHY ready"))
+
+			iHandle = aConn.iConnH;
+			iDevice.SetAddress(aConn.iBdaddr);
+
+			//Set Default Local Link Policy;
+			//Get this in early to try and avoid conditions where remote requests
+			//something we haven't told the controller we allow yet.
+			SetModesAllowed(iLinksMan.LinkManagerProtocol().ModesSupportedLocally(),
+							iLinksMan.RoleSwitchAllowed());
+
+			// try to get details of remote device
+			TRAP_IGNORE(GetRemoteDetailsL(aConn));
+			// ignore error - only optimisations
+
+			// assume we are going with 1 slot packets until notified otherwise, and in activemode
+			iLinkState.SetLinkState(TBTBasebandLinkState::ELinkUp);
+			iLinkState.SetMaxSlots(1);
+			iLinkState.SetLinkMode(EActiveMode);
+
+			// If the peer is in security mode 3 then authenication has completed.
+			if(iPeerInSecurityMode3)
+				{
+				iLinkState.SetAuthenticated(ETrue);
+				AuthenticationComplete(EAuthenticationRequestPending);
+				}
+
+			// update bb model for max slots
+			iLinksMan.Baseband().UpdateModel(aConn.iConnH, 1);
+
+			TBTBasebandEventNotification event(ENotifyPhysicalLinkUp);
+			NotifyStateChange(event);
+
+			if (aConn.iEncryptMode)
+				{
+				// pretend there's been an encryption event
+				EncryptionChange(aErr, aConn.iConnH, aConn.iEncryptMode);
+				}
+			}
+
+		// This is assuming that our stack only allows one synchronous link per phy link.
+ 		// SCO (not eSCO) Link getting notified here.
+ 		else if(aConn.iLinkType == ESCOLink)
+ 			{
+ 			TBTBasebandEventNotification event(ENotifySynchronousLinkUp);
+ 			NotifyStateChange(event);
+ 			}
+
+		// tell the logical links
+		NotifyLogicalLinkUp(aConn);
+		iLinksMan.ArbitrateAllPhysicalLinks();
+
+		// Check to see if we got a disconnect request during the period before the link was
+		// fully established.
+		if (iDisconnectRequested)
+			{
+			__ASSERT_DEBUG(aConn.iLinkType == EACLLink, Panic(EDisconnectRequestedNotOnACLLink));
+
+			// We allow the link to come up fully so that the link is not in a strange state between
+			// pending and up. This also means that the notifications to the logical links all work
+			// correctly, they just see a link come up and then go down again straight away.
+			TRAP_IGNORE(iLinksMan.HCIFacade().DisconnectL(aConn.iConnH, ERemoteUserEndedConnection));
+			}
+		else
+			{
+			TTime t;
+			t.UniversalTime();
+			iDevice.SetUsed(t);
+			TRAP_IGNORE(StoreDeviceL(EFalse));
+			}
+		}
+	else
+		{
+		// error occurred - need to see if it's PHY(and so ACL) or SCO that failed
+		// tell logical links
+		LOG2(_L("Physical link: connection complete returned an error on handle %d, type %d"), aConn.iConnH, aConn.iLinkType);
+
+		if(((aConn.iLinkType == ESCOLink)||(aConn.iLinkType == EeSCOLink)) && iSyncLogicalLink &&
+		iSyncLogicalLink->Handle() != KInvalidConnectionHandle && iSyncLogicalLink->Handle() != aConn.iConnH)
+			{
+			// This is a secondary SCO link we just rejected or something - don't mess with our own
+			// The KInvalidConnectionHandle test ensures that we ignore errored outgoing SCO connects.
+			return;
+			}
+
+		iLinksMan.Baseband().UpdateModelForConnectionError(aConn.iBdaddr, aConn.iLinkType);
+		NotifyLogicalLinkError(aConn.iLinkType, CHciUtil::SymbianErrorCode(aErr));
+		if (aConn.iLinkType == EACLLink)
+			{
+			// BT 1.2 says that as the ACL Link goes up and down, so does the physical link
+			// so if the ACL Link has gone, so has this
+			// for SCO we remain in place.
+			TBTBasebandEventNotification event(ENotifyPhysicalLinkError, CHciUtil::SymbianErrorCode(aErr));
+			NotifyStateChange(event);
+			delete this;
+			}
+		}
+	// ***Watchout*** delete this above: careful about code here
+	}
+
+
+void CPhysicalLink::SynchronousConnectionComplete(THCIErrorCode aErr,
+													const TBTConnect& aConn,
+													const TBTSyncConnectOpts& aSyncOpts)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(((aConn.iLinkType == ESCOLink) || (aConn.iLinkType == EeSCOLink)), Panic(EBTNonSyncConnectInSyncConnectFunc));
+
+	if(!iSyncLogicalLink)
+		{
+		LOG(_L("Got a SynchronousConnectionComplete when no SCO link is bound"))
+		//This situation can occur if ESock deletes the iSyncLogicalLink whilst it is waiting for
+		//a remote device to respond to a connection request.
+		iLinksMan.Baseband().UpdateModelForConnectionError(aConn.iBdaddr, aConn.iLinkType);
+
+		if(aErr==EOK) // if error, aConn.iConnH will refer to the ACL link used to initialise the SCO link, so dont disconnect that
+			{
+			//The baseband might actually have established a SCO link, so send a Disconnect.
+			//If no SCO link exists the command will fail gracefully.
+			TRAP_IGNORE(iLinksMan.HCIFacade().DisconnectL(aConn.iConnH, ERemoteUserEndedConnection));
+			}
+
+		return;
+		}
+
+	if (aErr == KErrNone)
+		{
+		// update bb model
+		iLinksMan.Baseband().UpdateModel(aConn);
+
+ 		// This is assuming that our stack only allows one synchronous link per phy link.
+ 		// eSCO (not SCO) Link getting notified here.
+ 		TBTBasebandEventNotification event(ENotifySynchronousLinkUp);
+ 		NotifyStateChange(event);
+
+		// tell the logical links
+		NotifyLogicalSyncLinkUp(aConn, aSyncOpts);
+		iLinksMan.ArbitrateAllPhysicalLinks();
+		}
+	else
+		{
+		// error occurred - need to see if it's PHY(and so ACL) or SCO that failed
+		// tell logical links
+		LOG2(_L("Physical link: connection complete returned an error on handle %d, type %d"), aConn.iConnH, aConn.iLinkType);
+		iLinksMan.Baseband().UpdateModelForConnectionError(aConn.iBdaddr, aConn.iLinkType);
+
+		// Before passing error around, check if it's actually for this link...
+		// A listening link will have a NULL handle, otherwise the handle should match.
+		// If the eSCO connection fails, the handle is KInvalidConnectionHandle (as initialised in CBTBasebandSAP)
+		// This avoids the situation where we get notified of a rejection.
+		if (iSyncLogicalLink &&
+			(iSyncLogicalLink->Handle() == 0 || iSyncLogicalLink->Handle() == KInvalidConnectionHandle || iSyncLogicalLink->Handle() == aConn.iConnH))
+			{
+			NotifyLogicalLinkError(aConn.iLinkType, CHciUtil::SymbianErrorCode(aErr));
+			}
+		}
+	}
+
+
+void CPhysicalLink::GetRemoteDetailsL(const TBTConnect& aConn)
+	{
+	LOG_FUNC
+	GetDeviceFromRegistryL();
+	iLinksMan.HCIFacade().ReadClockOffsetL(aConn.iConnH);
+	iLinksMan.HCIFacade().ReadRemoteSupportedFeaturesL(aConn.iConnH);
+	iLinksMan.HCIFacade().ReadRemoteVersionL(aConn.iConnH);
+	iLinksMan.LinkManagerProtocol().InquiryMgr().ReadRemoteNameL(aConn.iBdaddr);
+	}
+
+void CPhysicalLink::LinkSupervisionTimeoutChange(THCIErrorCode aErr, THCIConnHandle aConnH, TUint16 aNewTimeout)
+	{
+	LOG_FUNC
+	__CHECK_CONNECTION_HANDLE(aConnH);
+
+	if(aErr == EOK)
+		{
+		iLSTO = aNewTimeout;
+		}
+	}
+
+void CPhysicalLink::PacketTypeChange(THCIErrorCode aErr, THCIConnHandle aConnH, TUint16 aNewPacket)
+	{
+	LOG_FUNC
+	__CHECK_CONNECTION_HANDLE(aConnH);
+
+	if(aErr==EOK)
+		{
+		iLinksMan.Baseband().UpdateModelIfRecordExists(aConnH, aNewPacket);
+		iLinkState.SetPacketTypes(aNewPacket);
+		}
+
+	TUint32 eventType = 0;
+
+	if(aNewPacket & EPacketsDM1)
+		eventType |= ENotifyPacketsDM1;
+	if(aNewPacket & EPacketsDH1)
+		eventType |= ENotifyPacketsDH1;
+	if(aNewPacket & EPacketsDM3)
+		eventType |= ENotifyPacketsDM3;
+	if(aNewPacket & EPacketsDH3)
+		eventType |= ENotifyPacketsDH3;
+	if(aNewPacket & EPacketsDM5)
+		eventType |= ENotifyPacketsDM5;
+	if(aNewPacket & EPacketsDH5)
+		eventType |= ENotifyPacketsDH5;
+
+	if(aNewPacket & EPacketsHV1)
+		eventType |= ENotifyPacketsHV1;
+	if(aNewPacket & EPacketsHV2)
+		eventType |= ENotifyPacketsHV2;
+	if(aNewPacket & EPacketsHV3)
+		eventType |= ENotifyPacketsHV3;
+
+	TBTBasebandEventNotification event(eventType, aErr);
+	NotifyStateChange(event);
+	}
+
+void CPhysicalLink::MaxSlotsChange(THCIConnHandle aConnH, TUint8 aSlots)
+	{
+	LOG_FUNC
+	__CHECK_CONNECTION_HANDLE(aConnH);
+	LOG2(_L("Connection Handle 0x%04x, using %d slots"), aConnH, aSlots);
+	iLinksMan.Baseband().UpdateModel(aConnH, aSlots);	//event only for ACL
+	iLinkState.SetMaxSlots(aSlots);
+
+	TBTBasebandEventNotification event;
+	switch(aSlots)
+		{
+		case 1:
+			event.SetEventType(ENotifyMaxSlots1);
+			break;
+		case 3:
+			event.SetEventType(ENotifyMaxSlots3);
+			break;
+		case 5:
+			event.SetEventType(ENotifyMaxSlots5);
+			break;
+		};
+	NotifyStateChange(event);
+	}
+
+void CPhysicalLink::ModeChange(THCIErrorCode aErr, THCIConnHandle aConnH, TBTLinkMode aMode, TBasebandTime aInterval)
+	{
+	LOG_FUNC
+	__CHECK_CONNECTION_HANDLE(aConnH);
+	LOG3(_L("Connection Handle 0x%04x: ModeChange event %d->%d"), aConnH, iLinkState.LinkMode(), aMode);
+
+	TBTLinkMode oldMode = iLinkState.LinkMode();
+
+	iLinkState.SetLinkMode(aMode);	// remember the state for ourselves
+
+	if (aErr == EOK)
+		{
+		if (aMode == EParkMode)
+			{
+			iLinksMan.Baseband().ParkLink(aConnH);	//remove from model whilst parked
+			}
+
+		if (aMode == EActiveMode && oldMode==EParkMode)
+			{
+			// unparking we must tell the DataQ - we could always get the dataQ to ask us before sending of course
+			// but that might be performance harming
+			iLinksMan.Baseband().UnParkLink(aConnH); //NB Max Slots = 1 default
+			}
+
+		if (aMode == ESniffMode )
+			{
+			// Store the Sniff Interval
+			iSniffInterval = aInterval;
+			}
+
+		if (aMode == EActiveMode && iSniffInterval)
+			{
+			//Active mode enable and SniffInterval is set.
+			//Therefore must be leaving sniff mode.
+			__ASSERT_DEBUG(oldMode==ESniffMode, Panic(EBTPhysicalLinkModeChangeErrorLeavingSniffMode));
+
+			//When not in sniff mode, sniff interval is obviously 0!
+			iSniffInterval = 0;
+			}
+		}
+
+	// Generate a baseband event, and offer it to the proxySAP's associated
+	// with this link.
+	TBTBasebandEventNotification event;
+	switch(aMode)
+		{
+		case EActiveMode:
+			event.SetEventType(ENotifyActiveMode);
+			break;
+		case ESniffMode:
+			event.SetEventType(ENotifySniffMode);
+			break;
+		case EHoldMode:
+			event.SetEventType(ENotifyHoldMode);
+			break;
+		case EParkMode:
+			event.SetEventType(ENotifyParkMode);
+			break;
+		case EScatterMode:
+			break; // shouldnt be seen yet - if so, ignore for now
+		};
+	event.SetErrorCode(aErr);
+	NotifyStateChange(event);
+	}
+
+void CPhysicalLink::RoleChange(THCIErrorCode aErr, const TBTDevAddr& /*aAddr*/, TBTBasebandRole aRole)
+	{
+	LOG_FUNC
+	LOG1(_L("CPhysicalLink: Role change - new role %S"), aRole == EMaster ? _S("Master") : _S("Slave"));
+
+	if (aErr == EOK)
+		{
+		iLinkState.SetLinkRole(aRole);
+		}
+
+	// Generate a baseband event, and offer it to the proxySAP's associated
+	// with this link.
+	TBTBasebandEventNotification event;
+	if(aRole == EMaster)
+		{
+		event.SetEventType(ENotifyMaster);
+		}
+	else
+		{
+		event.SetEventType(ENotifySlave);
+		}
+	event.SetErrorCode(aErr);
+	NotifyStateChange(event);
+
+	// if iEncryptionEnforcer is present it means we asked for encryption, so we inform it
+	// about the roleswitch
+	if (iEncryptionEnforcer)
+		{
+		iEncryptionEnforcer->RoleSwitchEvent();
+		}
+	}
+
+void CPhysicalLink::WriteLinkPolicySettingsCompleteEvent(THCIErrorCode aErr, THCIConnHandle aConnH)
+	{
+	LOG_FUNC
+	__CHECK_CONNECTION_HANDLE(aConnH);
+	// we don't get the things back we set...
+	if (aErr != EOK)
+		{
+		// The command has failed.	Clear the cached values and re-arbitrate.
+		iLinkPolicy.SetModesAllowed(0);
+		iLinkPolicy.SetSwitchAllowed(EFalse);
+		Arbitrate();
+		}
+	}
+
+void CPhysicalLink::ClockOffset(THCIErrorCode aErr, THCIConnHandle aConnH, TBasebandTime aClockOffset)
+	{
+	LOG_FUNC
+	__CHECK_CONNECTION_HANDLE(aConnH);
+	if (aErr==EOK)
+		{
+		// tell inquiry manager - we found this information to help it
+		iLinksMan.LinkManagerProtocol().InquiryMgr().ClockOffsetResult(BDAddr(), aClockOffset);
+		}
+	// we don't do anything more useful knowing this information
+	}
+
+void CPhysicalLink::RemoteName(THCIErrorCode aErr, const TBTDevAddr& /*aAddr*/, const TBTDeviceName8& aName)
+/**
+	We don't keep this, but we're interested in persisting it in el Reg
+	By receiving this we now our attempts have completed in getting name
+*/
+	{
+	LOG_FUNC
+	if (aErr==EOK)
+		{
+		// slam it into the Registry - this will be useful to UIs if not us
+		TRAP_IGNORE(DoUpdateNameL(aName));
+		}
+	}
+
+void CPhysicalLink::DoUpdateNameL(const TBTDeviceName8& aName)
+	{
+	LOG_FUNC
+	CBTDeviceNameChanger* nameChanger = CBTDeviceNameChanger::NewL(iRegSess, *this);
+	iRegistryHelpers.AddLast(*nameChanger);
+	nameChanger->Start(BDAddr(), aName);
+	}
+
+void CPhysicalLink::Disconnection(THCIErrorCode aErr, THCIConnHandle aConnH, THCIErrorCode aResult)
+	{
+	LOG_FUNC
+	// tell our socket if it's our handle - otherwise the SCO SAP
+
+	LOG1(_L("Physical link: disconnection on handle %d"), aConnH);
+	LOG1(_L(" ACL link handle:	 %d"), Handle());
+#ifdef __FLOG_ACTIVE
+	if (iSyncLogicalLink) LOG1(_L(" Sync link handle: %d"), iSyncLogicalLink->Handle());
+#endif
+
+	//no matter if it is the socket or SCO SAP the ui dialogs will be cancelled
+	if (iNumericComparator && iNumericComparator->IsActive())
+		{
+		iNumericComparator->Cancel();
+		}
+	if (iPasskeyEntry && iPasskeyEntry->IsActive())
+		{
+		iPasskeyEntry->Cancel();
+		}
+
+	if (aConnH == Handle())
+		{
+		TBTBasebandEventNotification event;
+		switch(aErr)
+			{
+			case EHardwareFail:
+				{
+
+				if(iLinkState.LinkState() == TBTBasebandLinkState::ELinkUp ||
+				   iLinkState.LinkState() == TBTBasebandLinkState::ELinkPending)
+					{
+					//Only propagate if link is up or pending, otherwise
+					//we may get disconnection in unexpected states
+
+					//Fall through
+					}
+				else
+					{
+					// this object must go when no link
+					delete this;
+					break;
+					}
+				}
+			case EOK:
+				{
+				iLinkState.SetLinkState(TBTBasebandLinkState::ELinkDown);
+
+				// PHY went down - so have logical links then
+				NotifyLogicalLinkDown(EACLLink);
+				NotifyLogicalLinkDown(ESCOLink);
+
+				event.SetEventType(ENotifyPhysicalLinkDown);
+				event.SetErrorCode(aResult);
+				iLinksMan.Baseband().UpdateModelForDisconnection(aConnH, EACLLink);
+				HandlePrefetch();
+				NotifyStateChange(event);
+				// this object must go when no link
+				delete this;
+				break;
+				}
+			case ECommandDisallowed:
+				{
+				// The connection is still up, so do nothing, and wait for the next idle timer to fire
+				break;
+				}
+			default:
+				{
+				iLinkState.SetLinkState(TBTBasebandLinkState::ELinkDown);
+
+				// there's an error
+				NotifyLogicalLinkError(EACLLink, aErr);
+				NotifyLogicalLinkError(ESCOLink, aErr);
+				event.SetEventType(ENotifyPhysicalLinkError);
+				event.SetErrorCode(aErr);
+				// baseband model needs to err on the side of least bandwidth usage -
+				// a link COULD have come down
+				iLinksMan.Baseband().UpdateModelForDisconnection(aConnH, EACLLink);
+				HandlePrefetch();
+				NotifyStateChange(event);
+				// this object must go when no link
+				delete this;
+				break;
+				}
+			}
+		}
+
+	else if (iSyncLogicalLink && aConnH == iSyncLogicalLink->Handle())
+		{
+		TBTBasebandEventNotification event;
+		// just the SCO link is down so we'd better notify
+		if (aErr)
+			{
+			LOG(_L("Physical link: error disconnection on sync link"));
+			TLinkType type = iSyncLogicalLink->LinkType();
+			iSyncLogicalLink->Error(CHciUtil::SymbianErrorCode(aErr));
+			event.SetEventType(ENotifySynchronousLinkError);
+			event.SetErrorCode(aErr);
+			// baseband model needs to err on the side of least bandwidth usage -
+			// a sync link COULD have come down
+			iLinksMan.Baseband().UpdateModelForDisconnection(aConnH, type);
+			}
+		else
+			{
+			LOG(_L("Physical link: normal disconnection on sync link"));
+			TLinkType type = iSyncLogicalLink->LinkType();
+			iSyncLogicalLink->Disconnection();
+			event.SetEventType(ENotifySynchronousLinkDown);
+			event.SetErrorCode(aResult);
+			iLinksMan.Baseband().UpdateModelForDisconnection(aConnH, type);
+			}
+
+		NotifyStateChange(event);
+		}
+	// NOTE!! delete this above - careful about adding code
+	}
+
+void CPhysicalLink::ConnectionRequest(const TBTConnect& aConn)
+	{
+	LOG_FUNC
+	// we must be page scan enable for this to come in
+	// but still need to see if it got routed a listening SAP
+	TBool accept = EFalse;
+	TLogicalLinkListener* listener = NULL;
+
+	__ASSERT_DEBUG(iPendingConnection == EFalse, Panic(EPendingConnectionNotCleared));
+
+	switch (aConn.iLinkType)
+		{
+		case ESCOLink:
+		case EeSCOLink:
+			if (!iSyncLogicalLink)
+				{
+				// If we already have a sync link active, reject request.
+				LOG(_L("No current link, checking for listener"));
+				listener = iLinksMan.FindListener(aConn.iLinkType);
+				}
+			break;
+
+		default:
+			listener = iLinksMan.FindListener(aConn.iLinkType);
+			break;
+		}
+
+	if (listener)
+		{
+		accept = ETrue;
+		if (iLinksMan.IsAcceptPairedOnlyMode())
+			{
+			// do not accept if we are only accepting connection
+			// requests from paired devices and we are not already
+			// paired with the remote
+			CBTPairingsCache::TPairingState pairingState = iLinksMan.PairingsCache().IsPaired(aConn.iBdaddr);
+
+			if(pairingState == CBTPairingsCache::EDeferred)
+				{
+				// We're still waiting for the Pairing Caches paired device list to be filled.
+ 				// We'll respond when this is complete, so store details away for then.
+				LOG(_L("CPhysicalLink: Waiting for physical link manager's paired device list from Registry!"))
+				iPendingConnection = ETrue;
+    			iLastPendingConnection.iConnH = aConn.iConnH;
+			    iLastPendingConnection.iBdaddr = aConn.iBdaddr;
+			    iLastPendingConnection.iCoD = aConn.iCoD;
+			    iLastPendingConnection.iLinkType = aConn.iLinkType;
+			    iLastPendingConnection.iEncryptMode = aConn.iEncryptMode;
+			    // Return now as we are waiting and don't want to 'RejectConnection'
+				return;
+				}
+
+			// If here then the cache has either informed us that the device is paired
+ 			// or not.  We only accept paired connections.
+ 			accept = (pairingState == CBTPairingsCache::EPaired) ? ETrue : EFalse;
+			}
+		if (accept)
+			{
+			// this may mean the physical link's acceptance is determined by the
+			// ACL Logical link - but that's what the spec implies :-)
+			accept = static_cast<MLogicalLink*>(listener->iObserver)->ConnectRequest(aConn, *this);
+			// since we only support one SCO listener at present we dont need to mark it as the acceptor
+			// or indeed find a specific one: later we could choose via CoD
+			}
+		}
+
+	if (accept)
+		{
+
+		// store CoD now, not told it ever again! if connection fails this object will go
+		// Don't update CoD if it is 0 (NULL) as this is the default.
+		// This also prevents a valid CoD being overwritten.
+		if (aConn.iCoD != 0)
+			{
+			iDevice.SetDeviceClass(aConn.iCoD);
+			}
+
+		TUint8 roleSwitch = static_cast<TUint8>(iLinksMan.PassiveConnectBecomeMaster() ? 0x00 : 0x01);
+
+		TInt err;
+		if (aConn.iLinkType == EeSCOLink)
+			{
+			TBTeSCOLinkParams options;
+
+			CeSCOLink* eSCO = static_cast<CeSCOLink*>(listener->iObserver);
+			TUint16 packetMask = eSCO->GetPacketMask();
+			eSCO->GetExtOptions(options);
+
+			TRAP(err, iLinksMan.HCIFacade().AcceptSynchronousConnectionL(aConn.iBdaddr,
+				options.iBandwidth.iTransmit, options.iBandwidth.iReceive,
+				options.iLatency, options.iCoding, options.iRetransmissionEffort,
+				packetMask
+				));
+			}
+		else
+			{
+			TRAP(err, iLinksMan.HCIFacade().AcceptConnectionRequestL(aConn.iBdaddr, roleSwitch));
+			}
+
+		if (err == KErrNone)
+			{
+			// assume we're slave until told otherwise on ACL(=PHY) links
+			if (aConn.iLinkType == EACLLink)
+				{
+				// this is a PHY connect request too, so we should store our role
+				//The handle in 'aConn' is bogus, so..
+				iLinkState.SetLinkState(TBTBasebandLinkState::ELinkPending);
+				iLinksMan.Baseband().UpdateModel(aConn.iBdaddr, KHCIDefaultPacketType, aConn.iLinkType);
+				iLinkState.SetLinkRole((roleSwitch == 0x01) ? ESlave : EMaster);
+
+				// have to store it now, since the complete wont tell us anything!
+				}
+			else
+				{
+				//The handle in 'aConn' is bogus, so..
+				iLinksMan.Baseband().UpdateModel(aConn.iBdaddr, KHCIDefaultSCOPacketType, aConn.iLinkType);
+				}
+			}
+		else
+			{
+			// Out of memory:
+			// Cause newly created spawned SAP (whether ACL or SCO)
+			// to die.
+			// Attempt to reject connection with newly freed memory...
+			// ...this of course may fail in which case we have
+			// to rely on supervison timeouts.
+			if (aConn.iLinkType == EACLLink)
+				{
+				TInt last = iACLLogicalLinks.Count() - 1;
+				if(last>=0)
+					{
+					iACLLogicalLinks[last]->Error(err);
+					}
+				}
+			else
+				{
+				if(iSyncLogicalLink)
+					{
+					iSyncLogicalLink->Error(err);
+					}
+				}
+			RejectConnection(aConn);
+			}
+		}
+	else
+		{
+		// reject
+		RejectConnection(aConn);
+		}
+
+	// Tell inquiry manager - should have a valid CoD within 'aConn'
+	// Don't update CoD if it is 0 (NULL) as this is the default.
+	// This also prevents a valid CoD being overwritten.
+	if (aConn.iCoD != 0)
+		{
+		iLinksMan.LinkManagerProtocol().InquiryMgr().CoDResult(aConn.iBdaddr, aConn.iCoD);
+		}
+	}
+
+void CPhysicalLink::RejectConnection(const TBTConnect& aConn)
+	{
+	LOG_FUNC
+	if (aConn.iLinkType == EeSCOLink)
+		{
+		TRAP_IGNORE(iLinksMan.HCIFacade().RejectSynchronousConnectionL(aConn.iBdaddr, KDefaultRejectReason));
+		}
+	else
+		{
+		TRAP_IGNORE(iLinksMan.HCIFacade().RejectConnectionRequestL(aConn, KDefaultRejectReason));
+		}
+	}
+
+
+void CPhysicalLink::GetDeviceFromRegistryL()
+	{
+	LOG_FUNC
+	CBTDeviceGetter* getter = CBTDeviceGetter::NewL(iRegSess, *this);
+	iRegistryHelpers.AddLast(*getter);
+	getter->Start(BDAddr());		// now get the rest of the details
+	// just BDAddr?
+	}
+
+TInt CPhysicalLink::GetOption(TUint aLevel,TUint aName,TDes8& aOption) const
+	{
+	LOG_FUNC
+	// good stuff here!
+	if (aLevel == KSolBtLM)
+		{
+		switch (aName)
+			{
+			case KLMGetBasebandHandle:
+				{
+				if (aOption.Length() != sizeof(THCIConnHandle))
+					{
+					return KErrArgument;
+					}
+				aOption = TPtrC8(reinterpret_cast<const TUint8*>(&iHandle), sizeof(THCIConnHandle));
+				return KErrNone;
+				}
+
+			case EBBGetSniffInterval:
+				if (aOption.Length() != sizeof(TBasebandTime))
+					{
+					return KErrArgument;
+					}
+				aOption = TPtrC8(reinterpret_cast<const TUint8*>(&iSniffInterval), sizeof(TBasebandTime));
+			    return KErrNone;
+
+			default:
+				return KErrNotSupported;
+			}
+		}
+	else
+		{
+		return KErrNotSupported;
+		}
+	}
+
+TInt CPhysicalLink::Arbitrate(const TBool aImmediately, const TBool aLocalPriority)
+	{
+	LOG_FUNC
+	if (!IsConnected())
+		return KErrDisconnected;
+
+	if ( aImmediately )
+		{
+		iArbitrationDelay->Cancel();
+		}
+	else if (iArbitrationDelay->IsActive())
+		{
+		return KErrNone;
+		}
+
+	//start arbitrate process with what our local controller supports
+	TUint8 allowedModesMask = EHoldMode | EParkMode | ESniffMode; // local features sorted out later
+	TBool roleSwitchAllowed = EFalse;
+
+ 	if (iLinksMan.LinkManagerProtocol().IsRoleSwitchSupportedLocally() && iLinksMan.RoleSwitchAllowed())
+ 		{
+ 		roleSwitchAllowed = ETrue;
+ 		}
+
+	// ask proxies what they want from the PHY
+ 	TUint8 requestedModeMask = 0;
+ 	TUint8 requestedMode = 0;
+ 	TBool activeModeIsRequested = EFalse;
+	TSglQueIter<CBTProxySAP> iter(iProxySAPs);
+	while (iter)
+		{
+		CBTProxySAP* proxy = iter++;
+
+ 		requestedMode = proxy->GetRequestedModes();
+ 		requestedModeMask |= requestedMode;
+
+		if (requestedMode == EActiveMode && proxy->RequestedActiveMode())
+ 			{
+ 			// An Active Mode request will override all other local low power mode requests
+ 			// but continue to collect the requirement from the other proxies..
+ 			activeModeIsRequested = ETrue;
+ 			}
+
+		allowedModesMask &= proxy->GetAllowedModes();
+		roleSwitchAllowed &= proxy->IsRoleSwitchAllowed();
+		}
+
+ 	if (activeModeIsRequested)
+ 		{
+ 		// Any Active Mode request will override all other low power mode requests,
+ 		// so overwrite the requestedModeMask but keep allowedModesMask and roleSwitchAllowed
+ 		// as specified by all the local proxies
+ 		requestedModeMask = EActiveMode;
+ 		}
+
+	// clear out modes not supported by local Controller
+	allowedModesMask &= iLinksMan.LinkManagerProtocol().ModesSupportedLocally();
+
+	if(iOverrideParkRequests)
+		{
+		// We wish to ensure the physical link is not in PARK mode.
+		// The only way to guarantee this is to disallow PARK via the link policy settings.
+		allowedModesMask &= ~EParkMode;
+		}
+
+	if(allowedModesMask != iLinkPolicy.LowPowerModePolicy()
+		|| roleSwitchAllowed != iLinkPolicy.IsSwitchAllowed())
+		{
+		// Controller policy for the connection needs updating
+		SetModesAllowed(allowedModesMask, roleSwitchAllowed);
+		}
+
+	//If OverrideLPM flag is set, we do not disable LP modes via the link policy settings
+	//This is done because OverrideLPM should not prevent remotes putting us into an LPM
+	//Later on, when OverrideLPM flag is cancelled, it would allow us to enforce requested LPM
+	if(iOverrideLPMRequests)
+		{
+		// We need to ensure the physical link is in active mode.
+		allowedModesMask = EActiveMode;
+		}
+
+	TUint8 modeChangeMask = static_cast<TUint8>(requestedModeMask & allowedModesMask);
+	TUint8 modeCompareMask = 0;
+
+	if(aLocalPriority)
+		{
+		// If we want local priority, we go with what we want to do
+		// irrespective of what the remote may have previously requested.
+		modeCompareMask = modeChangeMask;
+		}
+	else
+		{
+ 		// This logic allows us to see if the current low power mode has recently
+ 		// changed NOT because of a change in the local proxies' requests, but (probably)
+ 		// because a remote device has changed it..
+
+		// modeCompareMask should start only having zero bits where
+		// requestedModeMask has a zero bit and iPreviousRequestedModeMask does not
+		// i.e. a mode is newly no longer requested.
+		modeCompareMask = requestedModeMask | ~iPreviousRequestedModeMask;
+
+		// Remove bits from modeCompareMask that are not in allowedModesMask
+		// We cannot stay in a power mode that we do not allow.
+		modeCompareMask &= allowedModesMask;
+		}
+
+	iPreviousRequestedModeMask = requestedModeMask; // Update previous requested to current value.
+
+	TUint8 currentModeMask = static_cast<TUint8>(iLinkState.LinkMode());
+	if(modeCompareMask & currentModeMask)
+		{
+		// The current state is the same as the permitted required role(s).
+		return KErrNone;
+		}
+
+	if(modeChangeMask == EActiveMode && currentModeMask != EActiveMode)
+		{
+		// The current low power mode should be exited.
+		return RequestActive();
+		}
+
+	if(modeChangeMask != EActiveMode)
+		{
+		if(currentModeMask != EActiveMode)
+			{
+			// The system is currently in a low power mode.  Exit this before
+			// entering the new mode.
+			TInt rerr = RequestActive();
+			if(rerr != KErrNone)
+				{
+				return rerr;
+				}
+			}
+
+		if(modeChangeMask & EHoldMode)
+			{
+			return RequestHold();
+			}
+		if(modeChangeMask & ESniffMode)
+			{
+			return RequestSniff();
+			}
+		if(modeChangeMask & EParkMode)
+			{
+			return RequestPark();
+			}
+		}
+
+	// This point in the code is reached if the Link Policy settings are
+	// changed but the mode is not.	 Return OK error code.
+	return KErrNone;
+	}
+
+void CPhysicalLink::SetPassKey(const TDesC8& aPassKey)
+	{
+	LOG_FUNC
+	// We store the key for use if it succeeds.
+	iNewPinCode.Copy(aPassKey);
+	iNewPinCodeValid = ETrue;
+	}
+
+const TBTPinCode& CPhysicalLink::PassKey() const
+	{
+	LOG_FUNC
+	return iDevice.PassKey();
+	}
+
+void CPhysicalLink::StartArbitrationTimer() const
+	{
+	LOG_FUNC
+	iArbitrationDelay->Start();
+	}
+
+TInt CPhysicalLink::Connect(TBasebandPageTimePolicy aPolicy)
+	{
+	LOG_FUNC
+	// assume that we will be master until told otherwise
+	ASSERT_DEBUG(!IsConnected());
+
+	UpdateFromInquiryCache();
+
+	TUint8 psrm = iDevice.IsValidPageScanRepMode() ? iDevice.PageScanRepMode() : TUint8(KDefaultBluetoothPageScanRepMode);
+	TUint8 psm = iDevice.IsValidPageScanMode() ? iDevice.PageScanMode() : TUint8(KDefaultBluetoothPageScanMode);
+	TUint16 clockOffset = iDevice.IsValidClockOffset() ? iDevice.ClockOffset() : TUint16(KDefaultBluetoothClockOffset);
+
+	TUint8 allowRoleSwitch = static_cast<TUint8>(iLinksMan.ActiveConnectRoleSwitchAllowed());
+
+	TUint16 pkt = KHCIDefaultPacketType;
+
+	// optimise paging (as a best-effort attempt).
+	TBasebandTime pagetimeout = CalculatePageTimeout(aPolicy, psrm, clockOffset & KHCIClockOffsetValidBit);
+	iLinksMan.TryToChangePageTimeout(pagetimeout);
+
+	TRAPD(ret, iLinksMan.HCIFacade().ConnectL(iDevice.Address(), pkt, psrm, psm, clockOffset, allowRoleSwitch));
+	if(ret==KErrNone)
+		{
+		iLinkState.SetLinkState(TBTBasebandLinkState::ELinkPending);
+		iLinksMan.Baseband().UpdateModel(iDevice.Address(), pkt, EACLLink);
+		iLinkState.SetLinkRole(EMaster);
+		}
+
+	return ret;
+	}
+
+TInt CPhysicalLink::SCOConnect()
+/**
+	A utility service provided to the SCO transport
+	We bring up a "default" SCO link, choosing the packet based on what we
+	know the controllers can support
+*/
+	{
+	LOG_FUNC
+	return SCOConnect(EAnySCOPacket);
+	}
+
+TInt CPhysicalLink::SCOConnect(const TUint16 aUserHVPacketTypes)
+/**
+	A utility service provided to the SCO transport
+	We bring up a SCO link which allows only the user specified packet types
+	Implementation of SCOSAP::SetOption guarantees aUserHVPacketTypes contains at
+	least one valid SCO packet type
+*/
+	{
+	LOG_FUNC
+	TBTSCOPackets scoPackets = iLinksMan.LinkManagerProtocol().PacketsSupportedLocally().SCOPackets();
+
+	scoPackets &= aUserHVPacketTypes;
+	// combine with remote supported packets
+	scoPackets &= iRemoteFeatures.SCOPackets();
+
+	TInt ret;
+
+	if (!scoPackets)
+		ret = KErrNotSupported;
+	else
+		{
+		// we put all in for now - if HW doesn't support that, the HCI can be changed
+		OverridePark();
+
+		TRAP(ret, iLinksMan.HCIFacade().SCOConnectL(Handle(), scoPackets, BDAddr()));
+		if(ret==KErrNone)
+			{
+			iLinksMan.Baseband().UpdateModel(Handle(), EACLLink, scoPackets, ESCOLink);
+			}
+		}
+	return ret;
+	}
+
+TInt CPhysicalLink::SynchronousConnect(TUint aTransmitBandwidth, TUint aReceiveBandwidth,
+		TUint16 aMaxLatency, TUint16 aVoiceSettings,
+		TUint8 aRetransmissionEffort, const TBTSyncPacketTypes aUserPacketTypes)
+/**
+	A utility service provided to the eSCO transport
+	We bring up a eSCO link which allows only the user specified packet types
+	Implementation of SCOSAP::SetOption guarantees aUserPacketTypes contains at
+	least one valid SCO packet type
+*/
+	{
+	LOG_FUNC
+	TBTSyncPacketTypes escoPackets = iLinksMan.LinkManagerProtocol().PacketsSupportedLocally().SyncPackets();
+
+	escoPackets &= aUserPacketTypes;
+	// combine with remote supported packets
+	escoPackets &= iRemoteFeatures.SyncPackets();
+
+	TInt ret;
+
+	if (!escoPackets)
+		ret = KErrNotSupported;
+	else
+		{
+		// we put all in for now - if HW doesn't support that, the HCI can be changed
+		// THCIConnHandle aACLHandle, TUint aTransmitBandwidth, TUint aReceiveBandwidth,
+		//	TUint16 aMaxLatency, TUint16 aVoiceSettings, TUint8	 aRetransmissionEffort,
+		//	TUint16 aPacketTypeMask
+		TRAP(ret, iLinksMan.HCIFacade().SetupSynchronousConnectionCommandL(
+			Handle(),
+			aTransmitBandwidth, aReceiveBandwidth,
+			aMaxLatency, aVoiceSettings, aRetransmissionEffort,
+			escoPackets,
+			BDAddr()
+			));
+		if(ret==KErrNone)
+			{
+			iLinksMan.Baseband().UpdateModel(Handle(), EACLLink, escoPackets, EeSCOLink);
+			}
+		}
+	return ret;
+	}
+
+TInt CPhysicalLink::SubscribeProxySAP(CBTProxySAP& aProxySAP)
+	{
+	LOG_FUNC
+	// a new Proxy SAP wishes to attach to this physical SAP
+#ifdef _DEBUG
+	// check to see if the same proxy is already there
+	TSglQueIter<CBTProxySAP> iter(iProxySAPs);
+	while (iter)
+		{
+		ASSERT_DEBUG(iter++!=&aProxySAP);
+		}
+#endif
+	SubscribeLinkObserver(aProxySAP);
+	iProxySAPs.AddLast(aProxySAP);
+	Arbitrate();
+	return KErrNone;
+	}
+
+void CPhysicalLink::UnsubscribeProxySAP(CBTProxySAP& aProxySAP)
+	{
+	LOG_FUNC
+	ASSERT_DEBUG(!iProxySAPs.IsEmpty());
+	iProxySAPs.Remove(aProxySAP);
+	UnsubscribeLinkObserver(aProxySAP);
+	Arbitrate(); // good time to see what phy properties should be used
+	PhysicalLinkUserIdle();
+	}
+
+
+TInt CPhysicalLink::AttachLogicalLink(TLinkType aLink, CBTBasebandSAP& aSAP)
+	{
+	// effectively this part of this class encapsulates the Logical Transports
+	// but they're not really worthy of a class - just some understanding that they're
+	// not ignored
+	LOG_FUNC
+	TInt retVal = KErrNone;
+
+	switch (aLink)
+		{
+		case EACLLink:
+			{
+			// it is important for error handling at the end of
+			// CPhysicalLink::ConnectionRequest that this remains an
+			// append i.e. 'aSAP' is added to the end of the queue
+			retVal = iACLLogicalLinks.Append(static_cast<CACLLink*>(&aSAP));
+			break;
+			}
+
+		case ESCOLink:
+			{
+			if (!(iRemoteFeatures.IsSCOLinkSupported()))
+				retVal = KErrNotSupported;
+			else
+				{
+				// Don't attach SCO if already exists, avoids confusion with some h/w.
+				// .....return the outcome, higher levels depend on this action
+				if (iSyncLogicalLink)
+					{
+					retVal = KErrInUse;
+					}
+				else
+					{
+					iSyncLogicalLink = static_cast<CSCOLink*>(&aSAP);
+					}
+				}
+			break;
+			}
+
+		case EeSCOLink:
+			{
+			if (!(iRemoteFeatures.IsExtendedSCOLinkSupported()))
+				retVal = KErrNotSupported;
+			else
+				{
+				if (iSyncLogicalLink)
+					{
+					retVal = KErrInUse;
+					}
+				else
+					{
+					iSyncLogicalLink = static_cast<CeSCOLink*>(&aSAP);
+					}
+				}
+			break;
+			}
+
+		default:
+			Panic(EBTUnknownLogicalLink);
+		}
+
+	if (retVal == KErrNone)
+		{
+		RemoveIdleTimer();
+		}
+
+	return retVal;
+	}
+
+void CPhysicalLink::DetachLogicalLink(TLinkType aLink, CBTBasebandSAP& aSAP)
+	{
+	LOG_FUNC
+	LOG3(_L("CPhysicalLink: Logical Link type %d, 0x%08x detaching from PHY 0x%08x"), aLink, &aSAP, this);
+	switch (aLink)
+		{
+		case EACLLink:
+			{
+#ifdef _DEBUG
+			TInt numFound = 0;
+#endif
+			for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
+				{
+				if (iACLLogicalLinks[i] == &aSAP)
+					{
+#ifdef _DEBUG
+					numFound++;
+#endif
+					iACLLogicalLinks.Remove(i); // don't delete - it's not ours!
+					}
+				}
+			__ASSERT_DEBUG(numFound==1, Panic(EBTACLLogicalLinkBadDebind));
+			break;
+			}
+
+		case ESCOLink:
+		case EeSCOLink:
+			{
+			__ASSERT_DEBUG(iSyncLogicalLink==&aSAP, Panic(EBTSCOLogicalLinkBadDebind));
+			iSyncLogicalLink = NULL;
+			break;
+			}
+
+		default:
+			Panic(EBTUnknownLogicalLink);
+		}
+	}
+
+void CPhysicalLink::PhysicalLinkUserIdle()
+	{
+	LOG_FUNC
+	if(IsPhysicalLinkIdle() && iLinkState.LinkState() == TBTBasebandLinkState::ELinkUp)
+		{
+		// try to stash device details - juice and last used date
+		TTime t;
+		t.UniversalTime();
+		iDevice.SetUsed(t);
+		TRAPD(ignore, StoreDeviceL(ETrue)); //ETrue: new meaning - prevent addition of device in registry
+
+		if (!iIdleTimerQueued)
+			// we don't want to send the read clock offset command more than one
+			// here, so just check if we already passed from here.
+			{
+			// Take this opportunity to get latest info on clock offset
+			// so that we can stash it in HR - subsequent connections may then be quicker
+			TRAP(ignore, iLinksMan.HCIFacade().ReadClockOffsetL(iHandle));
+			// ignore error - was an optimisation
+
+			// Disconnect the PHY, it is no longer required.
+			QueueIdleTimer(KPhysicalLinkIdleTimeout);
+			}
+		}
+	}
+
+
+TBool CPhysicalLink::IsPhysicalLinkIdle() const
+	{
+	LOG_FUNC
+	TBool physicalLinkIdle = ETrue;
+	// Check ACL and SCO links.	 If any link is not idle, then the PHY remains
+	// active.
+	for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
+		{
+		physicalLinkIdle &= iACLLogicalLinks[i]->IsIdle();
+		}
+
+	if(iSyncLogicalLink)
+		{
+		physicalLinkIdle &= iSyncLogicalLink->IsIdle();
+		}
+
+ 	if (iPinRequester)
+ 		{
+ 		physicalLinkIdle = EFalse;
+ 		}
+
+ 	if (IsAuthenticationPending())
+ 		{
+ 		physicalLinkIdle = EFalse;
+ 		}
+
+	return physicalLinkIdle;
+	}
+
+
+void CPhysicalLink::QueueIdleTimer(TInt aTime)
+/**
+	Queue idle timer entry.
+	When this timer expires, it'll call TryToClose, which actually
+	causes the thing to finally close down.
+**/
+	{
+	LOG_FUNC
+	if (!iIdleTimerQueued)
+		{
+		TCallBack cb(TerminateCallback, this);
+		iIdleTimerEntry.Set(cb);
+		iIdleTimeout = aTime;
+		BTSocketTimer::Queue(iIdleTimeout * 1000000, iIdleTimerEntry);
+		iIdleTimerQueued = ETrue;
+		}
+	}
+
+
+void CPhysicalLink::RemoveIdleTimer()
+/**
+	Called whenever we're opened.
+	Checks there are no idle timer entries queued.
+**/
+	{
+	LOG_FUNC
+	if (!iIdleTimerQueued)
+		{
+		// it's fine for callers to just try to remove the timer
+		return;
+		}
+	BTSocketTimer::Remove(iIdleTimerEntry);
+	iIdleTimerQueued = EFalse;
+	}
+
+void CPhysicalLink::QueueLPMOverrideTimer(TInt aTimeout)
+/**
+	Queue LPM Override timer entry.
+	When this timer expires, it'll call UndoLPMOverride.
+**/
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(aTimeout!=0, Panic(EBTPhysicalLinksInvalidArgument));
+	if (iLPMOverrideTimerQueued)
+		{
+		BTSocketTimer::Remove(iOverrideLPMTimerEntry);
+		}
+	BTSocketTimer::Queue(aTimeout, iOverrideLPMTimerEntry);
+	iLPMOverrideTimerQueued = ETrue;
+	}
+
+void CPhysicalLink::NotifyStateChange(TBTBasebandEventNotification& aEvent)
+	{
+	LOG_FUNC
+	// If this event is 'physical link down' / 'physical link error' then any ProxySAP
+	// subscribers will unsubscribe as a result of PhysicalLinkChange being called.
+	// This will result in the entry being removed from the iBasebandSubscribers array.
+	// Consequently the array must be traversed from end to start.
+	TDblQueIter<TPhysicalLinkObserverQLink> iter(iBasebandSubscribers);
+	TPhysicalLinkObserverQLink* l;
+	while ((l=iter++)!=NULL)
+		{
+		l->Item()->PhysicalLinkChange(aEvent, *this);
+		}
+
+	if(((aEvent.EventType() & ENotifyActiveMode) ||
+		(aEvent.EventType() & ENotifySniffMode)||
+		(aEvent.EventType() & ENotifyParkMode)||
+		(aEvent.EventType() & ENotifyHoldMode)) &&
+		(aEvent.ErrorCode() == KErrNone))
+		{
+		iArbitrationDelay->Start();
+		}
+	}
+
+/*static*/ TInt CPhysicalLink::TerminateCallback(TAny* aCPhysicalLink)
+	{
+	LOG_STATIC_FUNC
+	CPhysicalLink* c = reinterpret_cast<CPhysicalLink*>(aCPhysicalLink);
+	c->iIdleTimerQueued = EFalse;
+
+	TInt retVal = EFalse;
+	
+	// Queue another idle timer in case this disconnect fails
+	
+	// Check if the physical link is still idle after the timer.
+	if(c->IsPhysicalLinkIdle() && c->iLinkState.LinkState() == TBTBasebandLinkState::ELinkUp)
+		{
+		// Increase the idle time for the next callback by just over 50%
+		TInt newIdleTime = (c->iIdleTimeout * 3)/2 + 1;
+		if (newIdleTime > KMaxPhysicalLinkIdleTimeout)
+			{
+			newIdleTime = KMaxPhysicalLinkIdleTimeout;
+			}
+
+		c->QueueIdleTimer(newIdleTime);
+		if (c->Terminate(ERemoteUserEndedConnection) == KErrNone)
+			{
+			retVal = EFalse;
+			}
+		else
+			{
+			retVal = ETrue;
+			}
+		}
+	return retVal;
+	}
+
+TInt CPhysicalLink::Terminate(THCIErrorCode aReason)
+	{
+	LOG_FUNC
+	TInt err = KErrNone;
+
+	if (iLinkState.LinkState() == TBTBasebandLinkState::ELinkPending)
+		{
+		// If the Link is not yet up then we cannot know the correct connection handle
+		// to disconnect so remember the request so we disconnect the link straight away
+		// after it does come up.
+		iDisconnectRequested = ETrue;
+		}
+	else
+		{
+		__ASSERT_DEBUG(iLinkState.LinkState() == TBTBasebandLinkState::ELinkUp, Panic(EInvalidLinkStateDuringDisconnect));
+
+		TRAP(err, iDisconnectCtrl.Disconnect(aReason));
+		}
+
+	return err;
+	}
+
+void CPhysicalLink::SetModesAllowed(TUint8 aModesAllowed, TBool aRoleSwitchAllowed)
+	{
+	LOG_FUNC
+	TBTLinkModeSet supportedModes = iRemoteFeatures.LinkModes();
+	aModesAllowed &= supportedModes;
+	aRoleSwitchAllowed &= IsRoleSwitchSupported();
+
+	if(aModesAllowed != iLinkPolicy.LowPowerModePolicy() ||
+	   aRoleSwitchAllowed != iLinkPolicy.IsSwitchAllowed())
+		{
+		TLinkPolicy tempPolicy;
+		tempPolicy.SetModesAllowed(aModesAllowed);
+		tempPolicy.SetSwitchAllowed(aRoleSwitchAllowed);
+
+		TRAPD(err, iLinksMan.HCIFacade().WriteLinkPolicySettingsL(Handle(), tempPolicy.LinkPolicy()));
+
+		if (err==KErrNone)
+			{
+			iLinkPolicy = tempPolicy;
+			}
+		}
+	}
+
+void CPhysicalLink::NotifyLogicalLinkDown(TPhysicalLinkPort aPort)
+	{
+	LOG_FUNC
+	switch (aPort)
+		{
+		case EACLLink:
+			{
+			for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
+				{
+				// subscribers will unsubscribe as a result of this
+				// This will result in the entry being removed from the iACLLogicalLinks array.
+				// Consequently the array must be traversed from end to start.
+				iACLLogicalLinks[i]->Disconnection();
+				}
+			break;
+			}
+		case ESCOLink:
+		case EeSCOLink:
+			{
+			if (iSyncLogicalLink)
+				{
+				iSyncLogicalLink->Disconnection();
+				}
+			break;
+			}
+
+		default:
+			Panic(EBTPhysicalLinkBadPort);
+		}
+	}
+
+void CPhysicalLink::NotifyLogicalLinkUp(const TBTConnect& aConnect)
+	{
+	LOG_FUNC
+	switch (aConnect.iLinkType)
+		{
+		case EACLLink:
+			{
+			for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
+				{
+				// all of them are open
+				iACLLogicalLinks[i]->ConnectComplete(aConnect);
+				}
+			break;
+			}
+
+		case ESCOLink:
+		case EeSCOLink:
+			{
+			if (iSyncLogicalLink)
+				{
+				iSyncLogicalLink->ConnectComplete(aConnect);
+				}
+			break;
+			}
+
+		default:
+			Panic(EBTPhysicalLinkBadPort);
+		}
+	}
+
+void CPhysicalLink::NotifyLogicalSyncLinkUp(const TBTConnect& aConnect, const TBTSyncConnectOpts& aSyncOpts)
+	{
+	LOG_FUNC
+	switch (aConnect.iLinkType)
+		{
+		case ESCOLink:
+		case EeSCOLink:
+			{
+			if (iSyncLogicalLink)
+				{
+				iSyncLogicalLink->SyncConnectComplete(aConnect, aSyncOpts);
+				}
+			break;
+			}
+
+		default:
+			Panic(EBTPhysicalLinkBadPort);
+		}
+	}
+
+
+void CPhysicalLink::NotifyLogicalLinkError(TPhysicalLinkPort aPort, TInt aError)
+	{
+	LOG_FUNC
+	switch (aPort)
+		{
+		case EACLLink:
+			{
+			for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
+				{
+				// all of them are open
+				iACLLogicalLinks[i]->Error(aError);
+				}
+			break;
+			}
+
+		case ESCOLink:
+		case EeSCOLink:
+			{
+			if (iSyncLogicalLink)
+				{
+				iSyncLogicalLink->Error(aError);
+				}
+			break;
+			}
+
+		default:
+			Panic(EBTPhysicalLinkBadPort);
+		}
+	}
+
+
+TBool CPhysicalLink::IsModeSupportedRemotely(TBTLinkMode aMode) const
+	{
+	LOG_FUNC
+	return iRemoteFeatures.LinkModes() & aMode;
+	}
+
+TBool CPhysicalLink::IsEncryptionPauseResumeSupported() const
+	{
+	LOG_FUNC
+	return (iLinksMan.LinkManagerProtocol().IsEncryptionPauseResumeSupportedLocally() && iRemoteFeatures.IsEncryptionPauseResumeSupported());
+	}
+
+TBool CPhysicalLink::IsEncryptionSupported() const
+	{
+	LOG_FUNC
+	return (iRemoteFeatures.IsEncryptionSupported() && iLinksMan.LinkManagerProtocol().IsEncryptionSupportedLocally());
+	}
+
+TBool CPhysicalLink::IsRoleSwitchSupported() const
+	{
+	LOG_FUNC
+	// the physical link _can_ switch when encryption is on - see RequestChangeRole
+	return (iRemoteFeatures.IsRoleSwitchSupported() && iLinksMan.RoleSwitchAllowed());
+	}
+
+
+//
+// Change the packet types allowed on the link.
+// Note that this is only suitable for use on ACL and old-style (pre eSCO) SCO links.
+//
+TInt CPhysicalLink::ChangeConnectionPacketType(TUint16 aType)
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+
+	// Build a mask of locally supported packet types (we shouldn't care about
+	// what the remote supports (this is alluded to in the spec) probably
+	// because they can change too.
+
+	// DM1, DH1 and HV1 packets are always supported.
+	TUint16 supportedTypes = EPacketsDM1 | EPacketsDH1 | EPacketsHV1;
+
+	// get extra locally-supported packets (our controller may not support things)
+	TBTFeatures localPackets = iLinksMan.LinkManagerProtocol().PacketsSupportedLocally();
+
+	// 3 slot ACL packets
+	if (localPackets[ESupportedThreeSlotPacketsBit])
+		{
+		supportedTypes |= (EPacketsDM3 | EPacketsDH3);
+		}
+
+	// 5 slot ACL packets
+	if (localPackets[ESupportedFiveSlotPacketsBit])
+		{
+		supportedTypes |= (EPacketsDM5 | EPacketsDH5);
+		}
+
+	// HV2 single-slot packets
+	if (localPackets[ESupportedHV2PacketsBit])
+		{
+		supportedTypes |= EPacketsHV2;
+		}
+
+	// HV3 single-slot packets
+	if (localPackets[ESupportedHV3PacketsBit])
+		{
+		supportedTypes |= EPacketsHV3;
+		}
+
+	// Note: If one of the following EDR bits is set it indicates that the EDR
+	// packet type shall NOT be used on this physical link. However, we must
+	// not set any packet types that are not supported, so check first.
+
+	// EDR 2Mbps
+	if (localPackets[EEDR_ACL_2MbpsModeBit])
+		{
+		supportedTypes |= EPackets2_DH1;
+		// EDR 3 slot ACL packets
+		if (localPackets[ESupportedEDRThreeSlotPacketsBit])
+			{
+			supportedTypes |= EPackets2_DH3;
+			}
+		// EDR 5 slot ACL packets
+		if (localPackets[ESupportedEDRFiveSlotPacketsBit])
+			{
+			supportedTypes |= EPackets2_DH5;
+			}
+
+		// EDR 3Mbps - Have to have 2Mbps to have 3Mbps
+		if (localPackets[EEDR_ACL_3MbpsModeBit])
+			{
+			supportedTypes |= EPackets3_DH1;
+			// EDR 3 slot ACL packets
+			if (localPackets[ESupportedEDRThreeSlotPacketsBit])
+				{
+				supportedTypes |= EPackets3_DH3;
+				}
+			// EDR 5 slot ACL packets
+			if (localPackets[ESupportedEDRFiveSlotPacketsBit])
+				{
+				supportedTypes |= EPackets3_DH5;
+				}
+			}
+		}
+
+	supportedTypes &= aType;
+	// Check if the new supported types differ from the current configuration.
+	if(supportedTypes != iLinkState.PacketTypes())
+		{
+		rerr = iLinksMan.HCIFacade().ChangeConnectionPacketType(iHandle, supportedTypes);
+		}
+
+	if(rerr == KErrNone)
+		{
+		iConnectionPacketTypeChanged = ETrue;
+		}
+
+	return rerr;
+	}
+
+TInt CPhysicalLink::ExitMode(TBTLinkMode aMode)
+	{
+	LOG_FUNC
+	return iLowPowModeCtrl.ExitMode(aMode, iHandle);
+	}
+
+TInt CPhysicalLink::RequestMode(TBTLinkMode aMode)
+	{
+	LOG_FUNC
+	if (!IsConnected())
+		return KErrDisconnected;
+
+	// if active mode is required, try to exit whatever Low Power mode we are in -
+	// if neither sniff nor park nothing will happen.
+	if(aMode == EActiveMode)
+		{
+		return ExitMode(iLinkState.LinkMode());
+		}
+
+	// now request this connection goes to requested mode
+	return iLowPowModeCtrl.ChangeMode(aMode, iHandle);
+	}
+
+TInt CPhysicalLink::RequestChangeRole(TBTBasebandRole aRole)
+	{
+	LOG_FUNC
+	TInt err = KErrNone;
+
+	// Check that the requested role is not the current role.
+	if(iLinkState.LinkRole() != aRole)
+		{
+		if(!iLinkPolicy.IsSwitchAllowed())
+			{
+			err = KErrNotSupported;
+			}
+
+		// 1) Role switch is not allowed if there is a sync connection
+		//    (or there is a sync connection negotiation in progress)
+		else if (HasSyncLink() || SyncConnectPending())
+			{
+			err = KErrNotReady;
+			}
+
+		// 2) Check if role switch is supported on the remote device
+		else if (!IsRoleSwitchSupported())
+			{
+ 			err = KErrNotSupported;
+			}
+
+		// 3) Check if role switch is supported locally
+ 		else if (!iLinksMan.LinkManagerProtocol().IsRoleSwitchSupportedLocally())
+ 			{
+ 			err = KErrNotSupported;
+ 			}
+
+		// Same role request is already with LinkMgr, or most probably running just now
+		else if (iRoleSwitcher)
+			{
+			LOG(_L("CPhysicalLink - Same role change in progress!"))
+			err = KErrNone;
+			}
+
+		// ok to change role
+		// we kick off a role switcher state machine, to remove LPM and encryption, prior to
+		// role switch
+		else
+			{
+			TRAP(err, iRoleSwitcher = CRoleSwitcher::NewL(iLinksMan, *this, aRole));
+			}
+		}
+#ifdef _DEBUG
+	else
+		{
+		LOG(_L("CPhysicalLink We are in requested role!!!"))		
+		}
+#endif
+
+	return err;
+	}
+
+void CPhysicalLink::AsyncDeleteRoleSwitcher()
+	{
+	LOG_FUNC
+	iRoleSwitchCompleteCallBack->CallBack();
+	}
+
+/*static*/ TInt CPhysicalLink::RoleSwitchCompleteCallBack(TAny* aPhysicalLink)
+	{
+	LOG_STATIC_FUNC
+	CPhysicalLink* physicalLink = static_cast<CPhysicalLink*>(aPhysicalLink);
+	physicalLink->DeleteRoleSwitcher();
+	return EFalse;
+	}
+
+void CPhysicalLink::DeleteRoleSwitcher()
+	{
+	LOG_FUNC
+	delete iRoleSwitcher;
+	iRoleSwitcher = NULL;
+	}
+
+TBool CPhysicalLink::IsEncryptionDisabledForRoleSwitch() const
+/**
+	If link is encrypted, but role switcher temporarily disabled encryption, returns true.
+**/
+	{
+	LOG_FUNC
+	if (iRoleSwitcher)
+		{
+		return iRoleSwitcher->IsEncryptionDisabledForRoleSwitch();
+		}
+	return EFalse;
+	}
+
+TInt CPhysicalLink::OverridePark()
+/**
+	A request has come in that requires us to ensure we are not parked.
+
+	Try to make it happen
+**/
+	{
+	LOG_FUNC
+	iOverrideParkRequests = ETrue;
+
+	// Only arbitrate if Park mode is currently active.
+	TInt rerr = KErrNone;
+	if(iLinkState.LinkMode() & EParkMode)
+		{
+		rerr = Arbitrate();
+		}
+	return rerr;
+	}
+
+TInt CPhysicalLink::UndoOverridePark()
+/**
+	A need to ensure we are not parked has gone.
+
+	Try to go back to power mode resulting from an unoverriden Arbitrate
+**/
+	{
+	LOG_FUNC
+	iOverrideParkRequests = EFalse;
+
+	//Arbitrate even if there isn't an outstanding local park mode request beacuse
+	//the remote device may be requesting park mode.
+	return Arbitrate();
+	}
+
+TInt CPhysicalLink::OverrideLPMWithTimeout(TUint aTimeout)
+	{
+	LOG_FUNC
+	if(aTimeout == 0)
+		{
+		return KErrNone; //facility not wanted
+		}
+
+	TInt rerr = OverrideLPM();
+	QueueLPMOverrideTimer(aTimeout);
+
+	return rerr;
+	}
+
+TInt CPhysicalLink::OverrideLPM()
+/**
+	A request has come in that requires us to ensure we are not using
+	an LPM (low power mode).
+
+	Try to make it happen
+**/
+	{
+	LOG_FUNC
+	iOverrideLPMRequests = ETrue;
+
+	return Arbitrate();
+	}
+
+/*static*/ TInt CPhysicalLink::OverrideLPMTimeoutCallback(TAny* aCPhysicalLink)
+	{
+	LOG_STATIC_FUNC	
+	CPhysicalLink* c = reinterpret_cast<CPhysicalLink*>(aCPhysicalLink);
+	TInt err = c->UndoOverrideLPM();
+	//we deliberately ignore this return value because we can't do anything to correct the error situation
+	if (KErrNone != err)
+		{
+		LOG2(_L("Physical link: UndoOverrideLPM returned an error %d on the connection 0x%08x"), err, c);
+		}
+	c->iLPMOverrideTimerQueued = EFalse;
+	return KErrNone;
+	}
+
+TInt CPhysicalLink::UndoOverrideLPM()
+/**
+	A need to ensure we are not in LPM has gone.
+
+	Try to go back to power mode resulting from an unoverriden Arbitrate
+**/
+	{
+	LOG_FUNC
+	iOverrideLPMRequests = EFalse;
+
+	// Call Arbitrate(...) to determine if removing the override requires any change to
+	// the physical link state.
+	return Arbitrate();
+	}
+
+// security stuff
+// connection looks after this as it may be passively initiated and there wont
+// be a accessrequester in those situations
+
+void CPhysicalLink::PinRequest(const TBTDevAddr& aAddr, MPINCodeResponseHandler& aRequester)
+	{
+	LOG_FUNC
+	// If we receive a Pin_Request while another is outstanding then the controller is faulty. 
+	// So we just ignore any multiple requests, as they do not make sense.
+	// However, in UDEB we panic to raise awareness that the hardware is behaving incorrectly.
+	__ASSERT_DEBUG(!iPinRequester, Panic(EBTConnectionPINRequestedTwice));
+
+	SetAuthenticationPending(EPinRequestPending); // if not already set (because the remote initiated authentication).
+
+	if (iPinRequester)
+		{
+		return;
+		}
+
+	__ASSERT_DEBUG(aAddr == BDAddr(), Panic(EBTConnectionBadDeviceAddress));
+	
+	if(!IsPairable())
+		{
+		aRequester.PINCodeRequestNegativeReply(aAddr);
+		return;
+		}
+
+	TBTPinCode pinCode;
+	if(iLinksMan.PrefetchMan().IsPrefetchAvailable(aAddr, pinCode))
+		{
+		aRequester.PINCodeRequestReply(aAddr, pinCode);
+		return;
+		}
+
+	iPinHandler = &aRequester;
+
+	TRAPD(err, DoPinRequestL(aAddr, *this)); // physical links will proxy a request.
+
+	// if there is an error we should reply PIN negative...
+	if (err)
+		{
+		iPinHandler->PINCodeRequestNegativeReply(aAddr);
+		iPinHandler = NULL;
+		}
+	}
+
+void CPhysicalLink::DoPinRequestL(const TBTDevAddr& aAddr, MPINCodeResponseHandler& aResponseHandler)
+	{
+	LOG_FUNC
+	// Bluetooth is fairly poor at allowing name requests on partially connected links
+	// this typically occurs in security mode 3
+
+	// find out is it locally or remotley initiated authentication ...
+	TBool locallyInitiated; //Authentication
+	TBool strongKeyRequired;
+	TUint minPasskeyLength=0;
+	TUint recommendedPasskeyLength=0;
+
+	iLinksMan.SecMan().GetPassKeyLengthAndOriginator(aAddr, minPasskeyLength, locallyInitiated, strongKeyRequired);
+
+	/* If we have an activeAccessRequester and authentication was not actually required,
+	 * this is a speculative authentication.
+	 * In that case we did not expect to get a PinRequest. 
+	 * We leave as the link already exists, causing the PinRequest to get a negative reply.
+	 */
+	if(iDevice.IsValidLinkKey())
+		{
+		CBTAccessRequester* activeAccessRequester = iLinksMan.SecMan().FindActiveAccessRequester(aAddr);
+		if (activeAccessRequester)
+			{
+			if (!activeAccessRequester->AuthenticationRequired())
+				{
+				User::Leave(KErrAlreadyExists);
+				}
+			}
+		}
+
+	/* If link is already authenticated with a link key greater than the currently required minimum key length, then store 
+	 * the current passkey length as a recommended length. If this recommendation is followed, then the strength of 
+	 * the current link key will be maintained. This recommended length is made available via 
+	 * TBTPinCodeEntryNotifierParams::RecommendedPinCodeMinLength().
+	 */
+	TUint devicePasskeyLength = iDevice.PassKeyLength();
+	if(iDevice.IsValidLinkKey()&&devicePasskeyLength>minPasskeyLength)
+		{
+		//Ignore the previous link key length if it is not at least the minimum we require now anyway.
+		recommendedPasskeyLength=devicePasskeyLength;
+		}
+	else
+		{
+		//Recommend the currently required minimum length.
+		recommendedPasskeyLength=minPasskeyLength;
+		}
+
+	iPinRequester = CBTPinRequester::NewL(BDAddr(), aResponseHandler, iLinksMan.SecMan(),
+										  minPasskeyLength, locallyInitiated, strongKeyRequired, recommendedPasskeyLength);
+	}
+
+TBool CPhysicalLink::LinkKeyRequestPending()
+	{
+	LOG_FUNC
+	return iAuthStateMask & ELinkKeyRequestPending;
+	}
+
+void CPhysicalLink::SetAuthenticationPending(TUint8 aState)
+	{
+	LOG_FUNC
+	iAuthStateMask |= aState;
+	LOG1(_L("SetAuthenticationPending: set state [%d] in an authentication state mask"), aState);
+	}
+
+void CPhysicalLink::AuthenticationComplete(TUint8 aState)
+	{
+	LOG_FUNC
+	iAuthStateMask &= ~aState;
+	LOG1(_L("AuthenticationComplete: reset state [%d] in an authentication state mask"), aState);
+	PhysicalLinkUserIdle();
+	}
+
+TBool CPhysicalLink::IsPairable() const
+	{
+	// Determines if pairing can be performed on the physical link;
+	// it previously was determined by the PIN code notifier, but with
+	// 2.1 pairable mode has been replaced with bondable mode and
+	// so being pairable is now a function of the security level
+	// the device is operating under.
+	// Here the policy is as follows:
+	//
+	// Pairable if...
+	// 1) Not in paired only connections mode
+	// OR 
+	// 2) The link is already authenticated
+	// OR
+	// 3) A logical link was locally initiated
+	// OR
+	// 4) A dedicated bonding attempt is in progress
+	// (it might be better to migrate dedicated bonding above the logical
+	// link to remove this special case...)
+	//
+	// This policy provides strong security for while not applying
+	// blanket restricts on explicit user requests (i.e. outgoing
+	// connections).
+	TBool locallyInitiatedLogicalLink = EFalse;
+	for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
+		{
+		if(iACLLogicalLinks[i]->IsLocallyInitiated())
+			{
+			locallyInitiatedLogicalLink = ETrue;
+			}
+		}
+	return (!iLinksMan.IsAcceptPairedOnlyMode()) 
+		|| iLinkState.Authenticated() 
+		|| locallyInitiatedLogicalLink
+		|| iLinksMan.SecMan().IsDedicatedBondingAttempted(iDevice.Address());
+	}
+
+void CPhysicalLink::DeleteLinkKeyL()
+/**
+ensure that the LinkKey is removed from the device in the registry
+**/
+	{
+	LOG_FUNC
+	CBTLinkKeyDeleter* deleter = CBTLinkKeyDeleter::NewL(iRegSess, *this);
+	iRegistryHelpers.AddLast(*deleter);
+	deleter->Start(BDAddr());
+	}
+
+TBool CPhysicalLink::IsConnectionRequestPending() const
+	{
+	LOG_FUNC
+	return iPendingConnection;
+	}
+
+void CPhysicalLink::PendingConnectionRequest(TInt /*aError*/)
+	{
+	LOG_FUNC
+ 	__ASSERT_DEBUG(iPendingConnection, Panic(EPhysicalLinkNoConnectionPending));
+
+ 	// Note: We could do something clever with the error code returned
+ 	// to prevent us thrashing in OOM situations, but it is complicated.
+ 	// We want to try again as now the pending connection address may
+ 	// now be in the cache even if we have a non-KErrNone error code,
+ 	// and so with that we will proceed sucessfully.  But if we are
+ 	// deferred we may end up here again and again with, perhaps with a
+ 	// KErrNoMemory for example ... which is how the thrashing will occur.
+ 	// This thrashing will be limited however by the "Connection Accept
+ 	// Timeout" imposed by the controller on the host, and for this
+ 	// reason we live with it.
+	iPendingConnection = EFalse;
+	ConnectionRequest(iLastPendingConnection);
+	}
+
+TBool CPhysicalLink::IsPasskeyMinLengthOK()
+	{
+	LOG_FUNC
+/**
+	check whether current passkey (retrieved from BTRegistry) is long enough to fulfill user passkey requirement
+	It must be called only when iDevice is ready and has LinkKey
+**/
+	__ASSERT_DEBUG(iDeviceResult==KErrNone && iDevice.IsValidLinkKey(),
+		 Panic(EBTPhysicalLinkNotAuthenticated));
+
+	TUint newMinPasskeyLength=0;
+	TBool isPasskeyMinLengthOK = ETrue;
+
+	TBool locInit, strongKey;
+	iLinksMan.SecMan().GetPassKeyLengthAndOriginator( iDevice.Address(), newMinPasskeyLength, locInit, strongKey);
+
+	TBTPinCode currentPassKey = iDevice.PassKey();
+
+	// check current PIN code length against user requirement
+	if ( newMinPasskeyLength )
+		{
+		 if ( newMinPasskeyLength > currentPassKey().iLength )
+			{
+			// longer PIN code is requested
+			isPasskeyMinLengthOK = EFalse;
+			}
+		}
+
+	return isPasskeyMinLengthOK;
+	}
+
+void CPhysicalLink::LinkKeyRequest(const TBTDevAddr& aAddr, MLinkKeyResponseHandler& /*aRequester*/)
+	{
+	LOG_FUNC
+	// we don't keep a copy of the device record - we just leave the one copy with
+	// the baseband - it can tell us if there's a link key
+	// we can tell if the baseband has the device record or not
+
+	SetAuthenticationPending(ELinkKeyRequestPending); //authentication process started by remote
+
+	// If the ACL to the peer device is not yet connected, and the peer has initiated
+	// authentication then it must be in security mode 3.  This information is stored and
+	// if the connection completes the link will be set as authenticated.
+	if (!IsConnected())
+		{
+		iPeerInSecurityMode3 = ETrue;
+		}
+
+	if(!iPeerInSecurityMode3 && iLinksMan.SecMan().IsDedicatedBondingAttempted(iDevice.Address()))
+		{
+		// If we are doing DedicatedBonding then we should ignore the existing linkkey
+		// in an attempt to generate a stronger one if possible.
+		// Security mode 3 is a odd case - because we get what looks like double pairing (the remote
+		// initiated pairing on connection, then the dedicated bonding pairing).  So we have removed
+		// this feature for security mode 3 devices...they will have to suffer for their transgressions
+		LOG(_L("CPhysicalLink: Dedicated bonding attempt - Sending link key request negative reply"));
+		iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr);
+		iRequireAuthenticatedLinkKey = EFalse;
+		}
+	else if (iDeviceResult==KErrNone && iDevice.IsValidLinkKey())
+		{
+		if (iLinksMan.SecMan().DebugMode() && iDevice.LinkKeyType() != ELinkKeyDebug)
+			{
+			LOG(_L("CPhysicalLink: Debug mode - Link to debug link key"))
+			iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr);
+			}
+		else
+			{
+			if (iDevice.LinkKeyType() != ELinkKeyCombination)
+				{
+				if (iRequireAuthenticatedLinkKey && iDevice.LinkKeyType() == ELinkKeyUnauthenticatedUpgradable && IsPairable())
+					{
+					LOG(_L("CPhysicalLink: Requiring Authenticated link key but currently only have unauthenticated"))
+					iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr);
+					}
+				else
+					{
+					LOG(_L("CPhysicalLink: Issuing link key to HC now"))
+					iAuthenticationCtrl.LinkKeyRequestReply(aAddr, iDevice.LinkKey());
+					}
+				}
+			else if(IsPasskeyMinLengthOK() && SimplePairingMode() != EPhySimplePairingEnabled)
+				{
+				LOG(_L("CPhysicalLink: Issuing link key to HC now"))
+				iAuthenticationCtrl.LinkKeyRequestReply(aAddr, iDevice.LinkKey());
+				}
+			else
+				{
+				LOG(_L("CPhysicalLink: Current PIN code too short!"))
+				iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr);
+				}
+			}
+		iRequireAuthenticatedLinkKey = EFalse;
+		}
+	else if (iDeviceResult==KErrNone && !iDevice.IsValidLinkKey() || iDeviceResult==KErrNotFound)
+		{
+		LOG(_L("CPhysicalLink: No Link key available for the device"));
+		iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr);
+		iRequireAuthenticatedLinkKey = EFalse;
+		}
+	else
+		{
+		LOG(_L("CPhysicalLink: Waiting for link key from Registry!"))
+		// we're still waiting for the device....we'll respond when it turns up
+		iWaitingForLinkKeyFromRegistry = ETrue;
+		}
+
+	}
+
+TInt CPhysicalLink::PINCodeRequestReply(const TBTDevAddr& aDevAddr, const TDesC8& aPin) const
+	{
+	ASSERT_DEBUG(aDevAddr == this->BDAddr());
+	const_cast<CPhysicalLink*>(this)->PINCodeRequestReply(aDevAddr, aPin);
+	return KErrNone;
+	}
+
+TInt CPhysicalLink::PINCodeRequestNegativeReply(const TBTDevAddr& aDevAddr) const
+	{
+	ASSERT_DEBUG(aDevAddr == this->BDAddr());
+	const_cast<CPhysicalLink*>(this)->PINCodeRequestNegativeReply(aDevAddr);
+	return KErrNone;
+	}
+
+void CPhysicalLink::PINCodeRequestReply(const TBTDevAddr& aDevAddr, const TDesC8& aPin)
+	{
+	SetPassKey(aPin);
+	PinRequestSent();
+	iPinHandler->PINCodeRequestReply(aDevAddr, aPin);
+	PinRequestComplete();
+	}
+
+void CPhysicalLink::PINCodeRequestNegativeReply(const TBTDevAddr& aDevAddr)
+	{
+	iPinHandler->PINCodeRequestNegativeReply(aDevAddr);
+	PinRequestComplete();
+	}
+
+
+void CPhysicalLink::PinRequestSent()
+	{
+	LOG_FUNC
+	iLinkKeyPending = ETrue;
+	}
+
+void CPhysicalLink::PinRequestComplete()
+/**
+	Just clean up now that PIN request has completed
+**/
+	{
+	LOG_FUNC
+	delete iPinRequester;
+	iPinRequester = NULL;
+	iPinHandler = NULL;
+	AuthenticationComplete(EPinRequestPending);
+	}
+
+
+TInt CPhysicalLink::RequestHold()
+	{
+	LOG_FUNC
+	return RequestMode(EHoldMode);
+	}
+
+TInt CPhysicalLink::RequestSniff()
+	{
+	LOG_FUNC
+	return RequestMode(ESniffMode);
+	}
+
+TInt CPhysicalLink::RequestPark()
+	{
+	LOG_FUNC
+	return RequestMode(EParkMode);
+	}
+
+TInt CPhysicalLink::RequestActive()
+	{
+	LOG_FUNC
+	return RequestMode(EActiveMode);
+	}
+
+void CPhysicalLink::ReadNewPhysicalLinkMetricValue(TUint aIoctlName, CBTProxySAP& aSAP, TInt aCurrentValue)
+	{
+	LOG_FUNC
+	iPhysicalLinkMetrics->ReadNewPhysicalLinkMetricValue(aIoctlName, aSAP, aCurrentValue);
+	}
+
+void CPhysicalLink::GetCurrentBasebandState(TBTBasebandEventNotification & aEvent)
+	{
+	LOG_FUNC
+	// Populate the event with the current baseband state.
+	TUint32 events = 0;
+	switch(iLinkState.LinkRole())
+		{
+		case EMaster:
+			events |= ENotifyMaster;
+			break;
+		case ESlave:
+			events |= ENotifySlave;
+			break;
+		case ERoleUnknown:
+		default:
+			break;
+		};
+
+	switch(iLinkState.LinkMode())
+		{
+		case EActiveMode:
+			events |= ENotifyActiveMode;
+			break;
+		case ESniffMode:
+			events |= ENotifySniffMode;
+			break;
+		case EParkMode:
+			events |= ENotifyParkMode;
+			break;
+		case EHoldMode:
+			events |= ENotifyHoldMode;
+			break;
+		case EScatterMode:
+		default:
+			break;
+		};
+
+	switch(iLinkState.MaxSlots())
+		{
+		case 1:
+			events |= ENotifyMaxSlots1;
+			break;
+		case 3:
+			events |= ENotifyMaxSlots3;
+			break;
+		case 5:
+			events |= ENotifyMaxSlots5;
+			break;
+		default:
+			break;
+		};
+
+	TUint16 packetTypes = iLinkState.PacketTypes();
+	if(packetTypes & EPacketsDM1)
+		{
+		events |= ENotifyPacketsDM1;
+		}
+	if(packetTypes & EPacketsDM3)
+		{
+		events |= ENotifyPacketsDM3;
+		}
+	if(packetTypes & EPacketsDM5)
+		{
+		events |= ENotifyPacketsDM5;
+		}
+	if(packetTypes & EPacketsDH1)
+		{
+		events |= ENotifyPacketsDH1;
+		}
+	if(packetTypes & EPacketsDH3)
+		{
+		events |= ENotifyPacketsDH3;
+		}
+	if(packetTypes & EPacketsDH5)
+		{
+		events |= ENotifyPacketsDH5;
+		}
+	if(packetTypes & EPacketsHV1)
+		{
+		events |= ENotifyPacketsHV1;
+		}
+	if(packetTypes & EPacketsHV2)
+		{
+		events |= ENotifyPacketsHV2;
+		}
+	if(packetTypes & EPacketsHV3)
+		{
+		events |= ENotifyPacketsHV3;
+		}
+
+	if(iLinkState.Authenticated())
+		{
+		events |= ENotifyAuthenticationComplete;
+		}
+
+	if(iLinkState.Encrypted())
+		{
+		events |= ENotifyEncryptionChangeOn;
+		}
+	else
+		{
+		events |= ENotifyEncryptionChangeOff;
+		}
+
+	switch(iLinkState.LinkState())
+		{
+		case TBTBasebandLinkState::ELinkUp:
+			events |= ENotifyPhysicalLinkUp;
+
+			if (iSyncLogicalLink)
+				{
+				if (iSyncLogicalLink->IsOpen())
+					{
+					events |= ENotifySynchronousLinkUp;
+					}
+				else
+					{
+					events |= ENotifySynchronousLinkDown;
+					}
+				}
+			break;
+		case TBTBasebandLinkState::ELinkDown:
+			events |= ENotifyPhysicalLinkDown;
+			break;
+		default:
+			break;
+		};
+
+	aEvent.SetEventType(events);
+	aEvent.SetErrorCode(0);
+	}
+
+TBool CPhysicalLink::ACLConnectPending() const
+	{
+	LOG_FUNC
+	return !IsConnected();
+	}
+
+TBool CPhysicalLink::SyncConnectPending() const
+	{
+	LOG_FUNC
+	if (!iSyncLogicalLink)
+		{
+		return EFalse;
+		}
+
+	return iSyncLogicalLink->ConnectPending();
+	}
+
+TInt CPhysicalLink::ManageEncryptionEnforcement(THCIEncryptModeFlag aEnable)
+	{
+	LOG_FUNC
+	// Although this code has been written as if this method could be called more then once,
+	// with different argument values:
+	// ChangeEncryption() (and then ManageEncryptionEnforcement()) is called only
+	// once and only with aEnable = EPointToPointEncryption.
+	// It's called if during the physical link creation the encryption is requested.
+	// That is because the assertion below.
+	__ASSERT_DEBUG(aEnable != EEncryptionDisabled, Panic(EBTInvalidEncryptionDisableRequest));
+
+	// although "&& (iEncryptionEnforcer)" is not necessary, we leave it because
+	// clarify that we had asked for the encryption, so it's our responsability to
+	// delete iEncryptionEnforcer.
+	if ((aEnable == EEncryptionDisabled) && (iEncryptionEnforcer))
+		{
+		delete iEncryptionEnforcer;
+		iEncryptionEnforcer = NULL;
+		}
+	else if ((aEnable != EEncryptionDisabled) && (!iEncryptionEnforcer))
+		{
+		TRAPD(err, iEncryptionEnforcer = CEncryptionEnforcer::NewL(*this, aEnable));
+		return err;
+		}
+	return KErrNone;
+	}
+
+TBasebandTime CPhysicalLink::CalculatePageTimeout(TBasebandPageTimePolicy aPolicy, TUint8 aRepMode, TBool aValidClockOffset)
+	{
+	LOG_FUNC
+	TBasebandTime pageTimeout = TBasebandPolicy::KPageTimeoutR0;
+
+	switch (aRepMode)
+		{
+		// PSRM defined in spec
+		case 0x00:
+			// already set
+			break;
+
+		case 0x01:
+			pageTimeout = TBasebandPolicy::KPageTimeoutR1;
+			break;
+
+		case 0x02:
+			// fallthrough
+		default:
+			pageTimeout = TBasebandPolicy::KPageTimeoutR2;
+			break;
+		}
+
+	if (!aValidClockOffset)
+		{
+		pageTimeout*=2;
+		}
+	else
+		{
+//		normalPageTimeout+=5; //slots
+		}
+
+	// policy may not be valid - or dontcare/normal
+	switch (aPolicy)
+		{
+		case EPagingBestEffort:
+			pageTimeout = static_cast<TBasebandTime>(TBasebandPolicy::KBestEffortTimeMultiplier * pageTimeout);
+			break;
+
+		case EPagingQuick:
+			pageTimeout = static_cast<TBasebandTime>(TBasebandPolicy::KQuickTimeMultiplier * pageTimeout);
+			break;
+
+		default:
+			break;
+			// leave as is
+
+		}
+	return pageTimeout;
+	}
+
+// CArbitrationDelayTimer
+
+CArbitrationDelayTimer::CArbitrationDelayTimer(CPhysicalLink* aParent)
+	:CTimer(CActive::EPriorityStandard),
+	iParent(aParent)
+	{
+	LOG_FUNC
+	}
+
+void CArbitrationDelayTimer::ConstructL()
+	{
+	LOG_FUNC
+	CTimer::ConstructL();
+	CActiveScheduler::Add(this);
+	}
+
+CArbitrationDelayTimer* CArbitrationDelayTimer::NewL(CPhysicalLink* aParent)
+	{
+	LOG_STATIC_FUNC
+	CArbitrationDelayTimer* self = new (ELeave) CArbitrationDelayTimer(aParent);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+void CArbitrationDelayTimer::Start()
+	{
+	LOG_FUNC
+	if (IsActive())
+		{
+		Cancel();
+		}
+	After(KBTArbitrationDelay);
+	}
+
+
+void CArbitrationDelayTimer::RunL()
+/**
+Allow arbitration of low power modes when the timer expires
+**/
+	{
+	LOG_FUNC
+	if (iParent)
+		{
+		iParent->Arbitrate();
+		}
+	}
+
+TInt CPhysicalLink::GetNumPendingHandles(TInt& aConnectionHandles, TLinkType aLinkType) const
+	{
+	LOG_FUNC
+	aConnectionHandles = 0;
+
+	switch(aLinkType)
+		{
+	case EeSCOLink:
+
+		if (iSyncLogicalLink && iSyncLogicalLink->ConnectPending())
+			{
+			// Currently there is a maximum of one synclink per
+			// physical link
+
+			++aConnectionHandles;
+			}
+
+		break;
+
+	case ESCOLink:
+
+		if (iSyncLogicalLink && iSyncLogicalLink->ConnectPending())
+			{
+			// Currently there is a maximum of one synclink per
+			// physical link
+
+			++aConnectionHandles;
+			}
+
+		break;
+
+	case EACLLink:
+
+		if (!IsConnected())
+			{
+			// Currently there is a maximum of one asynclink per
+			// physical link
+			++aConnectionHandles;
+			}
+
+		break;
+
+	default:
+		return KErrUnknown;
+		}
+
+	return KErrNone;
+	}
+
+TInt CPhysicalLink::GetConnectionHandles(RHCIConnHandleArray& aConnectionHandles,
+					 TLinkType aLinkType) const
+	{
+	LOG_FUNC
+	// Always append to client supplied array - do not clear the
+	// contents.  Only return connected handles.
+
+	switch(aLinkType)
+		{
+	case EeSCOLink:
+
+		if (!iSyncLogicalLink || iSyncLogicalLink->ConnectPending())
+			{
+			return KErrNone;
+			}
+
+		if (iSyncLogicalLink->LinkType() == EeSCOLink)
+			{
+			// Currently there is a maximum of one synclink per
+			// physical link
+			aConnectionHandles.Append(iSyncLogicalLink->Handle());
+			}
+
+		break;
+
+	case ESCOLink:
+
+		if (!iSyncLogicalLink || iSyncLogicalLink->ConnectPending())
+			{
+			return KErrNone;
+			}
+
+		if (iSyncLogicalLink->LinkType() == ESCOLink)
+			{
+			// Currently there is a maximum of one synclink per
+			// physical link
+			aConnectionHandles.Append(iSyncLogicalLink->Handle());
+			}
+
+		break;
+
+	case EACLLink:
+		// Currently there is a maximum of one asynclink per physical
+		// link and they share the same handle - this may change in
+		// the future for QoS.
+
+		if (!IsConnected())
+			{
+			return KErrNone;
+			}
+
+		aConnectionHandles.Append(iHandle);
+		break;
+
+	default:
+		return KErrUnknown;
+		}
+
+	return KErrNone;
+	}
+
+void CPhysicalLink::IOCapabilityAskForResponse(THCIIoCapability aIOCapability, THCIOobDataPresence aOOBDataPresence, THCIAuthenticationRequirement aAuthenticationRequirement)
+	{
+	LOG_FUNC
+	iIOCapsReceived = ETrue;
+	iIOCapability = aIOCapability;
+	iOOBDataPresence = aOOBDataPresence;
+	iAuthenticationRequirement = aAuthenticationRequirement;
+	
+	//If we haven't determined the SSP pairing mode till now then enable it and notify the state m/c.
+	//This condition is to cater the fast remote device which responds very quickly,  
+	//even before we determine whether it supports simple pairing!*/ 
+	__ASSERT_DEBUG(((SimplePairingMode() == EPhySimplePairingEnabled) || (SimplePairingMode() == EPhySimplePairingUndefined)),Panic(EBTSSPModeChangedDuringConnection));
+	if(SimplePairingMode() == EPhySimplePairingUndefined)
+		{
+		//Since we have received a I/O cap response the simple pairing must be enabled
+		iSimplePairingMode = EPhySimplePairingEnabled;
+		iLinksMan.SecMan().SimplePairingSupportDetermined(BDAddr());
+		}
+	}
+
+
+TBool CPhysicalLink::AuthWithMITM() const
+	{
+	LOG_FUNC
+   	return (iLocalMITM || (iAuthenticationRequirement & KAuthenticationMitmReqMask))
+  			&& (iIOCapability != EIOCapsNoInputNoOutput);
+	}
+
+void CPhysicalLink::SetLocalMITM(TBool aLocalMITM)
+	{
+	LOG_FUNC
+	iLocalMITM = aLocalMITM;
+	}
+
+TPhysicalLinkSimplePairingMode CPhysicalLink::SimplePairingMode() const
+	{
+	LOG_FUNC
+	return iSimplePairingMode;
+	}
+
+TBool CPhysicalLink::HasRemoteOobData() const
+	{
+	LOG_FUNC
+	return iLinksMan.SecMan().OobDataManager().HasRemoteOobData(BDAddr());
+	}
+
+THCIAuthenticationRequirement CPhysicalLink::AuthenticationRequirement() const
+	{
+	LOG_FUNC
+	return iAuthenticationRequirement;
+	}
+
+void CPhysicalLink::NewNumericComparatorL(const TBTDevAddr aAddr,
+ 										  CBTSecMan& aSecMan,
+ 										  TUint32 aNumericValue,
+ 										  TBool	aInternallyInitiated)
+   	{
+	LOG_FUNC
+ 	__ASSERT_DEBUG(aAddr == BDAddr(), Panic(EBTConnectionBadDeviceAddress));
+ 	
+ 	// Check remote IO capabilities so that the BTNumericComparator can tell the user
+ 	TBTNumericComparisonParams::TComparisonScenario compScenario = TBTNumericComparisonParams::ERemoteCanConfirm;
+ 	if(iIOCapsReceived)
+ 		{
+ 		switch(iIOCapability)
+ 			{
+ 			case EIOCapsDisplayOnly:
+ 				compScenario = TBTNumericComparisonParams::ERemoteCannotConfirm;
+ 				break;
+ 			case EIOCapsDisplayYesNo:
+ 				compScenario = TBTNumericComparisonParams::ERemoteCanConfirm;
+ 				break;
+ 			default:
+ 				// If iIOCapability = EIOCapsKeyboardOnly or EIOCapsNoInputNoOutput 
+ 				// we should not be using a Numeric Comparitor
+ 				__ASSERT_DEBUG(EFalse, Panic(EUnexpectedIOCapability));
+ 				break;
+ 			}
+ 		}
+ 	iNumericComparator = CBTNumericComparator::NewL(aAddr, aSecMan, aNumericValue, compScenario, aInternallyInitiated);
+   	}
+
+CBTNumericComparator* CPhysicalLink::InstanceNumericComparator() const
+	{
+	LOG_FUNC
+	return iNumericComparator;
+	}
+
+TBool CPhysicalLink::IsNumericComparatorActive()const
+	{
+	LOG_FUNC
+	return iNumericComparator->IsActive();
+	}
+
+void CPhysicalLink::DeleteNumericComparator()
+	{
+	LOG_FUNC
+	delete iNumericComparator;
+	iNumericComparator = NULL;
+	}
+void CPhysicalLink::CancelNumericComparator()
+	{
+	LOG_FUNC
+	iNumericComparator->Cancel();
+	}
+
+void CPhysicalLink::NewPasskeyEntryL(const TBTDevAddr aAddr,
+												CBTSecMan& aSecMan,
+												TUint32 aNumericValue,
+												TBool aInternallyInitiated)
+	{
+	LOG_FUNC
+	iPasskeyEntry = CBTPasskeyEntry::NewL(aAddr, aSecMan, aNumericValue, aInternallyInitiated);
+	}
+
+CBTPasskeyEntry* CPhysicalLink::InstancePasskeyEntry() const
+	{
+	LOG_FUNC
+	return iPasskeyEntry;
+	}
+
+TBool CPhysicalLink::IsPasskeyEntryActive()const
+	{
+	LOG_FUNC
+	return iPasskeyEntry->IsActive();
+	}
+
+void CPhysicalLink::DeletePasskeyEntry()
+	{
+	LOG_FUNC
+	delete iPasskeyEntry;
+	iPasskeyEntry = NULL;
+	}
+
+void CPhysicalLink::CancelPasskeyEntry()
+	{
+	LOG_FUNC
+	iPasskeyEntry->Cancel();
+	}
+
+void CPhysicalLink::PasskeyEntryKeyPressed(THCIPasskeyEntryNotificationType aKey)
+	{
+	LOG_FUNC
+	iPasskeyEntry->KeyPressed(aKey);
+	}
+
+
+TBasebandTime CPhysicalLink::GetSniffInterval() const
+	{
+	return iSniffInterval;
+	}
+
+//
+// TLowPowModeCmdController
+//
+
+TLowPowModeCmdController::TLowPowModeCmdController(CPhysicalLink& aLink, MHCICommandQueue& aCmdController) : 	
+	iParent(aLink),
+	iCmdController(aCmdController),
+	iOutstandingCmd(EFalse)
+	{
+	LOG_FUNC
+	}
+
+void TLowPowModeCmdController::Abort()
+	{
+	LOG_FUNC
+	LOG(_L("TLowPowModeCmdController::Abort"));
+	iCmdController.MhcqRemoveAllCommands(*this);
+	}
+
+void TLowPowModeCmdController::DoExitModeL(TBTLinkMode aMode, THCIConnHandle aConnHandle)
+	{
+	LOG_FUNC
+	LOG2(_L("TLowPowModeCmdController::DoExitModeL: mode:%d iOutstandingCmd:%d"), aMode, iOutstandingCmd);
+	if (!iOutstandingCmd)
+		{
+		switch (aMode)
+			{
+			case ESniffMode:
+				{
+				ExitSniffL(aConnHandle);
+				break;
+				}
+			case EParkMode:
+				{
+				ExitParkL(aConnHandle);
+				break;
+				}
+			// Not possile to prematurely exit hold mode
+			default:
+				break;
+			}
+		iOutstandingCmd = ETrue;
+		}
+	else
+		{
+		LOG1(_L("TLowPowModeCmdController::DoExitModeL: cannot exit from mode:%d due to an outstanding command"), aMode);
+		}
+	}
+
+TInt TLowPowModeCmdController::ExitMode(TBTLinkMode aMode, THCIConnHandle aHandle)
+	{
+	LOG_FUNC
+	TRAPD(err, DoExitModeL(aMode, aHandle));
+	return err;
+	}
+
+void TLowPowModeCmdController::DoChangeModeL(TBTLinkMode aMode, THCIConnHandle aConnHandle)
+	{
+	LOG_FUNC
+	LOG2(_L("TLowPowModeCmdController::DoChangeModeL: mode:%d iOutstandingCmd:%d"), aMode, iOutstandingCmd);
+	if (!iOutstandingCmd)
+		{
+		switch (aMode)
+			{
+			case ESniffMode:
+				{
+				SniffL(aConnHandle);
+				break;
+				}
+			case EParkMode:
+				{
+				ParkL(aConnHandle);
+				break;
+				}
+			case EHoldMode:
+				{
+				HoldL(aConnHandle);
+				break;
+				}
+			case EScatterMode:
+			case EActiveMode:
+				__ASSERT_DEBUG(0, Panic(EHCICommandBadArgument));
+				break;
+			}
+		iOutstandingCmd = ETrue;
+		}
+	else
+		{
+		LOG1(_L("TLowPowModeCmdController::DoChangeModeL: cannot change to mode:%d due to an outstanding command"), aMode);
+		}
+	}
+
+TInt TLowPowModeCmdController::ChangeMode(TBTLinkMode aMode, THCIConnHandle aHandle)
+	{
+	LOG_FUNC
+	TRAPD(err, DoChangeModeL(aMode, aHandle));
+	return err;
+	}
+
+void TLowPowModeCmdController::SniffL(THCIConnHandle aHandleToRemote)
+	{
+	LOG_FUNC
+	CSniffModeCommand* cmd = CSniffModeCommand::NewL(aHandleToRemote, KBTSniffModeMaxInterval, KBTSniffModeMinInterval, KBTSniffModeAttempt, KBTSniffModeTimeout);
+
+	// Ownership of cmd transfered even if MhcqAddCommandL leaves
+	iCmdController.MhcqAddCommandL(cmd, *this);
+	}
+
+void TLowPowModeCmdController::ExitSniffL(THCIConnHandle aHandleToRemote)
+	{
+	LOG_FUNC
+	CExitSniffModeCommand* cmd = CExitSniffModeCommand::NewL(aHandleToRemote);
+
+	// Ownership of cmd transfered even if MhcqAddCommandL leaves
+	iCmdController.MhcqAddCommandL(cmd, *this);
+	}
+
+void TLowPowModeCmdController::HoldL(THCIConnHandle aHandle)
+	{
+	LOG_FUNC
+	CHoldModeCommand* cmd = CHoldModeCommand::NewL(aHandle, KBTHoldModeMaxInterval, KBTHoldModeMinInterval);
+
+	// Ownership of cmd transfered even if MhcqAddCommandL leaves
+	iCmdController.MhcqAddCommandL(cmd, *this);
+	}
+
+void TLowPowModeCmdController::ParkL(THCIConnHandle aHandle)
+	{
+	LOG_FUNC
+	CParkModeCommand* cmd = CParkModeCommand::NewL(aHandle, KBTParkModeBeaconMaxInterval, KBTParkModeBeaconMinInterval);
+
+	// Ownership of cmd transfered even if MhcqAddCommandL leaves
+	iCmdController.MhcqAddCommandL(cmd, *this);
+	}
+
+void TLowPowModeCmdController::ExitParkL(THCIConnHandle aHandle)
+	{
+	LOG_FUNC
+	CExitParkModeCommand* cmd = CExitParkModeCommand::NewL(aHandle);
+
+	// Ownership of cmd transfered even if MhcqAddCommandL leaves
+	iCmdController.MhcqAddCommandL(cmd, *this);
+	}
+
+void TLowPowModeCmdController::MhcqcCommandEventReceived(const THCIEventBase& aEvent, const CHCICommandBase* aRelatedCommand)
+	{
+	LOG_FUNC
+	THCIEventCode code = aEvent.EventCode();
+
+	__ASSERT_DEBUG(!(code != EModeChangeEvent && code != ECommandStatusEvent), Panic(EHCIUnknownCommandEvent));	// this should never happen
+	__ASSERT_DEBUG(aRelatedCommand != NULL, Panic(EHCIUnknownCommandEvent));	// this should never happen
+
+	if (!aRelatedCommand)	// it is not the response to our command - should never happen
+		return;
+
+	if ((code == ECommandStatusEvent) && (aEvent.ErrorCode() != EOK))
+		{
+		LOG3(_L("TLowPowModeCmdController::MhcqcCommandEventReceived: event:%d opcode:0x%x error:0x%x"), code, aRelatedCommand->Opcode(), aEvent.ErrorCode());
+		iOutstandingCmd = EFalse;	// reset the outstanding flag
+		iParent.StartArbitrationTimer();
+		}
+	else if (code == EModeChangeEvent)
+		{
+		iOutstandingCmd = EFalse;	// reset the outstanding flag
+
+		LOG2(_L("TLowPowModeCmdController::MhcqcCommandEventReceived: event:%d opcode:0x%x"), code, aRelatedCommand->Opcode());
+
+		const TModeChangeEvent& modeChangeEvent = TModeChangeEvent::Cast(aEvent);
+		TBTLinkMode mode = EActiveMode;
+		switch(modeChangeEvent.CurrentMode())
+			{
+			// Mode 0, as defined for the Mode Change Event, is Active Mode
+			case 0:
+				break;
+			case 1:
+				mode = EHoldMode;
+				break;
+			case 2:
+				mode = ESniffMode;
+				break;
+			case 3:
+				mode = EParkMode;
+				break;
+			default:
+				__ASSERT_ALWAYS(EFalse, Panic(EHCICommandBadArgument));
+				break;
+			}
+			// In the HCI_Facade, in this situation, CPhysicalLinksManager::ModeChanged() is called.
+			// Since this methods find the CPhysicalLink object (that is iParent) and then call its
+			// ModeChange method, we can call it directly.
+			iParent.ModeChange(aEvent.ErrorCode(), modeChangeEvent.ConnectionHandle(), mode, modeChangeEvent.Interval());
+		}
+	}
+
+void TLowPowModeCmdController::MhcqcCommandErrored(TInt __DEBUG_ONLY(aErrorCode), const CHCICommandBase* __DEBUG_ONLY(aCommand))
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(aCommand, Panic(EHCIUnknownCommandEvent));	// this should never happen
+	#ifdef _DEBUG
+	LOG2(_L("MhcqcCommandErrored: error code:%d opcode:0x%x"), aErrorCode, aCommand->Opcode());
+	#endif
+	iOutstandingCmd = EFalse;	// reset the outstanding flag
+	iParent.StartArbitrationTimer();
+	}
+
+
+
+
+//
+// CEncryptionEnforcer
+//
+
+CEncryptionEnforcer::CEncryptionEnforcer (CPhysicalLink& aLink, THCIEncryptModeFlag aEncryptionMode) :
+	CActive(EPriorityStandard), // Standard priority
+	iLink(aLink),
+	iEncryptionMode(aEncryptionMode),
+	iState(EInactive)
+	{
+	LOG_FUNC
+	}
+
+CEncryptionEnforcer* CEncryptionEnforcer::NewLC(CPhysicalLink& aLink, THCIEncryptModeFlag aEncryptionMode)
+	{
+	LOG_STATIC_FUNC
+	CEncryptionEnforcer* self = new ( ELeave ) CEncryptionEnforcer(aLink, aEncryptionMode);
+	CleanupStack::PushL (self);
+	self->ConstructL ();
+	return self;
+	}
+
+CEncryptionEnforcer* CEncryptionEnforcer::NewL(CPhysicalLink& aLink, THCIEncryptModeFlag aEncryptionMode)
+	{
+	LOG_STATIC_FUNC
+	CEncryptionEnforcer* self = CEncryptionEnforcer::NewLC (aLink, aEncryptionMode);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+void CEncryptionEnforcer::ConstructL()
+	{
+	LOG_FUNC
+	User::LeaveIfError (iTimer.CreateLocal () ); // Initialize timer
+	CActiveScheduler::Add (this); // Add to scheduler
+	}
+
+CEncryptionEnforcer::~CEncryptionEnforcer()
+	{
+	LOG_FUNC
+	Cancel (); // Cancel any request, if outstanding
+	iTimer.Close (); // Destroy the RTimer object
+	}
+
+void CEncryptionEnforcer::DoCancel ()
+	{
+	LOG_FUNC
+	iTimer.Cancel ();
+	ChangeState(EInactive);
+	}
+
+void CEncryptionEnforcer::Start(TInt delay)
+	{
+	LOG_FUNC
+	Cancel (); // Cancel any request, just to be sure
+	LOG(_L("CEncryptionEnforcer activate timer"))
+	iTimer.After (iStatus, delay); // Set for later
+	SetActive (); // Tell scheduler a request is active
+	}
+
+void CEncryptionEnforcer::Stop()
+	{
+	LOG_FUNC
+	Cancel();
+	}
+
+void CEncryptionEnforcer::RunL ()
+	{
+	LOG_FUNC
+	switch(iState)
+		{
+		case ERoleSwitchTimerInProgress:
+			LOG(_L("CEncryptionEnforcer::RunL() : timer expired after a roleswitch, enforce encryption"))
+			ChangeState(EForcingInProgress);
+			User::LeaveIfError(iLink.ChangeEncryption(iEncryptionMode));
+			break;
+		case ENoRoleSwitchTimerInProgress:
+			LOG(_L("CEncryptionEnforcer::RunL() : timer expired and no roleswitch performed, terminate the link"))
+			iLink.Terminate(ERemoteUserEndedConnection);
+			break;
+		default:
+			// this should never happen, so we assert in debug so to raise the problem.
+			// however it doesn't break our behaviour, so we don't care in release.
+			LOG(_L("CEncryptionEnforcer::RunL() : bad state"))
+			__ASSERT_DEBUG(EFalse, Panic(EEncryptionEnforcerBadState));
+			break;
+		}
+	}
+
+TInt CEncryptionEnforcer::RunError(TInt IF_FLOGGING(aError))
+	{
+	LOG_FUNC
+	LOG1(_L("CEncryptionEnforcer::RunError() : error = %d"), aError);
+	LOG(_L("CEncryptionEnforcer::RunError() : try to terminate the link"));
+	return iLink.Terminate(ERemoteUserEndedConnection);
+	}
+
+void CEncryptionEnforcer::ChangeState(TState aState)
+	{
+	LOG_FUNC
+	LOG2(_L("CEncryptionEnforcer::ChangeState(): old state = %d; new state = %d"), iState, aState);
+	iState = aState;
+	}
+
+void CEncryptionEnforcer::EncryptionEnabled()
+	{
+	LOG_FUNC
+	LOG1(_L("CEncryptionEnforcer::EncryptionEnabled() : current status = %d"), iState);
+	switch(iState)
+		{
+		case EInactive:
+			// do nothing, it means is the first encryption event as reply to our ChangeEncryption cmd.
+			break;
+		case EForcingInProgress:
+			ChangeState(EInactive);
+			break;
+		case ENoRoleSwitchTimerInProgress:
+		case ERoleSwitchTimerInProgress:
+			Stop();
+			break;
+		}
+	}
+
+void CEncryptionEnforcer::EncryptionDisabled(TBool aSecurityModeFour)
+	{
+	LOG_FUNC
+	LOG1(_L("CEncryptionEnforcer::EncryptionDisabled() : current status = %d"), iState);
+
+	if(aSecurityModeFour)
+		{
+		Stop();
+		ChangeState(EForcingInProgress);
+		}
+
+	switch(iState)
+		{
+		case EInactive:
+			LOG(_L("CEncryptionEnforcer::EncryptionDisabled() : start timer"))
+			ChangeState(ENoRoleSwitchTimerInProgress);
+			Start(KTimeOutDelay);
+			break;
+		case EForcingInProgress:
+			LOG(_L("CEncryptionEnforcer::EncryptionDisabled() : disconnect the link"))
+			iLink.Terminate(ERemoteUserEndedConnection);
+			break;
+		default:
+			// this should never happen, so we assert in debug so to raise the problem.
+			// however it doesn't break our behaviour, so we don't care in release.
+			LOG(_L("CEncryptionEnforcer::EncryptionDisabled() : bad state"))
+			__ASSERT_DEBUG(EFalse, Panic(EEncryptionEnforcerBadState));
+			break;
+		}
+	}
+
+void CEncryptionEnforcer::RoleSwitchEvent()
+	{
+	LOG_FUNC
+
+	switch(iState)
+		{
+		case EInactive:
+			// This is possible with EPR support - we don't need to do anything in this case.
+			break;
+		case ENoRoleSwitchTimerInProgress:
+			ChangeState(ERoleSwitchTimerInProgress);
+			break;
+		default:
+			// this should never happen, so we assert in debug so to raise the problem.
+			// however it doesn't break our behaviour, so we don't care in release.
+			LOG(_L("CEncryptionEnforcer::EncryptionDisabled() : bad state"))
+			__ASSERT_DEBUG(EFalse, Panic(EEncryptionEnforcerBadState));
+			break;
+		}
+	}
+
+void XAutoKeyRefreshToken::Release()
+	{
+	LOG_FUNC
+	iQueLink.Deque();
+	delete this;
+	}
+
+//
+// TRegistryDeviceOutChecker
+//
+TRegistryDeviceBeingModified::TRegistryDeviceBeingModified() :
+	iReferenceCount(0)
+	{
+	LOG_FUNC
+	}
+
+TBool TRegistryDeviceBeingModified::InUse() const
+	{
+	LOG_FUNC
+		return iReferenceCount > 0;
+	}
+
+TBool TRegistryDeviceBeingModified::IsEqual(const TBTNamelessDevice& aDevice) const
+	{
+	LOG_FUNC
+	return (iDevice == aDevice);
+	}
+
+void TRegistryDeviceBeingModified::Begin(const TBTNamelessDevice& aDevice)
+	{
+	LOG_FUNC
+	iDevice = aDevice;
+	iReferenceCount++;
+	}
+
+void TRegistryDeviceBeingModified::RequestCompleted()
+	{
+	LOG_FUNC
+	iReferenceCount--;
+	__ASSERT_DEBUG(iReferenceCount >= 0, Panic(ETRegistryDevBeingModUnexpectedCompleted));
+	}
+
+//
+// TDisconnectCmdController
+// HCI Command Queue Interface for Disconnect Commands
+//
+
+TDisconnectCmdController::TDisconnectCmdController(CPhysicalLink& aLink, MHCICommandQueue& aCmdController) : 	
+	iParent(aLink),
+	iCmdController(aCmdController)
+	{
+	LOG_FUNC
+	}
+
+void TDisconnectCmdController::Abort()
+	{
+	LOG_FUNC
+	iCmdController.MhcqRemoveAllCommands(*this);
+	}
+
+TInt TDisconnectCmdController::Disconnect(THCIErrorCode aReason)
+	{
+	LOG_FUNC
+	TRAPD(err, DoDisconnectL(aReason));
+	return err;
+	}
+
+void TDisconnectCmdController::DoDisconnectL(THCIErrorCode aReason)
+	{
+	LOG_FUNC
+	CDisconnectCommand* cmd = CDisconnectCommand::NewL(iParent.Handle(), aReason);
+
+	// Ownership of cmd transfered even if MhcqAddCommandL leaves
+	iCmdController.MhcqAddCommandL(cmd, *this);
+	}
+
+void TDisconnectCmdController::MhcqcCommandEventReceived(const THCIEventBase& aEvent, const CHCICommandBase* aRelatedCommand)
+	{
+	LOG_FUNC
+	THCIEventCode code = aEvent.EventCode();
+	
+	__ASSERT_DEBUG(!(code != EDisconnectionCompleteEvent && code != ECommandStatusEvent), Panic(EDiscCtrlUnexpectedCommandEvent));	// this should never happen
+	__ASSERT_DEBUG(aRelatedCommand != NULL, Panic(EDiscCtrlUnmatchedCommandEvent));	// this should never happen
+	
+	if (!aRelatedCommand)	// it is not the response to our command - should never happen
+		return;
+	
+	if ((code == ECommandStatusEvent) && (aEvent.ErrorCode() != EOK))
+		{
+		iParent.Disconnection(aEvent.ErrorCode(), iParent.Handle(), aEvent.ErrorCode());
+		}
+	else if (code == EDisconnectionCompleteEvent)
+		{
+		const TDisconnectionCompleteEvent& disconnectionCompleteEvent = TDisconnectionCompleteEvent::Cast(aEvent);
+		// The connection handle in the disconnect complete event should be the same as the physical link's connection handle
+		__ASSERT_DEBUG(disconnectionCompleteEvent.ConnectionHandle() == iParent.Handle(), Panic(EDiscCtrlUnexpectedConnectionHandle));
+		iParent.Disconnection(aEvent.ErrorCode(), disconnectionCompleteEvent.ConnectionHandle(), static_cast<THCIErrorCode>(disconnectionCompleteEvent.Reason()));
+		}
+	}
+
+void TDisconnectCmdController::MhcqcCommandErrored(TInt __DEBUG_ONLY(aErrorCode), const CHCICommandBase* __DEBUG_ONLY(aCommand))
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(aCommand, Panic(EDiscCtrlUnmatchedCommandEvent));	// this should never happen
+	#ifdef _DEBUG
+	LOG2(_L("MhcqcCommandErrored: error code:%d opcode:0x%x"), aErrorCode, aCommand->Opcode());
+	#endif
+	// Pass this to the physical link as Command Disallowed
+	iParent.Disconnection(ECommandDisallowed, iParent.Handle(), ECommandDisallowed);
+	}
+
+
+//
+// TAuthenticationCmdController
+//
+
+TAuthenticationCmdController::TAuthenticationCmdController(CPhysicalLink& aLink, MHCICommandQueue& aCmdController) : 	
+	iParent(aLink),
+	iCmdController(aCmdController)
+	{
+	LOG_FUNC
+	}
+
+void TAuthenticationCmdController::Abort()
+	{
+	LOG_FUNC
+	iCmdController.MhcqRemoveAllCommands(*this);
+	}
+
+TInt TAuthenticationCmdController::LinkKeyRequestReply(const TBTDevAddr& aBdaddr, const TDesC8& aLinkKey) const
+/**
+	We have obtained a linkkey, tell HC
+**/
+	{
+	CLinkKeyRequestReplyCommand* cmd = NULL;
+	TBTLinkKey linkkey;
+	linkkey.Copy(aLinkKey);
+
+	TRAPD(err, cmd = CLinkKeyRequestReplyCommand::NewL(aBdaddr, linkkey));
+	if (err == KErrNone)
+		{
+		// Ownership of cmd transfered even if MhcqAddCommandL leaves
+		TRAP(err,iCmdController.MhcqAddCommandL(cmd, (MHCICommandQueueClient&)(*this)));
+		}
+	if(err != KErrNone)
+		{
+		iParent.AuthenticationComplete(ELinkKeyRequestPending);
+		}
+	return err;
+	}
+	
+TInt TAuthenticationCmdController::LinkKeyRequestNegativeReply(const TBTDevAddr& aBdaddr) const
+/**
+	We have not obtained a linkkey, tell HC
+**/
+	{
+	CLinkKeyRequestReplyNegativeCommand* cmd = NULL;
+
+	TRAPD(err, cmd = CLinkKeyRequestReplyNegativeCommand::NewL(aBdaddr));
+	if (err == KErrNone)
+		{
+		// Ownership of cmd transfered even if MhcqAddCommandL leaves
+		TRAP(err, iCmdController.MhcqAddCommandL(cmd, (MHCICommandQueueClient&)(*this)));
+		}
+	
+	if(err != KErrNone)
+		{
+		iParent.AuthenticationComplete(ELinkKeyRequestPending);
+		}
+	return err;
+	}
+
+void TAuthenticationCmdController::MhcqcCommandEventReceived(const THCIEventBase& aEvent, const CHCICommandBase* aRelatedCommand)
+	{
+	LOG_FUNC
+	THCIEventCode code = aEvent.EventCode();
+
+	__ASSERT_DEBUG(!(code != ECommandCompleteEvent && code != ECommandStatusEvent), Panic(EHCIUnknownCommandEvent));	// this should never happen
+	__ASSERT_DEBUG(aRelatedCommand != NULL, Panic(EHCIUnknownCommandEvent));	// this should never happen
+
+	if (!aRelatedCommand)	// it is not the response to our command - should never happen
+		return;
+
+	if ((code == ECommandStatusEvent) && (aEvent.ErrorCode() != EOK))
+		{
+		LOG3(_L("TAuthenticationCmdController::MhcqcCommandEventReceived: event:%d opcode:0x%x error:0x%x"), code, aRelatedCommand->Opcode(), aEvent.ErrorCode());
+		}
+	else if (code == ECommandCompleteEvent)
+		{
+		LOG2(_L("TAuthenticationCmdController::MhcqcCommandEventReceived: event:%d opcode:0x%x"), code, aRelatedCommand->Opcode());
+		iParent.AuthenticationComplete(ELinkKeyRequestPending);
+		}
+	}
+
+void TAuthenticationCmdController::MhcqcCommandErrored(TInt __DEBUG_ONLY(aErrorCode), const CHCICommandBase* __DEBUG_ONLY(aCommand))
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(aCommand, Panic(EHCIUnknownCommandEvent));	// this should never happen
+	#ifdef _DEBUG
+	LOG2(_L("MhcqcCommandErrored: error code:%d opcode:0x%x"), aErrorCode, aCommand->Opcode());
+	#endif
+	iParent.AuthenticationComplete(ELinkKeyRequestPending);
+	}
+