// 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 signalling state machine for an L2Cap Channel
#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 "l2capSigStates.h"
#include "l2signalmgr.h"

#include "l2capSAPSignalHandler.h"
#include "l2cap.h"
#include "l2sap.h"

#include "l2util.h"

#ifdef __FLOG_ACTIVE
_LIT8(KLogComponent, LOG_COMPONENT_L2CAP);
#endif

/*************************************************************************/
//
// TL2CAPSigState Implementation
// 

TL2CAPSigState::TL2CAPSigState(const CL2CAPSignalStateFactory& aFactory):
	iFactory(aFactory)
	{
	LOG_FUNC
	}

// Events from the SAP
void TL2CAPSigState::CloseChannelRequest(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	// This is a standard action for most states.
	// Disconnect received from the SAP.  Return to the
	// closed state.
	aSignalHandler.HandleLinkError();
	aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EClosed));
		
	// Signal back to the SAP that disconnection is complete.
	aSignalHandler.SAP()->ChannelClosed();
	}

void TL2CAPSigState::OpenChannelRequest(CL2CapSAPSignalHandler& /*aSignalHandler*/) const	
	{
	LOG_FUNC
	PanicInState(EL2CAPUnexpectedSAPEvent);
	}

void TL2CAPSigState::ConnectRequestReceived(CL2CapSAPSignalHandler& /*aSignalHandler*/) const	
	{
	LOG_FUNC
	PanicInState(EL2CAPUnexpectedSAPEvent);
	}

void TL2CAPSigState::ConfigureChannelRequest(CL2CapSAPSignalHandler& /*aSignalHandler*/) const	
	{
	LOG_FUNC
	PanicInState(EL2CAPUnexpectedSAPEvent);
	}

// L2CAP commands received from the peer.
void TL2CAPSigState::ConnectResponse(CL2CapSAPSignalHandler& /*aSignalHandler*/,
						 			 HConnectionResponse* /*aConnectionResponse*/) const
	{
	LOG_FUNC
	LOG1(_L("Unhandled Connect Response, state = 0x%08x"), this);
	}


void TL2CAPSigState::ConfigRequest(CL2CapSAPSignalHandler& /*aSignalHandler*/,
										  HConfigureRequest* /*aConfigRequest*/) const
	{
	LOG_FUNC
	LOG1(_L("Unhandled Config Req, state = 0x%08x"), this);
	}

void TL2CAPSigState::ConfigResponse(CL2CapSAPSignalHandler& /*aSignalHandler*/,
 	                					   HConfigureResponse* /*aConfigResponse*/) const
	{
	LOG_FUNC
	LOG1(_L("Unhandled Config Response, state = 0x%08x"), this);
	}
	
void TL2CAPSigState::DisconnectRequest(CL2CapSAPSignalHandler& /*aSignalHandler*/, 
                                              TUint8 /*aId*/) const
	{
	LOG_FUNC
	LOG1(_L("Unhandled Disconnect Request, state = 0x%08x"), this);
	}

void TL2CAPSigState::DisconnectResponse(CL2CapSAPSignalHandler& /*aSignalHandler*/) const
	{
	LOG_FUNC
	LOG1(_L("Unhandled Disconnect Response, state = 0x%08x"), this);
	}

// Events from the Mux	
void TL2CAPSigState::Error(CL2CapSAPSignalHandler& aSignalHandler,
	                       TInt aErrorCode, 
	                       MSocketNotify::TOperationBitmasks aErrorAction) const
	{
	LOG_FUNC
	// An error has occurred in the Mux or the signal handler.  This means no
	// more signalling can be done.  Move to the closed state.  Signal link down
	// to clear any pending commands and stop waiting for outstanding responses.
	// Inform the SAP of the error.
	aSignalHandler.HandleLinkError();
	aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EClosed));

	// This will delete the signal handler if the SAP has detached.
	aSignalHandler.SignalHandlerErrorD(aErrorCode, aErrorAction);
	}

TInt TL2CAPSigState::UpdateChannelConfig(CL2CapSAPSignalHandler& aSignalHandler, const TL2CapConfig& aAPIConfig) const
	{
	LOG_FUNC
	// The default action is to update the values.  Different actions are taken
	// if configuration is in progress or the channel is open.
	TBool reconfigRequired; //  this only matters when the channel is already Open, can ignore it here
	return aSignalHandler.ChannelConfig().UpdateConfigAPIChange(aAPIConfig, reconfigRequired);
	}

TInt TL2CAPSigState::GetNegotiatedChannelMode(const CL2CapSAPSignalHandler& /*aSignalHandler*/, TL2CapChannelMode& /*aMode*/) const
	{
	return KErrNotReady;
	}

// Change of state events
void TL2CAPSigState::Enter(CL2CapSAPSignalHandler& /*aSignalHandler*/) const
	{
	LOG_FUNC
	}

void TL2CAPSigState::Exit(CL2CapSAPSignalHandler& /*aSignalHandler*/) const
	{
	LOG_FUNC
	}	

// Signal command queue event
void TL2CAPSigState::PendingCommandsDrained(CL2CapSAPSignalHandler& /*aSignalHandler*/) const
	{
	LOG_FUNC
	PanicInState(EL2CAPUnexpectedSAPSignalHandlerEvent);
	}


// Timers
void TL2CAPSigState::ConfigurationTimerExpiry(CL2CapSAPSignalHandler& /*aSignalHandler*/) const
	{
	LOG_FUNC
	PanicInState(EL2CAPUnexpectedSAPSignalHandlerEvent);
	}


// Helpers		
TBool TL2CAPSigState::IsChannelClosed() const
	{
	LOG_FUNC
	// Default is false.  Only true in closed state.
	return EFalse;
	}

TBool TL2CAPSigState::IsChannelOpen() const
	{
	// Only true in Open state.
	return EFalse;
	}

