diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/l2cap/l2capLinkSignalHandler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/l2cap/l2capLinkSignalHandler.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,552 @@ +// 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: +// + +#include + +#include "l2capLinkSignalHandler.h" + +#include "l2capSAPSignalHandler.h" + +#include "l2capSigPacketEcho.h" +#include "l2capSigPacketInformation.h" +#include "l2capSigPacketConnection.h" +#include "l2capSigPacketConfigure.h" +#include "l2capSigPacketDisconnection.h" +#include "l2capSigPacketCommandReject.h" + +#include "l2capCommand.h" + +#include "l2capMuxController.h" +#include "l2signalmgr.h" + +#include "l2capEntityConfig.h" + +#include "l2util.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_L2CAP); +#endif + +CL2CapLinkSignalHandler* CL2CapLinkSignalHandler::NewL(CL2CAPMux* aMuxer) + { + LOG_STATIC_FUNC + CL2CapLinkSignalHandler* linkSigHandler=new (ELeave) CL2CapLinkSignalHandler(aMuxer); + CleanupStack::PushL(linkSigHandler); + linkSigHandler->ConstructL(); + CleanupStack::Pop(); + return linkSigHandler; + } + +CL2CapLinkSignalHandler::~CL2CapLinkSignalHandler() + { + LOG_FUNC + DeleteCommands(iPendingCommands); + DeleteCommands(iCommandsAwaitingResponse); + } + + + +void CL2CapLinkSignalHandler::ConstructL() + { + LOG_FUNC + } + +// Disable warning WINS 4355: 'this' : used in base member initializer list +// This will not cause any problems in this usage and is preferable to the use of a +// non-owned pointer. +#pragma warning (disable: 4355) +CL2CapLinkSignalHandler::CL2CapLinkSignalHandler(CL2CAPMux* aMuxer) + : CL2CapSignalHandler(aMuxer), + iPeerL2CapEntityConfig(*this), + iSigMTU(KL2MinMTU) + { + LOG_FUNC + } +#pragma warning (default: 4355) + + +TBool CL2CapLinkSignalHandler::HandleConnectionRequest(HConnectionRequest* aConnectionRequest) + { + LOG_FUNC + const TL2CAPPort scid = aConnectionRequest->SourceCID(); + const TInt8 id = aConnectionRequest->ID(); + TBool scidValid = ETrue; + + if(scid < KL2CapDynamicCIDStart) + { + scidValid = EFalse; + } + else + { + CL2CapSAPSignalHandler* listeningSH = iMuxer->MuxController().FindListeningSignalHandler(aConnectionRequest->PSM()); + if(!listeningSH) + { + // Create a Connection Response Command + HL2CapCommand* command = HConnectionResponse::New(id, 0, scid, EConnectPSMNotSupported); + if(command) + { + AddToOutgoingQueue(command); + } + } + else + { + // Does given CID already exist in the queue? + CL2CapSAPSignalHandler* sh = iMuxer->GetSignalHandlerWithRemoteCID(scid); + if (sh) + { + // ... it does: check whether the Connection Request is a retransmission + // (and retransmit the response if so), or a new command (illegal - send CmdRej). + if (!sh->IsDuplicateCommandRequest(aConnectionRequest)) + { + LOG1(_L("Received a new Connection Request for an existing CID: 0x%04x"), scid) + scidValid = EFalse; + } + } + else + { + LOG1(_L("Remote CID (0x%04x) is free"),scid) + // Usual case, remote CID is free. + TInt err = listeningSH->PassiveConnectionRequest(iMuxer->RemoteBTAddr(), aConnectionRequest); + if(err != KErrNone) + { + HL2CapCommand* command = HConnectionResponse::New(id, 0, scid, EConnectPSMNotSupported); + if(command) + { + AddToOutgoingQueue(command); + } + } + // If the command could not be created the connection will fail in the + // signalling state machine. + } + } + } + + if (!scidValid) + { + TL2CAPCommandRejectData reason; + reason.iReason = EInvalidCID; + reason.iLocalEndpoint = 0; + reason.iRemoteEndpoint = scid; + reason.iMTUExceeded = 0; + + HL2CapCommand* command = HCommandReject::New(reason, id); + if(command) + { + AddToOutgoingQueue(command); + } + } + + // Always return true. This is the only signal handler + // that can process this command. + return ETrue; + } + +TInt CL2CapLinkSignalHandler::ConstructEchoRequest(const TDes8* aData, MEchoResponseHandler& aEchoResponseHandler) + { + LOG_FUNC + TInt rerr = KErrNone; + HEchoRequest* command = NULL; + + if(aData) + { + RMBufChain data; + TRAP(rerr, data.CreateL(*aData)); + if(rerr == KErrNone) + { + command = HEchoRequest::New(data, CurrentRTXTimerDuration(HL2CapCommand::KDefaultRTXTimerDuration)); + } + } + else + { + command = HEchoRequest::New(); + } + + if(rerr == KErrNone) + { + if(command) + { + command->SetEchoResponseHandler(aEchoResponseHandler); + AddToOutgoingQueue(command); + } + else + { + rerr = KErrNoMemory; + } + } + return rerr; + } + +void CL2CapLinkSignalHandler::DeregisterOutstandingEchoRequests(MEchoResponseHandler& aEchoResponseHandler) + { + LOG_FUNC + // Find any Echo Request and if the calling reference matches set + // it to NULL. + HL2CapCommand* l2CapCommand; + + TDblQueIter iter(iCommandsAwaitingResponse); + while((l2CapCommand = iter++) != NULL) + { + if(l2CapCommand->Code() == EEchoRequest) + { + HEchoRequest* request = static_cast(l2CapCommand); + if(request->EchoResponseHandler() == &aEchoResponseHandler) + { + // Found a matching request - there can only be one so stop searching. + request->RemoveEchoResponseHandler(); + break; + } + } + } + } + +TBool CL2CapLinkSignalHandler::HandleEchoResponse(HEchoResponse* aEchoResponse) + { + LOG_FUNC + // Check that the Echo Response has been requested. + HL2CapCommand* requestCommand = FindMatchingOutstandingRequest(EEchoRequest, aEchoResponse->ID()); + if(requestCommand) + { + HEchoRequest* request = static_cast(requestCommand); + if(request->EchoResponseHandler()) + { + RMBufChain data; + HBufC8* echoDataBuf = NULL; + if(aEchoResponse->GetData(data) == KErrNone) + { + echoDataBuf = HBufC8::New(data.Length()); + if(echoDataBuf) + { + const TDes8& bufc = echoDataBuf->Des(); + TDes8& echoData = const_cast(bufc); + echoData.SetMax(); + data.CopyOut(echoData); + } + data.Free(); + } + + iMuxer->EchoResponseReceived(echoDataBuf, *request->EchoResponseHandler()); + } + // Delete the request. + delete requestCommand; + } + // If the response does not have an outstanding request drop + // the command silently. Always return true, this command + // can only be for the link signal handler. + return ETrue; + } + +TBool CL2CapLinkSignalHandler::HandleEchoRequest(HEchoRequest* aEchoRequest) //Incoming + { + LOG_FUNC + //1. Create a Echo Response Command + HL2CapCommand* command = HEchoResponse::New(aEchoRequest->ID()); + + //2. Add to the list of outgoing commands + if(command) + { + AddToOutgoingQueue(command); + } + return ETrue; + } + +TInt CL2CapLinkSignalHandler::ConstructInformationRequest(TInfoType aInfoType) + { + LOG_FUNC + TInt rerr = KErrNone; + + //1. Create a ConnectionRequest Command + HL2CapCommand* command = HInformationRequest::New(aInfoType, CurrentRTXTimerDuration(HL2CapCommand::KDefaultRTXTimerDuration)); + if(command) + { + AddToOutgoingQueue(command); + } + else + { + rerr = KErrNoMemory; + } + return rerr; + } + +TBool CL2CapLinkSignalHandler::HandleInformationRequest(HInformationRequest* aInformationRequest) + { + LOG_FUNC + // The peer is requesting the L2CAP entities capabilities. + switch(aInformationRequest->InfoType()) + { + case EExtendedFeaturesSupported: + { + TUint32 extendedFeatures = KL2CAPExtendedFeaturesSupported; + + HL2CapCommand* command = HInformationResponse::New(EExtendedFeaturesSupported, ESuccess, aInformationRequest->ID(), extendedFeatures); + if(command) + { + // Add to the list of outgoing commands + AddToOutgoingQueue(command); + } + // If the command could not be created the connection will fail in the + // signalling state machine. + break; + } + + // If the info requested is Connectionless MTU or a info type that + // is not recognised, return a response with result code 'not supported'. + case EConnectionlessMTU: + default: + { + //Return InfoType and Not supported + HL2CapCommand* command = HInformationResponse::New(aInformationRequest->InfoType(), + ENotsupported, + aInformationRequest->ID()); + if(command) + { + // Add to the list of outgoing commands + AddToOutgoingQueue(command); + } + // If the command could not be created the connection will fail in the + // signalling state machine. + break; + } + }; + + //This should not occur as it should be handled by derived classes. + return(ETrue); + } + +TBool CL2CapLinkSignalHandler::HandleInformationResponse(HInformationResponse* aInformationResponse) + { + LOG_FUNC + // Check that the Information Response has been requested. + HL2CapCommand* requestCommand = FindMatchingOutstandingRequest(EInformationRequest, aInformationResponse->ID()); + if(requestCommand) + { + HInformationRequest* req = static_cast(requestCommand); + // The Info Type in the response should match that in the + // request. + if(req->InfoType() == aInformationResponse->InfoType()) + { + switch(aInformationResponse->InfoType()) + { + case EExtendedFeaturesSupported: + if(aInformationResponse->Result() == ESuccess) + { + iPeerL2CapEntityConfig.UpdatePeerL2CapSupportedFeatures(aInformationResponse->RemoteExtendedFeatureMask()); + } + else + { + // The result was a failure. Set the supported + // features to the default of not supported. + iPeerL2CapEntityConfig.UpdatePeerL2CapSupportedFeatures(TL2CapEntityInfo()); + } + iMuxer->L2CapEntityConfigUpdated(); + break; + + default: + break; + }; + } + else + { + // The request and response info types don't match. If the request + // was for extended features, [for the sake of interop] assume that + // no extended features are supported by the peer device. + if(req->InfoType() == EExtendedFeaturesSupported) + { + iPeerL2CapEntityConfig.UpdatePeerL2CapSupportedFeatures(TL2CapEntityInfo()); + iMuxer->L2CapEntityConfigUpdated(); + } + } + + // Delete the request. + delete requestCommand; + } + // If the response does not have an outstanding request drop + // the command silently. Always return true, this command + // can only be for the link signal handler. + return ETrue; + } + +TBool CL2CapLinkSignalHandler::HandleCommandReject(HCommandReject* aCommandReject) + { + LOG_FUNC + // Process the incoming command reject data + TL2CAPCommandRejectData rejectData; + + if (aCommandReject->RejectData(rejectData) != KErrNone) + { + LOG(_L("CL2CapLinkSignalHandler::HandleCommandReject - reject data could not be parsed")); + // Couldn't parse the reject data, but we've got no-one to + // pass handling this on to, so we have to say we've processed + // it. + return ETrue; + } + + LOG1(_L("CL2CapLinkSignalHandler::HandleCommandReject - reject data: reason %d"), rejectData.iReason); + + switch(rejectData.iReason) + { + case ECommandNotUnderstood: + { + // The peer does not recognise a command we have sent. + // Check if the outstanding request is causing the issue. + HL2CapCommand* requestCommand = FindMatchingOutstandingRequest(EMatchAnyL2CAPRequest, aCommandReject->ID()); + if(requestCommand) + { + // This is a work around to allow the new stack to interop with + // stacks that send Command Reject in response to Information Request. + if(requestCommand->Code() == EInformationRequest) + { + // Set the supported features to the default of not supported. + iPeerL2CapEntityConfig.UpdatePeerL2CapSupportedFeatures(TL2CapEntityInfo()); + iMuxer->L2CapEntityConfigUpdated(); + } + delete requestCommand; + } + } + break; + + case EMTUExceeded: + iSigMTU = Max(rejectData.iMTUExceeded, KL2MinMTU); + break; + + case EInvalidCID: + // This handles a corner case where a Disconnect Request command + // retransmission crosses over with the Response for the initial + // Disconnect Request. The peer will correctly send a Command Reject + // for the second Disconnect Request. This should be ignored. + break; + + default: + break; + }; + + return ETrue; + } + +TBool CL2CapLinkSignalHandler::HandleInvalidCommand(HInvalidCommand* aInvalidCommand) + { + LOG_FUNC + TL2CAPCommandRejectData reason; + reason.iReason = ECommandNotUnderstood; + // Other reject data fields are not used for Command Not Understood + reason.iMTUExceeded = 0; + reason.iLocalEndpoint = 0; + reason.iRemoteEndpoint = 0; + + HL2CapCommand* command = HCommandReject::New(reason, aInvalidCommand->ID()); + if(command) + { + AddToOutgoingQueue(command); + } + + // The link signal handler always services invalid commands. + return ETrue; + } + + +TBool CL2CapLinkSignalHandler::HandleConnectionResponse(HConnectionResponse* /*aConnectionResponse*/) + { + LOG_FUNC + // This command has not been handled by any of the SAP + // signal handlers. It's a response so just drop it. + return ETrue; + } + +TBool CL2CapLinkSignalHandler::HandleConfigureRequest(HConfigureRequest* aConfigRequest) + { + LOG_FUNC + // This command has not been handled by any of the SAP + // signal handlers. Send a Command Reject. + SendInvalidCIDCommandReject(aConfigRequest->ID(), + 0, + aConfigRequest->DestinationCID()); + + return ETrue; + } + +TBool CL2CapLinkSignalHandler::HandleConfigureResponse(HConfigureResponse* /*aConfigResponse*/) + { + LOG_FUNC + // This command has not been handled by any of the SAP + // signal handlers. It's a response so just drop it. + return ETrue; + } + +TBool CL2CapLinkSignalHandler::HandleDisconnectRequest(HDisconnectRequest* aDisconnectRequest) + { + LOG_FUNC + // This command has not been handled by any of the SAP + // signal handlers. Send a Command Reject. + SendInvalidCIDCommandReject(aDisconnectRequest->ID(), + aDisconnectRequest->SourceCID(), + aDisconnectRequest->DestinationCID()); + + return ETrue; + } + +TBool CL2CapLinkSignalHandler::HandleDisconnectResponse(HDisconnectResponse* /*aDisconnectResponse*/) + { + LOG_FUNC + // This command has not been handled by any of the SAP + // signal handlers. It's a response so just drop it. + return ETrue; + } + +void CL2CapLinkSignalHandler::PendingCommandsDrained() + { + LOG_FUNC + // No action is required here. + } + +// HIncomingSAPSigPDUHandler Interface Definition. +void CL2CapLinkSignalHandler::LinkUp() + { + LOG_FUNC + // No action required from the LinkSignalHandler when the link is established. + } + +void CL2CapLinkSignalHandler::Error(TInt /*aErrorCode*/, MSocketNotify::TOperationBitmasks /*aErrorAction*/) + { + LOG_FUNC + HandleLinkError(); + } + +void CL2CapLinkSignalHandler::CommandResponseFailure(HL2CapCommand* aCommand) + { + LOG_FUNC + switch(aCommand->Code()) + { + case EInformationRequest: + //For IOP We set extended feature to basic if there is no reply for info request. + iPeerL2CapEntityConfig.UpdatePeerL2CapSupportedFeatures(TL2CapEntityInfo()); + iMuxer->L2CapEntityConfigUpdated(); + break; + + default: + iMuxer->ErrorAllSignalHandlers(KErrL2CAPRequestTimeout); + break; + } + + delete aCommand; + } + +void CL2CapSignalHandler::PendingCommandsDrained() + { + LOG_FUNC + // Currently no action is required by the link signal handler. + } + + + +