diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/l2cap/L2CapPDU.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/l2cap/L2CapPDU.h Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,682 @@ +// 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: +// + +#ifndef L2CAPPDU_H_ +#define L2CAPPDU_H_ + +#include +#include +#include + +#include +#include + +#include "L2types.h" +#include "l2util.h" + +class HL2CapCommand; +class CServProviderBase; +class CL2CAPMux; +class CL2CapDataFlowController; + +class HIFramePDU; +class HBFramePDU; +class HGFramePDU; +class HCFramePDU; +class HSFramePDU; +class HL2CapPDU; + +class MOutgoingPDUHandler + { +public: + virtual TInt HandleOutgoingIFrame(HIFramePDU* aIFrame) = 0; + virtual TInt HandleOutgoingBFrame(HBFramePDU* aBFrame) = 0; + virtual TInt HandleOutgoingGFrame(HGFramePDU* aGFrame) = 0; + virtual TInt HandleOutgoingCFrame(HCFramePDU* aCFrame) = 0; + virtual TInt HandleOutgoingSFrame(HSFramePDU* aSFrame) = 0; + }; + +// A PDU owner gets notified though this interface when a PDU send operation is complete. +// It's then up to the owner to delete the PDU or resend it, and to move it between +// various PDU lists - a PDU should not call iLink.Deque() nor destruct itself unless +// it has been orphaned. +// In practice CL2CAPMux owns C-, S- and B-Frames, while (E)RTM Data Controllers own +// I-Frames. +class MPduOwner + { +public: + virtual void HandlePduSendComplete(HL2CapPDU& aPdu) = 0; + virtual void HandlePduSendError(HL2CapPDU& aPdu) = 0; + }; + +enum TL2CapSAR + { + EUnsegmentedL2CapSDU = 0, + EStartOfL2CapSDU = 1, + EEndOfL2CapSDU = 2, + EContinuationOfL2CapSDU = 3, + }; + +enum TSupervisoryFunction + { + EReceiverReady = 0, + EReject = 1, + EReceiverNotReady = 2, + ESelectiveReject = 3, + }; + +class MDataPlaneElement + { +public: + virtual void DataElementSent(TUint16 aElementID = 0) = 0; + virtual void DataElementFlushed(TUint16 aElementID = 0) = 0; + + virtual TBool IsPDUFlushed() const = 0; + }; + +NONSHARABLE_CLASS(TDataPlaneElementHandle) : public TSockAddr + { +public: + struct SDataPlaneElementHandle + { + MDataPlaneElement* iDataElement; + TUint16 iElementID; + }; + + TDataPlaneElementHandle(MDataPlaneElement* aDataElement, TUint16 aElementID = 0); + + MDataPlaneElement& DataPlaneElement() const; + TUint16 ElementID() const; + }; + + +// +// Base class for all PDU types. +// +NONSHARABLE_CLASS(HL2CapPDU) : public MDataPlaneElement + { +public: + // Base or shared length definitions. + const static TUint8 KPDUHeaderLength = 4; + const static TUint8 KControlFieldLength = 2; + const static TUint8 KSDULengthFieldLength = 2; + const static TUint8 KMaxPDUHeaderLength = KPDUHeaderLength + KControlFieldLength + KSDULengthFieldLength; + const static TUint8 KFCSFieldLength = 2; + const static TUint8 KSFrameLength = 8; + + const static TUint8 KNumberOfFragmentsUnknown = 0; + + virtual ~HL2CapPDU(); + + // Deliver Interface. + virtual void DeliverOutgoingPDU(MOutgoingPDUHandler& aPDUHandler); + + // Method for reconstructing SDU data. + virtual void AppendPayloadToBuffer(RMBufChain& aSDUData); + + // Message Accessors. + // Static + static TUint16 PDUPayloadLength(const RMBufChain& aPDU); + static TUint16 PDUCID(const RMBufChain& aPDU); + static TInt CheckDecode(const RMBufChain& aPDU); + + // This method appends a fragment to the aPDU parameter and then + // checks if the PDU is complete. + static TBool AddFragment(RMBufChain& aPDU, RMBufChain& aPDUFragment); + + // Member + inline TUint16 PDUPayloadLength() const; + + inline TUint16 PDUCID() const; + void SetPDUCID(TUint16 aCID); + + // Calculate the PDU length, and place it in the PDU header. + void WritePDUPayloadLength(); + + // For basic mode return the fragment overhead otherwise return the + // L2CAP PDU overhead in bytes. + static TInt GetPDUOrFragmentOverhead(TBool aBasicMode); + + // Return the optimal PDU size based on the MTU and controller buffer size. + static TInt GetPDUOrFragmentSize(TInt aMTU, TInt aMaxMps, TInt aBufSize, TBool aBasicMode); + + inline TInt OptimalFragmentSize() const; + + // Inline accessor methods. + inline RMBufChain& PDUBuffer(); + inline void SetPDUFlushed(); + inline TBool IsPDUFlushed() const; + inline TDataPlaneElementHandle& ElementHandle(); + + void PDUSendPending(TUint16 aTotalNumberOfFragments); + void PDUSendComplete(); + + inline void SetPduOwner(MPduOwner* aPduOwner); + inline TBool HasOwner() const; + inline TBool IsOwner(const MPduOwner* aPduOwner) const; + inline void DeregisterPduOwner(); + + inline TBool IsAwaitingHciCompletion() const; + + inline TBool IsQueuedForSend() const; + inline void SetQueuedForSend(TBool aQueuedForSend); + + // MDataPlaneElement Interface + void DataElementSent(TUint16 aElementID); + void DataElementFlushed(TUint16 aElementID); + +protected: + // An optimal fragment size of 0 indicates that we do not expect fragmentation to take place + HL2CapPDU(RMBufChain& aPDUData, TInt aOptimalFragmentSize); + HL2CapPDU(TInt aOptimalFragmentSize = 0); + + void SetPDUPayloadLength(TUint16 aLength); + + virtual void DeliverOutgoingPDUDoubleDispatch(MOutgoingPDUHandler& aPDUHandler) = 0; + virtual void SendComplete(); // for frame type-specific actions on send complete + + inline TUint16 CalcCRC(); + static TUint16 CalcCRC(const RMBufChain& aPDU); + +public: + // General link though which the PDU is "owned" by different entities during its lifetime. + // An I-Frame PDU starts as a member of the PDU list in its SDU, then is pulled by a data + // controller and depending on the data controller, proceeds through its unacknowledged I-Frame + // lists (RTM and ERTM). B-Frames start linked to their SDUs but then are put on link muxer's + // PDU list, because Basic mode doesn't do retransmissions, so the data controller doesn't care. + // S-Frames are created inside data controllers and are put on the link muxer's list as well - + // they are fire'n'forget too. + // Note that the owning relationship is formalized with the MPduOwner interface. + TDblQueLink iLink; + // This is an additional link that can be used by a data controller to manage internal PDU + // queues. (currently used in ERTM for Outgoing Q) + TSglQueLink iDataControllerInternalQLink; +protected: + // Base or shared field positions. + const static TUint8 KLengthByteOffset = 0; + const static TUint8 KCIDByteOffset = 2; + const static TUint8 KControlFieldByteOffset = 4; + const static TUint8 KSDULengthFieldByteOffset = 6; + + // Control field bit masks. + const static TUint16 KCtrlFrameTypeMask = 0x0001; + const static TUint16 KCtrlTxSeqMask = 0x007e; + const static TUint16 KCtrlReTxDisableMask = 0x0080; + const static TUint16 KCtrlFinalBitMask = 0x0080; + const static TUint16 KCtrlPollBitMask = 0x0010; + const static TUint16 KCtrlReqSeqMask = 0x3f00; + const static TUint16 KCtrlSARMask = 0xc000; + const static TUint16 KCtrlSupervisoryMask = 0x000c; + + // Control field bit shifters. + const static TUint16 KCtrlTxSeqShift = 1; + const static TUint16 KCtrlReTxDisableShift = 7; + const static TUint16 KCtrlReqSeqShift = 8; + const static TUint16 KCtrlSARShift = 14; + + // All message information is held in this buffer. + RMBufChain iPDUData; + + TBool iIsFlushed; + TBool iSendingError; + + // True when it's on the OutgoingQ in ERTM waiting for GetPdu to pull it. + // Used for protecting the consistency of the OutgoingQ list - we mustn't try to + // queue the same frame twice. Also useful for detecting misbehaving remotes, if + // they try to request a retransmission of the same I-Frame more than once (per + // one loss). + TBool iQueuedForSend; + + // This indicates whether an initiated transmission has been completed by HCI or not, + // so that a retransmission of the PDU can proceed or be stalled until this becomes false. + // A PDU is supposed to start its life with this condition = False so that the initial + // transmission can proceed, and then it should become True once the PDU is scheduled + // for transmission. This is important for PDUs that can are acknowledgable or can be + // rejected by the peer (read I-Frames). In case the acknowledgement/reject comes through + // before the HCI completion event (which happens with the USB HCTL on Windows) we want + // to wait until an I-Frame is transmitted until we can delete it/start re-transmitting it. + // Note: an I-Frame may be queued for a retransmission before being completed by HCI + // (because it may happen that we receive the (S)REJ before the HCI packet completion + // event). That's why there are separate boolean flags instead of a single PDU state + // enum. + TBool iAwaitingHciCompletion; + + TDataPlaneElementHandle iElementHandle; + MPduOwner* iPduOwner; // Non-owned pointer. + + // Optimal Fragment size. + // Only set for basic mode as we expect segmentation only in all other cases. + TInt iOptimalFragmentSize; + +public: + TUint16 iTotalNumberOfFragments; + TUint16 iFragmentAcksReceived; + }; + +// +// Class used to fragment and send any type of PDU. +// An instance of this class is currently owned by the Mux +// +NONSHARABLE_CLASS(HFragmentedPDUSender) + { +public: + enum TFragmentSenderStatus + { + EFragmentOK = 0, + EFragmentationComplete = 1, + EFlowControlledOff = 2, + }; + + HFragmentedPDUSender(CL2CAPMux& aMuxer); + ~HFragmentedPDUSender(); + + TInt FragmentPDU(HL2CapPDU& aPDU); + TFragmentSenderStatus WriteNextFragment(CServProviderBase& aSender, TInt aACLMTU); + inline TBool IsPDUBeingSent() const; + + void Reset(); + void CheckForFlushed(); + void PDUSenderFailed(); + +private: + HL2CapPDU* iPDU; // Non-owned + HBufC8* iPDUBuffer; // Buffer for the outgoing PDU. + CL2CAPMux& iMuxer; // Reference to the sending object. + TInt iCurrentWriteIndex; // Current position in the PDU buffer. + TUint16 iCurrentFragmentID; // Current [next] fragment ID to be sent (first fragment has ID = 0) + TInt iPDULength; // Length of the PDU being sent. + TInt iPDUFragmentSize; // Optimal fragment size. + }; + +// +// Basic frame PDU class. +// +NONSHARABLE_CLASS(HBFramePDU) : public HL2CapPDU + { +public: + ~HBFramePDU(); + + static HBFramePDU* New(RMBufChain& aPayloadData, TInt aOptimalFragmentSize); + + // Deliver Interface. + void DeliverOutgoingPDUDoubleDispatch(MOutgoingPDUHandler& aPDUHandler); + + static void RemoveHeaderBytes(RMBufChain& aPDU); + void AppendPayloadToBuffer(RMBufChain& aSDUData); + + void PDUFragmentSent(TUint16 aFragmentIx); + + static TInt CheckPayloadDecode(const RMBufChain& aPDU); + +private: + HBFramePDU(RMBufChain& aPDUData, TInt aOptimalFragmentSize); + + virtual void SendComplete(); + }; + + +// +// Information frame PDU class. +// +NONSHARABLE_CLASS(HIFramePDU) : public HL2CapPDU + { +public: + ~HIFramePDU(); + + static HIFramePDU* New(RMBufChain& aPayloadData, TL2CapSAR aPduSAR); + + // Deliver Interface. + void DeliverOutgoingPDUDoubleDispatch(MOutgoingPDUHandler& aPDUHandler); + + // Message Accessors. + // Static + static TBool IFrameIdentifier(const RMBufChain& aPDU); + static TBool CheckFCS(const RMBufChain& aPDU); + static TInt CheckPayloadDecode(const RMBufChain& aPDU); + static TInt CheckLengthWithinLimits(const RMBufChain& aPDU, TUint16 aMps); + static TInt CheckStartSduLength(const RMBufChain& aPDU, TUint16 aMtu); + static TL2CapSAR SAR(const RMBufChain& aPDU); + static void RemoveHeaderAndFCSBytes(RMBufChain& aPDU); + static TBool IsStartOfSDU(const RMBufChain& aPDU); + static TUint16 SDUSize(const RMBufChain& aPDU); + static TUint8 ReqSeqNumber(const RMBufChain& aPDU); + static TUint8 TxSeqNumber(const RMBufChain& aPDU); + static TBool FinalBit(const RMBufChain& aPDU); + static TBool RetransmitDisable(const RMBufChain& aPDU); + + // Member + void SetFinalBit(TBool aFinalBit); + inline TBool FinalBit() const; + + inline TUint8 TxSeqNumber() const; + void SetTxSeqNumber(TUint8 aTxSeqNum); + + void SetRetransmitDisable(TBool aReTxDisable); + + inline TUint8 ReqSeqNumber() const; + void SetReqSeqNumber(TUint8 aReqSeqNum); + + inline TL2CapSAR SAR() const; + void SetSAR(TL2CapSAR aSARValue); + + inline TUint16 SDUSize() const; + void SetSDUSize(TUint16 aSDUSize); + + void CalculateAndSetFCS(); + TBool CheckFCS(); + + void AppendPayloadToBuffer(RMBufChain& aSDUData); + + inline TBool IsStartOfSDU() const; + + // Re-transmission Methods + inline TBool CanTransmit(TUint8 aMaxNumberOfTransmissions) const; + inline TUint8 TransmissionCount() const; + + inline TBool Acked() const { return iAcked; } + inline void SetAcked(TBool aAcked) { iAcked = aAcked; } + +private: + const static TUint16 KIFrameControlDefault = 0x0000; + + HIFramePDU(RMBufChain& aPDUData); + + void SetIFrameControlDefault(); + virtual void SendComplete(); + + TUint8 iTransmissionCount; + TBool iAcked; // has been acknowledged by the remote + }; + +// +// Group frame PDU class. +// +NONSHARABLE_CLASS(HGFramePDU) : public HL2CapPDU + { +public: + HGFramePDU(RMBufChain& aPDUData); + ~HGFramePDU(); + + static HGFramePDU* New(RMBufChain& aPayloadData); + + // Deliver Interface. + void DeliverOutgoingPDUDoubleDispatch(MOutgoingPDUHandler& aPDUHandler); + + TUint16 PSM() const; + void SetPSM(TUint16 aPSM); + + void AppendPayloadToBuffer(RMBufChain& aSDUData); + +private: + const static TUint8 KGFramePSMLength = 2; + }; + + +// +// Control frame PDU class. +// +NONSHARABLE_CLASS(HCFramePDU) : public HL2CapPDU + { +public: + const static TUint8 KCFrameHeaderLength = 4; + const static TUint8 KCFrameCodeSize = 1; + const static TUint8 KCFrameIdentifierSize = 1; + const static TUint8 KCFrameLengthSize = 2; + + static HCFramePDU* New(TInt aOptimalFragmentSize); + + HCFramePDU(RMBufChain& aPDUData, TInt aOptimalFragmentSize); + + ~HCFramePDU(); + + // Deliver Interface. + void DeliverOutgoingPDUDoubleDispatch(MOutgoingPDUHandler& aPDUHandler); + + HL2CapCommand* FirstCommand(); + HL2CapCommand* NextCommand(); + + inline TBool HasCommands() const; + + TInt AddCommand(HL2CapCommand& aCommand, CL2CAPMux& aMuxer); + TInt CheckDecode(); + +private: + TInt CheckPayloadDecode(); + TInt CreateCommands(); + + //Dbl link list of commands included within this pdu + TDblQue iCommands; + TDblQueIter iCommandIter; + }; + + +// +// Supervisory frame PDU class. +// +NONSHARABLE_CLASS(HSFramePDU) : public HL2CapPDU + { +public: + HSFramePDU(RMBufChain& aPDUData); + ~HSFramePDU(); + + static HSFramePDU* New(TSupervisoryFunction aFunction); + static HSFramePDU* NewL(TSupervisoryFunction aFunction); + + // Deliver Interface. + void DeliverOutgoingPDUDoubleDispatch(MOutgoingPDUHandler& aPDUHandler); + + // Message Accessors. + // Static + static TInt CheckLengthField(const RMBufChain& aPDU); + static TBool CheckFCS(RMBufChain& aPDU); + static TInt CheckPayloadDecode(RMBufChain& aPDU); + static TSupervisoryFunction SupervisoryFunction(const RMBufChain& aPDU); + static TUint8 ReqSeqNumber(const RMBufChain& aPDU); + static TBool RetransmitDisable(const RMBufChain& aPDU); + static TBool FinalBit(const RMBufChain& aPDU); + static TBool PollBit(const RMBufChain& aPDU); + + // Members + void SetFinalBit(TBool aFinalBit); + inline TBool FinalBit() const; + + void SetPollBit(TBool aPollBit); + inline TBool PollBit() const; + + void SetSupervisoryFunction(TSupervisoryFunction aSupervisoryFunction); + inline TSupervisoryFunction SupervisoryFunction() const; + + void SetRetransmitDisable(TBool aReTxDisable); + void SetReqSeqNumber(TUint8 aReqSeqNum); + inline TUint8 ReqSeqNumber() const; + + void CalculateAndSetFCS(); + TInt CheckFCS(); + + void SetSFrameControlDefault(); + + void PDUFragmentSent(TUint16 aFragmentIx); + +private: + const static TUint16 KSFrameControlDefault = 0x0001; + }; + + +// +// PDU Fragment sender inline methods. +// +inline TBool HFragmentedPDUSender::IsPDUBeingSent() const + { + return (iPDU != NULL); + } + + +// +// Base class inline methods. +// +inline RMBufChain& HL2CapPDU::PDUBuffer() + { + return iPDUData; + } + +inline void HL2CapPDU::SetPDUFlushed() + { + iIsFlushed = ETrue; + } + +inline TBool HL2CapPDU::IsPDUFlushed() const + { + return iIsFlushed; + } + +inline TDataPlaneElementHandle& HL2CapPDU::ElementHandle() + { + return iElementHandle; + } + +inline TUint16 HL2CapPDU::PDUCID() const + { + return PDUCID(iPDUData); + } + +inline TUint16 HL2CapPDU::PDUPayloadLength() const + { + return PDUPayloadLength(iPDUData); + } + +inline TUint16 HL2CapPDU::CalcCRC() + { + return CalcCRC(iPDUData); + } + +inline TInt HL2CapPDU::OptimalFragmentSize() const + { + return iOptimalFragmentSize; + } + +inline void HL2CapPDU::SetPduOwner(MPduOwner* aPduOwner) + { + __ASSERT_ALWAYS(aPduOwner != NULL, Panic(EL2CAPNullOwnerSupplied)); + __ASSERT_ALWAYS(iPduOwner == NULL || iPduOwner == aPduOwner, Panic(EL2CAPTryingToStealOwnedPdu)); + iPduOwner = aPduOwner; + } + +inline TBool HL2CapPDU::HasOwner() const + { + return iPduOwner != NULL; + } + +inline TBool HL2CapPDU::IsOwner(const MPduOwner* aPduOwner) const + { + __ASSERT_ALWAYS(aPduOwner != NULL, Panic(EL2CAPNullOwnerSupplied)); + return iPduOwner == aPduOwner; + } + +inline void HL2CapPDU::DeregisterPduOwner() + { + iPduOwner = NULL; + } + +inline TBool HL2CapPDU::IsAwaitingHciCompletion() const + { + return iAwaitingHciCompletion; + } + +inline TBool HL2CapPDU::IsQueuedForSend() const + { + return iQueuedForSend; + } + +inline void HL2CapPDU::SetQueuedForSend(TBool aQueuedForSend) + { + iQueuedForSend = aQueuedForSend; + } + +// +// Information frame inline methods. +// +inline TBool HIFramePDU::CanTransmit(TUint8 aMaxNumberOfTransmissions) const + { + return (iTransmissionCount < aMaxNumberOfTransmissions); + } + +inline TUint8 HIFramePDU::TransmissionCount() const + { + return iTransmissionCount; + } + + +inline TL2CapSAR HIFramePDU::SAR() const + { + return SAR(iPDUData); + } + +inline TBool HIFramePDU::IsStartOfSDU() const + { + return IsStartOfSDU(iPDUData); + } + +inline TUint16 HIFramePDU::SDUSize() const + { + return SDUSize(iPDUData); + } + +inline TUint8 HIFramePDU::ReqSeqNumber() const + { + return ReqSeqNumber(iPDUData); + } + +inline TUint8 HIFramePDU::TxSeqNumber() const + { + return TxSeqNumber(iPDUData); + } + +inline TBool HIFramePDU::FinalBit() const + { + return FinalBit(iPDUData); + } + +// +// Control frame inline methods. +// +inline TBool HCFramePDU::HasCommands() const + { + return !iCommands.IsEmpty(); + } + +// +// Supervisory frame inline methods. +// +inline TBool HSFramePDU::FinalBit() const + { + return FinalBit(iPDUData); + } + +inline TBool HSFramePDU::PollBit() const + { + return PollBit(iPDUData); + } + +inline TSupervisoryFunction HSFramePDU::SupervisoryFunction() const + { + return SupervisoryFunction(iPDUData); + } + +inline TUint8 HSFramePDU::ReqSeqNumber() const + { + return ReqSeqNumber(iPDUData); + } + +#endif