void TL2CAPSigState::HandleDisconnectRequest(CL2CapSAPSignalHandler& aSignalHandler, TUint8 aId) const
	{
	LOG_FUNC
	aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EWaitDisconnect));

	TInt err = aSignalHandler.ConstructDisconnectResponse(aId);
	if(err == KErrNone)
		{
		aSignalHandler.DrainPendingCommands();
		}
	else
		{
		// Failed to send the response.  Go directly into the closed state
		// and inform the SAP that disconnection is complete.
		aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EClosed));
		aSignalHandler.HandleLinkError();
		aSignalHandler.SAP()->ChannelClosed();
		}
	}

void TL2CAPSigState::HandleSAPDisconnect(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	TInt err = KErrDisconnected;
	
	if(aSignalHandler.RemotePort() != 0)
		{
		// We have a remote CID from a response with result pending, issue a disconnect request
		aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EWaitDisconnect));
		err = aSignalHandler.ConstructDisconnectRequest();
		}	

	if(err != KErrNone)
		{
		// Failed to send the request or do not have a connection (without a valid CID). 
		// Go directly into the closed state and inform the SAP that disconnection is complete.
		aSignalHandler.HandleLinkError();
		aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EClosed));
		aSignalHandler.SAP()->ChannelClosed();
		}
	}

TInt TL2CAPSigState::SendConfigRequest(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	return aSignalHandler.ConstructConfigRequest();
	}

TInt TL2CAPSigState::SendConfigResponse(CL2CapSAPSignalHandler& aSignalHandler, TConfigResponseResult aResult) const
	{
	LOG_FUNC
	return aSignalHandler.ConstructConfigResponse(aSignalHandler.GetOutstandingRequestID(), 
	                                              HConfigureResponse::KNoConfigurationFlags,
	                                              aResult);
	}

void TL2CAPSigState::DisconnectWithError(CL2CapSAPSignalHandler& aSignalHandler, TInt aError) const
	{
	aSignalHandler.SetSignalHandlerErrorCode(aError);
	aSignalHandler.SetSignalHandlerErrorAction(MSocketNotify::EErrorAllOperations);
	aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EWaitDisconnect));
	TInt err = aSignalHandler.ConstructDisconnectRequest();
	if(err != KErrNone)
		{
		// Failed to send the request. Call the Error method, this
		// will move the state to closed and forward the error to
		// the SAP.
		Error(aSignalHandler, aError, MSocketNotify::EErrorAllOperations);
		}
	}


/**
Calls the appropriate panic function to encode the panic
code with the current state identifier.
@param aPanic The panic code that the state is panicking with.
*/
void TL2CAPSigState::PanicInState(TL2CAPPanic aPanic) const
	{
	LOG_FUNC
	Panic(aPanic, iFactory.StateIndex(this));
	}
	
/*************************************************************************/
// Implementation of TL2CAPStateClosed
//
// Starting state and state re-entered when L2Cap logical link has been disconnected.
//
TL2CAPSigStateClosed::TL2CAPSigStateClosed(const CL2CAPSignalStateFactory& aFactory)
	: TL2CAPSigState(aFactory)
	{
	LOG_FUNC
	}

void TL2CAPSigStateClosed::OpenChannelRequest(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	// The SAP has requested a channel be opened.

	TInt err = aSignalHandler.ChannelConfig().UpdateLocalConfigWithEntityCapabilities();
	if (err == KErrNone)
		{
		// Get a free source CID for this channel to use.
		TL2CAPPort port;
		err = aSignalHandler.Mux().GetFreeCID(port);
		if(err == KErrNone)
			{
			// Update local Port.
			aSignalHandler.SetLocalPort(port);
	
			// Override park to allow the connection to be made.
			aSignalHandler.OverrideParkMode();
	
			// Active connection.  Send a Connect Request command.
			err = aSignalHandler.ConstructConnectionRequest();
			if(err == KErrNone)
				{
				aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EWaitConnectRsp));
				}
			}
		}

	if (err != KErrNone)
		{
		// The L2CAP connection could not be initiated.  Error the SAP.
		Error(aSignalHandler, err, MSocketNotify::EErrorConnect);
		}
	}

void TL2CAPSigStateClosed::ConnectRequestReceived(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	TInt err = KErrNone;
	// An ACL has been established.
	// Get a free source CID for this channel to use.
	TL2CAPPort port;
	err = aSignalHandler.Mux().GetFreeCID(port);
	if(err == KErrNone)
		{
		// Update local Port.
		aSignalHandler.SetLocalPort(port);

		// Override park to allow the connection to be made.
		aSignalHandler.OverrideParkMode();
		// Override all LPMs (if timeout not set to zero)
		aSignalHandler.OverrideLPMWithTimeout();

		// Send a response with a 'pending' result and start security procedure.
		err = aSignalHandler.ConstructConnectionResponse(aSignalHandler.GetOutstandingRequestID(),
	                                                     EConnectPending,
		                                                 EConnectPendAuthorization);
		}

	if(err == KErrNone)
		{
		aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EWaitConnect));
		}
	else
		{
		// The L2CAP connection could not be initiated.  Error the SAP.
		Error(aSignalHandler, err, MSocketNotify::EErrorConnect);
		}
	}


void TL2CAPSigStateClosed::CloseChannelRequest(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	// Detach from the Mux.  Inform the SAP that disconnection is complete.
	aSignalHandler.DetachFromMux();
	aSignalHandler.SAP()->ChannelClosed();
	}

void TL2CAPSigStateClosed::ConfigRequest(CL2CapSAPSignalHandler& aSignalHandler,
								  		 HConfigureRequest* aConfigRequest) const
	{
	LOG_FUNC
	aSignalHandler.SendInvalidCIDCommandReject(aConfigRequest->ID(),
											   0,
											   aConfigRequest->DestinationCID());
	}

TBool TL2CAPSigStateClosed::IsChannelClosed() const
	{
	LOG_FUNC
	return ETrue;
	}

