bluetooth/btstack/eirman/eirmanager.cpp
changeset 0 29b1cd4cb562
child 23 32ba20339036
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/eirman/eirmanager.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,756 @@
+// Copyright (c) 2007-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:
+//
+
+/**
+ @file
+ @internalComponent
+*/
+
+#include "eirmanager.h"
+#include <bluetooth/hcicommandqueue.h>
+#include <bttypes.h>
+#include <bluetooth/hci/commandcompleteevent.h>
+#include <bluetooth/hci/commandstatusevent.h>
+#include <bluetooth/hci/command.h>
+#include <bluetooth/hci/hciconsts.h>
+#include <bluetooth/hci/writeextendedinquiryresponsecommand.h>
+#include <bluetooth/logger.h>
+#include "eirmansession.h"
+#include "linkmgr.h"
+#include "eirmanpanics.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_EIRMANAGER);
+#endif
+
+__ASSERT_COMPILE(KReservedNameBytes + KReservedUuid16Bytes + KReservedUuid128Bytes + KReservedManufacturerSpecificDataBytes + KReservedTxPowerLevelBytes == KEirPacketSize);
+
+CEirManager* CEirManager::NewL(MHCICommandQueue& aCommandQueue, CLinkMgrProtocol& aLinkMgrProtocol)
+	{
+	LOG_STATIC_FUNC
+	
+	CEirManager* self = new(ELeave) CEirManager(aCommandQueue, aLinkMgrProtocol);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CEirManager::~CEirManager()
+	{
+	LOG_FUNC
+	
+	// Delete all registered tags
+	for(TInt8 i = static_cast<TInt8>(EEirTagName); i < static_cast<TInt8>(EEirTagRESERVED); i++)
+		{
+		__ASSERT_DEBUG(!iNotifiees[i].iNotifiee, EIR_MANAGER_PANIC(EEirManagerNotifieeNotCleared));
+		}
+	
+	iCommandQueue.MhcqRemoveAllCommands(*this);
+	delete iWriteEirTimer;
+	}
+
+CEirManager::CEirManager(MHCICommandQueue& aCommandQueue, CLinkMgrProtocol& aLinkMgrProtocol)
+	: iOffersStale(EFalse)
+	, iCmdId(KInvalidCommandQueueItemId)
+	, iCmdCount(0)
+	, iCommandQueue(aCommandQueue)
+	, iLinkMgrProtocol(aLinkMgrProtocol)
+	{
+	LOG_FUNC
+	}
+
+void CEirManager::ConstructL()
+	{
+	LOG_FUNC
+	iWriteEirTimer = CWriteEirTimer::NewL(*this);
+	}
+
+void CEirManager::UnhookClient(TEirTag aTag)
+	{
+	LOG_FUNC
+	if (iNotifiees[aTag].iNotifiee)
+		{
+		iNotifiees[aTag].iNotifiee = NULL;
+		if(iNotifiees[aTag].iTagData)
+			{
+			delete iNotifiees[aTag].iTagData;
+			iNotifiees[aTag].iTagData = NULL;
+			}			
+		}
+	}
+
+// A client register aTag with EirManager
+TInt CEirManager::RegisterTag(TEirTag aTag, MEirManagerNotifiee& aNotifiee)
+	{
+	LOG_FUNC
+	TInt ret = KErrNone;
+	LOG1(_L("CEirManager::RegisterTag tag = %d"), aTag);
+
+	if(!TagValid(aTag))
+		{
+		LOG1(_L("CEirManager::RegisterTag INVALID TAG: %d"), aTag);
+		ret = KErrArgument;
+		}
+	else if(!TagAllowed(aTag))
+		{
+		LOG(_L("CEirManager::RegisterTag: IN USE"));
+		ret = KErrInUse;
+		}
+	else
+		{
+		iNotifiees[aTag].iNotifiee = &aNotifiee;
+		iNotifiees[aTag].iTagData = NULL;
+		iNotifiees[aTag].iOfferedBytes = 0;
+		iNotifiees[aTag].iEirDataMode = EEirDataComplete;
+		iNotifiees[aTag].iState = 0x00;
+
+		if (!OffersPending() && NeedToOffer())
+			{
+			// Do not consider this for insertion yet if there are offers pending.
+			// Instead, once the offers have been responded to, this client will be dealt with
+			NotifyClients();
+			}
+		}
+	return ret;
+	}
+
+// A client deregister aTag with EirManager
+void CEirManager::DeregisterTag(TEirTag aTag)
+	{
+	LOG_FUNC
+	LOG1(_L("CEirManager::DeregisterTag tag = %d"), aTag);
+
+	if(!TagValid(aTag))
+		{
+		LOG1(_L("CEirManager::DeregisterTag INVALID TAG: %d"), aTag);
+		return;
+		}
+
+	if (iNotifiees[aTag].iNotifiee)
+		{
+		UnhookClient(aTag);	
+		if(iNotifiees[aTag].iRequiredBytes > 0)
+			{
+			// It did require some bytes and now requires none, reevaluate offers
+			iNotifiees[aTag].iState &= ~KNeedOffer;
+			// A client could storm in a deregister before setting data - so we remove
+			// any pending offer as a SetData after deregistering will be errored - any
+			// previous offer has been voided.
+			iNotifiees[aTag].iState &= ~KOfferPending;
+			if(!OffersPending())
+				{
+				NotifyClients();
+				}
+			else if(iNotifiees[aTag].iOfferedBytes > 0)
+				{
+				iOffersStale = ETrue;
+				}
+			}
+		if(!NeedToOffer() && !OffersPending())
+			{
+			TryToUpdateHostController();
+			}
+		// Now tidy up offer and requested values (they are no longer valid).
+		iNotifiees[aTag].iRequiredBytes = 0;
+		iNotifiees[aTag].iOfferedBytes = 0;
+		}
+	}
+
+TInt CEirManager::SetData(TEirTag aTag, TDesC8& aData, TEirDataMode aEirDataMode)
+	{
+	LOG_FUNC
+	LOG1(_L("CEirManager::SetData tag = %d"), aTag);
+	
+	if(!iLinkMgrProtocol.IsExtendedInquiryResponseSupportedLocally())
+		{
+		return KErrNotSupported;
+		}
+
+	if(!TagValid(aTag))
+		{
+		LOG1(_L("CEirManager::SetData INVALID TAG: %d"), aTag);
+		return KErrArgument;
+		}
+
+	if(!iNotifiees[aTag].iNotifiee)
+		{
+		// No client registered with this tag
+		return KErrNotFound;
+		}
+
+	LOG2(_L("CEirManager::SetData data len = %d offered bytes: %d"), aData.Length(), iNotifiees[aTag].iOfferedBytes);
+
+	if (!(iNotifiees[aTag].iState & KOfferPending))
+		{
+		// No offer pending for this tag
+		return KErrNotReady;		
+		}
+	if(iNotifiees[aTag].iOfferedBytes < aData.Length())
+		{
+		// Client wants to publish more data than allowed to
+		return KErrTooBig;
+		}
+		
+	TInt ret = KErrNone;
+	
+	HBufC8** tagdata;
+	
+	FindTagData(aTag, tagdata);
+	
+	// Free up whatever there was before
+	if (*tagdata)
+		{
+		delete *tagdata;
+		}
+
+	HBufC8* data = NULL;
+
+	// If the descriptor is empty, mark the heap buffer pointer as NULL, 
+	// meaning that the client is registered for this tag, but there is no currently no data available for it
+	if(aData.Length() != 0x0)
+		{
+		data = aData.Alloc();
+		if (!data)
+			{
+			LOG(_L("CEirManager::SetData: NO MEMORY "));
+			ret = KErrNoMemory;
+			}
+		}
+
+	*tagdata = data;
+
+	// We no longer have a value pending.
+	iNotifiees[aTag].iState &= ~KOfferPending;
+	iNotifiees[aTag].iEirDataMode = aEirDataMode;
+
+	if(NeedToOffer() && !OffersPending())
+		{
+		NotifyClients();
+		}
+
+	if(!NeedToOffer() && !OffersPending())
+		{
+
+		// Regardless of whether the allocation failed or not, check whether all offers have been accepted
+		// and update the Host Controller accordingly
+		TryToUpdateHostController();
+		}
+		
+	LOG1(_L("CEirManager::SetData returning = %d"), ret);
+	return ret;
+	}
+
+void CEirManager::MhcqcCommandEventReceived(const THCIEventBase& aEvent, 
+											const CHCICommandBase* /*aRelatedCommand*/)
+	{
+	LOG_FUNC
+	if(aEvent.EventCode() == ECommandCompleteEvent)
+		{
+		const THCICommandCompleteEvent& completeEvent = static_cast<const THCICommandCompleteEvent&>(aEvent);
+		if(completeEvent.CommandOpcode() == KWriteExtendedInquiryResponseOpcode)
+			{
+			--iCmdCount;
+			}
+		else
+			{
+			LOG1(_L("Warning!! Unexpected Command Complete Event Received (command opcode: 0x%04x)"), completeEvent.CommandOpcode());
+			__ASSERT_DEBUG(EFalse, EIR_MANAGER_PANIC(EEirManagerUnexpectedCompleteEvent));
+			}
+		}
+	else
+		{
+		LOG1(_L("Warning!! Unexpected Event Received (event code: 0x%02x)"), aEvent.EventCode());
+		__ASSERT_DEBUG(EFalse, EIR_MANAGER_PANIC(EEirManagerUnexpectedEvent));
+		}
+	}
+
+void CEirManager::MhcqcCommandErrored(TInt aErrorCode, const CHCICommandBase* aCommand)
+	{
+	LOG_FUNC
+	aErrorCode = aErrorCode;   // Avoid unused warning
+	LOG1(_L("\taErrorCode = %d"), aErrorCode);
+	if(aCommand && aCommand->Opcode() == KWriteExtendedInquiryResponseOpcode)
+		{
+		--iCmdCount;
+		}
+	else
+		{
+		LOG(_L("Warning!! Unexpected Error Received"));
+		if(aCommand)
+			{
+			LOG1(_L("\taCommand->Opcode() = 0x%04x"), aCommand->Opcode());
+			}
+		}
+	}
+
+void CEirManager::WriteExtendedInquiryResponseL(TBool aFECRequired, const TDesC8& aEir)
+	{
+	LOG_FUNC
+	if(iCmdCount > 0)
+		{
+		// There is at least 1 outstanding command in the queue
+		TInt err = iCommandQueue.MhcqRemoveCommand(iCmdId, *this);
+		if(err == KErrNone)
+			{
+			--iCmdCount;
+			}
+		// else if there was an error it is because the command cannot be removed
+		// "at this time" so we just "leak" it.  This isn't a problem, this was a 
+		// best effort optimisation.
+		}
+	CWriteExtendedInquiryResponseCommand* cmd = CWriteExtendedInquiryResponseCommand::NewL(aFECRequired ? 1 : 0, aEir);
+	// Ownership of cmd transfered
+	iCmdId = iCommandQueue.MhcqAddCommandL(cmd, *this);
+	++iCmdCount;
+	}
+
+// Check whether a specific tag is allowed to be registered
+TBool CEirManager::TagAllowed(TEirTag aTag) const
+	{
+	LOG_FUNC
+	// We don't support 32 bit UUIDs
+	return (aTag!=EEirTagSdpUuid32);
+	}
+	
+// Check whether aTag is present
+TBool CEirManager::TagPresent(TEirTag aTag) const
+	{
+	LOG_FUNC
+	return (iNotifiees[aTag].iNotifiee != NULL);
+	}
+
+// Find the registered data for aTag
+void CEirManager::FindTagData(TEirTag aTag, HBufC8**& aOutPointerToData)
+	{
+	LOG_FUNC
+	aOutPointerToData = &iNotifiees[aTag].iTagData;
+	}
+
+// Count the number of registered tags
+TInt CEirManager::TagCount() const
+	{
+	LOG_FUNC
+	TInt count = 0;
+	for (TInt i=0; i<EEirTagRESERVED; i++)
+		{
+		if (iNotifiees[i].iNotifiee)
+			{
+			count++;
+			}
+		}
+	return count;
+	}
+
+TBool CEirManager::OffersPending() const
+	{
+	LOG_FUNC
+	for (TInt i=0; i<EEirTagRESERVED; i++)
+		{
+		if(iNotifiees[i].iState & KOfferPending)
+			{
+			return ETrue;
+			}
+		}
+	return EFalse;
+	}
+
+TBool CEirManager::NeedToOffer() const
+	{
+	LOG_FUNC
+	if(iOffersStale)
+		{
+		return ETrue;
+		}
+	for (TInt i=0; i<EEirTagRESERVED; i++)
+		{
+		if (iNotifiees[i].iState & KNeedOffer)
+			{
+			return ETrue;
+			}
+		}
+	return EFalse;
+	}
+
+// Notify EirManager there is a change of data from aTag client
+TInt CEirManager::NewData(TEirTag aTag, TInt aRequiredBytes)
+	{
+	LOG_FUNC
+	if(!iLinkMgrProtocol.IsExtendedInquiryResponseSupportedLocally())
+		{
+		return KErrNotSupported;
+		}
+
+	// including 2 bytes for length and tag
+	TInt requiredBytes = 0;
+	TInt currentBytes = iNotifiees[aTag].iRequiredBytes;
+	if(aRequiredBytes > 0)
+		{
+		requiredBytes = aRequiredBytes + KEirLengthTagLength;
+		}
+	else if(aTag == EEirTagName && aRequiredBytes == 0)
+		{
+		// This is a special case for device without local name
+		requiredBytes = KEirLengthTagLength;
+		}
+	else
+		{
+		requiredBytes = 0;
+		}
+	
+	iNotifiees[aTag].iRequiredBytes = requiredBytes; // always record what we last asked for.
+	
+	if(!(iNotifiees[aTag].iState & KNeedOffer))
+		{
+		// We currently aren't marked as needing an offer so we will need to fix that...
+		iNotifiees[aTag].iState |= KNeedOffer;
+		if(requiredBytes == currentBytes && !(iNotifiees[aTag].iState & KOfferPending) && !iOffersStale)
+			{
+			// If the same then we handle locally, if we aren't already waiting.
+			DoNotify(aTag);
+			}
+		else if(!OffersPending())
+			{
+			// If not waiting for anybody then lets kick off a round.
+			NotifyClients();
+			}
+		}
+	return KErrNone;
+	}
+	
+// Notify all registered clients about their allocated data length
+void CEirManager::NotifyClients()
+	{
+
+	LOG_FUNC
+
+	// go around each notifiee and ask for values
+	// our policy at the moment is: 
+	// after allocating reserved bytes for each tag, we will prioritize tag with big granularity
+
+	ComputeOfferLengths(); 
+
+	for (TInt i = 0; i<EEirTagRESERVED; i++)
+		{
+		if(TagPresent((TEirTag) i))
+			{
+			DoNotify((TEirTag)i);
+			}
+		}
+	}
+
+// Notify each tag about the available data length
+void CEirManager::DoNotify(TEirTag aTag)
+	{
+	LOG_FUNC
+
+	iNotifiees[aTag].iState &= ~KNeedOffer; // We got an offer!
+	
+	// Only make valid offers: offers more than 0 and 0 offers with non 0 request
+	if(iNotifiees[aTag].iOfferedBytes > 0)
+		{
+		// Notify client about offered bytes
+		iNotifiees[aTag].iState |= KOfferPending;
+		iNotifiees[aTag].iNotifiee->MemnEirBlockAvailable(aTag, iNotifiees[aTag].iOfferedBytes);
+		}
+	else if(iNotifiees[aTag].iOfferedBytes == 0)
+		{
+		if(iNotifiees[aTag].iRequiredBytes > 0)
+			{
+			// Notify client about 0 offer and not expecting client to call back
+			iNotifiees[aTag].iState |= KOfferPending;
+			iNotifiees[aTag].iNotifiee->MemnEirBlockAvailable(aTag, iNotifiees[aTag].iOfferedBytes);
+			}
+		else if(iNotifiees[aTag].iRequiredBytes == 0)
+			{
+			// request 0 and offered 0, delete client
+			iNotifiees[aTag].iState &= ~KNeedOffer;
+			delete iNotifiees[aTag].iTagData;
+			iNotifiees[aTag].iTagData = NULL;
+			}
+		}
+	}
+
+void CEirManager::ComputeOfferLengths()
+	{
+	LOG_FUNC
+	// count bytes available for EIR data, including length and tag
+	// due to the low cost of Tx Power Level, it is always given a space in EIR
+	TInt availableEirSize = EIRPacketSize();
+	TInt reservedByteForName = KReservedNameBytes - (KEirPacketSize - availableEirSize);
+	TInt totalSpare = 0;
+
+	ComputeClientInitialOffer(EEirTagName, totalSpare, reservedByteForName);
+	ComputeClientInitialOffer(EEirTagSdpUuid16, totalSpare, KReservedUuid16Bytes);
+	ComputeClientInitialOffer(EEirTagSdpUuid128, totalSpare, KReservedUuid128Bytes);
+	ComputeClientInitialOffer(EEirTagManufacturerSpecific, totalSpare, KReservedManufacturerSpecificDataBytes, ETrue);
+	ComputeClientInitialOffer(EEirTagTxPowerLevel, totalSpare, KReservedTxPowerLevelBytes, ETrue);
+
+	// if there is still bytes left after allocating the reserved bytes, 
+	// we will try to allocate them by giving high priority to tag with bigger granularity
+	/* tag			granularity (byte)
+	   name				1
+	  uuid16			2
+	  uuid128			16
+	  manufacturer		length
+	*/
+	LOG4(_L("RequiredBytes **** Name: %d, UUID16: %d, UUID128: %d, Manu: %d"), 
+			iNotifiees[0].iRequiredBytes, iNotifiees[1].iRequiredBytes, iNotifiees[3].iRequiredBytes, iNotifiees[4].iRequiredBytes);
+	LOG4(_L("OfferedBytes ----- Name: %d, UUID16: %d, UUID128: %d, Manu: %d"), 
+			iNotifiees[0].iOfferedBytes, iNotifiees[1].iOfferedBytes, iNotifiees[3].iOfferedBytes, iNotifiees[4].iOfferedBytes);
+	LOG1(_L("totalSpare: %d"), totalSpare);
+	ComputeClientFinalOffer(EEirTagManufacturerSpecific, totalSpare, KReservedManufacturerSpecificDataBytes, ETrue);
+	ComputeClientFinalOffer(EEirTagSdpUuid128, totalSpare, K128BitUuidGranularity);
+	ComputeClientFinalOffer(EEirTagSdpUuid16, totalSpare, K16BitUuidGranularity);
+	ComputeClientFinalOffer(EEirTagName, totalSpare, KDeviceNameGranularity);
+	// Tx Power Level should be after the allocating the reserved bytes
+	iOffersStale = EFalse; // We now know any "staleness" has been corrected.
+
+	LOG(_L("After allocating reserved"));
+	LOG4(_L("RequiredBytes **** Name: %d, UUID16: %d, UUID128: %d, Manu: %d"),
+			iNotifiees[0].iRequiredBytes, iNotifiees[1].iRequiredBytes, iNotifiees[3].iRequiredBytes, iNotifiees[4].iRequiredBytes);
+	LOG4(_L("OfferedBytes ----- Name: %d, UUID16: %d, UUID128: %d, Manu: %d"),
+			iNotifiees[0].iOfferedBytes, iNotifiees[1].iOfferedBytes, iNotifiees[3].iOfferedBytes, iNotifiees[4].iOfferedBytes);
+	LOG1(_L("totalSpare: %d"), totalSpare);
+	}
+
+/* compute offer based on client's request and the reserved bytes for this client*/
+void CEirManager::ComputeClientInitialOffer(TEirTag aTag, TInt& aTotalSpare, TInt aReservedBytes,
+											TBool aIsAllOrNothing)
+	{
+	if(TagPresent(aTag))
+		{
+		if(aIsAllOrNothing)
+			{
+			if(iNotifiees[aTag].iRequiredBytes <= aReservedBytes)
+				{
+				iNotifiees[aTag].iOfferedBytes = iNotifiees[aTag].iRequiredBytes;
+				aTotalSpare += (aReservedBytes - iNotifiees[aTag].iRequiredBytes);
+				iNotifiees[aTag].iEirDataMode = EEirDataComplete;
+				}
+			else
+				{
+				// the request exceeds reserved bytes for this client, the client is offered nothing
+				iNotifiees[aTag].iOfferedBytes = 0;
+				iNotifiees[aTag].iEirDataMode = EEirDataPartial;
+				aTotalSpare += aReservedBytes;
+				}
+			}
+		else
+			{
+			if(iNotifiees[aTag].iRequiredBytes <= aReservedBytes)
+				{
+				iNotifiees[aTag].iOfferedBytes = iNotifiees[aTag].iRequiredBytes;
+				aTotalSpare += (aReservedBytes - iNotifiees[aTag].iRequiredBytes);
+				iNotifiees[aTag].iEirDataMode = EEirDataComplete;
+				}
+			else
+				{
+				iNotifiees[aTag].iOfferedBytes = aReservedBytes;
+				iNotifiees[aTag].iEirDataMode = EEirDataPartial;
+				}
+			}
+		}
+	else
+		{
+		aTotalSpare += aReservedBytes;
+		}
+	}
+
+/* compute offer based on client's request and the spared bytes from the initial offers*/
+void CEirManager::ComputeClientFinalOffer(TEirTag aTag, TInt& aTotalSpare, TUint aGranularity,
+				  TBool aIsAllOrNothing)
+	{
+	if(iNotifiees[aTag].iRequiredBytes > iNotifiees[aTag].iOfferedBytes)
+		{
+		if(aIsAllOrNothing)
+			{
+			// this means we have more data to publish, which also indicates
+			// it required bytes are more than reserved bytes for this tag
+			if(iNotifiees[aTag].iRequiredBytes <= aTotalSpare)
+				{
+				aTotalSpare -= iNotifiees[aTag].iRequiredBytes;
+				iNotifiees[aTag].iOfferedBytes = iNotifiees[aTag].iRequiredBytes;
+				iNotifiees[aTag].iEirDataMode = EEirDataComplete;
+				}
+			}
+		else
+			{
+			// this means we have more data to publish
+			if((iNotifiees[aTag].iRequiredBytes - iNotifiees[aTag].iOfferedBytes) <= aTotalSpare)
+				{
+				aTotalSpare -= (iNotifiees[aTag].iRequiredBytes - iNotifiees[aTag].iOfferedBytes);
+				iNotifiees[aTag].iOfferedBytes = iNotifiees[aTag].iRequiredBytes;
+				iNotifiees[aTag].iEirDataMode = EEirDataComplete;
+				}
+			else
+				{
+				iNotifiees[aTag].iOfferedBytes += (aTotalSpare - aTotalSpare % aGranularity);
+				aTotalSpare %= aGranularity;
+				}
+			}
+		}
+	}
+
+// Calculate the valid size of EIR packet, excluding payload header	
+TInt CEirManager::EIRPacketSize() const
+	{
+	LOG_FUNC
+	TInt total = 0;
+	// default value of valid EIR packet is for using DM1
+	TInt ret = KEirPacketSize - KEirDM1PayloadHeaderSize;
+	for (TInt i = 0; i<EEirTagRESERVED; i++)
+		{
+		if(TagPresent((TEirTag) i) && iNotifiees[i].iRequiredBytes > 0)
+			{
+			total += iNotifiees[i].iRequiredBytes;
+			// Including 2 bytes for the length and tag
+			total += KEirLengthTagLength;
+			}
+		}
+		
+	if(total > KEirDM1PacketSize)
+		{
+		// None DM1 packet has 2 bytes of payload header, recalculate...
+		ret = KEirPacketSize - KEirNoneDM1PayloadHeaderSize;
+		}
+
+	return ret;
+	}
+
+void CEirManager::UpdateHostControllerL()
+	{
+	LOG_FUNC
+	
+	/* We don't handle the situation of handware swapping after stack is started
+	 * when the device changed from 2.1 to 2.0 and EIR is no longer supports, 
+	 * we'll quietly stop writing EIR and return KErrNotSupported for future requests.
+	we
+	 */
+	__ASSERT_DEBUG(iLinkMgrProtocol.IsExtendedInquiryResponseSupportedLocally(), EIR_MANAGER_PANIC(EEirManagerEirNotSupported));
+	TBuf8<KHCIExtendedInquiryResponseMaxLength> eir;
+
+	for (TInt i = 0; i<EEirTagRESERVED; i++)
+		{
+		TExtendedInquiryResponseDataType dataType = EEirLocalNamePartial;
+
+		// Skip non-registered and data-empty tags
+		if(iNotifiees[i].iNotifiee)
+			{
+			if(i == EEirTagName || iNotifiees[i].iTagData)
+				{
+				// We'll always try to publish empty device name according to the spec
+				switch (i)
+					{
+					// Name should go first
+					case EEirTagName:
+					dataType = iNotifiees[i].iEirDataMode == EEirDataPartial ? EEirLocalNamePartial : EEirLocalNameComplete;
+					break;
+					
+					case EEirTagSdpUuid16:
+					dataType = iNotifiees[i].iEirDataMode == EEirDataPartial ?  EEirUUID16Partial : EEirUUID16Complete;
+					break;
+									
+					case EEirTagSdpUuid128:
+					dataType = iNotifiees[i].iEirDataMode == EEirDataPartial ?  EEirUUID128Partial : EEirUUID128Complete;
+					break;
+					
+					case EEirTagManufacturerSpecific:
+					dataType = EEirVendorSpecific;
+					break;
+					
+					case EEirTagTxPowerLevel:
+					dataType = EEirTxPowerLevel;
+					break;
+					}
+				if(iNotifiees[i].iTagData)
+					{
+					// Data Structure length, add a byte for the Data Type
+					eir.Append(iNotifiees[i].iTagData->Length() + 1);
+					// Data Structure data: Data Type
+					eir.Append(dataType);
+					// Data Structure data: EIR data
+					eir.Append(*(iNotifiees[i].iTagData));
+					}
+				else
+					{
+					// Here is the case of empty device name again
+					eir.Append(1);
+					// Data Structure data: Data Type
+					eir.Append(dataType);
+					}
+				}
+			}
+		}
+
+	LOG1(_L("CEirManager::UpdateHostController about to send %d bytes of EIR data"), eir.Length());
+	// Send this down HCI
+	WriteExtendedInquiryResponseL(ETrue, eir);
+
+	LOG(_L("------- EIR verbose display"));
+	LOGHEXDESC(eir);
+	}
+
+void CEirManager::TryToUpdateHostController()
+	{
+	// In case there is an outstanding write eir command (a timer running) already, Cancel()
+	iWriteEirTimer->Cancel();
+	
+	// Start the timer to gurad against from clients' updates flooding the controller
+	iWriteEirTimer->Start();
+	}
+
+
+CWriteEirTimer* CWriteEirTimer::NewL(CEirManager& aEirManager)
+	{
+	CWriteEirTimer* timer = new (ELeave) CWriteEirTimer(aEirManager);
+	CleanupStack::PushL(timer);
+	timer->ConstructL();
+	CleanupStack::Pop(timer);
+	return timer;
+	}
+
+
+CWriteEirTimer::CWriteEirTimer(CEirManager& aEirManager)
+: CTimer(EPriorityStandard), iEirManager(aEirManager)
+	{
+	}
+
+void CWriteEirTimer::Start()
+	{
+	After(KWriteEirTimeout);
+	}
+
+void CWriteEirTimer::ConstructL()
+	{
+	CTimer::ConstructL();
+	CActiveScheduler::Add(this);
+	}
+
+void CWriteEirTimer::RunL()
+	{
+	// timer expires, which means no more write eir request within life of the time (1 second)
+	// write eir here now
+	iEirManager.UpdateHostControllerL();
+	}
+
+TInt CWriteEirTimer::RunError(TInt IF_FLOGGING(aError))
+	{
+	LOG_FUNC
+	// The only reason the error occurs is that the WriteExtendedInquiryResponseL has failed
+	// This may be caused by invalid EIR data, we'll do nothing here.
+	LOG1(_L("CWriteEirTimer::RunError() %d: "), aError);
+	return KErrNone;
+	}