/*
* 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:
* Implements a Session of a Symbian OS server for the RUsb API
*
*/

/**
 @file
*/

#include <usb/usblogger.h>
#include "CUsbSession.h"
#include "CUsbDevice.h"
#include "CUsbServer.h"

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
#include "CUsbOtg.h"
#include "cusbhost.h"
#endif // SYMBIAN_ENABLE_USB_OTG_HOST_PRIV

#include <usbstates.h>
#include <usberrors.h>

#include <usb/usbshared.h>
#include "CPersonality.h"
#include "rusb.h"
#include "UsbSettings.h"

#ifdef __FLOG_ACTIVE
_LIT8(KLogComponent, "USBSVR");
#endif

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
CUsbSession* CUsbSession::iCtlSession = NULL;
#endif // SYMBIAN_ENABLE_USB_OTG_HOST_PRIV

/**
 * Construct a Symbian OS session object.
 *
 * @internalComponent
 * @param	aServer		Service the session will be a member of
 *
 * @return	A new CUsbSession object
 */
CUsbSession* CUsbSession::NewL(CUsbServer* aServer)
	{
	LOG_STATIC_FUNC_ENTRY

	//this class has moved away from standard NewL() semantics
	//and now uses the virtual CSession2::CreateL() function
	//[instead of ConstructL()] which is called by CServer2
	//and finalises the construction of the session
	return (new (ELeave) CUsbSession(aServer));
	}


/**
 * Constructor.
 *
 * @internalComponent
 * @param	aServer	Service the session will be a member of
 */
CUsbSession::CUsbSession(CUsbServer* aServer)
	: iUsbServer(aServer)
	{
	LOG_FUNC

	iUsbServer->IncrementSessionCount();
	}


/**
 * Destructor.
 */
CUsbSession::~CUsbSession()
	{
	LOG_FUNC

	LOGTEXT2(_L8("About to Device().DeRegisterObserver(%08x"),this);
	iUsbServer->Device().DeRegisterObserver(*this);

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
#ifndef __OVER_DUMMYUSBDI__
	LOGTEXT2(_L8("About to Otg().DeRegisterObserver(%08x"),this);
	iUsbServer->Otg().DeRegisterObserver(*this);
#endif

	LOGTEXT2(_L8("About to Host().DeRegisterObserver(%08x"),this);
	iUsbServer->Host().DeregisterObserver(*this);

	if ( iCtlSession && (iCtlSession == this) )
		{
		iCtlSession = NULL;
		}
#endif // SYMBIAN_ENABLE_USB_OTG_HOST_PRIV

	LOGTEXT(_L8("About to iUsbServer->DecrementSessionCount()"));
	iUsbServer->DecrementSessionCount();
	}


/**
 * Called when a message is received from the client.
 *
 * @param	aMessage	Message received from the client
 */
void CUsbSession::ServiceL(const RMessage2& aMessage)
	{
	LOG_FUNC

	DispatchMessageL(aMessage);
	}

/**
 * Handles 2nd Phase Construction.  Implementation of the virtual method defined in CSession2 and called from
 * CServer2::DoConnectL() which executes when the client makes a connection request through CServer2::Connect().  If
 * a Leave occurs at any point the CUsbSession object is cleaned up in CServer2::DoConnect().
 */