void TL2CAPSigStateClosed::Enter(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	// Cancel the configuration timer.
	aSignalHandler.CancelTimer();

	// If Park mode has been overridden during either channel establishment or
	// channel disconnect, remove the override.
	// NB It is safe to call this method multiple times.
	aSignalHandler.UndoOverrideParkMode();

	// If the signal handler has left the closed state, it will have been added
	// to the SH list in the Muxer.  Now that the closed state is being
	// re-entered, remove the handler from the Muxer queue.
	// NB It is safe to call this method multiple times.
	aSignalHandler.DetachFromMux();
	}

	
/************************************************************/
// Implementation of void TL2CAPSigStateWaitConnectRsp
//
// This represents an Active SAP that is waiting for a connection response
TL2CAPSigStateWaitConnectRsp::TL2CAPSigStateWaitConnectRsp(const CL2CAPSignalStateFactory& aFactory)
	: TL2CAPSigState(aFactory)
	{
	LOG_FUNC
	}

// L2CAP commands received from the peer.
void TL2CAPSigStateWaitConnectRsp::ConnectResponse(CL2CapSAPSignalHandler& aSignalHandler,
								 				   HConnectionResponse* aConnectionResponse) const

	{
	LOG_FUNC	
	switch(aConnectionResponse->Result())
		{
		case EConnectSuccess:
			aSignalHandler.SetRemotePort(aConnectionResponse->DestinationCID());
			aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EWaitConfig));
			aSignalHandler.SAP()->ChannelOpened();
			break;

		case EConnectPending:
			aSignalHandler.SetRemotePort(aConnectionResponse->DestinationCID());
			break;	// No other action here - the ERTx timer has been started.

		default:
			// One of the failed, non-pending results
			
			// The connect has failed
			// We're re-connectable, so go back to orig state
			// NB Error now clears muxer and sets state to closed
			Error(aSignalHandler, ((aConnectionResponse->Result() == EConnectSecurityBlock) ? KErrL2CAPAccessRequestDenied : KErrCouldNotConnect), MSocketNotify::EErrorConnect);
			break;
		};
	}
	
void TL2CAPSigStateWaitConnectRsp::ConfigRequest(CL2CapSAPSignalHandler& /*aSignalHandler*/,
						 						 HConfigureRequest* /*aConfigRequest*/) const
	{
	LOG_FUNC
	// Override the default panicing action for this state as we may validly be
	// here.  This could happen if the remote has sent a connect response, followed
	// by this config request but we have not successfully processed the connect 
	// response.  This can happen when the response arrives after the rtx has
	// expired but we haven't yet resent the connect request.
	}

// Events from the Mux
void TL2CAPSigStateWaitConnectRsp::CloseChannelRequest(CL2CapSAPSignalHandler& aSignalHandler) const
	{	
	// Disconnect the channel.	
	HandleSAPDisconnect(aSignalHandler);
	}

void TL2CAPSigStateWaitConnectRsp::Enter(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	//set the remote CID to 0
	aSignalHandler.SetRemotePort(TL2CAPPort(0));
	}

/****************************************************************************/
// 
// Waiting for the SAP to indicate that the connection can be established.
TL2CAPSigStateWaitConnect::TL2CAPSigStateWaitConnect(const CL2CAPSignalStateFactory& aFactory)
	: TL2CAPSigState(aFactory)
	{
	LOG_FUNC
	}
	
// Events from the SAP
void TL2CAPSigStateWaitConnect::OpenChannelRequest(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC

	TInt updateConfigErr = aSignalHandler.ChannelConfig().UpdateLocalConfigWithEntityCapabilities();
	TInt sendErr = KErrNone;
	if (updateConfigErr == KErrNone)
		{
		// The connection has been allowed.  Send the connection response.
		sendErr = aSignalHandler.ConstructConnectionResponse(aSignalHandler.GetOutstandingRequestID(),
															 EConnectSuccess,
															 EConnectPendNoFurtherInfo);
		if (sendErr == KErrNone)
			{
			// Enter the initial config state and initiate local configuration.
			aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EWaitConfig));
			if(!aSignalHandler.DelayConfigRequest())
				// We should never get in here! The timer, which allows the delay
				// should have been started on entry to the state "WaitConfig". 
				{
				aSignalHandler.ConfigureChannelRequest();
				}
			}
		} // Success Response
	else
		{
		// We don't have a generic 'Just no' response code, 'No resources' comes closest.
		sendErr = aSignalHandler.ConstructConnectionResponse(aSignalHandler.GetOutstandingRequestID(),
														     EConnectNoResources,
															 EConnectPendNoFurtherInfo);
		if (sendErr == KErrNone)
			{
			// When the response is sent, PendingCommandsDrained() will call Error().
			aSignalHandler.SetSignalHandlerErrorCode(updateConfigErr);
			aSignalHandler.SetSignalHandlerErrorAction(MSocketNotify::EErrorConnect);
			aSignalHandler.DrainPendingCommands();
			}
		} // Failure response

	if (sendErr != KErrNone)
		{
		// The L2CAP connection could not be initiated.  Error the SAP.
		Error(aSignalHandler, sendErr, MSocketNotify::EErrorConnect);
		}
	}

// Events from the Mux
void TL2CAPSigStateWaitConnect::CloseChannelRequest(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC

	// Tell the peer that there has been a security error
	TInt err = aSignalHandler.ConstructConnectionResponse(aSignalHandler.GetOutstandingRequestID(),
	                                                      				EConnectSecurityBlock,
	                                                      				EConnectPendNoFurtherInfo);

	if(err == KErrNone)
		{
		aSignalHandler.DrainPendingCommands();
		}
	else
		{
		// Failed to send the response.  Go directly into the closed state
		// and inform the SAP that disconnection is complete.

		// HandleLinkError flushes all the signal handler queues.  This should be done
		// before making the transition to the EClosed state because the signal handler
		// will be detached from the mux it is bound to, at which point we expect there
		// to be no commands queued (as we will no longer be able to deal with them).
		aSignalHandler.HandleLinkError();
		aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EClosed));
		aSignalHandler.SAP()->ChannelClosed();
		}
	}

