bluetooth/btstack/l2cap/l2capSignalHandler.cpp
changeset 0 29b1cd4cb562
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/l2cap/l2capSignalHandler.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,498 @@
+// 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:
+// Base class for implementation of signalling handlers.  This class is able to sort incoming CFramePDUs and 
+// queue outgoing l2cap commands. This class can be derived by classes wanting to handle subsets of the 
+// signalling commands.
+// 
+//
+
+/**
+ @file
+ @internalComponent
+*/
+
+#include <bluetooth/logger.h>
+
+#include "l2capSignalHandler.h"
+
+#include "l2capCommand.h"
+#include "l2signalmgr.h"
+
+#include "l2capSigPacketCommandReject.h"
+
+#include "l2util.h"
+#include "L2CapDebugControlInterface.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_L2CAP);
+#endif
+
+//Constructor as used by the link signal handler
+CL2CapSignalHandler::CL2CapSignalHandler(CL2CAPMux* aMuxer)
+ : iMuxer(aMuxer),
+   iDrainPendingCommands(EFalse),
+   iPendingCommands(_FOFF(HL2CapCommand, iLink)),
+   iCommandsAwaitingResponse(_FOFF(HL2CapCommand, iLink)),
+   iSentResponses(_FOFF(HL2CapCommand, iLink))
+	{
+	LOG_FUNC
+	L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESigHandlers,
+	                             L2capDebugInfo::EAllocated));	
+	}
+
+CL2CapSignalHandler::~CL2CapSignalHandler()
+/**
+	Destruct the signal handler.
+**/
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iPendingCommands.IsEmpty(), Panic(EL2CAPSigHandlerDeletedWithResources));
+	__ASSERT_DEBUG(iCommandsAwaitingResponse.IsEmpty(), Panic(EL2CAPSigHandlerDeletedWithResources));
+
+	HL2CapCommand *cmd = NULL;
+	TDblQueIter<HL2CapCommand> responseIter(iSentResponses);
+	responseIter.SetToFirst();
+	while((cmd = responseIter++) != NULL)
+		{
+		delete cmd;
+		}
+
+	L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESigHandlers,
+	                             L2capDebugInfo::EDeleted));
+	}
+
+// Returns a signalling pdu awaiting transmission (might contain more than one L2CAP Command)
+HL2CapPDU* CL2CapSignalHandler::GetPDU()
+	{
+	LOG_FUNC
+	TBool pduComplete = EFalse;
+	TBool pduValid = EFalse;
+	TInt err = KErrNone;
+	
+	HCFramePDU* pdu = NULL;
+	
+	// 1. Check whether there are any commands to send
+	if(!iPendingCommands.IsEmpty())
+		{
+		// 2. Create a new PDU to put the commands into
+		pdu = HCFramePDU::New(iMuxer->GetACLMTU());
+		
+		if(pdu)
+			{
+			TDblQueIter<HL2CapCommand> iter(iPendingCommands);
+			HL2CapCommand* cmd;
+			while((cmd = iter++) != NULL && !pduComplete)
+				{
+				// 3. Attempts to add command onto end of pdu.
+				err = pdu->AddCommand(*cmd, *iMuxer);
+
+				if(err == KErrNone)
+			 		{
+			 		// The command has been added.  Remove it from the
+			 		// pending list.
+					pduValid = ETrue;
+			 		cmd->iLink.Deque();
+			 		
+					if(cmd->IsRequest())
+						{
+						// Add this to the awaiting response queue.
+						iCommandsAwaitingResponse.AddLast(*cmd);	
+
+						// Start response timer.
+						cmd->StartRTXTimer(HL2CapCommand::ERTXTimer, *this);
+						}
+					else 
+						{
+						if(cmd->StatefulResponse())
+							{
+							iSentResponses.AddLast(*cmd);
+							}
+						else
+							{
+							delete cmd;
+							}
+						}
+					}
+				else
+					{
+					if(err == KErrCompletion)
+						{
+						// The command could not be added.  This indicates that the
+						// CFrame is full.
+						pduComplete = ETrue;
+						}
+					else
+						{
+						// A different error has occurred.  Error the signal handler.
+						Error(err, MSocketNotify::EErrorAllOperations);
+						}
+					}
+			
+			 	}
+
+			// Check if the PDU is valid, i.e., contains at least one command.
+			if(!pduValid)
+				{
+				delete pdu;
+				pdu = NULL;
+				}
+				
+			// Check if any commands are outstanding.  If so request
+			// another callback.
+			if(!iPendingCommands.IsEmpty() || iDrainPendingCommands)
+				{
+				iMuxer->PDUAvailable();
+				}
+			}
+		else
+			{
+			// A C-Frame could not be created.
+			Error(KErrNoMemory, MSocketNotify::EErrorAllOperations);
+			}
+		}
+	else
+		{
+		if(iDrainPendingCommands && iCommandsAwaitingResponse.IsEmpty())
+			{
+			// Nothing more to send and nothing to wait for. 
+			// Signal disconnect to the state machine.
+			PendingCommandsDrained();
+			}
+		}
+	return pdu;
+	}
+
+
+HL2CapCommand* CL2CapSignalHandler::FindMatchingOutstandingRequest(TUint8 aExpectedCommandCode, TUint8 aId)
+	{
+	LOG_FUNC
+	HL2CapCommand* l2CapCommand = NULL;
+	HL2CapCommand* matchingCommand = NULL;
+	
+	TDblQueIter<HL2CapCommand> iter(iCommandsAwaitingResponse);
+	while((l2CapCommand = iter++) != NULL)
+		{
+		if(l2CapCommand->ID() == aId)
+			{
+			if(l2CapCommand->Code() == aExpectedCommandCode || 
+			   (aExpectedCommandCode == EMatchAnyL2CAPRequest && l2CapCommand->IsRequest()))
+				{
+				matchingCommand = l2CapCommand;
+				break;
+				}
+			}
+		}
+	return matchingCommand;
+	}
+	
+// Incoming PDU handler.
+TBool CL2CapSignalHandler::HandleIncomingCFrame(HCFramePDU* aCFrame)
+	{
+	LOG_FUNC
+	// The C-Frame can contain many L2CAP commands.  Not all of
+	// the commands will be for a SAP signal handler.  If a
+	// command can be processed it will be removed from the
+	// C-Frame.  Any remaining commands will be offered to the
+	// other SAP signal handlers, then the whole process of
+	// offering the commands to the SAP signal handlers is
+	// repeated until no more commands can be handled by them,
+	// after which the commands are offered to the link signal
+	// handler.  Only one command is handled at a time because a
+	// Command Reject may delete a SAP signal handler, thereby
+	// rendering it ineffective at processing further commands.
+
+	HL2CapCommand* cmd = aCFrame->FirstCommand();
+	while(cmd)
+		{
+		// This could be a duplicate sent from the peer due to an RTx timer.
+		// If the original was received and is being processed discard the
+		// command.
+		if(!IsDuplicateCommandRequest(cmd))
+			{
+			if (cmd->ProcessCommand(*this))
+				{
+				return ETrue;
+				}
+			}
+		cmd = aCFrame->NextCommand();
+		}
+	return EFalse;
+	}
+
+TBool CL2CapSignalHandler::IsDuplicateCommandRequest(HL2CapCommand* aCommand)
+	{
+	LOG_FUNC
+	TBool rcode = EFalse;
+	
+	// Check if this is a duplicate still kicking about waiting to be sent
+	if(aCommand->IsRequest())
+		{
+		HL2CapCommand *response = NULL;
+		
+		TDblQueIter<HL2CapCommand> pendIter(iPendingCommands);
+		while((response = pendIter++) != NULL)
+			{
+			// Check if this matches the last peer request.
+			if(aCommand->ID() == response->ID() &&
+			   (aCommand->Code() + 1) == response->Code())
+				{
+				delete aCommand;
+				rcode = ETrue;
+				
+				break;
+				}
+			}
+		}
+		
+	// Otherwise check if this is a response we've already sent
+	if(!rcode && aCommand->IsRequest())
+		{
+		HL2CapCommand *response = NULL;
+		
+		TDblQueIter<HL2CapCommand> respIter(iSentResponses);
+		while((response = respIter++) != NULL)
+			{
+			// Check if this matches the last peer request.
+			if(aCommand->ID() == response->ID() &&
+			   (aCommand->Code() + 1) == response->Code())
+				{			
+				// Re-send the stored response.
+				response->iLink.Deque();
+				AddToOutgoingQueue(response);
+
+				delete aCommand;
+				rcode = ETrue;
+				
+				break;
+				}
+			}
+		}
+
+	return rcode;
+	}
+
+/**
+Store this command on the outgoing queue.  
+
+@pre aCommand is not already on a queue.
+@param aCommand The command to add to the queue.
+*/
+void CL2CapSignalHandler::AddToOutgoingQueue(HL2CapCommand* aCommand)
+	{
+	LOG_FUNC
+	if(aCommand->CommandLength() <= iMuxer->SigMTU())
+		{
+		// 1. Add to the list of outgoing commands.
+		iPendingCommands.AddLast(*aCommand);
+
+		// 2. Signal to the PDU that data is available in the queue.
+		iMuxer->PDUAvailable();
+		}
+	else
+		{
+		// At present the stack will not create a command greater in 
+		// length than the minimum SigMTU.
+		__ASSERT_DEBUG(EFalse, Panic(EL2CAPAttemptToCreateCommandLongerThanSigMTU));
+		}
+	}
+
+/**
+This is called when we know the remote has finished configuring this
+channel, ie when we've received data from it.  At this point we can
+flush the queued responses.
+*/
+void CL2CapSignalHandler::ChannelConfigured()
+	{
+	LOG_FUNC
+	HL2CapCommand* cmd = NULL;
+	TDblQueIter<HL2CapCommand> responseIter(iSentResponses);
+	responseIter.SetToFirst();
+	while((cmd = responseIter++) != NULL)
+		{
+		cmd->iLink.Deque();
+		delete cmd;
+		}
+	}
+
+void CL2CapSignalHandler::DrainPendingCommands()
+	{
+	LOG_FUNC
+	iDrainPendingCommands = ETrue;
+	iMuxer->PDUAvailable();
+	}
+
+void CL2CapSignalHandler::FlushPendingCommands()
+	{
+	LOG_FUNC
+	// Remove all pending commands from the outgoing queue.
+	HL2CapCommand* cmd = NULL;
+	TDblQueIter<HL2CapCommand> pendIter(iPendingCommands);
+	while((cmd = pendIter++) != NULL)
+		{
+		LOG1(_L(" -- Removing 0x%02x command"), cmd->Code());
+		delete cmd;
+		}
+	LOG(_L(" - queue flushed"));
+	}
+
+void CL2CapSignalHandler::FlushAwaitingResponseCommands()
+	{
+	LOG_FUNC
+	DeleteCommands(iCommandsAwaitingResponse);
+	}
+
+void CL2CapSignalHandler::FlushAllQueues()
+	{
+	LOG_FUNC
+	DeleteCommands(iPendingCommands);
+	DeleteCommands(iCommandsAwaitingResponse);
+	DeleteCommands(iSentResponses);
+	}
+
+void CL2CapSignalHandler::HandleLinkError()
+	{
+	LOG_FUNC
+	// An error condition has occurred on the link.  
+	// Any commands awaiting transmission,
+	// or awaiting responses can be deleted as no more PDU can
+	// be sent or received on this link.
+	HL2CapCommand* cmd = NULL;
+	FlushPendingCommands();
+	
+	TDblQueIter<HL2CapCommand> waitIter(iCommandsAwaitingResponse);
+	waitIter.SetToFirst();
+	while((cmd = waitIter++) != NULL)
+		{
+		delete cmd;
+		}
+		
+	TDblQueIter<HL2CapCommand> responseIter(iSentResponses);
+	responseIter.SetToFirst();
+	while((cmd = responseIter++) != NULL)
+		{
+		cmd->iLink.Deque();
+		delete cmd;
+		}
+	}
+	
+	
+TUint8 CL2CapSignalHandler::CurrentRTXTimerDuration(TUint8 aBaseRTXTimerDuration) const
+	{
+	LOG_FUNC
+	__ASSERT_ALWAYS(iMuxer,Panic(EL2CAPMuxerDeletedUnexpectedly));
+	
+	return iMuxer->AdjustRTXTimerForSniffMode(aBaseRTXTimerDuration);
+	}
+	
+
+// This base class does not handle any Frame types or commands.  These
+// default implementations are here to allow derived class to only specify
+// commands and frames that are handled.
+TBool CL2CapSignalHandler::HandleConnectionResponse(HConnectionResponse* /*aConnectionResponse*/)
+	{
+	LOG_FUNC
+	return EFalse;
+	}
+
+TBool CL2CapSignalHandler::HandleConnectionRequest(HConnectionRequest* /*aConnectionRequest*/)
+	{
+	LOG_FUNC
+	return EFalse;
+	}
+TBool CL2CapSignalHandler::HandleConfigureRequest(HConfigureRequest* /*aConfigRequest*/)
+	{
+	LOG_FUNC
+	return EFalse;
+	}
+
+TBool CL2CapSignalHandler::HandleConfigureResponse(HConfigureResponse* /*aConfigResponse*/)
+	{
+	LOG_FUNC
+	return EFalse;
+	}
+
+TBool CL2CapSignalHandler::HandleEchoResponse(HEchoResponse* /*aEchoResponse*/)
+	{
+	LOG_FUNC
+	return EFalse;
+	}
+
+TBool CL2CapSignalHandler::HandleEchoRequest(HEchoRequest* /*aEchoRequest*/) // Take incoming EchoRequest extract any data to form a valid Echo Response from CEchoResponse	
+	{
+	LOG_FUNC
+	return EFalse;	
+	}
+TBool CL2CapSignalHandler::HandleInformationRequest(HInformationRequest* /*aInformationRequest*/)
+	{
+	LOG_FUNC
+	return EFalse;	
+	}
+	
+TBool CL2CapSignalHandler::HandleDisconnectRequest(HDisconnectRequest* /*aDisconnectRequest*/)
+	{
+	LOG_FUNC
+	return EFalse;	
+	}
+	
+TBool CL2CapSignalHandler::HandleDisconnectResponse(HDisconnectResponse* /*aDisconnectResponse*/)
+	{
+	LOG_FUNC
+	return EFalse;	
+	}
+
+TBool CL2CapSignalHandler::HandleInformationResponse(HInformationResponse* /*aInformationResponse*/)
+	{
+	LOG_FUNC
+	return EFalse;
+	}
+	
+TBool CL2CapSignalHandler::HandleCommandReject(HCommandReject* /*aCommandReject*/)
+	{
+	LOG_FUNC
+	return EFalse;
+	}
+
+TBool CL2CapSignalHandler::HandleInvalidCommand(HInvalidCommand* /*aInvalidCommand*/)
+	{
+	LOG_FUNC
+	return EFalse;
+	}
+
+
+void CL2CapSignalHandler::SendInvalidCIDCommandReject(TUint8 aId, TL2CAPPort aSourceCID, TL2CAPPort aDestinationCID)
+	{
+	LOG_FUNC
+	TL2CAPCommandRejectData reason;
+	reason.iReason = EInvalidCID;
+	reason.iMTUExceeded = 0;
+	reason.iLocalEndpoint = aDestinationCID;
+	reason.iRemoteEndpoint = aSourceCID;
+	
+	HL2CapCommand* command = HCommandReject::New(reason, aId);
+	if(command)
+		{
+		AddToOutgoingQueue(command);
+		}
+	}
+
+void CL2CapSignalHandler::DeleteCommands(TDblQue<HL2CapCommand>& aCommandsToDelete)
+	{
+	LOG_FUNC
+	HL2CapCommand* cmd = NULL;
+	TDblQueIter<HL2CapCommand> aDelCmdIter(aCommandsToDelete);
+	while((cmd = aDelCmdIter++) != NULL)
+		{
+		delete cmd;
+		}
+	}