diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/l2cap/L2CapPDU.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/l2cap/L2CapPDU.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,1374 @@ +// 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 +#include + +#include "L2CapPDU.h" +#include "l2capCommand.h" +#include "l2signalmgr.h" +#include "L2CapDataController.h" +#include "L2CapSDU.h" +#include "L2CapDebugControlInterface.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_L2CAP_PDU); +#endif + +TDataPlaneElementHandle::TDataPlaneElementHandle(MDataPlaneElement* aDataElement, TUint16 aElementID) + { + LOG_FUNC + SetUserLen(sizeof(SDataPlaneElementHandle)); + SDataPlaneElementHandle* handlePtr = reinterpret_cast(UserPtr()); + handlePtr->iDataElement = aDataElement; + handlePtr->iElementID = aElementID; + } + +MDataPlaneElement& TDataPlaneElementHandle::DataPlaneElement() const + { + LOG_FUNC + SDataPlaneElementHandle* handlePtr = reinterpret_cast(UserPtr()); + return *(handlePtr->iDataElement); + } + +TUint16 TDataPlaneElementHandle::ElementID() const + { + LOG_FUNC + SDataPlaneElementHandle* handlePtr = reinterpret_cast(UserPtr()); + return (handlePtr->iElementID); + } + + +// +// Base class for all PDU types. +// +// Disable warning WINS 4355: 'this' : used in base member initializer list +// This will not cause any problems in this usage and is preferable to the use of a +// non-owned pointer. +#pragma warning (disable: 4355) +HL2CapPDU::HL2CapPDU(RMBufChain& aPDUData, TInt aOptimalFragmentSize) + : iIsFlushed(EFalse), + iSendingError(EFalse), + iQueuedForSend(EFalse), + iAwaitingHciCompletion(EFalse), // first transmission never waits for a previous one to finish + iElementHandle(this), + iPduOwner(NULL), + iOptimalFragmentSize(aOptimalFragmentSize), + iTotalNumberOfFragments(KNumberOfFragmentsUnknown), + iFragmentAcksReceived(0) + { + LOG_FUNC + iPDUData.Assign(aPDUData); + iPDUData.Align(Min(iPDUData.Length(), KMaxPDUHeaderLength)); + + L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EBasePDU, + L2capDebugInfo::EAllocated)); + } + +HL2CapPDU::HL2CapPDU(TInt aOptimalFragmentSize) + : iIsFlushed(EFalse), + iSendingError(EFalse), + iQueuedForSend(EFalse), + iAwaitingHciCompletion(EFalse), // first transmission never waits for a previous one to finish + iElementHandle(this), + iPduOwner(NULL), + iOptimalFragmentSize(aOptimalFragmentSize), + iTotalNumberOfFragments(KNumberOfFragmentsUnknown), + iFragmentAcksReceived(0) + { + LOG_FUNC + L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EBasePDU, + L2capDebugInfo::EAllocated)); + } +#pragma warning (default: 4355) + + +HL2CapPDU::~HL2CapPDU() + { + LOG_FUNC + iPDUData.Free(); + + iLink.Deque(); + L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EBasePDU, + L2capDebugInfo::EDeleted)); + } + + +// Static Message Accessors. +/*static*/ TBool HL2CapPDU::AddFragment(RMBufChain& aPDU, RMBufChain& aPDUFragment) + { + LOG_STATIC_FUNC + if(aPDUFragment.First()!= NULL) + { + aPDU.Append(aPDUFragment); + return ((aPDU.Length() - KPDUHeaderLength) >= PDUPayloadLength(aPDU)); + } + else + { + return(EFalse); + } + } + +/*static*/ TUint16 HL2CapPDU::PDUPayloadLength(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KPDUHeaderLength, + Panic(EL2CapPDUInvalidLength)); + return LittleEndian::Get16((aPDU.First())->Ptr()+KLengthByteOffset); + } + +/*static*/ TUint16 HL2CapPDU::PDUCID(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KPDUHeaderLength, + Panic(EL2CapPDUInvalidLength)); + return LittleEndian::Get16((aPDU.First())->Ptr()+KCIDByteOffset); + } + +void HL2CapPDU::SetPDUPayloadLength(TUint16 aLength) + { + LOG_FUNC + __ASSERT_DEBUG(iPDUData.First() && (iPDUData.First())->Length() >= KPDUHeaderLength, + Panic(EL2CapPDUInvalidLength)); + LittleEndian::Put16((iPDUData.First())->Ptr()+KLengthByteOffset, aLength); + } + +void HL2CapPDU::SetPDUCID(TUint16 aCID) + { + LOG_FUNC + __ASSERT_DEBUG(iPDUData.First() && (iPDUData.First())->Length() >= KPDUHeaderLength, + Panic(EL2CapPDUInvalidLength)); + LittleEndian::Put16((iPDUData.First())->Ptr()+KCIDByteOffset, aCID); + } + +void HL2CapPDU::WritePDUPayloadLength() + { + LOG_FUNC + SetPDUPayloadLength(static_cast(iPDUData.Length() - KPDUHeaderLength)); + } + +/* + * Function to return the PDU L2CAP overhead. + */ +/*static*/ TInt HL2CapPDU::GetPDUOrFragmentOverhead(TBool aBasicMode) + { + LOG_STATIC_FUNC + if (aBasicMode) + { + // Basic mode does not segment into PDUs and therefore has no PDU overhead + return 0; + } + + // Non-basic mode overhead + return KPDUHeaderLength + KControlFieldLength + KFCSFieldLength; + } + +/* + * Return the payload size of the optimal pdu based on the baseband packet + * sizes, the negotiated MTU (aMTU) and the size of the buffers on the + * controller (aBufSize). + * SDU length field is taken into account when it get segmented by CL2CapSDU::SegmentSDUIntoPDUs + */ +/*static*/ TInt HL2CapPDU::GetPDUOrFragmentSize(TInt aMTU, TInt aMaxMps, TInt aBufSize, TBool aBasicMode) + { + LOG_STATIC_FUNC + // aBufSize == 0 means that getting the buffer size failed so assume the best + // case scenario for performance + TInt bufSize = aBufSize != 0 ? aBufSize : KBBPacketSize[KBaseBandPacketCount - 1]; + + // get the overhead size for every l2cap pdu + TInt packetOverhead = GetPDUOrFragmentOverhead(aBasicMode); + + // it's possible that the buffer size is so small that when including the packet overhead there is no + // room for a payload, this is of course no good. + __ASSERT_ALWAYS((bufSize - packetOverhead) > 0, Panic(EL2CAPACLBufferTooSmall)); + + // store the current baseband packet size as we go through the below for loop + TInt currentPacketSize; + + // iterate backwards through the baseband packet sizes array + for (TInt index = KBaseBandPacketCount - 1; index >= 0; index--) + { + currentPacketSize = KBBPacketSize[index]; + + if (aMTU >= (index > 0 ? ((currentPacketSize + KBBPacketSize[index - 1]) / 2) : currentPacketSize)) + { + if (bufSize >= currentPacketSize) + { + // if the current baseband packet size is less than the MTU size and the baseband + // packet will fit into the controllers buffer then use the baseband packet + return (aBasicMode ? (currentPacketSize - packetOverhead) : Min(aMaxMps, (currentPacketSize - packetOverhead))); + } + else if ((index > 0) && (bufSize >= ((currentPacketSize + KBBPacketSize[index - 1]) / 2))) + { + // if the current baseband packet is less than the MTU but the baseband packet + // won't fit into the controllers buffer see if the buffer size is closer to the + // current baseband packet than the next size down and if so use the buffer size + // of the controller + return (aBasicMode ? (bufSize - packetOverhead) : Min(aMaxMps, (bufSize - packetOverhead))); + } + } + } + + // to get here means that the buffer size of the controller or the MTU is less than the minimum + // baseband packet size. A small ACL buffer reduces performance as there is a big overhead to data + // ratio, but we handle that situation anyway. + return (aBasicMode ? (bufSize - packetOverhead) : Min(aMaxMps, (bufSize - packetOverhead))); + } + +/*static*/ TInt HL2CapPDU::CheckDecode(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + TInt rerr = KErrNone; + if(aPDU.Length() != (PDUPayloadLength(aPDU) + KPDUHeaderLength)) + { + rerr = KErrCorrupt; + } + return rerr; + } + +void HL2CapPDU::AppendPayloadToBuffer(RMBufChain& /*aSDUData*/) + { + LOG_FUNC + Panic(EL2CAPInvalidCallToAppendPayloadToBuffer); + } + +void HL2CapPDU::DeliverOutgoingPDU(MOutgoingPDUHandler& aPDUHandler) + { + iAwaitingHciCompletion = ETrue; + DeliverOutgoingPDUDoubleDispatch(aPDUHandler); + } + + +void HL2CapPDU::PDUSendPending(TUint16 aTotalNumberOfFragments) + { + LOG_FUNC + // The fragment sender has completed sending the PDU to the + // ACL. + __ASSERT_DEBUG(iTotalNumberOfFragments == KNumberOfFragmentsUnknown, Panic(EL2CAPPDUSendPendingCalledTwice)); + + iTotalNumberOfFragments = aTotalNumberOfFragments; + + // In some error conditions the expected number of outstanding fragments might + // have been reached before this method is called. + if(iFragmentAcksReceived == iTotalNumberOfFragments) + { + PDUSendComplete(); + } + } + +void HL2CapPDU::PDUSendComplete() + { + LOG_FUNC + iAwaitingHciCompletion = EFalse; + + if(!iIsFlushed && iPduOwner) + { + iFragmentAcksReceived = 0; + iTotalNumberOfFragments = KNumberOfFragmentsUnknown; + + if(!iSendingError) + { + // Do frame-type specific stuff. + SendComplete(); + + iPduOwner->HandlePduSendComplete(*this); + } + else + { + iSendingError = EFalse; // in case the owner decides to resend us + iPduOwner->HandlePduSendError(*this); + } + } + else + { + // No references are held to this PDU. Delete it. + delete this; + } + } + + +// MDataPlaneElement Interface +void HL2CapPDU::DataElementSent(TUint16 /*aElementID*/) + { + LOG_FUNC + // Check if the PDU has been fully sent to the peer. + if(++iFragmentAcksReceived == iTotalNumberOfFragments) + { + PDUSendComplete(); + } + } + +void HL2CapPDU::DataElementFlushed(TUint16 aElementID) + { + LOG_FUNC + // Check if this PDU has been flushed. + if(!iIsFlushed) + { + iSendingError = ETrue; + } + DataElementSent(aElementID); + } + +TUint16 HL2CapPDU::CalcCRC(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + TUint16 crcValue = 0; + TUint8 dataByte; + TInt i, bufLength; + TUint16 carryBit, bitCount; + + const RMBuf* bufPtr = aPDU.First(); + TInt fcsLength = aPDU.Length() - KFCSFieldLength; + TInt byteCount = 0; + while(bufPtr) + { + // Check if this is the last buffer. If so don't + // process the FCS bytes. + bufLength = bufPtr->Length(); + for(i=0;iGet(i); + byteCount++; + for(bitCount=0;bitCount<8;bitCount++) + { + carryBit = static_cast(crcValue & 0x8000 ? 1 : 0); + crcValue <<= 1; + if ((dataByte & 1) ^ carryBit) + { + crcValue ^= 0x8005; + } + dataByte >>= 1; + } + } + } + + bufPtr = bufPtr->Next(); + } + + carryBit = 0; + for(bitCount=0;bitCount<16;bitCount++) + { + carryBit = static_cast((carryBit >> 1) | (crcValue & 0x8000)); + crcValue <<= 1; + } + + return carryBit; + } + +void HL2CapPDU::SendComplete() + { + LOG_FUNC + // Nothing to do for most PDUs. + } + + +// +// Class used to fragment and send any type of PDU. +// An instance of this class is currently owned by the Mux +// +HFragmentedPDUSender::HFragmentedPDUSender(CL2CAPMux& aMuxer) + : iPDU(NULL), + iPDUBuffer(NULL), + iMuxer(aMuxer), + iCurrentWriteIndex(0), + iCurrentFragmentID(0), + iPDULength(0) + { + LOG_FUNC + } + +HFragmentedPDUSender::~HFragmentedPDUSender() + { + LOG_FUNC + delete iPDUBuffer; + __ASSERT_DEBUG(iPDU == NULL, Panic(EL2CAPFragmentSenderDeletedWhileSending)); + } + +TInt HFragmentedPDUSender::FragmentPDU(HL2CapPDU& aPDU) + { + LOG_FUNC + TInt rerr = KErrNone; + __ASSERT_DEBUG(!iCurrentWriteIndex && !iCurrentFragmentID, Panic(EL2CAPRequestToFragmentWhileBufferContainsData)); + + RMBufChain& pduData = aPDU.PDUBuffer(); + if(!iPDUBuffer || iPDUBuffer->Size() < pduData.Length()) + { + // Increase the size of the buffer. + delete iPDUBuffer; + iPDUBuffer = HBufC8::New(pduData.Length()); + } + + if(iPDUBuffer) + { + iPDU = &aPDU; + iPDULength = pduData.Length(); + + // Copy the Buffer into the descriptor + TPtr8 ptr = iPDUBuffer->Des(); + ptr.SetLength(iPDULength); + pduData.CopyOut(ptr); + + // store the optimal fragment size + iPDUFragmentSize = aPDU.OptimalFragmentSize(); + } + else + { + rerr = KErrNoMemory; + } + return rerr; + } + +HFragmentedPDUSender::TFragmentSenderStatus HFragmentedPDUSender::WriteNextFragment(CServProviderBase& aSender, TInt aACLMTU) + { + LOG_FUNC + // If optimal fragment size not set then this PDU should only have been segmented, not fragmented + __ASSERT_DEBUG((iPDULength <= aACLMTU) || (iPDUFragmentSize != 0), Panic(EL2CAPUnexpectedFragmentation)); + if (iPDUFragmentSize == 0) + { + // For udeb builds we know that this PDU can fit into the ACL buffer so just set the fragment size + // to be the ACL buffer size to allow logic below to work. For urel builds this can also recover the + // situation if the debug assert fails above, just not using optimal performance. + iPDUFragmentSize = aACLMTU; + } + + // Ensure that the optimal fragment size will fit into the ACL buffer. + __ASSERT_DEBUG(aACLMTU >= iPDUFragmentSize, Panic(EL2CAPOptimalFragmentSizeTooBigForACLBuffer)); + + TFragmentSenderStatus rStatus = EFragmentOK; + + TUint8 flags = iCurrentWriteIndex ? KContinuingHLFragment : KFirstHLFragment; + + TInt fragLength = Min(iPDULength - iCurrentWriteIndex, Min(iPDUFragmentSize, aACLMTU)); + + // The 'Write' returns the number of fragments (NOT bytes) + // that will be sent. This is typically either 0 or 1. + TUint fragmentsSent = aSender.Write(iPDUBuffer->Mid(iCurrentWriteIndex, fragLength), flags, &iPDU->ElementHandle()); + if(!fragmentsSent) + { + rStatus = EFlowControlledOff; + } + else + { + iCurrentWriteIndex += fragLength; + iCurrentFragmentID++; + } + + // Check if the PDU has been fully sent. + if(iCurrentWriteIndex >= iPDULength) + { + rStatus = EFragmentationComplete; + + // Inform the PDU that it has been sent. + iPDU->PDUSendPending(iCurrentFragmentID); + + // Reset the internal state of this sender. + Reset(); + } + return rStatus; + } + +void HFragmentedPDUSender::Reset() + { + LOG_FUNC + iPDU = NULL; + iPDULength = iCurrentWriteIndex = 0; + iCurrentFragmentID = 0; + iPDUFragmentSize = 0; + } + +void HFragmentedPDUSender::CheckForFlushed() + { + LOG_FUNC + // Check if the PDU currently being sent(if there is one) + // needs to be flushed. + if(iPDU) + { + if(iPDU->IsPDUFlushed()) + { + // Inform the PDU that it has been sent. + iPDU->PDUSendPending(iCurrentFragmentID); + + // Reset the internal state of this sender. + Reset(); + } + } + } + +void HFragmentedPDUSender::PDUSenderFailed() + { + LOG_FUNC + if(iPDU) + { + // Inform the PDU that it has been sent. + iPDU->PDUSendPending(iCurrentFragmentID); + + // Reset the internal state of this sender. + Reset(); + } + } + + + +// +// Basic frame PDU class. +// +HBFramePDU::HBFramePDU(RMBufChain& aPDUData, TInt aOptimalFragmentSize) + : HL2CapPDU(aPDUData, aOptimalFragmentSize) + { + LOG_FUNC + L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EBFrame, + L2capDebugInfo::EAllocated)); + } + +HBFramePDU::~HBFramePDU() + { + LOG_FUNC + L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EBFrame, + L2capDebugInfo::EDeleted)); + } + +HBFramePDU* HBFramePDU::New(RMBufChain& aPayloadData, TInt aOptimalFragmentSize) + { + LOG_STATIC_FUNC + HBFramePDU* self = NULL; + TRAPD(rerr, aPayloadData.PrependL(KPDUHeaderLength)); + if(rerr == KErrNone) + { + self = new HBFramePDU(aPayloadData, aOptimalFragmentSize); + } + + if(self) + { + self->WritePDUPayloadLength(); + } + return self; + } + +void HBFramePDU::DeliverOutgoingPDUDoubleDispatch(MOutgoingPDUHandler& aPDUHandler) + { + LOG_FUNC + aPDUHandler.HandleOutgoingBFrame(this); + } + +void HBFramePDU::AppendPayloadToBuffer(RMBufChain& aSDUData) + { + LOG_FUNC + // Remove header and trailing bytes. + iPDUData.TrimStart(KPDUHeaderLength); + aSDUData.Append(iPDUData); + } + +/*static*/ void HBFramePDU::RemoveHeaderBytes(RMBufChain& aPDU) + { + LOG_STATIC_FUNC + // Remove header bytes. + aPDU.TrimStart(KPDUHeaderLength); + } + +void HBFramePDU::SendComplete() + { + LOG_FUNC + L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EPDUSent, this, PDUCID())); + } + + +// +// Information frame PDU class. +// +HIFramePDU::HIFramePDU(RMBufChain& aPDUData) + : HL2CapPDU(aPDUData, 0), + iTransmissionCount(0), + iAcked(EFalse) + { + LOG_FUNC + L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EIFrame, + L2capDebugInfo::EAllocated)); + } + +HIFramePDU::~HIFramePDU() + { + LOG_FUNC + LOG1(_L("Deleting TxSeq = %d"), TxSeqNumber()); + + L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EIFrame, + L2capDebugInfo::EDeleted)); + } + + +HIFramePDU* HIFramePDU::New(RMBufChain& aPayloadData, TL2CapSAR aPduSAR) + { + LOG_STATIC_FUNC + TInt rerr = KErrNone; + HIFramePDU* self = NULL; + + switch(aPduSAR) + { + case EStartOfL2CapSDU: + TRAP(rerr, aPayloadData.PrependL(KPDUHeaderLength + KControlFieldLength + KSDULengthFieldLength)); + break; + case EUnsegmentedL2CapSDU: + case EEndOfL2CapSDU: + case EContinuationOfL2CapSDU: + TRAP(rerr, aPayloadData.PrependL(KPDUHeaderLength + KControlFieldLength)); + break; + + default: + Panic(EL2CAPInvalidPDUSAR); + break; + }; + + if(rerr == KErrNone) + { + TRAP(rerr, aPayloadData.AppendL(KFCSFieldLength)); + } + + if(rerr == KErrNone) + { + self = new HIFramePDU(aPayloadData); + if(self) + { + self->WritePDUPayloadLength(); + self->SetIFrameControlDefault(); + self->SetSAR(aPduSAR); + } + } + + return self; + } + +void HIFramePDU::DeliverOutgoingPDUDoubleDispatch(MOutgoingPDUHandler& aPDUHandler) + { + LOG_FUNC + aPDUHandler.HandleOutgoingIFrame(this); + } + +// Message Accessors. +/*static*/ TBool HIFramePDU::IFrameIdentifier(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + TUint16 ctrl = LittleEndian::Get16((aPDU.First())->Ptr()+KControlFieldByteOffset); + return (!(ctrl & KCtrlFrameTypeMask)); + } + +/*static*/ TUint8 HIFramePDU::TxSeqNumber(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + TUint16 ctrl = LittleEndian::Get16((aPDU.First())->Ptr()+KControlFieldByteOffset); + return static_cast((ctrl & KCtrlTxSeqMask) >> KCtrlTxSeqShift); + } + +/*static*/ TBool HIFramePDU::FinalBit(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + TUint16 ctrl = LittleEndian::Get16((aPDU.First())->Ptr()+KControlFieldByteOffset); + return (ctrl & KCtrlFinalBitMask); + } + +void HIFramePDU::SetFinalBit(TBool aFinalBit) + { + LOG_FUNC + SetRetransmitDisable(aFinalBit); + } + +void HIFramePDU::SetTxSeqNumber(TUint8 aTxSeqNum) + { + LOG_FUNC + __ASSERT_DEBUG(iPDUData.First() && (iPDUData.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + TUint16 ctrl = LittleEndian::Get16((iPDUData.First())->Ptr()+KControlFieldByteOffset); + ctrl &= ~KCtrlTxSeqMask; + ctrl |= (aTxSeqNum << KCtrlTxSeqShift); + LittleEndian::Put16((iPDUData.First())->Ptr()+KControlFieldByteOffset, ctrl); + } + +/*static*/ TBool HIFramePDU::RetransmitDisable(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + return (LittleEndian::Get16((aPDU.First())->Ptr()+KControlFieldByteOffset) & KCtrlReTxDisableMask); + } + +void HIFramePDU::SetRetransmitDisable(TBool aReTxDisable) + { + LOG_FUNC + __ASSERT_DEBUG(iPDUData.First() && (iPDUData.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + TUint16 ctrl = LittleEndian::Get16((iPDUData.First())->Ptr()+KControlFieldByteOffset); + ctrl &= ~KCtrlReTxDisableMask; + ctrl |= (aReTxDisable << KCtrlReTxDisableShift); + LittleEndian::Put16((iPDUData.First())->Ptr()+KControlFieldByteOffset, ctrl); + } + +/*static*/ TUint8 HIFramePDU::ReqSeqNumber(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + TUint16 ctrl = LittleEndian::Get16((aPDU.First())->Ptr()+KControlFieldByteOffset); + return static_cast((ctrl & KCtrlReqSeqMask) >> KCtrlReqSeqShift); + } + +void HIFramePDU::SetReqSeqNumber(TUint8 aReqSeqNum) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(iPDUData.First() && (iPDUData.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + TUint16 ctrl = LittleEndian::Get16((iPDUData.First())->Ptr()+KControlFieldByteOffset); + ctrl &= ~KCtrlReqSeqMask; + ctrl |= (aReqSeqNum << KCtrlReqSeqShift); + LittleEndian::Put16((iPDUData.First())->Ptr()+KControlFieldByteOffset, ctrl); + } + +/*static*/ TBool HIFramePDU::IsStartOfSDU(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + return (SAR(aPDU) == EUnsegmentedL2CapSDU || SAR(aPDU) == EStartOfL2CapSDU); + } + + +/*static*/ TL2CapSAR HIFramePDU::SAR(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + TUint16 ctrl = LittleEndian::Get16((aPDU.First())->Ptr()+KControlFieldByteOffset); + return TL2CapSAR((ctrl & KCtrlSARMask) >> KCtrlSARShift); + } + +void HIFramePDU::SetSAR(TL2CapSAR aSARValue) + { + LOG_FUNC + __ASSERT_DEBUG(iPDUData.First() && (iPDUData.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + TUint16 ctrl = LittleEndian::Get16((iPDUData.First())->Ptr()+KControlFieldByteOffset); + ctrl &= ~KCtrlSARMask; + TUint8 sar = static_cast(aSARValue); + ctrl |= (sar << KCtrlSARShift); + LittleEndian::Put16((iPDUData.First())->Ptr()+KControlFieldByteOffset, ctrl); + } + +/*static*/ TUint16 HIFramePDU::SDUSize(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KSDULengthFieldLength + KSDULengthFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + + return LittleEndian::Get16((aPDU.First())->Ptr()+KSDULengthFieldByteOffset); + } + +void HIFramePDU::SetSDUSize(TUint16 aSDUSize) + { + LOG_FUNC + LittleEndian::Put16((iPDUData.First())->Ptr()+KSDULengthFieldByteOffset, aSDUSize); + } + +void HIFramePDU::CalculateAndSetFCS() + { + LOG_FUNC + + TUint16 fcs = HL2CapPDU::CalcCRC(); + + RMBuf* buf = iPDUData.Last(); + if(buf->Length() < KFCSFieldLength) + { + // This can only mean that the last buffer contains one byte. + // Get a pointer to the penultimate buffer. + buf = iPDUData.First(); + for(TInt i=1;i<(iPDUData.NumBufs() - 1);i++) + { + buf = buf->Next(); + } + buf->Put(static_cast(fcs & 0x00ff), buf->Length() - 1); + fcs = static_cast(fcs >> 8); + buf = buf->Next(); + buf->Put(static_cast(fcs & 0x00ff), 0); + } + else + { + LittleEndian::Put16(buf->Ptr()+buf->Length()-KFCSFieldLength, fcs); + } + } + +/*static*/ TBool HIFramePDU::CheckFCS(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KPDUHeaderLength + KFCSFieldLength, + Panic(EL2CapPDUInvalidLength)); + + TUint16 msgFCS; + RMBuf* buf = aPDU.Last(); + if(buf->Length() < KFCSFieldLength) + { + // This can only mean that the last buffer contains one byte. + msgFCS = static_cast((buf->Get(0)) << 8); + + // Get a pointer to the penultimate buffer. + const RMBuf* cBuf = aPDU.First(); + for(TInt i=1;i<(aPDU.NumBufs() - 1);i++) + { + cBuf = cBuf->Next(); + } + msgFCS |= cBuf->Get(cBuf->Length() - 1); + } + else + { + msgFCS = LittleEndian::Get16(buf->Ptr()+buf->Length()-KFCSFieldLength); + } + + return (msgFCS == HL2CapPDU::CalcCRC(aPDU)); + } + +TBool HIFramePDU::CheckFCS() + { + LOG_FUNC + return CheckFCS(iPDUData); + } + +void HIFramePDU::SetIFrameControlDefault() + { + LOG_FUNC + LittleEndian::Put16((iPDUData.First())->Ptr()+KControlFieldByteOffset, KIFrameControlDefault); + } + + +void HIFramePDU::AppendPayloadToBuffer(RMBufChain& aSDUData) + { + LOG_FUNC + // Remove header and trailing bytes. + if(SAR() == EStartOfL2CapSDU) + { + iPDUData.TrimStart(KPDUHeaderLength + KControlFieldLength + KSDULengthFieldLength); + } + else + { + iPDUData.TrimStart(KPDUHeaderLength + KControlFieldLength); + } + iPDUData.TrimEnd(iPDUData.Length()-KFCSFieldLength); + aSDUData.Append(iPDUData); + } + +/*static*/ void HIFramePDU::RemoveHeaderAndFCSBytes(RMBufChain& aPDU) + { + // Remove header and trailing bytes. + if(SAR(aPDU) == EStartOfL2CapSDU) + { + aPDU.TrimStart(KPDUHeaderLength + KControlFieldLength + KSDULengthFieldLength); + } + else + { + aPDU.TrimStart(KPDUHeaderLength + KControlFieldLength); + } + aPDU.TrimEnd(aPDU.Length()-KFCSFieldLength); + } + +/*static*/ TInt HIFramePDU::CheckPayloadDecode(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + TInt rerr = KErrNone; + + if(PDUCID(aPDU) < KL2CapDynamicCIDStart) + { + rerr = KErrCorrupt; + } + else if(!CheckFCS(aPDU)) + { + rerr = KErrCorrupt; + } + return rerr; + } + +/*static*/ TInt HIFramePDU::CheckLengthWithinLimits(const RMBufChain& aPDU, TUint16 aMps) + { + LOG_STATIC_FUNC + // Check that the PDU size is >= than minimal and that the information payload size is <= MPS. + + TInt err = KErrNone; + TBool hasSduLength = (SAR(aPDU) == EStartOfL2CapSDU); + + TInt infoPayloadSize = PDUPayloadLength(aPDU) - KControlFieldLength - KFCSFieldLength; + if (hasSduLength) + { + infoPayloadSize -= KSDULengthFieldLength; + } + + if (infoPayloadSize > aMps) + { + err = KErrL2CAPIncomingIFrameTooBig; + } + else + { + if (aPDU.Length() < (KPDUHeaderLength + KControlFieldLength + KFCSFieldLength)) + { + err = KErrL2CAPIncomingIFrameTooSmall; + } + } + + return err; + } + +/*static*/ TInt HIFramePDU::CheckStartSduLength(const RMBufChain& aPDU, TUint16 aMtu) + { + LOG_STATIC_FUNC + TInt err = KErrNone; + + if (aPDU.Length() < (KPDUHeaderLength + KControlFieldLength + KSDULengthFieldLength + KFCSFieldLength)) + { + err = KErrL2CAPIncomingIFrameTooSmall; + } + else if (SDUSize(aPDU) > aMtu) + { + err = KErrL2CAPIncomingSduTooBig; + } + return err; + } + +void HIFramePDU::SendComplete() + { + LOG_FUNC + iTransmissionCount++; + +#ifdef _DEBUG + if(SAR() == EEndOfL2CapSDU || SAR() == EUnsegmentedL2CapSDU) + { + L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EPDUSent, this, PDUCID())); + } +#endif + } + + +// +// Supervisory frame PDU class. +// +HSFramePDU* HSFramePDU::New(TSupervisoryFunction aFunction) + { + LOG_STATIC_FUNC + TInt rerr = KErrNone; + HSFramePDU* self = NULL; + + RMBufChain frameBuffer; + TRAP(rerr, frameBuffer.AllocL(KSFrameLength)); + + if(rerr == KErrNone) + { + self = new HSFramePDU(frameBuffer); + if(self) + { + self->WritePDUPayloadLength(); + self->SetSFrameControlDefault(); + self->SetSupervisoryFunction(aFunction); + } + else + { + frameBuffer.Free(); + } + } + return self; + } + +HSFramePDU* HSFramePDU::NewL(TSupervisoryFunction aFunction) + { + LOG_STATIC_FUNC + HSFramePDU* self = New(aFunction); + if (self == NULL) + { + LEAVEL(KErrNoMemory); + } + return self; + } + +HSFramePDU::HSFramePDU(RMBufChain& aPDUData) + : HL2CapPDU(aPDUData, 0) + { + LOG_FUNC + L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESFrame, + L2capDebugInfo::EAllocated)); + } + +HSFramePDU::~HSFramePDU() + { + LOG_FUNC + L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESFrame, + L2capDebugInfo::EDeleted)); + } + +void HSFramePDU::DeliverOutgoingPDUDoubleDispatch(MOutgoingPDUHandler& aPDUHandler) + { + LOG_FUNC + aPDUHandler.HandleOutgoingSFrame(this); + } + +/*static*/ TInt HSFramePDU::CheckLengthField(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + TInt err = KErrNone; + + // The length field doesn't include the L2CAP Basic header. + if (PDUPayloadLength(aPDU) != KControlFieldLength + KFCSFieldLength) + { + err = KErrL2CAPInvalidIncomingSFrameSize; + } + + return err; + } + +/*static*/ TInt HSFramePDU::CheckPayloadDecode(RMBufChain& aPDU) + { + LOG_STATIC_FUNC + TInt rerr = KErrNone; + + if(!CheckFCS(aPDU)) + { + rerr = KErrCorrupt; + } + + return rerr; + } + +/*static*/ TSupervisoryFunction HSFramePDU::SupervisoryFunction(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + + TUint16 ctrl = LittleEndian::Get16((aPDU.First())->Ptr()+KControlFieldByteOffset); + return TSupervisoryFunction((ctrl & KCtrlSupervisoryMask) >> 2); + } + +/*static*/ TUint8 HSFramePDU::ReqSeqNumber(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + + TUint16 ctrl = LittleEndian::Get16((aPDU.First())->Ptr()+KControlFieldByteOffset); + return static_cast((ctrl & KCtrlReqSeqMask) >> 8); + } + +void HSFramePDU::SetSupervisoryFunction(TSupervisoryFunction aSupervisoryFunction) + { + LOG_FUNC + TUint16 ctrl = LittleEndian::Get16((iPDUData.First())->Ptr()+KControlFieldByteOffset); + ctrl &= ~KCtrlSupervisoryMask; + ctrl |= (aSupervisoryFunction << 2); + LittleEndian::Put16((iPDUData.First())->Ptr()+KControlFieldByteOffset, ctrl); + } + +/*static*/ TBool HSFramePDU::RetransmitDisable(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + + return (LittleEndian::Get16((aPDU.First())->Ptr()+KControlFieldByteOffset) & KCtrlReTxDisableMask); + } + +/*static*/ TBool HSFramePDU::FinalBit(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + + return (LittleEndian::Get16((aPDU.First())->Ptr()+KControlFieldByteOffset) & KCtrlFinalBitMask); + } + +/*static*/ TBool HSFramePDU::PollBit(const RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KControlFieldLength + KControlFieldByteOffset, + Panic(EL2CapPDUInvalidLength)); + + return (LittleEndian::Get16((aPDU.First())->Ptr()+KControlFieldByteOffset) & KCtrlPollBitMask); + } + +void HSFramePDU::SetFinalBit(TBool aFinalBit) + { + LOG_FUNC + TUint16 ctrl = LittleEndian::Get16((iPDUData.First())->Ptr()+KControlFieldByteOffset); + ctrl &= ~KCtrlFinalBitMask; + ctrl |= (aFinalBit << 7); + LittleEndian::Put16((iPDUData.First())->Ptr()+KControlFieldByteOffset, ctrl); + } + +void HSFramePDU::SetPollBit(TBool aPollBit) + { + LOG_FUNC + TUint16 ctrl = LittleEndian::Get16((iPDUData.First())->Ptr()+KControlFieldByteOffset); + ctrl &= ~KCtrlPollBitMask; + ctrl |= (aPollBit << 4); + LittleEndian::Put16((iPDUData.First())->Ptr()+KControlFieldByteOffset, ctrl); + } + +void HSFramePDU::SetRetransmitDisable(TBool aReTxDisable) + { + LOG_FUNC + TUint16 ctrl = LittleEndian::Get16((iPDUData.First())->Ptr()+KControlFieldByteOffset); + ctrl &= ~KCtrlReTxDisableMask; + ctrl |= (aReTxDisable << 7); + LittleEndian::Put16((iPDUData.First())->Ptr()+KControlFieldByteOffset, ctrl); + } + +void HSFramePDU::SetReqSeqNumber(TUint8 aReqSeqNum) + { + LOG_FUNC + TUint16 ctrl = LittleEndian::Get16((iPDUData.First())->Ptr()+KControlFieldByteOffset); + ctrl &= ~KCtrlReqSeqMask; + ctrl |= (aReqSeqNum << 8); + LittleEndian::Put16((iPDUData.First())->Ptr()+KControlFieldByteOffset, ctrl); + } + +void HSFramePDU::CalculateAndSetFCS() + { + LOG_FUNC + + TUint16 fcs = HL2CapPDU::CalcCRC(); + + RMBuf* lastBuf = iPDUData.Last(); + if(lastBuf->Length() < KFCSFieldLength) + { + // This can only be 8 bytes long. Align the buffer. + iPDUData.Align(iPDUData.Length()); + lastBuf = iPDUData.Last(); + } + LittleEndian::Put16(lastBuf->Ptr()+lastBuf->Length()-KFCSFieldLength, fcs); + } + +/*static*/ TBool HSFramePDU::CheckFCS(RMBufChain& aPDU) + { + LOG_STATIC_FUNC + __ASSERT_DEBUG(aPDU.First() && (aPDU.First())->Length() >= KPDUHeaderLength + KFCSFieldLength, + Panic(EL2CapPDUInvalidLength)); + + TUint16 msgFCS; + RMBuf* buf = aPDU.Last(); + if(buf->Length() < KFCSFieldLength) + { + // This can only be 8 bytes long. Align the buffer. + aPDU.Align(aPDU.Length()); + buf = aPDU.Last(); + } + + msgFCS = LittleEndian::Get16(buf->Ptr()+buf->Length()-KFCSFieldLength); + + return (msgFCS == HL2CapPDU::CalcCRC(aPDU)); + } + +TBool HSFramePDU::CheckFCS() + { + LOG_FUNC + return CheckFCS(iPDUData); + } + +void HSFramePDU::SetSFrameControlDefault() + { + LOG_FUNC + LittleEndian::Put16((iPDUData.First())->Ptr()+KControlFieldByteOffset, KSFrameControlDefault); + } + + +// +// Group frame PDU class. +// +HGFramePDU::HGFramePDU(RMBufChain& aPDUData) + : HL2CapPDU(aPDUData, 0) + { + LOG_FUNC + L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EGFrame, + L2capDebugInfo::EAllocated)); + } + +HGFramePDU::~HGFramePDU() + { + LOG_FUNC + L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EGFrame, + L2capDebugInfo::EDeleted)); + } + +void HGFramePDU::DeliverOutgoingPDUDoubleDispatch(MOutgoingPDUHandler& aPDUHandler) + { + LOG_FUNC + aPDUHandler.HandleOutgoingGFrame(this); + } + +void HGFramePDU::AppendPayloadToBuffer(RMBufChain& aSDUData) + { + LOG_FUNC + // Remove header and trailing bytes. + iPDUData.TrimStart(KPDUHeaderLength + KGFramePSMLength); + aSDUData.Append(iPDUData); + } + + +// +// Control frame PDU class. +// +/*static*/ HCFramePDU* HCFramePDU::New(TInt aOptimalFragmentSize) + { + LOG_STATIC_FUNC + RMBufChain buf; + HCFramePDU* cFrame = NULL; + + TRAPD(err, buf.AllocL(KCFrameHeaderLength)); + if(err == KErrNone) + { + cFrame = new HCFramePDU(buf, aOptimalFragmentSize); + if(cFrame) + { + cFrame->SetPDUCID(KL2CapSignallingCID); + cFrame->WritePDUPayloadLength(); + } + else + { + buf.Free(); + } + } + return cFrame; + } + +HCFramePDU::HCFramePDU(RMBufChain& aPDUData, TInt aOptimalFragmentSize) + : HL2CapPDU(aPDUData, aOptimalFragmentSize), + iCommands(_FOFF(HL2CapCommand, iLink)), + iCommandIter(iCommands) + { + LOG_STATIC_FUNC + L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ECFrame, + L2capDebugInfo::EAllocated)); + } + +HCFramePDU::~HCFramePDU() + { + LOG_FUNC + __ASSERT_DEBUG(iCommands.IsEmpty(), Panic(EL2CAPDeleteCFrameWhileContainingCommands)); + + while(!iCommands.IsEmpty()) + { + HL2CapCommand *cmd = iCommands.First(); + cmd->iLink.Deque(); + delete cmd; + } + + L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ECFrame, + L2capDebugInfo::EDeleted)); + } + +void HCFramePDU::DeliverOutgoingPDUDoubleDispatch(MOutgoingPDUHandler& aPDUHandler) + { + LOG_FUNC + aPDUHandler.HandleOutgoingCFrame(this); + } + +TInt HCFramePDU::CheckDecode() + { + LOG_FUNC + TInt rerr = KErrNone; + if(iPDUData.Length() != (PDUPayloadLength() + KPDUHeaderLength)) + { + rerr = KErrCorrupt; + } + else + { + rerr = CheckPayloadDecode(); + } + return rerr; + } + +TInt HCFramePDU::CheckPayloadDecode() + { + LOG_FUNC + return CreateCommands(); + } + + +// Takes this original PDU and extracts out a list of the contained Command Frames +// Upon completion the original PDU will be freed, but this class will hold a list +// CFrames. +TInt HCFramePDU::CreateCommands() + { + LOG_FUNC + // Trim off the standard header. + iPDUData.TrimStart(KCFrameHeaderLength); + + HL2CapCommand* cmd = NULL; + + // If a command is decoded it will be removed from the + // start of the buffer. + TInt err = KErrNone; + while(err == KErrNone) + { + err = HL2CapCommand::DecodeCommand(iPDUData, cmd); + LOG1(_L("HCFramePDU::CreateCommands, DecodeCommand returned %d"), err); + + if(err == KErrNone && cmd) + { + iCommands.AddLast(*cmd); + } + } + + // A C-Frame must contain at least one command. + if(iCommands.IsEmpty()) + { + err = KErrCorrupt; + } + return (err == KErrCompletion ? KErrNone : err); + } + + +HL2CapCommand* HCFramePDU::FirstCommand() + { + LOG_FUNC + iCommandIter.SetToFirst(); + return iCommandIter++; + } + +HL2CapCommand* HCFramePDU::NextCommand() + { + LOG_FUNC + return iCommandIter++; + } + + +TInt HCFramePDU::AddCommand(HL2CapCommand& aCommand, CL2CAPMux& aMuxer) + { + LOG_FUNC + TInt rerr = KErrNone; + // Check if there is room in the PDU. + if((aCommand.CommandLength() + PDUPayloadLength()) <= aMuxer.SigMTU()) + { + // Add this command onto end of PDU buff + RMBufChain buf; + rerr = aCommand.GetCommand(aMuxer, buf); + if(rerr == KErrNone) + { + iPDUData.Append(buf); + + WritePDUPayloadLength(); + } + } + else + { + rerr = KErrCompletion; + } + return rerr; + }