/*
* 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:
* Adheres to the UsbMan USB Class Controller API and talks to C32
* to manage the stub2.CSY that is used to provide a virtual
* serial port service to clients
*
*/

/**
 @file
*/

#include "Stub2CC.h"
#include <usb_std.h>
#include <es_ini.h>
#include <d32usbc.h>
#include <usb/usblogger.h>

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


#include "usbmaninternalconstants.h"
 

// Panic category 
_LIT( Kstub2CcPanicCategory, "Usbstub2Cc" );


/**
 * Panic codes for the USB stub2 Class Controller.
 */
enum Tstub2CcPanic
	{
	/** Class called while in an illegal state */
	EBadApiCall = 0,
	/** Asynchronous function called (not needed, as all requests complete synchronously) */
	EUnusedFunction = 1,
	/** Error reading ini file. */
	EPanicBadIniFile = 2,		
	/** Bad value for the iNumberOfstub2Functions member.*/
	EPanicBadNumberOfstub2Functions = 3,
	
	EPanicUnexpectedStatus,
	EPanicUnexpectedState

	};

_LIT16(KIfcName, "SubCC 1 Interface");
const TInt KMaxPacketTypeInterrupt = 64;	
const TInt KPollInterval  = 128; 



/**
 * Constructs a CUsbstub2ClassController object
 *
 * @param	aOwner	USB Device that owns and manages the class
 *
 * @return	A new CUsbstub2ClassController object
 */
CUsbstub2ClassController* CUsbstub2ClassController::NewL(
	MUsbClassControllerNotify& aOwner)
	{
	CUsbstub2ClassController* r = new (ELeave) CUsbstub2ClassController(aOwner);
	CleanupStack::PushL(r);
	r->ConstructL();
	CleanupStack::Pop();
	return r;
	}

/**
 * Destructor
 */
CUsbstub2ClassController::~CUsbstub2ClassController()
	{
	Cancel();

	iTimer.Close();
	
#ifndef __WINS__	
	iLdd.Close();
#endif

	}



/**
 * Constructor.
 *
 * @param	aOwner	USB Device that owns and manages the class
 */
CUsbstub2ClassController::CUsbstub2ClassController(
	MUsbClassControllerNotify& aOwner)
	: CUsbClassControllerPlugIn(aOwner, Kstub2StartupPriority),
	iStartDelay(Kstub2CCDefaultDelay),
	iStopDelay(Kstub2CCDefaultDelay),
	iFailToStart(EFalse),
	iFailToStop(EFalse)	

	{
	iTimer.CreateLocal();
	}



/**
 * 2nd Phase Construction.
 */
void CUsbstub2ClassController::ConstructL()
	{
	//read INI file
	TInt ret;
	CESockIniData* ini = 0;
	_LIT(KIniFile, "c:\\testdata\\config\\stub2cc.ini");
	TRAP(ret, ini=CESockIniData::NewL(KIniFile));	
	if(ret!=KErrNone)		
		return;	

	CleanupStack::PushL(ini);
	
	TInt val;
	if ((ini->FindVar(_L("0x10203287"),_L("StartDelay"), val)))
		{
		iStartDelay = val;
		}
	if ((ini->FindVar(_L("0x10203287"),_L("StopDelay"), val)))
		{
		iStopDelay = val;
		}
	if ((ini->FindVar(_L("0x10203287"),_L("FailToStart"), val)) && val!=0)
		{
		iFailToStart = ETrue;
		}
	if ((ini->FindVar(_L("0x10203287"),_L("FailToStop"), val)) && val!=0 )
		{
		iFailToStop = ETrue;
		}
	CleanupStack::PopAndDestroy(ini);						
	}

/**
 * Called by UsbMan when it wants to start the USB stub2 class. This always
 * completes immediately.
 *
 * @param aStatus The caller's request status, filled in with an error code
 */
