/*
* 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:
*
*
*/

/**
 @file
*/

#include <e32svr.h>
#include "transferserver.h"
#include "transfersession.h"
#include "transfersrv.h"
#include "transferserversecuritypolicy.h"
#include "transferhandle.h"
#include "tranhandlesrv.h"



TBool gVerbose = ETrue;
extern RTest test;


CTransferServer* CTransferServer::NewLC()
	{
	RDebug::Printf("CTransferServer::NewLC");
	CTransferServer* self = new(ELeave) CTransferServer;
	CleanupStack::PushL(self);
	self->StartL(KTransferServerName);
	self->ConstructL();
	return self;
	}

CTransferServer::~CTransferServer()
	{
	RDebug::Printf("CTransferServer::~CTransferServer");
	while (iLddPtr->iIFPtr)
	{
	IFConfigPtr* ifPtrPtr = & iLddPtr->iIFPtr;
	while ((*ifPtrPtr)->iPtrNext)
		{
		ifPtrPtr = &(*ifPtrPtr)->iPtrNext;
		}
	delete (*ifPtrPtr)->iInfoPtr->iString;
	delete (*ifPtrPtr)->iInfoPtr;
	delete (*ifPtrPtr);
	* ifPtrPtr = NULL;
	}

	while (iLddPtr)
		{
		LDDConfigPtr* lddPtrPtr = &iLddPtr;	
		while ((*lddPtrPtr)->iPtrNext)
			{
			lddPtrPtr = &(*lddPtrPtr)->iPtrNext;
			}
		delete (*lddPtrPtr)->iManufacturer;
		delete (*lddPtrPtr)->iProduct;
		delete (*lddPtrPtr)->iSerialNumber;
		delete (*lddPtrPtr);
		* lddPtrPtr = NULL;
		}

	delete iShutdownTimer;	
	delete iTransferHandle;	
	RDebug::Printf("<<<CTransferServer::~CTransferServer");
	}


CTransferServer::CTransferServer()
     : CPolicyServer(EPriorityHigh,KTransferServerPolicy)
	{
	}

void CTransferServer::ConstructL()
	{
	iShutdownTimer = new(ELeave) CShutdownTimer;
	iShutdownTimer->ConstructL(); 
	
	iTransferHandle = CTransferHandle::NewL(*this);
	RDebug::Printf("CTransferServer::ConstructL");
	}


CSession2* CTransferServer::NewSessionL(const TVersion &aVersion, const RMessage2& aMessage) const
	{
	(void)aMessage;//Remove compiler warning
	(void)aVersion;//Remove compiler warning
	
	CTransferServer* ncThis = const_cast<CTransferServer*>(this);
	
	CTransferSession* sess = CTransferSession::NewL(ncThis);
		
	return sess;
	}


void CTransferServer::Error(TInt aError)
	{
	RDebug::Printf("CTransferServer::Error");
	Message().Complete(aError);
	ReStart();
	}

void CTransferServer::IncrementSessionCount()
	{
	RDebug::Printf("CTransferServer::IncrementSessionCount");
	
	++iSessionCount;
	iShutdownTimer->Cancel();

	}

void CTransferServer::DecrementSessionCount()
	{
	--iSessionCount;	
	RDebug::Printf("CTransferServer::DecrementSessionCount");	
	if (iSessionCount == 0)
		{
		iShutdownTimer->After(KShutdownDelay);
		RDebug::Printf("CTransferServer::DecrementSessionCount1");
		}
	}

void CTransferServer::LaunchShutdownTimerIfNoSessions()
	{
	if (iSessionCount == 0)
		iShutdownTimer->After(KShutdownDelay);
	}

CTransferServer::CShutdownTimer::CShutdownTimer()
:	CTimer(EPriorityStandard)
	{
	CActiveScheduler::Add(this);
	}


void CTransferServer::CShutdownTimer::ConstructL()
	{
	RDebug::Printf("CTransferServer::CShutdownTimer::ConstructL");
	CTimer::ConstructL();
	}


void CTransferServer::CShutdownTimer::RunL()
	{
	RDebug::Printf("CShutdownTimer::RunL");
	CActiveScheduler::Stop();
	}


void CTransferServer::TransferHandleL()
	{
	RTranHandleSrv aServer;
	RChunk* commChunk = NULL;
	User::LeaveIfError(aServer.Connect());
	User::LeaveIfError(iPort[0].GetDataTransferChunk(commChunk));
	User::LeaveIfError(aServer.TransferHandle(iPort[0], *commChunk));	
	aServer.Close();
	commChunk->Close();
	iPort[0].Close();
	}