void TL2CAPSigStateWaitConnect::PendingCommandsDrained(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	if(aSignalHandler.SignalHandlerErrorCode() != KErrNone)
		{
		Error(aSignalHandler, aSignalHandler.SignalHandlerErrorCode(), aSignalHandler.SignalHandlerErrorAction());
		}
	else
		{
		// This will delete the signal handler if the SAP has detached.
		aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EClosed));
		aSignalHandler.SignalHandlerDisconnectedCanClose();
		}
	}

// L2CAP commands received from the peer.
void TL2CAPSigStateWaitConnect::DisconnectRequest(CL2CapSAPSignalHandler& aSignalHandler, TUint8 aId) const
	{
	LOG_FUNC
	// Call a common helper function to handle the Disconnect Request.
	HandleDisconnectRequest(aSignalHandler, aId);
	}


/**************************************************************************/
// Implementation of TL2CAPSigStateConfigBase
//
// This represents a SAP that is currently undergoing 
// configuration on the link whether it be initial or reconfiguration
// This is the base class and will never itself be instantiated.

TL2CAPSigStateConfigBase::TL2CAPSigStateConfigBase(const CL2CAPSignalStateFactory& aFactory)
	: TL2CAPSigState(aFactory)
	{
	LOG_FUNC
	}

// L2CAP commands received from the peer.
void TL2CAPSigStateConfigBase::DisconnectRequest(CL2CapSAPSignalHandler& aSignalHandler, TUint8 aId) const
	{
	LOG_FUNC
	// FIRST - Flush all three signalling queues
	aSignalHandler.HandleLinkError();
	
	// Call a common helper function to handle the Disconnect Request.
	// NB This will add a new command to the pending command list.
	HandleDisconnectRequest(aSignalHandler, aId);
	}

void TL2CAPSigStateConfigBase::Error(CL2CapSAPSignalHandler& aSignalHandler,
           							 TInt aErrorCode, 
           							 MSocketNotify::TOperationBitmasks aErrorAction) const
	{
	LOG_FUNC
	// Cancel the configuration timer.
	aSignalHandler.CancelTimer();

	// Call the base class error method.
	TL2CAPSigState::Error(aSignalHandler, aErrorCode, aErrorAction);
	}      							 	

// Events from the SAP
void TL2CAPSigStateConfigBase::CloseChannelRequest(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	// Disconnect the channel.
	HandleSAPDisconnect(aSignalHandler);
	}

TInt TL2CAPSigStateConfigBase::UpdateChannelConfig(CL2CapSAPSignalHandler& /*aSignalHandler*/, const TL2CapConfig& /*aAPIConfig*/) const
	{
	LOG_FUNC
	// Configuration is in progress.
	return KErrL2CAPConfigAlreadyInProgress;
	}

// Timer Events
void TL2CAPSigStateConfigBase::ConfigurationTimerExpiry(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	CloseChannelRequest(aSignalHandler);
	}
	
// Common handling of Config Request and Response commands.
void TL2CAPSigStateConfigBase::ConfigRequest(CL2CapSAPSignalHandler& aSignalHandler,
											 HConfigureRequest* aConfigRequest,
											 CL2CAPSignalStateFactory::TSigStates aConfigSuccessState) const
	{
	LOG_FUNC
	RMBufChain unknownOptions;
	// Following the state machine, we know that OUR config request
	// has not yet been sent (or even queued) if the next state
	// (on success) is EWaitSendConfig
	TBool configRequestSent = aConfigSuccessState != CL2CAPSignalStateFactory::EWaitSendConfig;

	TInt err = aSignalHandler.ChannelConfig().HandleConfigRequest(*aConfigRequest, unknownOptions);
	if(err == KErrNone)
		{
		// Check if the Config Request has been segmented using the continuation flag.
		if(!(aConfigRequest->Flags() & KConfigOptionContinuationMask))
			{
			TConfigResponseResult result;
			err = aSignalHandler.ChannelConfig().ConfigRequestComplete(configRequestSent, result);
			if (err == KErrConfigRejected)
				{
				// 'State 2' negotiation channel mode conflict - disconnect when a request
				// for anything other than our preferred channel mode is received.
				// Don't send a Config Response, send a Disconnect Request immediately.
				DisconnectWithError(aSignalHandler, err);
				}
			else
				{
				if (err == KErrNone)
					{
					aSignalHandler.SetOutstandingRequestID(aConfigRequest->ID());
					// Send the config response.
					err = SendConfigResponse(aSignalHandler, result);
					if(err == KErrNone)
						{
						// Check if the peer config was acceptable.
						switch(result)
							{
							case EConfigSuccess:
								// Move to next state.
								aSignalHandler.SetState(iFactory.GetState(aConfigSuccessState));
								break;
	
							case EConfigUnacceptableParams:
							case EConfigRejected:
								// Stay in current state.
								break;
	
							case EConfigUnknownOption: // impossible on this path
							default:
								__ASSERT_DEBUG(EFalse, PanicInState(EL2CAPInvalidConfigResponseCodeGenerated));
								break;
							}
						} // err == KErrNone
					} // err == KErrNone
				if (err)
					{
					// The L2CAP connection could not be initiated.  Error the SAP.
					Error(aSignalHandler, err, MSocketNotify::EErrorAllOperations);
					}
				} // err != KErrConfigRejected
			} // continuation bit not set
		else
			{
			// The config request has been segmented.  Send an empty Config Response
			// message and wait for the final config request containing a C-Flag set to zero.
			err = aSignalHandler.ConstructConfigResponse(aConfigRequest->ID(),
			                                             KConfigOptionContinuationMask,
			                                             EConfigSuccess);
			if(err != KErrNone)
				{
				// The L2CAP connection could not be initiated.  Error the SAP.
				Error(aSignalHandler, err, MSocketNotify::EErrorAllOperations);
				}
			}
		} // no error when parsing request command
	else
		{
		TBool segmentedRequest = aConfigRequest->Flags() & KConfigOptionContinuationMask;
		TConfigFlags responseFlags = segmentedRequest ? KConfigOptionContinuationMask :
														HConfigureResponse::KNoConfigurationFlags;

		// Switch on the reason for the incoming request parsing failure.
		switch(err)
			{
			case KErrBadPacketReceived:
				// The options could not be decoded.  Send a response
				// indicating that the config has been rejected.  Wait for
				// the peer to take action.
				err = aSignalHandler.ConstructConfigResponse(aConfigRequest->ID(),
				                                             responseFlags,
				                                             EConfigRejected);

				if(err != KErrNone)
					{
					Error(aSignalHandler, KErrCouldNotConnect, MSocketNotify::EErrorConnect);
					}
				break;

			case KErrConfigUnknownOptions:
				err = aSignalHandler.ConstructConfigResponse(aConfigRequest->ID(),
				                                             responseFlags,
				                                             EConfigUnknownOption,
				                                             unknownOptions);
				if(err != KErrNone)
					{
					Error(aSignalHandler, KErrCouldNotConnect, MSocketNotify::EErrorConnect);
					}
				break;

			default:
				// The L2CAP connection could not be initiated.  Error the SAP.
			    Error(aSignalHandler, err, MSocketNotify::EErrorConnect);
				break;
			}
		} // parsing not successful
	// Free the unknown options buffer.
	unknownOptions.Free();
	}

