kerneltest/e32test/system/d_tick.cpp
changeset 0 a41df078684a
child 87 2f92ad2dc5db
child 133 2a0ada0a1bf8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/system/d_tick.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,686 @@
+// 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 the License "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:
+// e32test\system\d_tick.cpp
+// LDD for testing tick-based timers
+// 
+//
+
+#include "platform.h"
+#include <assp.h>
+#if defined(__MEIG__)
+#include <cl7211.h>
+#elif defined(__MAWD__)
+#include <windermere.h>
+#elif defined(__MISA__)
+#include <sa1100.h>
+#elif defined(__MCOT__)
+#include <cotulla.h>
+#elif defined(__MI920__) || defined(__NI1136__)
+#include <integratorap.h>
+#elif defined(__IS_OMAP1510__) || defined(__IS_OMAP1610__) 
+#include <omap.h>
+#include <omap_timer.h>
+#elif defined(__WINS__)
+#include "nk_priv.h"
+#elif defined(__RVEMUBOARD__)
+#include <rvemuboard.h>
+#elif defined(__NE1_TB__)
+#include <upd35001_timer.h>
+#endif
+#include <kernel/kern_priv.h>
+#include "d_tick.h"
+#include "../misc/prbs.h"
+
+#if defined(__WINS__)
+typedef Int64 TCounter;
+typedef Int64 TDelta;
+#else
+typedef TUint TCounter;
+typedef TInt TDelta;
+#endif
+
+#if defined(__MISA__)|| defined(__MCOT__)
+inline TCounter TIMER()
+	{ return *(volatile TUint*)KHwRwOstOscr; }
+#endif
+#if defined(__IS_OMAP1510__) || defined(__IS_OMAP1610__) 
+inline TCounter TIMER()
+	{ return TOmapTimer::Timer3Value(); }
+#endif
+#ifdef __MAWD__
+inline TCounter TIMER()
+	{ return *(volatile TUint*)(KWindBaseAddress+KWindTimer1Value16)&0xffff; }
+#endif
+#ifdef __MEIG__
+inline TCounter TIMER()
+	{ return *(volatile TUint*)(KEigerBaseAddress+KEigerTimer1Data16)&0xffff; }
+#endif
+#if defined(__MI920__) || defined(__NI1136__)
+inline TCounter TIMER()
+	{ return *(volatile TUint*)(KHwCounterTimer1+KHoTimerValue)&0xffff;}
+#endif
+#if defined(__RVEMUBOARD__)
+inline TCounter TIMER()
+	{ return *(volatile TUint*)(KHwCounterTimer1+KHoTimerValue)&0xffff;}
+#endif
+#if defined(__NE1_TB__)
+inline TCounter TIMER()
+	{ return NETimer::Timer(2).iTimerCount; }
+#endif
+#if defined(__EPOC32__) && defined(__CPU_X86)
+TCounter TIMER();
+void SetUpTimerChannel2();
+#endif
+#ifdef __WINS__
+inline TCounter TIMER()
+	{
+	LARGE_INTEGER c;
+	QueryPerformanceCounter(&c);
+	return c.QuadPart;
+	}
+#endif
+
+#if defined(__MISA__) || defined(__MCOT__)
+inline TDelta TimeDelta(TCounter initial, TCounter final)
+	{ return final-initial; }				// SA1100 timer counts up
+inline TInt LongTimeDelta(TCounter initial, TCounter final, TUint, TUint)
+	{ return final-initial; }				// SA1100 timer counts up
+#endif
+#if defined(__IS_OMAP1510__) || defined(__IS_OMAP1610__)
+inline TDelta TimeDelta(TCounter initial, TCounter final)
+	{ return initial-final; }				// OMAP timer counts down
+inline TInt LongTimeDelta(TCounter initial, TCounter final, TUint, TUint)
+	{ return initial-final; }
+#endif
+#if defined(__MI920__) || defined(__NI1136__)
+inline TDelta TimeDelta(TCounter initial, TCounter final)
+	{ return (initial-final)&0xffff; }		// Integrator timer counts down
+TInt LongTimeDelta(TCounter initial, TCounter final, TUint init_ms, TUint final_ms)
+	{
+	TUint r=(initial-final)&0xffff;			// Integrator timer counts down
+	TUint ms=final_ms-init_ms;
+	ms=2*ms-r;
+	ms=(ms+32768)&~0xffff;
+	return r+ms;
+	}
+#endif
+#if defined(__RVEMUBOARD__)
+inline TDelta TimeDelta(TCounter initial, TCounter final)
+	{ return (initial-final)&0xffff; }		// Timer counts down
+TInt LongTimeDelta(TCounter initial, TCounter final, TUint init_ms, TUint final_ms)
+	{
+	TUint r=(initial-final)&0xffff;			// Timer counts down
+	TUint ms=final_ms-init_ms;
+	ms=2*ms-r;
+	ms=(ms+32768)&~0xffff;
+	return r+ms;
+	}
+#endif
+#if defined(__NE1_TB__)
+inline TDelta TimeDelta(TCounter initial, TCounter final)
+	{ return final - initial; }
+inline TDelta LongTimeDelta(TCounter initial, TCounter final, TUint, TUint)
+	{ return final - initial; }
+#endif
+#if defined(__MAWD__) || defined(__MEIG__)
+inline TDelta TimeDelta(TCounter initial, TCounter final)
+	{ return (initial-final)&0xffff; }		// Eiger/Windermere timer counts down
+TInt LongTimeDelta(TCounter initial, TCounter final, TUint init_ms, TUint final_ms)
+	{
+	TUint r=(initial-final)&0xffff;			// Eiger/Windermere timer counts down
+	TUint ms=final_ms-init_ms;
+	ms=2*ms-r;
+	ms=(ms+32768)&~0xffff;
+	return r+ms;
+	}
+#endif
+#if defined(__EPOC32__) && defined(__CPU_X86)
+TDelta TimeDelta(TUint initial, TUint final)
+	{
+	TUint tickdiff=(initial-final)&0xffff;
+	TUint msdiff=((final>>16)-(initial>>16))&0xffff;
+	msdiff=1193*msdiff-tickdiff;
+	msdiff=(msdiff+32768)&~0xffff;
+	return msdiff+tickdiff;
+	}
+
+TInt LongTimeDelta(TUint initial, TUint final, TUint init_ms, TUint final_ms)
+	{
+	TUint r=(initial-final)&0xffff;			// PC timer counts down
+	TUint ms=final_ms-init_ms;
+	ms=1193*ms-r;
+	ms=(ms+32768)&~0xffff;
+	return r+ms;
+	}
+#endif
+#ifdef __WINS__
+inline TDelta TimeDelta(TCounter initial, TCounter final)
+	{ return final-initial; }		// counts up
+inline TDelta LongTimeDelta(TCounter initial, TCounter final, TUint, TUint)
+	{ return final-initial; }		// counts up
+#endif
+
+const TInt KMajorVersionNumber=0;
+const TInt KMinorVersionNumber=1;
+const TInt KBuildVersionNumber=1;
+
+const TInt KDaysFrom0ADTo2000AD=730497;		// See US_TIME.CPP to verify this
+const TInt KSecondsPerDay=86400;
+
+TUint TicksToMicroseconds(TDelta aTicks)
+	{
+#if defined(__MISA__) || defined(__MCOT__)
+	Int64 ticks(aTicks);
+	ticks*=(1000000);
+	ticks+=KHwOscFreqHz/2;		// 3.6864MHz tick
+	ticks/=KHwOscFreqHz;
+	return (TUint)ticks;
+#endif
+#if defined(__IS_OMAP1510__) || defined(__IS_OMAP1610__) 
+	// Timer runs at 12Mhz/32 = 375kHz. Each tick is 2.66...us which is 16/6us
+	aTicks<<=4;		// * 16
+	aTicks+=3;	    // rounding to the closest number of us
+	return (TInt)(aTicks/6);	// us = (ticks*16+3)/6
+#endif
+#if defined(__MI920__) || defined(__NI1136__)
+	aTicks<<=14;	// 1 tick = 32/3 us
+	aTicks+=768;	// round
+	return (TInt)(aTicks/1536);
+#endif
+#if defined(__RVEMUBOARD__)
+	return (TInt)(aTicks*256);  // 1 tick = 256 us
+#endif
+#if defined(__NE1_TB__)
+	NETimer& T2 = NETimer::Timer(2);
+	TUint prescale = __e32_find_ms1_32(T2.iPrescaler & 0x3f);
+	TUint f = 66666667 >> prescale;
+	TUint64 x = I64LIT(1000000);
+	x *= TUint64(aTicks);
+	x += TUint64(f>>1);
+	x /= TUint64(f);
+	return (TUint)x;
+#endif
+#if defined(__MAWD__) || defined(__MEIG__)
+	return aTicks*500;					// 2kHz tick
+#endif
+#if defined(__EPOC32__) && defined(__CPU_X86)
+	return (aTicks*8381+4190)/10000;
+#endif
+#ifdef __WINS__
+	LARGE_INTEGER f;
+	QueryPerformanceFrequency(&f);
+	aTicks*=1000000;
+	aTicks+=f.QuadPart/2;
+	aTicks/=f.QuadPart;
+	return (TUint)aTicks;
+#endif
+	}
+
+class DTick;
+class TTickTimer
+	{
+public:
+	enum TMode
+		{
+		EOneShot,
+		EPeriodic,
+		EAbsolute,
+		ETickDelay,
+		};
+public:
+	TTickTimer();
+	TInt Start(TInt aMode, TUint aMin, TUint aRange, TInt aCount);
+	void Cancel();
+	void CompleteClient(TInt aValue);
+	static void TickCallBack(TAny* aPtr);
+	static void SecondCallBack(TAny* aPtr);
+public:
+	TTickLink iTickLink;
+	TSecondLink iSecondLink;
+	TMode iMode;
+	TInt iInterval;
+	TTimeK iExpiryTime;
+	TUint iMin;
+	TUint iRange;
+	TInt iParam;
+	TCounter iStartTime0;
+	TUint iStartTime1;
+	TCounter iStartTime;
+	TInt iMinErr;
+	TInt iMaxErr;
+	Int64 iTotalErr;
+	TInt iCount;
+	TInt iRequestedCount;
+	TInt iIgnore;
+	DTick* iLdd;
+	TInt iId;
+	TClientRequest* iRequest;
+	};
+
+class DTickFactory : public DLogicalDevice
+//
+// Tick timer LDD factory
+//
+	{
+public:
+	DTickFactory();
+	virtual TInt Install();						//overriding pure virtual
+	virtual void GetCaps(TDes8& aDes) const;	//overriding pure virtual
+	virtual TInt Create(DLogicalChannelBase*& aChannel);	//overriding pure virtual
+	};
+
+class DTick : public DLogicalChannelBase
+//
+// Tick timer LDD channel
+//
+	{
+public:
+	DTick();
+	~DTick();
+protected:
+	virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
+	virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
+public:
+	void TimerExpired(TInt anId);
+	inline DThread* Client() { return iThread; }
+public:
+	DThread* iThread;
+	TInt iTickPeriodUs;
+	TTimeK iYear2000;
+	TTickTimer iTickTimer[KMaxTimers];
+	TUint iSeed[2];
+	};
+
+TTickTimer::TTickTimer()
+	:	iMode(EOneShot),
+		iInterval(0),
+		iExpiryTime(0),
+		iMin(0),
+		iRange(1),
+		iParam(0),
+		iStartTime0(0),
+		iStartTime1(0),
+		iStartTime(0),
+		iMinErr(KMaxTInt),
+		iMaxErr(KMinTInt),
+		iTotalErr(0),
+		iCount(0),
+		iRequestedCount(0),
+		iIgnore(0),
+		iLdd(NULL),
+		iId(0),
+		iRequest(NULL)
+	{
+	}
+
+void TTickTimer::TickCallBack(TAny* aPtr)
+	{
+	TCounter timer_val=TIMER();
+	TTickTimer& m=*(TTickTimer*)aPtr;
+	TDelta time=TimeDelta(m.iStartTime, timer_val);
+	TInt time_us=TicksToMicroseconds(time);
+	TInt rounded_interval=((m.iInterval+500)/1000)*1000;
+	TInt error=time_us-rounded_interval;
+	if (!m.iIgnore)
+		{
+		if (error<m.iMinErr)
+			m.iMinErr=error;
+		if (error>m.iMaxErr)
+			m.iMaxErr=error;
+		m.iTotalErr+=error;
+		}
+	if (m.iIgnore==1 && m.iMode==EPeriodic)
+		{
+		m.iStartTime0=timer_val;
+		m.iStartTime1=NKern::TickCount();
+		}
+	if ((m.iIgnore && m.iIgnore--) || (++m.iCount<m.iRequestedCount))
+		{
+		if (m.iMode==EOneShot)
+			{
+			TUint rnd=Random(m.iLdd->iSeed);
+			TUint ticks=(rnd%m.iRange)+m.iMin;
+			m.iInterval=ticks*m.iLdd->iTickPeriodUs;
+			m.iStartTime=TIMER();
+			m.iTickLink.OneShot(m.iInterval, TickCallBack, &m);
+			}
+		else if (m.iMode==ETickDelay)
+			{
+			m.iStartTime=TIMER();
+			m.iTickLink.OneShot(m.iInterval, TickCallBack, &m);
+			NKern::Sleep(m.iRange);
+			}
+		else
+			m.iStartTime=timer_val;
+		return;
+		}
+	m.CompleteClient(KErrNone);
+	if (m.iMode==EPeriodic)
+		{
+		m.iStartTime0=LongTimeDelta(m.iStartTime0, timer_val, m.iStartTime1, NKern::TickCount());
+		m.iTickLink.Cancel();
+		}
+	}
+
+void TTickTimer::SecondCallBack(TAny* aPtr)
+	{
+	TTickTimer& m=*(TTickTimer*)aPtr;
+	TTimeK now=Kern::SystemTime();
+	Int64 error=now-m.iExpiryTime;
+	if (error>KMaxTInt)
+		error=KMaxTInt;
+	if (error<KMinTInt)
+		error=KMinTInt;
+	if (error<m.iMinErr)
+		m.iMinErr=(TInt)error;
+	if (error>m.iMaxErr)
+		m.iMaxErr=(TInt)error;
+	m.iTotalErr+=error;
+	if (++m.iCount<m.iRequestedCount)
+		{
+		TUint rnd=Random(m.iLdd->iSeed);
+		TUint secs=(rnd%m.iRange)+m.iMin;
+		m.iExpiryTime=now+secs*1000000+999999;
+		m.iExpiryTime/=1000000;
+		m.iExpiryTime*=1000000;
+		m.iSecondLink.At(m.iExpiryTime, SecondCallBack, &m);
+		return;
+		}
+	m.CompleteClient(KErrNone);
+	}
+
+TInt TTickTimer::Start(TInt aMode, TUint a1, TUint a2, TInt aCount)
+	{
+	TInt r=KErrGeneral;
+	iMode=(TMode)aMode;
+	iMin=a1;
+	iRange=a2;
+	iMinErr=KMaxTInt;
+	iMaxErr=KMinTInt;
+	iTotalErr=0;
+	iCount=0;
+	iRequestedCount=aCount;
+	switch (aMode)
+		{
+		case EOneShot:
+			{
+			TUint rnd=Random(iLdd->iSeed);
+			TUint ticks=(rnd%iRange)+iMin;
+			iInterval=ticks*iLdd->iTickPeriodUs;
+			iStartTime=TIMER();
+			iStartTime0=0;
+			iStartTime1=0;
+			iTickLink.OneShot(iInterval, TickCallBack, this);
+			iIgnore=Min(aCount-1,1);
+			r=KErrNone;
+			break;
+			}
+		case EPeriodic:
+			{
+			iInterval=iMin*iLdd->iTickPeriodUs;
+			iStartTime=TIMER();
+			iStartTime0=iStartTime;
+			iStartTime1=NKern::TickCount();
+			iTickLink.Periodic(iInterval, TickCallBack, this);
+			iIgnore=Min(aCount-1,1);
+			r=KErrNone;
+			break;
+			}
+		case EAbsolute:
+			{
+			TUint rnd=Random(iLdd->iSeed);
+			TUint secs=(rnd%iRange)+iMin;
+			TTimeK now=Kern::SystemTime();
+			iExpiryTime=now+secs*1000000+999999;
+			iExpiryTime/=1000000;
+			iExpiryTime*=1000000;
+			iSecondLink.At(iExpiryTime, SecondCallBack, this);
+			iIgnore=0;
+			iStartTime0=0;
+			iStartTime1=0;
+			r=KErrNone;
+			break;
+			}
+		case ETickDelay:
+			{
+			iInterval=iMin*iLdd->iTickPeriodUs;
+			iStartTime=TIMER();
+			iStartTime0=0;
+			iStartTime1=0;
+			iTickLink.OneShot(iInterval, TickCallBack, this);
+			iIgnore=Min(aCount-1,1);
+			r=KErrNone;
+			break;
+			}
+		default:
+			break;
+		}
+	return r;
+	}
+
+void TTickTimer::CompleteClient(TInt aValue)
+	{
+	Kern::QueueRequestComplete(iLdd->Client(),iRequest,aValue);
+	}
+
+void TTickTimer::Cancel()
+	{
+	iTickLink.Cancel();
+	iSecondLink.Cancel();
+	CompleteClient(KErrCancel);
+	}
+
+DECLARE_STANDARD_LDD()
+	{
+    return new DTickFactory;
+    }
+
+DTickFactory::DTickFactory()
+    {
+    iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
+    //iParseMask=0;//No units, no info, no PDD
+    //iUnitsMask=0;//Only one thing
+    }
+
+TInt DTickFactory::Create(DLogicalChannelBase*& aChannel)
+//
+// Create a new DTick on this logical device
+//
+    {
+	aChannel=new DTick;
+	return aChannel?KErrNone:KErrNoMemory;
+    }
+
+TInt DTickFactory::Install()
+//
+// Install the LDD - overriding pure virtual
+//
+    {
+    return SetName(&KTickTestLddName);
+    }
+
+void DTickFactory::GetCaps(TDes8& aDes) const
+//
+// Get capabilities - overriding pure virtual
+//
+    {
+    TCapsTickTestV01 b;
+    b.iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
+    Kern::InfoCopy(aDes,(TUint8*)&b,sizeof(b));
+    }
+
+DTick::DTick()
+//
+// Constructor
+//
+    {
+	TInt i;
+	for (i=0; i<KMaxTimers; i++)
+		{
+		iTickTimer[i].iLdd=this;
+		iTickTimer[i].iId=i;
+		}
+	iSeed[0]=NKern::TickCount();
+	iSeed[1]=0;
+	iTickPeriodUs=Kern::TickPeriod();
+
+	// Careful with the constants here or GCC will get it wrong
+	iYear2000=Int64(KDaysFrom0ADTo2000AD)*Int64(KSecondsPerDay);
+	iYear2000*=1000000;
+
+	iThread=&Kern::CurrentThread();
+	iThread->Open();
+    }
+
+TInt DTick::DoCreate(TInt /*aUnit*/, const TDesC8* /*anInfo*/, const TVersion& aVer)
+//
+// Create channel
+//
+    {
+
+    if (!Kern::QueryVersionSupported(TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber),aVer))
+		return KErrNotSupported;
+
+	for (TInt i=0; i<KMaxTimers; i++)
+		{
+		TInt r = Kern::CreateClientRequest(iTickTimer[i].iRequest);
+		if (r != KErrNone)
+			return r;
+		}
+	
+#if defined(__IS_OMAP1510__) || defined(__IS_OMAP1610__)
+	// Set up Timer3 as a free-running timer at 12Mhz/32 = 375kHz
+	TOmapTimer::SetTimer3Ctrl(	TOmapTimer::KHtOSTimer_Cntl_Ar
+									| TOmapTimer::KHtOSTimer_Cntl_Free
+									| TOmapTimer::KHtOSTimer_Cntl_ClkEnable );
+	TOmapTimer::SetTimer3Prescale( TOmapTimer::EPrescaleBy32 );
+	// Autoreload 0xFFFFFFFF to effectively wrap from zero back to 0xFFFFFFFF
+	TOmapTimer::SetTimer3LoadTim( 0xFFFFFFFF );
+	TOmapTimer::StartTimer3();
+#endif
+#if defined(__MI920__) || defined(__NI1136__)
+	// Set up timer 1 as free running 93.75KHz clock
+    TIntegratorAP::SetTimerLoad(TIntegratorAP::ECounterTimer1, 0);			// start from 0xffff downwards
+    TIntegratorAP::SetTimerMode(TIntegratorAP::ECounterTimer1, TIntegratorAP::ETimerModeFreeRunning);
+    TIntegratorAP::SetTimerPreScale(TIntegratorAP::ECounterTimer1, TIntegratorAP::ETimerPreScaleDiv256);	// 93.75kHz wrap 699ms
+    TIntegratorAP::EnableTimer(TIntegratorAP::ECounterTimer1, TIntegratorAP::EEnable);
+	TIntegratorAP::DisableIrq(TIntegratorAP::EIrqSet0,EIntIdTimer0);		// make sure timer int is disabled
+#endif
+#if defined(__RVEMUBOARD__)
+	// Switch timer 1 to a 1MHz clock in the system controller Ctrl register
+	TRvEmuBoard::SetSCCtrl(KTimer1EnSel);
+
+	// Set up timer 1 as free running 3.90625kHz clock
+	TRvEmuBoard::SetTimerMode(KHwCounterTimer1, TRvEmuBoard::ETimerModeFreeRunning);
+	TRvEmuBoard::SetTimerPreScale(KHwCounterTimer1, TRvEmuBoard::ETimerPreScaleDiv256);// 3.90625kHz wrap 16.777s
+	TRvEmuBoard::EnableTimer(KHwCounterTimer1, TRvEmuBoard::EEnable);
+#endif
+#if defined(__NE1_TB__)
+	// nothing to do since we use fast counter
+#endif
+#ifdef __MAWD__
+	// Set up timer 1 as free running 2kHz clock
+	TWind::SetBuzzerControl(0);		// disable buzzer
+	TWind::SetTimer1Control(KWindTimer1ControlTimerEnable);
+	TWind::SetTimer1Load(0);
+#endif
+#ifdef __MEIG__
+	// Set up timer 1 as free running 2kHz clock
+	TEiger::ModifyControl21(KEigerControlTimer1PreOrFree|KEigerControlTimer1K512OrK2|
+							KEigerControlBuzzerToggle|KEigerControlBuzzerTimer1OrToggle,0);
+	TEiger::SetTimer1Data(0);
+#endif
+#if defined(__EPOC32__) && defined(__CPU_X86)
+	// Set up timer channel 2 as free running counter at 14318180/12 Hz
+	SetUpTimerChannel2();
+#endif
+	return KErrNone;
+	}
+
+DTick::~DTick()
+//
+// Destructor
+//
+    {
+	TInt i;
+	for (i=0; i<KMaxTimers; i++)
+		{
+		iTickTimer[i].Cancel();
+		Kern::DestroyClientRequest(iTickTimer[i].iRequest);
+		}
+	Kern::SafeClose((DObject*&)iThread, NULL);
+    }
+
+TInt DTick::Request(TInt aFunction, TAny* a1, TAny* a2)
+//
+// Runs in context of client thread, system unlocked on entry and exit
+//
+	{
+	NKern::ThreadEnterCS();			// stop thread kills
+	TInt r=KErrNone;
+	TInt id=(TInt)a1;
+	TTickTimer& m=iTickTimer[id];
+	switch (aFunction)
+		{
+		case RTickTest::EControlStart:
+			{
+			STimerStartInfo info;
+			kumemget(&info,a2,sizeof(info));
+			r=m.iRequest->SetStatus(info.iStatus);
+			if (r==KErrNone)
+				r=m.Start(info.iMode, info.iMin, info.iRange, info.iCount);
+			break;
+			}
+		case RTickTest::EControlStop:
+			{
+			m.Cancel();
+			break;
+			}
+		case RTickTest::EControlGetInfo:
+			{
+			STickTestInfo info;
+			info.iMinErr=m.iMinErr;
+			info.iMaxErr=m.iMaxErr;
+			info.iCount=m.iCount;
+			info.iRequestedCount=m.iRequestedCount;
+			Int64 avg=m.iTotalErr/m.iCount;
+			info.iAvgErr=(TInt)avg;
+			info.iTotalTime=TicksToMicroseconds(m.iStartTime0);
+			kumemput(a2,&info,sizeof(info));
+			break;
+			}
+		case RTickTest::EControlReadRtc:
+			{
+			TInt hwrtc;
+			Arch::TheAsic()->SystemTimeInSecondsFrom2000(hwrtc);
+			*(TTimeK*)a1=Kern::SystemTime();
+			TTimeK hwtimeK=(TTimeK)hwrtc;
+			hwtimeK*=1000000;
+			hwtimeK+=iYear2000;
+			kumemput(a2,&hwtimeK,sizeof(hwtimeK));
+			break;
+			}
+		case RTickTest::EControlGetTickPeriod:
+			{
+			r=iTickPeriodUs;
+			break;
+			}
+		default:
+			r=KErrNotSupported;
+			break;
+		}
+	NKern::ThreadLeaveCS();
+	return r;
+	}
+