void CTransferServer::FillEndpointsResourceAllocation(IFConfigPtr aIfCfg)
	{
	
#ifdef USB_SC
		TUsbcScInterfaceInfo* iInfoPtr = aIfCfg->iInfoPtr;
#else
		TUsbcInterfaceInfo* iInfoPtr = aIfCfg->iInfoPtr;
#endif
	
	//	fill resource allocation info in the endpoint info with resource allocation v2
	for (TUint8 i = 1; i <= iInfoPtr->iTotalEndpointsUsed; i++)
		{
		if (aIfCfg->iEpDMA[i-1])
			{
			iInfoPtr->iEndpointData[i-1].iFeatureWord1 |= KUsbcEndpointInfoFeatureWord1_DMA;
			}
		else
			{
			iInfoPtr->iEndpointData[i-1].iFeatureWord1 &= (~KUsbcEndpointInfoFeatureWord1_DMA);
			}
	#ifndef USB_SC
		if (aIfCfg->iEpDoubleBuff[i-1])
			{
			iInfoPtr->iEndpointData[i-1].iFeatureWord1 |= KUsbcEndpointInfoFeatureWord1_DoubleBuffering;
			}
		else
			{
			iInfoPtr->iEndpointData[i-1].iFeatureWord1 &= (~KUsbcEndpointInfoFeatureWord1_DoubleBuffering);
			}
	#endif
		}	
	}


void CTransferServer::PopulateInterfaceResourceAllocation(IFConfigPtr aFirstIfCfg, TInt aPortNumber)
	{
	FillEndpointsResourceAllocation(aFirstIfCfg);
	
	IFConfigPtr ifCfgPtr = aFirstIfCfg->iPtrNext;
	while (ifCfgPtr != NULL)
		{
		if (ifCfgPtr->iAlternateSetting)
			{
			FillEndpointsResourceAllocation(ifCfgPtr);
			ifCfgPtr = ifCfgPtr->iPtrNext;
			}
		else
			{
			ifCfgPtr = NULL;
			}
		}
	}

void CTransferServer::SetupInterface(IFConfigPtr* aIfPtr, TInt aPortNumber)
	{
	test.Start (_L("Setup Interface"));
	
	// first of all set the default interface	
	TUSB_PRINT2 ("Set Default Interface with %d endpoints bandwidth 0x%x",(*aIfPtr)->iInfoPtr->iTotalEndpointsUsed,(*aIfPtr)->iBandwidthIn | (*aIfPtr)->iBandwidthOut);
#ifdef USB_SC
	TUsbcScInterfaceInfoBuf ifc = *((*aIfPtr)->iInfoPtr);
	TInt r = iPort[aPortNumber].SetInterface(0, ifc);
#else
	TUsbcInterfaceInfoBuf ifc = *((*aIfPtr)->iInfoPtr);
	TInt r = iPort[aPortNumber].SetInterface(0, ifc, (*aIfPtr)->iBandwidthIn | (*aIfPtr)->iBandwidthOut);
#endif
	test_KErrNone(r);

	TBuf8<KUsbDescSize_Interface> ifDescriptor;
	r = iPort[aPortNumber].GetInterfaceDescriptor(0, ifDescriptor);
	test_KErrNone(r);

	// Check the interface descriptor
	test(ifDescriptor[KIfcDesc_SettingOffset] == 0 && ifDescriptor[KIfcDesc_NumEndpointsOffset] == (*aIfPtr)->iInfoPtr->iTotalEndpointsUsed &&
		ifDescriptor[KIfcDesc_ClassOffset] == (*aIfPtr)->iInfoPtr->iClass.iClassNum &&
		ifDescriptor[KIfcDesc_SubClassOffset] == (*aIfPtr)->iInfoPtr->iClass.iSubClassNum &&
		ifDescriptor[KIfcDesc_ProtocolOffset] == (*aIfPtr)->iInfoPtr->iClass.iProtocolNum);

	if ((*aIfPtr)->iNumber != 0 && ifDescriptor[KIfcDesc_NumberOffset] != (*aIfPtr)->iNumber)
		{
		ifDescriptor[KIfcDesc_NumberOffset] = (*aIfPtr)->iNumber;
		r = iPort[aPortNumber].SetInterfaceDescriptor(0, ifDescriptor); 
		test_KErrNone(r);
		}
	else
		{
		(*aIfPtr)->iNumber = ifDescriptor[KIfcDesc_NumberOffset];	
		}
	TUint8 interfaceNumber = (*aIfPtr)->iNumber;
	TUSB_PRINT1 ("Interface Number %d",interfaceNumber);
	test.End();
	}


