/*
* 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 Symbian OS server that exposes the RUsb API
*
*/

/**
 @file
*/

#include <e32svr.h>
#include "UsbSettings.h"
#include "CUsbServer.h"
#include "CUsbSession.h"
#include "CUsbDevice.h"

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
#include "CUsbOtg.h"
#include "cusbhost.h"
#include <e32property.h> //Publish & Subscribe header
#endif // SYMBIAN_ENABLE_USB_OTG_HOST_PRIV

#include <usb/usblogger.h>
#include "UsbmanServerSecurityPolicy.h"
#include <usb/usblogger.h>

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

/**
 * The CUsbServer::NewL method
 *
 * Constructs a Usb Server
 *
 * @internalComponent
 *
 * @return	A new Usb Server object
 */
CUsbServer* CUsbServer::NewLC()
	{
	LOG_STATIC_FUNC_ENTRY

	CUsbServer* self = new(ELeave) CUsbServer;
	CleanupStack::PushL(self);
	self->StartL(KUsbServerName);
	self->ConstructL();
	return self;
	}


/**
 * The CUsbServer::~CUsbServer method
 *
 * Destructor
 *
 * @internalComponent
 */
CUsbServer::~CUsbServer()
	{
	LOG_FUNC

	delete iShutdownTimer;
	delete iUsbDevice;
	
#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	delete iUsbHost;
	
#ifndef __OVER_DUMMYUSBDI__
	// Check that this is A-Device
	LOGTEXT(_L8("Checking Id-Pin state..."));
	TInt value = 0;
	TInt err = RProperty::Get(KUidUsbManCategory, KUsbOtgIdPinPresentProperty,value);
	if (err == 0 && value == 1)
		{
		// Ensure VBus is dropped when Usb server exits
		LOGTEXT(_L8("Checking VBus state..."));
		err = RProperty::Get(KUidUsbManCategory, KUsbOtgVBusPoweredProperty,value);
		if ( err == KErrNone && value != 0 )
			{
			if ( iUsbOtg )
				{
				err = iUsbOtg->BusDrop();
				LOGTEXT2(_L8("BusDrop() returned err = %d"),err);
				LOGTEXT(_L8("USBMAN will wait until VBus is actually dropped"));
				// Wait 1 second for Hub driver to perform VBus drop
				RTimer timer;
				err = timer.CreateLocal();
				if ( err == KErrNone )
					{
					TRequestStatus tstatus;
					timer.After(tstatus, 1000000);
					User::WaitForRequest(tstatus);
					timer.Close();
					}
				else
					{
					LOGTEXT2(_L8("Failed to create local timer: err = %d"),err);
					}
				}
			else
				{
				LOGTEXT(_L8("Unexpected: OTG object is NULL"));
				}
			}
		else
			{
			LOGTEXT3(_L8("VBus is already dropped or an error occured: err = %d, value =%d"),err,value);
			}
		}
	else
		{
		LOGTEXT3(_L8("No Id-Pin is found or an error occured: err = %d, value = %d"), err, value);
		}
	
	delete iUsbOtg;
#endif
#endif // SYMBIAN_ENABLE_USB_OTG_HOST_PRIV

#ifdef __FLOG_ACTIVE
	CUsbLog::Close();
#endif
	}


/**
 * The CUsbServer::CUsbServer method
 *
 * Constructor
 *
 * @internalComponent
 */
CUsbServer::CUsbServer()
     : CPolicyServer(EPriorityHigh,KUsbmanServerPolicy)
	{
	}

/**
 * The CUsbServer::ConstructL method
 *
 * 2nd Phase Construction
 *
 * @internalComponent
 */
void CUsbServer::ConstructL()
	{
#ifdef __FLOG_ACTIVE
	// Set the logger up so that everything in this thread that logs using it 
	// will do so 'connectedly' (i.e. quickly). If this fails, we don't care- 
	// logging will still work, just 'statically' (i.e. slowly).
	static_cast<void>(CUsbLog::Connect());
#endif

	iShutdownTimer = new(ELeave) CShutdownTimer;
	iShutdownTimer->ConstructL(); 

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
#ifndef __OVER_DUMMYUSBDI__
	iUsbOtg = CUsbOtg::NewL();
	iUsbOtg->StartL();
#endif
#endif // SYMBIAN_ENABLE_USB_OTG_HOST_PRIV

	iUsbDevice = CUsbDevice::NewL(*this);
	LOGTEXT(_L8("About to load USB classes"));
	iUsbDevice->EnumerateClassControllersL();

#ifndef USE_DUMMY_CLASS_CONTROLLER	
	iUsbDevice->ReadPersonalitiesL();
	if (iUsbDevice->isPersonalityCfged())
		{
#ifndef __OVER_DUMMYUSBDI__
		iUsbDevice->ValidatePersonalitiesL();
#endif
		iUsbDevice->SetDefaultPersonalityL();		
		}
	else  
		{
		LOGTEXT(_L8("Personalities unconfigured, so using fallback CCs"));
		iUsbDevice->LoadFallbackClassControllersL();
		}
#else // USE_DUMMY_CLASS_CONTROLLER
	LOGTEXT(_L8("Using Dummy Class Controller, so using fallback CCs"));
	iUsbDevice->LoadFallbackClassControllersL();
#endif // USE_DUMMY_CLASS_CONTROLLER		

#ifdef SYMBIAN_ENABLE_USB_OTG_HOST_PRIV
	iUsbHost = CUsbHost::NewL();
	//moved to CUsbSession:StartDeviceL() and similar: iUsbHost->StartL();
#endif // SYMBIAN_ENABLE_USB_OTG_HOST_PRIV

	LOGTEXT(_L8("CUsbServer constructed"));
	}


