--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/l2cap/L2CapDataController.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,1338 @@
+// 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 <e32base.h>
+
+#include "L2CapDataController.h"
+#include "L2CapEnhancedDataController.h"
+
+#include "L2CapSDUQueue.h"
+#include "l2signalmgr.h"
+
+#include "btsockettimer.h"
+
+#include "l2util.h"
+#include "L2CapDebugControlInterface.h"
+
+#include "l2constants.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_L2CAP_DATA_CONTROLLER);
+#endif
+
+using namespace L2CapDataUtils;
+
+// ***** CL2CapBasicDataController Implementation
+/*static*/ CL2CapBasicDataController* CL2CapBasicDataController::NewL(TL2CAPPort aLocalCID, TL2CAPPort aRemoteCID, CL2CAPMux& aMuxer, CL2CapSDUQueue& aSDUQueue, TL2CapDataControllerConfig* aConfig)
+ {
+ LOG_STATIC_FUNC
+ // Create the required data controller.
+ CL2CapBasicDataController* controller = NULL;
+
+ switch(aConfig->LinkMode())
+ {
+ case EL2CAPBasicMode:
+ LOG(_L("FEC L2CapBasicDataController Basic Mode"))
+ controller = new(ELeave) CL2CapBasicDataController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig, ETrue);
+ break;
+
+ case EL2CAPRetransmissionMode:
+ LOG(_L("FEC L2CapBasicDataController Retransmission Mode"))
+ controller = new(ELeave) CL2CapDataReTxController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig);
+ break;
+
+ case EL2CAPFlowControlMode:
+ LOG(_L("FEC L2CapBasicDataController Flow Control Mode"))
+ controller = new(ELeave) CL2CapDataFlowController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig);
+ break;
+
+ case EL2CAPStreamingMode:
+ LOG(_L("FEC L2CapBasicDataController Streaming Mode"))
+ controller = new(ELeave) CL2CapStreamingController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig);
+ break;
+
+ case EL2CAPEnhancedRetransmissionMode:
+ LOG(_L("FEC L2CapBasicDataController Enhnaced Retransmission Mode"))
+ controller = CL2CapEnhancedReTxController::NewL(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig);
+ break;
+
+ default:
+ Panic(EL2CAPInvalidLinkMode);
+ break;
+ };
+
+ return controller;
+ }
+
+CL2CapBasicDataController::CL2CapBasicDataController(TL2CAPPort aLocalCID, TL2CAPPort aRemoteCID, CL2CAPMux& aMuxer, CL2CapSDUQueue& aSDUQueue, TL2CapDataControllerConfig* aConfig, TBool aIsBasicDataVersion)
+ : iSDUQueue(aSDUQueue),
+ iMuxer(aMuxer),
+ iLocalCID(aLocalCID),
+ iRemoteCID(aRemoteCID),
+ iConfig(aConfig),
+ iIsBasicDataVersion(aIsBasicDataVersion)
+ {
+ LOG_FUNC
+ // Register with the Mux.
+ iMuxer.RegisterDataPDUHandler(*this);
+
+ L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EDataPlanes,
+ L2capDebugInfo::EAllocated));
+ }
+
+CL2CapBasicDataController::~CL2CapBasicDataController()
+ {
+ LOG_FUNC
+ delete iConfig;
+
+ L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EDataPlanes,
+ L2capDebugInfo::EDeleted));
+ }
+
+HL2CapPDU* CL2CapBasicDataController::GetPdu()
+ {
+ LOG_FUNC
+ HL2CapPDU* pdu = NULL;
+ TRAPD(err, pdu = GetPduL());
+ if (err != KErrNone)
+ {
+ // The protocol here is that all leaves propagated up to here cause
+ // us to ErrorD() ourselves, which will destruct data plane objects
+ // (the data controller, SDU queue and the whole shebang) and error
+ // the socket with the leave error code.
+ ErrorD(err);
+ }
+ return pdu;
+ }
+
+HL2CapPDU* CL2CapBasicDataController::GetPduL()
+ {
+ LOG_FUNC
+ HL2CapPDU* pduToSend = NULL;
+ if((pduToSend = iSDUQueue.GetPDU()) != NULL)
+ {
+ pduToSend->SetPDUCID(iRemoteCID);
+ }
+ return pduToSend;
+ }
+
+void CL2CapBasicDataController::ProcessFlushTimerExpiry()
+ {
+ LOG_FUNC
+ iMuxer.ProcessFlushTimerExpiry();
+ }
+
+void CL2CapBasicDataController::OutgoingPduAvailableOnSduQ()
+ {
+ LOG_FUNC
+ iMuxer.PDUAvailable();
+ }
+
+void CL2CapBasicDataController::UpdateConfig(TL2CapDataControllerConfig* aConfig)
+ {
+ LOG_FUNC
+ TBool channelPriorityUpdated = (iConfig->ChannelPriority() != aConfig->ChannelPriority());
+ // Delete the existing config, and replace it with the new one.
+ delete iConfig;
+ iConfig = aConfig;
+
+ if(channelPriorityUpdated)
+ {
+ iMuxer.ChannelPriorityUpdated(*this);
+ }
+ }
+
+void CL2CapBasicDataController::UpdateChannelPriority(TUint8 aNewPriority)
+ {
+ LOG_FUNC
+ if(iConfig->ChannelPriority() != aNewPriority)
+ {
+ iConfig->SetChannelPriority(aNewPriority);
+ iMuxer.ChannelPriorityUpdated(*this);
+ }
+ }
+
+
+void CL2CapBasicDataController::ErrorD(TInt aError)
+ {
+ LOG_FUNC
+ LOG1(_L("FEC CL2CapBasicDataController::Error %d"), aError)
+ iDataPlaneErrored = ETrue;
+ iSDUQueue.ErrorD(aError);
+ }
+
+void CL2CapBasicDataController::SetIncomingSduQFull(TBool /*aIncomingSduQFull*/)
+ {
+ LOG_FUNC
+ // No action can be taken in basic mode.
+ }
+
+TBool CL2CapBasicDataController::DeliverOutgoingDataAndSignalToSduQWhenDone()
+ {
+ LOG_FUNC
+ // We can be deleted right away.
+ return ETrue;
+ }
+
+void CL2CapBasicDataController::DeregisterFromMuxer()
+ {
+ LOG_FUNC
+ iMuxer.DataChannelRemoved(this);
+ }
+
+#ifdef _DEBUG
+TInt CL2CapBasicDataController::GetDataPlaneConfig(TL2DataPlaneConfig& conf) const
+ {
+ LOG_FUNC
+ TInt rerr = KErrNone;
+ // Populate the debug structure.
+ if(iConfig)
+ {
+ conf.iPriority = iConfig->ChannelPriority();
+ conf.iLinkMode = iConfig->LinkMode();
+ conf.iTxWindowSize = iConfig->TXWindowSize();
+ conf.iMaxTransmit = iConfig->MaxTransmit();
+ conf.iRetransmissionTimeout = iConfig->RetransmissionTimeout();
+ conf.iMonitorTimeout = iConfig->MonitorTimeout();
+ }
+ else
+ {
+ rerr = KErrNotReady;
+ }
+ return rerr;
+ }
+#endif
+
+TBool CL2CapBasicDataController::HandleIncomingDataFrame(RMBufChain& aDataFrame)
+ {
+ LOG_FUNC
+ TBool packetDelivered = EFalse;
+ TRAPD(err, packetDelivered = HandleIncomingDataFrameL(aDataFrame));
+ if (err != KErrNone)
+ {
+ // The protocol here is that all leaves propagated up to here cause
+ // us to ErrorD() ourselves, which will destruct data plane objects
+ // (the data controller, SDU queue and the whole shebang) and error
+ // the socket with the leave error code.
+ ErrorD(err);
+
+ if (aDataFrame.IsEmpty())
+ {
+ packetDelivered = ETrue;
+ }
+ // else frame will be offered to other data controllers
+ }
+ return packetDelivered;
+ }
+
+void CL2CapBasicDataController::HandleIncomingIFrameL(RMBufChain& /*aDataFrame*/)
+ {
+ LOG_FUNC
+ Panic(EL2CAPIFrameOrSFrameHandledByBasicController);
+ }
+
+void CL2CapBasicDataController::HandleIncomingBFrameL(RMBufChain& aDataFrame)
+ {
+ LOG_FUNC
+ iSDUQueue.PutBFramePDU(aDataFrame);
+ }
+
+void CL2CapBasicDataController::HandleIncomingSFrameL(RMBufChain& /*aDataFrame*/)
+ {
+ LOG_FUNC
+ Panic(EL2CAPIFrameOrSFrameHandledByBasicController);
+ }
+
+TBool CL2CapBasicDataController::HandleIncomingDataFrameL(RMBufChain& aDataFrame)
+ {
+ LOG_FUNC
+ TBool PDUProcessed = EFalse;
+ TInt err = KErrNone;
+
+ if(HL2CapPDU::PDUCID(aDataFrame) == iLocalCID)
+ {
+ if(iIsBasicDataVersion)
+ {
+ // This can only be a B-Frame.
+ if(HBFramePDU::CheckDecode(aDataFrame) == KErrNone)
+ {
+ HandleIncomingBFrameL(aDataFrame);
+ }
+ else
+ {
+ // Invalid frame. Drop it.
+ aDataFrame.Free();
+ LOG(_L("FEC L2CapBasicDataController B-Frame Dropped"))
+ }
+ }
+ else
+ {
+ if(HIFramePDU::IFrameIdentifier(aDataFrame))
+ {
+ // This is an I-Frame
+
+ // Check stuff that causes us to drop frames first:
+ // - FCS (2.1 Core Spec Addendum 1: 3.3.7 Invalid Frame Detection Algorithm pt. 2)
+ // - length field matching the actual payload length (funnily 3.3.7 doesn't say
+ // what to do with it, pt. 7.2.2 Recombination Of L2CAP PDUs mentions it in
+ // passing).
+ // Note: the frame is dropped when length is incorrect (instead of disconnecting)
+ // because we don't know where FCS is (it's at the end of the packet and we don't
+ // know where that is), so have to assume that the length field problem is due to
+ // transmission corruption and not the remote misbehaving.
+ if (HIFramePDU::CheckPayloadDecode(aDataFrame) != KErrNone ||
+ HL2CapPDU::CheckDecode(aDataFrame) != KErrNone)
+ {
+ aDataFrame.Free();
+ LOG(_L("FEC L2CapBasicDataController I-Frame Dropped"))
+ }
+ else
+ {
+ // Now check stuff that causes us to disconnect (per CSA1 3.3.7).
+ err = HIFramePDU::CheckLengthWithinLimits(aDataFrame, iConfig->IncomingMps());
+ if (err != KErrNone)
+ {
+ LOG(_L("FEC L2CapBasicDataController I-Frame invalid"))
+
+ aDataFrame.Free();
+ // This will cause the channel to be closed.
+ LEAVEL(err);
+ }
+ else
+ {
+ HandleIncomingIFrameL(aDataFrame);
+ }
+ }
+ }
+ else
+ {
+ // S-Frame
+
+ // Check stuff that causes the frame to be dropped first.
+ if (HSFramePDU::CheckPayloadDecode(aDataFrame) != KErrNone ||
+ HL2CapPDU::CheckDecode(aDataFrame) != KErrNone)
+ {
+ LOG(_L("FEC L2CapBasicDataController S-Frame Dropped"))
+ }
+ else
+ {
+ // Now check stuff that causes us to disconnect (per CSA1 3.3.7).
+ err = HSFramePDU::CheckLengthField(aDataFrame);
+ if (err != KErrNone)
+ {
+ LOG(_L("FEC L2CapBasicDataController S-Frame Invalid"))
+
+ aDataFrame.Free();
+ // This will cause the channel to be closed.
+ LEAVEL(err);
+ }
+ else
+ {
+ HandleIncomingSFrameL(aDataFrame);
+ }
+ }
+ // Finished with the S-Frame.
+ aDataFrame.Free();
+ }
+ }
+ PDUProcessed = ETrue;
+ }
+ return PDUProcessed;
+ }
+
+// Outgoing PDU handler.
+TInt CL2CapBasicDataController::HandleOutgoingIFrame(HIFramePDU* /*aIFrame*/)
+ {
+ LOG_FUNC
+ return KErrNone;
+ }
+
+TInt CL2CapBasicDataController::HandleOutgoingBFrame(HBFramePDU* /*aBFrame*/)
+ {
+ LOG_FUNC
+ return KErrNone;
+ }
+
+TInt CL2CapBasicDataController::HandleOutgoingGFrame(HGFramePDU* /*aGFrame*/)
+ {
+ LOG_FUNC
+ return KErrNone;
+ }
+
+TInt CL2CapBasicDataController::HandleOutgoingCFrame(HCFramePDU* /*aCFrame*/)
+ {
+ LOG_FUNC
+ return KErrNone;
+ }
+
+TInt CL2CapBasicDataController::HandleOutgoingSFrame(HSFramePDU* /*aSFrame*/)
+ {
+ LOG_FUNC
+ return KErrNone;
+ }
+
+// ***** CL2CapDataFlowController Implementation
+CL2CapDataFlowController::CL2CapDataFlowController(TL2CAPPort aLocalCID, TL2CAPPort aRemoteCID, CL2CAPMux& aMuxer, CL2CapSDUQueue& aSDUQueue, TL2CapDataControllerConfig* aConfig)
+ : CL2CapBasicDataController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig, EFalse),
+ iPendingSentPDUs(_FOFF(HIFramePDU, iLink)),
+ iSentPDUs(_FOFF(HIFramePDU, iLink)),
+ iTimerMan(*this),
+ iIncomingSduQFull(EFalse)
+ {
+ LOG_FUNC
+ }
+
+CL2CapDataFlowController::~CL2CapDataFlowController()
+ {
+ LOG_FUNC
+ // Some PDUs may hold a reference to this data controller.
+ TDblQueIter<HIFramePDU> pendingIter(iPendingSentPDUs);
+ TDblQueIter<HIFramePDU> sentIter(iSentPDUs);
+ HIFramePDU* pduPtr;
+
+ while((pduPtr = pendingIter++) != NULL)
+ {
+ LOG1(_L("Deregistering TxSeq = %d"), pduPtr->TxSeqNumber());
+ pduPtr->DeregisterPduOwner();
+ }
+
+ // Delete any PDUs that have already been sent.
+ while((pduPtr = sentIter++) != NULL)
+ {
+ delete pduPtr;
+ }
+
+ // Cancel any outstanding timers.
+ iTimerMan.Close();
+ }
+
+
+void CL2CapDataFlowController::ProcessFlushTimerExpiry()
+ {
+ LOG_FUNC
+ iMuxer.ProcessFlushTimerExpiry();
+ PDUAvailable();
+
+ if(!iSentPDUs.IsEmpty())
+ {
+ iNextTxSeq = Mod64(iSentPDUs.Last()->TxSeqNumber() + 1);
+ }
+ else
+ {
+ iNextTxSeq = iExpectedAckSeq;
+ }
+ }
+
+void CL2CapDataFlowController::HandleIncomingIFrameL(RMBufChain& aDataFrame)
+ {
+ LOG_FUNC
+ // Check if flow control is active for incoming data.
+ if(iIncomingSduQFull)
+ {
+ // Check that the PDU sequence number is within the expected range.
+ if(!iSenderTxWindowClosed && Mod64(HIFramePDU::TxSeqNumber(aDataFrame) - iLastAckSentRxSeqNum) >= iConfig->PeerTXWindowSize())
+ {
+ // The number is not in range. No more I-Frames will be processed
+ // until flow control is deactivated.
+ iSenderTxWindowClosed = ETrue;
+ }
+ }
+
+ LOG(_L("FEC CL2CapDataFlowController: HandleIncomingIFrame"))
+ LOG1(_L("\tTxSeqNumber = %d"), HIFramePDU::TxSeqNumber(aDataFrame))
+ LOG1(_L("\tLastAckSentRxSeqNum = %d"), iLastAckSentRxSeqNum)
+
+ if(!iSenderTxWindowClosed)
+ {
+ ProcessIFrameL(aDataFrame);
+ }
+ else
+ {
+ // Drop the frame.
+ aDataFrame.Free();
+ LOG(_L("FEC CL2CapDataFlowController: Incoming PDU seq number of I-Frame is not in range!"))
+ }
+ }
+
+void CL2CapDataFlowController::ProcessIFrameL(RMBufChain& aDataFrame)
+ {
+ LOG_FUNC
+ iExpectedTxSeq = Mod64(iExpectedTxSeq + 1);
+ iExpectedAckSeq = HIFramePDU::ReqSeqNumber(aDataFrame);
+
+ LEAVEIFERRORL(iSDUQueue.PutIFramePDU(aDataFrame));
+
+ // Check if the acknowledged frame(s) has created
+ // space in the window.
+ RemoveAckedPDUsFromSentQueue();
+
+ // Only send an ack if incoming data is not currently flow controlled off.
+ if(!iIncomingSduQFull)
+ {
+ // Check if the peer frames need to be acknowledged now.
+ // Check the peer TxWindow. This check is
+ // (Number of I-Frames recv since last Ack was sent > (TxWindow - Constant)
+ if(Mod64(iExpectedTxSeq - iLastAckSentRxSeqNum) > (iConfig->PeerTXWindowSize() - KTxWinAckThresholdOffset))
+ {
+ // Send an ack.
+ iSendAckToPeer = ETrue;
+ }
+ else
+ {
+ // If the timer is not running, start it.
+ if(!iTimerMan.IsSendPeerAckTimerRunning())
+ {
+ if(!iTimerMan.StartSendPeerAckTimer())
+ {
+ // A timer could not be started. Send an ack immediately.
+ iSendAckToPeer = ETrue;
+ }
+ }
+ }
+ }
+ // Check if any outbound data can be sent.
+ PDUAvailable();
+ }
+
+void CL2CapDataFlowController::HandleIncomingSFrameL(RMBufChain& aDataFrame)
+ {
+ LOG_FUNC
+ // Check that the function is RR. If not this S-Frame is not valid for
+ // flow control mode.
+ if(HSFramePDU::SupervisoryFunction(aDataFrame) == EReceiverReady)
+ {
+ iExpectedAckSeq = HSFramePDU::ReqSeqNumber(aDataFrame);
+ // Check if the acknowledged frame(s) has created
+ // space in the window.
+ RemoveAckedPDUsFromSentQueue();
+ PDUAvailable();
+ LOG(_L("FEC CL2CapDataFlowController: Incoming S-Frame: RR"))
+ LOG1(_L("\tiLastAckedFrameNum = %d"), iExpectedAckSeq)
+ }
+ }
+
+TInt CL2CapDataFlowController::HandleOutgoingIFrame(HIFramePDU* aIFrame)
+ {
+ LOG_FUNC
+ aIFrame->SetPDUCID(iRemoteCID);
+ aIFrame->SetTxSeqNumber(iNextTxSeq);
+ LOG(_L("FEC CL2CapDataFlowController: HandleOutgoingIFrame"))
+ LOG1(_L("\tTxSeqNumber = %d"), iNextTxSeq)
+ iNextTxSeq = Mod64(iNextTxSeq + 1);
+
+ // If flow controlled off - don't acknowledge any new I-Frames
+ aIFrame->SetReqSeqNumber(iIncomingSduQFull ? iLastAckSentRxSeqNum : iExpectedTxSeq);
+
+ // Set the retransmission disable bit if flow control is on.
+ aIFrame->SetRetransmitDisable(iIncomingSduQFull);
+ aIFrame->CalculateAndSetFCS();
+
+ aIFrame->iLink.Deque(); // just in case, it shouldn't be on any list at this point
+ aIFrame->SetPduOwner(this);
+ iPendingSentPDUs.AddLast(*aIFrame);
+
+ // Cancel the peer ack timer.
+ iTimerMan.StopSendPeerAckTimer();
+ iSendAckToPeer = EFalse;
+
+ // Store the last acknowledged frame index.
+ if(!iIncomingSduQFull)
+ {
+ iLastAckSentRxSeqNum = iExpectedTxSeq;
+ }
+ LOG1(_L("\tLastAckSentRxSeqNum = %d"), iLastAckSentRxSeqNum)
+
+ return KErrNone;
+ }
+
+TInt CL2CapDataFlowController::HandleOutgoingSFrame(HSFramePDU* aSFrame)
+ {
+ LOG_FUNC
+ aSFrame->SetPDUCID(iRemoteCID);
+ // If flow controlled off - don't acknowledge any new I-Frames
+ aSFrame->SetReqSeqNumber(iIncomingSduQFull ? iLastAckSentRxSeqNum : iExpectedTxSeq);
+ LOG(_L("FEC CL2CapDataFlowController: HandleOutgoingSFrame"))
+ LOG1(_L("\tReqSeqNUmber = %d"), iIncomingSduQFull ? iLastAckSentRxSeqNum : iExpectedTxSeq)
+ // Set the retransmission disable bit if flow control is on.
+ aSFrame->SetRetransmitDisable(iIncomingSduQFull);
+ aSFrame->CalculateAndSetFCS();
+
+ // If the monitor timer is currently running. Re-start it.
+ if(!iTimerMan.IsAckTimerRunning())
+ {
+ iTimerMan.StartMonitorTimer();
+ }
+
+ // Store the last acknowledged frame index.
+ if(!iIncomingSduQFull)
+ {
+ iLastAckSentRxSeqNum = iExpectedTxSeq;
+ }
+
+ // Cancel the peer ack timer.
+ iTimerMan.StopSendPeerAckTimer();
+ iSendAckToPeer = EFalse;
+ LOG1(_L("\tLastAckSentRxSeqNum = %d"), iLastAckSentRxSeqNum)
+ return KErrNone;
+ }
+
+TBool CL2CapDataFlowController::CanSendPDU()
+ {
+ LOG_FUNC
+ TBool canSend = ETrue;
+
+ // This condition is (V(S) - V(A))mod64 >= TxWindow
+ if(Mod64(iNextTxSeq - iExpectedAckSeq) >= iConfig->TXWindowSize())
+ {
+ canSend = EFalse;
+ }
+ return canSend;
+ }
+
+
+HL2CapPDU* CL2CapDataFlowController::GetPduL()
+ {
+ LOG_FUNC
+ // This is called from the signal manager.
+ HL2CapPDU* pduToSend = NULL;
+
+ if(CanSendPDU())
+ {
+ pduToSend = iSDUQueue.GetPDU();
+ }
+
+ if(pduToSend)
+ {
+ pduToSend->DeliverOutgoingPDU(*this);
+ }
+
+ // Note: If pduToSend has a valid pointer then the call to
+ // pduToSend->DeliverOutgoingPDU(*this) (above) will clear the
+ // iSendAckToPeer flag.
+ if(iSendAckToPeer)
+ {
+ pduToSend = HSFramePDU::New(EReceiverReady);
+ if(pduToSend)
+ {
+ pduToSend->DeliverOutgoingPDU(*this);
+ }
+ else
+ {
+ ErrorD(KErrNoMemory);
+ // We've been deleted!
+ }
+ }
+ return pduToSend;
+ }
+
+
+void CL2CapDataFlowController::PDUAvailable()
+ {
+ LOG_FUNC
+
+ // Note that CanSend only checks whether there's available outgoing window space,
+ // not whether there really is some outstanding data on SDU Q. GetPduL will check
+ // that and do nothing if there isn't.
+ if(CanSendPDU() || iSendAckToPeer)
+ {
+ iMuxer.PDUAvailable();
+ }
+ }
+
+void CL2CapDataFlowController::RemoveAckedPDUsFromSentQueue()
+ {
+ LOG_FUNC
+ TDblQueIter<HIFramePDU> iter(iSentPDUs);
+ HIFramePDU* pduPtr;
+ TInt ackedPDUs = 0;
+ TInt count = 0;
+
+ TUint8 frameSeqNumber = Mod64(iExpectedAckSeq - 1);
+
+ // Find out how many PDU's have been ack'd
+ while((pduPtr = iter++) != NULL)
+ {
+ if(pduPtr->TxSeqNumber() == frameSeqNumber)
+ {
+ // Increment the count by one and exit the loop.
+ ackedPDUs = count + 1;
+ break;
+ }
+ count++;
+ }
+
+ // Remove all ack'd PDUs
+ iter.SetToFirst();
+ for(TInt i=0;i<ackedPDUs;i++)
+ {
+ pduPtr = iter++;
+ delete pduPtr;
+ }
+
+ // Some PDUs have been ack'd.
+ if(ackedPDUs)
+ {
+ if(iSentPDUs.IsEmpty())
+ {
+ iTimerMan.StartMonitorTimer();
+ }
+ else
+ {
+ iTimerMan.StopMonitorTimer();
+ iTimerMan.StartAckTimer();
+ }
+ }
+ }
+
+void CL2CapDataFlowController::MonitorTimerExpired()
+ {
+ LOG_FUNC
+ // The monitor timer has expired. Send a S-Frame to the peer by setting
+ // the ack outstanding flag and calling PDUAvailable. This method will
+ // call this back at which point the S-Frame will be created.
+ LOG(_L("FEC CL2CapDataFlowController::MonitorTimerExpired"))
+ iSendAckToPeer = ETrue;
+ PDUAvailable();
+ }
+
+
+void CL2CapDataFlowController::AckTimerExpired()
+ {
+ LOG_FUNC
+ // Remove the PDU that the timer is supervising. This can
+ // only be the first Sent PDU.
+ LOG(_L("FEC CL2CapDataFlowController::AckTimerExpired"))
+ __ASSERT_DEBUG(!iSentPDUs.IsEmpty(), Panic(EL2CAPAckTimerExpiryWithoutPDUToSupervise));
+
+ HL2CapPDU* pdu = iSentPDUs.First();
+ delete pdu;
+
+ iExpectedAckSeq = Mod64(iExpectedAckSeq + 1);
+
+ if(iSentPDUs.IsEmpty())
+ {
+ iTimerMan.StartMonitorTimer();
+ }
+ else
+ {
+ iTimerMan.StopMonitorTimer();
+ iTimerMan.StartAckTimer();
+ }
+ PDUAvailable();
+ }
+
+void CL2CapDataFlowController::SendPeerAckTimerExpired()
+ {
+ LOG_FUNC
+
+ // Indicate that an ack should be sent to the peer to
+ // acknowledge receipt of I-Frames.
+ iSendAckToPeer = ETrue;
+ PDUAvailable();
+ }
+
+TUint16 CL2CapDataFlowController::MonitorTimeout()
+ {
+ return iConfig->MonitorTimeout();
+ }
+
+TUint16 CL2CapDataFlowController::RetransmissionTimeout()
+ {
+ return iConfig->RetransmissionTimeout();
+ }
+
+TUint16 CL2CapDataFlowController::PeerRetransmissionTimeout()
+ {
+ return iConfig->PeerRetransmissionTimeout();
+ }
+
+
+void CL2CapDataFlowController::HandlePduSendComplete(HL2CapPDU& aPdu)
+ {
+ LOG_FUNC
+
+ // We only claim ownership of I-Frames.
+ HIFramePDU& IFrame = static_cast<HIFramePDU&>(aPdu);
+
+ // Remove the PDU from the pending send queue.
+ IFrame.iLink.Deque();
+
+ // If there are outstanding (i.e., un-ack'ed) PDU's, the
+ // Ack timer will already be running. Otherwise it should
+ // be started.
+ if(iSentPDUs.IsEmpty())
+ {
+ iTimerMan.StartAckTimer();
+ }
+
+ // It's waiting for acknowledgement now.
+ iSentPDUs.AddLast(IFrame);
+ PDUAvailable();
+ }
+
+void CL2CapDataFlowController::HandlePduSendError(HL2CapPDU& /*aPdu*/)
+ {
+ LOG_FUNC
+ // In FC mode the frame will get deleted when we get an acknowledgement.
+ // In RTM, protocol-level retransmissions will be run when peer rejects or
+ // our ack timer expires.
+ }
+
+
+void CL2CapDataFlowController::SetIncomingSduQFull(TBool aIncomingSduQFull)
+ {
+ LOG_FUNC
+ // Check that the FC status has changed
+ if(iIncomingSduQFull != aIncomingSduQFull)
+ {
+ iIncomingSduQFull = aIncomingSduQFull;
+ iSenderTxWindowClosed = EFalse;
+
+ // Send an acknowledgement to the peer indicate the new
+ // flow control status.
+ iSendAckToPeer = ETrue;
+ PDUAvailable();
+ }
+ }
+
+
+// ***** CL2CapDataReTxController Implementation
+CL2CapDataReTxController::CL2CapDataReTxController(TL2CAPPort aLocalCID, TL2CAPPort aRemoteCID, CL2CAPMux& aMuxer, CL2CapSDUQueue& aSDUQueue, TL2CapDataControllerConfig* aConfig)
+ : CL2CapDataFlowController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig),
+ iRejectPDU(0),
+ iRetransmitSentPDUs(EFalse),
+ iRejectSent(EFalse),
+ iRetransmissionDisabled(EFalse),
+ iRestartAckTimer(EFalse),
+ iRetransTxVal(iNextTxSeq)
+ {
+ LOG_FUNC
+ }
+
+void CL2CapDataReTxController::HandleIncomingIFrameL(RMBufChain& aDataFrame)
+ {
+ LOG_FUNC
+ TBool newRtxDisable = HIFramePDU::RetransmitDisable(aDataFrame);
+ if(iExpectedTxSeq == HIFramePDU::TxSeqNumber(aDataFrame))
+ {
+ LOG(_L("FEC CL2CapDataReTxController: HandleIncomingIFrame"))
+ LOG1(_L("\tTxSeqNumber = %d"), HIFramePDU::TxSeqNumber(aDataFrame))
+ ProcessIFrameL(aDataFrame);
+ iRejectSent = EFalse;
+ }
+ else
+ {
+ // This I-Frame has an invalid sequence number.
+ // Check that a Reject Exception is not already being
+ // handled.
+ if(!iRejectSent)
+ {
+ iRejectPDU = HSFramePDU::NewL(EReject);
+ iRejectPDU->DeliverOutgoingPDU(*this);
+ // Send the Reject PDU.
+ iMuxer.PDUAvailable();
+ iRejectSent = ETrue;
+ }
+ aDataFrame.Free();
+ LOG(_L("FEC CL2CapDataReTxController: Incoming PDU seq number of I-Frame is not in range!"))
+ }
+
+ // Check if the re-transmission disable bit has transitioned.
+ if(iRetransmissionDisabled != newRtxDisable)
+ {
+ if(newRtxDisable)
+ {
+ // Re-transmission has been disabled.
+ iTimerMan.StartMonitorTimer();
+ }
+ else
+ {
+ // Re-transmission has been enabled.
+ if(!iSentPDUs.IsEmpty())
+ {
+ iTimerMan.StopMonitorTimer();
+ iTimerMan.StartAckTimer();
+ }
+ }
+ iRetransmissionDisabled = newRtxDisable;
+ }
+ }
+
+void CL2CapDataReTxController::HandleIncomingSFrameL(RMBufChain& aDataFrame)
+ {
+ LOG_FUNC
+ if(HSFramePDU::SupervisoryFunction(aDataFrame) == EReject)
+ {
+ iRetransmitSentPDUs = ETrue;
+ iRetransTxVal = HSFramePDU::ReqSeqNumber(aDataFrame);
+ LOG(_L("FEC CL2CapDataReTxController: Incoming S-Frame: Reject"))
+ }
+ iExpectedAckSeq = HSFramePDU::ReqSeqNumber(aDataFrame);
+ LOG(_L("FEC CL2CapDataReTxController: Incoming S-Frame"))
+ LOG1(_L("\tLastAckedFrameNum = %d"), iExpectedAckSeq)
+
+ TBool newRtxDisable = HSFramePDU::RetransmitDisable(aDataFrame);
+
+ // Check if the acknowledged frame(s) has created
+ // space in the window.
+ RemoveAckedPDUsFromSentQueue();
+ PDUAvailable();
+
+ // Check if the re-transmission disable bit has transitioned.
+ if(iRetransmissionDisabled != newRtxDisable)
+ {
+ if(newRtxDisable)
+ {
+ // Re-transmission has been disabled.
+ iTimerMan.StartMonitorTimer();
+ }
+ else
+ {
+ // Re-transmission has been enabled.
+ if(!iSentPDUs.IsEmpty())
+ {
+ iTimerMan.StopMonitorTimer();
+ iTimerMan.StartAckTimer();
+ }
+ }
+ iRetransmissionDisabled = newRtxDisable;
+ }
+ }
+
+HL2CapPDU* CL2CapDataReTxController::GetPduL()
+ {
+ LOG_FUNC
+ // This is called from the signal manager.
+ HL2CapPDU* pduToSend = NULL;
+
+ // Check the re-transmission disabled flag. If its set
+ // then only S-Frames can be sent.
+ if(!iRetransmissionDisabled && !iDataPlaneErrored)
+ {
+ if(iRejectPDU)
+ {
+ pduToSend = iRejectPDU;
+ iRejectPDU = NULL;
+ }
+ else
+ {
+ if(iRetransmitSentPDUs)
+ {
+ pduToSend = RetransmitSentPDU();
+ }
+
+ if(!pduToSend && CanSendPDU())
+ {
+ pduToSend = iSDUQueue.GetPDU();
+ }
+ }
+ }
+
+ if(pduToSend)
+ {
+ pduToSend->DeliverOutgoingPDU(*this);
+ }
+
+ // Note: If pduToSend has a valid pointer then the call to
+ // pduToSend->DeliverOutgoingPDU(*this) (above) will clear the
+ // iSendAckToPeer flag.
+ if(iSendAckToPeer)
+ {
+ pduToSend = HSFramePDU::New(EReceiverReady);
+ if(pduToSend)
+ {
+ pduToSend->DeliverOutgoingPDU(*this);
+ }
+ else
+ {
+ ErrorD(KErrNoMemory);
+ // We've been deleted!
+ }
+ }
+ return pduToSend;
+ }
+
+void CL2CapDataReTxController::PDUAvailable()
+ {
+ LOG_FUNC
+
+ // Note that CanSend only checks whether there's available outgoing window space,
+ // not whether there really is some outstanding data on SDU Q. GetPduL will check
+ // that and do nothing if there isn't.
+ if(CanSendPDU() || iSendAckToPeer || iRetransmitSentPDUs)
+ {
+ iMuxer.PDUAvailable();
+ }
+ }
+
+void CL2CapDataReTxController::AckTimerExpired()
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(!iSentPDUs.IsEmpty(), Panic(EL2CAPAckTimerExpiryWithoutPDUToSupervise));
+
+ // Check if the PDU can be re-sent (i.e., the Max.
+ // transmit has not been exceeded.)
+
+ HIFramePDU* pdu = iSentPDUs.First();
+
+ if(pdu->CanTransmit(iConfig->MaxTransmit()))
+ {
+ // Set the next frame to be transmitted to the PDU
+ // whose timer expired.
+ iRetransTxVal = pdu->TxSeqNumber();
+ iRetransmitSentPDUs = ETrue;
+ iRestartAckTimer = ETrue;
+
+ // The frame will be re-transmitted when GetPDU is called.
+ // This will also re-start the Retransmission timer.
+ PDUAvailable();
+ }
+ else
+ {
+ // The channel can not support the requested quality.
+ // i.e., the number of retransmissions before the channel is
+ // disconnected has been exceeded.
+ // Error the SAP to disconnect the channel.
+ ErrorD(KErrL2CAPMaxTransmitExceeded);
+ LOG(_L("FEC CL2CapDataReTxController::AckTimerExpired - KErrL2CAPMaxTransmitExceeded"))
+ }
+ }
+
+HL2CapPDU* CL2CapDataReTxController::RetransmitSentPDU()
+ {
+ LOG_FUNC
+ HIFramePDU* pduToSend = NULL;
+ TDblQueIter<HIFramePDU> iter(iSentPDUs);
+ HIFramePDU* pduPtr;
+
+ while((pduPtr = iter++) != NULL)
+ {
+ if(pduPtr->TxSeqNumber() == iRetransTxVal)
+ {
+ pduToSend = pduPtr;
+ break;
+ }
+ }
+
+ if(!pduToSend)
+ {
+ iRetransmitSentPDUs = EFalse;
+ }
+ return pduToSend;
+ }
+
+void CL2CapDataReTxController::HandlePduSendComplete(HL2CapPDU& aPdu)
+ {
+ LOG_FUNC
+
+ // We only claim ownership of I-Frames.
+ HIFramePDU& IFrame = static_cast<HIFramePDU&>(aPdu);
+
+ // Remove the PDU from the pending send queue.
+ IFrame.iLink.Deque();
+
+ // If there are outstanding (i.e., un-ack'ed) PDU's, the
+ // Ack timer will already be running. Otherwise it should
+ // be started.
+ if(iSentPDUs.IsEmpty() || iRestartAckTimer)
+ {
+ iTimerMan.StopMonitorTimer();
+ iRestartAckTimer = EFalse;
+ iTimerMan.StartAckTimer();
+ }
+
+ iSentPDUs.AddLast(IFrame);
+
+ // When operating in lossy conditions such as when running
+ // concurrently with a name inquiry it is possible for an
+ // acknowledgement to arrive before the sent completion code is called
+ // for the relevant PDU. As a result aIFrame may have already been
+ // acknowledged. Now that we have ownership of the PDU we can
+ // remove it and cancel the Ack timer if necessary.
+
+ // N.B. Here we have assumed that the delay in the notification
+ // that a PDU has been sent (thereby moving it from the pending sent
+ // queue to the sent queue) is not so large that in the mean time we
+ // have sent so many new PDUs that the transmission window has wrapped
+ // so far around that an old I-Frame from the last cycle is
+ // considered an unacknowledged one because the Tx Seq value is once again
+ // in the valid transmission window. This would require BT to be much
+ // faster than it currently is (11/04/2007).
+ RemoveAckedPDUsFromSentQueue();
+ PDUAvailable();
+ }
+
+void CL2CapDataReTxController::RemoveAckedPDUsFromSentQueue()
+ {
+ LOG_FUNC
+ TDblQueIter<HIFramePDU> iter(iSentPDUs);
+ HIFramePDU* pduPtr;
+ TBool ackedPDUs = EFalse;
+
+ // Remove PDUs which have been previously acknowledged.
+ // The queue may be out of order due to retransmissions
+ // being appended at the end of the sent queue.
+ while((pduPtr = iter++) != NULL)
+ {
+ // If the PDU's Tx Seq number is not in the current valid transmission window it is considered a previously acknowledged I-Frame and is removed.
+ if (!(((pduPtr->TxSeqNumber()>= iExpectedAckSeq) && (pduPtr->TxSeqNumber()< (iExpectedAckSeq + iConfig->TXWindowSize()))) || (((iExpectedAckSeq + iConfig->TXWindowSize()) > KL2CapTxSeqValues) && (pduPtr->TxSeqNumber()< Mod64((iExpectedAckSeq + iConfig->TXWindowSize()))))))
+ {
+ ackedPDUs = ETrue;
+ delete pduPtr;
+ }
+ }
+
+ // Some PDUs have been ack'd.
+ if(ackedPDUs)
+ {
+ if(iSentPDUs.IsEmpty())
+ {
+ if (iDeliverOutgoingDataAndSignalToSduQWhenDone && iPendingSentPDUs.IsEmpty())
+ {
+ iSDUQueue.DataControllerDeliveredOutgoingData();
+ }
+ else
+ {
+ iTimerMan.StartMonitorTimer();
+ }
+ }
+ else
+ {
+ iTimerMan.StopMonitorTimer();
+ iTimerMan.StartAckTimer();
+ }
+ }
+ }
+
+TInt CL2CapDataReTxController::HandleOutgoingIFrame(HIFramePDU* aIFrame)
+ {
+ LOG_FUNC
+ aIFrame->SetPDUCID(iRemoteCID);
+
+ // When sending a retransmission iRetransmitSentPDUs = ETrue.
+ // When doing a retransmission the I-Frame has already had its Tx Seq
+ // value set and iRetransTxVal is incremented so that we will continue
+ // iterating through the sent PDUs for retransmission.
+ // When sending a new I-Frame iNextTxSeq is used to get the next
+ // brand new Tx Seq value (given the limitations of wrapping round the number
+ // space).
+ if (!iRetransmitSentPDUs)
+ {
+ aIFrame->SetTxSeqNumber(iNextTxSeq);
+ iNextTxSeq = Mod64(iNextTxSeq + 1);
+ }
+ else
+ {
+ ++iRetransTxVal;
+ }
+
+ LOG(_L("FEC CL2CapDataReTxController::HandleOutgoingIFrame"))
+ LOG1(_L("\tTxSeqNumber = %d"), (aIFrame->TxSeqNumber()))
+
+ // If flow controlled off - don't acknowledge any new I-Frames
+ aIFrame->SetReqSeqNumber(iIncomingSduQFull ? iLastAckSentRxSeqNum : iExpectedTxSeq);
+
+ // Set the retransmission disable bit if flow control is on.
+ aIFrame->SetRetransmitDisable(iIncomingSduQFull);
+ aIFrame->CalculateAndSetFCS();
+
+ aIFrame->iLink.Deque(); // may be on Sent PDU list if it's a retransmission
+ aIFrame->SetPduOwner(this);
+ iPendingSentPDUs.AddLast(*aIFrame);
+ if ((iSentPDUs.IsEmpty()) && iTimerMan.IsAckTimerRunning())
+ {
+ iRestartAckTimer = EFalse;
+ iTimerMan.StartMonitorTimer();
+ }
+
+ // Cancel the peer ack timer.
+ iTimerMan.StopSendPeerAckTimer();
+ iSendAckToPeer = EFalse;
+
+ // Store the last acknowledged frame index.
+ if(!iIncomingSduQFull)
+ {
+ iLastAckSentRxSeqNum = iExpectedTxSeq;
+ }
+ LOG1(_L("\tLastAckSentRxSeqNum = %d"), iLastAckSentRxSeqNum)
+
+ return KErrNone;
+ }
+
+TBool CL2CapDataReTxController::DeliverOutgoingDataAndSignalToSduQWhenDone()
+ {
+ LOG_FUNC
+ iDeliverOutgoingDataAndSignalToSduQWhenDone = ETrue;
+ // Returning true means we don't have any outstanding data to deliver and
+ // hence can be deleted immediately.
+ return iPendingSentPDUs.IsEmpty() && iSentPDUs.IsEmpty();
+ }
+
+
+RL2CapRetransmissionModeTimerManager::RL2CapRetransmissionModeTimerManager(MRetransmissionModeTimerClient& aClient)
+ : iClient(aClient)
+ {
+ LOG_FUNC
+ }
+
+void RL2CapRetransmissionModeTimerManager::Close()
+ {
+ LOG_FUNC
+ CancelFECTimer();
+ StopSendPeerAckTimer();
+ }
+
+void RL2CapRetransmissionModeTimerManager::StartMonitorTimer()
+ {
+ LOG_FUNC
+ // Monitor timer preempts the Ack timer, so we can stop whichever we have running.
+ CancelFECTimer();
+
+ TCallBack cb(FECTimerExpired, this);
+ iFECTimerEntry.Set(cb);
+
+ // Set the timeout. The value is in Milliseconds.
+ BTSocketTimer::Queue(iClient.MonitorTimeout()*1000, iFECTimerEntry);
+ iFECTimerState = EMonitorTimerRunning;
+ }
+
+void RL2CapRetransmissionModeTimerManager::StartAckTimer()
+ {
+ LOG_FUNC
+
+ if (iFECTimerState != EMonitorTimerRunning)
+ // Ack timer can't be run when the Monitor timer is running.
+ {
+ CancelFECTimer();
+
+ TCallBack cb(FECTimerExpired, this);
+ iFECTimerEntry.Set(cb);
+
+ // Set the timeout. The value is in Milliseconds.
+ BTSocketTimer::Queue(iClient.RetransmissionTimeout()*1000, iFECTimerEntry);
+ iFECTimerState = EAckTimerRunning;
+ }
+ }
+
+void RL2CapRetransmissionModeTimerManager::StopMonitorTimer()
+ {
+ LOG_FUNC
+ if (iFECTimerState == EMonitorTimerRunning)
+ {
+ CancelFECTimer();
+ }
+ }
+
+void RL2CapRetransmissionModeTimerManager::StopAckTimer()
+ {
+ LOG_FUNC
+ if (iFECTimerState == EAckTimerRunning)
+ {
+ CancelFECTimer();
+ }
+ }
+
+void RL2CapRetransmissionModeTimerManager::CancelFECTimer()
+ {
+ LOG_FUNC
+ // Cancel either the monitor or ack timer.
+ if(iFECTimerState != EFECTimerIdle)
+ {
+ // Cancel the current outgoing timer.
+ BTSocketTimer::Remove(iFECTimerEntry);
+ iFECTimerState = EFECTimerIdle;
+ }
+ }
+
+void RL2CapRetransmissionModeTimerManager::HandleFECTimerExpired()
+ {
+ LOG_FUNC
+ // Call the appropriate method to handle this expiry.
+ switch(iFECTimerState)
+ {
+ case EFECTimerIdle:
+ Panic(EL2CAPInvalidDataControllerTimerState);
+ break;
+
+ case EMonitorTimerRunning:
+ iFECTimerState = EFECTimerIdle;
+ iClient.MonitorTimerExpired();
+ break;
+
+ case EAckTimerRunning:
+ iFECTimerState = EFECTimerIdle;
+ iClient.AckTimerExpired();
+ break;
+
+ default:
+ Panic(EL2CAPInvalidDataControllerTimerState);
+ break;
+ }
+ }
+
+void RL2CapRetransmissionModeTimerManager::HandleSendPeerAckTimerExpired()
+ {
+ LOG_FUNC
+ __ASSERT_ALWAYS(iSendPeerAckTimerRunning, Panic(EL2CAPInvalidDataControllerTimerState));
+ iSendPeerAckTimerRunning = EFalse;
+ iClient.SendPeerAckTimerExpired();
+ }
+
+TBool RL2CapRetransmissionModeTimerManager::StartSendPeerAckTimer()
+ {
+ LOG_FUNC
+ // This timer should only be started if the peer is using a long re-transmission timer.
+ __ASSERT_ALWAYS(!iSendPeerAckTimerRunning, Panic(EL2CAPInvalidDataControllerTimerState));
+
+ TInt peerAckTimer = iClient.PeerRetransmissionTimeout() - KAveTimeToTransmitAckToPeer;
+ if(peerAckTimer > KMinimumPeerAckTimeout)
+ {
+ TCallBack cb(SendPeerAckTimerExpired, this);
+ iSendPeerAckTimerEntry.Set(cb);
+
+ // Set the timeout. The value os in Milliseconds.
+ BTSocketTimer::Queue(peerAckTimer*1000, iSendPeerAckTimerEntry);
+ iSendPeerAckTimerRunning = ETrue;
+ }
+ return iSendPeerAckTimerRunning;
+ }
+
+void RL2CapRetransmissionModeTimerManager::StopSendPeerAckTimer()
+ {
+ LOG_FUNC
+ if (iSendPeerAckTimerRunning)
+ {
+ BTSocketTimer::Remove(iSendPeerAckTimerEntry);
+ iSendPeerAckTimerRunning = EFalse;
+ }
+ }
+
+/*static*/ TInt RL2CapRetransmissionModeTimerManager::SendPeerAckTimerExpired(TAny* aTimerMan)
+ {
+ LOG_STATIC_FUNC
+ RL2CapRetransmissionModeTimerManager* timerMan = reinterpret_cast<RL2CapRetransmissionModeTimerManager*>(aTimerMan);
+ timerMan->HandleSendPeerAckTimerExpired();
+ return EFalse;
+ }
+
+/*static*/ TInt RL2CapRetransmissionModeTimerManager::FECTimerExpired(TAny* aTimeMan)
+ {
+ LOG_STATIC_FUNC
+ RL2CapRetransmissionModeTimerManager* timeMan = reinterpret_cast<RL2CapRetransmissionModeTimerManager*>(aTimeMan);
+ timeMan->HandleFECTimerExpired();
+ return EFalse;
+ }