void TL2CAPSigStateConfigBase::ConfigResponse(CL2CapSAPSignalHandler& aSignalHandler,
											 HConfigureResponse* aConfigResponse,
											 CL2CAPSignalStateFactory::TSigStates aConfigSuccessState) const
	{
	LOG_FUNC
	RMBufChain unknownOptions;
	TInt err = aSignalHandler.ChannelConfig().HandleConfigResponse(*aConfigResponse, unknownOptions);

	if(err == KErrNone)
		{
		// Check if the Config Response has been segmented using the continuation flag.
		if(!(aConfigResponse->Flags() & KConfigOptionContinuationMask))
			{
			// Check if the peer has accepted our config.
			CL2CapChannelConfig::TChannelConfigStatus status = aSignalHandler.ChannelConfig().LocalConfigurationStatus();

			// Modify the status according to the result in the response received.
			if(aConfigResponse->Results() == EConfigUnacceptableParams)
				{
				// If the result was unacceptable parameters always reconfigure.
				if(status == CL2CapChannelConfig::EChannelConfigComplete)
					{
					status = CL2CapChannelConfig::EChannelConfigOutstanding;
					}
				}
			else
				{
				if(aConfigResponse->Results() != EConfigSuccess)
					{
					// Either the peer has rejected the last request or some of the
					// parameters contained in the last request are not recognised.
					// Ensure the channel is disconnected.
					status = CL2CapChannelConfig::EChannelConfigFailed;
					}
				}

			switch(status)
				{
				case CL2CapChannelConfig::EChannelConfigComplete:
					// Configuration complete.  Switch to the next state.
					aSignalHandler.SetState(iFactory.GetState(aConfigSuccessState));
					break;

				case CL2CapChannelConfig::EChannelConfigOutstanding:
					// Local config should be restarted.
					// Send another Config Request and stay in this state.
					err = SendConfigRequest(aSignalHandler);
					if(err != KErrNone)
						{
						Error(aSignalHandler, KErrCouldNotConnect, MSocketNotify::EErrorAllOperations);
						}
					break;

				case CL2CapChannelConfig::EChannelConfigFailed:
					// Configuration has failed.
					// Store the error code and disconnect the channel.
					DisconnectWithError(aSignalHandler, KErrConfigRejected);
					break;

				default:
					__ASSERT_DEBUG(EFalse, PanicInState(EL2CAPInvalidPeerConfigCodeGenerated));
					break;
				};
			}
		// else - the config response has been segmented - wait for the final config
		// response containing a C-Flag set to zero.
		}
	else
		{
		Error(aSignalHandler, KErrCouldNotConnect, MSocketNotify::EErrorAllOperations);
		}
	// Free the unknown options buffer.	
	unknownOptions.Free();
	}

/**************************************************************************/
// Implementation of TL2CAPSigStateWaitConfig
//
// No config requests/responses sent or received.
// Waiting to receive peer L2Cap supported features.
//
TL2CAPSigStateWaitConfig::TL2CAPSigStateWaitConfig(const CL2CAPSignalStateFactory& aFactory)
	: TL2CAPSigStateConfigBase(aFactory) 
	{
	LOG_FUNC
	}

// Events from the SAP
void TL2CAPSigStateWaitConfig::ConfigureChannelRequest(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	// Local config can now be initiated.  The local config requirements must
	// take into account the peer entity capabilities.
	__ASSERT_DEBUG(aSignalHandler.ChannelConfig().IsPeerInfoDefined(),
				   Panic(EL2CAPFecConfigAttemptWithoutPeerInfo));

	if(!aSignalHandler.DelayConfigRequest())
		{
		// Send our config request.
		TInt err = SendConfigRequest(aSignalHandler);
		if(err == KErrNone)
			{
			aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EWaitConfigReqRsp));
			}						
		else
			{
			Error(aSignalHandler, KErrCouldNotConnect, MSocketNotify::EErrorAllOperations);
			}
		}
	// Else wait until the delay timer expires.
	}
	
// L2CAP commands received from the peer.
void TL2CAPSigStateWaitConfig::ConfigRequest(CL2CapSAPSignalHandler& aSignalHandler,
								  			 HConfigureRequest* aConfigRequest) const
	{
	LOG_FUNC
	// Call the config base class helper method to process this command.
	TL2CAPSigStateConfigBase::ConfigRequest(aSignalHandler, aConfigRequest, CL2CAPSignalStateFactory::EWaitSendConfig);
	}
		