void CUsbstub2ClassController::Start(TRequestStatus& aStatus)
	{
	LOG_FUNC

	aStatus = KRequestPending;
	iReportStatus = &aStatus;
	//If we are already started then just complete the request.
	if (iState == EUsbServiceStarted)
		{
		User::RequestComplete(iReportStatus, KErrNone);
		return;
		}

	if (iFailToStart)
		{
		User::RequestComplete(iReportStatus, KErrGeneral);
		return;
		}
	
	iState = EUsbServiceStarting;
#ifndef __WINS__	
	TInt ret = iLdd.Open(0);
	LOGTEXT2(_L8("Open LDD, ret=%d"), ret);
	ret = SetUpInterface();
	LOGTEXT2(_L8("SetUpInterface(), ret=%d"), ret);
#endif	


	iTimer.After(iStatus, iStartDelay*1000);  //convert from usec to msec
	SetActive();
	}

/**
 * Called by UsbMan when it wants to stop the USB stub2 class.
 *
 * @param aStatus The caller's request status: always set to KErrNone
 */
void CUsbstub2ClassController::Stop(TRequestStatus& aStatus)
	{
	LOG_FUNC

	aStatus = KRequestPending;
	iReportStatus = &aStatus;
	//If we are already idle then just complete the request.
	if (iState == EUsbServiceIdle)
		{
		User::RequestComplete(iReportStatus, KErrNone);
		return;
		}

	if (iFailToStop)
		{
		User::RequestComplete(iReportStatus, KErrGeneral);
		return;
		}

	iState = EUsbServiceStopping;
	
#ifndef __WINS__	
	iLdd.Close();
#endif

	iTimer.After(iStatus, iStopDelay*1000);  //convert from usec to msec
	SetActive();
	}

/**
 * Gets information about the descriptor which this class provides.
 *
 * @param aDescriptorInfo Descriptor info structure filled in by this function
 */
void CUsbstub2ClassController::GetDescriptorInfo(TUsbDescriptor& aDescriptorInfo) const
	{
	LOG_FUNC

	aDescriptorInfo.iLength = Kstub2DescriptorLength;
	aDescriptorInfo.iNumInterfaces = Kstub2NumberOfInterfacesPerstub2Function;
	}


/**
 * Standard active object RunL. 
 */
void CUsbstub2ClassController::RunL()
	{
	LOG_FUNC

	__ASSERT_DEBUG( iStatus == KErrNone, _USB_PANIC(Kstub2CcPanicCategory, EPanicUnexpectedStatus) );
	switch (iState)
		{
		case EUsbServiceStarting:
			iState = EUsbServiceStarted;
			break;
		case EUsbServiceStopping:
			iState = EUsbServiceIdle;
			break;
		default:	
			_USB_PANIC(Kstub2CcPanicCategory, EPanicUnexpectedState);
		}
	*iReportStatus = KErrNone;	
	User::RequestComplete(iReportStatus, iStatus.Int());	
	}

/**
 * Standard active object cancellation function. Never called because this
 * class has no asynchronous requests.
 */
void CUsbstub2ClassController::DoCancel()
	{

	if (IsActive())
		{
		iTimer.Cancel();	
		}
	switch (iState)
	{
		case EUsbServiceStarting:
			iState = EUsbServiceIdle;
			break;
		case EUsbServiceStopping:
			iState = EUsbServiceStarted;
			break;
		default:	
			_USB_PANIC(Kstub2CcPanicCategory, EPanicUnexpectedState);
	}
	*iReportStatus = KErrNone;		
	User::RequestComplete(iReportStatus, KErrCancel);	
	}

/**
 * Standard active object error function. Never called because this class has
 * no asynchronous requests, and hence its RunL is never called.
 *
 * @param aError The error code (unused)
 * @return Always KErrNone to avoid an active scheduler panic
 */
TInt CUsbstub2ClassController::RunError(TInt /*aError*/)
	{
	__ASSERT_DEBUG( EFalse, _USB_PANIC(Kstub2CcPanicCategory, EUnusedFunction) );
	return KErrNone;
	}

