diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/linkmgr/hcifacade.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/linkmgr/hcifacade.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,1264 @@ +// Copyright (c) 2006-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: +// The conduit to the HCI and HW - formulates and Qs commands, and implements HCI events +// +// + +#include +#include "hcifacade.h" +#include "linkmgr.h" +#include "hostresolver.h" +#include "linkutil.h" + +#include "AclDataQController.h" +#include "linkmuxer.h" +#include "linkconsts.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_HCI_FACADE); +#endif + +// definitions +const TBasebandTime TBasebandPolicy::KPageTimeoutR0 = static_cast(2.56 /*seconds*/ / KBasebandSlotTime); // in baseband slots +const TBasebandTime TBasebandPolicy::KPageTimeoutR1 = static_cast(5.12 /*seconds*/ / KBasebandSlotTime); // in baseband slots +const TBasebandTime TBasebandPolicy::KPageTimeoutR2 = static_cast(10.24 /*seconds*/ / KBasebandSlotTime); // in baseband slots +const TReal TBasebandPolicy::KBestEffortTimeMultiplier = 2; +const TReal TBasebandPolicy::KQuickTimeMultiplier = 0.5; + +_LIT(KHciUtilComponentName, "bluetooth_stack"); + +CHCIFacade* CHCIFacade::NewL(CLinkMgrProtocol& aProtocol) + { + LOG_STATIC_FUNC + CHCIFacade* p= new (ELeave) CHCIFacade(aProtocol); + CleanupStack::PushL(p); + p->ConstructL(); + CleanupStack::Pop(); + return p; + } + +void CHCIFacade::ConstructL() +/** + 2nd phase construct the HCI Facade. + + This is where the stack loads both the HCI CommandQ and the CoreHCI and + then distributes the required APIs between the different components. +**/ + { + LOG_FUNC + // Create HCI CommandQ + iCmdController = CHCICmdQController::NewL(); + + // Create HCI Utility library + iHciUtil = CHciUtil::NewL(KHciUtilComponentName); + + // If we can't open the ini file then this will be treated in the same way + // as not reading a valid UID from the ini file. + TRAP_IGNORE(iHciUtil->OpenIniFileL()); + + // Create Core HCI plugin + _LIT(KSection, "CoreHci"); + _LIT(KCoreHciUidTag, "EcomUid"); + + TUid coreHciImplUid = TUid::Null(); + TRAPD(err, coreHciImplUid = iHciUtil->GetUidFromFileL(KSection, KCoreHciUidTag)); + + if (err == KErrNone) + { + // Valid UID found, load it + iCoreHciPlugin = CCoreHCIPlugin::NewL(coreHciImplUid); + } + else + { + // No UID found in ini file, attempt to load single instance of + // implementation + iCoreHciPlugin = CCoreHCIPlugin::NewL(); + } + + // Get Core HCI APIs + iCoreHci = static_cast(iCoreHciPlugin->Interface(TUid::Uid(KCoreHciInterfaceUid))); + iHctl = static_cast(iCoreHciPlugin->Interface(TUid::Uid(KHCTLInterfaceUid))); + + iControllerInitialisor = static_cast + (iCoreHciPlugin->Interface(TUid::Uid(KControllerInitialisationInterfaceUid))); + if (iControllerInitialisor != NULL) + { + // attempt to get the Controller Initialisation Abort Extension API + iControllerInitAbortExtension = static_cast + (iCoreHciPlugin->Interface(TUid::Uid(KControllerInitialisationAbortExtenstionInterfaceUid))); + } + + iDataFramer = static_cast(iCoreHciPlugin->Interface(TUid::Uid(KHCIDataFramerInterfaceUid))); + iHardResetInitiator = static_cast(iCoreHciPlugin->Interface(TUid::Uid(KHCHardResetUid))); + + // Panic if we don't get the required interfaces, note that the initialisor + // interface is optional + __ASSERT_ALWAYS(iCoreHci && iHctl && iDataFramer && iHardResetInitiator, + Panic(EHCIIncompleteInterfaces)); + + // Provide direct APIs + iCmdController->SetLinkMuxNotifier(*this); + iCmdController->SetHCIUnmatchedEventObserver(*this); + iCmdController->SetHardResetInitiator(*this); + iCmdController->SetPhysicalLinksState(*this); + + iCoreHci->MchSetHardResetInitiator(*this); + iCoreHci->MchSetPhysicalLinksState(*this); + iCoreHci->MchSetDataEventObserver(*this); + iCoreHci->MchSetDataObserver(*this); + iCoreHci->MchSetChannelObserver(*this); + iCoreHci->MchSetControllerStateObserver(*this); + + // Provide Core HCI with CommandQ components + iCoreHci->MchSetCommandEventObserver(*(static_cast(iCmdController))); + iCoreHci->MchSetHCICommandQueue(*(static_cast(iCmdController))); + + // Provide CommandQ with HCI components + iCmdController->SetHCTLInterface(*iHctl); + iCmdController->SetHCICommandAllocator(*(static_cast(iCoreHciPlugin->Interface(TUid::Uid(KHCICommandAllocatorInterfaceUid))))); + + // Initialise initialisor plugin if present + if (iControllerInitialisor != NULL) + { + // Provide Initialisor with Stack + iControllerInitialisor->MciiSetControllerInitialisationObserver(*this); + + // Provide Initialisor with CommandQ + iControllerInitialisor->MciiSetHCICommandQueue(*(static_cast(iCmdController))); + } + + // Attempt to supply the HCI with the Client Usage API + MHCIClientUsageCallback* hciClientUsageCallback = static_cast + (iCoreHciPlugin->Interface(TUid::Uid(KHCIClientUsageCallbackUid))); + + if (hciClientUsageCallback) + { + // Support for the Client Usage Callback API is optional for CoreHCI plugins, pass the HCI + // the Client Usage API if supported. + hciClientUsageCallback->MhcucSetClientUsage(*this); + } + + iAFHTimer=CAFHTimer::NewL(*this); + + // Read low power mode override timeout from the configuration file + _LIT(KLPMSection, "lowpowermodeconfiguration"); + _LIT(KLPMTag, "overridelpmtimeout_microseconds"); + + TUint overrideLPMTimeout = 0; + TRAP(err, overrideLPMTimeout = iHciUtil->GetValueFromFileL(KLPMSection, KLPMTag)); + + if (err == KErrNone) + { + // LPM override timeout found, pass the value into link manager + iLinkMgrProtocol.SetOverrideLPMTimeout(overrideLPMTimeout); + } + + iLastPowerState = EBTOn; + + // used later to ensure that we have enough data to call SetEventMask + iReadLocalSupportedFeaturesComplete = EFalse; + iReadLocalVersionComplete = EFalse; + +#ifdef HOSTCONTROLLER_TO_HOST_FLOW_CONTROL + iMBufPool = CHostMBufPool::NewL(*this); +#endif + } + +void CHCIFacade::SetLinkMuxer(CLinkMuxer& aLinkMuxer) + { + LOG_FUNC + iLinkMuxer=&aLinkMuxer; + } + + +void CHCIFacade::SetPhysicalLinksMgr(CPhysicalLinksManager& aLinksMgr) + { + LOG_FUNC + iLinksMgr = &aLinksMgr; + } + +TInt CHCIFacade::SendInitialisationCommand(CHCICommandBase* aCommand) + /* + This method takes ownership of the command. + */ + { + LOG_FUNC + __ASSERT_ALWAYS(aCommand != NULL, Panic(EHCICommandNullItem)); + __ASSERT_ALWAYS(iInitState==EResetting || iInitState==EPostReset, Panic(EHCICtrlrInitAddingInitialisationCommandInBadState)); + if(iInitState==EResetting) + { + __ASSERT_ALWAYS(iOutstandingCommands.Count()==0, Panic(EHCICtrlrInitOnlyOneResetCmdAllowed)); + } + + TInt err = KErrNone; + TUint qid = 0; + + // Ownership of the command is passed to the queue. + // MhcqAddInitCommandL guarantees to take ownership even if it + // leaves. + + // see CHCIFacade::MhcqcCommandEventReceived for completion + TRAP(err, qid = iCmdController->MhcqAddInitCommandL(aCommand, *this)); + if (err != KErrNone) + { + iInitialisationError = ETrue; + + LOG1(_L("Error! CHCIFacade::SendInitialisationCommand %d"), err); + return err; + } + + // We need to know when we have completed initialisation of the stack so that + // we can Start() the HCI Command Queue. To do this we keep a list of all the + // initialisation command opcodes and then remove them from the list when we + // get the corresponding completion event. The stack initialisation is complete + // when the list of opcodes is empty. + if ((err = AddOutstandingCommandOpCode(aCommand->Opcode())) != KErrNone) + { + if (iCmdController->MhcqRemoveCommand(qid, *this) != KErrNone) + { + Panic(EHCICtrlrInitFailedToRemoveCmd); + } + iInitialisationError = ETrue; + + LOG1(_L("Error! CHCIFacade::SendInitialisationCommand %d"), err); + return err; + } + + return KErrNone; + } + +void CHCIFacade::InitL(const TBTLocalDevice& aDeviceConfig) +/** + The start-up strategy + + If there is an Initialisor the pre-reset initialisation method will + be called on it at this point. When the Initialisor pre-reset method + completes the stack will be called back to enable it to send the HCI + reset command, see McioPreResetCommandComplete(). + + If there is no Initialisor then this method will call the pre-reset + complete method directly allowing the stack to send the HCI reset + command. +**/ + { + LOG_FUNC + // Only re-initialise the stack is the current state is not pre-reset. + if(iInitState != EPreReset) + { + // Initialise the Command Queue + iCmdController->Initialise(); + iInitState = EPreReset; + + // DeviceClass is cached and retrieved from HCI if not currently + // set in aDeviceConfig + iDeviceClass = aDeviceConfig.DeviceClass(); + + // Get the Local Name from the Persist storage + TInt err(iLinkMgrProtocol.SetLocalDeviceName(aDeviceConfig.DeviceName())); + if (KErrNone != err && KErrNotSupported != err) + { + User::Leave(err); + } + if (iControllerInitialisor != NULL) + { + // see CHCIFacade::McioPreResetCommandComplete + iControllerInitialisor->MciiPreResetCommand(); + } + else + { + // There is no initialisation plugin so behave as if there + // were and it had completed its pre reset phase + McioPreResetCommandComplete(KErrNone); + } + } + } + +CHCIFacade::CHCIFacade(CLinkMgrProtocol& aProtocol) + : iLinkMgrProtocol(aProtocol), + iInitState(EIdle), + iInitialisationError(EFalse) + { + LOG_FUNC + } + +CHCIFacade::~CHCIFacade() + { + LOG_FUNC + delete iAFHTimer; + #ifdef HOSTCONTROLLER_TO_HOST_FLOW_CONTROL + delete iMBufPool; + #endif + + delete iCoreHciPlugin; + + if(iCmdController) + { + iCmdController->MhcqRemoveAllCommands(*this); + delete iCmdController; + } + + iOutstandingCommands.Close(); + delete iHciUtil; + } + +CHctlAclDataFrame* CHCIFacade::NewACLDataFrameL(TUint16 aSize) const + { + LOG_FUNC + return (iDataFramer->MhdfNewAclDataFrameL(aSize)); + } + +#ifdef STACK_SCO_DATA +CHctlSynchronousDataFrame* CHCIFacade::NewSynchronousDataFrameL(TUint8 aSCODataLen) const + { + LOG_FUNC + return (iDataFramer->MhdfNewSynchronousDataFrameL(aSCODataLen)); + } +#endif + +THCITransportChannel CHCIFacade::HCTLState() const + { + LOG_FUNC + return iHCTLState; // give to muxer + } + + + +void CHCIFacade::HandlePowerStatusChange(TBTPowerState aStatus) +/** + This method assumes that the power is actually changing, i.e. from off to on or + from on to off, and it is the responsibility of the caller, the hctl in non-error + conditions, to ensure this. + + The only real dangerous transition is from on to on as this would intialise the + stack even if it is currently in an on state with or without existing state and + connections. +**/ + { + LOG_FUNC + switch(aStatus) + { + case EBTOn: + // Start-up Bluetooth + // Avoid from ON to ON + __ASSERT_DEBUG (iLastPowerState == EBTOff, Panic(EHCIPowerStateError)); + + iLastPowerState = aStatus; + //recovery the channels + iLinkMgrProtocol.LinkMuxer().ChannelsFree(iHCTLState); + + TRAPD(err, InitL(iLinkMgrProtocol.LocalDevice())); + // Hopefully this should just work it won't rename the device though + // since that is persisted + if (err) + { + HandlePowerStatusChange(EBTOff); + return; + } + else + { + // Reset the inquiry manager + iLinkMgrProtocol.InquiryMgr().SetHWState(CBTInquiryMgr::EIdle); + // Clear debug mode + iLinkMgrProtocol.SecMan().ClearDebugMode(); + } + break; + + case EBTOff: + // Reset the Command Queue + // Avoid from OFF to OFF + __ASSERT_DEBUG (iLastPowerState == EBTOn, Panic(EHCIPowerStateError)); + + iLastPowerState = aStatus; + + // Determine if we are waiting for a callback from the initialisation plugin + if ((iInitState == EPreReset) || (iInitState == EReset)) + { + // cancel initialisation callback if possible + if (iControllerInitAbortExtension != NULL) + { + iControllerInitAbortExtension->MciaeiAbortInitialisation(); + } + } + iInitState = EIdle; + + iCmdController->Reset(); + iOutstandingCommands.Reset(); + + // Ensure that no command or data messages are output by blocking all channels + // + iLinkMgrProtocol.LinkMuxer().ChannelsClosed(KHCITransportAllChannels); + iLinkMgrProtocol.Error(KErrHardwareNotAvailable); + // Reset UI + iLinkMgrProtocol.SetUIConnecting(EFalse); + iLinkMgrProtocol.SetUINumPhysicalLinks(0); + // The h/w CoD has been reset, so we need to clear our persistent value, to reflect this + iLinkMgrProtocol.ClearPendingLocalDeviceSettingsCod(); + + // Removes any pending AFH Channel Classification command + // and cancels timer. + // NB This ensures AFH host channel classification command blocking is + // not in place if power comes back on. + iAFHTimer->Reset(); + break; + + default: + Panic(EHCIUnknownPowerState); + break; + } + + // Successfully changed power state + iLinkMgrProtocol.UpdateLocalDevicePower(aStatus); + } + +TInt CHCIFacade::ChangeMode(TBTLinkMode aMode, THCIConnHandle aConnHandle) + { + LOG_FUNC + TRAPD(err, DoChangeModeL(aMode, aConnHandle)); + return err; + } + +void CHCIFacade::DoChangeModeL(TBTLinkMode aMode, THCIConnHandle aConnHandle) + { + LOG_FUNC + switch (aMode) + { + case ESniffMode: + { + SniffL(aConnHandle); + break; + } + case EParkMode: + { + ParkL(aConnHandle); + break; + } + case EHoldMode: + { + HoldL(aConnHandle); + break; + } + case EScatterMode: + case EActiveMode: + __ASSERT_DEBUG(0, Panic(EHCICommandBadArgument)); + break; + } + } + +TInt CHCIFacade::ExitMode(TBTLinkMode aMode, THCIConnHandle aConnHandle) + { + LOG_FUNC + TRAPD(err, DoExitModeL(aMode, aConnHandle)); + return err; + } + +void CHCIFacade::DoExitModeL(TBTLinkMode aMode, THCIConnHandle aConnHandle) + { + LOG_FUNC + switch (aMode) + { + case ESniffMode: + { + ExitSniffL(aConnHandle); + break; + } + case EParkMode: + { + ExitParkL(aConnHandle); + break; + } + // Not possile to prematurely exit hold mode + default: + { + break; + } + } + } + +void CHCIFacade::SetupFlowControlL(TFlowControlMode aMode) +/** + Set up flow control scheme to be used. + SetupFlowControlL should only be called once on init because it may leave. + + If TFlowControlMode is chosen to be EFlowControlFromHostControllerOnly or + ETwoWayFlowControlEnabled, then it is the responsibility of the HCI client + to issue the appropriate HostBufferSize(..) command. +*/ + { + LOG_FUNC + switch (aMode) + { + case ENoFlowControl: + { +#ifndef _DEBUG + Panic(ELinkMgrBadFlowControlSetInReleaseBuild); +#endif + break; + } + case EFlowControlToHostControllerOnly: + { + // the host will not tell the Controller about its buffers + + User::LeaveIfError( + SendInitialisationCommand(CReadBufferSizeCommand::NewL())); + break; + } + case EFlowControlFromHostControllerOnly: + { +#ifdef _DEBUG + CHCICommandBase *command = CHostBufferSizeCommand::NewL(KLinkMgrIncomingBufferSize, + KStackSCOBuffersSize, KStackACLBuffersNum, + KStackSCOBuffersNum); + + User::LeaveIfError(SendInitialisationCommand(command)); + + command = CSetControllerToHostFlowControlCommand::NewL(ETrue); + + User::LeaveIfError(SendInitialisationCommand(command)); + +#else + Panic(ELinkMgrBadFlowControlSetInReleaseBuild); +#endif + break; + } + case ETwoWayFlowControlEnabled: + { + CHCICommandBase *command = CHostBufferSizeCommand::NewL(KLinkMgrIncomingBufferSize, + KStackSCOBuffersSize, KStackACLBuffersNum, + KStackSCOBuffersNum); + + User::LeaveIfError(SendInitialisationCommand(command)); + + command = CSetControllerToHostFlowControlCommand::NewL(ETrue); + + User::LeaveIfError(SendInitialisationCommand(command)); + + break; + } + default: + Panic(ELinkMgrNoSuchFlowControlMode); + break; + } + } + + +// simple forwarding functions so that all stack usage to HCI is via Facade. + +TInt CHCIFacade::WriteACLData(const CHctlAclDataFrame& aFrame) const + { + LOG_FUNC + return iHctl->MhiWriteAclData(aFrame.HCTLPayload()); + } + +TInt CHCIFacade::WriteSCOData(const CHctlSynchronousDataFrame& aFrame) const + { + LOG_FUNC + return iHctl->MhiWriteSynchronousData(aFrame.HCTLPayload()); + } + +void CHCIFacade::FormatACLData(CHctlAclDataFrame& aFrame, THCIConnHandle aHandle, + TUint8 aOptions, const TDesC8& aData) const + { + LOG_FUNC + TUint8 flags = aOptions; + TAclPacketBoundaryFlag pbFlag = static_cast(flags & KPacketPBFlagMask); + TAclPacketBroadcastFlag bcFlag = static_cast((flags & KPacketBCFlagMask) >> KPacketPBtoBCFlagShift); + iDataFramer->MhdfFormatAclData(aFrame, aHandle, pbFlag, bcFlag, aData); + } + +void CHCIFacade::FormatSCOData(CHctlSynchronousDataFrame& aFrame, THCIConnHandle aHandle, + const TDesC8& aData) const + { + LOG_FUNC + iDataFramer->MhdfFormatSynchronousData(aFrame, aHandle, aData); + } + +void CHCIFacade::MhriStartHardReset() + { + LOG_FUNC + /* + Could have forced a... + HandlePowerStatusChange(EBTOff); + here - but instead let HCTL call this. + */ + iHardResetInitiator->MhriStartHardReset(); + } + +TUint16 CHCIFacade::ReadACLReportingInterval() const + { + LOG_FUNC + return 0; + } + +TUint16 CHCIFacade::ReadACLFramingOverhead() const + { + LOG_FUNC + return 0; + } + +#ifdef HOSTCONTROLLER_TO_HOST_FLOW_CONTROL +RMBufChain CHCIFacade::TakeInboundACLDataBufferFromPool(const THCIConnHandle& aForConnHandle) + { + LOG_FUNC + return iMBufPool->TakeBuffer(aForConnHandle); + } +#endif + +// MControllerInitialisationObserver +void CHCIFacade::McioPreResetCommandComplete(TInt aError) + { + LOG_FUNC + CHCICommandBase* command(NULL); + TInt err; + + // check that we are in the correct state + __ASSERT_ALWAYS(iInitState == EPreReset, Panic(EIncorrectStateOnPreResetCallback)); + iInitState = EResetting; + + if (aError != KErrNone) + { + // The initialisor plugin has failed pre reset initialisation + err = aError; + } + else + { + // Send Reset command + TRAP(err, command = CResetCommand::NewL()); + + if (err == KErrNone) + { + err = SendInitialisationCommand(command); + } + } + + if (err != KErrNone) + { + // Initialisation has failed. At this point all we can do is power down + // Bluetooth. This means that initialisation can be attempted again by + // turning on the power but this again could fail. + HandlePowerStatusChange(EBTOff); + } + } + +void CHCIFacade::DoSendPostResetCommandsL() + { + LOG_FUNC + User::LeaveIfError(SendInitialisationCommand(CReadLocalSupportedFeaturesCommand::NewL())); + User::LeaveIfError(SendInitialisationCommand(CReadBdaddrCommand::NewL())); + User::LeaveIfError(SendInitialisationCommand(CReadLocalVersionInfoCommand::NewL())); + +#ifdef HOSTCONTROLLER_TO_HOST_FLOW_CONTROL +#pragma message("Transport FlowControl = Two Way") + + SetupFlowControlL(ETwoWayFlowControlEnabled); + +#else +#pragma message("Transport FlowControl = None from HostController") + + SetupFlowControlL(EFlowControlToHostControllerOnly); + +#endif + + User::LeaveIfError(SendInitialisationCommand(CWriteConnectionAcceptTimeoutCommand::NewL(KHCIDefaultAcceptTimeout))); + User::LeaveIfError(SendInitialisationCommand(CWriteVoiceSettingCommand::NewL(KBTVoiceSetting))); + + if (iDeviceClass) + { + // Some phones never set their CoD in s/w, we don't want to + // overwrite their h/w settings + LOG1(_L("DoSendPostResetCommandsL - SetDeviceClassL (0x%x)"), iDeviceClass); + iLinkMgrProtocol.SetDeviceClassL(iDeviceClass); + } + else + { + // We want to update the P&S value with the h/w value + User::LeaveIfError(SendInitialisationCommand(CReadClassOfDeviceCommand::NewL())); + } + + TRAPD(err, iLinkMgrProtocol.SetInquiryAndPageScanningL()); + if(err != KErrNone && err != KErrNotSupported) + { + User::Leave(err); + } + } + +void CHCIFacade::McioPostResetCommandComplete(TInt aError) + { + LOG_FUNC + TInt err; + + // check that we are in the correct state + __ASSERT_ALWAYS(iInitState == EReset, Panic(EIncorrectStateOnPostResetCallback)); + iInitState = EPostReset; + + if (aError == KErrNone) + { + // Send post reset commands required by stack + TRAP(err, DoSendPostResetCommandsL()); + } + else + { + // The initialisor plugin has failed post reset initialisation + err = aError; + } + + if (err != KErrNone) + { + // Initialisation has failed. At this point all we can do is power down + // Bluetooth. This means that initialisation can be attempted again by + // turning on the power but this again could fail. + HandlePowerStatusChange(EBTOff); + } + } + +// MHCIDataObserver +void CHCIFacade::MhdoProcessAclData(THCIConnHandle aConnH, TAclPacketBoundaryFlag aBoundaryFlag, TAclPacketBroadcastFlag aBroadcastFlag, const TDesC8& aData) + { + LOG_FUNC + // Stack currently works with the old flag passing mechanism. So recombine into the old + // flag type before continuing. + TUint8 flag = (aBoundaryFlag | (aBroadcastFlag << KPacketPBtoBCFlagShift)) << KPacketPBBCFlagShift; + + LOG3(_L("Link [HCIFacade_Events.cpp]: CHCIFacade data received on handle %d, flags %d, length %d"), aConnH, flag, aData.Length()); + + iLinksMgr->ACLDataReceived(aConnH, flag, aData); + } + +void CHCIFacade::MhdoProcessSynchronousData(THCIConnHandle aConnH, TUint8 /*aReserved*/, const TDesC8& aData) + { + LOG_FUNC + iLinksMgr->SCODataReceived(aConnH, aData); + } + +// MHCTLChannelObserver +void CHCIFacade::MhcoChannelOpen(THCITransportChannel aChannels) +/** + The HCI has notified us that transport channels have become free +**/ + { + LOG_FUNC + LOG1(_L("Transport channels 0x%04x now free"), aChannels); + + // record the channel status + iHCTLState |= aChannels; + + if (iLinkMuxer) + { + iLinkMuxer->ChannelsFree(aChannels); //try to send on this channel + } + } + +void CHCIFacade::MhcoChannelClosed(THCITransportChannel aChannels) + { + LOG_FUNC + // record the channel status + iHCTLState &= (~aChannels) & KHCITransportAllChannels; + + // Tell the Muxer these channels are closed, if Muxer is ready + if (iLinkMuxer) + { + iLinkMuxer->ChannelsClosed(aChannels); + } + } + +// MControllerStateObserver +void CHCIFacade::McsoProcessPowerChange(TInt aError, TControllerChangeType aChangeType, TBTPowerState aState) + { + LOG_FUNC + if(aError!=KErrNone) + //Don't do anything - assume error implies no change took place + { + return; + } + + if(aChangeType==MControllerStateObserver::EBTNonFatalChange) + //Don't do anything - controller change will not affect us + { + return; + } + + // For now continue to use HandlePowerStatusChange as common code for power states and hard reset states + HandlePowerStatusChange(aState); + } + +void CHCIFacade::McsoProcessHardResetPhaseChange(TInt aError, TControllerChangeType aChangeType, TBTHardResetState aState) + { + LOG_FUNC + if(aError!=KErrNone) + { + //We may need to inform user somehow that reset has failed - depends on + //nature of error. What if error is KErrInUse? Should we set a timer + //and try again? + return; + } + + if(aChangeType==MControllerStateObserver::EBTNonFatalChange) + //Don't do anything - controller change will not affect us + { + return; + } + + switch(aState) + { + case EBTResetStarted: + HandlePowerStatusChange(EBTOff); + break; + case EBTResetComplete: + HandlePowerStatusChange(EBTOn); + break; + default: + Panic(EHCIUnknownHardResetState); + break; + } + } + +// MPhysicalLinksState - Simply forward the request to iLinksMgr +TInt CHCIFacade::MplsGetConnectionHandles(RHCIConnHandleArray& aConnectionHandles, TLinkType aLinkType) const + { + LOG_FUNC + if (iLinksMgr) + { + return iLinksMgr->GetConnectionHandles(aConnectionHandles, aLinkType); + } + return KErrNotReady; + } + +TInt CHCIFacade::MplsGetNumPendingHandles(TInt& aConnectionHandles, TLinkType aLinkType) const + { + LOG_FUNC + if (iLinksMgr) + { + return iLinksMgr->GetNumPendingHandles(aConnectionHandles, aLinkType); + } + return KErrNotReady; + } + +TInt CHCIFacade::MplsGetConnectionHandles(RHCIConnHandleArray& aConnectionHandles, TLinkType aLinkType, const TBTDevAddr& aBDAddr) const + { + LOG_FUNC + if (iLinksMgr) + { + return iLinksMgr->GetConnectionHandles(aConnectionHandles, aLinkType, aBDAddr); + } + return KErrNotReady; + } + +TInt CHCIFacade::MplsGetNumPendingHandles(TInt& aConnectionHandles, TLinkType aLinkType, const TBTDevAddr& aBDAddr) const + { + LOG_FUNC + if (iLinksMgr) + { + return iLinksMgr->GetNumPendingHandles(aConnectionHandles, aLinkType, aBDAddr); + } + return KErrNotReady; + } + +TInt CHCIFacade::MplsGetRemoteAddress(TBTDevAddr& aBDAddr, THCIConnHandle aConnectionHandle) const + { + LOG_FUNC + if (iLinksMgr) + { + return iLinksMgr->GetRemoteAddress(aBDAddr, aConnectionHandle); + } + return KErrNotReady; + } + +TInt CHCIFacade::MplsGetRemoteDeviceClass(TBTDeviceClass& aDeviceClass, const TBTDevAddr& aBDAddr) const + { + LOG_FUNC + if (iLinksMgr) + { + return iLinksMgr->GetRemoteDeviceClass(aDeviceClass, aBDAddr); + } + return KErrNotReady; + } + +TInt CHCIFacade::MplsGetRemoteSupportedFeatures(TBTFeatures& aRemoteSupportedFeatures, const TBTDevAddr& aBDAddr) const + { + LOG_FUNC + if (iLinksMgr) + { + return iLinksMgr->GetRemoteSupportedFeatures(aRemoteSupportedFeatures, aBDAddr); + } + return KErrNotReady; + } + +TInt CHCIFacade::MplsGetLinkPolicySettings(TLinkPolicy& aLinkPolicySettings, const TBTDevAddr& aBDAddr) const + { + LOG_FUNC + if (iLinksMgr) + { + return iLinksMgr->GetLinkPolicySettings(aLinkPolicySettings, aBDAddr); + } + return KErrNotReady; + } + +TInt CHCIFacade::MplsGetBasebandLinkState(TBTBasebandLinkState& aBasebandLinkState, const TBTDevAddr& aBDAddr) const + { + LOG_FUNC + if (iLinksMgr) + { + return iLinksMgr->GetBasebandLinkState(aBasebandLinkState, aBDAddr); + } + return KErrNotReady; + } + +// MLinkMuxNotifier +void CHCIFacade::TryToSend() + { + LOG_FUNC + if (iLinkMuxer) + { + iLinkMuxer->TryToSend(); + } + } + +// from MHCIClientUsage +void CHCIFacade::MhcuOpenClientReference() + { + iLinkMgrProtocol.LocalOpen(); + } + +void CHCIFacade::MhcuCloseClientReference() + { + iLinkMgrProtocol.LocalClose(); + } + +// Should only be tracking outstanding commands during cmdQ initialisation +TInt CHCIFacade::AddOutstandingCommandOpCode(THCIOpcode aOpCode) + { + LOG_FUNC + __ASSERT_ALWAYS(iInitState != EInitialised, Panic(EHCICmdQNotInitialising)); + TUint32 opcode = static_cast(aOpCode); + return iOutstandingCommands.Append(opcode); + } + +// Should only be tracking outstanding commands during cmdQ initialisation +TInt CHCIFacade::FindOutstandingCommandOpCode(THCIOpcode aOpCode) const + { + LOG_FUNC + __ASSERT_ALWAYS(iInitState != EInitialised, Panic(EHCICmdQNotInitialising)); + TUint32 opcode = static_cast(aOpCode); + return iOutstandingCommands.Find(opcode); + } + +// TBasebandPolicy + +void TBasebandPolicy::InitialPolicy(const TBasebandPolicyParams& aParams) + { + LOG_FUNC + iPageTimePolicy = aParams.iPageTimePolicy; + } + +TInt TBasebandPolicy::TryToChangePolicy(const TBasebandPolicyParams& aNewParams) + { + LOG_FUNC + // just deal with pagetime for now + TBasebandPageTimePolicy& current = iPageTimePolicy; + + switch (current) + { + case EPagingDontCare: + break; + + case EPagingNormal: + current = aNewParams.iPageTimePolicy; + break; + + case EPagingBestEffort: + //only change if user wants Quick paging + current = (aNewParams.iPageTimePolicy == EPagingQuick) ? aNewParams.iPageTimePolicy : current; + break; + + case EPagingQuick: + //only change if user wants Best effort paging + current = (aNewParams.iPageTimePolicy == EPagingBestEffort) ? aNewParams.iPageTimePolicy : current; + break; + + default: + LOG(_L("TBasebandPolicy: bogus policy requested for pagetimeout")); + return KErrArgument; + } + + LOG1(_L("TBasebandPolicy: now using page time policy %d"), aNewParams.iPageTimePolicy); + return (current == aNewParams.iPageTimePolicy) ? KErrNone : KErrInUse; + } + + +TBasebandPageTimePolicy TBasebandPolicy::PageTimePolicy() const + { + LOG_FUNC + return iPageTimePolicy; + } + +//class CAFHTimer +CAFHTimer* CAFHTimer::NewL(CHCIFacade& aParent) + { + LOG_STATIC_FUNC + CAFHTimer* self = new (ELeave) CAFHTimer(aParent); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + return self; + } + +void CAFHTimer::ConstructL() + { + LOG_FUNC + CTimer::ConstructL(); + CActiveScheduler::Add(this); + } + +CAFHTimer::CAFHTimer(CHCIFacade& aParent) +: CTimer(CActive::EPriorityStandard), iParent(aParent), iPending(EFalse) + { + LOG_FUNC + } + +void CAFHTimer::SetPending(const TBTAFHHostChannelClassification& aHCC) + { + LOG_FUNC + iHCC.Copy(aHCC); + iPending=ETrue; + } + +void CAFHTimer::RemovePendingHostChannelClassifications() + { + LOG_FUNC + iHCC.Reset(); + iPending=EFalse; + } + +void CAFHTimer::Reset() + { + LOG_FUNC + RemovePendingHostChannelClassifications(); + Cancel(); + } + +void CAFHTimer::RunL() + { + LOG_FUNC + if(iPending) + { + if(iStatus==KErrNone) + { + iParent.SetAFHHostChannelClassificationL(iHCC); + } + RemovePendingHostChannelClassifications(); + } + } + +TInt CAFHTimer::RunError(TInt /*aError*/) + { + LOG_FUNC + return KErrNone; + } + +#ifdef HOSTCONTROLLER_TO_HOST_FLOW_CONTROL + +CHostMBufPool* CHostMBufPool::NewL(CHCIFacade& aHCIFacade) + { + LOG_FUNC + CHostMBufPool* self = new (ELeave) CHostMBufPool(aHCIFacade); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +void CHostMBufPool::DeletePool(TSglQue& aQueue) + { + LOG_FUNC + TPoolBuffer* tmpItem = NULL; + TSglQueIter iter(aQueue); + while(iter) + { + tmpItem=iter++; + aQueue.Remove(*tmpItem); + delete tmpItem; + } + } + +CHostMBufPool::~CHostMBufPool() + { + LOG_FUNC + Cancel(); + DeletePool(iBufferPool); + DeletePool(iWaitingAllocPool); + } + +CHostMBufPool::CHostMBufPool(CHCIFacade& aHCIFacade) : + CActive(0),iHCIFacade(aHCIFacade),iBufferPool(_FOFF(TPoolBuffer,iLink)), + iWaitingAllocPool(_FOFF(TPoolBuffer,iLink)),iCurrAckHandle(KErrNotFound),iCurrCompletedPackets(0) + { + LOG_FUNC + } + +void CHostMBufPool::ConstructL() +/** +2nd phase constructor for the Host MBuf Pool. + +This method will attempt to reserve enough MBufs from the global pool +for bluetooth use. +@leave KErrNoMemory If the required number of MBufs couldn't be reserved +*/ + { + LOG_FUNC + LOG2(_L("CHostMBufPool: now reserving %d size %d MBufChains"),KStackACLBuffersNum,KLinkMgrIncomingBufferSize); + + for (TInt i=0;i<=KStackACLBuffersNum-1;i++) + { + TPoolBuffer* thisBuffer = new (ELeave) TPoolBuffer(); + CleanupStack::PushL(thisBuffer); + thisBuffer->iCurrentHandle=KErrNotFound; //we assert on this later + thisBuffer->iMBufChain.AllocL(KLinkMgrIncomingBufferSize); + iBufferPool.AddFirst(*thisBuffer); + CleanupStack::Pop(thisBuffer); + } + + CActiveScheduler::Add(this); + } + +void CHostMBufPool::DoCancel() + { + LOG_FUNC + iMBufRequester.Cancel(); + } + +RMBufChain CHostMBufPool::TakeBuffer(const THCIConnHandle& aConnHandle) +/** +Takes a buffer from the pool and schedules an asynchronous allocation +of the next buffer. Only when that allocation has succeeded will the host +controller be signalled with a host_number_of_completed_packets. Hence, +if we cannot allocate a buffer from the global MBuf pool, the host controller +will be flowed off and no data will be lost. +*/ + { + LOG_FUNC + TPoolBuffer* ready = iBufferPool.First(); + iBufferPool.Remove(*ready); + __ASSERT_DEBUG(!ready->iMBufChain.IsEmpty(),Panic(ELinkMgrHostControllerHasOverflowedHost)); + __ASSERT_DEBUG(ready->iCurrentHandle==KErrNotFound,Panic(ELinkMgrHostControllerHasOverflowedHost)); + ready->iCurrentHandle = aConnHandle; + + RMBufChain retChain; + retChain.Assign(ready->iMBufChain); + + if (IsActive()) + { + //This buffer will be reclaimed from the global pool + //after the one(s) we're currently trying to reclaim + LOG(_L("CHostMBufPool: TakeBuffer, buffer taken while alloc outstanding: queued alloc")); + iWaitingAllocPool.AddLast(*ready); + } + else + { + LOG(_L("CHostMBufPool: TakeBuffer, buffer taken")); + iBufferPool.AddLast(*ready); //NB the Controller cannot use this + //buffer until it is alloced as it will + //be flowed off. + iMBufRequester.Alloc(ready->iMBufChain,KLinkMgrIncomingBufferSize,iStatus); + SetActive(); + } + + return retChain; + } + +void CHostMBufPool::RunL() + { + LOG_FUNC + if (iStatus.Int()!=KErrNone) + { + LOG1(_L("Error! CHostMBufPool:: RunL %d"),iStatus.Int()); + __DEBUGGER(); + } + else + { + TPoolBuffer* justAllocd = iBufferPool.Last(); + + + if (iCurrAckHandle==KErrNotFound) + { + //This is the first completion we have ever seen + iCurrAckHandle=justAllocd->iCurrentHandle; + } + + TBool ackNow=((justAllocd->iCurrentHandle!=iCurrAckHandle)); + + if (!ackNow) + { + iCurrCompletedPackets++; + LOG2(_L("CHostMBufPool: CompletedPackets++ for conn: %d [->%d]"),justAllocd->iCurrentHandle,iCurrCompletedPackets); + + if (iCurrCompletedPackets>=KStackACLBuffersTideMarkNum) + { + ackNow=ETrue; + } + } + + if (ackNow) + { + TInt err=KErrNone; + + if (iCurrCompletedPackets>0) + { + LOG2(_L("CHostMBufPool: Sending HostNumberOfCompletedPackets for conn: %d [%d completed]"),iCurrAckHandle,iCurrCompletedPackets); + //Acknowledge the completed packets + TRAP(err, iHCIFacade.HostNumberOfCompletedPacketsL(iCurrAckHandle,iCurrCompletedPackets)); + //if this failed we probably couldn't alloc the memory for the command frame, + //the HC is still flowed off. + __ASSERT_DEBUG(err==KErrNone,Panic(ELinkMgrCouldNotSendHostNumberOfCompletedPackets)); + } + + iCurrCompletedPackets= (justAllocd->iCurrentHandle!=iCurrAckHandle) ? 1:0; + iCurrAckHandle=justAllocd->iCurrentHandle; + } + + justAllocd->iCurrentHandle=KErrNotFound; + + if (!iWaitingAllocPool.IsEmpty()) + { + TPoolBuffer* needsAlloc = iWaitingAllocPool.First(); + iBufferPool.AddLast(*needsAlloc); + iWaitingAllocPool.Remove(*needsAlloc); + iMBufRequester.Alloc(needsAlloc->iMBufChain,KLinkMgrIncomingBufferSize,iStatus); + SetActive(); + } + } + } + +#endif