--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/l2cap/L2CapSDU.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,400 @@
+// 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 "btsockettimer.h"
+
+#include "L2CapSDU.h"
+#include "L2CapPDU.h"
+#include "l2util.h"
+#include "L2CapDebugControlInterface.h"
+
+#ifdef _DEBUG
+#include "L2CapDebugControlInterface.h"
+#endif
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_L2CAP_SDU);
+#endif
+
+CL2CapSDU* CL2CapSDU::NewLC(RMBufChain& aSDUData,
+ ML2CapSDUHandler& aParent,
+ TUint16 aPDUSize,
+ TBool aIsBasicDataVersion,
+ TUint16 aTimerDuration)
+ {
+ LOG_STATIC_FUNC
+ CL2CapSDU* self = new(ELeave) CL2CapSDU(aParent);
+ CleanupStack::PushL(self);
+ self->ConstructL(aSDUData, aIsBasicDataVersion, aTimerDuration, aPDUSize);
+ return self;
+ }
+
+
+CL2CapSDU* CL2CapSDU::NewL(RMBufChain& aSDUData,
+ ML2CapSDUHandler& aParent,
+ TUint16 aPDUSize,
+ TBool aIsBasicDataVersion,
+ TUint16 aTimerDuration)
+ {
+ LOG_STATIC_FUNC
+ CL2CapSDU* self = NewLC(aSDUData, aParent, aPDUSize, aIsBasicDataVersion, aTimerDuration);
+ CleanupStack::Pop();
+ return self;
+ }
+
+CL2CapSDU::CL2CapSDU(ML2CapSDUHandler& aParent)
+ : iParent(aParent),
+ iPDUs(_FOFF(HL2CapPDU, iLink)),
+ iCurrentPDU(iPDUs)
+ {
+ LOG_FUNC
+ L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESDU,
+ L2capDebugInfo::EAllocated));
+ }
+
+void CL2CapSDU::ConstructL(RMBufChain& aSDUData, TBool aIsBasicDataVersion, TUint16 aTimerDuration, TUint16 aPDUSize)
+ {
+ LOG_FUNC
+ LEAVEIFERRORL(SegmentSDUIntoPDUs(aSDUData, aIsBasicDataVersion, aPDUSize));
+
+ // Start the flush timer if required.
+ StartFlushTimer(aTimerDuration);
+ }
+
+CL2CapSDU::~CL2CapSDU()
+ {
+ LOG_FUNC
+ iLink.Deque();
+ iCurrentPDU.SetToFirst();
+ HL2CapPDU* pduPtr;
+ while(iCurrentPDU)
+ {
+ pduPtr = iCurrentPDU++;
+ pduPtr->iLink.Deque();
+ delete pduPtr;
+ }
+
+ if(iFlushTimerRunning)
+ {
+ BTSocketTimer::Remove(iFlushTimerEntry);
+ }
+
+ L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESDU,
+ L2capDebugInfo::EDeleted));
+ }
+
+/*
+ * Function to return any additional SDU L2CAP overhead. This is in addition to any PDU
+ * overhead as defined by HL2CapPDU::GetPDUOverhead.
+ */
+/*static*/ TInt CL2CapSDU::GetSDUOverhead(TBool aBasicMode)
+ {
+ LOG_STATIC_FUNC
+ if (aBasicMode)
+ {
+ return HL2CapPDU::KPDUHeaderLength;
+ }
+
+ // Non-basic mode overhead
+ return HL2CapPDU::KSDULengthFieldLength;
+ }
+
+TInt CL2CapSDU::SegmentSDUIntoPDUs(RMBufChain& aSDUData, TBool aIsBasicDataVersion, TUint16 aPDUPayloadSize)
+ {
+ LOG_FUNC
+ TInt sduLength = aSDUData.Length();
+ TInt rerr = KErrNone;
+
+ if(aIsBasicDataVersion)
+ {
+ // This is basic mode. The entire SDU should be placed into
+ // a B-Frame PDU.
+ HBFramePDU* pdu = HBFramePDU::New(aSDUData, aPDUPayloadSize);
+ if(pdu)
+ {
+ iPDUs.AddLast(*pdu);
+ L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EBFrameCreated, pdu));
+ }
+ else
+ {
+ rerr = KErrNoMemory;
+ }
+ }
+ else
+ {
+ // Check if the SDU needs to be segmented.
+ if(aPDUPayloadSize >= sduLength)
+ {
+ HIFramePDU* pdu = HIFramePDU::New(aSDUData, EUnsegmentedL2CapSDU);
+ if(pdu)
+ {
+ iPDUs.AddLast(*pdu);
+ L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EUnsegmentedFrameCreated, pdu));
+ }
+ else
+ {
+ rerr = KErrNoMemory;
+ }
+ }
+ else
+ {
+ // Work backwards through the SDU segmenting it into PDU's. The first segment will always
+ // contain an extra SDU length field so this is taken into account when working out the
+ // size of the last segment below.
+ TInt sduPosition = sduLength - ((sduLength + HL2CapPDU::KSDULengthFieldLength) % aPDUPayloadSize);
+
+ // If the PDU size is a factor of the SDU length then start from the
+ // SDU length - PDU size.
+ if(sduPosition == sduLength)
+ {
+ sduPosition -= aPDUPayloadSize;
+ }
+
+ HIFramePDU* pdu = NULL;
+ RMBufChain pduData;
+
+ TRAP(rerr, aSDUData.SplitL(sduPosition, pduData));
+ if(rerr == KErrNone)
+ {
+ pdu = HIFramePDU::New(pduData, EEndOfL2CapSDU);
+ if(pdu)
+ {
+ sduPosition -= aPDUPayloadSize;
+ iPDUs.AddFirst(*pdu);
+ L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EIFrameCreated, pdu));
+
+ while(sduPosition > 0)
+ {
+ __ASSERT_DEBUG(sduPosition >= 0, Panic(EL2CAPSDUPositionNegativeDuringSegmentation));
+
+ TRAP(rerr, aSDUData.SplitL(sduPosition, pduData));
+ if(rerr == KErrNone)
+ {
+ pdu = HIFramePDU::New(pduData, EContinuationOfL2CapSDU);
+ if(pdu)
+ {
+ sduPosition -= aPDUPayloadSize;
+ iPDUs.AddFirst(*pdu);
+ }
+ else
+ {
+ rerr = KErrNoMemory;
+ break;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if(rerr == KErrNone)
+ {
+ // Create and add the first PDU.
+ pdu = HIFramePDU::New(aSDUData, EStartOfL2CapSDU);
+ if(pdu)
+ {
+ pdu->SetSDUSize(static_cast<TUint16>(sduLength));
+ iPDUs.AddFirst(*pdu);
+ }
+ else
+ {
+ rerr = KErrNoMemory;
+ }
+ }
+ }
+ else
+ {
+ rerr = KErrNoMemory;
+ }
+ }
+ }
+ }
+
+ if(rerr == KErrNone)
+ {
+ // Set the current PDU pointer to the first PDU.
+ iCurrentPDU.SetToFirst();
+ }
+ return rerr;
+ }
+
+TBool CL2CapSDU::GetPDU(HL2CapPDU*& aReturnedPDU)
+ {
+ LOG_FUNC
+ TBool isLastPDU = EFalse;
+ if(!iPDUs.IsEmpty())
+ {
+ aReturnedPDU = iCurrentPDU++;
+
+ // TODO: this was put in as a fix and is in needed in general, but it also
+ // actually hoses up some logic, i.e. CurrentPDUIsFirstPDU will always return
+ // true. We either need a separate link for the list of PDUs inside the SDU,
+ // or if possible just get rid of iCurrentPDU and have a iFirstPDU flag
+ // set to one during initial segmentation.
+ // Note - we the iCurrentPDU/iFirstPDU business is actually only used in two cases:
+ // 1. to repack current SDU when PDU size has changed on a live connection - currently
+ // unused and unnecessary.
+ // 2. on flush timer expiry, to mark already sent packets as flushed and purge the
+ // unsent ones - currently unused, but may get implemented in the future. However
+ // the marking of the packets may be done their current owner, which is a data
+ // controller or the muxer, so again this is unnecessary.
+ // Conclusion: refactor to simplify.
+ aReturnedPDU->iLink.Deque();
+
+ if(iCurrentPDU == NULL)
+ {
+ // At the end of the PDU list.
+ L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EGetPDUCalled, aReturnedPDU));
+ isLastPDU = ETrue;
+ }
+ }
+
+ return isLastPDU;
+ }
+
+
+/*static*/ TInt CL2CapSDU::FlushTimerExpired(TAny* aL2CapSDU)
+ {
+ LOG_STATIC_FUNC
+ CL2CapSDU* sdu = reinterpret_cast<CL2CapSDU*>(aL2CapSDU);
+ sdu->HandleFlushTimerExpired();
+ return EFalse;
+ }
+
+void CL2CapSDU::HandleFlushTimerExpired()
+ {
+ LOG_FUNC
+ // Note that the timer is no longer running.
+ iFlushTimerRunning = EFalse;
+
+ // Any PDU's that have already been sent need to be
+ // informed of the flush.
+ TDblQueIter<HL2CapPDU> pduIter(iPDUs);
+ TBool done = EFalse;
+
+ HL2CapPDU* pduPtr;
+ while((pduPtr = pduIter++) != NULL && !done)
+ {
+ if(pduPtr == iCurrentPDU)
+ {
+ done = ETrue;
+ }
+ else
+ {
+ pduPtr->SetPDUFlushed();
+ }
+ }
+
+ // The remaining PDU's (if any) that have not yet
+ // been sent can be deleted.
+ while((pduPtr = iCurrentPDU++) != NULL)
+ {
+ pduPtr->iLink.Deque();
+ delete pduPtr;
+ }
+
+ // Inform the SDU queue.
+ iParent.ProcessFlushTimerExpiry(*this);
+ }
+
+TBool CL2CapSDU::IsSDUEmpty() const
+ {
+ LOG_FUNC
+ return iPDUs.IsEmpty();
+ }
+
+
+TBool CL2CapSDU::CurrentPDUIsFirstPDU()
+ {
+ LOG_FUNC
+ return iPDUs.IsFirst(iCurrentPDU);
+ }
+
+void CL2CapSDU::StartFlushTimer(TUint16 aTimerDuration)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(!iFlushTimerRunning, Panic(EL2CAPAttemptToRestartFlushTimer));
+
+ // Only start the timer if the duration is valid.
+ if(aTimerDuration >= TL2CapConfig::EMinDataObsolescenceTimeout &&
+ aTimerDuration != KInfiniteFlush)
+ {
+ TCallBack cb(FlushTimerExpired, this);
+ iFlushTimerEntry.Set(cb);
+ // Timer period is a factor of the duration parameter and 0.625ms
+ BTSocketTimer::Queue(aTimerDuration*625, iFlushTimerEntry);
+ iFlushTimerRunning = ETrue;
+ }
+ }
+
+TInt CL2CapSDU::ChangeSDUSegmentation(TBool aIsBasicDataVersion, TUint16 aPDUSize)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(CurrentPDUIsFirstPDU(), Panic(EL2CAPAttemptToChangeSegmentationForPartiallySentSDU));
+
+ TInt rerr = KErrNone;
+ RMBufChain sduData;
+
+ // Get the current SDU data, and delete all current
+ // PDU's
+ TDblQueIter<HL2CapPDU> iter(iPDUs);
+ HL2CapPDU* pduPtr;
+ while((pduPtr = iter++) != NULL)
+ {
+ pduPtr->AppendPayloadToBuffer(sduData);
+ pduPtr->iLink.Deque();
+ delete pduPtr;
+ }
+
+ rerr = SegmentSDUIntoPDUs(sduData, aIsBasicDataVersion, aPDUSize);
+
+ return rerr;
+ }
+
+
+#ifdef _DEBUG
+TInt CL2CapSDU::DebugManualFlush()
+ {
+ LOG_FUNC
+ TInt rcode = -1;
+
+ if(!IsSDUEmpty())
+ {
+ RMBuf* buf = iPDUs.First()->PDUBuffer().First();
+ if(buf->Length() > 8)
+ {
+ rcode = buf->Get(8);
+ }
+ else
+ {
+ buf = buf->Next();
+ if(buf)
+ {
+ rcode = buf->Get(0);
+ }
+ }
+ }
+
+ if(iFlushTimerRunning)
+ {
+ BTSocketTimer::Remove(iFlushTimerEntry);
+ }
+ HandleFlushTimerExpired();
+ return rcode;
+ }
+#endif