bluetooth/btstack/l2cap/l2signalmgr.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 15 Jan 2010 08:13:17 +0200
changeset 0 29b1cd4cb562
child 34 9d84592f5036
permissions -rw-r--r--
Revision: 200951_001

// 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;
	}