TInt CUsbstub2ClassController::SetUpInterface()
/**
 * Set up the interface for use. This involves finding a "Interrupt IN" 
 * endpoint and, if found, configuring the interface.
 */
	{
	LOGTEXT(_L8(">>CCdcControlInterface::SetUpInterface"));

	TUsbDeviceCaps dCaps;
	TInt ret = iLdd.DeviceCaps(dCaps);
	LOGTEXT(_L8("\tchecking result of DeviceCaps"));
	if ( ret )
		{
		LOGTEXT2(_L8("<<CCdcControlInterface::SetUpInterface ret=%d"), ret);
		return ret;
		}

	const TUint KRequiredNumberOfEndpoints = 1; // in addition to endpoint 0.

	const TUint totalEndpoints = static_cast<TUint>(dCaps().iTotalEndpoints);
	LOGTEXT2(_L8("\tiTotalEndpoints = %d"), totalEndpoints);
	if ( totalEndpoints < KRequiredNumberOfEndpoints )
		{
		LOGTEXT2(_L8("<<CCdcControlInterface::SetUpInterface ret=%d"), 
			KErrGeneral);
		return KErrGeneral;
		}
	
	// Endpoints
	TUsbcEndpointData data[KUsbcMaxEndpoints];
	TPtr8 dataptr(reinterpret_cast<TUint8*>(data), sizeof(data), sizeof(data));
	ret = iLdd.EndpointCaps(dataptr);
	LOGTEXT(_L8("\tchecking result of EndpointCaps"));
	if ( ret )
		{
		LOGTEXT2(_L8("<<CCdcControlInterface::SetUpInterface ret=%d"), ret);
		return ret;
		}

	// Set the active interface
	TUsbcInterfaceInfoBuf ifc;
	TBool epFound = EFalse;
	for ( TUint i = 0 ; i < totalEndpoints ; i++ )
		{
		const TUsbcEndpointCaps* caps = &data[i].iCaps;
		__ASSERT_DEBUG(caps,_USB_PANIC(Kstub2CcPanicCategory, EPanicUnexpectedStatus));

		if (data[i].iInUse)
			{
			continue;
			}

		if ((caps->iTypesAndDir & (KUsbEpTypeInterrupt | KUsbEpDirIn)) == 
			(KUsbEpTypeInterrupt | KUsbEpDirIn))
			{
			// EEndpoint1 is interrupt endpoint
			ifc().iEndpointData[0].iType  = KUsbEpTypeInterrupt;
			ifc().iEndpointData[0].iDir   = KUsbEpDirIn; 

			//get the max packet size it can potentially support
			//it's possible that it can support Isoch (1023) which is greater
			//than max for Int at 64
			TInt maxSize = Min(caps->MaxPacketSize(), KMaxPacketTypeInterrupt);
			
			ifc().iEndpointData[0].iSize  = maxSize;

			ifc().iEndpointData[0].iInterval = KPollInterval; 
			epFound = ETrue;
			break;
			}
		}
	LOGTEXT(_L8("\tchecking epFound"));
	if ( !epFound )
		{
		LOGTEXT2(_L8("<<CCdcControlInterface::SetUpInterface ret=%d"), 
			KErrGeneral);
		return KErrGeneral;
		}

	TName ifcName(KIfcName);
	ifc().iString = &ifcName;
	ifc().iTotalEndpointsUsed = KRequiredNumberOfEndpoints;
	// Codes taken from USBCDC 1.1.
	ifc().iClass.iClassNum	  = 0x02; // Table 15- Communication Interface Class
	ifc().iClass.iSubClassNum = 0x02; // Table 16- Abstract Control Model
	ifc().iClass.iProtocolNum = 0x01; // Table 17- Hayes compatible

	LOGTEXT(_L8("\tabout to call SetInterface"));
	// Zero effectively indicates that alternate interfaces are not used.
	ret = iLdd.SetInterface(0, ifc);

	LOGTEXT2(_L8("<<CCdcControlInterface::SetUpInterface ret=%d"), ret);
	return ret;
	}

//
// End of file