TInt CTransferServer::SetupLdds(TDes& aFileName)
	{
	TInt r;
	User::LeaveIfError(iFs.Connect());

	RDebug::Printf ("Configuration");
	
	RDebug::Printf ("Open configuration file");
	// set the session path to use the ROM if no drive specified
	r=iFs.SetSessionPath(_L("Z:\\test\\"));
	test_KErrNone(r);

	r = iConfigFile.Open(iFs, aFileName, EFileShareReadersOnly | EFileStreamText | EFileRead);
	test_KErrNone(r);
	RDebug::Printf("Configuration file %s Opened successfully", aFileName.PtrZ());

	RDebug::Printf ("Process configuration file");
	test(ProcessConfigFile (iConfigFile,NULL,&iLddPtr));
	
	iConfigFile.Close();
	iFs.Close();

	RDebug::Printf ("LDD in configuration file");
	test_NotNull(iLddPtr);
		
	LDDConfigPtr lddPtr = iLddPtr;
	TInt nextPort = 0;
	while (lddPtr != NULL)
		{
		// Load logical driver (LDD)
		// (There's no physical driver (PDD) with USB: it's a kernel extension DLL which
		//	was already loaded at boot time.)
		RDebug::Printf ("Loading USB LDD");
		TUSB_PRINT1("Loading USB LDD ",lddPtr->iName.PtrZ());
		r = User::LoadLogicalDevice(lddPtr->iName);
		test(r == KErrNone || r == KErrAlreadyExists);
	
		IFConfigPtr ifPtr = lddPtr->iIFPtr;
		
		RDebug::Printf ("Opening Channels");
		for (TInt portNumber = nextPort; portNumber < nextPort+lddPtr->iNumChannels; portNumber++)
			{
			test_Compare(lddPtr->iNumChannels,>,0);
			test_Compare(lddPtr->iNumChannels,==,1);

			// Open USB channel
			r = iPort[portNumber].Open(0);
			test_KErrNone(r);
			TUSB_PRINT("Successfully opened USB port");

			// Query the USB device/Setup the USB interface
			if (portNumber == nextPort)
				{
				// Change some descriptors to contain suitable values
				//SetupDescriptors(lddPtr, &iPort[portNumber]);
				}
				

			test_NotNull(ifPtr);
			
			if (iSupportResourceAllocationV2)
				{
				PopulateInterfaceResourceAllocation(ifPtr, portNumber);
				}
				
			SetupInterface(&ifPtr,portNumber);
					
	#ifdef USB_SC
//			RChunk *tChunk = &gChunk;
			test_KErrNone(iPort[portNumber].FinalizeInterface());
	#endif

			}
	
		iTotalChannels += lddPtr->iNumChannels;
		nextPort += lddPtr->iNumChannels;	
		lddPtr = lddPtr->iPtrNext;	
		}
		
	TUSB_PRINT("All Interfaces and Alternate Settings successfully set up");
	
	iTransferHandle->StartTimer();

	return KErrNone;
	}

