diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/linkmgr/linkmuxer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/linkmgr/linkmuxer.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,314 @@ +// 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: +// HCIChannelMux.cpp +// HCI Channel Multiplexer implementation. +// +// + +#include +#include +#include "linkmuxer.h" +#include "AclDataQController.h" +#include "linkconsts.h" +#include "hcifacade.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR); +#endif + +CLinkMuxer* CLinkMuxer::NewL(CLinkMgrProtocol& aLinkMgrProtocol, CHCIFacade& aHCIFacade) + { + LOG_STATIC_FUNC + CLinkMuxer* self = new (ELeave) CLinkMuxer(aLinkMgrProtocol, aHCIFacade); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + return self; + } + +void CLinkMuxer::ConstructL() + { + LOG_FUNC + // Get pointer to CommandQ + iCommandController = &iHCIFacade.CommandQController(); + +#ifdef PROXY_COMMUNICATES + const TUint16 noBufs = iHCIFacade.ReadACLReportingInterval(); +#else + const TUint16 noBufs = ++iHCIFacade.ReadACLReportingInterval(); // another slot for bc handle +#endif + const TUint16 framingOverhead = iHCIFacade.ReadACLFramingOverhead(); + + iDataController= CACLDataQController::NewL(iLinkMgrProtocol, + *this, + KHCIACLMinDataBufferSize, + framingOverhead, + noBufs); + + // Tell the limited data Q controller our data credits from HC is 0 + // when we establish how many HC really has we will notify again + // that will happen on the reception of the first ReadBufferSize result + iDataController->InitialDataCredits(0); + + iChannelsFree = iHCIFacade.HCTLState(); + + TCallBack cb(TryToSendCallBackStatic, this); + iSendCallBack = new (ELeave)CAsyncCallBack(cb, EActiveMedPriority); + } + +CLinkMuxer::CLinkMuxer(CLinkMgrProtocol& aLinkMgrProtocol, CHCIFacade& aHCIFacade) + : iHCIFacade(aHCIFacade), iChannelsFree(KHCITransportNoChannels), iLinkMgrProtocol(aLinkMgrProtocol) +/** + We expect the transport to notify us when the transport channels are ready +**/ + { + LOG_FUNC + } + +CLinkMuxer::~CLinkMuxer() + { + LOG_FUNC + delete iSendCallBack; + delete iDataController; + } + +TInt CLinkMuxer::ACLPacketMTU() const + { + LOG_FUNC + __ASSERT_DEBUG(iACLPacketMTU, Panic(ELinkMgrPacketMTUUseBeforeSet)); + return iACLPacketMTU; + } + + +TInt CLinkMuxer::TryToSendCallBackStatic(TAny* aCLinkMuxer) + { + LOG_STATIC_FUNC + static_cast(aCLinkMuxer)->DoSend(); + return EFalse; + } + +void CLinkMuxer::TryToSend() + { + LOG_FUNC + // fireup async callback + iSendCallBack->CallBack(); + } + +void CLinkMuxer::DoSend() +/** + This is the method where it is decided whether a command or data packet will be issued next. + A send may not occur - the signal to send may have occurred through the addition to a Q + whereas the destination channel for the relevant packet may not be free + + There could have been a 'Strategy' pattern here, for applying different + scheduling in the future. The likely hood of this is very low, hence the + simpler implementation. + + The scheduling is very simple: + If we have command packets to send and credits to do so ,send one + otherwise send a data packet if possible/available. +*/ + { + LOG_FUNC + LOG1(_L("LinkMuxer: Dosend- free channels 0x%04x"), iChannelsFree); + + if (!iChannelsFree) + return; // no channel to send on available + + TUint theDataQLevel=0; + TUint16 theDataCredits=0; + + iDataController->GetDataQRecords(theDataQLevel,theDataCredits); + + // the assumption here is that a channel is free + // note the implicit precedence of commands over data + + if (iChannelsFree & KHCITransportCommandChannel) + { + // have commands and command channel is free + iCommandController->DoSend(); + } + + if((iChannelsFree & KHCITransportACLDataChannel) + && theDataQLevel && theDataCredits) + { + // have data, and credits, and data channel is free, try to send some data + iDataController->IssueNextACLDataFragment(); // may or may not issue! + } + } + + +/** + When channels become free this method gets called to tell the muxer + The muxer takes the opportunity to attempt a send +*/ + +void CLinkMuxer::ChannelsFree(THCITransportChannel aChannel) + { + LOG_FUNC + iChannelsFree |= aChannel; + + if (iChannelsFree != KHCITransportNoChannels) + { + // only call async callback - don't call synchronous + TryToSend(); + } + } +/** + When channels become closed this method is called to remove them + from the "free" list. + + Note: The channels that require closing have their respective bits set + hence the reason for the bit inversion within this method. +*/ + +void CLinkMuxer::ChannelsClosed(THCITransportChannel aChannel) + { + LOG_FUNC + iChannelsFree &= (~aChannel); + iChannelsFree &= KHCITransportAllChannels; + } + + +#ifdef STACK_SCO_DATA +TBool CLinkMuxer::CanWriteSCOData() + { + LOG_FUNC + return (iChannelsFree & KHCITransportSCODataChannel); + } +#endif + +void CLinkMuxer::RecordHostControllerToHostFlowControl(TBool aFlowFlag) +/** + Called when HCIFacade receives a Command Complete event to the SetHostControllerToHostFlowControl command + @param aFlowFlag - true is command succeeded, false otherwise + +**/ + { + LOG_FUNC + // check our current mode + switch (iFlowControlMode) + { + case ENoFlowControl: + { +#ifdef _DEBUG + if(aFlowFlag) + {iFlowControlMode=EFlowControlFromHostControllerOnly;} +#else + Panic(ELinkMgrNoFlowControlSetInReleaseBuild); +#endif + break; + } + case EFlowControlToHostControllerOnly: + { + if(aFlowFlag) + {iFlowControlMode=ETwoWayFlowControlEnabled;} + break; + } + case EFlowControlFromHostControllerOnly: + { +#ifdef _DEBUG + if(aFlowFlag==EFalse) + {iFlowControlMode=ENoFlowControl;} +#else + Panic(ELinkMgrNoFlowControlSetInReleaseBuild); +#endif + break; + } + case ETwoWayFlowControlEnabled: + { + if(aFlowFlag==EFalse) + { + // tried to do two-way but the HC can't to HC->H FC + iFlowControlMode=EFlowControlToHostControllerOnly; + } + break; + } + default: + Panic(ELinkMgrNoSuchFlowControlMode); + } //switch + } + + +CACLDataQController* CLinkMuxer::HandleLocalReadBufferSizeResult( + TUint16 aAclMaxLen, + TUint8 /*aScoMaxLen*/, + TUint16 aNoACL, + TUint16 /*aNoSCO*/) +/** + This method handles the results of the local (intra HCI) inquiry of the HC + buffer capabilities. + The results are then used for setting the HCI MTU (=HC MTU) and for + modeling the HC's pool usage. + This method must be called very early upon start-up in order to set up the + data Q and its controller, for the flow control between L2CAP -> HCI -> + HC. + + @param aAclMaxLen Maximum length of each ACLDataPacket. + @param aScoMaxLen Maximum length of each SCODataPacket. + @param aNoACL Total no. of ACL Data Packets. + @param aNoSCO Total no. of SCO Data Packets. + @return Return address (not ownership) of new/same ACL Data Controller. +*/ + { + LOG_FUNC + LOG2(_L("CLinkMuxer::HandleLocalReadBufferSizeResult aAclMaxLen = %d, aNoACL = %d"), + aAclMaxLen, aNoACL); + + const TUint preferredNumBuffers = Max( + static_cast(KHCIPreferedNumberOfHCIACLDataBuffers), + static_cast(aNoACL) + ); + + // If the HC supports more buffers than we already allocated (in + // ConstructL) then make a new (bigger!) Q controller. NB This may fail, + // in which case just go with the earlier one- we always return from this + // (non-leaving) function leaving behind a valid iDataController. + TUint16 bufSize; + TUint numBufs; + iDataController->GetBufferInfo(bufSize, numBufs); + if ( preferredNumBuffers >= numBufs ) + { + const TUint16 framingOverhead = iHCIFacade.ReadACLFramingOverhead(); + + CACLDataQController* tmpDataQController = NULL; + TRAP_IGNORE(tmpDataQController = + CACLDataQController::NewL(iLinkMgrProtocol, + *this, + aAclMaxLen, + framingOverhead, + preferredNumBuffers)); + if ( tmpDataQController ) + { + delete iDataController; + iDataController = tmpDataQController; + } + } + + // Tell the data Q controller our HC data credits + iDataController->InitialDataCredits(aNoACL); + + iDataController->GetBufferInfo(bufSize, numBufs); + iACLPacketMTU = bufSize; + __ASSERT_DEBUG(numBufs >= iHCIFacade.ReadACLReportingInterval(), + Panic(ELinkMgrDataQBufferIsNotSufficient)); + + LOG1(_L("CLinkMuxer::HandleLocalReadBufferSizeResult iDataController = 0x%08x"), + iDataController); + + return iDataController; + } + +// +// End of file