bluetooth/btstack/linkmgr/BasebandModel.cpp
changeset 0 29b1cd4cb562
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/linkmgr/BasebandModel.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,785 @@
+// Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// Captures the current slot commitments of our device
+// so this can be viewed of as representing (partially) the
+// Basic Piconet Physical *Channel*
+// 
+//
+
+#include <bt_sock.h>
+#include <bluetooth/logger.h>
+#include "linkconsts.h"
+#include "linkutil.h"
+#include "basebandsap.h"
+#include "Basebandmodel.h"
+#include "hcifacade.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR);
+#endif
+
+#ifdef _DEBUG
+PANICCATEGORY("basebndm");
+#endif
+
+TACLLinkType TPhysicalLinkData::ACLActualMinSlots() const
+	{
+	LOG_FUNC
+	return (iACLMaxSlotsAllowed < iACLMinSlotsAllowed) ? ENoACLLink : iACLMinSlotsAllowed;
+	}
+
+TPhysicalLinkData::TPhysicalLinkData()
+: iBdaddr(0), iConnH(0), iSCOConnH(0), ieSCOConnH(0), iACLMinSlotsAllowed(ENoACLLink), iACLMaxSlotsAllowed(EOneSlotPackets), iSCOMinRequirement(ENoSCOLink), iParked(EFalse)
+	{
+	LOG_FUNC
+	}
+
+void TPhysicalLinkData::Park()
+	{
+	LOG_FUNC
+	iParked = ETrue;
+	}
+
+void TPhysicalLinkData::UnPark()
+	{
+	LOG_FUNC
+	iParked = EFalse;
+	}
+
+TBool TPhysicalLinkData::IsParked() const
+	{
+	LOG_FUNC
+	return iParked;
+	}
+
+
+//PUBLIC FUNCTIONS
+CBTBasebandModel* CBTBasebandModel::NewL(CLinkMgrProtocol& aLMProt)
+	{
+	LOG_STATIC_FUNC
+	CBTBasebandModel* self = new(ELeave) CBTBasebandModel(aLMProt);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+CBTBasebandModel::~CBTBasebandModel()
+	{
+	LOG_FUNC
+	iLinkData.Reset();
+	iLinkData.Close();
+	}
+
+TInt CBTBasebandModel::UpdateModel(const TBTDevAddr& aAddr, TUint16 aPacketMask, TLinkType aLinkTypeForPacketMask)
+	{
+	LOG_FUNC
+	TBTConnect conn;
+	SetupConnectRecord(conn, 0, aAddr, 0, aLinkTypeForPacketMask, EDisabled);
+	return UpdateModel(conn, aPacketMask);
+	}
+
+TInt CBTBasebandModel::UpdateModel(THCIConnHandle aConnH, TLinkType aLinkTypeForHandle, TUint16 aPacketMask, TLinkType aLinkTypeForPacketMask)
+	{
+	LOG_FUNC
+	//See if link record already exists
+	TInt found = FindByHandle(aConnH);
+	//If so, update
+	if(found != KErrNotFound)
+		{
+		if (!iLinkData[found].IsParked())
+			{
+			UpdateLink(iLinkData[found], aLinkTypeForPacketMask, aPacketMask);
+			}
+		return KErrNone;
+		}
+
+	TPhysicalLinkData ld;
+	TBTConnect conn;
+	SetupConnectRecord(conn, aConnH, TBTDevAddr(0), 0, aLinkTypeForHandle, EDisabled);
+	UpdateLink(ld, conn);
+	UpdateLink(ld, aLinkTypeForPacketMask, aPacketMask);
+	iLinkData.Append(ld); //NB failure => we become too tolerant => unnecessary HCI traffic
+	return KErrNone;
+	}
+
+TInt CBTBasebandModel::UpdateModel(const TBTConnect& aConn)
+/**
+	Tries to find the record containing either the BD address, or, if not, the handle
+	supplied in 'aConn'. 
+	If successful, updates record with those values in 'aConn' that are not already 
+	in the record, and a value deduced 'aPacketMask'.
+	Whether all these values are used for ACL or SCO is decided by 'aConn.iLinkType'.
+	If unsuccessful, creates a new record with 'aConn', and 'aPacketMask'.
+*/
+	{
+	LOG_FUNC
+	return DoUpdateModel(aConn, 0, ETrue);
+	}
+
+TInt CBTBasebandModel::UpdateModel(const TBTConnect& aConn, TUint16 aPacketMask)
+/**
+	Tries to find the record containing either the BD address, or, if not, the handle
+	supplied in 'aConn'. 
+	If successful, updates record with those values in 'aConn' that are not already 
+	in the record, and a value deduced 'aPacketMask'.
+	Whether all these values are used for ACL or SCO is decided by 'aConn.iLinkType'.
+	If unsuccessful, creates a new record with 'aConn', and 'aPacketMask'.
+*/
+	{
+	LOG_FUNC
+	return DoUpdateModel(aConn, aPacketMask, EFalse);
+	}
+
+TInt CBTBasebandModel::UpdateModel(THCIConnHandle aConnH, TUint8 aMaxSlots)
+	{
+	LOG_FUNC
+	TInt found = FindByACLHandle(aConnH);
+	if(found==KErrNotFound)
+		{
+		return KErrArgument;
+		}
+	//convert param 2 to contraints of CBTBasebandModel
+	TACLLinkType maxSlots;
+	switch(aMaxSlots)
+		{
+		case 1:
+			maxSlots = EOneSlotPackets;
+			break;
+		case 3:
+			maxSlots = EThreeSlotPackets;
+			break;
+		case 5:
+			maxSlots = EFiveSlotPackets;
+			break;
+		default:
+			maxSlots = ENoACLLink; //not sure - make most likely to pass
+			break;
+		}
+
+	iLinkData[found].iACLMaxSlotsAllowed = maxSlots;
+	return KErrNone;
+	}
+
+void CBTBasebandModel::UpdateModelIfRecordExists(THCIConnHandle aConnH, TUint16 aPacketMask)
+   	{
+	LOG_FUNC
+   	#pragma message("eSCO ignored in baseband model")
+   	// Updates link bandwidth consumption statistics
+   	
+   	TInt found = FindByACLHandle(aConnH);
+  	if(found != KErrNotFound)
+  		{
+  		UpdateLink(iLinkData[found], EACLLink, aPacketMask);
+  		return;
+   		}
+   		
+	found = FindBySCOHandle(aConnH);
+	if(found != KErrNotFound)
+		{
+		UpdateLink(iLinkData[found], ESCOLink, aPacketMask);
+		return;
+		}
+   	}
+   
+void CBTBasebandModel::UpdateModelIfNoRecord(THCIConnHandle aConnH, TUint16 aPacketMask, TLinkType aLinkType)
+	{
+	LOG_FUNC
+	if(FindByHandle(aConnH) == KErrNotFound)
+		{
+		TBTConnect conn;
+		SetupConnectRecord(conn, aConnH, TBTDevAddr(0), 0, aLinkType, EDisabled);
+		TPhysicalLinkData ld;
+		UpdateLink(ld, conn);
+		UpdateLink(ld, aLinkType, aPacketMask);
+		iLinkData.Append(ld); //NB failure => we become too tolerant => unnecessary HCI traffic
+		}
+	}
+
+void CBTBasebandModel::UpdateModelForDisconnection(THCIConnHandle aConnH, TLinkType aLinkType)
+	{
+	LOG_FUNC
+	TInt found = KErrNotFound;
+	
+	LOG2(_L("Baseband model: updating for disconnect. Handle %d, link type %d"), aConnH, aLinkType);
+	
+	switch (aLinkType)
+		{
+		case EACLLink:
+			found = FindByACLHandle(aConnH);
+			if (found != KErrNotFound)
+				{
+				iLinkData.Remove(found); //remove record if ACL
+				}
+			break;
+		
+		case ESCOLink:
+			found = FindBySCOHandle(aConnH);
+			if(found != KErrNotFound)
+				{
+				iLinkData[found].iSCOConnH = 0; //zero SCO handle if SCO 
+				iLinkData[found].iSCOMinRequirement = ENoSCOLink;//set SCO requirements to 0
+				}
+				
+		#ifdef _DEBUG
+			found = FindBySCOHandle(aConnH);
+			ASSERT_DEBUG(found == KErrNotFound);
+		#endif
+			
+			break;
+		
+		case EeSCOLink:
+			found = FindByeSCOHandle(aConnH);
+			if (found != KErrNotFound)
+				{
+				LOG1(_L("Baseband model: Found eSCO link at index %d"), found);
+				iLinkData[found].ieSCOConnH = 0; // zero eSCO handle
+				}
+				
+		#ifdef _DEBUG
+			found = FindByeSCOHandle(aConnH);
+			ASSERT_DEBUG(found == KErrNotFound);
+		#endif
+			
+			break;
+		
+		// Explicitly ignore 'default' in the hope of compiler warnings if link types
+		// grow again.
+		}
+	}
+
+void CBTBasebandModel::UpdateModelForConnectionError(TBTDevAddr aAddr, TLinkType aLinkType)
+	{
+	LOG_FUNC
+	TInt found = KErrNotFound;
+	
+	LOG1(_L("Baseband model: updating for connection error. link type %d"), aLinkType);
+	LOG(_L("  BDADDR: "));
+	LOGBTDEVADDR(aAddr);
+	
+	found = FindByAddress(aAddr);
+
+	if (found != KErrNotFound)
+		{
+		switch(aLinkType)
+			{
+			case (EACLLink):
+				iLinkData.Remove(found); //remove record if ACL
+				break;
+				
+			case (ESCOLink):
+				iLinkData[found].iSCOConnH = 0; //zero SCO handle 
+				iLinkData[found].iSCOMinRequirement = ENoSCOLink;//set SCO requirements to 0
+				break;
+				
+			case (EeSCOLink):
+				LOG1(_L("Baseband model: Found eSCO link at index %d"), found);
+				iLinkData[found].ieSCOConnH = 0; // zero eSCO handle
+				break;
+			}
+		}
+	}
+	
+void CBTBasebandModel::ParkLink(THCIConnHandle aConnH)
+	{
+	LOG_FUNC
+	TInt found = FindByACLHandle(aConnH);
+	if (found != KErrNotFound)
+		{
+		iLinkData[found].Park();
+		}
+	}
+
+
+void CBTBasebandModel::UnParkLink(THCIConnHandle aConnH)
+	{
+	LOG_FUNC
+	TInt found = FindByACLHandle(aConnH);
+	if (found != KErrNotFound)
+		{
+		iLinkData[found].UnPark();
+		}
+	}
+
+
+TBool CBTBasebandModel::IsACLPossible() const
+/**
+	Decide if ACL link with default packet type is possible
+	If error, or not sure, return ETrue and let hardware sort it out.
+*/
+	{
+	LOG_FUNC
+	return IsACLPossible(ACLRequirement(KHCIDefaultPacketType));
+	}
+
+
+TBool CBTBasebandModel::IsSCOPossible() const
+/**
+	Decide if SCO link with default packet type is possible
+	If error, or not sure, return ETrue and let hardware sort it out.
+*/
+	{
+	LOG_FUNC
+	return IsSCOPossible(SCORequirement(KHCIDefaultSCOPacketType));
+	}
+
+
+
+//PRIVATE FUNCTIONS
+CBTBasebandModel::CBTBasebandModel(CLinkMgrProtocol& aLMProt) 
+: iLinkData(KBTBasebandModelLinkArrayGranularity),
+iLMProt(aLMProt)
+	{
+	LOG_FUNC
+	}
+
+void CBTBasebandModel::ConstructL()
+	{
+	LOG_FUNC
+	}
+
+
+TBool CBTBasebandModel::IsACLPossible(TACLLinkType aACLType) const
+/**
+	Decide if another ACL link allowing minimum of 'aACLType' packets
+	will be allowed by hardware LMP.
+	If error, or not sure, return ETrue and let hardware sort it out.
+*/
+	{
+	LOG_FUNC
+	TACLLinkType aclLinkType = ENoACLLink;
+	TInt scoBandwidth = 0;
+	TInt activePhyCount = 0;
+	TInt scoCount = 0;
+	GarnerInfo(activePhyCount, scoCount, aclLinkType, scoBandwidth);
+	CheckInfo(activePhyCount, scoCount, aclLinkType, scoBandwidth);
+
+	// Check that the stack could accommodate another ACL link
+	if (activePhyCount > KMaxActivePhysicalLinks)
+		{
+		return EFalse;
+		}
+	
+	return IsCombinationAllowed(scoBandwidth, aACLType);
+	}
+
+TBool CBTBasebandModel::IsSCOPossible(TSCOLinkType aSCOType) const
+/**
+	Decide if SCO link allowing minimum of 'aSCOType' packets
+	will be allowed by hardware LMP.
+	If error, or not sure, return ETrue and let hardware sort it out.
+*/
+	{
+	LOG_FUNC
+	TACLLinkType aclLinkType = ENoACLLink;
+	TInt scoBandwidth = 0;
+	TInt scoCount = 0;
+	TInt activePhyCount = 0;
+	GarnerInfo(activePhyCount, scoCount, aclLinkType, scoBandwidth);
+	CheckInfo(activePhyCount, scoCount, aclLinkType, scoBandwidth);
+	
+	
+	#ifdef _DEBUG
+	TInt bw = Bandwidth(aSCOType);
+	LOG(_L("IsSCOLinkPossible: Pre-existing:-"));
+	LOG2(_L("IsSCOLinkPossible: PHY count = %d, scoCount = %d, ACL link type = %d, "), activePhyCount, scoCount);
+	LOG2(_L("IsSCOLinkPossible: ACL link type = %d, scoBandwidth = %d"), aclLinkType, scoBandwidth);
+	LOG(_L("IsSCOLinkPossible: New requirement:-"));
+	LOG2(_L("IsSCOLinkPossible: SCO type required = %d, => bandwidth needed = %d"), aSCOType, bw);
+	#endif
+
+  	
+  	//Deal with too much SCO
+  	if((scoBandwidth + Bandwidth(aSCOType))>EBandwidthAllUsed)
+   		{
+   		return EFalse;
+   		}
+  	//Deal with too many PHYs using SCOs
+  	if(scoCount>=KMaxPhysicalLinksWithSCO&&aSCOType!=ENoSCOLink)
+   		{
+  		return EFalse;
+  		}
+  	return IsCombinationAllowed(scoBandwidth + Bandwidth(aSCOType), aclLinkType);
+	}
+
+TBool CBTBasebandModel::IsCombinationAllowed(TInt aSCOBandwidth, TACLLinkType aACLType) const
+	{
+	LOG_FUNC
+	//Now go through each ACL/SCOcombination carefully!
+	switch(aSCOBandwidth)
+		{
+		case EBandwidthNoneUsed:
+			return ETrue;
+		case EBandwidthOneThirdUsed:
+			return (aACLType<=EThreeSlotPackets);
+		case EBandwidthOneHalfUsed:
+		case EBandwidthTwoThirdsUsed:
+			return (aACLType<=EOneSlotPackets);
+		default:
+			return (aSCOBandwidth <= EBandwidthAllUsed);
+		}
+	}
+
+TBandwidth CBTBasebandModel::Bandwidth(TSCOLinkType aLinkType) const
+	{
+	LOG_FUNC
+	switch (aLinkType)
+		{
+		case ENoSCOLink:
+			return EBandwidthNoneUsed;
+			
+		case ESCODVPackets:
+			return EBandwidthAllUsed;
+			
+		case ESCOHV1Packets:
+			return EBandwidthAllUsed;
+			
+		case ESCOHV2Packets:
+			return EBandwidthOneHalfUsed;
+			
+		case ESCOHV3Packets:
+			return EBandwidthOneThirdUsed;
+			
+		default:
+			__ASSERT_DEBUG(ETrue, Panic(EBTBasebandModelBadRecord));
+			return EBandwidthNoneUsed;
+		}
+	}
+
+void CBTBasebandModel::GarnerInfo(TInt& aActivePhyCount, TInt& aSCOCount, TACLLinkType& aGreatestMinSlotRequirement, TInt& aSCOBandwidth) const
+	{
+	LOG_FUNC
+	aSCOCount = 0;
+	aGreatestMinSlotRequirement = ENoACLLink;
+	aSCOBandwidth = 0;
+
+	TInt totalPhyCount = iLinkData.Count();
+	aActivePhyCount = 0;
+	for (TInt i=(totalPhyCount-1); i>=0; i--)
+		{
+		if (!iLinkData[i].IsParked())
+			{
+			aActivePhyCount++;
+			if (iLinkData[i].iSCOMinRequirement != ENoSCOLink)
+				{
+				aSCOCount++; //only one SCO link per PHY for the time being
+				aSCOBandwidth += Bandwidth(iLinkData[i].iSCOMinRequirement);
+				}
+			aGreatestMinSlotRequirement = aGreatestMinSlotRequirement<iLinkData[i].ACLActualMinSlots()?
+								iLinkData[i].iACLMinSlotsAllowed:aGreatestMinSlotRequirement;
+			}
+		}
+	__ASSERT_DEBUG(aSCOBandwidth <= EBandwidthAllUsed,
+				   Panic(EBTBasebandModelBadRecord));
+	}
+
+void CBTBasebandModel::CheckInfo(TInt __DEBUG_ONLY(aActivePhyCount),
+								 TInt __DEBUG_ONLY(aSCOCount),
+								 TACLLinkType __DEBUG_ONLY(aGreatestMinSlotRequirement),
+								 TInt __DEBUG_ONLY(aSCOBandwidth)) const
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(aActivePhyCount <= KMaxActivePhysicalLinks,
+				   Panic(EBTBasebandModelBadRecord));
+	__ASSERT_DEBUG(aSCOCount <= KMaxPhysicalLinksWithSCO,
+				   Panic(EBTBasebandModelBadRecord));
+	__ASSERT_DEBUG((aSCOBandwidth == EBandwidthNoneUsed ||
+				   aSCOBandwidth == EBandwidthOneThirdUsed ||
+				   aSCOBandwidth == EBandwidthOneHalfUsed ||
+				   aSCOBandwidth == EBandwidthTwoThirdsUsed ||
+				   aSCOBandwidth == EBandwidthAllUsed),
+				   Panic(EBTBasebandModelBadRecord));
+	__ASSERT_DEBUG((aGreatestMinSlotRequirement == ENoACLLink ||
+				   aGreatestMinSlotRequirement == EOneSlotPackets ||
+				   aGreatestMinSlotRequirement == EThreeSlotPackets ||
+				   aGreatestMinSlotRequirement == EFiveSlotPackets),
+				   Panic(EBTBasebandModelBadRecord));
+	}
+
+TBandwidth CBTBasebandModel::CostOfACLLink(TACLLinkType aACLType) const
+	{
+	LOG_FUNC
+	switch(aACLType)
+		{
+		case EOneSlotPackets:
+		return EBandwidthOneTwelfthUsed;
+		case EThreeSlotPackets:
+		return EBandwidthFiveSixthsUsed;
+		case EFiveSlotPackets:
+		return EBandwidthElevenTwelfthsUsed;
+		default:
+		return EBandwidthNoneUsed;
+		}
+	}
+
+TACLLinkType CBTBasebandModel::ACLRequirement(TUint16 aPacketMask) const
+	{
+	LOG_FUNC
+	if(aPacketMask & (KDM1Packet | KDH1Packet | KAUX1Packet))
+		{
+		return EOneSlotPackets;
+		}
+	if(aPacketMask & (KDM3Packet | KDH3Packet))
+		{
+		return EThreeSlotPackets;
+		}
+	if(aPacketMask & (KDM5Packet | KDH5Packet))
+		{
+		return EFiveSlotPackets;
+		}
+	//No ACL link type given so assume...
+//	__DEBUGGER();
+	return ENoACLLink;
+	}
+
+TSCOLinkType CBTBasebandModel::SCORequirement(TUint16 aPacketMask) const
+	{
+	LOG_FUNC
+	//Baseband needs to err towards the least bandwidth used,
+	//so start checking mask for HV3 packets
+	if(aPacketMask & ESCOHV3Packets)
+		{
+		return ESCOHV3Packets;
+		}
+	if(aPacketMask & ESCOHV2Packets)
+		{
+		return ESCOHV2Packets;
+		}
+	if(aPacketMask & ESCOHV1Packets)
+		{
+		return ESCOHV1Packets;
+		}
+	//DV as expensive as HV1 but do allow ACL data transmission over
+	//the physical link on which the SCO link has been created
+	if(aPacketMask & ESCODVPackets)
+		{
+		return ESCODVPackets;
+		}
+	//No SCO link type given so assume...
+	__DEBUGGER();
+	return ENoSCOLink;
+	}
+
+TInt CBTBasebandModel::FindByAddress(const TBTDevAddr& aAddr) const
+	{
+	LOG_FUNC
+	if(aAddr == TBTDevAddr(0))
+		//Do not search on zero.
+		{
+		return KErrNotFound;
+		}
+
+	TInt count = iLinkData.Count();
+	for (TInt i=(count-1); i>=0; i--)
+		{
+		if (iLinkData[i].iBdaddr == aAddr && (!iLinkData[i].IsParked()))
+			{
+			return i;
+			}
+		}
+	return KErrNotFound;
+	}
+
+TInt CBTBasebandModel::FindByHandle(THCIConnHandle aHandle) const
+	{
+	LOG_FUNC
+	TInt ret = FindByACLHandle(aHandle);
+	if(ret == KErrNotFound)
+		{
+		ret = FindBySCOHandle(aHandle);
+		}
+	if(ret == KErrNotFound)
+		{
+		ret = FindByeSCOHandle(aHandle);
+		}
+	return ret;
+	}	
+
+TInt CBTBasebandModel::FindByACLHandle(THCIConnHandle aHandle) const
+	{
+	LOG_FUNC
+	if(!aHandle)
+		//Do not search on zero.
+		{
+		return KErrNotFound;
+		}
+
+	for (TInt i=(iLinkData.Count()-1); i>=0; i--)
+		{
+		const TPhysicalLinkData& handle = iLinkData[i];
+		if ((handle.iConnH == aHandle))
+			{
+			return i;
+			}
+		}
+	return KErrNotFound;
+	}
+
+TInt CBTBasebandModel::FindBySCOHandle(THCIConnHandle aHandle) const
+	{
+	LOG_FUNC
+	if(!aHandle)
+		//Do not search on zero.
+		{
+		return KErrNotFound;
+		}
+
+	TInt count = iLinkData.Count();
+	for (TInt i=(count-1); i>=0; i--)
+		{
+		if (iLinkData[i].iSCOConnH == aHandle)
+			{
+			return i;
+			}
+		}
+	return KErrNotFound;
+	}
+
+TInt CBTBasebandModel::FindByeSCOHandle(THCIConnHandle aHandle) const
+	{
+	LOG_FUNC
+	if(!aHandle)
+		//Do not search on zero.
+		{
+		return KErrNotFound;
+		}
+
+	TInt count = iLinkData.Count();
+	for (TInt i=(count-1); i>=0; i--)
+		{
+		if (iLinkData[i].ieSCOConnH == aHandle)
+			{
+			return i;
+			}
+		}
+	return KErrNotFound;
+	}
+
+TInt CBTBasebandModel::DoUpdateModel(const TBTConnect& aConn, TUint16 aPacketMask, TBool iIgnorePacketMask)
+/**
+	Tries to find the record containing either the BD address, or, if not, the handle
+	supplied in 'aConn'. 
+	If successful, updates record with those values in 'aConn' that are not already 
+	in the record, and a value deduced 'aPacketMask'.
+	Whether all these values are used for ACL or SCO is decided by 'aConn.iLinkType'.
+	If unsuccessful, creates a new record with 'aConn', and 'aPacketMask'.
+*/
+	{
+	LOG_FUNC
+	//See if link record already exists
+	TInt found = FindByAddress(aConn.iBdaddr);
+	if(found == KErrNotFound)
+		{
+		//try finding by handle
+		found = FindByHandle(aConn.iConnH);
+		}
+	//If so, update
+	if(found != KErrNotFound)
+		{
+		if (!iLinkData[found].IsParked())
+			{
+			UpdateLink(iLinkData[found], aConn);
+			if(!iIgnorePacketMask)
+				{
+				UpdateLink(iLinkData[found], aConn.iLinkType, aPacketMask);
+				}
+			}
+		return KErrNone;
+		}
+
+	//Got to here - this is a new PHY
+	__ASSERT_DEBUG(aConn.iLinkType==EACLLink, Panic(EBTBasebandModelBadRecord));
+	if(aConn.iLinkType!=EACLLink)
+	{
+	return KErrArgument;
+	}
+
+	TPhysicalLinkData ld;
+	UpdateLink(ld, aConn);
+	if(!iIgnorePacketMask)
+		{
+		UpdateLink(ld, aConn.iLinkType, aPacketMask);
+		}
+	(void)iLinkData.Append(ld); //NB failure => we become too tolerant => unnecessary HCI traffic
+	return KErrNone;
+	}
+
+void CBTBasebandModel::UpdateLink(TPhysicalLinkData& aLinkData, const TBTConnect& aConn)
+/**
+	Use aPacketMask together those values aConn not already in 
+	aLinkData to update aLinkData.
+	NB An ACL handle may have been supplied in aConn, 
+	   but the update is for a SCO Link
+*/
+	{
+	LOG_FUNC
+	LOG5(_L("Baseband model: updating record %d,%d,%d, to %d, type %d"), aLinkData.iConnH, aLinkData.iSCOConnH, aLinkData.ieSCOConnH, aConn.iConnH, aConn.iLinkType);
+	__ASSERT_DEBUG((aLinkData.iBdaddr==TBTDevAddr(0) || aLinkData.iBdaddr==aConn.iBdaddr),
+					Panic(EBTBasebandModelBadRecord));
+	aLinkData.iBdaddr = aConn.iBdaddr;
+	
+	switch (aConn.iLinkType)
+		{
+		case ESCOLink:
+			__ASSERT_DEBUG((aLinkData.iSCOConnH==0 || aLinkData.iSCOConnH==aConn.iConnH),
+							Panic(EBTBasebandModelBadRecord));
+			aLinkData.iSCOConnH = aConn.iConnH;
+			break;
+
+		case EACLLink:
+			__ASSERT_DEBUG((aLinkData.iConnH==0 || aLinkData.iConnH==aConn.iConnH),
+							Panic(EBTBasebandModelBadRecord));
+			aLinkData.iConnH = aConn.iConnH;
+			break;
+		
+		case EeSCOLink:
+			__ASSERT_DEBUG((aLinkData.ieSCOConnH==0 || aLinkData.ieSCOConnH==aConn.iConnH),
+							Panic(EBTBasebandModelBadRecord));
+			aLinkData.ieSCOConnH = aConn.iConnH;
+			break;
+		
+		default:
+			Panic(EBTUnknownLogicalLink);
+		}
+	}
+
+void CBTBasebandModel::UpdateLink(TPhysicalLinkData& aLinkData, TLinkType aLinkType, TUint16 aPacketMask)
+	{
+	LOG_FUNC
+	if(aLinkType==ESCOLink)
+		{
+		aLinkData.iSCOMinRequirement = SCORequirement(aPacketMask);
+		}
+	else if (aLinkType == EACLLink)
+		{
+		aLinkData.iACLMinSlotsAllowed = ACLRequirement(aPacketMask);
+		}
+	}
+
+void CBTBasebandModel::SetupConnectRecord(TBTConnect& aConn, THCIConnHandle aConnH, 
+												const TBTDevAddr& aAddr,
+												TUint aCoD,
+												TLinkType aLinkType,
+												TEncryptMode aEncryptMode) const
+/**
+	Helper class to fill out a TBTConnect
+*/
+	{
+	LOG_FUNC
+    aConn.iConnH = aConnH;
+    aConn.iBdaddr = aAddr;
+    aConn.iCoD = aCoD;
+    aConn.iLinkType = aLinkType;
+    aConn.iEncryptMode = aEncryptMode;
+	}