diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/l2cap/L2CapDataController.cpp --- /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 + +#include + +#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 pendingIter(iPendingSentPDUs); + TDblQueIter 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 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;iMonitorTimeout(); + } + +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(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 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(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 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(aTimerMan); + timerMan->HandleSendPeerAckTimerExpired(); + return EFalse; + } + +/*static*/ TInt RL2CapRetransmissionModeTimerManager::FECTimerExpired(TAny* aTimeMan) + { + LOG_STATIC_FUNC + RL2CapRetransmissionModeTimerManager* timeMan = reinterpret_cast(aTimeMan); + timeMan->HandleFECTimerExpired(); + return EFalse; + }