bluetooth/btstack/linkmgr/physicallinkmetrics.cpp
changeset 0 29b1cd4cb562
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/linkmgr/physicallinkmetrics.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,618 @@
+// Copyright (c) 1999-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:
+//
+
+#include <bluetooth/logger.h>
+
+#include "physicallinkmetrics.h"
+#include "physicallinks.h"
+#include "linkutil.h"
+#include "ProxySAP.h"
+#include "btsockettimer.h"
+
+#include <bluetooth/hci/hciutil.h>
+#include <bluetooth/hci/readfailedcontactcountercommand.h>
+#include <bluetooth/hci/readlinkqualitycommand.h>
+#include <bluetooth/hci/readrssicommand.h>
+#include <bluetooth/hci/readtransmitpowerlevelcommand.h>
+#include <bluetooth/hci/readrssicompleteevent.h>
+#include <bluetooth/hci/readlinkqualitycompleteevent.h>
+#include <bluetooth/hci/readfailedcontactcountercompleteevent.h>
+#include <bluetooth/hci/readtransmitpowerlevelcompleteevent.h>
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR);
+#endif
+
+
+
+
+TPLMManager::TPLMManager()
+	:iPLMQue(_FOFF(CBTProxySAP, iPLMLink))
+	{
+	LOG_FUNC
+	}
+void TPLMManager::UpdatePLMState(THciCommandState aPLMState)
+	{
+	LOG_FUNC
+	FTRACE(FPrint(_L("TPLMManager::UpdatePLMState, Previous state :: %d\n"), iPLMState));
+	iPLMState = aPLMState;
+	FTRACE(FPrint(_L("TPLMManager::UpdatePLMState, Changed state :: %d\n"), iPLMState));
+	}
+
+void TPLMManager::RegisterProxySAP(CBTProxySAP& aSAP)
+	{
+	LOG_FUNC
+	iPLMQue.AddLast(aSAP);
+	}
+
+void TPLMManager::UpdatePLMValue(TInt aPLMValue)
+	{
+	LOG_FUNC
+	FTRACE(FPrint(_L("TPLMManager::UpdatePLMValue, Previous PLM value :: %d\n"), (PLMValue())() ));
+	iPLMValue() = aPLMValue;
+	FTRACE(FPrint(_L("TPLMManager::UpdatePLMValue, Changed PLM value :: %d\n"), (PLMValue())() ));
+	}
+
+TPckgBuf<TInt>& TPLMManager::PLMValue()
+	{
+	LOG_FUNC
+	return iPLMValue;
+	}
+
+THciCommandState TPLMManager::PLMState() const
+	{
+	LOG_FUNC
+	return iPLMState;
+	}
+
+void TPLMManager::NotifyQueuedProxySAPs(TInt aErr, TUint aName, TDesC8* aBuf)
+	{
+	LOG_FUNC
+	TDblQueIter<CBTProxySAP> iter(iPLMQue);
+	CBTProxySAP* thisSAP;
+	
+	// Iterate through all ProxySAPs that have requested this PLM, notifying them
+	while ((thisSAP = iter++) != NULL)
+		{
+		thisSAP->IoctlComplete(aErr, KSolBtLMProxy, aName, aBuf);
+		thisSAP->iPLMLink.Deque();
+		}
+	}
+
+/*Check if we have any outstanding requests registered by the clients*/
+TBool TPLMManager::AnyOutstandingClientRequest() const
+	{
+	LOG_FUNC
+	if(iPLMQue.IsEmpty())
+		{
+		return EFalse;
+		}
+	else
+		{
+		return ETrue;
+		}
+	}
+
+// ------------------------------------------------------------------------
+// class CPhysicalLinkMetrics
+// ------------------------------------------------------------------------
+
+
+CPhysicalLinkMetrics* CPhysicalLinkMetrics::NewL(CPhysicalLink& aLink, MHCICommandQueue& aCmdController)
+	{
+	LOG_STATIC_FUNC
+	
+	CPhysicalLinkMetrics* self = new(ELeave) CPhysicalLinkMetrics(aLink, aCmdController);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CPhysicalLinkMetrics::CPhysicalLinkMetrics(CPhysicalLink& aLink, MHCICommandQueue& aCmdController)
+	: iParent(aLink),
+	  iCmdController(aCmdController),
+	  iQueLink(this)
+	{
+	TCallBack plmCb(PLMEventReceived, this);
+	iPLMTimer.Set(plmCb);
+	
+	iParent.SubscribeLinkObserver(*this);
+	}
+
+void CPhysicalLinkMetrics::ConstructL()
+	{
+	LOG_FUNC
+	}
+
+CPhysicalLinkMetrics::~CPhysicalLinkMetrics()
+	{
+	RemovePLMCommands();
+	}
+
+void CPhysicalLinkMetrics::PhysicalLinkChange(const TBTBasebandEventNotification& aEvent, CPhysicalLink& /* aPhysicalLink */)
+	{
+	LOG_FUNC;
+	if (aEvent.EventType() == ENotifyPhysicalLinkDown || aEvent.EventType() == ENotifyPhysicalLinkError)
+		{
+		RemovePLMPoll();
+		iRssi.NotifyQueuedProxySAPs(KErrDisconnected, KLMReadRssiIoctl, &iDummyResult);
+		iLinkQuality.NotifyQueuedProxySAPs(KErrDisconnected, KLMReadLinkQualityIoctl, &iDummyResult);
+		iFailedContactCounter.NotifyQueuedProxySAPs(KErrDisconnected, KLMReadFailedContactCounterIoctl, &iDummyResult);
+		iTransmitPowerLevel.NotifyQueuedProxySAPs(KErrDisconnected, KLMReadCurrentTransmitPowerLevelIoctl, &iDummyResult);
+		}
+	}
+
+TPhysicalLinkObserverQLink& CPhysicalLinkMetrics::ObserverQLink()
+	{
+	return iQueLink;
+	}
+
+void CPhysicalLinkMetrics::ReadNewPhysicalLinkMetricValue(TUint aIoctlName, CBTProxySAP& aSAP, TInt aCurrentValue)
+	{
+	LOG_FUNC;
+	switch (aIoctlName)
+		{
+		case KLMReadRssiIoctl:
+			{
+			// If the command has not been issued, we issue now
+			if (iRssi.PLMState() == ECommandIdle)
+				{
+				iRssi.UpdatePLMValue(aCurrentValue);
+				iRssi.RegisterProxySAP(aSAP);
+				//Issue the command and change the state to ECommandIssued
+				ReadRssiCommand();
+				}
+			// Otherwise, if the command is waiting for the timer to expire, and the current value the user has is
+			// different to the currently cached value, we complete straight away
+			else if (iRssi.PLMState() == ECommandBlockedForTimer && aCurrentValue != iRssi.PLMValue()())
+				{
+				aSAP.IoctlComplete(KErrNone, KSolBtLMProxy, KLMReadRssiIoctl, &iRssi.PLMValue());
+				}
+			// Otherwise, if we are waiting for the command to complete, or we are waiting for the timer to expire,
+			// and the value the user has is the same as the cached valuewe just queue the SAP.
+			else
+				{
+				iRssi.RegisterProxySAP(aSAP);
+				}
+			}
+			break;
+		case KLMReadLinkQualityIoctl:
+			{
+			// If the command has not been issued, we issue now
+			if (iLinkQuality.PLMState() == ECommandIdle)
+				{
+				iLinkQuality.UpdatePLMValue(aCurrentValue);
+				iLinkQuality.RegisterProxySAP(aSAP);
+				//Issue the command and change the state to ECommandIssued
+				ReadLinkQualityCommand();
+				}
+			// Otherwise, if the command is waiting for the timer to expire, and the current value the user has is
+			// different to the currently cached value, we complete straight away
+			else if (iLinkQuality.PLMState() == ECommandBlockedForTimer && aCurrentValue != iLinkQuality.PLMValue()())
+				{
+				aSAP.IoctlComplete(KErrNone, KSolBtLMProxy, KLMReadLinkQualityIoctl, &iLinkQuality.PLMValue());
+				}
+			// Otherwise, if we are waiting for the command to complete, or we are waiting for the timer to expire,
+			// and the value the user has is the same as the cached valuewe just queue the SAP.
+			else
+				{
+				iLinkQuality.RegisterProxySAP(aSAP);
+				}
+			}
+			break;
+		case KLMReadFailedContactCounterIoctl:
+			{
+			// If the command has not been issued, we issue now
+			if (iFailedContactCounter.PLMState() == ECommandIdle)
+				{
+				iFailedContactCounter.UpdatePLMValue(aCurrentValue);
+				iFailedContactCounter.RegisterProxySAP(aSAP);
+				//Issue the command and change the state to ECommandIssued
+				ReadFailedContactCounterCommand();
+				}
+			// Otherwise, if the command is waiting for the timer to expire, and the current value the user has is
+			// different to the currently cached value, we complete straight away
+			else if (iFailedContactCounter.PLMState() == ECommandBlockedForTimer && aCurrentValue != iFailedContactCounter.PLMValue()())
+				{
+				aSAP.IoctlComplete(KErrNone, KSolBtLMProxy, KLMReadFailedContactCounterIoctl, &iFailedContactCounter.PLMValue());
+				}
+			// Otherwise, if we are waiting for the command to complete, or we are waiting for the timer to expire,
+			// and the value the user has is the same as the cached valuewe just queue the SAP.
+			else
+				{
+				iFailedContactCounter.RegisterProxySAP(aSAP);
+				}
+			}
+			break;
+		case KLMReadCurrentTransmitPowerLevelIoctl:
+			{
+			// If the command has not been issued, we issue now
+			if (iTransmitPowerLevel.PLMState() == ECommandIdle)
+				{
+				iTransmitPowerLevel.UpdatePLMValue(aCurrentValue);
+				iTransmitPowerLevel.RegisterProxySAP(aSAP);
+				//Issue the command and change the state to ECommandIssued
+				ReadTransmitPowerLevelCommand();
+				}
+			// Otherwise, if the command is waiting for the timer to expire, and the current value the user has is
+			// different to the currently cached value, we complete straight away
+			else if (iTransmitPowerLevel.PLMState() == ECommandBlockedForTimer && aCurrentValue != iTransmitPowerLevel.PLMValue()())
+				{
+				aSAP.IoctlComplete(KErrNone, KSolBtLMProxy, KLMReadCurrentTransmitPowerLevelIoctl, &iTransmitPowerLevel.PLMValue());
+				}
+			// Otherwise, if we are waiting for the command to complete, or we are waiting for the timer to expire,
+			// and the value the user has is the same as the cached valuewe just queue the SAP.
+			else
+				{
+				iTransmitPowerLevel.RegisterProxySAP(aSAP);
+				}
+			}
+			break;
+		default:
+			__ASSERT_DEBUG(EFalse, Panic(EBTProxySAPInvalidIoctl));
+		}
+	}
+
+void CPhysicalLinkMetrics::ReadRssiCommand()
+	{
+	LOG_FUNC
+	TRAPD(err, ReadRssiCommandL());
+	if (err != KErrNone)
+		{
+		iRssi.NotifyQueuedProxySAPs(err, KLMReadRssiIoctl, &iDummyResult);
+		}
+	}
+
+void CPhysicalLinkMetrics::ReadRssiCommandL()
+	{
+	LOG_FUNC
+	// Ownership of cmd transfered even if MhcqAddCommandL leaves
+	CReadRSSICommand* cmd = CReadRSSICommand::NewL(iParent.Handle());
+	iCmdController.MhcqAddCommandL(cmd, *this);
+	iRssi.UpdatePLMState(ECommandIssued);
+	}
+
+void CPhysicalLinkMetrics::ReadLinkQualityCommand()
+	{
+	LOG_FUNC
+	TRAPD(err, ReadLinkQualityCommandL());
+	if (err != KErrNone)
+		{
+		iLinkQuality.NotifyQueuedProxySAPs(err, KLMReadLinkQualityIoctl, &iDummyResult);
+		}
+	}
+
+void CPhysicalLinkMetrics::ReadLinkQualityCommandL()
+	{
+	LOG_FUNC
+	// Ownership of cmd transfered even if MhcqAddCommandL leaves
+	CReadLinkQualityCommand* cmd = CReadLinkQualityCommand::NewL(iParent.Handle());
+	iCmdController.MhcqAddCommandL(cmd, *this);
+	iLinkQuality.UpdatePLMState(ECommandIssued);
+	}
+
+void CPhysicalLinkMetrics::ReadFailedContactCounterCommand()
+	{
+	LOG_FUNC
+	TRAPD(err, ReadFailedContactCounterCommandL());
+	if (err != KErrNone)
+		{
+		iFailedContactCounter.NotifyQueuedProxySAPs(err, KLMReadFailedContactCounterIoctl, &iDummyResult);
+		}
+	}
+
+void CPhysicalLinkMetrics::ReadFailedContactCounterCommandL()
+	{
+	LOG_FUNC
+	// Ownership of cmd transfered even if MhcqAddCommandL leaves
+	CReadFailedContactCounterCommand* cmd = CReadFailedContactCounterCommand::NewL(iParent.Handle());
+	iCmdController.MhcqAddCommandL(cmd, *this);
+	iFailedContactCounter.UpdatePLMState(ECommandIssued);
+	}
+
+void CPhysicalLinkMetrics::ReadTransmitPowerLevelCommand()
+	{
+	LOG_FUNC
+	TRAPD(err, ReadTransmitPowerLevelCommandL());
+	if (err != KErrNone)
+		{
+		iTransmitPowerLevel.NotifyQueuedProxySAPs(err, KLMReadCurrentTransmitPowerLevelIoctl, &iDummyResult);
+		}
+	}
+
+void CPhysicalLinkMetrics::ReadTransmitPowerLevelCommandL()
+	{
+	LOG_FUNC
+	// Ownership of cmd transfered even if MhcqAddCommandL leaves
+	CReadTransmitPowerLevelCommand* cmd = CReadTransmitPowerLevelCommand::NewL(iParent.Handle(), ECurrentTransmitPowerLevel);
+	iCmdController.MhcqAddCommandL(cmd, *this);
+	iTransmitPowerLevel.UpdatePLMState(ECommandIssued);
+	}
+
+void CPhysicalLinkMetrics::MhcqcCommandErrored(TInt aErrorCode, const CHCICommandBase* aCommand)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(aCommand, Panic(EHCIUnknownCommandEvent));   // this should never happen
+	LOG2(_L("error code:%d opcode:0x%x"), aErrorCode, aCommand->Opcode());
+	switch (aCommand->Opcode())
+		{
+		case KReadFailedContactCounterOpcode:
+			iFailedContactCounter.UpdatePLMState(ECommandIdle);			
+			iFailedContactCounter.NotifyQueuedProxySAPs(aErrorCode, KLMReadFailedContactCounterIoctl, &iDummyResult);
+			break;
+		
+		case KReadLinkQualityOpcode:
+			iLinkQuality.UpdatePLMState(ECommandIdle);
+			iLinkQuality.NotifyQueuedProxySAPs(aErrorCode, KLMReadLinkQualityIoctl, &iDummyResult);
+			break;
+		
+		case KReadRSSIOpcode:
+			iRssi.UpdatePLMState(ECommandIdle);
+			iRssi.NotifyQueuedProxySAPs(aErrorCode, KLMReadRssiIoctl, &iDummyResult);			
+			break;
+		
+		case KReadTransmitPowerLevelOpcode:
+			iTransmitPowerLevel.UpdatePLMState(ECommandIdle);
+			iTransmitPowerLevel.NotifyQueuedProxySAPs(aErrorCode, KLMReadCurrentTransmitPowerLevelIoctl, &iDummyResult);						
+			break;
+			
+		default:
+			//LOG1(_L("physicallinkmetrics.cpp: Invalid command opcode, expecting only PLM commands but got %d command"),aCommand->Opcode());
+			__ASSERT_DEBUG(EFalse, Panic(EHCICommandBadArgument));
+		}
+	}
+
+
+// From MHCICommandQueueClient
+void CPhysicalLinkMetrics::MhcqcCommandEventReceived(const THCIEventBase& aEvent,
+													 const CHCICommandBase* /*aRelatedCommand*/)
+	{
+	LOG_FUNC
+	switch(aEvent.EventCode())
+		{
+		case ECommandCompleteEvent:
+			{
+			const THCICommandCompleteEvent& completeEvent = THCICommandCompleteEvent::Cast(aEvent);
+			CommandCompleteEvent(completeEvent);
+			break;
+			}
+		
+		default:
+			{
+			LOG1(_L("Warning!! Unexpected Event Received (event code:%d)"), aEvent.EventCode());
+			__ASSERT_DEBUG(EFalse, Panic(EHCIUnexpectedEvent));
+			break;
+			}
+		}	
+	}
+
+void CPhysicalLinkMetrics::CommandCompleteEvent(const THCICommandCompleteEvent& aEvent)
+	{
+	LOG_FUNC
+	THCIOpcode opcode = aEvent.CommandOpcode();
+
+	switch (opcode)
+		{
+		case KReadFailedContactCounterOpcode:
+			{
+			const TReadFailedContactCounterCompleteEvent& event = TReadFailedContactCounterCompleteEvent::Cast(aEvent);
+			HandleReadFailedContactCounterCompleteEvent(event);
+			}
+			break;
+		
+		case KReadLinkQualityOpcode:
+			{
+			const TReadLinkQualityCompleteEvent& event = TReadLinkQualityCompleteEvent::Cast(aEvent);
+			HandleReadLinkQualityCompleteEvent(event);
+			}
+			break;
+		
+		case KReadRSSIOpcode:
+			{
+			const TReadRSSICompleteEvent& event = TReadRSSICompleteEvent::Cast(aEvent);
+			HandleReadRssiCompleteEvent(event);
+			}
+			break;
+		
+		case KReadTransmitPowerLevelOpcode:
+			{
+			const TReadTransmitPowerLevelCompleteEvent& event = TReadTransmitPowerLevelCompleteEvent::Cast(aEvent);
+			HandleReadTransmitPowerLevelCompleteEvent(event);
+			}
+			break;
+		
+		default:
+			LOG2(_L("Warning: Unexpected Command complete event! Opcode %d error code %d"), opcode, aEvent.ErrorCode());
+			__ASSERT_DEBUG(EFalse, Panic(EHCIUnknownCommandCompleteOpcode));
+			break;
+		}
+	}
+
+void CPhysicalLinkMetrics::HandleReadFailedContactCounterCompleteEvent(const TReadFailedContactCounterCompleteEvent& aEvent)
+	{
+	LOG_FUNC
+	TUint8 result = 0;
+	TInt err = CHciUtil::SymbianErrorCode(aEvent.ErrorCode());
+	if (err == KErrNone)
+		{
+		result = aEvent.FailedContactCounter();
+		}
+	
+	if (err != KErrNone || static_cast<TInt>(result) != iFailedContactCounter.PLMValue()())
+		{
+		iFailedContactCounter.UpdatePLMValue(result);
+		// Notify all SAPs via IoctlComplete, with the result or an error code
+		iFailedContactCounter.NotifyQueuedProxySAPs(err, KLMReadFailedContactCounterIoctl, &iFailedContactCounter.PLMValue());
+		}
+	iFailedContactCounter.UpdatePLMState(ECommandBlockedForTimer);		
+	QueueNextPLMPollIfNotAlreadyQueued();	
+	}
+
+void CPhysicalLinkMetrics::HandleReadLinkQualityCompleteEvent(const TReadLinkQualityCompleteEvent& aEvent)
+	{
+	LOG_FUNC
+	TUint8 result = 0;
+	TInt err = CHciUtil::SymbianErrorCode(aEvent.ErrorCode());
+	if (err == KErrNone)
+		{
+		result = aEvent.LinkQuality();
+		}
+	
+	// Notify all SAPs via IoctlComplete, with the result or an error code
+
+	if (err != KErrNone || static_cast<TInt>(result) != iLinkQuality.PLMValue()())
+		{
+		iLinkQuality.UpdatePLMValue(result);
+		// Notify all SAPs via IoctlComplete, with the result or an error code
+		iLinkQuality.NotifyQueuedProxySAPs(err, KLMReadLinkQualityIoctl, &iLinkQuality.PLMValue());
+		}
+	iLinkQuality.UpdatePLMState(ECommandBlockedForTimer);
+	QueueNextPLMPollIfNotAlreadyQueued();
+	}
+
+void CPhysicalLinkMetrics::HandleReadRssiCompleteEvent(const TReadRSSICompleteEvent& aEvent)
+	{
+	LOG_FUNC
+	TUint8 result = 0;
+	TInt err = CHciUtil::SymbianErrorCode(aEvent.ErrorCode());
+	if (err == KErrNone)
+		{
+		result = aEvent.RSSI().RSSI();
+		}
+	
+	if (err != KErrNone || static_cast<TInt>(result) != iRssi.PLMValue()())
+		{
+		iRssi.UpdatePLMValue(result);
+		// Notify all SAPs via IoctlComplete, with the result or an error code
+		iRssi.NotifyQueuedProxySAPs(err, KLMReadRssiIoctl, &iRssi.PLMValue());
+		}
+	iRssi.UpdatePLMState(ECommandBlockedForTimer);
+	QueueNextPLMPollIfNotAlreadyQueued();
+	}
+
+void CPhysicalLinkMetrics::HandleReadTransmitPowerLevelCompleteEvent(const TReadTransmitPowerLevelCompleteEvent& aEvent)
+	{
+	LOG_FUNC
+	TUint8 result = 0;
+	TInt err = CHciUtil::SymbianErrorCode(aEvent.ErrorCode());
+	if (err == KErrNone)
+		{
+		result = aEvent.TransmitPowerLevel();
+		}
+	
+	if (err != KErrNone || static_cast<TInt>(result) != iTransmitPowerLevel.PLMValue()())
+		{
+		iTransmitPowerLevel.UpdatePLMValue(result);
+		// Notify all SAPs via IoctlComplete, with the result or an error code
+		iTransmitPowerLevel.NotifyQueuedProxySAPs(err, KLMReadCurrentTransmitPowerLevelIoctl, &iTransmitPowerLevel.PLMValue());
+		}
+	iTransmitPowerLevel.UpdatePLMState(ECommandBlockedForTimer);
+	QueueNextPLMPollIfNotAlreadyQueued();
+	}
+
+/*static*/ TInt CPhysicalLinkMetrics::PLMEventReceived(TAny* aThis)
+	{
+	static_cast<CPhysicalLinkMetrics*>(aThis)->iPLMTimerQueued = EFalse;	
+	
+	/*Send the PLM commands if there are any outstanding requests*/	
+	static_cast<CPhysicalLinkMetrics*>(aThis)->DoRssiTimerEvent();
+	static_cast<CPhysicalLinkMetrics*>(aThis)->DoLinkQualityTimerEvent();
+	static_cast<CPhysicalLinkMetrics*>(aThis)->DoFailedContactCounterTimerEvent();
+	static_cast<CPhysicalLinkMetrics*>(aThis)->DoTransmitPowerLevelTimerEvent();
+	
+	return KErrNone;
+	}
+
+
+void CPhysicalLinkMetrics::DoRssiTimerEvent()
+	{
+	LOG_FUNC
+	iRssi.UpdatePLMState(ECommandIdle);
+	if (iRssi.AnyOutstandingClientRequest())
+		{
+		ReadRssiCommand();
+		}
+	}
+
+void CPhysicalLinkMetrics::DoLinkQualityTimerEvent()
+	{
+	LOG_FUNC
+	iLinkQuality.UpdatePLMState(ECommandIdle);
+	if (iLinkQuality.AnyOutstandingClientRequest())
+		{
+		ReadLinkQualityCommand();
+		}
+	}
+
+void CPhysicalLinkMetrics::DoFailedContactCounterTimerEvent()
+	{
+	LOG_FUNC
+	iFailedContactCounter.UpdatePLMState(ECommandIdle);
+	if (iFailedContactCounter.AnyOutstandingClientRequest())
+		{
+		ReadFailedContactCounterCommand();
+		}
+	}
+
+void CPhysicalLinkMetrics::DoTransmitPowerLevelTimerEvent()
+	{
+	LOG_FUNC
+	iTransmitPowerLevel.UpdatePLMState(ECommandIdle);
+	if (iTransmitPowerLevel.AnyOutstandingClientRequest())
+		{
+		ReadTransmitPowerLevelCommand();
+		}
+	}
+
+void CPhysicalLinkMetrics::RemovePLMCommands()
+	{
+	LOG_FUNC
+	iCmdController.MhcqRemoveAllCommands(*this);
+	}
+
+
+/* A common timer is used for all the 4 PLMs. Which ever PLM command completes first will start the timer  
+ * for the subsequent requests. When the timer completes we will send all the PLM commands (for which there are 
+ * outstanding requests) and start the timer again with the first completed PLM command.
+ */ 
+void CPhysicalLinkMetrics::QueueNextPLMPollIfNotAlreadyQueued()
+	{	
+	LOG_FUNC
+	TUint sniffInterval = iParent.GetSniffInterval();
+	if(!iPLMTimerQueued)
+		{
+		//If we are in sniff mode (i.e. sniff interval is greater than zero) and the sniff interval is greater 
+		// then default PLM poll interval then PLM commands should be sent after every sniff interval only  
+		if( sniffInterval > KPLMPollInterval)
+			{
+			BTSocketTimer::Queue(sniffInterval, iPLMTimer);
+			}
+		else
+			{
+			BTSocketTimer::Queue(KPLMPollInterval, iPLMTimer);
+			}
+		iPLMTimerQueued = ETrue;
+		}
+	}
+
+void CPhysicalLinkMetrics::RemovePLMPoll()
+	{
+	LOG_FUNC
+	//It is safe to call remove even if the timer is already removed.
+	BTSocketTimer::Remove(iPLMTimer);
+	iPLMTimerQueued = EFalse;
+	}
+