diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/l2cap/L2CapEnhancedDataController.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/l2cap/L2CapEnhancedDataController.h Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,566 @@ +// Copyright (c) 2008-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 L2CAPENHANCEDDATACONTROLLER_H +#define L2CAPENHANCEDDATACONTROLLER_H + +#include +#include + +#include "L2CapPDU.h" +#include "L2CapSDUQueue.h" +#include "L2CapDataController.h" +#include "l2signalmgr.h" + + +NONSHARABLE_CLASS(CL2CapStreamingController) : public CL2CapBasicDataController, public MPduOwner + { +public: + CL2CapStreamingController(TL2CAPPort aLocalCID, TL2CAPPort aRemoteCID, CL2CAPMux& aMuxer, CL2CapSDUQueue& aSDUQueue, TL2CapDataControllerConfig* aConfig); + virtual ~CL2CapStreamingController(); + + virtual void ProcessFlushTimerExpiry(); + + // MPduOwner + virtual void HandlePduSendComplete(HL2CapPDU& aPdu); + virtual void HandlePduSendError(HL2CapPDU& aPdu); + +protected: + virtual void HandleIncomingIFrameL(RMBufChain& aIFrame); + virtual TInt HandleOutgoingIFrame(HIFramePDU* aIFrame); + + virtual HL2CapPDU* GetPduL(); + +protected: + // Window related information, as defined in the spec. + // Sequencer for outgoing I-Frame numbers. + TUint8 iNextTxSeq; + // Next expected incoming I-Frame number. + TUint8 iExpectedTxSeq; + }; + + +NONSHARABLE_CLASS(RL2CapErtmTimerManager) : public RL2CapRetransmissionModeTimerManager + { +public: + RL2CapErtmTimerManager(class CL2CapEnhancedReTxController& aClient); + void Close(); + + void StartLocalBusyDelayTimer(); + void StopLocalBusyDelayTimer(); + inline TBool IsLocalBusyDelayTimerRunning() const; +private: + void HandleLocalBusyDelayTimerExpired(); + static TInt LocalBusyDelayTimerExpired(TAny* aTimerMan); +private: + // Peer Retransmission Timer value is divided by this to obtain the LB delay timer + // value to use. + const static TInt KLocalBusyDelayTimerDenominator = 2; +private: + TBool iLocalBusyDelayTimerRunning; + TDeltaTimerEntry iLocalBusyDelayTimerEntry; + }; + + +class CL2CapEnhancedReTxController; +class RL2CapErtmOutgoingQueue; + +// Encapsulates a list of unacknowledged I-Frames and an index for fast TxSeqs +// lookups for SREJ retransmissions. +NONSHARABLE_CLASS(RL2CapErtmUnacknowledgedIFrames) + { +public: + RL2CapErtmUnacknowledgedIFrames(); + void Close(); + + inline void Append(HIFramePDU& aFrame); + inline void Remove(HIFramePDU& aFrame); + inline HIFramePDU* First() const; + inline HIFramePDU* Last() const; + inline HIFramePDU* operator[](TUint8 aTxSeq) const; + inline TBool IsEmpty() const; + inline TDblQueIter Iterator(); +private: + // Ordered chronologically, oldest to youngest. + TDblQue iFrameList; + // Indexed by TxSeq. + TFixedArray iFrameIndex; + }; + +NONSHARABLE_CLASS(CL2CapErtmDataTransmitter) + { +public: + static CL2CapErtmDataTransmitter* NewL(CL2CapEnhancedReTxController& aController); + virtual ~CL2CapErtmDataTransmitter(); + + void HandleIncomingIFrame(RMBufChain& aIFrame); + void HandleIncomingSFrameL(RMBufChain& aSFrame); + + TBool HaveSpaceInOutgoingWindow() const; + HIFramePDU* GetIFrameToSendL(); + + void HciCompletedIFrame(HIFramePDU& aIFrame); + + void AckTimerExpired(); + + inline TBool RemoteBusy() const; + + inline TBool IsWaitAckStatePending() const; + inline void EnterWaitAckState(); + inline TBool InWaitAckState() const; + + inline TUint8 NextTxSeq() const; + inline TUint8 ExpectedAckSeq() const; + + inline TBool IsReqSeqValid(TUint8 aReqSeq); + + inline TBool HaveUnackedIFrames() const; + inline TBool IsRetransmittingUnackedIFrames() const; + inline TBool IsNextUnackedIFrameAwaitingHciCompletion() const; + +private: + CL2CapErtmDataTransmitter(CL2CapEnhancedReTxController& aController); + + void HandleReqSeqAck(TUint8 aReqSeq); + void HandleFinalAck(); + + void HandleIncomingSRejL(RMBufChain& aSRejFrame); + + void RetransmitUnackedIFrames(); + + inline void SendOrPendL(HIFramePDU& aIFrame); + + inline RL2CapRetransmissionModeTimerManager& TimerMan(); + inline RL2CapErtmOutgoingQueue& OutgoingQ(); +private: + CL2CapEnhancedReTxController& iController; + + // Outgoing window information, as defined by the spec. + // Sequencer for outgoing I-Frame numbers. With the little exception that when + // a decision to retransmit all unacked I-Frames is made, this gets set to the + // first number of the bunch and frames are retransmitted until iUnackedIFrames[iNextTxSeq] + // is NULL again. + TUint8 iNextTxSeq; + // I-Frames preceding this number have been acknowledged by the peer. + TUint8 iExpectedAckSeq; + + RL2CapErtmUnacknowledgedIFrames iUnackedIFrames; + + // If remote indicates busy condition we keep transmitting within send window limits, + // but disable our retransmission timer. + TBool iRemoteBusy; + + // For protecting against duplicates. Explained in HandleIncomingSFrameL(). + // See state tables in the spec. + TBool iSRejActioned; + TUint8 iSRejSaveReqSeq; + + // Signals to GetPdu that we need to enter WAIT_ACK. + TBool iWaitAckStatePending; + // Xmit state WAIT_ACK (syncing ReqSeq to start retransmission from) - see spec. + TBool iInWaitAckState; + }; + + +class CL2CapErtmDataReceiver; + +NONSHARABLE_CLASS(TL2CapErtmMissingTxSeqs) + { +public: + TL2CapErtmMissingTxSeqs(); + + // Register SREJ-requested I-Frame TxSeq. + inline void AppendTxSeq(TUint8 aTxSeq); + + // Received an SREJ requested I-Frame. Take note of this fact and indicate whether + // it was out of sequence wrt to previous ones. If so, GetNextTxSeqForResend() needs + // to be called in a loop to return all the TxSeqs to re-request. + TBool ReceivedTxSeq(TUint8 aTxSeq); + inline TBool GetNextTxSeqForResend(TUint8& aTxSeq); + + TBool IsTxSeqOnTheList(TUint8 aTxSeq) const; + inline TUint8 LastTxSeq() const; + inline TBool IsEmpty() const; + + inline TBool AllRequestedFramesReceived() const; + inline TUint8 ExpectedSRejTxSeq() const; + inline TInt NumMissingTxSeqs() const; + inline TBool HaveSpaceForNewTxSeqs(TInt aNumTxSeqs) const; + inline void Reset(); +private: +#ifdef __FLOG_ACTIVE + void Log(); +#endif +public: + const static TUint KMaxSRejsInFlight = 1; +private: + TFixedArray iMissingTxSeqs; + // Number of TxSeqs missing since entering SREJ_SENT. + // The array is split in two parts - frames already received and the ones we're still + // waiting for. + TInt iNumMissingTxSeqs; + // Frames below this index have been received. + TInt iExpectedRecvIdx; + // Frames starting from this index need to be re-requested. + TInt iResendIdx; + }; + + +NONSHARABLE_CLASS(TL2CapErtmReceiverStateBase) + { +public: + // Note: think about the frame parameter as "the frame that caused the transition", + // though it's only really used for SREJ_SENT. Still, this is the cleanest form + // of passing the required information. + virtual void EnterL(RMBufChain* aIFrame); + virtual void ExitL(); + virtual void HandleIncomingIFrameL(RMBufChain& aIFrame) = 0; + virtual void HandlePollL() = 0; + virtual TBool IsLocalBusySupported() const; + + // Called to notify the state that the incoming SDU Q has just consumed some + // I-Frames and we could slide the receive window if we can (we can't in SREJ_SENT). + virtual void TxSeqExpectedBySduQChanged(TUint8 aTxSeq) = 0; +protected: + TL2CapErtmReceiverStateBase(CL2CapErtmDataReceiver& aReceiver); +protected: + CL2CapErtmDataReceiver& iReceiver; + }; + +NONSHARABLE_CLASS(TL2CapErtmReceiverStateRecv) : public TL2CapErtmReceiverStateBase + { +public: + TL2CapErtmReceiverStateRecv(CL2CapErtmDataReceiver& aReceiver); + virtual void EnterL(RMBufChain* aIFrame); + virtual void HandleIncomingIFrameL(RMBufChain& aIFrame); + virtual void HandlePollL(); + virtual TBool IsLocalBusySupported() const; + virtual void TxSeqExpectedBySduQChanged(TUint8 aTxSeq); + }; + +NONSHARABLE_CLASS(TL2CapErtmReceiverStateRejSent) : public TL2CapErtmReceiverStateBase + { +public: + TL2CapErtmReceiverStateRejSent(CL2CapErtmDataReceiver& aReceiver); + virtual void EnterL(RMBufChain* aIFrame); + virtual void HandleIncomingIFrameL(RMBufChain& aIFrame); + virtual void HandlePollL(); + virtual void TxSeqExpectedBySduQChanged(TUint8 aTxSeq); + }; + +NONSHARABLE_CLASS(TL2CapErtmReceiverStateSRejSent) : public TL2CapErtmReceiverStateBase + { +public: + TL2CapErtmReceiverStateSRejSent(CL2CapErtmDataReceiver& aReceiver); + virtual void EnterL(RMBufChain* aIFrame); + virtual void ExitL(); + virtual void HandleIncomingIFrameL(RMBufChain& aIFrame); + virtual void HandlePollL(); + virtual void TxSeqExpectedBySduQChanged(TUint8 aTxSeq); +private: + void SendSRejsUpToReceivedIFrameL(RMBufChain& aIFrame); +private: + TBool iGoToRej; + TL2CapErtmMissingTxSeqs iMissingTxSeqs; + }; + + +// Incoming I-Frame queue. Used both when the SDU Q is full and we have to buffer incoming +// frames (a whole window is buffered) and when SREJ_SENT is entered and we wait for +// SREJ-requested retransmissions to come through. +NONSHARABLE_CLASS(RL2CapErtmIncomingIFrameQueue) + { +public: + RL2CapErtmIncomingIFrameQueue(); + void Close(); + + // Enqueue the new I-Frame and try to consume as much of the existing ones as possible. + void HandleIncomingIFrameL(RMBufChain& aIFrame, const CL2CapErtmDataReceiver& aReceiver); + // Pass as many consequent I-Frames to SDU Q ("consume them") as possible. + void ConsumeUpToFirstGapL(const CL2CapErtmDataReceiver& aReceiver); + inline void DeleteAllFrames(); + + inline TBool IsEmpty() const; + inline TUint8 TxSeqExpectedBySduQ() const; +private: + void Insert(RMBufChain& aIFrame); +#ifdef _DEBUG + void LogQ(); +#endif +private: + RMBufPktQ iQueue; + // Next TxSeq in sequence for SDU Q. + // If we're not in SREJ_SENT, it's = BufferSeq. + // If we're in SREJ_SENT, it's >= BufferSeq. + TUint8 iTxSeqExpectedBySduQ; + }; + +NONSHARABLE_CLASS(CL2CapErtmDataReceiver) + { +public: + static CL2CapErtmDataReceiver* NewL(CL2CapEnhancedReTxController& aController); + virtual ~CL2CapErtmDataReceiver(); + + void HandleIncomingIFrameL(RMBufChain& aIFrame); + void HandleIncomingSFrameL(RMBufChain& aSFrame); + + // Recv window management. + void SendPeerAckTimerExpiredL(); + TBool IsEndOfReceiveWindowApproaching() const; + template + void StampWithReqSeq(FrameType& aFrame); + + // Send SREJ or REJ ? + TBool IsSRejPreferredToRej(TUint8 aExpectedTxSeq, TUint8 aReceivedTxSeq); + + // Incoming SDU Q status/LocalBusy. + void SetIncomingSduQFullL(TBool aIncomingSduQFull); + void UpdateLocalBusyStatusL(); + inline TBool IsIncomingSduQFull() const; + inline TBool LocalBusy() const; + void LocalBusyDelayTimerExpired(); + + inline void FlushBufferedIncomingIFrames(); + + inline TBool IsWaitFStatePending() const; + inline void EnterWaitFState(); + inline TBool InWaitFState() const; + + // Window state accessors, both for state objects and the controller. + inline TUint8 ExpectedTxSeq() const; + inline void SetExpectedTxSeq(TUint8 aTxSeq); + inline void IncExpectedTxSeq(); + + inline TUint8 BufferSeq() const; + inline void SetBufferSeq(TUint8 aTxSeq); + + inline TUint8 LastAckReqSeq() const; + + inline TUint8 TxSeqExpectedBySduQ() const; + + inline TBool SendAck() const; + + // As defined in the spec. + inline TBool IsTxSeqUnexpected(TUint8 aTxSeq); + inline TBool IsTxSeqInvalid(TUint8 aTxSeq); + + // Handy helpers. + HSFramePDU* GetAckFrameL(TBool aFinal = EFalse); + inline void PassToIncomingQL(RMBufChain& aIFrame); + inline void PassToSduQL(RMBufChain& aIFrame) const; + + inline void SetStateRecvL(); + inline void SetStateRejSentL(); + inline void SetStateSRejSentL(RMBufChain& aUnexpectedIFrame); + + inline RL2CapErtmTimerManager& TimerMan(); + inline RL2CapErtmOutgoingQueue& OutgoingQ(); + inline CL2CapEnhancedReTxController& Controller() const; + +private: + CL2CapErtmDataReceiver(CL2CapEnhancedReTxController& aController); + void ConstructL(); + + void SetStateL(TL2CapErtmReceiverStateBase& aState, RMBufChain* aIFrame); + + void HandleFinalAckL(); + + void EnterLocalBusyL(); + void ExitLocalBusy(); + +private: + // This value represents the number of frames the peer can still send within its current TxWin + // when we decide to send an acknowledgement. E.g. if the peer TxWindow is 20 then we'll send + // an acknowledgement after receiving the 18th frame, thus moving the peer's window and ready + // to receive next 18 frames before sending the next ack. Sending the ack before the end + // of the receive window is reached prevents transmission delays caused by the peer waiting + // for an ack before it can send more. + const static TUint8 KReceiveWinFreeSpaceLeftToTriggerAck = 2; + // Used to decide whether to send an SREJ or a REJ. An SREJ will never be sent if the gap + // between ExpectedTxSeq and the received frame's TxSeq is wider than this value. + const static TUint8 KSRejMissingFrameThreshold = 1; + + __ASSERT_COMPILE(KSRejMissingFrameThreshold <= TL2CapErtmMissingTxSeqs::KMaxSRejsInFlight); + +private: + CL2CapEnhancedReTxController& iController; + + // Window variables as defined in the spec. + // Next expected I-Frame num. + TUint8 iExpectedTxSeq; + // Ack num (ReqSeq) that can be sent to the peer. Usually equal to ExpectedTxSeq, + // unless SDU Queue is full or we're in SREJ_SENT. IOW the beginning of our receive window. + TUint8 iBufferSeq; + // Stores the acknowledgment number most recently sent to the peer. + // Used to detect that we're reaching the end of peer's send window and hence should + // send an ack. + TUint8 iLastAckReqSeq; + + // This means SDU queue won't accept any more data, so we can't acknowledge anything new. + // Whether LocalBusy can be entered depends on the receive state (RECV only) and other nasty + // details. + TBool iIncomingSduQFull; + // This means we're sending out RNRs instead of RRs. Proper LocalBusy. + TBool iLocalBusy; + // Xmit state WAIT_F (exiting Local Busy) - see spec. + TBool iInWaitFState; + // Signals to GetPdu that we need to enter WAIT_F. + TBool iWaitFStatePending; + + // This is set to True when we're approaching the end of receive window or the send peer ack + // timer expires, or sending a generic ack is needed for some other reason. + // While flags are bad bad bad in general because they don't store time-ordering information + // of related events (that's why we have the outgoing Q instead), it is fine this case - + // - it's legitimate to send a vanilla ack at any time. It's an optimization that prevents + // multiple acks to be queued when the outgoing pipe gets clogged for a while but we keep + // receiving incoming I-Frames. + TBool iSendAck; + + RL2CapErtmIncomingIFrameQueue iIncomingIFrameQ; + + // "Heavyweight" State pattern. + TL2CapErtmReceiverStateBase* iReceiveState; + + TL2CapErtmReceiverStateRecv iStateRecv; + TL2CapErtmReceiverStateRejSent iStateRejSent; + TL2CapErtmReceiverStateSRejSent iStateSRejSent; + }; + +// This encapsulates the managament of the outgoing packet queue. +// Every packet should go through the queue before it's sent - certain actions need to be +// executed when a packet is enqueued/dequeued and to make sure that it's always done +// the queue is hidden in this class. +NONSHARABLE_CLASS(RL2CapErtmOutgoingQueue) + { +public: + RL2CapErtmOutgoingQueue(CL2CapEnhancedReTxController& aController); + void Close(); + + // These queue frames that can be sent immediately. + void QueueIFrameL(HIFramePDU& aIFrame); + void QueueAckingSFrame(HSFramePDU& aSFrame); + void QueueNonAckingSFrame(HSFramePDU& aSFrame, TUint8 aReqSeq); + + // This queues the I-Frame onto a separate list pending clearing of WAIT_ACK. + // It's used for I-Frames requested with SREJs coming through in WAIT_ACK. + void PendRetransmitIFrameL(HIFramePDU& aIFrame); + + // This requeues the I-Frames from the pending list onto the normal send queue. + void SendPendingRetransmitIFramesL(); + // Used when we decide to retransmit all I-Frames when we still haven't sent + // out the SREJ-requested ones. + void CancelPendingRetransmitIFrames(); + + void DeleteAllFrames(); + + // Called by controller's GetPdu when the link muxer asks for a frame to send. + HL2CapPDU* DequeueNextToSend(); + + inline TBool HaveFramesToTransmit() const; + inline TBool HavePendingRetransmitIFrames() const; +private: + CL2CapEnhancedReTxController& iController; + TSglQue iOutgoingQ; + // I-Frames SREJ-requested by the remote when we're in WAIT_ACK and thus can't send data. + TSglQue iPendRetransmitIFrameQ; + }; + +NONSHARABLE_CLASS(CL2CapEnhancedReTxController) + : public CL2CapBasicDataController, public MPduOwner, public MRetransmissionModeTimerClient + { +public: + static CL2CapEnhancedReTxController* NewL(TL2CAPPort aLocalCID, TL2CAPPort aRemoteCID, CL2CAPMux& aMuxer, CL2CapSDUQueue& aSDUQueue, TL2CapDataControllerConfig* aConfig); + virtual ~CL2CapEnhancedReTxController(); + + // MPduOwner + // Called back by I-Frames on HCI completion. + virtual void HandlePduSendComplete(HL2CapPDU& aPdu); + virtual void HandlePduSendError(HL2CapPDU& aPdu); + + // Called back by PDUs going out through the HL2CapPDU::DeliverOutgoingPDU + // double-dispatch trickery, in the context of GetPduL. + virtual TInt HandleOutgoingIFrame(HIFramePDU* aIFrame); + virtual TInt HandleOutgoingSFrame(HSFramePDU* aSFrame); + + // Called by the SDU Q when it gets full/space frees up. + virtual void SetIncomingSduQFull(TBool aIncomingSduQFull); + + // Called by the SDU Q when the connection is being shutdown by the application + // and data needs to be delivered reliably before we disconnect. + virtual TBool DeliverOutgoingDataAndSignalToSduQWhenDone(); + inline TBool IsOutgoingDataPathClosing(); + // The data transmitter calls this when it wants to notify the SDU Q that it has + // delivered all outgoing data and so we can now be destroyed. + inline void SignalOutgoingDataDeliveredToSduQ(); + + // Incoming Q calls this to pass an I-Frame to the SDU Q. + inline void PassToSduQL(RMBufChain& aIFrame); + + // Called from SDU Q when it's got new PDUs to send. + virtual void OutgoingPduAvailableOnSduQ(); + + // Called to evaluate whether we want to send anything. + inline void NotifyMuxerOfPdusToSendIfHaveSome(); + + // Puts the current acknowledgement number into the frame being queued for send. + // Templated to handle both I-Frames and S-Frames. + template + inline void StampWithReqSeq(FrameType& aFrame); + + // A helper for when a ReqSeq/Final ack needs to be sent - tries to piggy-back on I-Frames. + void SendIOrRrOrRnrL(TBool aFinal); + + virtual void LocalBusyDelayTimerExpired(); + + inline TBool IsPollOutstanding() const; + inline TBool IsFBitValid(TBool aFinal); + + inline RL2CapErtmOutgoingQueue& OutgoingQ(); + inline RL2CapErtmTimerManager& TimerMan(); + +protected: + CL2CapEnhancedReTxController(TL2CAPPort aLocalCID, TL2CAPPort aRemoteCID, CL2CAPMux& aMuxer, CL2CapSDUQueue& aSDUQueue, TL2CapDataControllerConfig* aConfig); + void ConstructL(); + + // These are the entry points for incoming PDUs, called and trapped by a dispatch routine + // in CL2CapBasicDataController. + virtual void HandleIncomingIFrameL(RMBufChain& aIFrame); + virtual void HandleIncomingSFrameL(RMBufChain& aSFrame); + + // Provide a PDU for transmission. Called and trapped by GetPdu in CL2CapBasicDataController. + virtual HL2CapPDU* GetPduL(); + + void HandleFinalAck(); + + // From MErtmModeTimerClient + virtual void MonitorTimerExpired(); + virtual void AckTimerExpired(); + virtual void SendPeerAckTimerExpired(); + virtual TUint16 MonitorTimeout(); + virtual TUint16 RetransmissionTimeout(); + virtual TUint16 PeerRetransmissionTimeout(); +private: + TUint8 iPollSFrameTransmitCount; + TBool iDeliverOutgoingDataAndSignalToSduQWhenDone; + + RL2CapErtmOutgoingQueue iOutgoingQ; + RL2CapErtmTimerManager iTimerMan; + CL2CapErtmDataReceiver* iReceiver; + CL2CapErtmDataTransmitter* iTransmitter; + }; + +#include "L2CapEnhancedDataController.inl" + +#endif /*L2CAPENHANCEDDATACONTROLLER_H*/