diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/l2cap/l2capSigStates.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/l2cap/l2capSigStates.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,1289 @@ +// 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 + +#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(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; + } +