bluetooth/btstack/l2cap/L2CapSDU.cpp
changeset 0 29b1cd4cb562
equal deleted inserted replaced
-1:000000000000 0:29b1cd4cb562
       
     1 // Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include <bluetooth/logger.h>
       
    17 
       
    18 #include "btsockettimer.h"
       
    19 
       
    20 #include "L2CapSDU.h"
       
    21 #include "L2CapPDU.h"
       
    22 #include "l2util.h"
       
    23 #include "L2CapDebugControlInterface.h"
       
    24 
       
    25 #ifdef _DEBUG
       
    26 #include "L2CapDebugControlInterface.h"
       
    27 #endif
       
    28 
       
    29 #ifdef __FLOG_ACTIVE
       
    30 _LIT8(KLogComponent, LOG_COMPONENT_L2CAP_SDU);
       
    31 #endif
       
    32 
       
    33 CL2CapSDU* CL2CapSDU::NewLC(RMBufChain& aSDUData,
       
    34                             ML2CapSDUHandler& aParent,
       
    35 			                TUint16 aPDUSize,
       
    36                             TBool aIsBasicDataVersion,
       
    37 			                TUint16 aTimerDuration)
       
    38 	{
       
    39 	LOG_STATIC_FUNC
       
    40 	CL2CapSDU* self = new(ELeave) CL2CapSDU(aParent);
       
    41 	CleanupStack::PushL(self);
       
    42 	self->ConstructL(aSDUData, aIsBasicDataVersion, aTimerDuration, aPDUSize);
       
    43 	return self;
       
    44 	}
       
    45 
       
    46 
       
    47 CL2CapSDU* CL2CapSDU::NewL(RMBufChain& aSDUData,
       
    48 		                   ML2CapSDUHandler& aParent,
       
    49 			               TUint16 aPDUSize,
       
    50 		                   TBool aIsBasicDataVersion, 
       
    51 			               TUint16 aTimerDuration)
       
    52 	{
       
    53 	LOG_STATIC_FUNC
       
    54 	CL2CapSDU* self = NewLC(aSDUData, aParent, aPDUSize, aIsBasicDataVersion, aTimerDuration);
       
    55 	CleanupStack::Pop();
       
    56 	return self;
       
    57 	}
       
    58 
       
    59 CL2CapSDU::CL2CapSDU(ML2CapSDUHandler& aParent)
       
    60  : iParent(aParent),
       
    61    iPDUs(_FOFF(HL2CapPDU, iLink)),
       
    62    iCurrentPDU(iPDUs)
       
    63 	{
       
    64 	LOG_FUNC
       
    65 	L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESDU,
       
    66 	                             L2capDebugInfo::EAllocated));
       
    67 	}
       
    68 
       
    69 void CL2CapSDU::ConstructL(RMBufChain& aSDUData, TBool aIsBasicDataVersion, TUint16 aTimerDuration, TUint16 aPDUSize)
       
    70 	{
       
    71 	LOG_FUNC
       
    72 	LEAVEIFERRORL(SegmentSDUIntoPDUs(aSDUData, aIsBasicDataVersion, aPDUSize));
       
    73 	
       
    74 	// Start the flush timer if required.
       
    75 	StartFlushTimer(aTimerDuration);
       
    76 	}
       
    77 
       
    78 CL2CapSDU::~CL2CapSDU()
       
    79 	{
       
    80 	LOG_FUNC
       
    81 	iLink.Deque();
       
    82 	iCurrentPDU.SetToFirst();
       
    83 	HL2CapPDU* pduPtr;
       
    84 	while(iCurrentPDU)
       
    85 		{
       
    86 		pduPtr = iCurrentPDU++;
       
    87 		pduPtr->iLink.Deque();
       
    88 		delete pduPtr;
       
    89 		}
       
    90 
       
    91 	if(iFlushTimerRunning)
       
    92 		{
       
    93 		BTSocketTimer::Remove(iFlushTimerEntry);
       
    94 		}	
       
    95 
       
    96 	L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESDU,
       
    97 	                             L2capDebugInfo::EDeleted));
       
    98 	}
       
    99 
       
   100 /*
       
   101  * Function to return any additional SDU L2CAP overhead. This is in addition to any PDU
       
   102  * overhead as defined by HL2CapPDU::GetPDUOverhead.
       
   103  */
       
   104 /*static*/ TInt CL2CapSDU::GetSDUOverhead(TBool aBasicMode)
       
   105 	{
       
   106 	LOG_STATIC_FUNC
       
   107 	if (aBasicMode)
       
   108 		{
       
   109 		return HL2CapPDU::KPDUHeaderLength;
       
   110 		}
       
   111 
       
   112 	// Non-basic mode overhead
       
   113 	return HL2CapPDU::KSDULengthFieldLength;
       
   114 	}
       
   115 
       
   116 TInt CL2CapSDU::SegmentSDUIntoPDUs(RMBufChain& aSDUData, TBool aIsBasicDataVersion, TUint16 aPDUPayloadSize)
       
   117 	{
       
   118 	LOG_FUNC
       
   119 	TInt sduLength = aSDUData.Length();
       
   120 	TInt rerr = KErrNone;
       
   121 		
       
   122 	if(aIsBasicDataVersion)
       
   123 		{
       
   124 		// This is basic mode.  The entire SDU should be placed into
       
   125 		// a B-Frame PDU.
       
   126 		HBFramePDU* pdu = HBFramePDU::New(aSDUData, aPDUPayloadSize);
       
   127 		if(pdu)
       
   128 			{
       
   129 			iPDUs.AddLast(*pdu);
       
   130 			L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EBFrameCreated, pdu));
       
   131 			}
       
   132 		else
       
   133 			{
       
   134 			rerr = KErrNoMemory;
       
   135 			}
       
   136 		}
       
   137 	else
       
   138 		{
       
   139 		// Check if the SDU needs to be segmented.
       
   140 		if(aPDUPayloadSize >= sduLength)
       
   141 			{
       
   142 			HIFramePDU* pdu = HIFramePDU::New(aSDUData, EUnsegmentedL2CapSDU);
       
   143 			if(pdu)
       
   144 				{
       
   145 				iPDUs.AddLast(*pdu);
       
   146 				L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EUnsegmentedFrameCreated, pdu));
       
   147 				}
       
   148 			else
       
   149 				{
       
   150 				rerr = KErrNoMemory;
       
   151 				}				
       
   152 			}
       
   153 		else
       
   154 			{
       
   155 			// Work backwards through the SDU segmenting it into PDU's. The first segment will always
       
   156 			// contain an extra SDU length field so this is taken into account when working out the 
       
   157 			// size of the last segment below.
       
   158 			TInt sduPosition = sduLength - ((sduLength + HL2CapPDU::KSDULengthFieldLength) % aPDUPayloadSize);
       
   159 
       
   160 			// If the PDU size is a factor of the SDU length then start from the
       
   161 			// SDU length - PDU size.
       
   162 			if(sduPosition == sduLength)
       
   163 				{
       
   164 				sduPosition -= aPDUPayloadSize;
       
   165 				}
       
   166 												
       
   167 			HIFramePDU* pdu = NULL;
       
   168 			RMBufChain pduData;
       
   169 			
       
   170 			TRAP(rerr, aSDUData.SplitL(sduPosition, pduData));
       
   171 			if(rerr == KErrNone)
       
   172 				{
       
   173 				pdu = HIFramePDU::New(pduData, EEndOfL2CapSDU);
       
   174 				if(pdu)
       
   175 					{
       
   176 					sduPosition -= aPDUPayloadSize;
       
   177 					iPDUs.AddFirst(*pdu);
       
   178 					L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EIFrameCreated, pdu));
       
   179 					
       
   180 					while(sduPosition > 0)
       
   181 						{
       
   182 						__ASSERT_DEBUG(sduPosition >= 0, Panic(EL2CAPSDUPositionNegativeDuringSegmentation));
       
   183 
       
   184 						TRAP(rerr, aSDUData.SplitL(sduPosition, pduData));
       
   185 						if(rerr == KErrNone)
       
   186 							{
       
   187 							pdu = HIFramePDU::New(pduData, EContinuationOfL2CapSDU);
       
   188 							if(pdu)
       
   189 								{
       
   190 								sduPosition -= aPDUPayloadSize;
       
   191 								iPDUs.AddFirst(*pdu);
       
   192 								}
       
   193 							else
       
   194 								{
       
   195 								rerr = KErrNoMemory;
       
   196 								break;
       
   197 								}
       
   198 							}
       
   199 						else
       
   200 							{
       
   201 							break;
       
   202 							}							
       
   203 						}
       
   204 					
       
   205 					if(rerr == KErrNone)
       
   206 						{
       
   207 						// Create and add the first PDU.
       
   208 						pdu = HIFramePDU::New(aSDUData, EStartOfL2CapSDU);
       
   209 						if(pdu)
       
   210 							{
       
   211 							pdu->SetSDUSize(static_cast<TUint16>(sduLength));
       
   212 							iPDUs.AddFirst(*pdu);
       
   213 							}
       
   214 						else
       
   215 							{
       
   216 							rerr = KErrNoMemory;
       
   217 							}
       
   218 						}
       
   219 					}
       
   220 				else
       
   221 					{
       
   222 					rerr = KErrNoMemory;
       
   223 					}				
       
   224 				}
       
   225 			}
       
   226 		}
       
   227 		
       
   228 	if(rerr == KErrNone)
       
   229 		{
       
   230 		// Set the current PDU pointer to the first PDU.
       
   231 		iCurrentPDU.SetToFirst();	
       
   232 		}
       
   233 	return rerr;
       
   234 	}
       
   235 
       
   236 TBool CL2CapSDU::GetPDU(HL2CapPDU*& aReturnedPDU)
       
   237 	{
       
   238 	LOG_FUNC
       
   239 	TBool isLastPDU = EFalse;
       
   240 	if(!iPDUs.IsEmpty())
       
   241 		{
       
   242 		aReturnedPDU = iCurrentPDU++;
       
   243 
       
   244 		// TODO: this was put in as a fix and is in needed in general, but it also
       
   245 		// actually hoses up some logic, i.e. CurrentPDUIsFirstPDU will always return
       
   246 		// true. We either need a separate link for the list of PDUs inside the SDU,
       
   247 		// or if possible just get rid of iCurrentPDU and have a iFirstPDU flag
       
   248 		// set to one during initial segmentation.
       
   249 		// Note - we the iCurrentPDU/iFirstPDU business is actually only used in two cases:
       
   250 		// 1. to repack current SDU when PDU size has changed on a live connection - currently
       
   251 		//    unused and unnecessary.
       
   252 		// 2. on flush timer expiry, to mark already sent packets as flushed and purge the
       
   253 		//    unsent ones - currently unused, but may get implemented in the future. However
       
   254 		//    the marking of the packets may be done their current owner, which is a data
       
   255 		//    controller or the muxer, so again this is unnecessary.
       
   256 		// Conclusion: refactor to simplify.
       
   257 		aReturnedPDU->iLink.Deque();
       
   258 
       
   259 		if(iCurrentPDU == NULL)
       
   260 			{
       
   261 			// At the end of the PDU list.
       
   262 			L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EGetPDUCalled, aReturnedPDU));
       
   263 			isLastPDU = ETrue;
       
   264 			}
       
   265 		}
       
   266 
       
   267 	return isLastPDU;
       
   268 	}
       
   269 	
       
   270 
       
   271 /*static*/ TInt CL2CapSDU::FlushTimerExpired(TAny* aL2CapSDU)
       
   272 	{
       
   273 	LOG_STATIC_FUNC
       
   274 	CL2CapSDU* sdu = reinterpret_cast<CL2CapSDU*>(aL2CapSDU);
       
   275 	sdu->HandleFlushTimerExpired();
       
   276 	return EFalse;
       
   277 	}
       
   278 	
       
   279 void CL2CapSDU::HandleFlushTimerExpired()
       
   280 	{
       
   281 	LOG_FUNC
       
   282 	// Note that the timer is no longer running.
       
   283 	iFlushTimerRunning = EFalse;
       
   284 
       
   285 	// Any PDU's that have already been sent need to be
       
   286 	// informed of the flush.
       
   287 	TDblQueIter<HL2CapPDU> pduIter(iPDUs);
       
   288 	TBool done = EFalse;
       
   289 
       
   290 	HL2CapPDU* pduPtr;
       
   291 	while((pduPtr = pduIter++) != NULL  && !done)
       
   292 		{
       
   293 		if(pduPtr == iCurrentPDU)
       
   294 			{
       
   295 			done = ETrue;
       
   296 			}
       
   297 		else
       
   298 			{
       
   299 			pduPtr->SetPDUFlushed();
       
   300 			}
       
   301 		}
       
   302 
       
   303 	// The remaining PDU's (if any) that have not yet
       
   304 	// been sent can be deleted. 
       
   305 	while((pduPtr = iCurrentPDU++) != NULL)
       
   306 		{
       
   307 		pduPtr->iLink.Deque();
       
   308 		delete pduPtr;
       
   309 		}
       
   310 
       
   311 	// Inform the SDU queue.
       
   312 	iParent.ProcessFlushTimerExpiry(*this);
       
   313 	}
       
   314 	
       
   315 TBool CL2CapSDU::IsSDUEmpty() const
       
   316 	{
       
   317 	LOG_FUNC
       
   318 	return iPDUs.IsEmpty();
       
   319 	}
       
   320 
       
   321 	
       
   322 TBool CL2CapSDU::CurrentPDUIsFirstPDU()
       
   323 	{
       
   324 	LOG_FUNC
       
   325 	return iPDUs.IsFirst(iCurrentPDU);
       
   326 	}
       
   327 
       
   328 void CL2CapSDU::StartFlushTimer(TUint16 aTimerDuration)
       
   329 	{
       
   330 	LOG_FUNC
       
   331 	__ASSERT_DEBUG(!iFlushTimerRunning, Panic(EL2CAPAttemptToRestartFlushTimer));
       
   332 	
       
   333 	// Only start the timer if the duration is valid.
       
   334 	if(aTimerDuration >= TL2CapConfig::EMinDataObsolescenceTimeout && 
       
   335 	   aTimerDuration != KInfiniteFlush)
       
   336 		{
       
   337 		TCallBack cb(FlushTimerExpired, this);
       
   338 		iFlushTimerEntry.Set(cb);
       
   339 		// Timer period is a factor of the duration parameter and 0.625ms
       
   340 		BTSocketTimer::Queue(aTimerDuration*625, iFlushTimerEntry);
       
   341 		iFlushTimerRunning = ETrue;
       
   342 		}
       
   343 	}
       
   344 	
       
   345 TInt CL2CapSDU::ChangeSDUSegmentation(TBool aIsBasicDataVersion, TUint16 aPDUSize)
       
   346 	{
       
   347 	LOG_FUNC
       
   348 	__ASSERT_DEBUG(CurrentPDUIsFirstPDU(), Panic(EL2CAPAttemptToChangeSegmentationForPartiallySentSDU));
       
   349 
       
   350 	TInt rerr = KErrNone;
       
   351 	RMBufChain sduData;
       
   352 
       
   353 	// Get the current SDU data, and delete all current
       
   354 	// PDU's
       
   355 	TDblQueIter<HL2CapPDU> iter(iPDUs);
       
   356 	HL2CapPDU* pduPtr;
       
   357 	while((pduPtr = iter++) != NULL)
       
   358 		{
       
   359 		pduPtr->AppendPayloadToBuffer(sduData);
       
   360 		pduPtr->iLink.Deque();
       
   361 		delete pduPtr;
       
   362 		}
       
   363 		
       
   364 	rerr = SegmentSDUIntoPDUs(sduData, aIsBasicDataVersion, aPDUSize);
       
   365 	
       
   366 	return rerr;
       
   367 	}
       
   368 
       
   369 
       
   370 #ifdef _DEBUG
       
   371 TInt CL2CapSDU::DebugManualFlush()
       
   372 	{
       
   373 	LOG_FUNC
       
   374 	TInt rcode = -1;
       
   375 	
       
   376 	if(!IsSDUEmpty())
       
   377 		{
       
   378 		RMBuf* buf = iPDUs.First()->PDUBuffer().First();
       
   379 		if(buf->Length() > 8)
       
   380 			{
       
   381 			rcode = buf->Get(8);
       
   382 			}
       
   383 		else
       
   384 			{
       
   385 			buf = buf->Next();
       
   386 			if(buf)
       
   387 				{
       
   388 				rcode = buf->Get(0);
       
   389 				}
       
   390 			}
       
   391 		}
       
   392 		
       
   393 	if(iFlushTimerRunning)
       
   394 		{
       
   395 		BTSocketTimer::Remove(iFlushTimerEntry);
       
   396 		}
       
   397 	HandleFlushTimerExpired();
       
   398 	return rcode;
       
   399 	}
       
   400 #endif