bluetooth/btstack/l2cap/l2capSigPacketConfigure.cpp
changeset 0 29b1cd4cb562
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/l2cap/l2capSigPacketConfigure.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,669 @@
+// 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 <bluetooth/logger.h>
+
+#include "l2capSigPacketConfigure.h"
+
+#include "l2capSignalHandler.h"
+#include "L2CapChannelConfig.h"
+
+#include "l2util.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_L2CAP);
+#endif
+
+//					CONFIGURE REQUEST COMMAND 
+
+
+//Pass in a TL2ConfigOptions packets.  Any options set to valid will be negotiated with there set values
+HConfigureRequest* HConfigureRequest::New(TL2CAPPort aRemotePort, CL2CapChannelConfig& aConfig,
+		  	               				  TUint8 aRTXTimerDuration,
+	                                      TUint16 aERTXTimerDuration)
+
+	{
+	LOG_STATIC_FUNC
+	HConfigureRequest* cmd = NULL;
+	RMBufChain buf;
+	TRAPD(rerr, buf.AllocL(KConfigRequestEmptyLength));
+	if(rerr == KErrNone)
+		{
+		cmd = new HConfigureRequest(buf, aRTXTimerDuration, aERTXTimerDuration);
+		if(cmd)
+			{
+			// Setup message contents.
+			cmd->SetCode(EConfigureRequest);
+			cmd->SetID(KUninitialisedID);
+						
+			cmd->SetDestinationCID(aRemotePort);
+			cmd->SetFlags(KNoConfigurationFlags);
+
+			// Add the options
+			rerr = cmd->AddOptionsToCommand(aConfig);
+			if(rerr != KErrNone)
+				{
+				delete cmd;
+				cmd = NULL;
+				}
+			}
+		else
+			{
+			// Free the allocated buffer.
+			buf.Free();
+			}
+		}
+	return cmd;
+	}
+	
+/**
+Verifies that the buffer contains a structurally correct command. 
+This does not ensure that the content of the command is semantically valid.
+
+@param aCommmand A new HL2CapCommand if the buffer contains a structurally valid command.
+@param aBuffer The buffer containing the command
+@return KErrNone if the command if created.
+		KErrNoMemory if the command structure is valid but cannot be created.
+		KErrCorrupt if the command structure is invalid.
+*/
+TInt HConfigureRequest::NewCommandIfValid(RMBufChain& aBuffer, HL2CapCommand*& aCommand)
+	{
+	LOG_STATIC_FUNC
+	TInt length = aBuffer.Length();
+	if(length < KConfigRequestEmptyLength)
+		{
+		// Dodge length!
+		return KErrCorrupt;
+		}
+		
+	if(TL2CapConfigParamOptions::VerifyOptionsStructure(KOptionsOffset, aBuffer))
+		{
+		// Top!  The structure's fine, go ahead and create the command.
+		aCommand = new HConfigureRequest(aBuffer);
+		if(aCommand)
+			{
+			return KErrNone;
+			}
+		else
+			{
+			return KErrNoMemory;
+			}
+		}
+	else
+		{
+		// We'll not have any of this nonsense!
+		return KErrCorrupt;
+		}	
+	}
+	
+HConfigureRequest::HConfigureRequest(RMBufChain& aCommand,
+	  	               				 TUint8 aRTXTimerDuration,
+	                                 TUint16 aERTXTimerDuration)
+ : HL2CapCommand(aCommand, aRTXTimerDuration, aERTXTimerDuration)
+	{
+	LOG_FUNC
+	}
+
+TBool HConfigureRequest::ProcessCommand(CL2CapSignalHandler& aSignalHandler)
+	{
+	LOG_FUNC
+	if(aSignalHandler.HandleConfigureRequest(this))
+		{
+		// The command has been handled.  Delete it.
+		delete this;
+		return ETrue;
+		}
+	else
+		{
+		return EFalse;
+		}
+	}
+
+TInt HConfigureRequest::ParseOptions(TL2CapConfigParamOptions& aConfigOptions, RMBufChain& aUnknownOptions) const
+	{
+	LOG_FUNC
+	return aConfigOptions.ParseOptions(KOptionsOffset, iCommand, aUnknownOptions);
+	}
+
+TInt HConfigureRequest::AddOptionsToCommand(CL2CapChannelConfig& aConfig)
+	{
+	LOG_FUNC
+	TInt rerr = TL2CapConfigParamOptions::AddConfigRequestOptions(aConfig, KOptionsOffset, iCommand);
+	if(rerr == KErrNone)
+		{
+		WriteDataLength();
+		}
+	return rerr;
+	}
+
+
+//					CONFIGURE RESPONSE COMMAND 
+//Constructs a command response ready for transmission
+HConfigureResponse* HConfigureResponse::New(TUint8 aId, TL2CAPPort aRemotePort, TConfigFlags aFlags, TConfigResponseResult aResult) 
+	{
+	LOG_STATIC_FUNC
+	HConfigureResponse* cmd = NULL;
+	RMBufChain buf;
+	TRAPD(rerr, buf.AllocL(KConfigResponseEmptyLength));
+	if(rerr == KErrNone)
+		{
+		cmd = new HConfigureResponse(buf);
+		if(cmd)
+			{
+			// Setup message contents.
+			cmd->SetCode(EConfigureResponse);
+			cmd->SetID(aId);
+						
+			cmd->SetSourceCID(aRemotePort);
+			cmd->SetFlags(aFlags);
+			cmd->SetResults(aResult);
+			}
+		else
+			{
+			// Free the allocated buffer.
+			buf.Free();
+			}
+		}
+	return cmd;
+	}
+
+/**
+Verifies that the buffer contains a structurally correct command. 
+This does not ensure that the content of the command is semantically valid.
+
+@param aCommmand A new HL2CapCommand if the buffer contains a structurally valid command.
+@param aBuffer The buffer containing the command
+@return KErrNone if the command if created.
+		KErrNoMemory if the command structure is valid but cannot be created.
+		KErrCorrupt if the command structure is invalid.
+*/
+TInt HConfigureResponse::NewCommandIfValid(RMBufChain& aBuffer, HL2CapCommand*& aCommand)
+	{
+	LOG_STATIC_FUNC	
+	if(aBuffer.Length() < KConfigResponseEmptyLength)
+		{
+		// Dodge length!
+		return KErrCorrupt;
+		}
+		
+	if(TL2CapConfigParamOptions::VerifyOptionsStructure(KOptionsOffset, aBuffer))
+		{
+		// Top!  The structure's fine, go ahead and create the command.
+		aCommand = new HConfigureResponse(aBuffer);
+		if(aCommand)
+			{
+			return KErrNone;
+			}
+		else
+			{
+			return KErrNoMemory;
+			}
+		}
+	else
+		{
+		// We'll not have any of this nonsense!
+		return KErrCorrupt;
+		}	
+	}
+
+HConfigureResponse::HConfigureResponse(RMBufChain& aCommand) 
+ : HL2CapCommand(aCommand)
+	{
+	LOG_FUNC
+	}
+
+TBool HConfigureResponse::ProcessCommand(CL2CapSignalHandler& aSignalHandler)	
+	{
+	LOG_FUNC
+	if(aSignalHandler.HandleConfigureResponse(this))
+		{
+		// The command has been handled.  Delete it.
+		delete this;
+		return ETrue;
+		}
+	else
+		{
+		return EFalse;
+		}
+	}
+	
+//Put this here to allow base function to be protected, ie not all commands expose functionality
+TInt HConfigureResponse::ParseOptions(TL2CapConfigParamOptions& aConfigOptions, RMBufChain& aUnknownOptions) const
+	{
+	LOG_FUNC
+	return aConfigOptions.ParseOptions(KOptionsOffset, iCommand, aUnknownOptions);
+	}
+																									
+TInt HConfigureResponse::AddOptionsToCommand(CL2CapChannelConfig& aConfig, TConfigResponseResult aResult)
+	{
+	LOG_FUNC
+	TInt rerr = TL2CapConfigParamOptions::AddConfigResponseOptions(aConfig, KOptionsOffset, iCommand, aResult);
+	if(rerr == KErrNone)
+		{
+		WriteDataLength();
+		}
+	return rerr;
+	}
+
+void HConfigureResponse::AddUnknownOptionsToCommand(RMBufChain& aUnknownOptions)
+	{
+	LOG_FUNC
+	iCommand.Append(aUnknownOptions);
+	WriteDataLength();	
+	}
+
+
+TL2CapConfigParamOptions::TL2CapConfigParamOptions()
+ :	iMtuSet(EFalse),
+	iMtu(ML2CapConfigurationOption::ESpecDefaultValue),
+	iFlushTimeoutSet(EFalse),
+	iFlushTimeout(ML2CapConfigurationOption::ESpecDefaultValue),
+	iFecSet(EFalse),
+	iQosSet(EFalse),
+	iQos(ML2CapConfigurationOption::ESpecDefaultValue)
+	{
+	LOG_FUNC
+	}
+
+TBool TL2CapConfigParamOptions::operator==(const TL2CapConfigParamOptions& aThat)
+	{
+	LOG_FUNC
+	// The exactly same options must be included in both sets...
+	if (iMtuSet != aThat.iMtuSet ||
+		iFlushTimeoutSet != aThat.iFlushTimeoutSet ||
+		iFecSet != aThat.iFecSet ||
+		iQosSet != aThat.iQosSet)
+		{
+		return EFalse;
+		}
+	else
+		{
+		// ... and the included option's values must be the same.
+		return (!iMtuSet || iMtu == aThat.iMtu) &&
+			   (!iFlushTimeoutSet || iFlushTimeout == aThat.iFlushTimeout) &&
+			   (!iFecSet || iFec == aThat.iFec) &&
+			   (!iQosSet || iQos == aThat.iQos);
+		}
+	}
+
+TInt TL2CapConfigParamOptions::ParseOptions(TUint8 aOptionOffset, const RMBufChain& aCommand, RMBufChain& aUnknownOptions)
+	{
+	LOG_FUNC
+	TUint8 optionDataLen = 0;
+	TInt startOfCurrentOption = aOptionOffset;
+	TInt commandLength = aCommand.Length();
+	TInt rerr = KErrNone;
+
+	// Parse the command options.  Ensure that there is at least an option header
+	// left to read.
+	while(startOfCurrentOption + KOptionHeaderLength <= commandLength && 
+	      (rerr == KErrNone || rerr == KErrConfigUnknownOptions))
+		{
+		TBuf8<KOptionHeaderLength> headerBuf(KOptionHeaderLength);
+		aCommand.CopyOut(headerBuf, startOfCurrentOption);
+
+		TUint8 optionType = headerBuf[KTypeOffset];
+		optionDataLen = headerBuf[KLengthOffset];
+
+		// Switch on the option type. Option type could have hint bit set, remove it
+		switch(optionType & ~KOptionTypeIsHintMask)
+			{
+			case EConfigOptionTypeMTU:
+				{
+				TBuf8<KMTUOptionDataLen> mtuBuf(KMTUOptionDataLen);
+				aCommand.CopyOut(mtuBuf, startOfCurrentOption + KDataOffset);
+
+				TMTUOption mtu(LittleEndian::Get16(mtuBuf.Ptr()));
+				SetMtu(mtu);
+				}
+				break;
+
+			case EConfigOptionTypeFlushTimeoutDuration:
+				{
+				TBuf8<KFlushOptionDataLen> flushTimeoutBuf(KFlushOptionDataLen);
+				aCommand.CopyOut(flushTimeoutBuf, startOfCurrentOption + KDataOffset);
+
+				TFlushTimeoutDurationOption flushTimeoutDuration(LittleEndian::Get16(flushTimeoutBuf.Ptr()));
+				SetFlushTimeout(flushTimeoutDuration);
+				}
+				break;
+				
+			case EConfigOptionTypeQOS:
+				{
+				TBuf8<KQOSOptionDataLen> qosBuf(KQOSOptionDataLen);
+				aCommand.CopyOut(qosBuf, startOfCurrentOption + KDataOffset);
+				
+				TQualityOfServiceOption qos(static_cast<TQOSServiceType>(qosBuf[KQOSServiceTypeOffset]),
+				                            LittleEndian::Get32(qosBuf.Ptr()+KTokenRateOffset),
+											LittleEndian::Get32(qosBuf.Ptr()+KTokenBucketSizeOffset),
+											LittleEndian::Get32(qosBuf.Ptr()+KPeakBandwidthOffset),
+											LittleEndian::Get32(qosBuf.Ptr()+KLatencyOffset),
+											LittleEndian::Get32(qosBuf.Ptr()+KDelayVariationOffset));
+				SetQos(qos);
+				}
+				break;
+			
+			case EConfigOptionTypeRTxAndFEC:
+				{
+				TBuf8<KFECOptionDataLen> fecBuf(KFECOptionDataLen);
+				aCommand.CopyOut(fecBuf, startOfCurrentOption + KDataOffset);
+			
+				TRetransmissionAndFlowControlOption fec(static_cast<TL2CapChannelMode>(fecBuf[KLinkModeOffset]),
+														fecBuf[KTxWindowSizeOffset],
+														fecBuf[KMaxTransmitOffset],
+														LittleEndian::Get16(fecBuf.Ptr() + KRetransmissionTimeoutOffset),
+														LittleEndian::Get16(fecBuf.Ptr() + KMonitorTimeoutOffset),
+														LittleEndian::Get16(fecBuf.Ptr() + KMaximumPDUSizeOffset));
+				SetFec(fec);
+				}
+				break;
+
+			case EConfigOptionTypeFcs:
+				// Special case. Even though we don't support the 'FCS option' option
+				// (i.e. we always do FCS), this can be silently ignored because of
+				// its specific semantics: FCS is only disabled on a channel on which
+				// both ends include this option with the value = 'No FCS'. Since we
+				// will never do that, the value we receive from the peer has no effect
+				// and can be ignored instead of responding with 'Unknown Option',
+				// even though 'FCS Option' is 0 in our supported feature mask - some
+				// people still send the option even in this case.
+				break;
+				
+			default:
+				{
+				// Store the unknown options into the buffer argument.
+				// If the hint bit is set (MSB of option type field) ignore the unknown
+				// parameter.
+				if(!(optionType & KOptionTypeIsHintMask))
+					{
+					// This is not a hint option - add it to the list of unknown options
+					// to be sent in the subsequent response.
+					RMBufChain unknownOption;
+					TRAP(rerr, aCommand.CopyL(unknownOption, startOfCurrentOption, KOptionHeaderLength + optionDataLen));
+					if(rerr == KErrNone)
+						{
+						aUnknownOptions.Append(unknownOption);
+						rerr = KErrConfigUnknownOptions;
+						}
+					}
+				}
+				break;
+			}
+
+		// Move to the next option.
+		startOfCurrentOption += (optionDataLen + KOptionHeaderLength);
+		}
+	return rerr;
+	}
+
+/*static*/ TInt TL2CapConfigParamOptions::AddConfigResponseOptions(CL2CapChannelConfig& aConfig, TUint8 aOptionOffset, RMBufChain& aOptions, TConfigResponseResult aResult)
+	{
+	LOG_STATIC_FUNC
+	TInt rerr = KErrNone;
+	TInt currentCommandIndex = 0;
+	// General rule is that we include accepted options only in positive responses.
+	TBool isUnacceptableParams = (aResult == EConfigUnacceptableParams);
+
+	const TL2CapConfigurationOptionGroupBase::TOptionConfigStatus mtuStatus = aConfig.OutgoingMTU().ConfigOptionStatus();
+	const TL2CapConfigurationOptionGroupBase::TOptionConfigStatus flushToStatus = aConfig.IncomingFlushTimeout().ConfigOptionStatus();
+	const TL2CapConfigurationOptionGroupBase::TOptionConfigStatus qosStatus = aConfig.IncomingQOS().ConfigOptionStatus();
+	const TL2CapConfigurationOptionGroupBase::TOptionConfigStatus fecStatus = aConfig.FecNegotiator().OutgoingConfigOptionStatus();
+
+	__ASSERT_DEBUG(isUnacceptableParams ||
+					(mtuStatus != TL2CapConfigurationOptionGroupBase::EOptionConfigOutstanding &&
+					 flushToStatus != TL2CapConfigurationOptionGroupBase::EOptionConfigOutstanding &&
+					 qosStatus != TL2CapConfigurationOptionGroupBase::EOptionConfigOutstanding &&
+					 fecStatus != TL2CapConfigurationOptionGroupBase::EOptionConfigOutstanding),
+				   Panic(EL2CapConstructingPositiveConfigResponseWithUnresolvedOptionStatus));
+
+	// The BT spec (sec 5.1) specifies that positive Config Responses will always
+	// contain the MTU that is to be used on the channel.
+	if (!(isUnacceptableParams && mtuStatus != TL2CapConfigurationOptionGroupBase::EOptionConfigFailed))
+		{
+		TRAP(rerr, currentCommandIndex += AddMtuOptionL(aConfig.OutgoingMTU().Preferred().MTU(), 
+											aOptionOffset + currentCommandIndex, 
+											aOptions));
+		}
+
+	if (rerr == KErrNone &&
+		((isUnacceptableParams && flushToStatus == TL2CapConfigurationOptionGroupBase::EOptionConfigFailed) ||
+		 (!isUnacceptableParams && aConfig.IncomingFlushTimeout().NeedToIncludeInPositiveConfigResponse())))
+		{
+		TRAP(rerr, currentCommandIndex += AddFlushOptionL(aConfig.IncomingFlushTimeout().Preferred().FlushTimeoutDuration(), 
+											aOptionOffset + currentCommandIndex, 
+											aOptions));
+		}
+
+	if (rerr == KErrNone &&
+		((isUnacceptableParams && qosStatus == TL2CapConfigurationOptionGroupBase::EOptionConfigFailed) ||
+		 (!isUnacceptableParams && aConfig.IncomingQOS().NeedToIncludeInPositiveConfigResponse())))
+		{
+		TRAP(rerr, currentCommandIndex += AddQosOptionL(aConfig.IncomingQOS().Preferred(), 
+											aOptionOffset + currentCommandIndex,
+											aOptions));
+		}
+
+	if (rerr == KErrNone &&
+		((isUnacceptableParams && fecStatus == TL2CapConfigurationOptionGroupBase::EOptionConfigFailed) ||
+		 (!isUnacceptableParams && aConfig.FecNegotiator().NeedToIncludeInPositiveConfigResponse())))
+		{
+		TRAP(rerr, currentCommandIndex += AddFecOptionL(aConfig.FecNegotiator().OutgoingPreferred(),
+											aOptionOffset + currentCommandIndex,
+											aOptions));
+		}
+
+	return rerr;
+	}
+
+/*static*/ TInt TL2CapConfigParamOptions::AddConfigRequestOptions(CL2CapChannelConfig& aConfig, TUint8 aOptionOffset, RMBufChain& aOptions)
+	{
+	LOG_STATIC_FUNC
+	TInt rerr = KErrNone;
+	TInt currentCommandIndex = 0;
+	
+	// Config Request parameters.
+	
+	if(aConfig.IncomingMTU().ConfigOptionStatus() != TL2CapConfigurationOptionGroupBase::EOptionConfigComplete && rerr == KErrNone)
+		{		
+		TRAP(rerr, currentCommandIndex += AddMtuOptionL(aConfig.IncomingMTU().Preferred().MTU(), 
+											aOptionOffset + currentCommandIndex, 
+											aOptions));
+		}
+
+	if(aConfig.OutgoingFlushTimeout().ConfigOptionStatus() != TL2CapConfigurationOptionGroupBase::EOptionConfigComplete && rerr == KErrNone)
+		{		
+		TRAP(rerr, currentCommandIndex += AddFlushOptionL(aConfig.OutgoingFlushTimeout().Preferred().FlushTimeoutDuration(), 
+											  aOptionOffset + currentCommandIndex, 
+											  aOptions));
+		}
+
+	if(aConfig.OutgoingQOS().ConfigOptionStatus() != TL2CapConfigurationOptionGroupBase::EOptionConfigComplete && rerr == KErrNone)
+		{			
+		TRAP(rerr, currentCommandIndex += AddQosOptionL(aConfig.OutgoingQOS().Preferred(), 
+											aOptionOffset + currentCommandIndex,
+											aOptions));
+		}
+		
+	if(aConfig.FecNegotiator().IncomingConfigOptionStatus() != TL2CapConfigurationOptionGroupBase::EOptionConfigComplete && rerr == KErrNone)
+		{
+		TRAP(rerr, currentCommandIndex += AddFecOptionL(aConfig.FecNegotiator().IncomingPreferred(),
+											aOptionOffset + currentCommandIndex,
+											aOptions));
+		}
+
+	return rerr;		
+	}
+
+/* static */ TInt TL2CapConfigParamOptions::AddMtuOptionL(TInt16 aMtu, TUint8 aOptionOffset, RMBufChain& aOptions)
+	{
+	LOG_STATIC_FUNC
+	aOptions.AppendL(KOptionHeaderLength + KMTUOptionDataLen);
+	
+	TBuf8<KOptionHeaderLength + KMTUOptionDataLen> mtuBuf(KOptionHeaderLength + KMTUOptionDataLen);
+	
+	mtuBuf[KTypeOffset] = EConfigOptionTypeMTU;
+	mtuBuf[KLengthOffset] = KMTUOptionDataLen;
+	LittleEndian::Put16(&mtuBuf[KDataOffset], aMtu);
+
+	aOptions.CopyIn(mtuBuf, aOptionOffset);
+	
+	return KOptionHeaderLength + KMTUOptionDataLen;
+	}
+	
+/* static */ TInt TL2CapConfigParamOptions::AddFlushOptionL(TInt16 aFlushTimeout, TUint8 aOptionOffset, RMBufChain& aOptions)
+	{
+	LOG_STATIC_FUNC
+	aOptions.AppendL(KOptionHeaderLength + KFlushOptionDataLen);
+	
+	TBuf8<KOptionHeaderLength + KFlushOptionDataLen> flushBuf(KOptionHeaderLength + KFlushOptionDataLen);
+
+	flushBuf[KTypeOffset] = EConfigOptionTypeFlushTimeoutDuration;
+	flushBuf[KLengthOffset] = KFlushOptionDataLen;
+	LittleEndian::Put16(&flushBuf[KDataOffset], aFlushTimeout);
+
+	aOptions.CopyIn(flushBuf, aOptionOffset);
+	
+	return KOptionHeaderLength + KFlushOptionDataLen;
+	}
+	
+/* static */ TInt TL2CapConfigParamOptions::AddQosOptionL(const TQualityOfServiceOption& aQosOption, TUint8 aOptionOffset, RMBufChain& aOptions)
+	{
+	LOG_STATIC_FUNC
+	aOptions.AppendL(KOptionHeaderLength + KQOSOptionDataLen);
+	
+	TBuf8<KOptionHeaderLength + KQOSOptionDataLen> qosBuf(KOptionHeaderLength + KQOSOptionDataLen);
+
+	qosBuf[KTypeOffset] = EConfigOptionTypeQOS;
+	qosBuf[KLengthOffset] = KQOSOptionDataLen;
+	qosBuf[KDataOffset] = 0;
+
+	// Now insert each of the QOS options
+	qosBuf[KDataOffset + KQOSServiceTypeOffset] = static_cast<TUint8>(aQosOption.ServiceType());
+	LittleEndian::Put32(&qosBuf[KDataOffset + KTokenRateOffset], aQosOption.TokenRate());	
+	LittleEndian::Put32(&qosBuf[KDataOffset + KTokenBucketSizeOffset], aQosOption.TokenBucketSize());	
+	LittleEndian::Put32(&qosBuf[KDataOffset + KPeakBandwidthOffset], aQosOption.PeakBandwidth());	
+	LittleEndian::Put32(&qosBuf[KDataOffset + KLatencyOffset], aQosOption.Latency());	
+	LittleEndian::Put32(&qosBuf[KDataOffset + KDelayVariationOffset], aQosOption.DelayVariation());	
+	
+	aOptions.CopyIn(qosBuf, aOptionOffset);	
+	
+	return KOptionHeaderLength + KQOSOptionDataLen;
+	}
+
+/* static */ TInt TL2CapConfigParamOptions::AddFecOptionL(const TRetransmissionAndFlowControlOption& aFecOption, TUint8 aOptionOffset, RMBufChain& aOptions)
+	{
+	LOG_STATIC_FUNC
+	aOptions.AppendL(KOptionHeaderLength + KFECOptionDataLen);
+	
+	TBuf8<KOptionHeaderLength + KFECOptionDataLen> fecBuf(KOptionHeaderLength + KFECOptionDataLen);
+
+	fecBuf[KTypeOffset] = EConfigOptionTypeRTxAndFEC;	
+	fecBuf[KLengthOffset] = KFECOptionDataLen;
+	
+	fecBuf[KDataOffset] = aFecOption.LinkModeAsUnsignedByte();	
+	fecBuf[KDataOffset + KTxWindowSizeOffset] = aFecOption.TxWindowSize();	
+	fecBuf[KDataOffset + KMaxTransmitOffset] = aFecOption.MaxTransmit();	
+	LittleEndian::Put16(&fecBuf[KDataOffset + KRetransmissionTimeoutOffset], aFecOption.RetransmissionTimeout());	
+	LittleEndian::Put16(&fecBuf[KDataOffset + KMonitorTimeoutOffset], aFecOption.MonitorTimeout());	
+	LittleEndian::Put16(&fecBuf[KDataOffset + KMaximumPDUSizeOffset], aFecOption.MaximumPDUSize());	
+
+	aOptions.CopyIn(fecBuf, aOptionOffset);
+	
+	return KOptionHeaderLength + KFECOptionDataLen;
+	}
+
+/* static */ TBool TL2CapConfigParamOptions::VerifyOptionsStructure(TUint8 aOptionOffset, const RMBufChain& aCommand)
+	{
+	LOG_STATIC_FUNC
+	TUint8 optionDataLen = 0;
+	TInt startOfCurrentOption = aOptionOffset;
+	TInt commandLength = aCommand.Length();
+	TBuf8<KOptionHeaderLength> headerBuf(KOptionHeaderLength);
+
+	TBool valid = ETrue;
+	// Parse the command options.  Ensure that there is at least an option header
+	// left to read.
+	while(startOfCurrentOption + KOptionHeaderLength <= commandLength && valid)
+		{
+		aCommand.CopyOut(headerBuf, startOfCurrentOption);
+		
+		TUint8 optionType = headerBuf[KTypeOffset];
+		optionDataLen = headerBuf[KLengthOffset];
+
+		// Switch on the option type. Option type could have hint bit set, remove it
+		switch(optionType & ~KOptionTypeIsHintMask)
+			{
+			case EConfigOptionTypeMTU:
+				if(optionDataLen != KMTUOptionDataLen || 
+				   startOfCurrentOption + KOptionHeaderLength + optionDataLen > commandLength)
+					{
+					valid = EFalse;
+					}
+				break;
+
+			case EConfigOptionTypeFlushTimeoutDuration:
+				if(optionDataLen != KFlushOptionDataLen || 
+				   startOfCurrentOption + KOptionHeaderLength + optionDataLen > commandLength)
+					{
+					valid = EFalse;
+					}
+				break;
+				
+			case EConfigOptionTypeQOS:
+				if(optionDataLen != KQOSOptionDataLen || 
+				   startOfCurrentOption + KOptionHeaderLength + optionDataLen > commandLength)
+					{
+					valid = EFalse;
+					}
+				break;
+			
+			case EConfigOptionTypeRTxAndFEC:
+				if(optionDataLen != KFECOptionDataLen || 
+				   startOfCurrentOption + KOptionHeaderLength + optionDataLen > commandLength)
+					{
+					valid = EFalse;
+					}
+				break;
+
+			case EConfigOptionTypeFcs:
+				if(optionDataLen != KFcsOptionDataLen || 
+			       startOfCurrentOption + KOptionHeaderLength + optionDataLen > commandLength)
+					{
+					valid = EFalse;
+					}
+				break;
+				
+			default:
+				// Store the unknown options into the buffer argument.
+				if(startOfCurrentOption + KOptionHeaderLength + optionDataLen > commandLength)
+					{
+					// The option is not correctly formatted.
+					valid = EFalse;
+					}
+				break;
+			};
+		// Move to the next option.
+		startOfCurrentOption += (optionDataLen + KOptionHeaderLength);
+		}
+
+	if(startOfCurrentOption != commandLength)
+		{
+		// Spurious bytes
+		valid = EFalse;
+		}
+
+	return valid;
+	}
+