/**
 * The CUsbServer::NewSessionL method
 *
 * Create a new client on this server
 *
 * @internalComponent
 * @param	&aVersion	Vesion of client
 * @param  	&aMessage 	Client's IPC message
 *
 * @return	A new USB session to be used for the client
 */
CSession2* CUsbServer::NewSessionL(const TVersion &aVersion, const RMessage2& aMessage) const
	{
	LOG_LINE
	LOG_FUNC

	(void)aMessage;//Remove compiler warning
	
	TVersion v(KUsbSrvMajorVersionNumber,KUsbSrvMinorVersionNumber,KUsbSrvBuildVersionNumber);

	LOGTEXT(_L8("CUsbServer::NewSessionL - creating new session..."));
	if (!User::QueryVersionSupported(v, aVersion))
		{
		LEAVEL(KErrNotSupported);
		}

	CUsbServer* ncThis = const_cast<CUsbServer*>(this);
	
	CUsbSession* sess = CUsbSession::NewL(ncThis);
		
	return sess;
	}


/**
 * Inform the client there has been an error.
 *
 * @param	aError	The error that has occurred
 */
void CUsbServer::Error(TInt aError)
	{
	LOGTEXT2(_L8("CUsbServer::Error [aError=%d]"), aError);

	Message().Complete(aError);
	ReStart();
	}

/**
 * Increment the open session count (iSessionCount) by one.
 * 
 * @post	the number of open sessions is incremented by one
 */
void CUsbServer::IncrementSessionCount()
	{
	LOGTEXT2(_L8(">CUsbServer::IncrementSessionCount %d"), iSessionCount);
	__ASSERT_DEBUG(iSessionCount >= 0, _USB_PANIC(KUsbSvrPncCat, EICSInvalidCount));
	
	++iSessionCount;
	iShutdownTimer->Cancel();

	LOGTEXT(_L8("<CUsbServer::IncrementSessionCount"));
	}

/**
 * Decrement the open session count (iSessionCount) by one.
 * 
 * @post		the number of open sessions is decremented by one
 */
void CUsbServer::DecrementSessionCount()
	{
	LOGTEXT3(_L8("CUsbServer::DecrementSessionCount %d, %d"), iSessionCount, Device().ServiceState());
	__ASSERT_DEBUG(iSessionCount > 0, _USB_PANIC(KUsbSvrPncCat, EDCSInvalidCount));
	
	--iSessionCount;
	
	if (iSessionCount == 0 && Device().ServiceState() == EUsbServiceIdle)
		{
		iShutdownTimer->After(KShutdownDelay);
		}
	}

/**
 * If there are no sessions then launch the shutdown timer.  This function
 * is provided for the case where the sole session stops the classes but dies
 * before they are completely stopped.  The server must then be shut down
 * from CUsbDevice::SetServiceState().
 *
 * @pre		the services have been stopped.
 * @see		CUsbDevice::SetServiceStateIdle
 */
void CUsbServer::LaunchShutdownTimerIfNoSessions()
	{
	LOGTEXT(_L8("CUsbServer::LaunchShutdownTimerIfNoSessions"));
	__ASSERT_DEBUG(Device().ServiceState() == EUsbServiceIdle, _USB_PANIC(KUsbSvrPncCat, ELSTNSNotIdle));

	if (iSessionCount == 0)
		iShutdownTimer->After(KShutdownDelay);
	}

/**
 * Initialize this shutdown timer as a normal-priority
 * (EPriorityStandard) active object.
 */
CUsbServer::CShutdownTimer::CShutdownTimer()
:	CTimer(EPriorityStandard)
	{
	CActiveScheduler::Add(this);
	}

/**
 * Forwarding function call's CTimer's ConstructL() to initialize the RTimer.
 */
void CUsbServer::CShutdownTimer::ConstructL()
	{
	CTimer::ConstructL();
	}

/**
 * Server shutdown callback.  This stops the active scheduler,
 * and so closes down the server.
 */
void CUsbServer::CShutdownTimer::RunL()
	{
	CActiveScheduler::Stop();
	}

