// 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);#endifTPLMManager::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 MHCICommandQueueClientvoid 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; }