usbmgmt/usbmgr/device/classdrivers/acm/classimplementation/ecacm/src/BreakController.cpp
changeset 0 c9bc50fca66e
child 15 f92a4f87e424
--- /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 <usb/usblogger.h>
+
+#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<void>(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<void>(aThis); // remove warning 
+	static_cast<void>(aDelay); // remove warning
+	
+	return( EFalse );
+	}
+
+TBool CBreakController::ScInactive(CBreakController *aThis, 
+								   TTimeIntervalMicroSeconds32 aDelay)
+	{
+	LOG_STATIC_FUNC_ENTRY
+	
+	static_cast<void>(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<void>(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