// Change of state events
void TL2CAPSigStateWaitConfig::Enter(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	aSignalHandler.StartConfigurationTimer();
	}

/**************************************************************************/
// Implementation of EWaitSendConfig
//
// Remote's config request received and responded to with 'success'.
// Waiting to send outgoing config request...
// ...waiting to receive peer L2Cap supported features.
//
TL2CAPSigStateWaitSendConfig::TL2CAPSigStateWaitSendConfig(const CL2CAPSignalStateFactory& aFactory)
  : TL2CAPSigStateConfigBase(aFactory) 
	{
	LOG_FUNC
	}
	
// Events from the SAP
void TL2CAPSigStateWaitSendConfig::ConfigureChannelRequest(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	// Local config can now be initiated.  The local config requirements must
	// take into account the peer entity capabilities.
	__ASSERT_DEBUG(aSignalHandler.ChannelConfig().IsPeerInfoDefined(),
				   Panic(EL2CAPFecConfigAttemptWithoutPeerInfo));

	// Send our config request.
	TInt err = SendConfigRequest(aSignalHandler);
	if(err == KErrNone)
		{
		aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EWaitConfigRsp));
		}						
	else
		{
		Error(aSignalHandler, KErrCouldNotConnect, MSocketNotify::EErrorAllOperations);
		}
	}

/**************************************************************************/
// Implementation of TL2CAPSigStateWaitConfigReqRsp
//
// Outgoing config request sent.
// Waiting for remote's config request, and remote's config response.
//
TL2CAPSigStateWaitConfigReqRsp::TL2CAPSigStateWaitConfigReqRsp(const CL2CAPSignalStateFactory& aFactory)
	: TL2CAPSigStateConfigBase(aFactory) 
	{
	LOG_FUNC
	}

// L2CAP commands received from the peer.
void TL2CAPSigStateWaitConfigReqRsp::ConfigRequest(CL2CapSAPSignalHandler& aSignalHandler,
								  	  			   HConfigureRequest* aConfigRequest) const
	{
	LOG_FUNC
	// Call the config base class helper method to process this command.
	TL2CAPSigStateConfigBase::ConfigRequest(aSignalHandler, aConfigRequest, CL2CAPSignalStateFactory::EWaitConfigRsp);
	}

void TL2CAPSigStateWaitConfigReqRsp::ConfigResponse(CL2CapSAPSignalHandler& aSignalHandler,
					  		  	                    HConfigureResponse* aConfigResponse) const
	{
	LOG_FUNC
	// Call the config base class helper method to process this command.
	TL2CAPSigStateConfigBase::ConfigResponse(aSignalHandler, aConfigResponse, CL2CAPSignalStateFactory::EWaitConfigReq);
	}

/**************************************************************************/
// Implementation of EWaitConfigRsp
//
// Remote's config request received and responded to with 'success'.
// Outgoing config request sent.
// Waiting for remote's config response.
//
TL2CAPSigStateWaitConfigRsp::TL2CAPSigStateWaitConfigRsp(const CL2CAPSignalStateFactory& aFactory)
	: TL2CAPSigStateConfigBase(aFactory) 
	{
	LOG_FUNC
	}

void TL2CAPSigStateWaitConfigRsp::ConfigResponse(CL2CapSAPSignalHandler& aSignalHandler,
					  		  	                 HConfigureResponse* aConfigResponse) const
	{
	LOG_FUNC
	// Call the config base class helper method to process this command.
	TL2CAPSigStateConfigBase::ConfigResponse(aSignalHandler, aConfigResponse, CL2CAPSignalStateFactory::EOpen);
	}

/**************************************************************************/
// Implementation of EWaitConfigReq
//
// Outgoing config request sent, and responded to with 'success'.
// Waiting for remote's config request.
//
TL2CAPSigStateWaitConfigReq::TL2CAPSigStateWaitConfigReq(const CL2CAPSignalStateFactory& aFactory)
	: TL2CAPSigStateConfigBase(aFactory) 
	{
	LOG_FUNC
	}

// L2CAP commands received from the peer.
void TL2CAPSigStateWaitConfigReq::ConfigRequest(CL2CapSAPSignalHandler& aSignalHandler,
								  	  		    HConfigureRequest* aConfigRequest) const
	{
	LOG_FUNC
	// Call the config base class helper method to process this command.
	TL2CAPSigStateConfigBase::ConfigRequest(aSignalHandler, aConfigRequest, CL2CAPSignalStateFactory::EOpen);
	}

	

/**************************************************************************/
// Implementation of void TL2CAPSigStateOpen
//
// L2Cap logical link open. Data can be sent and received.
//
TL2CAPSigStateOpen::TL2CAPSigStateOpen(const CL2CAPSignalStateFactory& aFactory)
	: TL2CAPSigState(aFactory) 
	{
	LOG_FUNC
	}

void TL2CAPSigStateOpen::ConfigRequest(CL2CapSAPSignalHandler& aSignalHandler,
								  	   HConfigureRequest* aConfigRequest) const
	{
	LOG_FUNC
	// Starts of reconfiguration instigated by remote device
	aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EWaitConfig));

	// Inform the data plane [via the SAP] that reconfig is in progress and data
	// should not be sent until it completes.
	aSignalHandler.ReconfiguringChannel();
	static_cast<void>(aSignalHandler.HandleConfigureRequest(aConfigRequest));

	// Start local reconfiguration.
	aSignalHandler.ConfigureChannelRequest();
	}