void CUsbSession::CreateL()
	{
	LOG_FUNC

	// This code originally existed in the typical non-virtual ConstructL() method.
	// However it was moved to this method for minor optimisation reasons [three less
	// function calls and several lines less code in the NewL() method].

	iPersonalityCfged = iUsbServer->Device().isPersonalityCfged();

    LOGTEXT(_L8("Registering Device Observer\n"));
	iUsbServer->Device().RegisterObserverL(*this);

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
#ifndef __OVER_DUMMYUSBDI__
	LOGTEXT(_L8("Registering OTG Observer\n"));
	iUsbServer->Otg().RegisterObserverL(*this);
#endif

	LOGTEXT(_L8("Registering HOST Observer\n"));
	iUsbServer->Host().RegisterObserverL(*this);
#endif // SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	}

/**
 * Called by CUsbDevice when the service state changes.
 * CUsbSession is an observer of the device.
 *
 * @param	aLastError	The last error happened
 * @param	aOldState	The device's service state just before it changed
 * @param	aNewState	The device's new and current service state
 */
void CUsbSession::UsbServiceStateChange(TInt aLastError, TUsbServiceState aOldState,
										TUsbServiceState aNewState)
	{
	LOG_FUNC
	LOGTEXT3(_L8("    aOldState=0x%X, aNewState=0x%X"), aOldState, aNewState);
	(void) aOldState; // a-void build warning in UREL

	// Note that it's possible to have both a start and a stop outstanding!

	if (iStartOutstanding)
		{
		HandleServiceStateChangeWhileStarting(aLastError, aNewState);
		}

	if (iStopOutstanding)
		{
		HandleServiceStateChangeWhileStopping(aLastError, aNewState);
		}

	// Check whether we have an observer of the service state.

	if (iServiceObserverOutstanding)
		{
		TPckg<TUint32> pckg(aNewState);
		iServiceObserverOutstanding = EFalse;
		const TInt err = iServiceObserverMessage.Write(0, pckg);
		iServiceObserverMessage.Complete(err);
		}
	}

/**
 * Handles a state change while a start request is currently outstanding.
 *
 * @param aLastError The last error happened
 * @param aNewState The state we've moved to
 */
void CUsbSession::HandleServiceStateChangeWhileStarting(TInt aLastError,
												 TUsbServiceState aNewState)
	{
	LOG_FUNC

	switch (aNewState)
		{
	case EUsbServiceStarted:
		LOGTEXT(_L8("    Completing Start successfully"));

		// If the user has tried to cancel the start, they're too late!
		if (iCancelOutstanding)
			{
			LOGTEXT(_L8("    Completing cancel request with KErrNone"));
			iCancelOutstanding = EFalse;
			iCancelMessage.Complete(KErrNone);
			}

		iStartMessage.Complete(KErrNone);
		iStartOutstanding = EFalse;
		break;

	case EUsbServiceIdle:
		LOGTEXT2(_L8("    Completing Start with error=%d"), aLastError);

		// If there hasn't actually been an error, but we're in an unexpected
		// state now, that means that this client cancelled the request, or
		// another client stopped the service.
		if (aLastError == KErrNone)
			{
			// If there's a cancel outstanding, then that message succeeded, but
			// the start message should be completed with KErrCancel.
			if (iCancelOutstanding)
				{
				LOGTEXT(_L8("    Completing original message with KErrCancel"));
				iCancelOutstanding = EFalse;
				iCancelMessage.Complete(KErrNone);
				iStartMessage.Complete(KErrCancel);
				}
			else
				{
				iStartMessage.Complete(KErrUsbServiceStopped);
				}
			}
		else
			{
			// There's been some kind of error, so complete the original message
			// with the right error code.
			if (iCancelOutstanding)
				{
				iCancelOutstanding = EFalse;
				iCancelMessage.Complete(KErrNone);
				}
			iStartMessage.Complete(aLastError);
			}

		iStartOutstanding = EFalse;
		break;

	default:
		break;
		}
	}

/**
 * Handles a state change while a stop request is currently outstanding.
 *
 * @param aLastError The last error happened
 * @param aNewState The state we've moved to
 */
void CUsbSession::HandleServiceStateChangeWhileStopping(TInt aLastError,
												 TUsbServiceState aNewState)
	{
	LOG_FUNC

	switch (aNewState)
		{
	case EUsbServiceStarted:
		LOGTEXT2(_L8("    Completing Stop with error=%d"), aLastError);

		// If there hasn't actually been an error, but we're in an unexpected
		// state now, that means that this client cancelled the request, or
		// another client has started the service.
		if (aLastError == KErrNone)
			{
			// If there's a cancel outstanding, then that message succeeded, but
			// the stop message should be completed with KErrCancel.
			if (iCancelOutstanding)
				{
				LOGTEXT(_L8("    Completing original message with KErrCancel"));
				iCancelOutstanding = EFalse;
				iCancelMessage.Complete(KErrNone);
				iStopMessage.Complete(KErrCancel);
				}
			else
				{
				iStopMessage.Complete(KErrUsbServiceStarted);
				}
			}
		else
			{
			// There's been some kind of error, so complete the original message
			// with the right error code.
			if (iCancelOutstanding)
				{
				iCancelOutstanding = EFalse;
				iCancelMessage.Complete(KErrNone);
				}
			iStopMessage.Complete(aLastError);
			}

		iStopOutstanding = EFalse;
		break;

	case EUsbServiceIdle:
		LOGTEXT(_L8("    Completing Stop with KErrNone"));

		// If the user has tried to cancel the stop, they're too late!
		if (iCancelOutstanding)
			{
			LOGTEXT(_L8("    Completing cancel request with KErrNone"));
			iCancelOutstanding = EFalse;
			iCancelMessage.Complete(KErrNone);
			}

		iStopMessage.Complete(KErrNone);
		iStopOutstanding = EFalse;
		break;

	default:
		break;
		}
	}

/**
 * Called by CUsbDevice when it state change. CUsbSession is an observer of
 * the device. If the client has an Observer outstanding then complete it,
 * otherwise put it in a circular queue.
 *
 * @internalComponent
 * @param	aLastError	The last error happened
 * @param	aOldState	The device's state just before it changed
 * @param	aNewState	The device's new and current state
 */
void CUsbSession::UsbDeviceStateChange(TInt /*aLastError*/, TUsbDeviceState /*aOldState*/,
									   TUsbDeviceState aNewState)
	{
	LOG_FUNC

	// can we bypass the queue?
 	if ((iDeviceObserverOutstanding) && (iDevStateQueueHead == iDevStateQueueTail))
		{
		if ((iDeviceObserverMessage.Int0() & aNewState) ||
			(aNewState == EUsbDeviceStateUndefined))
			{
			TPckg<TUint32> pckg(aNewState);

			iNotifiedDevState = aNewState;

			iDeviceObserverOutstanding = EFalse;
			const TInt err = iDeviceObserverMessage.Write(1, pckg);
			iDeviceObserverMessage.Complete(err);
			}
		}
	else if (iObserverQueueEvents)
		{
		TBool addToQueue = ETrue;

 		// Search queue for similar event, truncate event queue if found
 		if (aNewState == EUsbDeviceStateUndefined)
 			{
 			// erase Event queue, just want this event - not interested in how we got here
 			iDevStateQueueTail = iDevStateQueueHead;

 			// if this is also the event mostly recently notified then don't bother to queue it
 			if(aNewState == iNotifiedDevState)
 				addToQueue = EFalse;
 			}
 		else
 			{
 			TInt queuePtr = iDevStateQueueTail;

 			// search forward from tail to head
 			while (queuePtr != iDevStateQueueHead)
 				{
 				if (aNewState == iDeviceStateQueue[queuePtr])
 					{
 					// Event is already queued; discard the duplicate and in-between events
 					LOGTEXT3(_L8("--- collapsing queue head (%d, %d)"),
						iDevStateQueueHead,
						(queuePtr + 1) % KDeviceStatesQueueSize);

 					// queue head moved to position following the match
 					iDevStateQueueHead = (queuePtr + 1) % KDeviceStatesQueueSize;
 					addToQueue = EFalse;
 					break;
 					}

 				// work our way through queue
 				queuePtr = (queuePtr + 1) % KDeviceStatesQueueSize;
 				}
 			}

 		// still want to add to queue?
 		if (addToQueue)
 			{
 			// add event to head of queue
 			iDeviceStateQueue[iDevStateQueueHead] = aNewState;
 			iDevStateQueueHead = (iDevStateQueueHead + 1) % KDeviceStatesQueueSize;
 			LOGTEXT3(_L8("+++ addqueue (%d, %d)"), iDevStateQueueHead,
				iDevStateQueueTail);
 			}

 		// UsbDeviceDequeueEvent() will read from queue when RegisterObserver()
		// is next called.
		}
	}

/**
 * Dequeues an event and completes the observer's request with it.
 */
void CUsbSession::UsbDeviceDequeueEvent()
 	{
	LOG_FUNC

 	// Work our way through the queue, until we reach the end
 	// OR we find an event the current observer wants.
 	while ((iDeviceObserverOutstanding) && (iDevStateQueueHead != iDevStateQueueTail))
 		{
 		// inform the observer of state changes they are interested in AND
 		// if the cable is pulled out (EUsbDeviceStateUndefined)
 		TUsbDeviceState newState = iDeviceStateQueue[iDevStateQueueTail];

 		// advance tail towards the head
 		iDevStateQueueTail = (iDevStateQueueTail + 1) % KDeviceStatesQueueSize;

 		// is this state one the Observer wants?
 		if ((iDeviceObserverMessage.Int0() & newState) ||
			(newState == EUsbDeviceStateUndefined))
 			{
 			TPckg<TUint32> pckg(newState);

 			iNotifiedDevState = newState;

 			LOGTEXT3(_L8(">>> dequeued event #%d (0x%x)"), iDevStateQueueTail, newState);

  			iDeviceObserverOutstanding = EFalse;
			const TInt err = iDeviceObserverMessage.Write(1, pckg);
 			iDeviceObserverMessage.Complete(err);
 			break;
   			}
   		}
   	}

/**
 * Handles the request (in the form of a the message) received from the client
 *
 * @internalComponent
 * @param	aMessage	The received message
 */
void CUsbSession::DispatchMessageL(const RMessage2& aMessage)
	{
	LOG_FUNC

	TBool complete = ETrue;
	TInt ret = KErrNone;

	LOGTEXT2(_L8("CUsbSession::DispatchMessageL(): func# %d"), aMessage.Function());

	switch (aMessage.Function())
		{
	case EUsbStart:
		ret = StartDeviceL(aMessage, complete);
		break;
	case EUsbStop:
		ret = StopDeviceL(aMessage, complete);
		break;
	case EUsbGetCurrentState:
		ret = GetCurrentServiceState(aMessage);
		break;
	case EUsbGetCurrentDeviceState:
		ret = GetCurrentDeviceState(aMessage);
		break;
	case EUsbRegisterServiceObserver:
		ret = RegisterServiceObserver(aMessage, complete);
		break;
	case EUsbRegisterObserver:
		ret = RegisterDeviceObserver(aMessage, complete);
		break;
	case EUsbStartCancel:
		ret = StartCancel(aMessage, complete);
		break;
	case EUsbStopCancel:
		ret = StopCancel(aMessage, complete);
		break;
	case EUsbCancelServiceObserver:
		ret = DeRegisterServiceObserver();
		break;
	case EUsbCancelObserver:
		ret = DeRegisterDeviceObserver();
		break;
	case EUsbTryStart:
		ret = TryStartDeviceL(aMessage, complete);
		break;
	case EUsbTryStop:
		ret = TryStopDeviceL(aMessage, complete);
		break;
	case EUsbCancelInterest:
		ret = CancelInterest(aMessage);
		break;
	case EUsbGetCurrentPersonalityId:
		ret = GetCurrentPersonalityId(aMessage);
		break;
	case EUsbGetSupportedClasses:
		ret = GetSupportedClasses(aMessage);
		break;
	case EUsbGetPersonalityIds:
		ret = GetPersonalityIds(aMessage);
		break;
	case EUsbGetDescription:
		ret = GetDescription(aMessage);
		break;
	case EUsbGetDetailedDescription:
		ret = GetDetailedDescription(aMessage);
		break;
	case EUsbGetPersonalityProperty:
		ret = GetPersonalityProperty(aMessage);
		break;
	case EUsbClassSupported:
		ret = ClassSupported(aMessage);
		break;

#ifdef _DEBUG
	// Heap failure debug APIs.

	case EUsbDbgMarkHeap:
		LOGTEXT(_L8("Marking heap"));
		__UHEAP_MARK;
		break;
	case EUsbDbgCheckHeap:
		LOGTEXT2(_L8("Checking heap (expecting %d cells)"), aMessage.Int0());
		__UHEAP_CHECK(aMessage.Int0());
		break;
	case EUsbDbgMarkEnd:
		LOGTEXT2(_L8("End of marking heap (expecting %d cells)"), aMessage.Int0());
		__UHEAP_MARKENDC(aMessage.Int0());
		break;
	case EUsbDbgFailNext:
		{
		LOGTEXT2(_L8("Simulating failure after %d allocation(s)"), aMessage.Int0());
		if (aMessage.Int0() == 0)
			__UHEAP_RESET;
		else
			__UHEAP_FAILNEXT(aMessage.Int0());
		}
		break;
	case EUsbDbgAlloc:
		{
		ret = KErrNone;
#ifdef _DEBUG
		LOGTEXT(_L8("\tallocate on the heap"));
		TInt* x = NULL;
		TRAP(ret, x = new(ELeave) TInt);
		delete x;
#endif // _DEBUG
		}
		break;

#endif

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	case EUsbSetCtlSessionMode:
		ret = SetCtlSessionMode(aMessage);
		break;
	case EUsbRegisterMessageObserver:
		ret = RegisterMsgObserver(aMessage, complete);
		break;
	case EUsbCancelMessageObserver:
		ret = DeRegisterMsgObserver();
		break;
#ifndef __OVER_DUMMYUSBDI__
	case EUsbBusRequest:
		ret = BusRequest();
		break;
	case EUsbBusRespondSrp:
		ret = BusRespondSrp();
		break;
	case EUsbBusClearError:
		ret = BusClearError();
		break;
	case EUsbBusDrop:
		ret = BusDrop();
		break;
#else
	case EUsbBusRequest:
	case EUsbBusRespondSrp:
	case EUsbBusClearError:
	case EUsbBusDrop:
		ret = KErrNone;
		break;
#endif
	case EUsbRegisterHostObserver:
		ret = RegisterHostObserver(aMessage, complete);
		break;
	case EUsbCancelHostObserver:
		ret = DeRegisterHostObserver();
		break;
	case EUsbEnableFunctionDriverLoading:
		ret = EnableFunctionDriverLoading();
		break;
	case EUsbDisableFunctionDriverLoading:
		ret = DisableFunctionDriverLoading();
		break;
	case EUsbGetSupportedLanguages:
		ret = GetSupportedLanguages(aMessage);
		break;
	case EUsbGetManufacturerStringDescriptor:
		ret = GetManufacturerStringDescriptor(aMessage);
		break;
	case EUsbGetProductStringDescriptor:
		ret = GetProductStringDescriptor(aMessage);
		break;
	case EUsbGetOtgDescriptor:
		ret = GetOtgDescriptor(aMessage);
		break;
	case EUsbRequestSession:
		ret = RequestSession();
		break;
#else // !SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	case EUsbSetCtlSessionMode:
	case EUsbRegisterMessageObserver:
	case EUsbCancelMessageObserver:
	case EUsbBusRequest:
	case EUsbBusRespondSrp:
	case EUsbBusClearError:
	case EUsbBusDrop:
	case EUsbRegisterHostObserver:
	case EUsbCancelHostObserver:
	case EUsbEnableFunctionDriverLoading:
	case EUsbDisableFunctionDriverLoading:
	case EUsbGetSupportedLanguages:
	case EUsbGetManufacturerStringDescriptor:
	case EUsbGetProductStringDescriptor:
	case EUsbGetOtgDescriptor:
	case EUsbRequestSession:
		ret = KErrNotSupported;
		break;
#endif // SYMBIAN_ENABLE_USB_OTG_HOST_PRIV

	default:
		LOGTEXT2(_L8("Illegal IPC argument(%d) - Panicking Client..."), aMessage.Function());
		aMessage.Panic(KUsbCliPncCat, EUsbPanicIllegalIPC);
		complete = EFalse;
		break;
		}

	if (complete)
		aMessage.Complete(ret);
	}


/**
 * Client request to start the device.
 *
 * @param	aMessage	Message received from the client
 * @param	aComplete	Whether the request is complete or not
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::StartDeviceL(const RMessage2& aMessage, TBool& aComplete)
	{
	LOG_FUNC

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	// Only 'control' session is allowed to start USB support
	if ( !iSessionCtlMode )
		{
		return KErrAccessDenied;
		}
#endif

	if (iStartOutstanding)
		return KErrInUse;

#ifndef __OVER_DUMMYUSBDI__

	// If the service is idle or stopping, then we just need to start it.
	// If it's starting (ie. by another client), then we need to perform nothing
	// but wait for the start to complete.
	// If it's already started, we just return immediately.
	TUsbServiceState state = iUsbServer->Device().ServiceState();

	if ((state == EUsbServiceIdle) || (state == EUsbServiceStopping))
		{
#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
		iUsbServer->Host().StartL();
#endif
		iUsbServer->Device().StartL();

		aComplete = EFalse;
		iStartMessage = aMessage;
		iStartOutstanding = ETrue;
		}
	else if (state == EUsbServiceStarting)
		{
		aComplete = EFalse;
		iStartMessage = aMessage;
		iStartOutstanding = ETrue;
		}

	return KErrNone;

#else
	// pretend that the server is in Started state
#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	iUsbServer->Host().StartL();
#endif
	iStartOutstanding = EFalse;
	aMessage.IsNull();
	aComplete = ETrue;
	return KErrNone;

#endif
	}

/**
 * Client request to stop the device.
 *
 * @param	aMessage	Message received from the client
 * @param	aComplete	Whether the request is complete or not
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::StopDeviceL(const RMessage2& aMessage, TBool& aComplete)
    {
	LOG_FUNC

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	// Only 'control' session is allowed to stop USB support
	if ( !iSessionCtlMode )
		{
		return KErrAccessDenied;
		}
#endif

	if (iStopOutstanding)
		{
		return KErrInUse;
		}

#ifndef __OVER_DUMMYUSBDI__

	// Only do anything if the service isn't currently idle. If it is, we just
	// need to complete the user's request immediately.
	if (iUsbServer->Device().ServiceState() != EUsbServiceIdle)
		{
		iUsbServer->Device().Stop();
#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
		iUsbServer->Host().Stop();
#endif

		aComplete = EFalse;
		iStopMessage = aMessage;
		iStopOutstanding = ETrue;
		}

	return KErrNone;

#else
	// pretend that the server is in Started state
#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	iUsbServer->Host().Stop();
#endif
	aComplete = ETrue;
	aMessage.IsNull();
	iStopOutstanding = EFalse;
	return KErrNone;

#endif
    }

/**
 * Cancel the pending start operation. Note that this can just be implemented
 * as a synchronous stop, if the start operation is pending. However, we have to
 * retain the cancel message, so we can complete it when the stop completes.
 *
 * @param aMessage The message from the client
 * @param aComplete Whether the message is complete or not
 * @return Always KErrNone
 */
TInt CUsbSession::StartCancel(const RMessage2& aMessage, TBool& aComplete)
	{
	LOG_FUNC

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	// Only 'control' session is allowed to cancel outstaning start request
	if ( !iSessionCtlMode )
		{
		return KErrAccessDenied;
		}
#endif

	if (!iStartOutstanding)
		return KErrNone;

	aComplete = EFalse;
	iCancelMessage = aMessage;
	iCancelOutstanding = ETrue;

	if (iUsbServer->Device().ServiceState() != EUsbServiceIdle)
		{
		iUsbServer->Device().Stop();
#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
		iUsbServer->Host().Stop();
#endif
		}

	return KErrNone;
	}

/**
 * Cancel the pending stop operation. Note that this can just be implemented as
 * a synchronous start, if the stop operation is pending. However, we have to
 * retain the cancel message, so we can complete it when the start completes.
 *
 * @param aMessage The message from the client
 * @param aComplete Whether the message is complete or not
 * @return KErrNone on success, otherwise standard error codes
 */
TInt CUsbSession::StopCancel(const RMessage2& aMessage, TBool& aComplete)
	{
	LOG_FUNC

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	// Only 'control' session is allowed to cancel outstaning stop request
	if ( !iSessionCtlMode )
		{
		return KErrAccessDenied;
		}
#endif

	if (!iStopOutstanding)
		{
		return KErrNone;
		}

	aComplete = EFalse;
	iCancelMessage = aMessage;
	iCancelOutstanding = ETrue;

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	TRAPD(errHost,iUsbServer->Host().StartL());
	if (errHost != KErrNone)
		return errHost;
#endif
	TRAPD(err, iUsbServer->Device().StartL());
	return err;
	}

/**
 * Client request to observe the device (for state changes).
 * Asks the device to register the session as an observer.
 * Assures initialisation/dequeueing of Event queue.
 *
 * @param	aMessage	Message received from the client
 * @param	aComplete	set to true to complete the request
 *
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::RegisterDeviceObserver(const RMessage2& aMessage, TBool& aComplete)
	{
	LOG_FUNC

	if (iDeviceObserverOutstanding)
		{
		return KErrInUse;
		}

	iDeviceObserverMessage = aMessage;
	iDeviceObserverOutstanding = ETrue;
	aComplete = EFalse;

 	if (iObserverQueueEvents == EFalse)
	 	{
 		// This is the first observer after c'tor or DeregisterObserver(),
 		// so zap the device event queue.
 		LOGTEXT(_L8("    Reset Device Event Queue"));
 		iDevStateQueueHead = 0;
 		iDevStateQueueTail = 0;
 		iObserverQueueEvents = ETrue;
	 	}
 	else if (iDevStateQueueHead != iDevStateQueueTail)
	 	{
 		// event(s) queued, we can de-queue one now
 		UsbDeviceDequeueEvent();
	 	}

	return KErrNone;
	}

/**
 * Client request to observe the service (for state changes)
 * Asks the device to register the session as an observer
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @param	aComplete	set to true to complete the request
 *
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::RegisterServiceObserver(const RMessage2& aMessage, TBool& aComplete)
	{
	LOG_FUNC

	if (iServiceObserverOutstanding)
		{
		return KErrInUse;
		}

	iServiceObserverMessage = aMessage;
	iServiceObserverOutstanding = ETrue;
	aComplete = EFalse;
	return KErrNone;
	}


/**
 * Client request to fetch the current service state of the device
 * Asks the device for its current service state
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 *
 * @return	Any errors that occurred or KErrNone
 */
TInt CUsbSession::GetCurrentServiceState(const RMessage2& aMessage)
	{
	LOG_FUNC

	TUsbServiceState state = iUsbServer->Device().ServiceState();
	LOGTEXT2(_L8("\tstate = %d"), state);
	TPckg<TUint32> pckg(state);
	return aMessage.Write(0, pckg);
	}

/**
 * Client request to fetch the current device state of the device
 * Asks the device for its current device state
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 *
 * @return	Any errors that occurred or KErrNone
 */
TInt CUsbSession::GetCurrentDeviceState(const RMessage2& aMessage)
	{
	LOG_FUNC

	TUsbDeviceState state = iUsbServer->Device().DeviceState();
	LOGTEXT2(_L8("\tstate = %d"), state);
	TPckg<TUint32> pckg(state);
	return aMessage.Write(0, pckg);
	}


/**
 * Deregister the client as an observer of device state changes. Note that we don't
 * deregister ourselves as an observer, because we need to be notified when the
 * device state changes, so we can complete Start and Stop requests.
 *
 * @return Always KErrNone
 */
TInt CUsbSession::DeRegisterDeviceObserver()
	{
	LOG_FUNC

	if (!iDeviceObserverOutstanding)
		{
		return KErrNone;
		}

	iDeviceObserverOutstanding = EFalse;
	iDeviceObserverMessage.Complete(KErrCancel);

	// client doesn't need events queuing any more
 	iObserverQueueEvents = EFalse;

	return KErrNone;
	}

/**
 * Deregister the client as an observer of service state changes. Note that we don't
 * deregister ourselves as an observer, because we need to be notified when the
 * service state changes, so we can complete Start and Stop requests.
 *
 * @return Always KErrNone
 */
TInt CUsbSession::DeRegisterServiceObserver()
	{
	LOG_FUNC

	if (!iServiceObserverOutstanding)
		{
		return KErrNone;
		}

	iServiceObserverOutstanding = EFalse;
	iServiceObserverMessage.Complete(KErrCancel);
	return KErrNone;
	}

/**
 * Try starting the USB device.
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @param	aComplete	set to true to complete the request
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::TryStartDeviceL(const RMessage2& aMessage, TBool& aComplete)
	{
	LOG_FUNC

#ifndef __OVER_DUMMYUSBDI__

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	// Only 'control' session is allowed to start USB support
	if ( !iSessionCtlMode )
		{
		return KErrAccessDenied;
		}
#endif

	if (!iPersonalityCfged)
	{
	return KErrNotSupported;
	}

	if (iStartOutstanding || iStopOutstanding)
		{
		return KErrServerBusy;
		}

	// Obtains the curent service state
	TUsbServiceState state = iUsbServer->Device().ServiceState();


	// USB Peripheral Stack Starting sequence:
	// if the server is in the idle state
	//		start it;
	// if the server is in the started state
	//		return KErrNone immediately;
	// if the server is in the stopping state
	//		return KErrServerBusy immediately;
 	// if the server is in the starting state
	// (was already called by this very session in OTG/Host configuration and/or
	//  by another session for Client Only configuration)
	// 		if requested personality is not equal to the current one
	//			return KErrAbort;
	//		else
 	//			mark this request as outstanding and let the caller to wait
	//			for start operation to complete;
	if (state == EUsbServiceIdle)
		{
#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
		iUsbServer->Host().StartL();
#endif
		iUsbServer->Device().TryStartL(aMessage.Int0());
		aComplete = EFalse;
		iStartMessage = aMessage;
		iStartOutstanding = ETrue;
		}
	else if (state == EUsbServiceStarting || state == EUsbServiceStarted)
		{
		if (aMessage.Int0() != iUsbServer->Device().CurrentPersonalityId())
			{
			return KErrAbort;
			}

		if (state == EUsbServiceStarting)
			{
			aComplete = EFalse;
			iStartMessage = aMessage;
			iStartOutstanding = ETrue;
			}
		}
	else if (state == EUsbServiceStopping)
		{
		return KErrServerBusy;
		}

	return KErrNone;

#else
	// pretend that the server is in Started state
#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	iUsbServer->Host().StartL();
#endif
	iStartOutstanding = EFalse;
	aMessage.IsNull();
	aComplete = ETrue;
	return KErrNone;
#endif
	}

/**
 * Try stopping the USB device.
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @param	aComplete	Whether the request is complete or not
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::TryStopDeviceL(const RMessage2& aMessage, TBool& aComplete)
	{
	LOG_FUNC

#ifndef __OVER_DUMMYUSBDI__

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	// Only 'control' session is allowed to stop USB support
	if ( !iSessionCtlMode )
		{
		return KErrAccessDenied;
		}
#endif

	if (!iPersonalityCfged)
		{
		return KErrNotSupported;
		}

	if (iStartOutstanding || iStopOutstanding)
		{
		return KErrServerBusy;
		}

	// Obtains the curent service state
	TUsbServiceState state = iUsbServer->Device().ServiceState();

	// USB Peripheral Stack Stopping sequence:
	// if the server is in the started state
	//		stop it;
	// if the server is in the starting state
	//		return KErrServerBusy immediately;
	// if the server is in the idle state
	//		return KErrNone immediately;
	// if the server is in the stopping state (must by another client)
 	//			mark this request as outstanding and let the caller to wait
	//			for stop operation to complete;
	if (state == EUsbServiceStarted)
		{
		iUsbServer->Device().Stop();
#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
		iUsbServer->Host().Stop();
#endif

		aComplete = EFalse;
		iStopMessage = aMessage;
		iStopOutstanding = ETrue;
		}
	else if (state == EUsbServiceStarting)
		{
		return KErrServerBusy;
		}
	else if (state == EUsbServiceStopping)
		{
		aComplete = EFalse;
		iStopMessage = aMessage;
		iStopOutstanding = ETrue;
		}

	return KErrNone;

#else
	// pretend that the server is in Started state
#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	iUsbServer->Host().Stop();
#endif
	aMessage.IsNull();
	aComplete = ETrue;
	iStopOutstanding = EFalse;
	return KErrNone;
#endif
	}

/**
 * Cancels the interest to the outstanding request. The acutal request itself
 * is not cancelled.
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @return	KErrCancel
 */
TInt CUsbSession::CancelInterest(const RMessage2& aMessage)
	{
	LOG_FUNC

	if (!iPersonalityCfged)
		{
		return KErrNotSupported;
		}

	TUsbMessages toBeCancelledMsg = static_cast<TUsbMessages>(aMessage.Int0());
	if (toBeCancelledMsg == EUsbStart || toBeCancelledMsg == EUsbTryStart)
		{
		if (iStartOutstanding)
			{
			iStartMessage.Complete(KErrNone);
			iStartOutstanding = EFalse;
			}
		}
	else if (toBeCancelledMsg == EUsbStop || toBeCancelledMsg == EUsbTryStop)
		{
		if (iStopOutstanding)
			{
			iStopMessage.Complete(KErrNone);
			iStopOutstanding = EFalse;
			}
		}

	return KErrCancel;
	}

/**
 * Gets the current personality id.
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::GetCurrentPersonalityId(const RMessage2& aMessage)
	{
	LOG_FUNC

	if (!iPersonalityCfged)
		{
		return KErrNotSupported;
		}

	TInt currentPersonalityId = iUsbServer->Device().CurrentPersonalityId();
	LOGTEXT2(_L8("\tcurrentPersonalityId = %d"), currentPersonalityId);
	TPckgC<TInt> pckg(currentPersonalityId);
	return aMessage.Write(0, pckg);
	}

/**
 * Gets supported classes.
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @return	KerrTooBig if supported class > KUsbMaxSupportedClasses;
 *          KErrNotSupported if personality is not configured
 * 			return code from RMessage2.Write()
 */
TInt CUsbSession::GetSupportedClasses(const RMessage2& aMessage)
	{
	LOG_FUNC

	if (!iPersonalityCfged)
		{
		return KErrNotSupported;
		}

	// +1 for the size of actual class uid count
	TInt32 classUids[KUsbMaxSupportedClasses + 1];
	classUids[0] = 0;	// initializes class uids count to zero
	// Gets all class uids for the given personality
	const RPointerArray<CPersonality>&  personalities = iUsbServer->Device().Personalities();
	TInt personalityCount = personalities.Count();
	for (TInt i = 0; i < personalityCount; i++)
		{
		__ASSERT_ALWAYS(personalities[i] != NULL, _USB_PANIC(KUsbSvrPncCat, ENullPersonalityPointer));
		if (aMessage.Int0() == personalities[i]->PersonalityId())
			{
			classUids[0] = personalities[i]->SupportedClasses().Count();
			for (TInt j = 1; j <= classUids[0]; j++)
				{
				if (j < KUsbMaxSupportedClasses + 1)
					{
					classUids[j] = personalities[i]->SupportedClasses()[j - 1].iUid;
					LOGTEXT3(_L8("\tclassUids[%d] = %d"), j, classUids[j]);
					}
				else
					{
					return KErrTooBig;
					}
				}
			break;
			}
		}

	if (classUids[0] == 0)
		{
		// No supported classes are found
		return KErrNotSupported;
		}

	TInt ret;
	HBufC8* buf = NULL;
	TRAP(ret, buf = HBufC8::NewL((classUids[0] + 1)*sizeof(TInt32)));
	if (ret == KErrNone)
		{
		TPtr8 ptr8 = buf->Des();
		ptr8.Copy(reinterpret_cast<TUint8*>(classUids), (classUids[0] + 1)*sizeof(TInt32));
		ret = aMessage.Write(1, ptr8);
		}

	delete buf;
	return ret;
	}

/**
 * Gets all personality ids.
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::GetPersonalityIds(const RMessage2& aMessage)
	{
	LOG_FUNC

	if (!iPersonalityCfged)
		{
		return KErrNotSupported;
		}

	// +1 for the size of actual personality id count
	TInt personalityIds[KUsbMaxSupportedPersonalities + 1];

	const RPointerArray<CPersonality>&  personalities = iUsbServer->Device().Personalities();
	TInt personalityCount = personalities.Count();
	for (TInt i = 0; i < personalityCount; ++i)
		{
		__ASSERT_ALWAYS(personalities[i] != NULL, _USB_PANIC(KUsbSvrPncCat, ENullPersonalityPointer));
		personalityIds[i + 1] = personalities[i]->PersonalityId();
		}
	personalityIds[0] = personalityCount;

	TInt ret;
	HBufC8* buf = NULL;
	TRAP(ret, buf = HBufC8::NewL((personalityCount + 1)*sizeof(TInt)));
	if (ret == KErrNone)
		{
		TPtr8 ptr8 = buf->Des();
		ptr8.Copy(reinterpret_cast<TUint8*>(personalityIds), (personalityCount + 1)*sizeof(TInt));
		ret = aMessage.Write(0, ptr8);
		}

	delete buf;
	return ret;
	}

/**
 * Gets personality description
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::GetDescription(const RMessage2& aMessage)
	{
	LOG_FUNC

	if (!iPersonalityCfged)
		{
		return KErrNotSupported;
		}

	TInt personalityId = aMessage.Int0();
	const CPersonality* personality = iUsbServer->Device().GetPersonality(personalityId);
	if (personality)
		{
		return aMessage.Write(1, *(personality->Description()));
		}

	// We should never reach here
	return KErrNotSupported;
	}

/**
 * Gets personality detailed description
 *
 * @internalComponent
 * @param   aMessage    Message received from the client
 * @return  Any error that occurred or KErrNone
 */
TInt CUsbSession::GetDetailedDescription(const RMessage2& aMessage)
	{
	LOG_FUNC

 	if (!iPersonalityCfged)
		{
		return KErrNotSupported;
		}

	TInt personalityId = aMessage.Int0();
	const CPersonality* personality = iUsbServer->Device().GetPersonality(personalityId);
    	if (personality)
        	{
        	if(personality->Version() < EUsbManagerResourceVersionTwo)
            		{
            		return KErrNotFound;
            		}
		return aMessage.Write(1, *(personality->DetailedDescription()));
		}

	// We should never reach here
	return KErrNotSupported;
	}

/**
 * Gets personality property
 *
 * @internalComponent
 * @param   aMessage    Message received from the client
 * @return  Any error that occurred or KErrNone
 */
TInt CUsbSession::GetPersonalityProperty(const RMessage2& aMessage)
	{
		LOG_FUNC

		if (!iPersonalityCfged)
			{
			return KErrNotSupported;
			}

		TInt personalityId = aMessage.Int0();
		const CPersonality* personality = iUsbServer->Device().GetPersonality(personalityId);
		if (personality)
			{
			if(personality->Version() < EUsbManagerResourceVersionThree)
				{
				return KErrNotFound;
				}
			TPckg<TUint32> pckg(personality->Property());
			return aMessage.Write(1, pckg);
			}

		return KErrNotSupported;
	}

/**
 * Checks if a given class is supported by a personality.
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::ClassSupported(const RMessage2& aMessage)
	{
	LOG_FUNC

	if (!iPersonalityCfged)
		{
		return KErrNotSupported;
		}

	TBool isSupported = EFalse;
	TInt personalityId = aMessage.Int0();
	TUid classUid = TUid::Uid(aMessage.Int1());
	const CPersonality* personality = iUsbServer->Device().GetPersonality(personalityId);
	if (personality)
		{
		isSupported = (personality->ClassSupported(classUid) != KErrNotFound);
		TPckg<TBool> pkg2(isSupported);
		return aMessage.Write(2, pkg2);
		}

	// We should never reach here
	return KErrNotSupported;
	}

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
/**
 * Sets or resets the control mode flag for this session.
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::SetCtlSessionMode(const RMessage2& aMessage)
	{
	LOG_FUNC

	TInt ret = KErrNone;

	TBool value = (TBool)aMessage.Int0();
	LOGTEXT2(_L8("\tSetting = %d"), static_cast<TInt>(value));

	// Verify if this is the same session which set the value before
	if ( iCtlSession && (iCtlSession != this) )
		{
		ret = KErrAccessDenied;
		}
	else
		{
		iSessionCtlMode = value;

		// Set control session pointer if the flag is set
		if ( iSessionCtlMode )
			{
			iCtlSession = this;
			}
		else
			{
			iCtlSession = NULL;
			}
		}

	return ret;
	}

/**
 * Asserts a request to drive VBus.
 *
 * @internalComponent
 * @return	If control mode flag is not set returns KErrAccessDenied
 * 			An error code for all other cases
 */
TInt CUsbSession::BusRequest()
	{
	LOG_FUNC

	TInt ret = KErrNone;
	if ( iSessionCtlMode )
		{
		ret = iUsbServer->Otg().BusRequest();
		}
	else
		{
		ret = KErrAccessDenied;
		}

	return ret;
	}

/**
 * Asserts a request to raise VBUS but assumes this is after B-Device
 * has used SRP to make a request to become session Host, so may be able
 * to take some short cuts when enumerating the B-Device.
 *
 * @internalComponent
 * @return	If control mode flag is not set returns KErrAccessDenied
 * 			Any error that occurred or KErrNone for all other cases
 */
TInt CUsbSession::BusRespondSrp()
	{
	LOG_FUNC

	TInt ret = KErrNone;
	if ( iSessionCtlMode )
		{
		ret = iUsbServer->Otg().BusRespondSrp();
		}
	else
		{
		ret = KErrAccessDenied;
		}

	return ret;
	}

/**
 * Clears a possible VBUS error condition (VBUS inexplicably low after
 * having been driven)
 *
 * @internalComponent
 * @return	If control mode flag is not set returns KErrAccessDenied
 * 			Any error that occurred or KErrNone for all other cases
 */
TInt CUsbSession::BusClearError()
	{
	LOG_FUNC

	TInt ret = KErrNone;
	if ( iSessionCtlMode )
		{
		ret = iUsbServer->Otg().BusClearError();
		}
	else
		{
		ret = KErrAccessDenied;
		}

	return ret;
	}

/**
 * Drops VBus.
 *
 * @internalComponent
 * @return	If control mode flag is not set returns KErrAccessDenied
 * 			Any error that occurred or KErrNone for all other cases
 */
TInt CUsbSession::BusDrop()
	{
	LOG_FUNC

	TInt ret = KErrNone;
	if ( iSessionCtlMode )
		{
		ret = iUsbServer->Otg().BusDrop();
		}
	else
		{
		ret = KErrAccessDenied;
		}

	return ret;
	}

/**
 * Enables loading of Function Drivers.
 *
 * @internalComponent
 * @return	If control mode flag is not set returns KErrAccessDenied
 * 			Any error that occurred or KErrNone for all other cases
 */
TInt CUsbSession::EnableFunctionDriverLoading()
	{
	LOG_FUNC

	TInt ret = KErrNone;
	if ( iSessionCtlMode )
		{
		ret = iUsbServer->Host().EnableDriverLoading();
		}
	else
		{
		ret = KErrAccessDenied;
		}

	return ret;
	}

/**
 * Disables loading of Function Drivers.
 *
 * @internalComponent
 * @return	If control mode flag is not set returns KErrAccessDenied
 * 			KErrNone for all other cases
 */
TInt CUsbSession::DisableFunctionDriverLoading()
	{
	LOG_FUNC

	TInt ret = KErrNone;
	if ( iSessionCtlMode )
		{
		iUsbServer->Host().DisableDriverLoading();
		}
	else
		{
		ret = KErrAccessDenied;
		}

	return ret;
	}

/**
 * Requests an array of language identifiers supported by connected device.
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::GetSupportedLanguages(const RMessage2& aMessage)
	{
	LOG_FUNC
	TRAPD(err, GetSupportedLanguagesL(aMessage));
	return err;
	}

/**
 * Requests an array of language identifiers supported by connected device.
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::GetSupportedLanguagesL(const RMessage2& aMessage)
	{
	LOG_FUNC


	const TUint deviceId = aMessage.Int0();
	LOGTEXT2(_L8("\tdeviceId = %d"), deviceId);

	RArray<TUint> langIds;
	CleanupClosePushL(langIds);
	TInt ret = iUsbServer->Host().GetSupportedLanguages(deviceId,langIds);

	if (ret == KErrNone)
		{
		const TUint count = langIds.Count();
		LOGTEXT2(_L8("\tcount = %d"), count);

		// Set error code if there is no languages or there are too many
		if ( count == 0 )
			{
			ret = KErrNotSupported;
			}
		else if ( count > KUsbMaxSupportedLanguageIds )
			{
			ret = KErrTooBig;
			}

		if ( ret == KErrNone )
			{
			// Create a buffer to keep an array size and all received language Ids
			RBuf8 buf;
			buf.CreateL((count + 1) * sizeof(TUint));
			CleanupClosePushL(buf);

			// Save the length of the array
			buf.Append((TUint8*)&count, sizeof(TUint));

			// Save all received language Ids
			for ( TUint ii = 0 ; ii < count; ++ii )
				{
				buf.Append((TUint8*)&(langIds[ii]), sizeof(TUint));
				LOGTEXT3(_L8("Append langID[%d] = %d"),ii,langIds[ii]);
				}

			// Write back to the client.
			ret = aMessage.Write(1, buf);
			CleanupStack::PopAndDestroy(&buf);
			}
		}

	CleanupStack::PopAndDestroy();

	return ret;
	}

/**
 * Requests a manufacturer string descriptor of connected device.
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::GetManufacturerStringDescriptor(const RMessage2& aMessage)
	{
	LOG_FUNC

	const TUint deviceId = aMessage.Int0();
	const TUint langId = aMessage.Int1();
	LOGTEXT3(_L8("\tdeviceId = %d, langId = %d"), deviceId, langId);

	TName string;
	TInt ret = iUsbServer->Host().GetManufacturerStringDescriptor(deviceId,langId,string);
	if (ret == KErrNone)
		{
		LOGTEXT2(_L("\tstring = \"%S\""), &string);
		ret = aMessage.Write(2, string);
		}

	return ret;
	}

/**
 * Requests a product string descriptor of connected device.
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::GetProductStringDescriptor(const RMessage2& aMessage)
	{
	LOG_FUNC

	const TUint deviceId = aMessage.Int0();
	const TUint langId = aMessage.Int1();
	LOGTEXT3(_L8("\tdeviceId = %d, langId = %d"), deviceId, langId);

	TName string;
	TInt ret = iUsbServer->Host().GetProductStringDescriptor(deviceId,langId,string);
	if (ret == KErrNone)
		{
		LOGTEXT2(_L("\tstring = \"%S\""), &string);
		ret = aMessage.Write(2, string);
		}

	return ret;
	}

/**
 * Requests a OTG descriptor of connected device.
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::GetOtgDescriptor(const RMessage2& aMessage)
	{
	LOG_FUNC

	const TUint deviceId = aMessage.Int0();
	LOGTEXT2(_L8("\tdeviceId = %d"), deviceId);

    TOtgDescriptor otgDescriptor;
	TInt ret = iUsbServer->Host().GetOtgDescriptor(deviceId, otgDescriptor);
	if (ret == KErrNone)
		{
		TPckg<TOtgDescriptor> buf(otgDescriptor);
		ret = aMessage.Write(1, buf);
		}

	return ret;
	}

/**
 * Client request to observe the host (for state changes).
 * Asks the host to register the session as an observer.
 * Assures initialisation/dequeueing of Event queue.
 * No events are queued until the first observer is registered
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @param	aComplete	set to true to complete the request
 *
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::RegisterHostObserver(const RMessage2& aMessage, TBool& aComplete)
	{
	LOG_FUNC

	if (iHostEventObserverOutstanding)
		{
		return KErrInUse;
		}

	iHostEventObserverMessage = aMessage;
	iHostEventObserverOutstanding = ETrue;
	aComplete = EFalse;

 	if (iHostEventObserverQueueEvents == EFalse)
	 	{
 		// This is the first observer after c'tor or DeregisterObserver(),
 		// so zap the device event queue.
 		LOGTEXT(_L8("    Reset OTG Host State Queue"));
 		iHostEventQueueHead = 0;
 		iHostEventQueueTail = 0;
 		iHostEventObserverQueueEvents = ETrue;
	 	}
 	else if (iHostEventQueueHead != iHostEventQueueTail)
	 	{
 		// event(s) queued, we can de-queue one now
 		UsbHostEventDequeue();
	 	}

	return KErrNone;
	}

/**
 * Deregister the client as an observer of host state changes.
 *
 * @internalComponent
 * @return Always KErrNone
 */
TInt CUsbSession::DeRegisterHostObserver()
	{
	LOG_FUNC


	if (!iHostEventObserverOutstanding)
		{
		return KErrNone;
		}

	iHostEventObserverOutstanding = EFalse;
	iHostEventObserverMessage.Complete(KErrCancel);

	// client doesn't need events queuing any more
 	iHostEventObserverQueueEvents = EFalse;

	return KErrNone;
	}

/**
 * Client request to observe both OTG and HOST (for events and errors).
 * Asks HOST and OTGDI components to register the session as an observer.
 * Assures initialisation/dequeueing of Event queue.
 * No events are queued until the first observer is registered
 *
 * @internalComponent
 * @param	aMessage	Message received from the client
 * @param	aComplete	set to true to complete the request
 *
 * @return	Any error that occurred or KErrNone
 */
TInt CUsbSession::RegisterMsgObserver(const RMessage2& aMessage, TBool& aComplete)
	{
	LOG_FUNC

	if (iMsgObserverOutstanding)
		{
		return KErrInUse;
		}

	iMsgObserverMessage = aMessage;
	iMsgObserverOutstanding = ETrue;
	aComplete = EFalse;

 	if (iMsgObserverQueueEvents == EFalse)
	 	{
 		// This is the first observer after c'tor or DeregisterObserver(),
 		// so zap the device event queue.
 		LOGTEXT(_L8("    Reset OTG Message Queue"));
 		iMsgQueueHead = 0;
 		iMsgQueueTail = 0;
 		iMsgObserverQueueEvents = ETrue;
	 	}
 	else if (iMsgQueueHead != iMsgQueueTail)
	 	{
 		// event(s) queued, we can de-queue one now
 		UsbMsgDequeue();
	 	}

	return KErrNone;
	}

/**
 * Deregister the client as an observer of OTG/HOST events and errors.
 *
 * @internalComponent
 * @return Always KErrNone
 */
TInt CUsbSession::DeRegisterMsgObserver()
	{
	LOG_FUNC

	if (!iMsgObserverOutstanding)
		{
		return KErrNone;
		}

	iMsgObserverOutstanding = EFalse;
	iMsgObserverMessage.Complete(KErrCancel);

	// client doesn't need events queuing any more
 	iMsgObserverQueueEvents = EFalse;

	return KErrNone;
	}



/**
 * Called by CUsbOtg or CUsbHost when the USB OTG/HOST message has arrived
 *
 * @internalComponent
 * @param aMessage The new OTG Message
 */
void CUsbSession::UsbOtgHostMessage(TInt aMessage)
	{
	LOG_FUNC

	// can we bypass the queue?
 	if ((iMsgObserverOutstanding) && (iMsgQueueHead == iMsgQueueTail))
		{
		TPckg<TInt> pckg(aMessage);

		iNotifiedMsg = aMessage;

		iMsgObserverOutstanding = EFalse;
		const TInt err = iMsgObserverMessage.Write(0, pckg);
		iMsgObserverMessage.Complete(err);
		}
	else if (iMsgObserverQueueEvents)
		{
		// add event to head of queue
		iMsgQueue[iMsgQueueHead] = aMessage;
		iMsgQueueHead = (iMsgQueueHead + 1) % KOtgHostMessageQueueSize;
		LOGTEXT3(_L8("+++ CUsbSession::UsbOtgMessage() addqueue (%d, %d)"), iMsgQueueHead,
			iMsgQueueTail);

 		// UsbMsgDequeueEvent() will read from queue when RegisterMsgObserver()
		// is next called.
		}
	}

/**
 * Called by CUsbHost when it state change. CUsbSession is an observer of
 * the device. If the client has an Observer outstanding then complete it,
 * otherwise put it in a circular queue.
 *
 * @internalComponent
 * @param	aDevInfo	The information about the device being attached or detached
 * 						along with the status of Function Driver loading
 */
void CUsbSession::UsbHostEvent(TDeviceEventInformation& aDevInfo)
	{
	LOG_FUNC

	// can we bypass the queue?
 	if ((iHostEventObserverOutstanding) && (iHostEventQueueHead == iHostEventQueueTail))
		{
		iNotifiedHostState = aDevInfo;
		iHostEventObserverOutstanding = EFalse;

		LOGTEXT(_L8("CUsbSession::UsbHostEvent() detected outstanding request"));

		TPckg<TDeviceEventInformation> info(aDevInfo);
		const TInt err = iHostEventObserverMessage.Write(0, info);
		iHostEventObserverMessage.Complete(err);
		LOGTEXT2(_L8("CUsbSession::UsbHostEvent() detects outstanding request: request is compeleted with %d"), err);
		}
	else if (iHostEventObserverQueueEvents)
		{
		// add dev info to head of queue
		iHostStateQueue[iHostEventQueueHead] = aDevInfo;
		iHostEventQueueHead = (iHostEventQueueHead + 1) % KDeviceStatesQueueSize;
		LOGTEXT3(_L8("+++ CUsbSession::UsbHostEvent() addqueue (%d, %d)"), iHostEventQueueHead,
			iHostEventQueueTail);

 		// UsbHostStateDequeueEvent() will read from queue when RegisterHostObserver()
		// is next called.
		}
	}

/**
 * Dequeues an event and completes the observer's request with it.
 */
void CUsbSession::UsbMsgDequeue()
 	{
	LOG_FUNC

	// Work our way through the queue, until we reach the end
 	// OR we find an event the current observer wants.
 	if ((iMsgObserverOutstanding) && (iMsgQueueHead != iMsgQueueTail))
 		{
 		TInt newMsg = iMsgQueue[iMsgQueueTail];

 		// advance tail towards the head
 		iMsgQueueTail = (iMsgQueueTail + 1) % KOtgHostMessageQueueSize;

 		TPckg<TUint32> pckg(newMsg);
 		iNotifiedMsg = newMsg;

 		LOGTEXT3(_L8(">>> dequeued event #%d (0x%x)"), iMsgQueueTail, newMsg);

		iMsgObserverOutstanding = EFalse;
		const TInt err = iMsgObserverMessage.Write(0, pckg);
		iMsgObserverMessage.Complete(err);
   		}
  	}

/**
 * Dequeues an event and completes the observer's request with it.
 */
void CUsbSession::UsbHostEventDequeue()
 	{
	LOG_FUNC

	// Work our way through the queue, until we reach the end
 	// OR we find an event the current observer wants.
 	if ((iHostEventObserverOutstanding) && (iHostEventQueueHead != iHostEventQueueTail))
 		{
 		// inform the observer of state changes they are interested in AND
 		// if the cable is pulled out (EUsbDeviceStateUndefined)
 		TDeviceEventInformation newDevInfo = iHostStateQueue[iHostEventQueueTail];
		iNotifiedHostState = newDevInfo;

 		// advance tail towards the head
 		iHostEventQueueTail = (iHostEventQueueTail + 1) % KDeviceStatesQueueSize;

		LOGTEXT3(_L8(">>> CUsbSession::UsbHostStateDequeueEvent() dequeued event #%d (0x%x)"), iHostEventQueueTail, newDevInfo.iEventType);

		TPckg<TDeviceEventInformation> info(newDevInfo);
		iHostEventObserverOutstanding = EFalse;
		const TInt err = iHostEventObserverMessage.Write(0, info);
		iHostEventObserverMessage.Complete(err);

		LOGTEXT2(_L8("CUsbSession::UsbHostStateDequeueEvent() detects outstanding request: request is compeleted with %d"), err);
   		}
   	}

TInt CUsbSession::RequestSession()
	{
	return DoRequestSession();
	}

TInt CUsbSession::DoRequestSession()
	{
	if ( iCtlSession )
		{
		if ( iCtlSession == this )
			{
	 		if (iMsgObserverQueueEvents)
	 			{
	 			UsbOtgHostMessage(KUsbMessageRequestSession);
	 			return KErrNone;
	 			}
		 	else
		 		{
		 		return KErrNotFound;
		 		}
			}
		else
			{
			return iCtlSession->DoRequestSession();
			}
		}
 	return KErrNotFound;
	}

#endif // SYMBIAN_ENABLE_USB_OTG_HOST_PRIV

