diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/l2cap/l2signalmgr.cpp --- /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 + +#include + +#include +#include + +#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 resendIter(iPDUsWaitingResendQ); + TDblQueIter 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(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(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 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 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(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 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 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 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 sigIter(iSapSignalHandlers); + TDblQueIter 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 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(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 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 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 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 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 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 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 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 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 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; + }