TInt TL2CAPSigStateOpen::UpdateChannelConfig(CL2CapSAPSignalHandler& aSignalHandler, const TL2CapConfig& aAPIConfig) const
	{
	LOG_FUNC
	TBool reconfigRequired = EFalse;
	TInt rerr = aSignalHandler.ChannelConfig().UpdateConfigAPIChange(aAPIConfig, reconfigRequired);
	if (reconfigRequired && rerr == KErrNone)
		{
		aSignalHandler.ReconfiguringChannel();
		aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EWaitConfig));
		aSignalHandler.ConfigureChannelRequest();
		rerr = KErrL2CAPConfigPending;
		}
	return rerr;
	}


void TL2CAPSigStateOpen::CloseChannelRequest(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	// Disconnect the channel.
	HandleSAPDisconnect(aSignalHandler);
	}
	
void TL2CAPSigStateOpen::DisconnectRequest(CL2CapSAPSignalHandler& aSignalHandler, TUint8 aId) const
	{
	LOG_FUNC
	// A Disconnect Request has been received and therefore no
	// more data can be sent.  Close the outgoing SDU queue.
	aSignalHandler.SAP()->CloseOutgoingSDUQueue();

	// Call a common helper function to handle the Disconnect Request.
	HandleDisconnectRequest(aSignalHandler, aId);
	}

// "Artificial" state events
void TL2CAPSigStateOpen::Enter(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC

	TBool downgrade = EFalse;
	TInt err = aSignalHandler.ChannelConfig().FecNegotiator().CheckNegotiatedChannelMode(downgrade);
	if (err != KErrNone)
		{
		DisconnectWithError(aSignalHandler, err);
		}
	else if (downgrade)
   		{
		LOG(_L("CL2CapSigStateOpen::Enter doing reconfig"));
		// now get reconfiguring
		aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EWaitConfig));
		aSignalHandler.ConfigureChannelRequest();
		}
	else
		{
		// Remove and park override.
		aSignalHandler.UndoOverrideParkMode();
		
		// Cancel the configuration timer.
		aSignalHandler.CancelTimer();
		
		// Inform the SAP that the channel is now configured and ready to use.
		aSignalHandler.SAP()->ChannelConfigured(aSignalHandler.ChannelConfig(),
		                                        aSignalHandler.Mux(),
	    	                                    aSignalHandler.LocalPort(),
	        	                                aSignalHandler.RemotePort());
		}
	}

TBool TL2CAPSigStateOpen::IsChannelOpen() const
	{
	return ETrue;
	}

TInt TL2CAPSigStateOpen::GetNegotiatedChannelMode(const CL2CapSAPSignalHandler& aSignalHandler, TL2CapChannelMode& aMode) const
	{
	return aSignalHandler.ChannelConfig().GetChannelMode(aMode);
	}

/**************************************************************************/
// Implementation of void TL2CAPSigStateWaitDisconnect
//
// Logical link still exists but is waiting to be disconnected. 
// NB Signalling can still be received, and should be handled.
//
TL2CAPSigStateWaitDisconnect::TL2CAPSigStateWaitDisconnect(const CL2CAPSignalStateFactory& aFactory) 
	: TL2CAPSigState(aFactory) 
	{
	LOG_FUNC
	}

// L2CAP commands received from the peer.
void TL2CAPSigStateWaitDisconnect::DisconnectRequest(CL2CapSAPSignalHandler& aSignalHandler, TUint8 aId) const
	{
	LOG_FUNC
	// We may have a ConfigReq awaiting response. This DisconnectReq may be coming from
	// a State 2 peer that didn't like our ConfigReq and disconnected immediately instead
	// of sending a ConfigRsp. Which means we should flush the ConfigReq so that we don't
	// wait forever. We can delete all commands in WAIT_DISCONNECT anyway:
	// - the only response we really wait for in WAIT_DISCONNECT is the DisconnectRsp,
	//   but since peer has just sent us a DisconnectReq on its own, we know it's closing
	//   anyway and it doesn't matter if it's received our DisconnectReq;
	// - the rest of the commands - Connect, Configure - can be just flushed - they don't
	//   matter once we enter the disconnection path.
	aSignalHandler.FlushAllQueues();

	// Call a common helper function to handle the Disconnect Request.
	HandleDisconnectRequest(aSignalHandler, aId);
	}
	
void TL2CAPSigStateWaitDisconnect::DisconnectResponse(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	aSignalHandler.DrainPendingCommands();
	}

// "Artificial" state events
void TL2CAPSigStateWaitDisconnect::PendingCommandsDrained(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	// If an error condition has been recorded forward the error 
	// to the SAP.
	if(aSignalHandler.SignalHandlerErrorCode() != KErrNone)
		{
		Error(aSignalHandler, aSignalHandler.SignalHandlerErrorCode(), aSignalHandler.SignalHandlerErrorAction());
		}
	else
		{
		// This will delete the signal handler if the SAP has detached.
		aSignalHandler.SetState(iFactory.GetState(CL2CAPSignalStateFactory::EClosed));
		aSignalHandler.SignalHandlerDisconnectedCanClose();
		}
	}
		
void TL2CAPSigStateWaitDisconnect::Enter(CL2CapSAPSignalHandler& aSignalHandler) const
	{
	LOG_FUNC
	// Override park to allow the disconnect to continue.
	aSignalHandler.OverrideParkMode();

	// Cancel the configuration timer.
	aSignalHandler.CancelTimer();
	}

void TL2CAPSigStateWaitDisconnect::CloseChannelRequest(CL2CapSAPSignalHandler& /*aSignalHandler*/) const
	{
	LOG_FUNC
	// Already disconnecting.
	}

TInt TL2CAPSigStateWaitDisconnect::UpdateChannelConfig(CL2CapSAPSignalHandler& /*aSignalHandler*/, const TL2CapConfig& /*aAPIConfig*/) const
	{
	LOG_FUNC
	// Disconnecting - no point.
	return KErrDisconnected;
	}

void TL2CAPSigStateWaitDisconnect::ConnectResponse(CL2CapSAPSignalHandler& /*aSignalHandler*/,
								 HConnectionResponse* /*aConnectionResponse*/) const
	{
	LOG_FUNC
	// Could be here if we disconnect between receiving a pending connect response and
	// a final one. 
	}

