diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/linkmgr/physicallinkmetrics.cpp --- /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 + +#include "physicallinkmetrics.h" +#include "physicallinks.h" +#include "linkutil.h" +#include "ProxySAP.h" +#include "btsockettimer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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& 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 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(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(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(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(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(aThis)->iPLMTimerQueued = EFalse; + + /*Send the PLM commands if there are any outstanding requests*/ + static_cast(aThis)->DoRssiTimerEvent(); + static_cast(aThis)->DoLinkQualityTimerEvent(); + static_cast(aThis)->DoFailedContactCounterTimerEvent(); + static_cast(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; + } +