// 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);#endifCL2CapSDU* 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 _DEBUGTInt 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