void CTransferServer::QueryUsbClientL(LDDConfigPtr aLddPtr, RDEVCLIENT* aPort)
	{
	test.Start(_L("Query device and Endpoint Capabilities"));


	TUsbDeviceCaps d_caps;
	TInt r = aPort->DeviceCaps(d_caps);
	test_KErrNone(r);

	const TInt n = d_caps().iTotalEndpoints;

	TUSB_PRINT("###  USB device capabilities:");
	TUSB_PRINT1("Number of endpoints:				 %d", n);
	TUSB_PRINT1("Supports Software-Connect: 		 %s",
				d_caps().iConnect ? _S("yes") : _S("no"));
	TUSB_PRINT1("Device is Self-Powered:			 %s",
				d_caps().iSelfPowered ? _S("yes") : _S("no"));
	TUSB_PRINT1("Supports Remote-Wakeup:			 %s",
				d_caps().iRemoteWakeup ? _S("yes") : _S("no"));
	TUSB_PRINT1("Supports High-speed:				 %s",
				d_caps().iHighSpeed ? _S("yes") : _S("no"));
	TUSB_PRINT1("Supports unpowered cable detection: %s\n",
				(d_caps().iFeatureWord1 & KUsbDevCapsFeatureWord1_CableDetectWithoutPower) ?
				_S("yes") : _S("no"));
	TUSB_PRINT1("Supports endpoint resource allocation v2 scheme: %s\n",
				(d_caps().iFeatureWord1 & KUsbDevCapsFeatureWord1_EndpointResourceAllocV2) ?
				_S("yes") : _S("no"));					
	TUSB_PRINT("");

	iSoftwareConnect = d_caps().iConnect;					// we need to remember this
	test_Equal(aLddPtr->iSoftConnect,iSoftwareConnect);

	iSupportResourceAllocationV2 = ((d_caps().iFeatureWord1 & KUsbDevCapsFeatureWord1_EndpointResourceAllocV2) != 0);
	
	// only check capabilities if set; therefore allowing them to be disabled
	if (aLddPtr->iSelfPower)
		{
		test(d_caps().iSelfPowered);	
		}
	
	// only check capabilities if set; therefore allowing them to be disabled
	if (aLddPtr->iRemoteWakeup)
		{
		test(d_caps().iRemoteWakeup);		
		}

	test_Equal(d_caps().iFeatureWord1 & KUsbDevCapsFeatureWord1_CableDetectWithoutPower,aLddPtr->iFeatures);

	// only check capability if set; therefore allowing it to be disabled
	if (aLddPtr->iHighSpeed)
		{
		test(d_caps().iHighSpeed);		
		}
	
	test_Equal(aLddPtr->iNumEndpoints,n);

	// Endpoints
	TUsbcEndpointData data[KUsbcMaxEndpoints];
	TPtr8 dataptr(reinterpret_cast<TUint8*>(data), sizeof(data), sizeof(data));
	r = aPort->EndpointCaps(dataptr);
	test_KErrNone(r);

	TUSB_PRINT("### USB device endpoint capabilities:");
	for (TInt i = 0; i < n; i++)
		{
		const TUsbcEndpointCaps* caps = &data[i].iCaps;
		
		
		TBuf<40> sizeStr(_S("unknown"));
		if (caps->iSizes == KUsbEpNotAvailable)
			{
			sizeStr = _S("Not Available");	
			}		
		else
			{
			sizeStr.SetLength(0);
			if (caps->iSizes & KUsbEpSizeCont)
				sizeStr.Append(_S(" Continuous"),11);
			if (caps->iSizes & KUsbEpSize8)
				sizeStr.Append(_S(" 8"),2);
			if (caps->iSizes & KUsbEpSize16)
				sizeStr.Append(_S(" 16"),3);
			if (caps->iSizes & KUsbEpSize32)
				sizeStr.Append(_S(" 32"),3);
			if (caps->iSizes & KUsbEpSize64)
				sizeStr.Append(_S(" 64"),3);
			if (caps->iSizes & KUsbEpSize128)
				sizeStr.Append(_S(" 128"),4);
			if (caps->iSizes & KUsbEpSize256)
				sizeStr.Append(_S(" 256"),4);
			if (caps->iSizes & KUsbEpSize512)
				sizeStr.Append(_S(" 512"),4);
			if (caps->iSizes & KUsbEpSize1023)
				sizeStr.Append(_S(" 1023"),5);
			if (caps->iSizes & KUsbEpSize1024)
				sizeStr.Append(_S(" 1024"),5);
			}

		TBuf<40> typeStr(_S("unknown"));
		if (caps->iTypesAndDir == KUsbEpNotAvailable)
			typeStr = _S("Not Available");
		if (caps->iTypesAndDir & (KUsbEpTypeControl | KUsbEpTypeBulk | KUsbEpTypeInterrupt | KUsbEpTypeIsochronous))
			{
			typeStr.SetLength(0);
			if (caps->iTypesAndDir & KUsbEpTypeBulk)
				typeStr.Append(_S("Control "),8);
			if (caps->iTypesAndDir & KUsbEpTypeBulk)
				typeStr.Append(_S("Bulk "),5);
			if (caps->iTypesAndDir & KUsbEpTypeInterrupt)
				typeStr.Append(_S("Interrupt "),10);
			if (caps->iTypesAndDir & KUsbEpTypeIsochronous)
				typeStr.Append(_S("Isochronous"),11);			
			}
			
		TBuf<20> directionStr(_S("unknown"));
		
		if (caps->iTypesAndDir & KUsbEpDirIn)
			directionStr = _S("In");
		if (caps->iTypesAndDir & KUsbEpDirOut)
			directionStr = _S("Out");
		if (caps->iTypesAndDir & KUsbEpDirBidirect)
			directionStr = _S("Both");
				
		TUSB_PRINT4("Endpoint:%d Sizes =%s Type = %s - %s",
					i+1,sizeStr.PtrZ(), typeStr.PtrZ(), directionStr.PtrZ());
		}
	TUSB_PRINT("");

	test.End();
			
	}

