bluetooth/btstack/l2cap/L2CapEnhancedDataController.inl
changeset 0 29b1cd4cb562
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/l2cap/L2CapEnhancedDataController.inl	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,441 @@
+// 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_INL
+#define L2CAPENHANCEDDATACONTROLLER_INL
+
+
+inline TBool RL2CapErtmTimerManager::IsLocalBusyDelayTimerRunning() const
+	{
+	return iLocalBusyDelayTimerRunning;
+	}
+
+
+inline void RL2CapErtmUnacknowledgedIFrames::Append(HIFramePDU& aFrame)
+	{
+	__ASSERT_DEBUG(iFrameIndex[aFrame.TxSeqNumber()] == NULL,
+				   Panic(EL2CAPTryingToAppendSameTxSeqIFrameTwice));
+
+	iFrameList.AddLast(aFrame);
+	iFrameIndex[aFrame.TxSeqNumber()] = &aFrame;
+	}
+
+inline void RL2CapErtmUnacknowledgedIFrames::Remove(HIFramePDU& aFrame)
+	{
+	__ASSERT_DEBUG(iFrameIndex[aFrame.TxSeqNumber()] == &aFrame,
+				   Panic(EL2CAPTryingToRemoveUnackedIFrameWithSameTxSeqButDifferentAddress));
+
+	aFrame.iLink.Deque();
+	iFrameIndex[aFrame.TxSeqNumber()] = NULL;
+	}
+
+inline HIFramePDU* RL2CapErtmUnacknowledgedIFrames::First() const
+	{
+	return iFrameList.First();
+	}
+
+inline HIFramePDU* RL2CapErtmUnacknowledgedIFrames::Last() const
+	{
+	return iFrameList.Last();
+	}
+
+inline HIFramePDU* RL2CapErtmUnacknowledgedIFrames::operator[](TUint8 aTxSeq) const
+	{
+	return iFrameIndex[aTxSeq];
+	}
+
+inline TBool RL2CapErtmUnacknowledgedIFrames::IsEmpty() const
+	{
+	return iFrameList.IsEmpty();
+	}
+
+inline TDblQueIter<HIFramePDU> RL2CapErtmUnacknowledgedIFrames::Iterator()
+	{
+	return TDblQueIter<HIFramePDU>(iFrameList);
+	}
+
+
+inline TBool CL2CapErtmDataTransmitter::RemoteBusy() const
+	{
+	return iRemoteBusy;
+	}
+
+inline TBool CL2CapErtmDataTransmitter::IsWaitAckStatePending() const
+	{
+	return iWaitAckStatePending;
+	}
+
+inline void CL2CapErtmDataTransmitter::EnterWaitAckState()
+	{
+	__ASSERT_DEBUG(!iController.IsPollOutstanding(),
+				   Panic(EL2CAPAttemptToEnterWaitAckWhenAlreadyInAWaitState));
+
+	iWaitAckStatePending = EFalse;
+	iInWaitAckState = ETrue;
+	}
+
+inline TBool CL2CapErtmDataTransmitter::InWaitAckState() const
+	{
+	return iInWaitAckState;
+	}
+
+inline void CL2CapErtmDataTransmitter::SendOrPendL(HIFramePDU& aIFrame)
+	{
+	if (iInWaitAckState)
+		{
+		iController.OutgoingQ().PendRetransmitIFrameL(aIFrame);
+		}
+	else
+		{
+		iController.OutgoingQ().QueueIFrameL(aIFrame);
+		}
+	}
+
+inline RL2CapRetransmissionModeTimerManager& CL2CapErtmDataTransmitter::TimerMan()
+	{
+	return iController.TimerMan();
+	}
+
+inline RL2CapErtmOutgoingQueue& CL2CapErtmDataTransmitter::OutgoingQ()
+	{
+	return iController.OutgoingQ();
+	}
+
+inline TBool CL2CapErtmDataTransmitter::HaveUnackedIFrames() const
+	{
+	return !iUnackedIFrames.IsEmpty();
+	}
+
+inline TBool CL2CapErtmDataTransmitter::IsRetransmittingUnackedIFrames() const
+	{
+	return iUnackedIFrames[iNextTxSeq] != NULL;
+	}
+
+inline TBool CL2CapErtmDataTransmitter::IsNextUnackedIFrameAwaitingHciCompletion() const
+    {
+    __ASSERT_DEBUG(IsRetransmittingUnackedIFrames(), Panic(EL2CapReferencingUnackedIFrameWhenNoRetransmissionGoing));
+    return iUnackedIFrames[iNextTxSeq]->IsAwaitingHciCompletion();
+    }
+
+inline TBool CL2CapErtmDataTransmitter::IsReqSeqValid(TUint8 aReqSeq)
+	{
+	// Per the spec:
+	// With-Valid-ReqSeq: The ReqSeq of the received frame is in the range
+	// ExpectedAckSeq <= ReqSeq <= NextTxSeq.
+	//
+	// We can't just use NextTxSeq though because in our implementation we slip it back when we
+	// retransmit all unacked I-Frames, which means it doesn't always point to the next TxSeq to
+	// allocate, it's rather the next TxSeq to allocate OR retransmit. The real boundary of the
+	// TxSeqs sent out so far in the current window is the youngest I-Frame on the unacknowledged
+	// list, so we use that in this check.
+	//
+	// Note: ideally we should just be able to use NextTxSeq because the protocol is designed
+	// so that you only retransmit all unacked I-Frames once you've made sure they haven't been
+	// received by the peer, which would mean that we only slip NextTxSeq back having made
+	// sure that the peer will not acknowledge anything beyond that value. There is however
+	// one loophole in the protocol: when the peer exits LocalBusy and sends us an RR (we need
+	// to restart the transmission from the ReqSeq given in that RR) while we keep on sending
+	// I-Frames during the RR's travel time. These I-Frames will obviously have newer TxSeqs
+	// than the ReqSeq from the RR acknowledges and will thus be retransmitted when we receive
+	// the RR. So if the peer didn't ignore the original transmissions that came through
+	// when it was in WAIT_F, it will detect the retransmissions as duplicates and (if it's a
+	// correct implementation) close the connection. Some unwisely coded remotes however will
+	// accept the originals and ignore the duplicates, and in that case we may receive a ReqSeq
+	// for the originals while we're retransmitting them, which means it will be a younger
+	// seqNum than NextTxSeq.
+
+	const TUint8 lastValidReqSeq = iUnackedIFrames.IsEmpty() ? iNextTxSeq : L2CapDataUtils::Mod64(iUnackedIFrames.Last()->TxSeqNumber() + 1); 
+	return L2CapDataUtils::InWindow(aReqSeq, iExpectedAckSeq, lastValidReqSeq);
+	}
+
+inline TUint8 CL2CapErtmDataTransmitter::ExpectedAckSeq() const
+	{
+	return iExpectedAckSeq;
+	}
+
+inline TUint8 CL2CapErtmDataTransmitter::NextTxSeq() const
+	{
+	return iNextTxSeq;
+	}
+
+
+inline void TL2CapErtmMissingTxSeqs::AppendTxSeq(TUint8 aTxSeq)
+	{
+	__ASSERT_DEBUG(iNumMissingTxSeqs < KMaxSRejsInFlight,
+				   Panic(EL2CAPGivenMoreSRejedTxSeqsToTraceThanCanStore));
+
+	iMissingTxSeqs[iNumMissingTxSeqs++] = aTxSeq;
+	}
+
+inline TBool TL2CapErtmMissingTxSeqs::GetNextTxSeqForResend(TUint8& aTxSeq)
+	{
+	__ASSERT_DEBUG(iResendIdx < iNumMissingTxSeqs,
+				   Panic(EL2CAPStrayCallToGetTxSeqForResend));
+
+	aTxSeq = iMissingTxSeqs[iResendIdx++];
+	return iResendIdx < iNumMissingTxSeqs;
+	}
+
+inline TUint8 TL2CapErtmMissingTxSeqs::LastTxSeq() const
+	{
+	__ASSERT_DEBUG(!IsEmpty(),
+				   Panic(EL2CAPLastTxSeqCalledWhenNoneAreExpected));
+
+	return iMissingTxSeqs[iNumMissingTxSeqs - 1];
+	}
+
+inline TBool TL2CapErtmMissingTxSeqs::IsEmpty() const
+	{
+	return iNumMissingTxSeqs == 0;
+	}
+
+inline TBool TL2CapErtmMissingTxSeqs::AllRequestedFramesReceived() const
+	{
+	return iExpectedRecvIdx == iNumMissingTxSeqs;
+	}
+
+inline TUint8 TL2CapErtmMissingTxSeqs::ExpectedSRejTxSeq() const
+	{
+	__ASSERT_DEBUG(!IsEmpty() && !AllRequestedFramesReceived(),
+				   Panic(EL2CAPNextExpectedTxSeqCalledWhenNoneAreExpected));
+	return iMissingTxSeqs[iExpectedRecvIdx];
+	}
+
+inline TInt TL2CapErtmMissingTxSeqs::NumMissingTxSeqs() const
+	{
+	return iNumMissingTxSeqs;
+	}
+
+inline TBool TL2CapErtmMissingTxSeqs::HaveSpaceForNewTxSeqs(TInt aNumTxSeqs) const
+	{
+	return KMaxSRejsInFlight - iNumMissingTxSeqs >= aNumTxSeqs;
+	}
+
+
+inline void TL2CapErtmMissingTxSeqs::Reset()
+	{
+	iNumMissingTxSeqs = iExpectedRecvIdx = iResendIdx = 0;
+	}
+
+
+inline TBool RL2CapErtmIncomingIFrameQueue::IsEmpty() const
+	{
+	return iQueue.IsEmpty();
+	}
+
+inline void RL2CapErtmIncomingIFrameQueue::DeleteAllFrames()
+	{
+	iQueue.Free();
+	}
+
+inline TUint8 RL2CapErtmIncomingIFrameQueue::TxSeqExpectedBySduQ() const
+	{
+	return iTxSeqExpectedBySduQ;
+	}
+
+inline void CL2CapErtmDataReceiver::PassToIncomingQL(RMBufChain& aIFrame)
+	{
+	iIncomingIFrameQ.HandleIncomingIFrameL(aIFrame, *this);
+	}
+
+inline void CL2CapErtmDataReceiver::PassToSduQL(RMBufChain& aIFrame) const
+	{
+	iController.PassToSduQL(aIFrame);
+	}
+
+inline TBool CL2CapErtmDataReceiver::IsIncomingSduQFull() const
+	{
+	return iIncomingSduQFull;
+	}
+
+inline TBool CL2CapErtmDataReceiver::LocalBusy() const
+	{
+	return iLocalBusy;
+	}
+
+inline void CL2CapErtmDataReceiver::FlushBufferedIncomingIFrames()
+	{
+	__ASSERT_DEBUG(iReceiveState == &iStateRecv,
+				   Panic(EL2CAPFlushingIncomingIFramesInStateOtherThanRecv));
+	iIncomingIFrameQ.DeleteAllFrames();
+	iExpectedTxSeq = iIncomingIFrameQ.TxSeqExpectedBySduQ();
+	}
+
+inline TBool CL2CapErtmDataReceiver::IsWaitFStatePending() const
+	{
+	return iWaitFStatePending;
+	}
+
+inline void CL2CapErtmDataReceiver::EnterWaitFState()
+	{
+	__ASSERT_DEBUG(!iController.IsPollOutstanding(),
+				   Panic(EL2CAPAttemptToEnterWaitFWhenAlreadyInAWaitState));
+
+	iWaitFStatePending = EFalse;
+	iInWaitFState = ETrue;
+	}
+
+inline TBool CL2CapErtmDataReceiver::InWaitFState() const
+	{
+	return iInWaitFState;
+	}
+
+inline TBool CL2CapErtmDataReceiver::SendAck() const
+	{
+	return iSendAck;
+	}
+
+inline RL2CapErtmTimerManager& CL2CapErtmDataReceiver::TimerMan()
+	{
+	return iController.TimerMan();
+	}
+
+inline RL2CapErtmOutgoingQueue& CL2CapErtmDataReceiver::OutgoingQ()
+	{
+	return iController.OutgoingQ();
+	}
+
+inline TUint8 CL2CapErtmDataReceiver::ExpectedTxSeq() const
+	{
+	return iExpectedTxSeq;
+	}
+
+inline void CL2CapErtmDataReceiver::SetExpectedTxSeq(TUint8 aTxSeq)
+	{
+	iExpectedTxSeq = aTxSeq;
+	}
+
+inline void CL2CapErtmDataReceiver::IncExpectedTxSeq()
+	{
+	iExpectedTxSeq = L2CapDataUtils::Mod64(iExpectedTxSeq + 1);
+	}
+
+inline TUint8 CL2CapErtmDataReceiver::BufferSeq() const
+	{
+	return iBufferSeq;
+	}
+
+inline void CL2CapErtmDataReceiver::SetBufferSeq(TUint8 aTxSeq)
+	{
+	iBufferSeq = aTxSeq;
+	}
+
+inline TUint8 CL2CapErtmDataReceiver::LastAckReqSeq() const
+	{
+	return iLastAckReqSeq;
+	}
+
+inline TUint8 CL2CapErtmDataReceiver::TxSeqExpectedBySduQ() const
+	{
+	return iIncomingIFrameQ.TxSeqExpectedBySduQ();
+	}
+
+inline void CL2CapErtmDataReceiver::SetStateRecvL()
+	{
+	SetStateL(iStateRecv, NULL);
+	}
+
+inline void CL2CapErtmDataReceiver::SetStateRejSentL()
+	{
+	SetStateL(iStateRejSent, NULL);
+	}
+
+inline void CL2CapErtmDataReceiver::SetStateSRejSentL(RMBufChain& aUnexpectedIFrame)
+	{
+	SetStateL(iStateSRejSent, &aUnexpectedIFrame);
+	}
+
+inline CL2CapEnhancedReTxController& CL2CapErtmDataReceiver::Controller() const
+	{
+	return iController;
+	}
+
+inline TBool CL2CapErtmDataReceiver::IsTxSeqUnexpected(TUint8 aTxSeq)
+	{
+	// With-unexpected-TxSeq: The TxSeq of the received I-frame is within the
+	// TxWindow of the L2CAP entity receiving the I-frame but has a TxSeq "greater"
+	// than ExpectedTxSeq where "greater" means later in sequence than ExpectedTxSeq.
+	return // in sender's window
+		   L2CapDataUtils::InWindow(aTxSeq, iBufferSeq, L2CapDataUtils::Mod64(iBufferSeq + iController.Config().PeerTXWindowSize() - 1)) &&
+		   // ... and "greater" than the expected one
+		   !L2CapDataUtils::InWindow(aTxSeq, iBufferSeq, iExpectedTxSeq);
+	}
+
+inline TBool CL2CapErtmDataReceiver::IsTxSeqInvalid(TUint8 aTxSeq)
+	{
+	// With-Invalid-TxSeq: The TxSeq of the received I-frame is not within the
+	// TxWindow of the L2CAP entity receiving the frame.
+	return !L2CapDataUtils::InWindow(aTxSeq, iBufferSeq, L2CapDataUtils::Mod64(iBufferSeq + iController.Config().PeerTXWindowSize() - 1));
+	}
+
+
+inline TBool RL2CapErtmOutgoingQueue::HaveFramesToTransmit() const
+	{
+	return !iOutgoingQ.IsEmpty();
+	}
+
+inline TBool RL2CapErtmOutgoingQueue::HavePendingRetransmitIFrames() const
+	{
+	return !iPendRetransmitIFrameQ.IsEmpty();
+	}
+
+
+template<typename FrameType>
+inline void CL2CapEnhancedReTxController::StampWithReqSeq(FrameType& aFrame)
+	{
+	iReceiver->StampWithReqSeq(aFrame);
+	}
+
+inline RL2CapErtmOutgoingQueue& CL2CapEnhancedReTxController::OutgoingQ()
+	{
+	return iOutgoingQ;
+	}
+
+inline RL2CapErtmTimerManager& CL2CapEnhancedReTxController::TimerMan()
+	{
+	return iTimerMan;
+	}
+
+inline TBool CL2CapEnhancedReTxController::IsOutgoingDataPathClosing()
+	{
+	return iDeliverOutgoingDataAndSignalToSduQWhenDone;
+	}
+
+inline void CL2CapEnhancedReTxController::SignalOutgoingDataDeliveredToSduQ()
+	{
+	iSDUQueue.DataControllerDeliveredOutgoingData();
+	}
+
+inline void CL2CapEnhancedReTxController::PassToSduQL(RMBufChain& aIFrame)
+	{
+	User::LeaveIfError(iSDUQueue.PutIFramePDU(aIFrame));
+	}
+
+inline TBool CL2CapEnhancedReTxController::IsPollOutstanding() const
+	{
+	return iTransmitter->InWaitAckState() || iReceiver->InWaitFState();
+	}
+
+inline TBool CL2CapEnhancedReTxController::IsFBitValid(TBool aFinal)
+	{
+	// With-Valid-F-bit: The F-bit of a received frame is valid if it is 0 or if it is 1 and
+	// a frame sent with P=1 by the local L2CAP entity is unanswered (i.e. the local
+	// L2CAP entity send a frame with P=1 and has not yet received a frame with F=1
+	// until receiving this one). If the Transmitter state machine is in the WAIT_ACK or
+	// WAIT_F states then a frame sent with P=1 is unanswered.
+	return (aFinal && IsPollOutstanding()) || !aFinal; 
+	}
+
+#endif /*L2CAPENHANCEDDATACONTROLLER_INL*/