--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/l2cap/l2signalmgr.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,1239 @@
+// Copyright (c) 1999-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:
+// Implements the l2cap Muxer.
+//
+//
+
+#include <bluetooth/logger.h>
+
+#include <math.h>
+
+#include <bluetooth/hci/aclpacketconsts.h>
+#include <bluetooth/aclsockaddr.h>
+
+#include "l2signalmgr.h"
+#include "L2CapPDU.h"
+#include "l2capLinkSignalHandler.h"
+#include "l2capSAPSignalHandler.h"
+#include "L2CapDataController.h"
+#include "l2capMuxController.h"
+#include "l2capEntityConfig.h"
+#include "l2cap.h"
+#include "l2util.h"
+#include "btconsts.h"
+#include "L2CapDebugControlInterface.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_L2CAP);
+#endif
+
+#ifdef _DEBUG
+PANICCATEGORY("l2cap");
+#endif
+
+CL2CAPMux* CL2CAPMux::NewL(CL2CAPMuxController& aMuxController, const TBTDevAddr& aAddr)
+ {
+ LOG_STATIC_FUNC
+ CL2CAPMux* sig=new (ELeave) CL2CAPMux(aMuxController, aAddr);
+ CleanupStack::PushL(sig);
+ sig->ConstructL();
+ CleanupStack::Pop();
+ return sig;
+ }
+
+CL2CAPMux::CL2CAPMux(CL2CAPMuxController& aMuxController, const TBTDevAddr& aAddr)
+ : iMuxController(aMuxController),
+ iLinkState(ELinkNone),
+ iRemote(aAddr),
+ iIdleTimerActive(EFalse),
+ iDataHandlers(_FOFF(CL2CapBasicDataController, iLink)),
+ iSapSignalHandlers(_FOFF(CL2CapSAPSignalHandler, iLink)),
+ iPDUsWaitingResendQ(_FOFF(HL2CapPDU, iLink)),
+ iPDUsWaitingSend(_FOFF(HL2CapPDU, iLink)),
+ iTryToSendPriority(CActive::EPriorityHigh),
+ iUpdateTryToSendPriority(EFalse),
+ iNextCID(KL2CapDynamicCIDStart),
+ iQlink(this)
+ {
+ LOG_FUNC
+ TCallBack cb(IdleTimerExpired, this);
+ iIdleTimerEntry.Set(cb);
+
+#ifdef _DEBUG
+ // Set up the next CID to be one off of the wrap point to
+ // check that functionality
+ iNextCID = 0xfffe;
+ L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EMuxes,
+ L2capDebugInfo::EAllocated));
+#endif
+ }
+
+
+void CL2CAPMux::ConstructL()
+ {
+ LOG_FUNC
+ iBoundLinkSignalHandler = CL2CapLinkSignalHandler::NewL(this);
+ iFragmentSender = new (ELeave) HFragmentedPDUSender(*this);
+ TCallBack canSendCallBack(AsyncCallBackForTryToSend, this);
+ iAsyncCallBackForTryToSend = new (ELeave)CAsyncCallBack(canSendCallBack, iTryToSendPriority);
+ TCallBack idleMuxCallBack(AsyncCallBackForIdleMux, this);
+ iAsyncCallBackForIdleMux = new (ELeave)CAsyncCallBack(idleMuxCallBack, CActive::EPriorityHigh);
+ }
+
+
+//Passive connections call this with a valid SAP
+//Active connections pass NULL into this and one gets created.
+void CL2CAPMux::CompleteACLConnect(CServProviderBase* aSAP)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(!iBoundSAP, Panic(EL2CAPLinkUpWhenNoLinkRequested));
+
+ if (!aSAP)
+ {
+ TRAPD(err, iBoundSAP = MuxController().Protocol().LowerProtocol()->NewSAPL(KSockBluetoothTypeACL));
+ if (err == KErrNone)
+ {
+ iBoundSAP->SetNotify(this);
+
+ const TBTDevAddr& addr = iRemote;
+ TBTSockAddr sockAddr;
+ sockAddr.SetBTAddr(addr);
+
+ TInt err = iBoundSAP->SetRemName(sockAddr);
+ __ASSERT_ALWAYS(!err, Panic(EL2CAPCouldNotSetBasebandAddress));
+
+ // tell the SAP what data we can handle
+ TACLSockAddr aclSock;
+ aclSock.SetPort(EACLPortL2CAP);
+ err = iBoundSAP->SetLocalName(aclSock);
+ iLinkState = ELinkPending;
+ iBoundSAP->ActiveOpen();
+ }
+ }
+ else
+ {
+ iBoundSAP = aSAP; // we take ownership of this connected SAP
+ iBoundSAP->SetNotify(this);
+ iBoundSAP->Start(); // and open the passive connection
+ ConnectComplete(); // gets BB MTU and configures this Mux
+ }
+
+ //Now the Link is up Subscribe for Physical Link Notifications
+ MuxController().Protocol().ControlPlane().SubscribePhysicalLink(*this, iRemote);
+ }
+
+
+CL2CAPMux::~CL2CAPMux()
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iDataHandlers.IsEmpty() && iSapSignalHandlers.IsEmpty(),
+ Panic(EL2CAPMuxDeleteWithResourcesStillRegistered));
+
+ iAsyncCallBackForTryToSend->Cancel();
+ delete iAsyncCallBackForTryToSend;
+
+ iAsyncCallBackForIdleMux->Cancel();
+ delete iAsyncCallBackForIdleMux;
+
+ MuxController().Protocol().ControlPlane().UnsubscribePhysicalLink(*this,iRemote);
+ delete iBoundSAP;
+
+ // Remove timer entry if one has been queued.
+ CancelIdle();
+
+ iFragmentSender->PDUSenderFailed();
+
+ // Some PDUs may hold a reference to this Mux.
+ TDblQueIter<HL2CapPDU> resendIter(iPDUsWaitingResendQ);
+ TDblQueIter<HL2CapPDU> sendIter(iPDUsWaitingSend);
+ HL2CapPDU* pduPtr;
+
+ while((pduPtr = resendIter++) != NULL)
+ {
+ pduPtr->DeregisterPduOwner();
+ }
+ while((pduPtr = sendIter++) != NULL)
+ {
+ pduPtr->DeregisterPduOwner();
+ }
+
+ iIncomingPDU.Free();
+ iMuxControllerLink.Deque();
+ delete iBoundLinkSignalHandler;
+ delete iFragmentSender;
+
+#ifdef _DEBUG
+ L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EMuxes,
+ L2capDebugInfo::EDeleted));
+#endif
+ }
+
+
+TInt CL2CAPMux::SetOption(TUint level,TUint name,const TDesC8 &anOption)
+ {
+ LOG_FUNC
+ return iBoundSAP->SetOption(level, name, anOption);
+ }
+
+
+TInt CL2CAPMux::GetOption(TUint level,TUint name,TDes8& anOption) const
+ {
+ LOG_FUNC
+ return iBoundSAP->GetOption(level, name, anOption);
+ }
+
+
+void CL2CAPMux::DetachFromMux(CL2CapSAPSignalHandler& aSAPSigHandler)
+ {
+ LOG_FUNC
+ // Check if this SSH had a Echo Request outstanding. If it did discard the
+ // response when it arrives.
+ iBoundLinkSignalHandler->DeregisterOutstandingEchoRequests(aSAPSigHandler);
+
+ if(Idle())
+ {
+ LOG(_L("CL2CAPMux: DetatchFromMux -- no current SAPs, queuing idle callback"));
+
+ // The Muxer will be destroyed and
+ // the ACL / baseband will be removed by CL2CAPMux::AsyncCallBackForIdleMux()...
+ iAsyncCallBackForIdleMux->CallBack();
+ }
+ else
+ {
+ LOG(_L("CL2CAPMux: DetatchFromMux -- still have SAPs present or are still sending data"));
+ }
+ }
+
+TInt CL2CAPMux::MuxIdled()
+ {
+ LOG_FUNC
+
+ // register we have no intention of sending out for now..
+ // if data comes in we may if necessary send data later, in which
+ // case we will reopen our link
+ if (iLinkState != ELinkNone)
+ {
+ LOG(_L("CL2CAPMux: MuxIdled -- iLinkState != ELinkNone"));
+
+ // Don't attempt to send anymore data.
+ iBoundLinkSignalHandler->FlushPendingCommands();
+
+ // Shutdown the ACL.
+ iBoundSAP->Shutdown(CServProviderBase::EStopOutput); // we might get overtures back in
+ iLinkState = ELinkInbound;
+ }
+ else
+ {
+ delete this;
+ }
+ return KErrNone;
+ }
+
+TUint8 CL2CAPMux::NextSigId()
+ {
+ LOG_FUNC
+ // 0 is invalid, so don't use it
+ ++iSigId;
+ if (iSigId == 0)
+ {
+ iSigId = 1;
+ }
+ LOG1(_L("L2CAP [l2signalmgr.cpp]: Next signalling Id is %d"), iSigId);
+ return iSigId;
+ }
+
+
+TBool CL2CAPMux::Idle()
+/**
+ Test to see if this L2CAP mux is no longer required.
+ If there are no SSHs connected to this muxer, shutdown the ACL
+**/
+ {
+ LOG_FUNC
+ return (iSapSignalHandlers.IsEmpty() &&
+ iLinkState != ELinkPending &&
+ !iFragmentSender->IsPDUBeingSent() &&
+ !iIdleTimerActive);
+ }
+
+void CL2CAPMux::StartIdleTimer()
+ {
+ LOG_FUNC
+ CancelIdle();
+ iIdleTimerActive = ETrue;
+ BTSocketTimer::Queue(KL2MuxerIdleTimeout * KL2ProtocolSecondTimerMultiplier, iIdleTimerEntry);
+ }
+
+
+TInt CL2CAPMux::AsyncCallBackForIdleMux(TAny *aMuxer)
+/**
+ Static function called through TCallBack. aMuxer holds the muxer
+ that is idle. Ask mux to bring down its own link.
+*/
+ {
+ LOG_STATIC_FUNC
+
+ CL2CAPMux* muxer = static_cast<CL2CAPMux*>(aMuxer);
+
+ if (muxer->Idle())
+ {
+ // We're (still) idle! Let's disconnect.
+ muxer->MuxIdled();
+ }
+ return EFalse; // no more callbacks thanks!
+ }
+
+
+TInt CL2CAPMux::AsyncCallBackForTryToSend(TAny *aMuxer)
+/**
+ Static function called whenever idle processing can be scheduled.
+ This deals with doing async requests to send data over HCI,
+ notifications to SSHs
+**/
+ {
+ LOG_STATIC_FUNC
+ CL2CAPMux* muxer = static_cast<CL2CAPMux*>(aMuxer);
+
+ muxer->TryToSend();
+ return EFalse; // Don't call back any more.
+ }
+
+// from MSocketNotify
+void CL2CAPMux::ConnectComplete()
+/**
+ The link for this muxer has come up.
+ SSHs are notified by the protocol (who notifies us too).
+ Set the Idle timer going here, so if no SAP attaches to this
+ Mux, it'll just tidy itself up.
+**/
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iLinkState != EConnected, Panic(EL2CAPLinkDownConnectComplete));
+
+ // Get the MTU
+ TPckgBuf<TInt> buf;
+
+ TInt err;
+
+ err = iBoundSAP->GetOption(KSolBtACL, ELMOutboundACLSize, buf);
+ ASSERT_DEBUG(!err);
+ iACLMTU = buf();
+
+ err = iBoundSAP->GetOption(KSolBtACL, ELMInboundACLSize, buf);
+ ASSERT_DEBUG(!err);
+ iACLMRU = buf();
+
+ (void)(err != KErrNone); // keep the compiler happy by taking err as an r-value in urel
+
+ iLinkState = EConnected;
+
+ // Send an Information Request as soon as possible.
+ TL2CapEntityInfo info;
+ iBoundLinkSignalHandler->PeerEntityConfig().PeerL2CapSupportedFeatures(info);
+
+ TDblQueIter<CL2CapSAPSignalHandler> sapSignalIter(iSapSignalHandlers);
+ sapSignalIter.SetToFirst();
+ CL2CapSAPSignalHandler* sapPtr;
+
+ while((sapPtr = sapSignalIter++) != NULL)
+ {
+ sapPtr->LinkUp();
+ }
+
+ // No point hanging around if nobody actually connects to us!
+ // Mainly useful for incoming connections
+ if(Idle())
+ {
+ StartIdleTimer();
+ }
+ }
+
+void CL2CAPMux::NewL2CAPData(RMBufChain& aData, TUint8 aFlag)
+ {
+ LOG_FUNC
+ TUint8 l2Flags = static_cast<TUint8>(aFlag>>KPacketPBBCFlagShift & KPacketPBFlagMask);
+ LOG1(_L("L2CAP Mux received data with flags, %x"), aFlag);
+
+ switch(l2Flags)
+ {
+ case KFirstHLFragment:
+ // This is the start of a PDU. If an incoming PDU already exists
+ // delete it and start a new one.
+ if(!iIncomingPDU.IsEmpty())
+ {
+ LOG(_L("L2CAP [l2signalmgr.cpp]: Warning: Dropped previous incoming packet - start of next packet received"));
+
+ iIncomingPDU.Free();
+ }
+ break;
+
+ case KContinuingHLFragment:
+ // This is a continuation so an existing PDU should exist.
+ // If it doesn't drop this data and wait for the next start of
+ // PDU.
+ if(iIncomingPDU.IsEmpty())
+ {
+ LOG(_L("L2CAP [l2signalmgr.cpp]: Warning: Continuation fragment received and dropped - waiting for start"));
+
+ aData.Free();
+ return;
+ }
+ break;
+
+ default:
+ LOG1(_L("L2CAP [l2signalmgr.cpp] Warning: Unexpected ACL packet type received: %d"), aFlag);
+ return;
+ };
+
+ if(HL2CapPDU::AddFragment(iIncomingPDU, aData))
+ {
+ // PDU complete.
+ PacketComplete();
+ __ASSERT_DEBUG(iIncomingPDU.IsEmpty(), Panic(EL2CAPPacketCompleteFailedToConsumeIncomingPDU));
+ }
+ }
+
+// A full packet is now in the buffer. Process it
+void CL2CAPMux::PacketComplete()
+ {
+ LOG_FUNC
+ TBool packetDelivered = EFalse;
+
+ if(iIncomingPDU.Length() >= HL2CapPDU::KPDUHeaderLength)
+ {
+ iIncomingPDU.Align(HL2CapPDU::KPDUHeaderLength);
+ TUint16 cid = HL2CapPDU::PDUCID(iIncomingPDU);
+
+ switch(cid)
+ {
+ case KL2CapSignallingCID:
+ {
+ // This is a signalling packet
+ HCFramePDU* cFramePtr = new HCFramePDU(iIncomingPDU, iACLMTU);
+ if(cFramePtr)
+ {
+ cFramePtr->CheckDecode();
+ if(cFramePtr->HasCommands())
+ {
+ // The C-Frame can contain many L2CAP commands. Not all of
+ // the commands will be for a SAP signal handler. If a
+ // command can be processed it will be removed from the
+ // C-Frame. Any remaining commands will be offered to the
+ // other SAP signal handlers, then the whole process of
+ // offering the commands to the SAP signal handlers is
+ // repeated until no more commands can be handled by them,
+ // after which the commands are offered to the link signal
+ // handler. Only one command is handled at a time because a
+ // Command Reject may delete a SAP signal handler, thereby
+ // rendering it ineffective at processing further commands.
+
+ TBool commandsWereHandled;
+ TDblQueIter<CL2CapSAPSignalHandler> sapSignalIter(iSapSignalHandlers);
+ CL2CapSAPSignalHandler* sshPtr;
+ do
+ {
+ commandsWereHandled = EFalse;
+ sapSignalIter.SetToFirst();
+ while (!packetDelivered && (sshPtr = sapSignalIter++) != NULL)
+ {
+ commandsWereHandled |= sshPtr->HandleIncomingCFrame(cFramePtr);
+
+ // If all commands from the C-Frame have been processed then
+ // consider the packet delivered.
+ packetDelivered = !cFramePtr->HasCommands();
+ }
+ }
+ while (!packetDelivered && commandsWereHandled);
+
+ if (!packetDelivered)
+ {
+ // Offer the remaining commands to the link signal handler.
+ while (iBoundLinkSignalHandler->HandleIncomingCFrame(cFramePtr))
+ ;
+ }
+ }
+
+ // Delete the CFrame.
+ delete cFramePtr;
+ }
+ else
+ {
+ ErrorAllSignalHandlers(KErrNoMemory);
+ iIncomingPDU.Free();
+ }
+ }
+ break;
+
+ case KL2CapConnectionlessCID:
+ // Connectionless L2CAP is not currently supported.
+ iIncomingPDU.Free();
+ break;
+
+ default:
+ {
+ if(cid >= KL2CapDynamicCIDStart)
+ {
+ // This is a data frame.
+ TDblQueIter<CL2CapBasicDataController> dataControllerIter(iDataHandlers);
+ CL2CapBasicDataController* dcPrt;
+ while((dcPrt = dataControllerIter++) != NULL && !packetDelivered)
+ {
+ packetDelivered = dcPrt->HandleIncomingDataFrame(iIncomingPDU);
+ }
+ }
+
+ if(!packetDelivered)
+ {
+ // This data frame was not for any channel or was using an
+ // invalid CID. Drop it.
+ iIncomingPDU.Free();
+ }
+ }
+ break;
+ };
+ }
+ else
+ {
+ // Invalid PDU length. Drop it.
+ iIncomingPDU.Free();
+ }
+ }
+
+void CL2CAPMux::ErrorAllSignalHandlers(TInt aError)
+ {
+ LOG_FUNC
+ TDblQueIter<CL2CapSAPSignalHandler> iter(iSapSignalHandlers);
+ CL2CapSAPSignalHandler* sapPtr;
+ while((sapPtr = iter++) != NULL)
+ {
+ sapPtr->Error(aError, MSocketNotify::EErrorAllOperations);
+ }
+ iBoundLinkSignalHandler->Error(aError, MSocketNotify::EErrorAllOperations);
+ }
+
+
+
+void CL2CAPMux::NewData(TUint aCount)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iBoundSAP, Panic(EL2CAPUnexpectedSocketUpcall));
+
+ // got data in - we may have half-shutdown (in which case link is ENone(!)
+ // reactivate link as we're likely to send out data - we're unlikely to be
+ // going idle at this rate
+ if (iLinkState == ELinkInbound)
+ {
+ // to regain full duplex
+ iBoundSAP->ActiveOpen();
+ }
+
+ while (aCount)
+ {
+ RMBufChain recvBuffer;
+ // Params: data, length, options, aAddr.
+ // Of these only data is used in CACLLink::GetData()
+ iBoundSAP->GetData(recvBuffer, 0, 0, NULL);
+
+ LOG(_L("CL2CAPMux: Getting data from bound SAP"))
+
+ // we get a datagram
+ // format...
+ // |-----|------------------/
+ // |flag |data /
+ // |-----|------------------/
+
+ ASSERT_DEBUG(recvBuffer.Length()>=1);
+ LOG1(_L("CL2CAPMux: Got data length, %d"), recvBuffer.Length())
+ TUint8 flags = (recvBuffer.First())->Ptr()[0];
+ recvBuffer.TrimStart(1); // Remove the flags
+ NewL2CAPData(recvBuffer, flags);
+
+ aCount--;
+ }
+ }
+
+void CL2CAPMux::ConnectComplete(const TDesC8& /*aConnectData*/)
+ {
+ LOG_FUNC
+#ifdef _DEBUG
+ Panic(EL2CAPUnexpectedSocketUpcall);
+#endif
+ }
+
+void CL2CAPMux::ConnectComplete(CServProviderBase& /*aSSP*/)
+ {
+ LOG_FUNC
+ // passive connects should come via the listener
+#ifdef _DEBUG
+ Panic(EL2CAPUnexpectedSocketUpcall);
+#endif
+ }
+
+void CL2CAPMux::ConnectComplete(CServProviderBase& /*aSSP*/,const TDesC8& /*aConnectData*/)
+ {
+ LOG_FUNC
+#ifdef _DEBUG
+ Panic(EL2CAPUnexpectedSocketUpcall);
+#endif
+ }
+
+void CL2CAPMux::CanClose(TDelete aDelete)
+ {
+ LOG_FUNC
+ if (aDelete == EDetach)
+ {
+ // so we don't delete the bound SAP - it's decided to own itself
+ iBoundSAP = NULL;
+ }
+ // Delete the Mux.
+ delete this;
+ }
+
+void CL2CAPMux::CanSend()
+ {
+ LOG_FUNC
+ iAsyncCallBackForTryToSend->CallBack();
+ }
+
+void CL2CAPMux::TryToSend()
+/**
+ We may now be able to send data.
+ Drain out the Q while we don't get an error on write.
+ Deal with sent packets: either delete, or put onto response pendingQ
+ if the ResponseIsExpected flag is set.
+**/
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iACLMTU, Panic(EL2CAPZeroAclMtuInTryToSend)); // zero would be useless...
+
+ TBool flowControlledOff = EFalse;
+
+ TBool dataToSend = ETrue;
+
+ TDblQueIter<CL2CapSAPSignalHandler> sigIter(iSapSignalHandlers);
+ TDblQueIter<CL2CapBasicDataController> dcIter(iDataHandlers);
+
+ HL2CapPDU* pduToSend = NULL;
+
+ if(iUpdateTryToSendPriority && !iAsyncCallBackForTryToSend->IsActive())
+ {
+ iAsyncCallBackForTryToSend->SetPriority(iTryToSendPriority);
+ iUpdateTryToSendPriority = EFalse;
+ }
+
+ while(dataToSend && !flowControlledOff)
+ {
+ if(!iFragmentSender->IsPDUBeingSent())
+ {
+ pduToSend = iBoundLinkSignalHandler->GetPDU();
+ if(!pduToSend && !iSapSignalHandlers.IsEmpty())
+ {
+ //Check to see if there are any signalling packets ready
+ sigIter.SetToFirst();
+
+ CL2CapSAPSignalHandler* sapPtr;
+ while((sapPtr = sigIter++) != NULL && !pduToSend)
+ {
+ pduToSend = sapPtr->GetPDU();
+ }
+ }
+
+ if(!pduToSend && !iDataHandlers.IsEmpty())
+ {
+ // This data is prioritised.
+ dcIter.SetToFirst();
+
+ CL2CapBasicDataController* dcPtr;
+ while((dcPtr = dcIter++) != NULL && !pduToSend)
+ {
+ pduToSend = dcPtr->GetPdu();
+ // If the next controller in the ordered list has the same priority
+ // Remove this one and move it to the end of the controllers with
+ // equal priority.
+ if(pduToSend && dcIter &&
+ ((CL2CapBasicDataController*)dcIter)->Config().ChannelPriority() == dcPtr->Config().ChannelPriority())
+ {
+ dcPtr->iLink.Deque();
+ PriorityAddDataController(*dcPtr);
+ }
+ }
+ }
+
+ if(pduToSend)
+ {
+ TInt err = iFragmentSender->FragmentPDU(*pduToSend);
+ if(err == KErrNone)
+ {
+ if (!pduToSend->HasOwner())
+ {
+ // If the data controller doesn't claim ownership of the PDU, we take over.
+ // DCs take care of I-Frames and we own pretty much everything else
+ // (B-,C-,S-Frames).
+ pduToSend->SetPduOwner(this);
+ iPDUsWaitingSend.AddLast(*pduToSend);
+ }
+ }
+ else
+ {
+ // Error all the SAPs
+ ErrorAllSignalHandlers(err);
+ delete pduToSend;
+ }
+ }
+ else
+ {
+ dataToSend = EFalse;
+ }
+ }
+
+ if(iFragmentSender->IsPDUBeingSent())
+ {
+ HFragmentedPDUSender::TFragmentSenderStatus rStatus = iFragmentSender->WriteNextFragment(*iBoundSAP, iACLMTU);
+ switch(rStatus)
+ {
+ case HFragmentedPDUSender::EFragmentOK:
+ break;
+
+ case HFragmentedPDUSender::EFragmentationComplete:
+ __ASSERT_DEBUG(!iFragmentSender->IsPDUBeingSent(), Panic(EL2CAPPDUBeingSentAfterFragmentationComplete));
+
+ // Check if PDU's are still present in the re-send queue.
+ if(!iPDUsWaitingResendQ.IsEmpty())
+ {
+ pduToSend = iPDUsWaitingResendQ.First();
+ pduToSend->iLink.Deque();
+ TInt err = iFragmentSender->FragmentPDU(*pduToSend);
+ if(err == KErrNone)
+ {
+ __ASSERT_DEBUG(pduToSend->IsOwner(this), Panic(EL2CAPUnownedPduOnMuxQ));
+ iPDUsWaitingSend.AddLast(*pduToSend);
+ }
+ else
+ {
+ delete pduToSend;
+ // Error all the SAPs
+ ErrorAllSignalHandlers(err);
+ }
+ }
+ else
+ {
+ LOG(_L("CL2CAPMux: TryToSend -- finished sending a PDU, no more waiting"));
+
+ // If we have no PDUs to send, the last PDU *may* have been the last disconnect
+ // on the link. This would mean that we have no SAPs left, so we can destroy the
+ // underlying ACL link.
+
+ if(Idle())
+ {
+ LOG(_L("CL2CAPMux: TryToSend -- no current SAPs, queuing idle callback"));
+
+ // The Muxer will be destroyed and
+ // the ACL / baseband will be removed by CL2CAPMux::AsyncCallBackForIdleMux()...
+ iAsyncCallBackForIdleMux->CallBack();
+ }
+ else
+ {
+ LOG(_L("CL2CAPMux: TryToSend -- still have SAPs present"));
+ }
+ }
+ break;
+
+ case HFragmentedPDUSender::EFlowControlledOff:
+ flowControlledOff = ETrue;
+ break;
+
+ default:
+ Panic(EL2CAPInvalidHFragmentedPDUSenderStatusCode);
+ break;
+ };
+ }
+ }
+ }
+
+void CL2CAPMux::CanClose(const TDesC8& /*aDisconnectData*/, TDelete /*aDelete*/)
+ {
+ LOG_FUNC
+#ifdef _DEBUG
+ Panic(EL2CAPUnexpectedSocketUpcall);
+#endif
+ }
+
+void CL2CAPMux::Error(TInt aError,TUint /*aOperationMask*/)
+/**
+ The link has not come up.
+ Error any SSHs that are hanging around.
+**/
+ {
+ LOG_FUNC
+ iLinkState = ELinkNone;
+
+ // In the case where a SAP requested a connection but then
+ // disconnected prior to the connection failing ensure that
+ // the idle timer is started.
+ if(Idle())
+ {
+ // The Muxer will be destroyed and
+ // the ACL / baseband will be removed by CL2CAPMux::AsyncCallBackForIdleMux()...
+ iAsyncCallBackForIdleMux->CallBack();
+ }
+ else
+ {
+ // Leave Mux around - SSHs detach as necessary
+ ErrorAllSignalHandlers(aError);
+ }
+ }
+
+void CL2CAPMux::Disconnect()
+/**
+ We no longer have a link to run on.
+**/
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iLinkState == EConnected, Panic(EL2CAPLinkDownWhenNotUp));
+
+ // no link!
+ iLinkState = ELinkNone;
+
+ // Signal to all signal handlers that the link has gone.
+ TDblQueIter<CL2CapSAPSignalHandler> sapSignalIter(iSapSignalHandlers);
+ CL2CapSAPSignalHandler* sapPtr;
+
+ while((sapPtr = sapSignalIter++) != NULL)
+ {
+ sapPtr->Error(KErrHCILinkDisconnection, MSocketNotify::EErrorAllOperations);
+ }
+ iBoundLinkSignalHandler->Error(KErrHCILinkDisconnection, MSocketNotify::EErrorAllOperations);
+ delete this;
+ }
+
+
+TInt CL2CAPMux::IdleTimerExpired(TAny *aMuxer)
+/**
+ Idle timer has expired.
+ Static function called through TCallBack. aMuxer holds the muxer
+ that has actually expired. Ask mux to down its own link.
+**/
+ {
+ LOG_STATIC_FUNC
+ CL2CAPMux* muxer = static_cast<CL2CAPMux*>(aMuxer);
+ // record that no further idle timer is queued:
+ muxer->iIdleTimerActive = EFalse;
+
+ if (muxer->Idle())
+ {
+ // We're (still) idle! Lets disconnect.
+ muxer->MuxIdled();
+ }
+ return EFalse; // no more callbacks thanks!
+ }
+
+
+void CL2CAPMux::CancelIdle()
+ {
+ LOG_FUNC
+ if(iIdleTimerActive)
+ {
+ // there's something to cancel
+ iIdleTimerActive = EFalse;
+ BTSocketTimer::Remove(iIdleTimerEntry);
+ }
+ }
+
+
+
+void CL2CAPMux::Disconnect(TDesC8& /*aDisconnectData*/)
+ {
+ LOG_FUNC
+#ifdef _DEBUG
+ Panic(EL2CAPUnexpectedSocketUpcall);
+#endif
+ }
+
+void CL2CAPMux::IoctlComplete(TDesC8* /*aBuf*/)
+ {
+ LOG_FUNC
+#ifdef _DEBUG
+ Panic(EL2CAPUnexpectedSocketUpcall);
+#endif
+ }
+
+void CL2CAPMux::NoBearer(const TDesC8& /*aConnectionInfo*/)
+ {
+ LOG_FUNC
+#ifdef _DEBUG
+ Panic(EL2CAPUnexpectedSocketUpcall);
+#endif
+ }
+
+void CL2CAPMux::Bearer(const TDesC8& /*aConnectionInfo*/)
+ {
+ LOG_FUNC
+#ifdef _DEBUG
+ Panic(EL2CAPUnexpectedSocketUpcall);
+#endif
+ }
+
+void CL2CAPMux::HandlePduSendComplete(HL2CapPDU& aPdu)
+ {
+ LOG_FUNC
+ // We were honored with the ownership of the PDU, so it must be on our list.
+ aPdu.iLink.Deque();
+ delete &aPdu;
+ }
+
+void CL2CAPMux::HandlePduSendError(HL2CapPDU& aPdu)
+ {
+ LOG_FUNC
+ L2CAP_DEBUG(UpdateFlushCounters(L2capDebugInfo::EFlushedPDUMuxerResend));
+ // Note: this only handles PDUs we're the owner of.
+
+ // Delink it from iPDUsWaitingSendQ.
+ aPdu.iLink.Deque();
+
+#ifdef _DEBUG
+ // Check that the PDU does not already exist in the list.
+ TDblQueIter<HL2CapPDU> iter(iPDUsWaitingResendQ);
+ HL2CapPDU* pduPtr;
+
+ while((pduPtr = iter++) != NULL)
+ {
+ if(pduPtr == &aPdu)
+ {
+ Panic(EL2CAPRequestToResendAPDUThatIsAlreadyInTheResendQueue);
+ }
+ }
+#endif
+ iPDUsWaitingResendQ.AddLast(aPdu);
+ }
+
+void CL2CAPMux::RegisterDataPDUHandler(CL2CapBasicDataController& aPDUDataHandler)
+ {
+ LOG_FUNC
+ PriorityAddDataController(aPDUDataHandler);
+ iNumberOfDataChannels++;
+
+ iMuxController.UpdateMuxerPriorities();
+ }
+
+void CL2CAPMux::DataChannelRemoved(CL2CapBasicDataController* aDataController)
+ {
+ LOG_FUNC
+ aDataController->iLink.Deque();
+ iNumberOfDataChannels--;
+
+ iMuxController.UpdateMuxerPriorities();
+ }
+
+void CL2CAPMux::ChannelPriorityUpdated(CL2CapBasicDataController& aDataController)
+ {
+ LOG_FUNC
+ // First remove the channel.
+ aDataController.iLink.Deque();
+
+ // Now re-register the channel with the Muxer.
+ RegisterDataPDUHandler(aDataController);
+ }
+
+void CL2CAPMux::MuxerPriorityUpdate(TInt aPriority)
+ {
+ LOG_FUNC
+ iTryToSendPriority = aPriority;
+ if(!iAsyncCallBackForTryToSend->IsActive())
+ {
+ iAsyncCallBackForTryToSend->SetPriority(iTryToSendPriority);
+ }
+ else
+ {
+ iUpdateTryToSendPriority = ETrue;
+ }
+ }
+
+TInt CL2CAPMux::GetSumMuxerChannelPriorities()
+ {
+ LOG_FUNC
+ // The sum is used to calculate the mux priority for inter-mux scheduling.
+ TInt prioritySum = 0;
+
+ TDblQueIter<CL2CapBasicDataController> dataControllerIter(iDataHandlers);
+ CL2CapBasicDataController* dcPrt;
+ while((dcPrt = dataControllerIter++) != NULL)
+ {
+ prioritySum += dcPrt->Config().ChannelPriority();
+ }
+
+ return prioritySum;
+ }
+
+
+void CL2CAPMux::PDUAvailable()
+ {
+ LOG_FUNC
+ iAsyncCallBackForTryToSend->CallBack();
+ }
+
+TBool CL2CAPMux::HardwareFlushAllowed()
+// H/W flush allowed if no channel using Basic Mode.
+// Channels using flow control are deemed unreliable
+// so expect to be flushed if need be.
+// Channels using L2Cap retransmission are covered by that
+// retransmission, and so can have the current transmission
+// flushed if need be.
+ {
+ LOG_FUNC
+ TDblQueIter<CL2CapBasicDataController> iter(iDataHandlers);
+ CL2CapBasicDataController* handlerPtr;
+ TBool rerr = ETrue;
+
+ while((handlerPtr = iter++) != NULL)
+ {
+ if(handlerPtr->IsBasicDataVersion())
+ {
+ rerr = EFalse;
+ break;
+ }
+ }
+ return rerr;
+ }
+
+void CL2CAPMux::ProcessFlushTimerExpiry()
+ {
+ LOG_FUNC
+ // The PDU currently being sent could have been flushed. If it has
+ // set the current outgoing PDU pointer to zero.
+ iFragmentSender->CheckForFlushed();
+
+ if(HardwareFlushAllowed())
+ {
+ iBoundSAP->SetOption(KSolBtACL, KSolBtACLFlushOccured, KNullDesC8);
+ }
+
+ PDUAvailable();
+ }
+
+void CL2CAPMux::RegisterSAPSignalHandler(CL2CapSAPSignalHandler& aSAPSigHandler)
+ {
+ LOG_FUNC
+ // Cancel the idle timer if it is running.
+ CancelIdle();
+
+#ifdef _DEBUG
+ // Check that the SAP signal handler does not already exist in the list.
+ TDblQueIter<CL2CapSAPSignalHandler> sapSignalIter(iSapSignalHandlers);
+ while (sapSignalIter)
+ {
+ if (&aSAPSigHandler == sapSignalIter++)
+ {
+ Panic(EL2CAPSAPSignalHandlerRegisteredTwice);
+ }
+ }
+#endif
+ iSapSignalHandlers.AddLast(aSAPSigHandler);
+
+ aSAPSigHandler.SAPSignalHandlerRegistered(*this, iBoundLinkSignalHandler->PeerEntityConfig());
+ // If the ACL link is closing, re-open it by calling active
+ // open. If there is currently is no link and this Muxer
+ // is configured for the desired remote address, then
+ // call active open to establish a connection.
+ if(iLinkState == ELinkInbound ||
+ ((iLinkState == ELinkNone) && iBoundSAP))
+ {
+ iLinkState = ELinkPending;
+ iBoundSAP->ActiveOpen();
+ }
+ else
+ {
+ // The link is already up. Inform the SAP signal handler.
+ if(iLinkState == EConnected)
+ {
+ aSAPSigHandler.LinkUp();
+ }
+ }
+ }
+
+void CL2CAPMux::L2CapEntityConfigUpdated()
+ {
+ LOG_FUNC
+ TDblQueIter<CL2CapSAPSignalHandler> sapSignalIter(iSapSignalHandlers);
+ sapSignalIter.SetToFirst();
+ CL2CapSAPSignalHandler* sapPtr;
+
+ while((sapPtr = sapSignalIter++) != NULL)
+ {
+ sapPtr->L2CapEntityConfigUpdated();
+ }
+ }
+
+TUint16 CL2CAPMux::SigMTU() const
+ {
+ LOG_FUNC
+ return iBoundLinkSignalHandler->SigMTU();
+ }
+
+TInt CL2CAPMux::SendEchoRequest(const TDes8* aData, CL2CapSAPSignalHandler& aEchoResponseHandler)
+ {
+ LOG_FUNC
+ return iBoundLinkSignalHandler->ConstructEchoRequest(aData, aEchoResponseHandler);
+ }
+
+void CL2CAPMux::EchoResponseReceived(const TDesC8* aData, MEchoResponseHandler& aEchoResponseHandler)
+ {
+ LOG_FUNC
+ // Pass the response to the originator.
+ aEchoResponseHandler.EchoResponseReceived(aData);
+ }
+
+TInt CL2CAPMux::GetFreeCID(TL2CAPPort& aReturnPort)
+ {
+ LOG_FUNC
+ TBool CIDIsFree;
+ TL2CAPPort startCID = iNextCID;
+
+ TDblQueIter<CL2CapSAPSignalHandler> sapSignalIter(iSapSignalHandlers);
+ CL2CapSAPSignalHandler* handler;
+
+ aReturnPort = 0;
+ do
+ {
+ sapSignalIter.SetToFirst();
+ CIDIsFree = ETrue;
+ while((handler = sapSignalIter++) != NULL)
+ {
+ if(handler->LocalPort() == iNextCID)
+ {
+ CIDIsFree = EFalse;
+ break;
+ }
+ }
+
+ if(CIDIsFree)
+ {
+ aReturnPort = iNextCID;
+ }
+
+ if(iNextCID++ == KL2CapDynamicCIDEnd)
+ {
+ iNextCID = KL2CapDynamicCIDStart;
+ }
+ } while(!aReturnPort && startCID != iNextCID);
+
+ return aReturnPort?KErrNone:KErrL2CAPNoFreeCID;
+ }
+
+TInt CL2CAPMux::GetACLMTU() const
+ {
+ return iACLMTU;
+ }
+
+CL2CapSAPSignalHandler* CL2CAPMux::GetSignalHandlerWithRemoteCID(TL2CAPPort aRemoteCID)
+ {
+ LOG_FUNC
+ TDblQueIter<CL2CapSAPSignalHandler> sapSignalIter(iSapSignalHandlers);
+ sapSignalIter.SetToFirst();
+ CL2CapSAPSignalHandler* sapPtr;
+
+ while((sapPtr = sapSignalIter++) != NULL)
+ {
+ if(sapPtr->RemotePort() == aRemoteCID)
+ {
+ break;
+ }
+ }
+ // returns CL2CapSAPSignalHandler with matching remote CID or NULL if one not found
+ return sapPtr;
+ }
+
+// Adds the data controller to the Muxers list in priority order.
+// If a Data controller of equal priority in the queue, the new one
+// will be added after it.
+void CL2CAPMux::PriorityAddDataController(CL2CapBasicDataController& aDataController)
+ {
+ LOG_FUNC
+ if(iDataHandlers.IsEmpty())
+ {
+ iDataHandlers.AddFirst(aDataController);
+ }
+ else
+ {
+ TUint8 channelPriority = aDataController.Config().ChannelPriority();
+
+ TDblQueIter<CL2CapBasicDataController> iter(iDataHandlers);
+ CL2CapBasicDataController* handler;
+
+ if(channelPriority > (iter++)->Config().ChannelPriority())
+ {
+ iDataHandlers.AddFirst(aDataController);
+ }
+ else
+ {
+ TBool controllerAdded = EFalse;
+ while((handler = iter++) != NULL && !controllerAdded)
+ {
+ if(channelPriority > handler->Config().ChannelPriority())
+ {
+ handler->iLink.AddBefore(&(aDataController.iLink));
+ controllerAdded = ETrue;
+ }
+ }
+ if(!controllerAdded)
+ {
+ iDataHandlers.AddLast(aDataController);
+ }
+ }
+ }
+ }
+
+
+void CL2CAPMux::PhysicalLinkChange(const TBTBasebandEventNotification& aEvent, CPhysicalLink& /*aPhysicalLink*/)
+ {
+ LOG_FUNC
+
+ if (aEvent.EventType() & ENotifySniffMode)
+ {
+ //Entered SniffMode
+ TPckgBuf<TBasebandTime> buf = 0;
+ TInt err = iBoundSAP->GetOption(KSolBtLM, EBBGetSniffInterval, buf);
+ __ASSERT_DEBUG(!err, Panic(EL2CAPGetOptionFailed));
+ if (KErrNone==err)
+ {
+ iSniffInterval = buf();
+ }
+ }
+
+ if ((aEvent.EventType() & ENotifyActiveMode) && iSniffInterval)
+ {
+ //Exited SniffMode
+ iSniffInterval = 0;
+ }
+ }
+
+TUint8 CL2CAPMux::AdjustRTXTimerForSniffMode(TUint8 aBaseRTXTimerDuration) const
+ {
+ LOG_FUNC
+ TUint8 sniffModeRTXTimerDuration = aBaseRTXTimerDuration;
+
+ if (iSniffInterval != 0)
+ {
+ // RTX Timer value must be set to at least twice the sniff interval to allow
+ // us to send a request in one transmission slot and for the remote device
+ // to respond in the next. This assumes that the remote device can turn around the
+ // request in one sniff interval but if we have a small interval this may not
+ // be the case so we need to add a constant to allow for the remote to process the
+ // request.
+ TReal sniffModeAdjustment = (iSniffInterval * KBasebandSlotTime * 2) + KRemoteDeviceProcessRequestDuration;
+
+ sniffModeRTXTimerDuration = Max(ceil(sniffModeAdjustment),aBaseRTXTimerDuration);
+ }
+
+ return sniffModeRTXTimerDuration;
+ }