void TL2CAPSigStateWaitDisconnect::ConfigRequest(CL2CapSAPSignalHandler& /*aSignalHandler*/,
										  		 HConfigureRequest* /*aConfigRequest*/) const
	{
	LOG_FUNC
	// This often happens under the following scenario:
	// 1. Connection Req/Rsp are exchanged and the local entity sends a Config Req.
	// 2. Local entity sends Config Req first.
	// 3. Remote responds with Config Rsp Unacceptable Parameters which is not acceptable
	//    for us and hence we decide to disconnect, send a DisconnectReq and enter this
	//    state (WAIT_DISCONNECT).
	// 4. Remote legitimately sends ConfigReq before it gets the DisconnectReq and we end
	//    up here.
	//
	// The state tables in the spec say we should respond with a CmdRej Invalid CID but it is
	// unneeded and introduces a bit of unnecessary chaos. Let's ignore this ConfigReq and wait
	// until the remote acknowledges disconnection. If it retransmits the ConfigReq after that,
	// we'll then respond with a CmdRej Invalid CID which will be closer to the truth and won't
	// cause more unnecessary traffic.
	}

void TL2CAPSigStateWaitDisconnect::ConfigResponse(CL2CapSAPSignalHandler& /*aSignalHandler*/,
 	                					   		  HConfigureResponse* /*aConfigResponse*/) const
	{
	LOG_FUNC
	// Could well be in this state - just drop.
	}

void TL2CAPSigStateWaitDisconnect::ConfigureChannelRequest(CL2CapSAPSignalHandler& /*aSignalHandler*/) const
	{
	LOG_FUNC
	// This may be called if an Information Request times out
	// waiting for an Information Response, and we have since
	// moved into the Wait Disconnect state.  Therefore don't
	// panic, however there is nothing else we can do here.
	}

	
/**************************************************************************/
// CL2CAPSignalStateFactory implementation
//
// That which creates the states, and manages state change
//
CL2CAPSignalStateFactory::CL2CAPSignalStateFactory()
	{
	LOG_FUNC
	}

CL2CAPSignalStateFactory::~CL2CAPSignalStateFactory()
	{
	LOG_FUNC
	// Destroy all the state objects
	iSigStates.DeleteAll();
	}
	
CL2CAPSignalStateFactory* CL2CAPSignalStateFactory::NewL()
	{
	LOG_STATIC_FUNC
	CL2CAPSignalStateFactory* factory= new (ELeave) CL2CAPSignalStateFactory();
	CleanupStack::PushL(factory);
	factory->ConstructL();
	CleanupStack::Pop();
	return factory;
	}
	
void CL2CAPSignalStateFactory::ConstructL()
	{
	LOG_FUNC
	// Create all the new states
	iSigStates[EClosed] 			= new (ELeave) TL2CAPSigStateClosed(*this);
	iSigStates[EWaitConnectRsp] 	= new (ELeave) TL2CAPSigStateWaitConnectRsp(*this);
	iSigStates[EWaitConnect] 		= new (ELeave) TL2CAPSigStateWaitConnect(*this);
	iSigStates[EWaitConfig] 		= new (ELeave) TL2CAPSigStateWaitConfig(*this);
	iSigStates[EWaitSendConfig] 	= new (ELeave) TL2CAPSigStateWaitSendConfig(*this);
	iSigStates[EWaitConfigReqRsp]	= new (ELeave) TL2CAPSigStateWaitConfigReqRsp(*this);
	iSigStates[EWaitConfigRsp] 		= new (ELeave) TL2CAPSigStateWaitConfigRsp(*this);
	iSigStates[EWaitConfigReq] 		= new (ELeave) TL2CAPSigStateWaitConfigReq(*this);
	iSigStates[EOpen] 				= new (ELeave) TL2CAPSigStateOpen(*this);
	iSigStates[EWaitDisconnect]		= new (ELeave) TL2CAPSigStateWaitDisconnect(*this);
	
	LOG1(_L("L2CAP SigState Closed = 0x%08x"), iSigStates[EClosed]);
	LOG1(_L("L2CAP SigState WaitConnectRsp = 0x%08x"), iSigStates[EWaitConnectRsp]);
	LOG1(_L("L2CAP SigState WaitConnect = 0x%08x"), iSigStates[EWaitConnect]);
	LOG1(_L("L2CAP SigState WaitConfig = 0x%08x"), iSigStates[EWaitConfig]);
	LOG1(_L("L2CAP SigState WaitSendConfig= 0x%08x"), iSigStates[EWaitSendConfig]);
	LOG1(_L("L2CAP SigState WaitConfigReqRsp = 0x%08x"), iSigStates[EWaitConfigReqRsp]);
	LOG1(_L("L2CAP SigState WaitConfigRsp = 0x%08x"), iSigStates[EWaitConfigRsp]);
	LOG1(_L("L2CAP SigState WaitConfigReq = 0x%08x"), iSigStates[EWaitConfigReq]);
	LOG1(_L("L2CAP SigState Open = 0x%08x"), iSigStates[EOpen]);
	LOG1(_L("L2CAP SigState WaitDisconnect = 0x%08x"), iSigStates[EWaitDisconnect]);
	}	

TL2CAPSigState& CL2CAPSignalStateFactory::GetState(const TSigStates aState) const
	{
	LOG_FUNC
	__ASSERT_DEBUG(aState != EMaxState, Panic(EL2CAPMuxerStateOutOfBounds));
	return *iSigStates[aState];
	}
	
TInt CL2CAPSignalStateFactory::StateIndex(const TL2CAPSigState* aState) const
	{
	LOG_FUNC
	TInt state;
	for (state = 0; state < EMaxState; state++)
		{
		if (iSigStates[state] == aState)
			{
			return state;
			}
		}
	
	return KL2UnknownState;
	}

