bluetooth/btstack/l2cap/L2CapChannelConfig.cpp
changeset 0 29b1cd4cb562
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/l2cap/L2CapChannelConfig.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,736 @@
+// Copyright (c) 1999-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:
+// Manages the negotiation of L2Cap configuration parameters.
+// 
+//
+
+#include <bluetooth/logger.h>
+#include <es_prot.h>
+#include "l2util.h"
+#include "L2CapChannelConfig.h"
+#include "l2capEntityConfig.h"
+#include "l2capSigPacketConfigure.h"
+
+#ifndef __UNITTest__
+    #include "l2constants.h"
+	#include "l2capSAPSignalHandler.h"
+#else
+	#include "cl2capconfigproxy.h"
+	#ifdef _DEBUG
+	#include "l2capDebugControlInterface.h"
+	#endif
+#endif
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_L2CAP);
+#endif
+
+
+// A helper for the implementation of a workaround for a bug in the negotiation algorithm
+// of some remotes.
+NONSHARABLE_STRUCT(SL2CapChannelIncomingConfigOptionSnapshot)
+    {
+    SL2CapChannelIncomingConfigOptionSnapshot(const CL2CapChannelConfig& aChannelConfig);
+    TBool operator==(const SL2CapChannelIncomingConfigOptionSnapshot& aThat);
+
+    TMTUOption                          iMtu;
+    TL2CapConfigurationOptionGroupBase::TOptionConfigStatus iMtuStatus;
+
+    TFlushTimeoutDurationOption         iFlushTimeout;
+    TL2CapConfigurationOptionGroupBase::TOptionConfigStatus iFlushTimeoutStatus;
+
+    TRetransmissionAndFlowControlOption iFec;
+    TL2CapConfigurationOptionGroupBase::TOptionConfigStatus iFecStatus;
+
+    TQualityOfServiceOption             iQos;
+    TL2CapConfigurationOptionGroupBase::TOptionConfigStatus iQosStatus;
+    };
+
+
+/*static*/ CL2CapChannelConfig* CL2CapChannelConfig::NewL(CL2CapSAPSignalHandler& aSAPSignalHandler)
+	{
+	LOG_STATIC_FUNC
+	CL2CapChannelConfig* self = new(ELeave) CL2CapChannelConfig(aSAPSignalHandler);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+CL2CapChannelConfig::~CL2CapChannelConfig()
+	{
+	LOG_FUNC
+	delete iIncomingMTU;
+	delete iOutgoingMTU;
+	delete iIncomingFlushTimeoutDuration;
+	delete iOutgoingFlushTimeoutDuration;
+	delete iIncomingQOS;
+	delete iOutgoingQOS;
+	}
+	
+void CL2CapChannelConfig::ConstructL()
+	{
+	LOG_FUNC
+
+	// Initialise the configuration options.
+	iIncomingMTU = new (ELeave)TL2CapConfigurationOptionGroup<TMTUOption>
+	           (TMTUOption::EPreferredValue,
+				TMTUOption::EAbsoluteMinimumValue,
+				TMTUOption::EPreferredValue,
+				TMTUOption::ESpecDefaultValue,
+				TL2CapConfigurationOptionGroupBase::ENegotiateToMinimum);
+	iOutgoingMTU = new (ELeave)TL2CapConfigurationOptionGroup<TMTUOption>
+	           (TMTUOption::EMaximumValue,
+				TMTUOption::ESpecMinimumValue,
+			    TMTUOption::EPreferredValue,
+			    TMTUOption::ESpecDefaultValue,
+				TL2CapConfigurationOptionGroupBase::ENegotiateToMinimum);
+
+	// Flush timeout is settable in the SAP but not implemented so we're not looking at the SAP value.
+	iIncomingFlushTimeoutDuration = new (ELeave)TL2CapConfigurationOptionGroup<TFlushTimeoutDurationOption>
+	           (TFlushTimeoutDurationOption::EMaximumValue,
+			    TFlushTimeoutDurationOption::ESpecMinimumValue,
+	            TFlushTimeoutDurationOption::ESpecDefaultValue,
+	            TFlushTimeoutDurationOption::ESpecDefaultValue,
+				TL2CapConfigurationOptionGroupBase::ENegotiated);
+	iOutgoingFlushTimeoutDuration = new (ELeave)TL2CapConfigurationOptionGroup<TFlushTimeoutDurationOption>
+               (TFlushTimeoutDurationOption::EMaximumValue,
+				TFlushTimeoutDurationOption::EMaximumValue,
+				TFlushTimeoutDurationOption::ESpecDefaultValue,
+				TFlushTimeoutDurationOption::ESpecDefaultValue,
+				TL2CapConfigurationOptionGroupBase::ENegotiated);
+
+	// QOS isn't implemented nor settable in the SAP.
+	iIncomingQOS = new (ELeave)TL2CapConfigurationOptionGroup<TQualityOfServiceOption>
+	           (TQualityOfServiceOption::EMaximumValue,
+				TQualityOfServiceOption::ESpecMinimumValue,
+				TQualityOfServiceOption::ESpecDefaultValue,
+				TQualityOfServiceOption::ESpecDefaultValue,
+				TL2CapConfigurationOptionGroupBase::ENegotiated);
+	iOutgoingQOS = new (ELeave)TL2CapConfigurationOptionGroup<TQualityOfServiceOption>
+	           (TQualityOfServiceOption::EMaximumValue,
+				TQualityOfServiceOption::ESpecMinimumValue,
+				TQualityOfServiceOption::ESpecDefaultValue,
+				TQualityOfServiceOption::ESpecDefaultValue,
+				TL2CapConfigurationOptionGroupBase::ENegotiated);
+
+	iRequestedChannelReliability		= TL2CapConfig::EReliableChannel;
+	iLegacyModesDisallowed				= EFalse;
+	iRequestedFlushTimeout				= TL2CapConfig::EDefaultDataObsolescenceTimeout;
+	iRequestedRetransmissionTimeout		= TL2CapConfig::EDefaultRetransmission;
+
+	LOG2(_L("CL2CapChannelConfig.iFecNegotiator = %X.%X"), (TAny*)this, (TAny*)&iFecNegotiator)
+	}
+
+
+#ifndef __UNITTest__
+CL2CapChannelConfig::CL2CapChannelConfig(CL2CapSAPSignalHandler& aSAPSignalHandler)
+ : iSAPSignalHandler(aSAPSignalHandler)
+#else
+CL2CapChannelConfig::CL2CapChannelConfig(CL2CapConfigProxy& aSAPSignalHandler)
+ : iSAPSignalHandler(aSAPSignalHandler)
+#endif	
+	{
+	LOG_FUNC
+	}
+
+void CL2CapChannelConfig::CloneChannelConfig(const CL2CapChannelConfig& aCopy)
+	{
+	LOG_FUNC
+
+	// Copy all aspects of this class that should be cloned from the listening SAP.
+	*iIncomingMTU 	= *(aCopy.iIncomingMTU);
+	*iOutgoingMTU 	= *(aCopy.iOutgoingMTU);
+	*iIncomingFlushTimeoutDuration 	= *(aCopy.iIncomingFlushTimeoutDuration);
+	*iOutgoingFlushTimeoutDuration 	= *(aCopy.iOutgoingFlushTimeoutDuration);
+	*iOutgoingQOS 	= *(aCopy.iOutgoingQOS);
+	*iIncomingQOS 	= *(aCopy.iIncomingQOS);
+	
+	iRequestedChannelReliability	= aCopy.iRequestedChannelReliability;
+	iLegacyModesDisallowed			= aCopy.iLegacyModesDisallowed;
+	iRequestedFlushTimeout			= aCopy.iRequestedFlushTimeout;
+	iRequestedRetransmissionTimeout	= aCopy.iRequestedRetransmissionTimeout;
+	// iFecNegotiator gets updated using the iRequested... field values on OpenChannelRequest. 
+	}
+
+
+TInt CL2CapChannelConfig::HandleConfigRequest(HConfigureRequest& aConfigRequest, RMBufChain& aUnknownOptions)
+	{
+	LOG_FUNC
+	// Pile these options on top of the previously received ones in case it's
+	// a continuation packet. They're all processed once we've received the
+	// whole transaction, in ConfigRequestComplete().
+	return aConfigRequest.ParseOptions(iReceivedConfigRequestOptions, aUnknownOptions);
+	}
+
+TInt CL2CapChannelConfig::HandleConfigResponse(HConfigureResponse& aConfigResponse, RMBufChain& aUnknownOptions)
+	{
+	LOG_FUNC
+    // Note: If it's a positive response (Success), then any parameters not specified by
+    // the peer are assumed to be as requested.
+    // If it's negative then it only includes suggested values for rejected parameters
+    // and the ones not rejected will have to be re-requested in following message exchanges.
+
+    TL2CapConfigParamOptions options;
+    TInt err = aConfigResponse.ParseOptions(options, aUnknownOptions);
+    if (err)
+        {
+        return err;
+        }
+
+    const TBool isUnacceptableParameters = (aConfigResponse.Results() == EConfigUnacceptableParams);
+    const TBool isSuccess = (aConfigResponse.Results() == EConfigSuccess);
+
+    // Prelude to the negotiation problem described at the bottom:
+    // snapshot current option status for comparison after the message is processed.
+    SL2CapChannelIncomingConfigOptionSnapshot optionStatusBeforeProcessing(*this);
+    
+    // MTU
+    if (options.IsMtuSet())
+        {
+        if (isUnacceptableParameters)
+            {
+            iIncomingMTU->PeerRejectsOption(options.Mtu());
+            }
+        else
+            {
+            iIncomingMTU->PeerAcceptsOption(options.Mtu());
+            }
+        }
+    else if (isSuccess)
+        {
+        // A positive response acknowledges all options - if one is not
+        // included then it means that the peer agrees with our proposal
+        // (or the default, or the last accepted value).
+        iIncomingMTU->PeerAcceptsOption();
+        }
+    // [else]: if an option is not explicitly mentioned and it's not
+    // a positive response, then the option's status remains untouched.
+
+    // Flush Timeout Duration
+    if (options.IsFlushTimeoutSet())
+        {
+        if (isUnacceptableParameters)
+            {
+            iOutgoingFlushTimeoutDuration->PeerRejectsOption(options.FlushTimeout());
+            }
+        else
+            {
+            // Note: we're ignoring the explicitly set value as it can only
+            // be what we sent, otherwise it's a broken/malicious peer and we don't
+            // want to use the value.
+            iOutgoingFlushTimeoutDuration->PeerAcceptsOption();
+            }
+        }
+    else if (isSuccess)
+        {
+        iOutgoingFlushTimeoutDuration->PeerAcceptsOption();
+        }
+
+    // Quality Of Service
+    if (options.IsQosSet())
+        {
+        if (isUnacceptableParameters)
+            {
+            iOutgoingQOS->PeerRejectsOption(options.Qos());
+            }
+        else
+            {
+            iOutgoingQOS->PeerAcceptsOption(options.Qos());
+            }
+        }
+    else if (isSuccess)
+        {
+        iOutgoingQOS->PeerAcceptsOption();
+        }
+
+    // Retransmission And Flow Control
+    if (options.IsFecSet())
+        {
+        if (isUnacceptableParameters)
+            {
+            err = iFecNegotiator.PeerRejectsOption(options.Fec());
+            }
+        else
+            {
+            err = iFecNegotiator.PeerAcceptsOption(options.Fec());
+            }
+        }
+    else if (isSuccess)
+        {
+        iFecNegotiator.PeerAcceptsOption();
+        }
+
+    if (isUnacceptableParameters && !err)
+        {
+        // negotiation algorithm bug workaround.
+
+        // The problem this tries to work around is as follows:
+        // S - local Symbian entity, C - remote host
+        // SDP connection is being opened, C supports (E)RTM so we try to negotiate it:
+        // 1. S sends ConfigReq[FEC=ERTM, MTU=n].
+        // 2. C responds with ConfigRsp(UnacceptableParameters)[MTU=n].
+        // 3. S obeys the reject, thinking C is rejecting the MTU (note the MTU value proposed
+        //    by C is the same value we sent in (1));
+        //    S sends a new ConfigReq, including the proposed MTU and the FEC which is still pending
+        //    a response, thus creating a ConfigReq identical to the one from (1).
+        // 4. C responds like in (2).
+        // 5. S reacts like in (3).
+        // -- we got into an infinite loop --
+        // The whole situation is due to C in (2) responding with ConfigRsp(UnacceptableParameters)[MTU=n]
+        // while what it really means is ConfigRsp(UnacceptableParameters)[FEC=Basic].
+        // This was found at UPF33 - June 2009 and they fixed their code then, but there still
+        // might be a lot of devices out doing the wrong thing.
+        //
+        // The workaround is to snapshot option status before and after the peer's
+        // Config Response is processed and compare it. If it's exactly the same then
+        // we're in a stalemate, so we cross our fingers and then drop our FEC proposal
+        // to Basic. Remote accepts the ConfigReq with Basic mode and the whole palaver is over.
+        // (the status snapshot includes the Preferred value and the config status of all
+        // options).
+        SL2CapChannelIncomingConfigOptionSnapshot optionStatusAfterProcessing(*this);
+        if (optionStatusBeforeProcessing == optionStatusAfterProcessing)
+            {
+            err = iFecNegotiator.PeerRejectsOption(TRetransmissionAndFlowControlOption());
+            // Note: we'll also fall here if an option has started off with status Failed and
+            // the peer rejected our preferred value, suggesting a value that's again unacceptable.
+            // Example: user requests incoming MTU = 500 which is less than 672, which means
+            // the default peer value of 672 is unacceptable and so the option's status is Failed
+            // by default. If the value of 500 is then rejected by the peer, and it suggests
+            // something else, be it 672 or anything > 500, the status and the actual value will
+            // remain Failed and 500 - so if the status of the other options hasn't changed,
+            // we'll fall in here. It's harmless though because it means we've failed to agree
+            // and will disconnect, so it doesn't matter whether the FEC value is changed.
+            }
+        }
+    return err;
+    }
+
+TInt CL2CapChannelConfig::ConfigRequestComplete(TBool aConfigRequestSent, TConfigResponseResult& aResult)
+    {
+    LOG_FUNC
+    TInt err = KErrNone;
+
+	// Note that this method applies a different interpretation of the spec than
+	// HandleConfigResponse. This is due to an ambiguity in the spec which yields two
+	// possible interpretations:
+	// (Interpretation A) When handling a Config Response, we assume that:
+	// (1) if it's positive, then it acknowledges all the options we've sent in Config Request;
+	// (2) if it's negative, then it only rejects the options that it includes, and neither
+	//     acknowledges nor rejects the ones that are not included.
+	// (Interpretation B) When responding to a Config Request, we effectively assume
+	// the following:
+	// (1) is the same,
+	// (2) if it's negative, then it only rejects the options that it includes and acknowledges
+	//     the ones that are not included.
+	// There are implementations using both interpetations, so we do different things depending
+	// on negotiation direction for maximum interop - we assume that interpretation A taken in
+	// HandleConfigResponse makes more sense, but to be interoperable with both algorithms, we
+	// respond to a ConfigRequest using interpretation B.
+	// Here is why, by example:
+	// 1. Peer sends us Config Request with MTU and FEC;
+	// 2. We reject the MTU but FEC is ok, so we send a Config Response [Unacceptable Parameters](MTU);
+	//    According to interpretation B we also accept the FEC, while interpretation A
+	//    means we ignore it and Peer will include it again in the following ConfigRequest.
+	// 3a. If Peer implements interpretation B, then it assumes we've accepted the FEC
+	//    and considers the option value to have been agreed, so it will not include it
+	//    in the next Config Request along with MTU. Fortunately, we use the same
+	//    interpretation here so we're in sync.
+	// 3b. If Peer implements interpretation A, then it will include the FEC in the next
+	//    Config Request anyway because it's still outstanding, so what we do to the option
+	//    now doesn't matter. Theoretically Peer could suddenly change its mind and forego
+	//    negotiating FEC, but obviously unless it's a human no implemntation will do it :)
+
+    if (iReceivedConfigRequestOptions.IsMtuSet())
+        {
+        iOutgoingMTU->PeerRequestsOption(iReceivedConfigRequestOptions.Mtu());
+        }
+    else
+        {
+        iOutgoingMTU->PeerRequestsLastAcceptedValue();
+        }
+
+    if (iReceivedConfigRequestOptions.IsFlushTimeoutSet())
+        {
+        iIncomingFlushTimeoutDuration->PeerRequestsOption(iReceivedConfigRequestOptions.FlushTimeout());
+        }
+    else
+        {
+        iIncomingFlushTimeoutDuration->PeerRequestsLastAcceptedValue();
+        }
+
+    if (iReceivedConfigRequestOptions.IsQosSet())
+        {
+        iIncomingQOS->PeerRequestsOption(iReceivedConfigRequestOptions.Qos());
+        }
+    else
+        {
+        iIncomingQOS->PeerRequestsLastAcceptedValue();
+        }
+
+    if (iReceivedConfigRequestOptions.IsFecSet())
+        {
+        err = iFecNegotiator.PeerRequestsOption(iReceivedConfigRequestOptions.Fec());
+        }
+    else
+        {
+        err = iFecNegotiator.PeerRequestsLastAcceptedValue();
+        if (err == KErrNone)
+            {
+            // Check if the outgoing FEC connection needs to be downgraded to basic mode.
+            // NB This ONLY makes sense if our config request has not already been queued...
+            //    ...otherwise it would break the config algorithm.
+            if(!aConfigRequestSent)
+                {
+                FecNegotiator().DowngradeIncomingToBasicIfOutgoingIsBasic();
+                }
+            }
+        }
+
+    // Now reset the Config Request option store in case there's a next negotiation round.
+    iReceivedConfigRequestOptions = TL2CapConfigParamOptions();
+    
+    /*
+    For a config request we know the peer values - so the peer and the actual values should
+    either be the same, or the peer should be outside our limits (See the implementation of
+    TL2CapConfigurationOptionGroupBase::TOptionConfigStatus.)
+    If this __ASSERT_DEBUG is hit, it is likely that the API has been upgraded or
+    the default values have been upgraded in a way which breaks the configuration logic.
+    */
+    __ASSERT_DEBUG(iOutgoingMTU->ConfigOptionStatus() != TL2CapConfigurationOptionGroupBase::EOptionConfigOutstanding &&
+                   iIncomingFlushTimeoutDuration->ConfigOptionStatus() != TL2CapConfigurationOptionGroupBase::EOptionConfigOutstanding &&
+                   iIncomingQOS->ConfigOptionStatus() != TL2CapConfigurationOptionGroupBase::EOptionConfigOutstanding &&
+                   iFecNegotiator.OutgoingConfigOptionStatus() != TL2CapConfigurationOptionGroupBase::EOptionConfigOutstanding,
+                   Panic(EL2CAPInvalidConfigOptionState));
+
+    // Check the response code required for the subsequent Config Response.
+    if(iOutgoingMTU->ConfigOptionStatus() == TL2CapConfigurationOptionGroupBase::EOptionConfigFailed ||
+       iIncomingFlushTimeoutDuration->ConfigOptionStatus() == TL2CapConfigurationOptionGroupBase::EOptionConfigFailed ||
+       iIncomingQOS->ConfigOptionStatus() == TL2CapConfigurationOptionGroupBase::EOptionConfigFailed ||
+       iFecNegotiator.OutgoingConfigOptionStatus() == TL2CapConfigurationOptionGroupBase::EOptionConfigFailed)
+        {
+        aResult = EConfigUnacceptableParams;
+        }
+    else
+        {
+        aResult = EConfigSuccess;
+        }
+
+    return err;
+    }
+
+/**	
+	Process the entity information received from peer. Used to make sure that we don't try
+	and negotiate a configuration that the peer is not capable of negotiating.  The predominant use
+	case is to make sure that we do not negotiate FEC parameters with a device that is only capable
+	of basic mode.
+**/
+TInt CL2CapChannelConfig::UpdateLocalConfigWithEntityCapabilities()
+	{
+	LOG_FUNC
+	TL2CapEntityInfo config; 
+	TInt err = iL2CapEntityConfig->PeerL2CapSupportedFeatures(config);
+	if (err == KErrNone)
+		{
+		// Signalling state machine is responsible for not calling us until we've got
+		// peer entity information.
+		__ASSERT_DEBUG(config.LinkInfoState() == EL2CapEntityInfoDefined,
+					   Panic(EL2CAPUpdateLocalConfigCalledWithoutPeerEntityConfig));
+		// Based on knowledge about the peer's extended information, set up mode negotiator.
+		TBool peerSupportsRequestedConfig = GenerateFecOptions(config);
+		if (!peerSupportsRequestedConfig)
+			{
+			err = KErrL2CAPPeerDoesNotSupportRequestedChannelMode;
+			}
+		}
+	return err;
+	}
+
+/**	
+	Handles configurable options provided in TL2CapConfig by user APIs.
+	aOnTheAirConfigRequired will be set to True if the specified parameters require the
+	L2CAP reconfiguration process, False otherwise.
+**/
+TInt CL2CapChannelConfig::UpdateConfigAPIChange(const TL2CapConfig& aApiConfig, TBool& aOnTheAirReconfigRequired)
+	{
+	LOG_FUNC
+	TInt err = KErrNone;
+
+	aOnTheAirReconfigRequired = EFalse;
+
+	err = UpdateReliability(aApiConfig);
+	if (err == KErrNone)
+		{
+		err = UpdateMtuMru(aApiConfig, aOnTheAirReconfigRequired);
+		}
+
+	// GenerateFecOptions() will be called on OpenChannelRequest to transform these options
+	// into concrete FEC option values groups.
+
+	return err;
+	}
+
+
+/**	
+	Returns true if all the configuration elements included in a incoming configuration response
+	have been successfully configured
+**/
+CL2CapChannelConfig::TChannelConfigStatus CL2CapChannelConfig::LocalConfigurationStatus() const	
+	{
+	LOG_FUNC
+	TChannelConfigStatus status = EChannelConfigOutstanding;
+	
+	if((iIncomingMTU->ConfigOptionStatus() == TL2CapConfigurationOptionGroupBase::EOptionConfigComplete) &&
+	   (iOutgoingFlushTimeoutDuration->ConfigOptionStatus() == TL2CapConfigurationOptionGroupBase::EOptionConfigComplete) &&
+	   (iOutgoingQOS->ConfigOptionStatus() == TL2CapConfigurationOptionGroupBase::EOptionConfigComplete) &&
+	   (iFecNegotiator.IncomingConfigOptionStatus() == TL2CapConfigurationOptionGroupBase::EOptionConfigComplete))
+		{
+		status = EChannelConfigComplete;
+		}
+	else
+		{
+		if((iIncomingMTU->ConfigOptionStatus() == TL2CapConfigurationOptionGroupBase::EOptionConfigFailed) ||
+		   (iOutgoingFlushTimeoutDuration->ConfigOptionStatus() == TL2CapConfigurationOptionGroupBase::EOptionConfigFailed) ||
+		   (iOutgoingQOS->ConfigOptionStatus() == TL2CapConfigurationOptionGroupBase::EOptionConfigFailed) ||
+		   (iFecNegotiator.IncomingConfigOptionStatus() == TL2CapConfigurationOptionGroupBase::EOptionConfigFailed))
+			{
+			status = EChannelConfigFailed;
+			}
+		}
+	return status;
+	}
+
+/**	
+	Public function that registers the entity channel configuration with this channel configuration
+**/
+void CL2CapChannelConfig::RegisterL2CapEntityConfig(TL2CAPEntityConfig& aL2CapEntityConfig)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(!iL2CapEntityConfig, Panic(EL2CAPAttemptToRegisterEntityConfigTwice));
+	iL2CapEntityConfig = &aL2CapEntityConfig;
+	}	
+
+/**
+	Channel configuration is being taken down. 
+**/
+void CL2CapChannelConfig::DetachChannelConfig()
+	{
+	LOG_FUNC
+	// If the channel config has registered with the Entity
+	// config remove the association.
+	iL2CapEntityConfig = NULL;
+	}
+
+// Translates abstract 'Channel Reliability' etc. into concrete option groups.
+// Called just-in-time on OpenChannelRequest, when the L2CAP connection is being made.
+// Should only be called once in the lifetime of a connection.
+TBool CL2CapChannelConfig::GenerateFecOptions(const TL2CapEntityInfo& aPeerEntityConfig)
+	{
+	LOG_FUNC
+
+	__ASSERT_DEBUG(aPeerEntityConfig.LinkInfoState() == EL2CapEntityInfoDefined,
+				   Panic(EL2CAPFecConfigAttemptWithoutPeerInfo));
+
+	// We may end up falling back from Streaming to ERTM, so set up a sensible MaxTransmit value
+	// for both Reliable and Unreliable case.
+	TUint8 maxTransmit = TRetransmissionAndFlowControlOption::KMaxValidEnhancedNumberTransmit;
+	// TL2CapConfig::EDefaultRetransmission is 0xffff, which is a special value in the API that
+	// means 'maximum possible MaxTransmit'. Note that we assume here that the maximum MaxTransmit
+	// is 0, which is only true for enhanced modes. If a legacy mode is negotiated, FECNegotiator
+	// will change the 0 to 255.
+	if (iRequestedRetransmissionTimeout != TL2CapConfig::EDefaultRetransmission)
+		{
+		maxTransmit = static_cast<TUint8>(iRequestedRetransmissionTimeout / TRetransmissionAndFlowControlOption::KDefaultRetransmissionTimeout);
+		}
+
+	TBool peerSupportsRequestedConfig = iFecNegotiator.Setup(iRequestedChannelReliability, iLegacyModesDisallowed, aPeerEntityConfig, maxTransmit);
+	if (peerSupportsRequestedConfig)
+		{
+		// Set the outgoing flush parameters NOT to allow flushing.
+		// Note: the TL2CapConfig API does allow setting of the flush timeout, and we do have some
+		// support for it across the board, but that code is unused. This is the place where
+		// iRequestedFlushTimeout gets ignored and if ever start supporting flushing, then we should
+		// take it into account and set the flushTimeoutDuration accordingly if unreliable channel
+		// is requested.
+		TFlushTimeoutDurationOption flushTimeoutDuration(TFlushTimeoutDurationOption::EMaximumValue);
+		iOutgoingFlushTimeoutDuration->SetRequiredValue(flushTimeoutDuration, flushTimeoutDuration, flushTimeoutDuration);
+		}
+
+	return peerSupportsRequestedConfig;
+	}
+
+TInt CL2CapChannelConfig::UpdateReliability(const TL2CapConfig& aApiConfig)
+	{
+	LOG_FUNC
+	TInt err = KErrNone;
+	TBool specified = EFalse;
+	TL2CapConfig::TChannelReliability reliability = aApiConfig.ChannelReliability(specified);
+	if (specified)
+		{
+		if (iSAPSignalHandler.IsChannelClosed())
+			{
+			TBool getFlushTimeout = EFalse;
+			TBool getRtxTimeout = EFalse;
+	
+			if (reliability == TL2CapConfig::EUnreliableDesiredChannel)
+				{
+				getFlushTimeout = getRtxTimeout = ETrue;
+				}
+			else if (reliability == TL2CapConfig::EUnreliableChannel)
+				{
+				getFlushTimeout = ETrue;
+				}
+			else // Reliable
+				{
+				getRtxTimeout = ETrue;
+				}
+	
+			TUint16 timeout = 0;
+	
+			if (getFlushTimeout)
+				{
+				timeout = aApiConfig.ObsolescenceTimer(specified);
+				if (!specified)
+					{
+					err = KErrArgument;
+					}
+				else
+					{
+					iRequestedFlushTimeout = timeout;
+					}
+				}
+			
+			if (getRtxTimeout)
+				{
+				timeout = aApiConfig.RetransmissionTimer(specified);
+				if (!specified)
+					{
+					err = KErrArgument;
+					}
+				else
+					{
+					iRequestedRetransmissionTimeout = timeout;
+					}
+				}
+	
+			if (err == KErrNone)
+				{
+				iRequestedChannelReliability = reliability;
+				iLegacyModesDisallowed = aApiConfig.LegacyModesDisallowed();
+		
+				if (iRequestedFlushTimeout < KMinOutgoingFlushTimeout)
+					{
+					iRequestedFlushTimeout = KMinOutgoingFlushTimeout;
+					}
+				}
+			} // channel Closed
+		else
+			{
+			// Channel not in Closed state - can't change reliability.
+			err = KErrInUse;
+			}
+		} // reliability specified
+	return err;
+	}
+
+TInt CL2CapChannelConfig::UpdateMtuMru(const TL2CapConfig& aApiConfig, TBool& aOnTheAirReconfigRequired)
+	{
+	LOG_FUNC
+
+	// MTU-related params.
+	TBool mtuSpecified = EFalse;
+	TBool minMtuSpecified = EFalse;
+	TUint16 newMTU = aApiConfig.MaxTransmitUnitSize(mtuSpecified);
+	TUint16 newMinMTU = aApiConfig.MinMTU(minMtuSpecified);
+	TMTUOption currentMTU = iOutgoingMTU->Preferred();
+
+	// MRU-related params.
+	TBool mruSpecified = EFalse;
+	TBool minMruSpecified = EFalse;
+	TUint16 newMRU = aApiConfig.MaxReceiveUnitSize(mruSpecified);
+	TUint16 newMinMRU = aApiConfig.MinMRU(minMruSpecified);
+	TMTUOption currentMRU = iIncomingMTU->Preferred();
+
+	if ((mtuSpecified || minMtuSpecified || mruSpecified || minMruSpecified) &&
+		!iSAPSignalHandler.IsChannelClosed() && !iSAPSignalHandler.IsChannelOpen())
+		{
+		// Not the brightest idea to mess with these during ongoing config.
+		return KErrInUse;
+		}
+	else
+		{
+		if (mtuSpecified || minMtuSpecified)
+			{
+			TMTUOption mtuToSet = mtuSpecified ? newMTU : currentMTU;
+			// if !minMtuSpecified then assume the lower bound to be = newMTU
+			TMTUOption minMtuToSet = minMtuSpecified ? newMinMTU : newMTU;
+
+			if (minMtuToSet > mtuToSet)
+				{
+				return KErrL2CAPAttemptToSetMinMtuGreaterThanMtu;
+				}
+
+			iOutgoingMTU->SetRequiredValue(TMTUOption(TMTUOption::EMaximumValue), minMtuToSet, mtuToSet);
+			aOnTheAirReconfigRequired = ETrue;
+			}
+
+		if (mruSpecified || minMruSpecified)
+			{
+			TMTUOption mruToSet = mruSpecified ? newMRU : currentMRU;
+			 // if !minMruSpecified then assume the lower bound is the spec minimum
+			TMTUOption minMruToSet = minMruSpecified ? newMinMRU : TMTUOption(TMTUOption::ESpecMinimumValue);
+
+			if (minMruToSet > mruToSet)
+				{
+				return KErrL2CAPAttemptToSetMinMruGreaterThanMru;
+				}
+
+			iIncomingMTU->SetRequiredValue(mruToSet, minMruToSet, mruToSet);
+			aOnTheAirReconfigRequired = ETrue;
+			}
+		}
+	return KErrNone;
+	}
+
+TInt CL2CapChannelConfig::GetChannelMode(TL2CapChannelMode& aMode) const
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iFecNegotiator.IncomingLinkMode() == iFecNegotiator.OutgoingLinkMode(),
+				   Panic(EL2CAPAsymmetricChannelModes));
+	aMode = iFecNegotiator.IncomingLinkMode();
+	return KErrNone;
+	}
+
+
+SL2CapChannelIncomingConfigOptionSnapshot::SL2CapChannelIncomingConfigOptionSnapshot(const CL2CapChannelConfig& aChannelConfig)
+ :  iMtu(aChannelConfig.IncomingMTU().Preferred()),
+    iMtuStatus(aChannelConfig.IncomingMTU().ConfigOptionStatus()),
+    iFlushTimeout(aChannelConfig.OutgoingFlushTimeout().Preferred()),
+    iFlushTimeoutStatus(aChannelConfig.OutgoingFlushTimeout().ConfigOptionStatus()),
+    iFec(aChannelConfig.FecNegotiator().IncomingPreferred()),
+    iFecStatus(aChannelConfig.FecNegotiator().IncomingConfigOptionStatus()),
+    iQos(aChannelConfig.OutgoingQOS().Preferred()),
+    iQosStatus(aChannelConfig.OutgoingQOS().ConfigOptionStatus())
+    {
+    LOG_FUNC
+    }
+
+TBool SL2CapChannelIncomingConfigOptionSnapshot::operator==(const SL2CapChannelIncomingConfigOptionSnapshot& aThat)
+    {
+    LOG_FUNC
+    return iMtu == aThat.iMtu                   && iMtuStatus == aThat.iMtuStatus &&
+           iFlushTimeout == aThat.iFlushTimeout && iFlushTimeoutStatus == aThat.iFlushTimeoutStatus &&
+           iFec == aThat.iFec                   && iFecStatus == aThat.iFecStatus &&
+           iQos == aThat.iQos                   && iQosStatus == aThat.iQosStatus;
+    }