bluetooth/btstack/l2cap/l2capSAPSignalHandler.cpp
changeset 0 29b1cd4cb562
child 23 32ba20339036
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/l2cap/l2capSAPSignalHandler.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,920 @@
+// Copyright (c) 2004-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:
+// Implements the handling of l2cap signal packets (both inbound and outbound) from SAP interface
+// 
+//
+
+#include <bt_sock.h>
+#include <bluetooth/logger.h>
+
+
+#include "l2capSAPSignalHandler.h"
+#include "l2capSigStates.h"
+#include "l2capSigPacketConnection.h"
+#include "l2capSigPacketConfigure.h"
+#include "l2capSigPacketDisconnection.h"
+#include "l2capSigPacketEcho.h"
+#include "l2capSigPacketCommandReject.h"
+#include "l2util.h"
+#include "l2sap.h"
+#include "l2cap.h"
+
+#include "l2capEntityConfig.h"
+#include "l2signalmgr.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_L2CAP);
+#endif
+
+/*static*/ CL2CapSAPSignalHandler* CL2CapSAPSignalHandler::NewL(CL2CAPConnectionSAP& aSAP)
+	{
+	LOG_STATIC_FUNC
+	CL2CapSAPSignalHandler* self = new(ELeave) CL2CapSAPSignalHandler(aSAP);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+// Disable warning WINS 4355: 'this' : used in base member initializer list
+// This will not cause any problems in this usage and is preferable to the use of a
+// non-owned pointer.
+#pragma warning (disable: 4355)
+CL2CapSAPSignalHandler::CL2CapSAPSignalHandler(CL2CAPConnectionSAP& aSAP)
+ : CL2CapSignalHandler(),
+   iSAP(&aSAP),
+   iLocalBTAddress(TBTDevAddr(0)),
+   iDefaultRTXTimerDuration(HL2CapCommand::KDefaultRTXTimerDuration),
+   iERTXTimerDuration(HL2CapCommand::KDefaultERTXTimerDuration),
+   iSigState(&aSAP.Protocol().SigStateFactory().GetState(CL2CAPSignalStateFactory::EClosed)),
+   iSAPSignalHandlerTimerState(ETimerIdle)
+	{
+	LOG_FUNC
+	}
+#pragma warning (default: 4355)
+
+void CL2CapSAPSignalHandler::ConstructL()
+	{
+	LOG_FUNC
+	iChannelConfig = CL2CapChannelConfig::NewL(*this);
+	LOG2(_L("CL2CapSAPSignalHandler.iChannelConfig = %X.%X"), (TAny*)this, (TAny*)iChannelConfig)
+	}
+
+CL2CapSAPSignalHandler::~CL2CapSAPSignalHandler()
+	{
+	LOG_FUNC
+	DetachFromMux();
+	delete iChannelConfig;
+	
+	__ASSERT_DEBUG(iMuxer == NULL, Panic(EL2CAPSigHandlerDeletedWithResources));
+	// If our ConstructL leaves we will be deleted with iSAP non-NULL.  This is ok as
+	// the SAP will also be deleted as part of the cleanup process.  Because it's useful
+	// to keep this assert for the normal case we test iChannelConfig (which may never be
+	// NULL after ConstructL) to detect the case where ConstructL has left and iSAP may
+	// validly be non-NULL
+	__ASSERT_DEBUG((iSAP == NULL) || (iChannelConfig == NULL), Panic(EL2CAPSigHandlerDeletedWithResources));
+	}
+
+TInt CL2CapSAPSignalHandler::ConstructConnectionResponse(TUint8 aId, TConnectResponseResult aResult, TConnectResponseStatus aStatus/*= EConnectPendNoFurtherInfo*/)
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+	
+	HL2CapCommand* command = HConnectionResponse::New(aId, iLocalPort, iRemotePort, aResult, aStatus);
+	if(command)
+		{
+		AddToOutgoingQueue(command);
+		}
+	else
+		{
+		rerr = KErrNoMemory;
+		}
+	return rerr;
+	}
+
+TInt CL2CapSAPSignalHandler::ConstructConnectionRequest()
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+
+	HL2CapCommand* command = HConnectionRequest::New(iLocalPort, iRemotePort, CurrentRTXTimerDuration(iDefaultRTXTimerDuration), iERTXTimerDuration);
+	if(command)
+		{
+		AddToOutgoingQueue(command);
+		}
+	else
+		{
+		rerr = KErrNoMemory;
+		}
+	return rerr;
+	}
+
+
+TInt CL2CapSAPSignalHandler::ConstructConfigRequest()
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+
+	HL2CapCommand* command = HConfigureRequest::New(RemotePort(), *iChannelConfig, CurrentRTXTimerDuration(iDefaultRTXTimerDuration), iERTXTimerDuration);
+	if(command)
+		{
+		AddToOutgoingQueue(command);
+		}
+	else
+		{
+		rerr = KErrNoMemory;
+		}
+	return rerr;
+	}
+
+/**
+	Used for constructing a configuration response for unknown parameters
+	@internalComponent
+**/
+TInt CL2CapSAPSignalHandler::ConstructConfigResponse(TUint8 aId, TConfigFlags aFlags, TConfigResponseResult aResult, RMBufChain& aUnknownParameters)
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+
+	HConfigureResponse* command = HConfigureResponse::New(aId, RemotePort(), aFlags, aResult);
+	if(command)
+		{
+		command->AddUnknownOptionsToCommand(aUnknownParameters);
+		AddToOutgoingQueue(command);
+		}
+	else
+		{
+		rerr = KErrNoMemory;
+		}
+	return rerr;
+	}
+
+
+TInt CL2CapSAPSignalHandler::ConstructConfigResponse(TUint8 aId, TConfigFlags aFlags, TConfigResponseResult aResult)
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+
+	HConfigureResponse* command = HConfigureResponse::New(aId, RemotePort(), aFlags, aResult);
+	if(command)
+		{
+		// Only add options if this is not a response to a Config Request
+		// with the C-flag set.
+		if(aFlags & KConfigOptionContinuationMask)
+			{
+			command->WriteDataLength();
+			}
+		else if (aResult == EConfigSuccess || aResult == EConfigUnacceptableParams)
+			{
+			// Add the options
+			rerr = command->AddOptionsToCommand(*iChannelConfig, aResult);
+			if(rerr != KErrNone)
+				{
+				delete command;
+				command = NULL;
+				}
+			}
+		
+		if(command)
+			{
+			AddToOutgoingQueue(command);
+			}
+		}
+	else
+		{
+		rerr = KErrNoMemory;
+		}
+	return rerr;
+	}
+	
+TInt CL2CapSAPSignalHandler::ConstructDisconnectRequest()
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+	
+	HL2CapCommand* command = HDisconnectRequest::New(iLocalPort, iRemotePort, CurrentRTXTimerDuration(iDefaultRTXTimerDuration), iERTXTimerDuration);
+	if(command)
+		{
+		AddToOutgoingQueue(command);
+		}
+	else
+		{
+		rerr = KErrNoMemory;
+		}
+	return rerr;
+	}
+	
+TInt CL2CapSAPSignalHandler::ConstructDisconnectResponse(TUint8 aId)
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+
+	HL2CapCommand* command = HDisconnectResponse::New(aId, iLocalPort, iRemotePort);
+	if(command)
+		{
+		AddToOutgoingQueue(command);
+		}
+	else
+		{
+		rerr = KErrNoMemory;
+		}
+	return rerr;
+	}
+
+
+TBool CL2CapSAPSignalHandler::HandleConnectionResponse(HConnectionResponse* aConnectionResponse)
+	{
+	LOG_FUNC
+	TBool rcode = EFalse;
+	
+	// Check the response is for this CID.
+	if(aConnectionResponse->SourceCID() == iLocalPort)
+		{
+		// Check that the Connection Response has been requested.
+		HL2CapCommand* requestCommand = FindMatchingOutstandingRequest(EConnectionRequest, aConnectionResponse->ID());
+		if(requestCommand)
+			{
+			// If this has a pending result.  Re-start the the Extended RTX timer
+			// on the Request.
+			if(aConnectionResponse->Result() == EConnectPending)
+				{
+				requestCommand->StartRTXTimer(HL2CapCommand::EERTXTimer, *this);
+				}
+			else
+				{
+				// Delete the request.
+				delete requestCommand;
+				}
+
+			iSigState->ConnectResponse(*this, aConnectionResponse);		// the state m/c will update the Destination CID
+			}
+		else
+			{
+			LOG(_L("CL2CapSAPSignalHandler::HandleConnectionResponse, no matching req"));
+			}
+
+		// NOTE: If there was no Connection Request outstanding
+		// (i.e., requestCommand == NULL) but the command IS for this
+		// CID this could be a duplicate response, one that has arrived in
+		// the window between RTX expiry and retransmission, or just bogus.  
+		// Silently discard the packet.			
+		rcode = ETrue;
+		}
+
+	return rcode;
+	}
+
+
+TBool CL2CapSAPSignalHandler::HandleConfigureRequest(HConfigureRequest* aConfigRequest)
+	{
+	LOG_FUNC
+	TBool rcode = EFalse;
+	
+	// Check the response is for this CID.
+	if(aConfigRequest->DestinationCID() == iLocalPort)
+		{
+		iSigState->ConfigRequest(*this, aConfigRequest);
+		rcode = ETrue;
+		}
+
+	return rcode;
+	}	
+	
+
+TBool CL2CapSAPSignalHandler::HandleConfigureResponse(HConfigureResponse* aConfigResponse)	
+	{
+	LOG_FUNC
+	TBool rcode = EFalse;
+	
+	// Check the response is for this CID.
+	if(aConfigResponse->SourceCID() == iLocalPort)
+		{
+		// Check that the Config Response has been requested.
+		HL2CapCommand* requestCommand = FindMatchingOutstandingRequest(EConfigureRequest, aConfigResponse->ID());
+		if(requestCommand)
+			{
+			// Delete the corresponding request.
+			delete requestCommand;
+
+			iSigState->ConfigResponse(*this, aConfigResponse);
+			}
+		// NOTE: If there was no Configure Request outstanding
+		// (i.e., requestCommand == NULL) this could be a duplicate 
+		// response, or just bogus.  Silently discard the packet.			
+		rcode = ETrue;			
+		}
+		
+	return rcode;
+	}
+
+TBool CL2CapSAPSignalHandler::HandleDisconnectRequest(HDisconnectRequest* aDisconnectRequest)
+	{
+	LOG_FUNC
+	TBool rcode = EFalse;
+	
+	// Check the response is for this CID.
+	if(aDisconnectRequest->DestinationCID() == iLocalPort &&
+	   aDisconnectRequest->SourceCID() == iRemotePort)
+		{	
+		iSigState->DisconnectRequest(*this, aDisconnectRequest->ID());
+		rcode = ETrue;
+		}
+		
+	return rcode;
+	}
+	
+TBool CL2CapSAPSignalHandler::HandleDisconnectResponse(HDisconnectResponse* aDisconnectResponse)
+	{
+	LOG_FUNC
+	TBool rcode = EFalse;
+	
+	// Check the response is for this CID.
+	if(aDisconnectResponse->DestinationCID() == iRemotePort &&
+	   aDisconnectResponse->SourceCID() == iLocalPort)
+		{	
+		// Check that the Disconnect Response has been requested.
+		HL2CapCommand* requestCommand = FindMatchingOutstandingRequest(EDisconnectionRequest, aDisconnectResponse->ID());
+		if(requestCommand)
+			{
+			// Delete the corresponding request.
+			delete requestCommand;
+
+			iSigState->DisconnectResponse(*this);			
+			}
+		// NOTE: If there was no Disconnect Request outstanding
+		// (i.e., requestCommand == NULL) this could be a duplicate 
+		// response, or just bogus.  Silently discard the packet.			
+		rcode = ETrue;			
+		}
+		
+	return rcode;
+	}
+	
+
+TBool CL2CapSAPSignalHandler::HandleCommandReject(HCommandReject* aCommandReject)
+	{
+	LOG_FUNC
+	TBool rcode = EFalse;
+	
+	// Process the incoming command reject data
+	TL2CAPCommandRejectData rejectData;
+	
+	if (aCommandReject->RejectData(rejectData) != KErrNone) return rcode;
+
+	switch(rejectData.iReason)
+		{
+		case EInvalidCID:
+			{
+			TBool forThisChannel = EFalse;
+			
+			// To be considered as 'for this channel', either both pairs should match
+			// OR only one of the endpoints should be present and match.
+			if(rejectData.iLocalEndpoint != 0 &&
+			   rejectData.iLocalEndpoint == iRemotePort)
+				{
+				forThisChannel = ETrue;
+				}
+				
+			if(rejectData.iRemoteEndpoint != 0)
+				{
+				if(rejectData.iRemoteEndpoint == iLocalPort)
+			   		{
+			   		forThisChannel = ETrue;
+			   		}
+				else
+					{
+			   		forThisChannel = EFalse;
+			   		}
+				}
+
+			if(forThisChannel)
+				{
+				LOG1(_L("CL2CapSAPSignalHandler::HandleCommandReject(): forThisChannel = ETrue for commandID = 0x%04x"),aCommandReject->ID())
+				LOG2(_L("rejectData.iLocalEndpoint = 0x%04x, iRemotePort = 0x%04x"),rejectData.iLocalEndpoint,iRemotePort)
+				LOG2(_L("rejectData.iRemoteEndpoint = 0x%04x, iLocalPort = 0x%04x"),rejectData.iRemoteEndpoint,iLocalPort)
+				
+				// If there is no matching command then this rejection is not for this handler
+				if (FindMatchingOutstandingRequest(EMatchAnyL2CAPRequest,aCommandReject->ID()))
+					{
+					// The channel has been disconnected or is not
+					// recognised by the peer.  Error the channel.
+					iSigState->Error(*this, KErrL2PeerRejectedCommand, MSocketNotify::EErrorAllOperations);
+					rcode = ETrue;
+					}
+				else
+					{
+					LOG(_L("CL2CapSAPSignalHandler::HandleCommandReject(): No matching command is found, skipping it"))
+					}
+				}
+			break;
+			}
+		default:
+			break;
+		};
+					
+	return rcode;
+	}
+	
+void CL2CapSAPSignalHandler::CloneChannelConfig(CL2CapChannelConfig& aL2CAPChannelConfig)
+	{
+	LOG_FUNC
+	iChannelConfig->CloneChannelConfig(aL2CAPChannelConfig);
+	}
+
+
+TInt CL2CapSAPSignalHandler::UpdateChannelConfig(const TL2CapConfig& aAPIConfig)
+	{
+	LOG_FUNC
+	return iSigState->UpdateChannelConfig(*this, aAPIConfig);
+	}
+
+void CL2CapSAPSignalHandler::SetState(TL2CAPSigState& aState)
+	{
+	LOG_FUNC
+	LOG2(_L("CL2CapSAPSignalHandler::SetState 0x%08x -> 0x%.8x"), iSigState, &aState);
+	
+	iSigState->Exit(*this);
+	iSigState = &aState;
+	iSigState->Enter(*this);
+	}
+
+void CL2CapSAPSignalHandler::SetLocalPort(TL2CAPPort aPort)
+	{
+	LOG_FUNC
+	LOG1(_L("Setting local port to %d"), aPort);
+	iLocalPort = aPort;
+	}
+
+void CL2CapSAPSignalHandler::SetRemotePort(TL2CAPPort aPort)
+	{
+	LOG_FUNC
+	LOG1(_L("Setting remote port to %d"), aPort);
+	iRemotePort = aPort;
+	}
+
+void CL2CapSAPSignalHandler::SetRemName(TL2CAPSockAddr& aAddr)
+	{
+	LOG_FUNC
+	SetRemotePort(aAddr.Port());
+	}
+
+void CL2CapSAPSignalHandler::GetRemName(TL2CAPSockAddr& aAddr) const
+	{
+	LOG_FUNC
+	aAddr.SetBTAddr(RemoteBTAddress());
+	aAddr.SetPort(iRemotePort);
+	}
+
+void CL2CapSAPSignalHandler::SetLocalParameters(TL2CAPSockAddr& aAddr)
+	{
+	LOG_FUNC
+	iLocalBTAddress = aAddr.BTAddr();
+	LOGBTDEVADDR(iLocalBTAddress);	
+	SetLocalPort(aAddr.Port());
+	}
+
+void CL2CapSAPSignalHandler::GetLocalParameters(TL2CAPSockAddr& aAddr) const
+	{
+	LOG_FUNC
+	aAddr.SetBTAddr(iLocalBTAddress);
+	aAddr.SetPort(iLocalPort);
+	}
+	
+TInt CL2CapSAPSignalHandler::SetOption(TUint aLevel, TUint aName, const TDesC8& aOption)
+	{
+	LOG_FUNC
+	// iMuxer may be NULL
+	if (iMuxer != NULL)
+		{
+		return iMuxer->SetOption(aLevel, aName, aOption);
+		}
+	else
+		{
+		return KErrNotReady;
+		}
+	}
+	
+	
+TInt CL2CapSAPSignalHandler::GetOption(TUint aLevel, TUint aName, TDes8& aOption) const
+	{
+	LOG_FUNC
+	// iMuxer may be NULL
+	if (iMuxer != NULL)
+		{
+		return iMuxer->GetOption(aLevel, aName, aOption);
+		}
+	else
+		{
+		return KErrNotReady;
+		}
+	}
+
+void CL2CapSAPSignalHandler::SAPSignalHandlerRegistered(CL2CAPMux& aMuxer, TL2CAPEntityConfig& aEntityConfig)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iMuxer == NULL, Panic(EL2CAPSAPSignalHandlerRegisteredTwice));
+	iMuxer = &aMuxer;
+	iSAP->SetRemoteDev(iMuxer->RemoteBTAddr());
+	iChannelConfig->RegisterL2CapEntityConfig(aEntityConfig);
+	}
+
+
+// Indicates that the baseband is up and going and that all the objects are tied together ok.
+void CL2CapSAPSignalHandler::LinkUp()
+	{
+	LOG_FUNC
+	iSAP->LinkUp();
+	}
+
+void CL2CapSAPSignalHandler::Error(TInt aErrorCode, MSocketNotify::TOperationBitmasks aErrorAction)
+	{
+	LOG_FUNC
+	iSigState->Error(*this, aErrorCode, aErrorAction);
+	}
+	
+//Called by the state machine when we go into channel reconfiguration
+void CL2CapSAPSignalHandler::ReconfiguringChannel()
+	{
+	LOG_FUNC
+	iSAP->ReconfiguringChannel();
+	}
+
+void CL2CapSAPSignalHandler::SignalHandlerDisconnectedCanClose()
+	{
+	LOG_FUNC
+	// If the SAP exists signal the disconnect.  Otherwise delete this
+	// signal handler.
+	if(iSAP)
+		{
+		iSAP->ChannelClosed();
+		}
+	else
+		{
+		delete this;
+		}
+	}
+
+void CL2CapSAPSignalHandler::SignalHandlerErrorD(TInt aErrorCode, MSocketNotify::TOperationBitmasks aErrorAction)
+	{
+	LOG_FUNC
+	// If the SAP exists signal the error.  Otherwise delete this
+	// signal handler.
+	if(iSAP)
+		{
+		iSAP->SignalHandlerError(aErrorCode, aErrorAction);
+		}
+	else
+		{
+		delete this;
+		}
+	}
+
+//Called by the link signal handler 	
+TInt CL2CapSAPSignalHandler::PassiveConnectionRequest(const TBTDevAddr& aAddr,HConnectionRequest* aConnectionRequest)
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+	CL2CAPConnectionSAP *clonedSAP = NULL;
+
+	TUint8 id = aConnectionRequest->ID();
+	TL2CAPPort scid = aConnectionRequest->SourceCID(); 
+
+	// Check if the SAP is ready to accept a connection.
+	// Note.  If the SAP is not in the listening state this
+	// will return with the not ready error code.
+	if(!iSAP->CanAcceptConnection())
+		{
+		rerr = KErrNotReady;
+		}
+	else
+		{
+		if((clonedSAP = iSAP->CloneListeningSAP(aAddr)) == NULL)
+			{
+			rerr = KErrNoMemory;
+			}
+		else
+			{
+			CL2CapSAPSignalHandler& clonedSH = clonedSAP->SignalHandler();
+
+			clonedSH.iRemotePort = scid;
+			clonedSH.iOutstandingRequestID = id;
+
+			clonedSH.CloneChannelConfig(*iChannelConfig);
+			rerr = iSAP->Protocol().MuxController().AttachPassiveSignalHandler(clonedSH, aAddr);
+			}
+		}
+	return rerr;
+	}
+
+void CL2CapSAPSignalHandler::ActiveConnectionRequest()
+	{
+	LOG_FUNC
+	iMuxer->CompleteACLConnect(NULL);
+	}
+
+void CL2CapSAPSignalHandler::L2CapEntityConfigUpdated()
+ 	{
+ 	LOG_FUNC
+ 	if (iOpenChannelRequestAwaitingPeerEntityConfig)
+ 		{
+ 		// SAP state machine requested that we open the channel before we could do it.
+ 		// We can do it now.
+ 		iOpenChannelRequestAwaitingPeerEntityConfig = EFalse;
+ 		iSigState->OpenChannelRequest(*this);
+ 		}
+ 	}
+
+TBool CL2CapSAPSignalHandler::DelayConfigRequest()
+	{
+	LOG_FUNC
+	iAwaitingConfigRequestDelayTimer = iSAPSignalHandlerTimerState==EConfigRequestDelayTimer;
+
+	return iAwaitingConfigRequestDelayTimer; //true if we are delaying
+	}
+				
+void CL2CapSAPSignalHandler::ConfigRequestDelayTimerExpiry()	
+ 	{
+ 	LOG_FUNC
+ 	if(iAwaitingConfigRequestDelayTimer)
+ 		{
+ 		iAwaitingConfigRequestDelayTimer = EFalse;
+		ConfigureChannelRequest();
+ 		}
+ 	}
+
+void CL2CapSAPSignalHandler::CloseChannelRequest()
+	{
+	LOG_FUNC
+	iSigState->CloseChannelRequest(*this);
+	}
+
+void CL2CapSAPSignalHandler::OpenChannelRequest()
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(!iOpenChannelRequestAwaitingPeerEntityConfig,
+				   Panic(EL2CAPOpenChannelRequestCalledTwice));
+
+	if (IsPeerInfoDefined())
+		{
+		iSigState->OpenChannelRequest(*this);
+		}
+	else
+		{
+		iOpenChannelRequestAwaitingPeerEntityConfig = ETrue;
+		// We'll realize the OpenChannelRequest as soon as we receive
+		// the Information Response.
+		}
+	}
+
+void CL2CapSAPSignalHandler::ConnectRequestReceived()
+	{
+	LOG_FUNC
+	iSigState->ConnectRequestReceived(*this);
+	}
+
+void CL2CapSAPSignalHandler::ConfigureChannelRequest()
+	{
+	LOG_FUNC
+	iSigState->ConfigureChannelRequest(*this);
+	}
+
+void CL2CapSAPSignalHandler::PendingCommandsDrained()
+	{
+	LOG_FUNC
+	iSigState->PendingCommandsDrained(*this);
+	}
+
+// SAP signal handler timer methods.	
+/*static*/ TInt CL2CapSAPSignalHandler::TimerExpired(TAny* aSAPSignalHandler)
+	{
+	LOG_STATIC_FUNC
+	CL2CapSAPSignalHandler* sh = reinterpret_cast<CL2CapSAPSignalHandler*>(aSAPSignalHandler);
+	sh->HandleTimerExpired();
+	return EFalse;
+	}
+
+void CL2CapSAPSignalHandler::StartConfigurationTimer()
+	{
+	LOG_FUNC
+	switch(iSAPSignalHandlerTimerState)
+		{
+		case EConfigurationTimer:
+		case EConfigRequestDelayTimer:
+			// The timer is already running. Cancel it and start it again.
+			BTSocketTimer::Remove(iSAPSignalHandlerTimerEntry);
+			iSAPSignalHandlerTimerState = ETimerIdle;
+			break;
+					
+		case ETimerIdle:
+		default:
+			// No timer running, nothing needs to be done.
+			break;
+		};
+
+	if(iSAPSignalHandlerTimerState == ETimerIdle)
+		{
+		TCallBack cb(TimerExpired, this);
+		iSAPSignalHandlerTimerEntry.Set(cb);
+		BTSocketTimer::Queue(KL2ConfigRequestDelayTimout, 
+		                     iSAPSignalHandlerTimerEntry);
+		iSAPSignalHandlerTimerState = EConfigRequestDelayTimer;
+		}
+	}
+	
+void CL2CapSAPSignalHandler::CancelTimer()
+	{
+
+	LOG_FUNC
+	if(iSAPSignalHandlerTimerState != ETimerIdle)
+		{
+		BTSocketTimer::Remove(iSAPSignalHandlerTimerEntry);
+		iSAPSignalHandlerTimerState = ETimerIdle;
+		}
+	}
+
+void CL2CapSAPSignalHandler::HandleTimerExpired()
+	{
+	LOG_FUNC
+	switch(iSAPSignalHandlerTimerState)
+		{
+		case ETimerIdle:
+			Panic(EL2CAPSSHTimerExpiredWhileInIdleState);
+			break;
+			
+		case EConfigRequestDelayTimer:
+			{
+			TCallBack cb(TimerExpired, this);
+			iSAPSignalHandlerTimerEntry.Set(cb);
+			BTSocketTimer::Queue(KL2ConfigWatchdogTimeout * KL2ProtocolSecondTimerMultiplier, 
+			                     iSAPSignalHandlerTimerEntry);
+			iSAPSignalHandlerTimerState = EConfigurationTimer;
+			ConfigRequestDelayTimerExpiry();
+			}
+			break;
+			
+		case EConfigurationTimer:
+			iSAPSignalHandlerTimerState = ETimerIdle;
+			iSigState->ConfigurationTimerExpiry(*this);
+			break;
+
+		default:
+			Panic(EL2CAPInvalidSAPSHTimerState);
+			break;
+		};		
+	}
+
+void CL2CapSAPSignalHandler::DetachFromMux()
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iPendingCommands.IsEmpty(), Panic(EL2CAPSigHandlerDeletedWithResources));
+	__ASSERT_DEBUG(iCommandsAwaitingResponse.IsEmpty(), Panic(EL2CAPSigHandlerDeletedWithResources));
+
+	// If this is a listening Socket then it will not have
+	// an associated muxer to detach.
+	if(iMuxer)
+		{
+		iLink.Deque();
+		
+		iMuxer->DetachFromMux(*this);
+
+		// Check to see if the stack needs to be brought down
+		iMuxer->MuxController().Protocol().TryToClose();
+		
+		iMuxer = NULL;
+
+		// If the SAP's Config object has registered with the Entity
+		// Config object then remove this association.
+		iChannelConfig->DetachChannelConfig();
+		}
+	}
+	
+void CL2CapSAPSignalHandler::SAPClosed()
+	{
+	LOG_FUNC
+	// The SAP is about to be detached from this signal handler.
+	// Ensure no park overrides are active.
+	UndoOverrideParkMode();
+	
+	// Check if this SH has any unsent commands
+	// outstanding.
+	iSAP = NULL;
+	if(iMuxer == NULL)	
+		{
+		delete this;
+		}
+	}
+	
+void CL2CapSAPSignalHandler::CommandResponseFailure(HL2CapCommand* aCommand)
+	{
+	LOG_FUNC
+	// Pass on this error to the state and delete the command.
+	iSigState->Error(*this, KErrL2CAPRequestTimeout, MSocketNotify::EErrorAllOperations);
+	delete aCommand;
+	}
+	
+TInt CL2CapSAPSignalHandler::SendEchoRequest(const TDes8* aData)
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+	if(iMuxer)
+		{
+		// Check to ensure that the echo payload isn't too big to send
+		if ((!aData) || (aData->Size() <= iMuxer->SigMTU() - HEchoRequest::KEchoRequestEmptyLength))
+			{
+			rerr = iMuxer->SendEchoRequest(aData, *this);
+			}
+		else
+			{
+		    rerr = KErrTooBig;
+			}
+		}
+	else
+		{
+		rerr = KErrDisconnected;
+		}
+	return rerr;
+	}
+
+void CL2CapSAPSignalHandler::EchoResponseReceived(const TDesC8* aData)
+	{
+	LOG_FUNC
+	if(iSAP)
+		{
+		iSAP->EchoResponseReceived(aData);
+		}
+	}
+
+TInt CL2CapSAPSignalHandler::SetRTXTimerDuration(TUint8 aDuration)
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+	if(aDuration > 0 && aDuration < HL2CapCommand::KMaxRTXTimerDuration)
+		{
+		iDefaultRTXTimerDuration = aDuration;
+		}
+	else
+		{
+		rerr = KErrArgument;	
+		}
+	return rerr;
+	}
+	
+TInt CL2CapSAPSignalHandler::SetERTXTimerDuration(TUint16 aDuration)
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+	if(aDuration > 0 && aDuration < HL2CapCommand::KMaxERTXTimerDuration)
+		{
+		iERTXTimerDuration = aDuration;
+		}
+	else
+		{
+		rerr = KErrArgument;	
+		}
+	return rerr;	
+	}
+	
+void CL2CapSAPSignalHandler::OverrideParkMode()
+	{
+	LOG_FUNC
+	// Override Park mode.  A reference to the SAP is required
+	// to get the remote device address.
+	if(iSAP)
+		{
+		iSAP->Protocol().ControlPlane().ModifyPhysicalLink(EOverridePark, iSAP->RemoteDev());
+		}
+	}
+	
+void CL2CapSAPSignalHandler::UndoOverrideParkMode()
+	{
+	LOG_FUNC
+	// Remove Park mode override.  A reference to the SAP is required
+	// to get the remote device address.
+	if(iSAP)
+		{
+		iSAP->Protocol().ControlPlane().ModifyPhysicalLink(EUndoOverridePark, iSAP->RemoteDev());
+		}
+	}
+	
+void CL2CapSAPSignalHandler::OverrideLPMWithTimeout()
+	{
+	LOG_FUNC
+	// Temporarily override all low power modes.  A reference to the SAP is required
+	// to get the remote device address.
+	if(iSAP)
+		{
+		iSAP->Protocol().ControlPlane().ModifyPhysicalLink(EOverrideLPMWithTimeout, iSAP->RemoteDev());
+		}
+	}
+