diff -r 000000000000 -r c9bc50fca66e usbmgmt/usbmgr/device/classdrivers/acm/classimplementation/ecacm/src/BreakController.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbmgmt/usbmgr/device/classdrivers/acm/classimplementation/ecacm/src/BreakController.cpp Tue Feb 02 02:02:59 2010 +0200 @@ -0,0 +1,346 @@ +/* +* Copyright (c) 1997-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: +* +*/ + +#include "BreakController.h" +#include "CdcAcmClass.h" +#include "AcmUtils.h" +#include "HostPushedChangeObserver.h" +#include "AcmPanic.h" +#include "BreakObserver.h" +#include + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, "ECACM"); +#endif + +CBreakController::CBreakController(CCdcAcmClass& aParentAcm) +/** + * Constructor. + * + * @param aParentAcm Observer. + */ + : CActive(CActive::EPriorityStandard), + iBreakState(EInactive), + iParentAcm(aParentAcm) + { + CActiveScheduler::Add(this); + + // now populate the state machine that manages the transfers between + // the declared state values of TBreakState. + TInt oldBS; + TInt newBS; + + for ( oldBS = 0 ; oldBS < ENumStates ; oldBS++ ) + { + for ( newBS = 0 ; newBS < ENumStates ; newBS++ ) + { + StateDispatcher[oldBS][newBS] = ScInvalid; + } + } + + // Note that these state transitions are the simple states of the machine. + // Checking which entity is currently in control of the break (if any) is + // done elsewhere. + // Old State -> New State + // | | + StateDispatcher[EInactive ][ETiming ] = &ScSetTimer; + StateDispatcher[EInactive ][ELocked ] = &ScLocked; + + StateDispatcher[ETiming ][EInactive ] = &ScInactive; + StateDispatcher[ETiming ][ETiming ] = &ScSetTimer; + StateDispatcher[ETiming ][ELocked ] = &ScLocked; + + StateDispatcher[ELocked ][EInactive ] = &ScInactive; + StateDispatcher[ELocked ][ETiming ] = &ScSetTimer; + } + +CBreakController* CBreakController::NewL(CCdcAcmClass& aParentAcm) +/** + * Factory function. + * + * @param aParentAcm Parent. + * @return Ownership of a new CBreakController object. + */ + { + LOG_STATIC_FUNC_ENTRY + + CBreakController* self = new(ELeave) CBreakController(aParentAcm); + CleanupStack::PushL(self); + self->ConstructL(); + CLEANUPSTACK_POP(self); + return self; + } + +void CBreakController::ConstructL() +/** + * 2nd-phase constructor. + */ + { + LEAVEIFERRORL(iTimer.CreateLocal()); + } + +CBreakController::~CBreakController() +/** + * Destructor. + */ + { + LOG_FUNC + + Cancel(); + iTimer.Close(); + } + +void CBreakController::RunL() +/** + * Called by the active scheduler; handles timer completion. + */ + { + LOG_LINE + LOG_FUNC + + // check the status to see if the timer has matured, if so go straight + // to INACTIVE state (and publish new state) + if ( iStatus == KErrNone ) + { + // Use iRequester to turn the break off. This should not fail. + TInt err = BreakRequest(iRequester, EInactive); + static_cast(err); + __ASSERT_DEBUG(!err, + _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + } + } + +void CBreakController::DoCancel() +/** + * Called by the framework; handles cancelling the outstanding timer request. + */ + { + LOG_FUNC + + iTimer.Cancel(); + } + +TInt CBreakController::BreakRequest(TRequester aRequester, + TState aState, + TTimeIntervalMicroSeconds32 aDelay) +/** + * Make a break-related request. + * + * @param aRequester The entity requesting the break. + * @param aState The request- either a locked break, a timed break, or to + * make the break inactive. + * @param aDelay The time delay, only used for a timed break. + * @return Error, for instance if a different entity already owns the break. + */ + { + LOG_FUNC + LOGTEXT4(_L8("\taRequester = %d, aState = %d, aDelay = %d"), + aRequester, aState, aDelay.Int()); + + // Check the validity of the request. + if ( aRequester != iRequester && iRequester != ENone ) + { + LOGTEXT3(_L8("\t*** %d is in charge- cannot service request " + "from %d- returning KErrInUse"), iRequester, aRequester); + return KErrInUse; + } + + iRequester = aRequester; + + StateMachine(aState, aDelay); + + // Reset the owner member if relevant. + if ( aState == EInactive ) + { + iRequester = ENone; + } + + return KErrNone; + } + +void CBreakController::StateMachine(TState aBreakState, + TTimeIntervalMicroSeconds32 aDelay) +/** + * The generic BREAK state machine. + * + * @param aBreakState The state to go to now. + * @param aDelay Only used if going to a breaking state, the delay. + */ + { + LOG_FUNC + + TBool resultOK = EFalse; + + // Invoke the desired function. + PBFNT pfsDispatch = StateDispatcher[iBreakState][aBreakState]; + __ASSERT_DEBUG(pfsDispatch, + _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + resultOK = ( *pfsDispatch )(this, aDelay); + + if ( resultOK ) + { + LOGTEXT(_L8("\tbreak state dispatcher returned *SUCCESS*")); + + // check to see if the state change will need to result + // in a modification to the public state of BREAK which is + // either NO-BREAK == EBreakInactive + // or BREAK-ON == (anything else) + if( ( iBreakState != aBreakState ) + && ( + ( iBreakState == EInactive ) + || ( aBreakState == EInactive ) + ) + ) + { + Publish(aBreakState); + } + + // accept the state change ready for next time + iBreakState = aBreakState; + } + else + { + LOGTEXT(_L8("\tbreak state dispatcher returned *FAILURE*")); + } + } + +void CBreakController::Publish(TState aNewState) +/** + * Pointer-safe method to inform the (USB) Host and the Client of BREAK + * changes. + * + * @param aNewState The next state we're about to go to. + */ + { + LOG_FUNC + LOGTEXT2(_L8("\taNewState = %d"), aNewState); + + __ASSERT_DEBUG(aNewState != iBreakState, + _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + + // send the new BREAK state off to the USB Host + // this function is normally used so that ACMCSY can send client + // changes to RING, DSR and DCD to the USB Host, however we use + // it here to force it to refresh all states together with the + // new BREAK state. + // TODO: check return value + iParentAcm.SendSerialState( + iParentAcm.RingState(), + iParentAcm.DsrState(), + iParentAcm.DcdState()); + + // inform the ACM Class client that the BREAK signal has just changed, + // this should cause it to be toggled there. + if( iParentAcm.BreakCallback() ) + { + LOGTEXT(_L8("\tabout to call back break state change")); + iParentAcm.BreakCallback()->BreakStateChange(); + } + + // If we're going to the inactive state, and if the device is interested, + // we tell the MBreakObserver (ACM port) that the break has completed. + if ( aNewState == EInactive ) + { + LOGTEXT(_L8("\tnew state is break-inactive")); + if ( iRequester == EDevice ) + { + LOGTEXT(_L8("\tdevice is interested")); + if( iParentAcm.BreakCallback() ) + { + LOGTEXT(_L8("\tabout to call back break completion")); + iParentAcm.BreakCallback()->BreakRequestCompleted(); + } + } + + // We just got to break-inactive state. Blank the requester record. + iRequester = ENone; + } + } + +/** + * +----------------------------------------------+ + * | Set of state-machine functions to be used in | + * | a two-dimensional dispatcher matrix | + * +----------------------------------------------+ + */ + +TBool CBreakController::ScInvalid(CBreakController *aThis, + TTimeIntervalMicroSeconds32 aDelay) + { + LOG_STATIC_FUNC_ENTRY + + static_cast(aThis); // remove warning + static_cast(aDelay); // remove warning + + return( EFalse ); + } + +TBool CBreakController::ScInactive(CBreakController *aThis, + TTimeIntervalMicroSeconds32 aDelay) + { + LOG_STATIC_FUNC_ENTRY + + static_cast(aDelay); // remove warning + + // this may have been called while a BREAK is already current, cancel the + // timer. + aThis->Cancel(); + + aThis->iParentAcm.SetBreakActive(EFalse); + + return( ETrue ); + } + +TBool CBreakController::ScSetTimer(CBreakController *aThis, + TTimeIntervalMicroSeconds32 aDelay) + { + LOG_STATIC_FUNC_ENTRY + + // don't try to set any delay if the caller wants something impossible + if ( aDelay.Int() <= 0 ) + { + return( EFalse ); + } + + aThis->Cancel(); // in case we're already active. + + aThis->iTimer.After(aThis->iStatus, aDelay); + aThis->SetActive(); + + aThis->iParentAcm.SetBreakActive(ETrue); + + return( ETrue ); + } + +TBool CBreakController::ScLocked(CBreakController *aThis, + TTimeIntervalMicroSeconds32 aDelay) + { + LOG_STATIC_FUNC_ENTRY + + static_cast(aDelay); // remove warning + + // this may have been called while a BREAK is already current, so cancel + // the timer. + aThis->Cancel(); + + aThis->iParentAcm.SetBreakActive(ETrue); + + return( ETrue ); + } + +// +// End of file