diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/l2cap/L2CapSDU.cpp --- /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 + +#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(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(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 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 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