// Copyright (c) 2007-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:
// e32\drivers\trace\btrace.cpp
//


#include <kern_priv.h>
#include <kernel.h>
#include <platform.h>
#include "te_d32perfdrv.h"
//#include "te_instrumentationpoints.h"
#include "te_perfcacheflush.h"
#include "timer/te_perfkerneltimer.h"
#include "sanity/te_perfsanitytimer.h"




//
// Device driver
//

class DUptUTraceFactory : public DLogicalDevice
	{
public:
	DUptUTraceFactory();
	virtual TInt Install();
	virtual void GetCaps(TDes8& aDes) const;
	virtual TInt Create(DLogicalChannelBase*& aChannel);
	};

/**
 * We need this class to derive from DLogicalChannel rather than DLogicalChannelBase
 * This is for several reasons:
 * We are making calls that take a long time, which means we don't want the
 * user side thread to block while doing this, and we don't want to be interrupted
 * and loose our state. So we solve this by running the device driver as
 * as it's own separate thread in the kernel (DLogicalChannelBase runs a user thread in kernel mode,
 * DLogicalChannel runs a kernel thread called from a user thread).
 * We also solve the long timing issue by making an asynchrounous call by using the DoRequest call.
 * For this to work we need to create a DfcQ, i.e. a queue of all the incoming calls from the user side.
 * These then get's run in turn.
 */
class DUptUTraceChannel : public DLogicalChannel
	{
public:
	DUptUTraceChannel();
	virtual ~DUptUTraceChannel();
	//	Inherited from DObject
	/**
	 * I think this is similar to RunL when using active objects...
	 * Because we are now using a Dfc queue all calls are routed
	 * through this method.
	 * The aMsg.iValue are these: http://lon-engbuild130:6794/doc_source/EKA2DeviceDriver/WritingANewDeviceDriver/LogicalChannel.guide.html#DD%2dlogical%2dchannel%2emodel1%2ehandling%2dreqs%2eyou1
	 */
	virtual void HandleMsg(TMessageBase* aMsg);
	// Inherited from DLogicalChannel
	virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
	//Make an asynchronous call...
	virtual TInt DoRequest(TInt aReqNo, TRequestStatus* aStatus, TAny* a1, TAny* a2);

	void TestUptTraces(const TApiRunConfig& aApiRunConfig, TApiRunResults& aApiRunResults);
	void TimeUptTraces(const TApiRunConfig& aApiRunConfig, TApiRunResults& aApiRunResults);

private:
	//This current thread
	DThread*		iClient;
	//This is a queue of requests (calls) to this device driver
	TDfcQue* 		iDfcQ;

	TKernelTimer	iTimer;
 	//This is used to signal when one of the async calls (to DoRequest) is finished.
 	TRequestStatus	iStatus;
	};


DECLARE_STANDARD_LDD()
{
	return new DUptUTraceFactory;
}

EXPORT_C DLogicalDevice* CreateLogicalDevice();

//
// DBTraceFactory
//
DUptUTraceFactory::DUptUTraceFactory()
	{
	iVersion = RUptUTrace::VersionRequired();
	}


TInt DUptUTraceFactory::Install()
	{
#ifdef TE_UPT_TRACE_ENABLED
	return SetName(&KUptUTraceDDEnabled);
#else
	return SetName(&KUptUTraceDDDisabled);
#endif
	}

void DUptUTraceFactory::GetCaps(TDes8& aDes) const
	{
	Kern::InfoCopy(aDes,0,0);
	}

TInt DUptUTraceFactory::Create(DLogicalChannelBase*& aChannel)
	{
	aChannel=new DUptUTraceChannel();
	if(!aChannel)
		return KErrNoMemory;
	return KErrNone;
	}


//
// DBTraceChannel
//

DUptUTraceChannel::DUptUTraceChannel()
	{
	iClient = &Kern::CurrentThread();
	((DObject*)iClient)->Open();
	}

DUptUTraceChannel::~DUptUTraceChannel()
	{
	Kern::SafeClose((DObject*&)iClient,NULL);
	}

TInt DUptUTraceChannel::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/)
	{
	//skipping security checks and version control since this is just a test driver
	iDfcQ = Kern::DfcQue0();
	SetDfcQ(iDfcQ);
	iMsgQ.Receive();
	return KErrNone;
	}

void DUptUTraceChannel::HandleMsg(TMessageBase* aMsg)
	{
	TThreadMessage& message = *(TThreadMessage*) aMsg;
	TInt id = message.iValue;
	if(id == (TInt) ECloseMsg)
		{
		//Close DUptUTraceChannel
		message.Complete(KErrNone, EFalse);
		return;
		}
	else if(id == KMaxTInt)
		{
		//DoCancel(message.Int0());
		message.Complete(KErrNone, ETrue);
		return;
		}
	if(id < 0 )  //it was a call for DoRequest()
		{
		TRequestStatus* status = (TRequestStatus*)message.Ptr0();
		DoRequest(~id, status, message.Ptr1(), message.Ptr2());
		message.Complete(KErrNone, ETrue);
		}
	else //it was a call for DoControl, we dont implement that now so just fall through
		{
		TRequestStatus* status=(TRequestStatus*)message.Ptr0();
		Kern::RequestComplete(iClient,status,KErrNotSupported);
		message.Complete(KErrNotSupported, ETrue);
		}
	}

