--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/l2cap/L2CapFecNegotiator.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,874 @@
+// Copyright (c) 2006-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:
+//
+
+
+
+/**
+ @file
+ @internalComponent
+*/
+
+#include <bluetooth/logger.h>
+#include "L2CapFecNegotiator.h"
+#include "L2CapPDU.h"
+
+static TBool operator<=(TL2CapChannelMode aLeft, TL2CapChannelMode aRight);
+template<typename T> static inline TBool IsWithinBounds(const T aValue, const T aLeftBound, const T aRightBound);
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_L2CAP);
+#endif
+
+//
+// Mode-specific handlers
+//
+
+TBool TModeSpecificFecOptionHandlerBase::BuildPositiveResponse(TRetransmissionAndFlowControlOption& aPreferred,
+ const TRetransmissionAndFlowControlOption& aPeer) const
+ {
+ LOG_FUNC
+ // As simple as that according to the general negotiation process, i.e. not when negotiating
+ // the 2.1 Core Spec Addendum 1 - introduced modes.
+ aPreferred = aPeer;
+ // Don't include the exactly same FEC in response.
+ return EFalse;
+ }
+
+void TModeSpecificFecOptionHandlerBase::BuildNegativeResponse(TRetransmissionAndFlowControlOption& /*aPreferred*/,
+ const TRetransmissionAndFlowControlOption& /*aPeer*/) const
+ {
+ LOG_FUNC
+ // Just send what we've got in Preferred.
+ }
+
+void TModeSpecificFecOptionHandlerBase::SetMaxTransmit(TRetransmissionAndFlowControlOption& aFecOption, TUint8 /*aMaxTransmit*/) const
+ {
+ LOG_FUNC
+ aFecOption.SetMaxTransmit(0);
+ }
+
+void TModeSpecificFecOptionHandlerBase::PrepareImplicitPeerResponse(TRetransmissionAndFlowControlOption& aImplicitResponse,
+ const TRetransmissionAndFlowControlOption& aPreferred) const
+ {
+ LOG_FUNC
+ aImplicitResponse = aPreferred;
+ }
+
+void TModeSpecificFecOptionHandlerBase::ZeroUnspecifiedRequestFields(TRetransmissionAndFlowControlOption& /*aFecOption*/) const
+ {LOG_FUNC}
+
+void TModeSpecificFecOptionHandlerBase::ZeroUnspecifiedResponseFields(TRetransmissionAndFlowControlOption& /*aFecOption*/) const
+ {LOG_FUNC}
+
+
+// Streaming Mode
+
+TBool TStreamingFecHandler::IsOptionValid(const TRetransmissionAndFlowControlOption& aFecOption) const
+ {
+ LOG_FUNC
+ return aFecOption.MaximumPDUSize() >= TRetransmissionAndFlowControlOption::KMinValidMaximumPDUSize;
+ }
+
+TBool TStreamingFecHandler::IsPeerResponseAcceptable(const TRetransmissionAndFlowControlOption& aPreferred,
+ const TRetransmissionAndFlowControlOption& aPeer) const
+ {
+ LOG_FUNC
+ return aPeer.MaximumPDUSize() <= aPreferred.MaximumPDUSize();
+ }
+
+TBool TStreamingFecHandler::BuildPositiveResponse(TRetransmissionAndFlowControlOption& aPreferred,
+ const TRetransmissionAndFlowControlOption& aPeer) const
+ {
+ LOG_FUNC
+ TRetransmissionAndFlowControlOption response = aPeer;
+ // Trim MPS to our desired value.
+ response.SetMaximumPDUSize(Min(TRetransmissionAndFlowControlOption::KDefaultMaximumPDUSize,
+ response.MaximumPDUSize()));
+ // Zero the ignored fields no matter what peer sent us in them.
+ ZeroUnspecifiedResponseFields(response);
+
+ aPreferred = response;
+ // Include the FEC in ConfigRsp.
+ return ETrue;
+ }
+
+void TStreamingFecHandler::BuildNegativeResponse(TRetransmissionAndFlowControlOption& aPreferred,
+ const TRetransmissionAndFlowControlOption& /*aPeer*/) const
+ {
+ LOG_FUNC
+ // Channel Mode has been already set and that's the parameter that what we're actually
+ // rejecting. The rest is informational and should be set by the remote to its own
+ // liking based on the mode proposed, so we _could_ set them to 0, but just in case
+ // we're talking to a dumb peer - or a bit older version of my own code that wouldn't
+ // accept 0s, for that matter - set them to sensible defaults.
+ aPreferred = TRetransmissionAndFlowControlOption(aPreferred.LinkMode(), ETrue);
+ ZeroUnspecifiedRequestFields(aPreferred);
+ }
+
+void TStreamingFecHandler::ZeroUnspecifiedRequestFields(TRetransmissionAndFlowControlOption& aFecOption) const
+ {
+ LOG_FUNC
+ aFecOption.SetTxWindowSize(0);
+ aFecOption.SetMaxTransmit(0);
+ aFecOption.SetRetransmissionTimeout(0);
+ aFecOption.SetMonitorTimeout(0);
+ }
+
+void TStreamingFecHandler::ZeroUnspecifiedResponseFields(TRetransmissionAndFlowControlOption& aFecOption) const
+ {
+ LOG_FUNC
+ aFecOption.SetTxWindowSize(0);
+ aFecOption.SetMaxTransmit(0);
+ aFecOption.SetRetransmissionTimeout(0);
+ aFecOption.SetMonitorTimeout(0);
+ }
+
+
+// Enhanced Retransmission Mode
+
+TBool TErtmFecHandler::IsOptionValid(const TRetransmissionAndFlowControlOption& aFecOption) const
+ {
+ LOG_FUNC
+ return aFecOption.MaximumPDUSize() >= TRetransmissionAndFlowControlOption::KMinValidMaximumPDUSize &&
+ aFecOption.TxWindowSize() >= TRetransmissionAndFlowControlOption::KMinValidTxWindowSize &&
+ aFecOption.TxWindowSize() <= TRetransmissionAndFlowControlOption::KMaxValidEnhancedTxWindowSize;
+ }
+
+TBool TErtmFecHandler::IsPeerResponseAcceptable(const TRetransmissionAndFlowControlOption& aPreferred,
+ const TRetransmissionAndFlowControlOption& aPeer) const
+ {
+ LOG_FUNC
+ return // MaxTransmit in response doesn't matter.
+ IsWithinBounds(aPeer.RetransmissionTimeout(),
+ TRetransmissionAndFlowControlOption::KMinAcceptableRetransmissionTimeout,
+ TRetransmissionAndFlowControlOption::KMaxAcceptableRetransmissionTimeout) &&
+ IsWithinBounds(aPeer.MonitorTimeout(),
+ TRetransmissionAndFlowControlOption::KMinAcceptableMonitorTimeout,
+ TRetransmissionAndFlowControlOption::KMaxAcceptableMonitorTimeout) &&
+ aPeer.TxWindowSize() <= aPreferred.TxWindowSize() &&
+ aPeer.MaximumPDUSize() <= aPreferred.MaximumPDUSize();
+ }
+
+TBool TErtmFecHandler::BuildPositiveResponse(TRetransmissionAndFlowControlOption& aPreferred,
+ const TRetransmissionAndFlowControlOption& aPeer) const
+ {
+ LOG_FUNC
+ TRetransmissionAndFlowControlOption response = aPeer;
+ // Peer sends us 0s in Retransmission and Monitor time-outs in Request and expects us
+ // to send values for the timers we're going to use in the response.
+ response.SetRetransmissionTimeout(TRetransmissionAndFlowControlOption::KDefaultRetransmissionTimeout);
+ response.SetMonitorTimeout(TRetransmissionAndFlowControlOption::KDefaultMonitorTimeout);
+ ZeroUnspecifiedResponseFields(response);
+
+ // Trim TxWindow and MPS to our preferred values.
+ response.SetTxWindowSize(Min(TRetransmissionAndFlowControlOption::KDefaultEnhancedTxWindowSize,
+ response.TxWindowSize()));
+ response.SetMaximumPDUSize(Min(TRetransmissionAndFlowControlOption::KDefaultMaximumPDUSize,
+ response.MaximumPDUSize()));
+
+ aPreferred = response;
+ // Include the FEC in ConfigRsp.
+ return ETrue;
+ }
+
+void TErtmFecHandler::BuildNegativeResponse(TRetransmissionAndFlowControlOption& aPreferred,
+ const TRetransmissionAndFlowControlOption& /*aPeer*/) const
+ {
+ LOG_FUNC
+ // Channel Mode has been already set and that's the parameter that what we're actually
+ // rejecting. The rest is informational and should be set by the remote to its own
+ // liking based on the mode proposed, so we _could_ set them to 0, but just in case
+ // we're talking to a dumb peer - or a bit older version of my own code that wouldn't
+ // accept 0s, for that matter - set them to sensible defaults.
+ aPreferred = TRetransmissionAndFlowControlOption(aPreferred.LinkMode(), ETrue);
+ ZeroUnspecifiedRequestFields(aPreferred);
+ }
+
+void TErtmFecHandler::SetMaxTransmit(TRetransmissionAndFlowControlOption& aFecOption, TUint8 aMaxTransmit) const
+ {
+ aFecOption.SetMaxTransmit(aMaxTransmit);
+ }
+
+void TErtmFecHandler::PrepareImplicitPeerResponse(TRetransmissionAndFlowControlOption& aImplicitResponse,
+ const TRetransmissionAndFlowControlOption& aPreferred) const
+ {
+ LOG_FUNC
+ TModeSpecificFecOptionHandlerBase::PrepareImplicitPeerResponse(aImplicitResponse, aPreferred);
+ // Peer is supposed to send us time-out values that it will use. It should always send
+ // a response FEC if we're negotiating enhanced modes, so eventually we will get the real
+ // values (unless the peer is broken).
+ aImplicitResponse.SetRetransmissionTimeout(TRetransmissionAndFlowControlOption::KDefaultRetransmissionTimeout);
+ aImplicitResponse.SetMonitorTimeout(TRetransmissionAndFlowControlOption::KDefaultMonitorTimeout);
+ }
+
+void TErtmFecHandler::ZeroUnspecifiedRequestFields(TRetransmissionAndFlowControlOption& aFecOption) const
+ {
+ aFecOption.SetRetransmissionTimeout(0);
+ aFecOption.SetMonitorTimeout(0);
+ }
+
+void TErtmFecHandler::ZeroUnspecifiedResponseFields(TRetransmissionAndFlowControlOption& aFecOption) const
+ {
+ aFecOption.SetMaxTransmit(0);
+ }
+
+// Flow Control Mode and Retransmission Mode
+
+TBool TLegacyFecHandler::IsOptionValid(const TRetransmissionAndFlowControlOption& aFecOption) const
+ {
+ LOG_FUNC
+ return aFecOption.MaximumPDUSize() >= TRetransmissionAndFlowControlOption::KMinValidMaximumPDUSize &&
+ aFecOption.TxWindowSize() >= TRetransmissionAndFlowControlOption::KMinValidTxWindowSize &&
+ aFecOption.TxWindowSize() <= TRetransmissionAndFlowControlOption::KMaxValidLegacyTxWindowSize &&
+ aFecOption.MaxTransmit() >= TRetransmissionAndFlowControlOption::KMinValidNumberTransmit &&
+ aFecOption.MaxTransmit() <= TRetransmissionAndFlowControlOption::KMaxValidLegacyNumberTransmit;
+ }
+
+TBool TLegacyFecHandler::IsPeerResponseAcceptable(const TRetransmissionAndFlowControlOption& aPreferred,
+ const TRetransmissionAndFlowControlOption& aPeer) const
+ {
+ LOG_FUNC
+ return aPeer == aPreferred;
+ }
+
+void TLegacyFecHandler::SetMaxTransmit(TRetransmissionAndFlowControlOption& aFecOption, TUint8 aMaxTransmit) const
+ {
+ aFecOption.SetMaxTransmit(aMaxTransmit == TRetransmissionAndFlowControlOption::KMaxValidEnhancedNumberTransmit ?
+ TRetransmissionAndFlowControlOption::KMaxValidLegacyNumberTransmit :
+ aMaxTransmit);
+ }
+
+// Basic mode
+
+TBool TBasicFecHandler::IsOptionValid(const TRetransmissionAndFlowControlOption& /*aFecOption*/) const
+ {
+ LOG_FUNC
+ // Only the mode field is specified for Basic, and we know the mode is Basic already...
+ return ETrue;
+ }
+
+TBool TBasicFecHandler::IsPeerResponseAcceptable(const TRetransmissionAndFlowControlOption& /*aPreferred*/,
+ const TRetransmissionAndFlowControlOption& /*aPeer*/) const
+ {
+ LOG_FUNC
+ // Only the mode field is specified for Basic, and we know the mode is Basic already...
+ return ETrue;
+ }
+
+// TFecOptionHandlerDelegator
+
+TModeSpecificFecOptionHandlerBase& TFecOptionHandlerDelegator::Handler(const TRetransmissionAndFlowControlOption& aFecOption)
+ {
+ TModeSpecificFecOptionHandlerBase* handler = ModeToHandler(aFecOption.LinkMode());
+ __ASSERT_ALWAYS(handler != NULL, Panic(EL2CAPUnknownChannelMode));
+ return *handler;
+ }
+
+const TModeSpecificFecOptionHandlerBase& TFecOptionHandlerDelegator::Handler(const TRetransmissionAndFlowControlOption &aFecOption) const
+ {
+ const TModeSpecificFecOptionHandlerBase* handler = ModeToHandler(aFecOption.LinkMode());
+ __ASSERT_ALWAYS(handler != NULL, Panic(EL2CAPUnknownChannelMode));
+ return *handler;
+ }
+
+TModeSpecificFecOptionHandlerBase* TFecOptionHandlerDelegator::ModeToHandler(TL2CapChannelMode aMode)
+ {
+ TModeSpecificFecOptionHandlerBase* processor = 0;
+ switch (aMode)
+ {
+ case EL2CAPStreamingMode:
+ processor = &iStreamingFecHandler;
+ break;
+ case EL2CAPEnhancedRetransmissionMode:
+ processor = &iErtmFecHandler;
+ break;
+ case EL2CAPRetransmissionMode:
+ case EL2CAPFlowControlMode:
+ processor = &iLegacyFecHandler;
+ break;
+ case EL2CAPBasicMode:
+ processor = &iBasicFecHandler;
+ break;
+ default:
+ break;
+ // yes, return NULL.
+ }
+ return processor;
+ }
+
+const TModeSpecificFecOptionHandlerBase* TFecOptionHandlerDelegator::ModeToHandler(TL2CapChannelMode aMode) const
+ {
+ return const_cast<TFecOptionHandlerDelegator&>(*this).ModeToHandler(aMode);
+ }
+
+#ifdef __FLOG_ACTIVE
+void TFecOptionHandlerDelegator::LogCurrentValues(const TRetransmissionAndFlowControlOption& aPreferred,
+ const TRetransmissionAndFlowControlOption& aPeer) const
+ {
+ TBuf<TRetransmissionAndFlowControlOption::KReadableDesSpaceRequired> buf;
+ aPeer.GetReadable(buf);
+ LOG1(_L("\tCurrent Peer: %S"), &buf)
+ buf.Zero();
+ aPreferred.GetReadable(buf);
+ LOG1(_L("\tCurrent Preferred: %S"), &buf)
+ }
+#endif
+
+//
+// TL2CapSingleDirectionFecNegotiatorBase
+//
+void TL2CapSingleDirectionFecNegotiatorBase::SetupForRenegotiation()
+ {
+ LOG_FUNC
+ if (iFecNegotiator.DesiredMode() == EL2CAPBasicMode &&
+ // Only skip negotiating Basic if it still is the last accepted value.
+ iPeer.LinkMode() == EL2CAPBasicMode)
+ {
+ // See comment in Setup().
+ iConfigStatus = EOptionConfigComplete;
+ }
+#ifdef __FLOG_ACTIVE
+ LogState();
+#endif
+ }
+
+TBool TL2CapSingleDirectionFecNegotiatorBase::IsPeerModeAcceptable(TL2CapChannelMode aPeerMode,
+ TBool& aDisconnect) const
+ {
+ LOG_FUNC
+ TBool acceptable = ETrue;
+ aDisconnect = EFalse;
+
+ LOG3(_L("\tNegotiation Behavior = %d, Desired Mode = %d, Peer Mode = %d"),
+ iFecNegotiator.NegotiationBehavior(), iFecNegotiator.DesiredMode(), aPeerMode)
+
+ if (iFecNegotiator.NegotiationBehavior() == TL2CapFecNegotiator::EState2)
+ {
+ if (iFecNegotiator.DesiredMode() != aPeerMode)
+ {
+ // Game over.
+ acceptable = EFalse;
+ aDisconnect = ETrue;
+ }
+ }
+ else // some sort of State 1
+ {
+ // A safety net - check that it's legal for our peer to propose this mode.
+ if (!iFecNegotiator.IsModeLegal(aPeerMode))
+ {
+ // Remote is behaving out of spec.
+ acceptable = EFalse;
+ LOG1(_L("Peer proposed an illegal mode = %d"), aPeerMode);
+ }
+ else
+ {
+ // Check that the mode is within our limits according to the spec-defined precedence
+ // hierarchy. Note that the spec only defines the precedence and State 1/2 algorithm
+ // for the new modes + Basic, but we try and apply it to the legacy modes as well for
+ // predictable & consistent behavior.
+ acceptable = aPeerMode <= iFecNegotiator.DesiredMode();
+ // Now, the State 1 algorithm gives us two choices in case peer proposes a mode
+ // that's higher precedence than our desired mode (aPeerMode < iDesiredMode) -
+ // - we can either accept it or close the connection - we cannot propose a different
+ // one. EState1NoUnreliableToReliable means we do not want to allow fallback from an
+ // unreliable mode to a reliable one, i.e. when we propose an unreliable mode,
+ // but the remote proposes a reliable one (ERTM is higher prec. than Streaming,
+ // ditto RTM vs FC).
+ if (acceptable && iFecNegotiator.NegotiationBehavior() == TL2CapFecNegotiator::EState1NoUnreliableToReliable &&
+ (iFecNegotiator.DesiredMode() == EL2CAPStreamingMode || iFecNegotiator.DesiredMode() == EL2CAPFlowControlMode) &&
+ (aPeerMode == EL2CAPEnhancedRetransmissionMode || aPeerMode == EL2CAPRetransmissionMode))
+ {
+ acceptable = EFalse;
+ if (!(iFecNegotiator.DesiredMode() == EL2CAPFlowControlMode && aPeerMode == EL2CAPRetransmissionMode))
+ {
+ // Only disconnect immediately if the peer seems to be implementing enhanced
+ // modes - mostly to be interoperable with pre-2.1 Core Spec Addendum 1 Symbian
+ // code, which doesn't handle disconnects well when there're still unresponded
+ // request commands.
+ // If we don't disconnect, a FEC for our desired mode will be sent, and we'll
+ // see what happens - free style negotiation is allowed with legacy remotes.
+ aDisconnect = ETrue;
+ }
+ LOG(_L("\tRefusing to fall back from Unreliable to Reliable"));
+ }
+ }
+ } // State 1
+
+ LOG2(_L("\tPeer channel mode acceptable = %d, disconnect required = %d"), acceptable, aDisconnect);
+ return acceptable;
+ }
+
+#ifdef __FLOG_ACTIVE
+void TL2CapSingleDirectionFecNegotiatorBase::LogState() const
+ {
+ LOG3(_L("\tdesired channel mode = %d, mode negotiation behavior = %d, config status = %d"),
+ iFecNegotiator.DesiredMode(), iFecNegotiator.NegotiationBehavior(), iConfigStatus)
+ TBuf<TRetransmissionAndFlowControlOption::KReadableDesSpaceRequired> readable;
+ iPreferred.GetReadable(readable);
+ LOG1(_L("\tpreferred FEC = %S"), &readable);
+ readable.Zero();
+ iPeer.GetReadable(readable);
+ LOG1(_L("\tpeer FEC = %S"), &readable);
+ }
+#endif
+
+//
+// TL2CapIncomingFecNegotiator
+//
+void TL2CapIncomingFecNegotiator::Setup()
+ {
+ LOG_FUNC
+ // Set up spec default as initial peer value.
+ iPeer = TRetransmissionAndFlowControlOption();
+
+ // Set up our initial request.
+ BuildRequest(iFecNegotiator.DesiredMode(), iPreferred);
+
+ if (iFecNegotiator.DesiredMode() == EL2CAPBasicMode)
+ {
+ // Basic mode is the implicit default, so it's complete unless peer says otherwise.
+ // Besides, the fact that Basic is our desired mode normally means that the peer doesn't
+ // support anything else, so it would not be able to parse a TRetransmissionAndFlowControl
+ // option if we sent it.
+ iConfigStatus = EOptionConfigComplete;
+ }
+
+#ifdef __FLOG_ACTIVE
+ LogState();
+#endif
+ }
+
+void TL2CapIncomingFecNegotiator::ProcessPeerValue(const TRetransmissionAndFlowControlOption& aFecOption,
+ TBool aIsUnacceptableParameters)
+ {
+ LOG_FUNC
+
+ iPeer = aFecOption;
+
+ // 'return' parameter from IsPeerModeAcceptable() - we'll ignore it because in
+ // incoming direction we disconnect if response config status is failed anyway.
+ TBool disconnect;
+
+ TBool peerAcceptable = IsPeerModeAcceptable(iPeer.LinkMode(), disconnect);
+ if (peerAcceptable && !aIsUnacceptableParameters)
+ {
+ peerAcceptable = iFecNegotiator.ModeSpecificHandlers().IsPeerResponseAcceptable(iPreferred, iPeer);
+ }
+
+ if (!peerAcceptable)
+ {
+ iConfigStatus = EOptionConfigFailed;
+ }
+ else
+ {
+ if (aIsUnacceptableParameters)
+ {
+ iConfigStatus = EOptionConfigOutstanding;
+ // Only take the channel mode from peer's proposal and set informational
+ // (i.e. all other) parameters to our values.
+ BuildRequest(aFecOption.LinkMode(), iPreferred);
+ }
+ else
+ {
+ iConfigStatus = EOptionConfigComplete;
+ }
+ }
+ LOG1(_L("\tIncoming FEC Config Status is now %d"), iConfigStatus);
+ }
+
+void TL2CapIncomingFecNegotiator::ProcessImplicitPeerValue()
+ {
+ LOG_FUNC
+ // We need to assume that the peer accepted our request and sent an appropriate response back.
+ TRetransmissionAndFlowControlOption response;
+ iFecNegotiator.ModeSpecificHandlers().PrepareImplicitPeerResponse(response, iPreferred);
+ ProcessPeerValue(response, EFalse);
+ }
+
+void TL2CapIncomingFecNegotiator::BuildRequest(TL2CapChannelMode aMode, TRetransmissionAndFlowControlOption& aFecOption)
+ {
+ LOG_FUNC
+ aFecOption = TRetransmissionAndFlowControlOption(aMode, ETrue);
+ iFecNegotiator.ModeSpecificHandlers().SetMaxTransmit(aFecOption, iFecNegotiator.MaxTransmit());
+ iFecNegotiator.ModeSpecificHandlers().ZeroUnspecifiedRequestFields(aFecOption);
+ }
+
+//
+// TL2CapOutgoingFecNegotiator
+//
+void TL2CapOutgoingFecNegotiator::Setup()
+ {
+ LOG_FUNC
+ // Set up spec default as initial peer value.
+ iPeer = TRetransmissionAndFlowControlOption();
+
+ // iPreferred will be constructed when we process peer value once a Config Request has been received.
+#ifdef __FLOG_ACTIVE
+ LogState();
+#endif
+ }
+
+TInt TL2CapOutgoingFecNegotiator::ProcessPeerValue(const TRetransmissionAndFlowControlOption& aFecOption)
+ {
+ LOG_FUNC
+
+ TInt err = KErrNone;
+ TBool disconnect = EFalse;
+ TBool peerModeAcceptable = IsPeerModeAcceptable(aFecOption.LinkMode(), disconnect);
+
+ if (peerModeAcceptable)
+ {
+ iPeer = aFecOption;
+ iIncludeValueInPositiveConfigResponse = iFecNegotiator.ModeSpecificHandlers().BuildPositiveResponse(iPreferred, iPeer);
+ iConfigStatus = EOptionConfigComplete;
+ }
+ else
+ {
+ if (disconnect)
+ {
+ // Disconnect immediately without sending an Unacceptable Parameters response.
+ iConfigStatus = EOptionConfigFailed;
+ err = KErrConfigRejected;
+ }
+ else
+ {
+ iPreferred.SetLinkMode(iFecNegotiator.DesiredMode());
+ iFecNegotiator.ModeSpecificHandlers().BuildNegativeResponse(iPreferred, aFecOption);
+ // Cause an Unacceptable Parameters response.
+ iConfigStatus = EOptionConfigFailed;
+ // Preferred contains our desired FEC, it will be included in the Unaccepted Parameters response.
+ }
+ }
+ LOG1(_L("\tOutgoing FEC Config Status is now %d"), iConfigStatus);
+ return err;
+ }
+
+
+//
+// TL2CapFecNegotiator
+//
+TBool TL2CapFecNegotiator::Setup(TL2CapConfig::TChannelReliability aChannelReliability,
+ TBool aLegacyModesDisallowed,
+ const TL2CapEntityInfo& aPeerEntityConfig,
+ TUint8 aMaxTransmit)
+ {
+ LOG_FUNC
+
+ __ASSERT_DEBUG(aPeerEntityConfig.LinkInfoState() == EL2CapEntityInfoDefined,
+ Panic(EL2CAPFecConfigAttemptWithoutPeerInfo));
+ iPeerSupportedModes = aPeerEntityConfig;
+ iMaxTransmit = aMaxTransmit;
+
+ TBool modeNegotiable = EFalse;
+
+ iDesiredMode = EL2CAPBasicMode;
+
+ // From 2.4 Modes of Operation:
+ // "Flow Control Mode and Retransmission mode shall only be enabled when communicating with
+ // L2CAP entities that do not support either Enhanced Retransmission mode or Streaming mode."
+ // From 5.4 Retransmission And Flow Control Option:
+ // "Basic mode, Flow Control mode and Retransmission mode shall only be used for backwards
+ // compatibility with L2CAP entities that do not support Enhanced Retransmission mode or
+ // Streaming mode."
+ //
+ // Make of that what you will, but the official Symbian interpretation is that RTM or FC may
+ // only be used if the remote doesn't support any of the new modes.
+ const TBool enhancedModeSupported = iPeerSupportedModes.SupportsEnhancedRetransmissionMode() ||
+ iPeerSupportedModes.SupportsStreamingMode();
+
+ switch (aChannelReliability)
+ {
+ case TL2CapConfig::EReliableChannel:
+ iNegotiationBehavior = aLegacyModesDisallowed ? EState2 : EState1;
+ if (iPeerSupportedModes.SupportsEnhancedRetransmissionMode())
+ {
+ iDesiredMode = EL2CAPEnhancedRetransmissionMode;
+ modeNegotiable = ETrue;
+ }
+ else if (!aLegacyModesDisallowed)
+ {
+ if (iPeerSupportedModes.SupportsRetranmission() && !enhancedModeSupported)
+ {
+ iDesiredMode = EL2CAPRetransmissionMode;
+ modeNegotiable = ETrue;
+ }
+ else
+ {
+ iDesiredMode = EL2CAPBasicMode;
+ modeNegotiable = ETrue;
+ }
+ }
+ break;
+
+ case TL2CapConfig::EUnreliableChannel:
+ iNegotiationBehavior = aLegacyModesDisallowed ? EState2 : EState1NoUnreliableToReliable;
+ if (iPeerSupportedModes.SupportsStreamingMode())
+ {
+ iDesiredMode = EL2CAPStreamingMode;
+ modeNegotiable = ETrue;
+ }
+ else if (!aLegacyModesDisallowed)
+ {
+ if (iPeerSupportedModes.SupportsFlowControl() && !enhancedModeSupported)
+ {
+ iDesiredMode = EL2CAPFlowControlMode;
+ modeNegotiable = ETrue;
+ }
+ else
+ {
+ iDesiredMode = EL2CAPBasicMode;
+ modeNegotiable = ETrue;
+ }
+ }
+ break;
+
+ case TL2CapConfig::EUnreliableDesiredChannel:
+ // Be open to all proposals within spec-defined constraints.
+ iNegotiationBehavior = EState1;
+ if (iPeerSupportedModes.SupportsStreamingMode())
+ {
+ iDesiredMode = EL2CAPStreamingMode;
+ modeNegotiable = ETrue;
+ }
+ else if (iPeerSupportedModes.SupportsFlowControl() & !enhancedModeSupported)
+ {
+ iDesiredMode = EL2CAPFlowControlMode;
+ modeNegotiable = ETrue;
+ }
+ else if (iPeerSupportedModes.SupportsEnhancedRetransmissionMode())
+ {
+ iDesiredMode = EL2CAPEnhancedRetransmissionMode;
+ modeNegotiable = ETrue;
+ }
+ else if (iPeerSupportedModes.SupportsRetranmission() && !enhancedModeSupported)
+ {
+ iDesiredMode = EL2CAPRetransmissionMode;
+ modeNegotiable = ETrue;
+ }
+ else
+ {
+ iDesiredMode = EL2CAPBasicMode;
+ modeNegotiable = ETrue;
+ }
+ break;
+ }
+
+ if (modeNegotiable)
+ {
+ iIncomingNegotiator.Setup();
+ iOutgoingNegotiator.Setup();
+ }
+ return modeNegotiable;
+ }
+
+void TL2CapFecNegotiator::SetupForRenegotiation()
+ {
+ LOG_FUNC
+ iIncomingNegotiator.SetupForRenegotiation();
+ iOutgoingNegotiator.SetupForRenegotiation();
+ }
+
+// A helper that optimizes the negotiation process by downgrading incoming preferred mode
+// to Basic if the remote requests Basic, we accept it, and haven't sent out our request yet.
+// Caveat: This should be only called if our Config Request hasn't been sent yet
+// and we know we've received peer's Config Request.
+void TL2CapFecNegotiator::DowngradeIncomingToBasicIfOutgoingIsBasic()
+ {
+ LOG_FUNC
+ // If we've already accepted Peer's Basic mode request, we may downgrade the incoming
+ // direction to Basic as well, since FEC in a single direction is forbidden.
+ // Otherwise we may end up with FEC in just one direction and would have to
+ // downgrade and renegotiate anyway.
+ // Note: this only makes sense if we haven't sent our Config Request yet.
+ // The caller is responsible for checking this.
+ if ((iNegotiationBehavior == EState1 ||
+ iNegotiationBehavior == EState1NoUnreliableToReliable) &&
+ iOutgoingNegotiator.ConfigOptionStatus() != TL2CapConfigurationOptionGroupBase::EOptionConfigFailed &&
+ iOutgoingNegotiator.Preferred().LinkMode() == EL2CAPBasicMode &&
+ iIncomingNegotiator.Preferred().LinkMode() != EL2CAPBasicMode)
+ {
+ LOG(_L("\tReceived Basic mode Config Request, downgrading incoming channel mode to Basic"));
+ // Downgrade incoming FEC to be basic mode.
+ TRetransmissionAndFlowControlOption basicFec;
+ iIncomingNegotiator.SetPreferred(basicFec);
+ }
+ }
+
+TInt TL2CapFecNegotiator::CheckNegotiatedChannelMode(TBool& aDowngrade)
+ {
+ LOG_FUNC
+ TInt err = KErrNone;
+
+ if ((iNegotiationBehavior == EState1 ||
+ iNegotiationBehavior == EState1NoUnreliableToReliable)
+ &&
+ ((iOutgoingNegotiator.Preferred().LinkMode() == EL2CAPBasicMode &&
+ iIncomingNegotiator.Peer().LinkMode() != EL2CAPBasicMode)
+ ||
+ (iIncomingNegotiator.Peer().LinkMode() == EL2CAPBasicMode &&
+ iOutgoingNegotiator.Preferred().LinkMode() != EL2CAPBasicMode)))
+ {
+ LOG(_L("\tDowngrading unidirectional FEC to Basic in both directions"));
+
+ TRetransmissionAndFlowControlOption basicFec;
+ iIncomingNegotiator.SetPreferred(basicFec);
+ iOutgoingNegotiator.SetPreferred(basicFec);
+
+ SetupForRenegotiation();
+ aDowngrade = ETrue;
+ }
+ else if (IncomingLinkMode() != OutgoingLinkMode())
+ {
+ LOG2(_L("\tSomehow managed to negotiate %d incoming mode and %d outgoing mode"),
+ IncomingLinkMode(), OutgoingLinkMode());
+ err = KErrL2CAPNegotiatedDifferentModesForEachDirection;
+ }
+ else
+ {
+ aDowngrade = EFalse;
+ }
+
+ return err;
+ }
+
+TBool TL2CapFecNegotiator::IsModeLegal(TL2CapChannelMode aPeerMode) const
+ {
+ LOG_FUNC
+ TBool legal = ETrue;
+ if ((aPeerMode == EL2CAPEnhancedRetransmissionMode && !iPeerSupportedModes.SupportsEnhancedRetransmissionMode()) ||
+ (aPeerMode == EL2CAPStreamingMode && !iPeerSupportedModes.SupportsStreamingMode()) ||
+ (aPeerMode == EL2CAPRetransmissionMode && !iPeerSupportedModes.SupportsRetranmission()) ||
+ (aPeerMode == EL2CAPFlowControlMode && !iPeerSupportedModes.SupportsFlowControl()) ||
+ // if any of the new modes is supported then none of the old ones shall be used
+ ((aPeerMode == EL2CAPRetransmissionMode || aPeerMode == EL2CAPFlowControlMode) &&
+ (iPeerSupportedModes.SupportsEnhancedRetransmissionMode() || iPeerSupportedModes.SupportsStreamingMode())))
+ {
+ legal = EFalse;
+ }
+ return legal;
+ }
+
+// The enhanced modes are ordered according to the 2.1 Core Spec Addendum 1
+// "state 1" precedence (pt 5.4), but in reverse order.
+// 1. Streaming
+// 2. ERTM
+// 3. Basic
+// The legacy modes are ordered similarly:
+// 1. Flow Control
+// 2. RTM
+// 3. Basic
+// Additionally pairs consisting of a legacy and a new mode are never in relation.
+static TBool operator<=(TL2CapChannelMode aLeft, TL2CapChannelMode aRight)
+ {
+ TBool inRelation = EFalse;
+ switch(aLeft)
+ {
+ case EL2CAPBasicMode:
+ inRelation = ETrue;
+ break;
+
+ case EL2CAPRetransmissionMode:
+ switch (aRight)
+ {
+ case EL2CAPBasicMode:
+ inRelation = EFalse;
+ break;
+ case EL2CAPRetransmissionMode:
+ inRelation = ETrue;
+ break;
+ case EL2CAPFlowControlMode:
+ inRelation = ETrue;
+ break;
+ case EL2CAPEnhancedRetransmissionMode:
+ inRelation = EFalse;
+ break;
+ case EL2CAPStreamingMode:
+ inRelation = EFalse;
+ break;
+ }
+ break;
+
+ case EL2CAPFlowControlMode:
+ switch (aRight)
+ {
+ case EL2CAPBasicMode:
+ inRelation = EFalse;
+ break;
+ case EL2CAPRetransmissionMode:
+ inRelation = EFalse;
+ break;
+ case EL2CAPFlowControlMode:
+ inRelation = ETrue;
+ break;
+ case EL2CAPEnhancedRetransmissionMode:
+ inRelation = EFalse;
+ break;
+ case EL2CAPStreamingMode:
+ inRelation = EFalse;
+ break;
+ }
+ break;
+
+ case EL2CAPEnhancedRetransmissionMode:
+ switch (aRight)
+ {
+ case EL2CAPBasicMode:
+ inRelation = EFalse;
+ break;
+ case EL2CAPRetransmissionMode:
+ inRelation = EFalse;
+ break;
+ case EL2CAPFlowControlMode:
+ inRelation = EFalse;
+ break;
+ case EL2CAPEnhancedRetransmissionMode:
+ inRelation = ETrue;
+ break;
+ case EL2CAPStreamingMode:
+ inRelation = ETrue;
+ break;
+ }
+ break;
+
+ case EL2CAPStreamingMode:
+ switch (aRight)
+ {
+ case EL2CAPBasicMode:
+ inRelation = EFalse;
+ break;
+ case EL2CAPRetransmissionMode:
+ inRelation = EFalse;
+ break;
+ case EL2CAPFlowControlMode:
+ inRelation = EFalse;
+ break;
+ case EL2CAPEnhancedRetransmissionMode:
+ inRelation = EFalse;
+ break;
+ case EL2CAPStreamingMode:
+ inRelation = ETrue;
+ break;
+ }
+ break;
+ }
+
+ return inRelation;
+ }
+
+template<typename T>
+static inline TBool IsWithinBounds(const T aValue, const T aLeftBound, const T aRightBound)
+ {
+ return aLeftBound <= aValue && aValue <= aRightBound;
+ }