diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/l2cap/L2CapFecNegotiator.cpp --- /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 +#include "L2CapFecNegotiator.h" +#include "L2CapPDU.h" + +static TBool operator<=(TL2CapChannelMode aLeft, TL2CapChannelMode aRight); +template 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(*this).ModeToHandler(aMode); + } + +#ifdef __FLOG_ACTIVE +void TFecOptionHandlerDelegator::LogCurrentValues(const TRetransmissionAndFlowControlOption& aPreferred, + const TRetransmissionAndFlowControlOption& aPeer) const + { + TBuf 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 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 +static inline TBool IsWithinBounds(const T aValue, const T aLeftBound, const T aRightBound) + { + return aLeftBound <= aValue && aValue <= aRightBound; + }