TInt DUptUTraceChannel::DoRequest(TInt aReqNo, TRequestStatus* aStatus, TAny* a1, TAny* a2)
	{
	TInt error = KErrNone;

/**
 * This is the beginning of the new implementation for creating a new thread
 * to run the timers in.
 *
	 NKern::ThreadEnterCS();
	{
		SThreadCreateInfo info;
		#ifdef DEBUG
			TPtrC8 name((TUint8*)get_thread_name(priority));
		#endif
		info.iType = EThreadSupervisor;
		info.iFunction = thread_wrapper;
		info.iPtr = thread;
		info.iSupervisorStack = NULL;
		info.iSupervisorStackSize = 0; // zero means use default value
		info.iInitialThreadPriority = hal_convert_priority(priority);
		info.iTotalSize = sizeof(info);
		#ifdef DEBUG
			info.iName.Set(name);
		#endif
		if (Kern::ThreadCreate(info) != KErrNone)
		{
		     rc = EINVAL;
		       goto Error;
		 }

		thread->func = func;
		thread->arg = arg;
			Kern::ThreadResume(*(DThread *)info.iHandle);
		}
		NKern::ThreadLeaveCS();


	//TInt NKern::ThreadCreate(NThread *aThread, SNThreadCreateInfo &anInfo);
	 *
	 * */
	if(!error)
		{
		switch(aReqNo)
			{
			case RUptUTrace::ETestUTrace:
				{
				TApiRunConfig configData;
			    error = Kern::ThreadRawRead(iClient, a1, (TUint8 *)&configData, sizeof(TApiRunConfig));
				TApiRunResults configResults;
			    error = Kern::ThreadRawRead(iClient, a2, (TUint8 *)&configResults, sizeof(TApiRunResults));
			    if(!error)
			    	TestUptTraces(configData, configResults);
			    if(!error)
			    	error = Kern::ThreadRawWrite(iClient, a2, (TUint8 *)&configResults, sizeof(TApiRunResults));
				}
			break;
			case RUptUTrace::ETimeUTrace:
				{
				TApiRunConfig configData;
			    error = Kern::ThreadRawRead(iClient, a1, (TUint8 *)&configData, sizeof(TApiRunConfig));
				TApiRunResults configResults;
			    error = Kern::ThreadRawRead(iClient, a2, (TUint8 *)&configResults, sizeof(TApiRunResults));
			    if(!error)
			    	{
			    	TimeUptTraces(configData, configResults);
			    	}
			    if(!error)
			    	error = Kern::ThreadRawWrite(iClient, a2, (TUint8 *)&configResults, sizeof(TApiRunResults));
				}
			break;
			case RUptUTrace::ESanityTestTimer:
			case RUptUTrace::ESanityUtraceTimer:
			case RUptUTrace::ESanityTestLongTimer:
				{
				TSanityResults results;
			    error = Kern::ThreadRawRead(iClient, a1, (TUint8 *)&results, sizeof(TSanityResults));
			    if(!error)
			    	{
			    	TTestTimer timer;
			    	if(aReqNo == RUptUTrace::ESanityTestTimer)
			    		results.iPass = timer.TestKernelTimer(results.iTime);
			    	if(aReqNo == RUptUTrace::ESanityTestLongTimer)
			    		results.iPass = timer.TestKernelLongTimer(results.iTime);
			    	if(aReqNo == RUptUTrace::ESanityUtraceTimer)
			    		results.iPass = timer.TestUTraceKernelTimer(results.iTime);
			    	}
			    if(!error)
			    	error = Kern::ThreadRawWrite(iClient, a1, (TUint8 *)&results, sizeof(TSanityResults));
				}
			break;

			default:
				error = KErrNotSupported;
			break;
			}
		}
	Kern::RequestComplete(iClient, aStatus, error);
	return error;
	}

void DUptUTraceChannel::TestUptTraces(const TApiRunConfig& /*aApiRunConfig*/, TApiRunResults& aApiRunResults)
	{
	//TInt error = TUptTraceCalls::DoSendTraceL(aApiRunConfig, aApiRunResults); //not implemented
	TInt error = KErrNotSupported;
	aApiRunResults.iError = error;
	}

void DUptUTraceChannel::TimeUptTraces(const TApiRunConfig& /*aApiRunConfig*/, TApiRunResults& aApiRunResults)
	{
/*	TInt error = iTimer.StartBackgroundTimer(); //not implemented
	if(!error)
		{
	    while(!iTimer.IsTimerExpired())
	   	 {
	   	 if(aApiRunConfig.iDoFlush)
	   		 {
	   		 Te_Flush_The_Cache();
	   		 }
	   	 	aApiRunResults.iError = TUptTraceCalls::DoSendTraceL(aApiRunConfig, aApiRunResults);
	   	 	aApiRunResults.iError = KErrNotSupported;
	   	 	iTimer.IncreaseCount();
	   	 }
	    aApiRunResults.iApiTraceTime = iTimer.FinalTime();
	    aApiRunResults.iTimeCount = iTimer.FinalCount();
		}
		*/
 	aApiRunResults.iError = KErrNotSupported;
	}
