os/kernelhwsrv/kernel/eka/nkern/nk_timer.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
sl@0
     1
// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0
     2
// All rights reserved.
sl@0
     3
// This component and the accompanying materials are made available
sl@0
     4
// under the terms of the License "Eclipse Public License v1.0"
sl@0
     5
// which accompanies this distribution, and is available
sl@0
     6
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0
     7
//
sl@0
     8
// Initial Contributors:
sl@0
     9
// Nokia Corporation - initial contribution.
sl@0
    10
//
sl@0
    11
// Contributors:
sl@0
    12
//
sl@0
    13
// Description:
sl@0
    14
// e32\nkern\nk_timer.cpp
sl@0
    15
// Fast Millisecond Timer Implementation
sl@0
    16
// This file is just a template - you'd be mad not to machine code this
sl@0
    17
// 
sl@0
    18
//
sl@0
    19
sl@0
    20
#include "nk_priv.h"
sl@0
    21
sl@0
    22
const TInt KTimerQDfcPriority=6;
sl@0
    23
sl@0
    24
GLDEF_D NTimerQ TheTimerQ;
sl@0
    25
sl@0
    26
#ifndef __MSTIM_MACHINE_CODED__
sl@0
    27
#ifdef _DEBUG
sl@0
    28
#define __DEBUG_CALLBACK(n)	{if (iDebugFn) (*iDebugFn)(iDebugPtr,n);}
sl@0
    29
#else
sl@0
    30
#define __DEBUG_CALLBACK(n)
sl@0
    31
#endif
sl@0
    32
sl@0
    33
sl@0
    34
/** Starts a nanokernel timer in one-shot mode with ISR callback.
sl@0
    35
	
sl@0
    36
	Queues the timer to expire in the specified number of nanokernel ticks. The
sl@0
    37
	actual wait time will be at least that much and may be up to one tick more.
sl@0
    38
	The expiry handler will be called in ISR context.
sl@0
    39
	
sl@0
    40
	Note that NKern::TimerTicks() can be used to convert milliseconds to ticks.
sl@0
    41
sl@0
    42
	@param	aTime Timeout in nanokernel ticks
sl@0
    43
	
sl@0
    44
	@return	KErrNone if no error; KErrInUse if timer is already active.
sl@0
    45
	
sl@0
    46
	@pre	Any context
sl@0
    47
	
sl@0
    48
	@see    NKern::TimerTicks()
sl@0
    49
 */
sl@0
    50
EXPORT_C TInt NTimer::OneShot(TInt aTime)
sl@0
    51
	{
sl@0
    52
	return OneShot(aTime,FALSE);
sl@0
    53
	}
sl@0
    54
sl@0
    55
sl@0
    56
/** Starts a nanokernel timer in one-shot mode with ISR or DFC callback.
sl@0
    57
	
sl@0
    58
	Queues the timer to expire in the specified number of nanokernel ticks. The
sl@0
    59
	actual wait time will be at least that much and may be up to one tick more.
sl@0
    60
	The expiry handler will be called in either ISR context or in the context
sl@0
    61
	of the nanokernel timer thread (DfcThread1).
sl@0
    62
sl@0
    63
    Note that NKern::TimerTicks() can be used to convert milliseconds to ticks.
sl@0
    64
sl@0
    65
	@param	aTime Timeout in nanokernel ticks
sl@0
    66
	@param	aDfc TRUE if DFC callback required, FALSE if ISR callback required.
sl@0
    67
	
sl@0
    68
	@return	KErrNone if no error; KErrInUse if timer is already active.
sl@0
    69
	
sl@0
    70
	@pre	Any context
sl@0
    71
	
sl@0
    72
	@see    NKern::TimerTicks()
sl@0
    73
 */
sl@0
    74
EXPORT_C TInt NTimer::OneShot(TInt aTime, TBool aDfc)
sl@0
    75
	{
sl@0
    76
	__NK_ASSERT_DEBUG(aTime>=0);
sl@0
    77
sl@0
    78
	/** iFunction could be set to NULL after NTimer::OneShot(TInt, TDfc&) call.
sl@0
    79
	Call-back mechanism cannot be changed in the life time of a timer. */
sl@0
    80
	__NK_ASSERT_DEBUG(iFunction!=NULL); 
sl@0
    81
sl@0
    82
	TInt irq=NKern::DisableAllInterrupts();
sl@0
    83
	if (iState!=EIdle)
sl@0
    84
		{
sl@0
    85
		NKern::RestoreInterrupts(irq);
sl@0
    86
		return KErrInUse;
sl@0
    87
		}
sl@0
    88
	iCompleteInDfc=TUint8(aDfc?1:0);
sl@0
    89
	iTriggerTime=TheTimerQ.iMsCount+(TUint32)aTime;
sl@0
    90
	TheTimerQ.Add(this);
sl@0
    91
	NKern::RestoreInterrupts(irq);
sl@0
    92
	return KErrNone;
sl@0
    93
	}
sl@0
    94
sl@0
    95
/** Starts a nanokernel timer in one-shot mode with callback in dfc thread that provided DFC belongs to.
sl@0
    96
	
sl@0
    97
	Queues the timer to expire in the specified number of nanokernel ticks. The
sl@0
    98
	actual wait time will be at least that much and may be up to one tick more.
sl@0
    99
	On expiry aDfc will be queued in ISR context.
sl@0
   100
sl@0
   101
    Note that NKern::TimerTicks() can be used to convert milliseconds to ticks.
sl@0
   102
sl@0
   103
	@param	aTime Timeout in nanokernel ticks
sl@0
   104
	@param	aDfc - Dfc to be queued when the timer expires.
sl@0
   105
	
sl@0
   106
	@return	KErrNone if no error; KErrInUse if timer is already active.
sl@0
   107
	
sl@0
   108
	@pre	Any context
sl@0
   109
	
sl@0
   110
	@see    NKern::TimerTicks()
sl@0
   111
 */
sl@0
   112
EXPORT_C TInt NTimer::OneShot(TInt aTime, TDfc& aDfc)
sl@0
   113
	{
sl@0
   114
	__NK_ASSERT_DEBUG(aTime>=0);
sl@0
   115
	TInt irq=NKern::DisableAllInterrupts();
sl@0
   116
	if (iState!=EIdle)
sl@0
   117
		{
sl@0
   118
		NKern::RestoreInterrupts(irq);
sl@0
   119
		return KErrInUse;
sl@0
   120
		}
sl@0
   121
	iCompleteInDfc = 0;
sl@0
   122
	iFunction = NULL;
sl@0
   123
	iPtr = (TAny*) &aDfc;
sl@0
   124
	iTriggerTime=TheTimerQ.iMsCount+(TUint32)aTime;
sl@0
   125
	TheTimerQ.Add(this);
sl@0
   126
	NKern::RestoreInterrupts(irq);
sl@0
   127
	return KErrNone;
sl@0
   128
	}
sl@0
   129
sl@0
   130
sl@0
   131
/** Starts a nanokernel timer in zero-drift periodic mode with ISR or DFC callback.
sl@0
   132
sl@0
   133
	Queues the timer to expire in the specified number of nanokernel ticks,
sl@0
   134
	measured from the time at which it last expired. This allows exact periodic
sl@0
   135
	timers to be implemented with no drift caused by delays in requeueing the
sl@0
   136
	timer.
sl@0
   137
sl@0
   138
	The expiry handler will be called in the same context as the previous timer
sl@0
   139
	expiry. Generally the way this is used is that NTimer::OneShot() is used to start 
sl@0
   140
	the first time interval and this specifies whether the callback is in ISR context 
sl@0
   141
	or in the context of the nanokernel timer thread (DfcThread1) or other Dfc thread.
sl@0
   142
	The expiry handler then uses NTimer::Again() to requeue the timer.
sl@0
   143
sl@0
   144
	@param	aTime Timeout in nanokernel ticks
sl@0
   145
sl@0
   146
	@return	KErrNone if no error; KErrInUse if timer is already active;
sl@0
   147
	        KErrArgument if the requested expiry time is in the past.
sl@0
   148
	        
sl@0
   149
	@pre	Any context
sl@0
   150
 */
sl@0
   151
EXPORT_C TInt NTimer::Again(TInt aTime)
sl@0
   152
//
sl@0
   153
// Wait aTime from last trigger time - used for periodic timers
sl@0
   154
//
sl@0
   155
	{
sl@0
   156
	__NK_ASSERT_DEBUG(aTime>0);
sl@0
   157
	TInt irq=NKern::DisableAllInterrupts();
sl@0
   158
	if (iState!=EIdle)
sl@0
   159
		{
sl@0
   160
		NKern::RestoreInterrupts(irq);
sl@0
   161
		return KErrInUse;
sl@0
   162
		}
sl@0
   163
	TUint32 nextTick=TheTimerQ.iMsCount;
sl@0
   164
	TUint32 trigger=iTriggerTime+(TUint32)aTime;
sl@0
   165
	TUint32 d=trigger-nextTick;
sl@0
   166
	if (d>=0x80000000)
sl@0
   167
		{
sl@0
   168
		NKern::RestoreInterrupts(irq);
sl@0
   169
		return KErrArgument;		// requested time is in the past
sl@0
   170
		}
sl@0
   171
	iTriggerTime=trigger;
sl@0
   172
	TheTimerQ.Add(this);
sl@0
   173
	NKern::RestoreInterrupts(irq);
sl@0
   174
	return KErrNone;
sl@0
   175
	}
sl@0
   176
sl@0
   177
sl@0
   178
/** Cancels a nanokernel timer.
sl@0
   179
sl@0
   180
	Removes this timer from the nanokernel timer queue. Does nothing if the
sl@0
   181
	timer is inactive or has already expired.
sl@0
   182
	Note that if the timer was queued and DFC callback requested it is possible
sl@0
   183
	for the expiry handler to run even after Cancel() has been called. This will
sl@0
   184
	occur in the case where DfcThread1 is preempted just before calling the
sl@0
   185
	expiry handler for this timer and the preempting thread/ISR/IDFC calls
sl@0
   186
	Cancel() on the timer.
sl@0
   187
sl@0
   188
	@pre	Any context
sl@0
   189
	@return	TRUE if timer was actually cancelled
sl@0
   190
	@return	FALSE if timer was not cancelled - this could be because it was not
sl@0
   191
				active or because its expiry handler was already running on
sl@0
   192
				another CPU or in the timer DFC.
sl@0
   193
 */
sl@0
   194
EXPORT_C TBool NTimer::Cancel()
sl@0
   195
	{
sl@0
   196
	TBool result = TRUE;
sl@0
   197
	TInt irq=NKern::DisableAllInterrupts();
sl@0
   198
	if (iState>ETransferring)	// idle or transferring timers are not on a queue
sl@0
   199
		Deque();
sl@0
   200
	switch (iState)
sl@0
   201
		{
sl@0
   202
		case ETransferring:	// signal DFC to abort this iteration
sl@0
   203
			TheTimerQ.iTransferringCancelled=TRUE;
sl@0
   204
			break;
sl@0
   205
		case ECritical:		// signal DFC to abort this iteration
sl@0
   206
			TheTimerQ.iCriticalCancelled=TRUE;
sl@0
   207
			break;
sl@0
   208
		case EFinal:
sl@0
   209
			{
sl@0
   210
			// Need to clear bit in iPresent if both final queues now empty
sl@0
   211
			// NOTE: Timer might actually be on the completed queue rather than the final queue
sl@0
   212
			//		 but the check is harmless in any case.
sl@0
   213
			TInt i=iTriggerTime & NTimerQ::ETimerQMask;
sl@0
   214
			NTimerQ::STimerQ& q=TheTimerQ.iTickQ[i];
sl@0
   215
			if (q.iIntQ.IsEmpty() && q.iDfcQ.IsEmpty())
sl@0
   216
				TheTimerQ.iPresent &= ~(1<<i);
sl@0
   217
			break;
sl@0
   218
			}
sl@0
   219
		case EIdle:			// nothing to do
sl@0
   220
			result = FALSE;
sl@0
   221
		case EHolding:		// just deque
sl@0
   222
		case EOrdered:		// just deque
sl@0
   223
			break;
sl@0
   224
		}
sl@0
   225
	iState=EIdle;
sl@0
   226
	NKern::RestoreInterrupts(irq);
sl@0
   227
	return result;
sl@0
   228
	}
sl@0
   229
#endif
sl@0
   230
sl@0
   231
sl@0
   232
/** Check if a nanokernel timer is pending or not
sl@0
   233
sl@0
   234
	@return	TRUE if the timer is pending (OneShot() etc. would return KErrInUse)
sl@0
   235
	@return FALSE if the timer is idle (OneShot() etc. would succeed)
sl@0
   236
	@pre	Any context
sl@0
   237
sl@0
   238
	@publishedPartner
sl@0
   239
	@prototype
sl@0
   240
 */
sl@0
   241
EXPORT_C TBool NTimer::IsPending()
sl@0
   242
	{
sl@0
   243
	return iState != EIdle;
sl@0
   244
	}
sl@0
   245
sl@0
   246
sl@0
   247
/** Obtains the address of the nanokernel timer queue object.
sl@0
   248
sl@0
   249
	Not intended for general use. Intended only for base ports in order to get
sl@0
   250
	the address used to call NTimerQ::Tick() with.
sl@0
   251
sl@0
   252
	@return	The address of the nanokernel timer queue object
sl@0
   253
	@pre	Any context
sl@0
   254
 */
sl@0
   255
EXPORT_C TAny* NTimerQ::TimerAddress()
sl@0
   256
	{
sl@0
   257
	return &TheTimerQ;
sl@0
   258
	}
sl@0
   259
sl@0
   260
NTimerQ::NTimerQ()
sl@0
   261
	:	iDfc(NTimerQ::DfcFn,this,NULL,KTimerQDfcPriority)
sl@0
   262
	{
sl@0
   263
	// NOTE: All other members are initialised to zero since the single instance
sl@0
   264
	//		 of NTimerQ resides in .bss
sl@0
   265
	}
sl@0
   266
sl@0
   267
void NTimerQ::Init1(TInt aTickPeriod)
sl@0
   268
	{
sl@0
   269
	TheTimerQ.iTickPeriod=aTickPeriod;
sl@0
   270
	__KTRACE_OPT(KBOOT,DEBUGPRINT("NTimerQ::Init1 - period %d us",aTickPeriod));
sl@0
   271
	__KTRACE_OPT(KMEMTRACE, DEBUGPRINT("MT:P %d",aTickPeriod));
sl@0
   272
	}
sl@0
   273
sl@0
   274
void NTimerQ::Init3(TDfcQue* aDfcQ)
sl@0
   275
	{
sl@0
   276
	__KTRACE_OPT(KBOOT,DEBUGPRINT("NTimerQ::Init3 DFCQ at %08x",aDfcQ));
sl@0
   277
	TheTimerQ.iDfc.SetDfcQ(aDfcQ);
sl@0
   278
	}
sl@0
   279
sl@0
   280
#ifndef __MSTIM_MACHINE_CODED__
sl@0
   281
void NTimerQ::Add(NTimer* aTimer)
sl@0
   282
//
sl@0
   283
//	Internal function to add a timer to the queue.
sl@0
   284
//	Enter and return with all interrupts disabled.
sl@0
   285
//
sl@0
   286
	{
sl@0
   287
	TInt t=TInt(aTimer->iTriggerTime-iMsCount);
sl@0
   288
	if (t<ENumTimerQueues)
sl@0
   289
		AddFinal(aTimer);
sl@0
   290
	else
sl@0
   291
		{
sl@0
   292
		// >=32ms to expiry, so put on holding queue
sl@0
   293
		aTimer->iState=NTimer::EHolding;
sl@0
   294
		iHoldingQ.Add(aTimer);
sl@0
   295
		}
sl@0
   296
	}
sl@0
   297
sl@0
   298
void NTimerQ::AddFinal(NTimer* aTimer)
sl@0
   299
//
sl@0
   300
//	Internal function to add a timer to the corresponding final queue.
sl@0
   301
//	Enter and return with all interrupts disabled.
sl@0
   302
//
sl@0
   303
	{
sl@0
   304
	TInt i=aTimer->iTriggerTime & ETimerQMask;
sl@0
   305
	SDblQue* pQ;
sl@0
   306
	if (aTimer->iCompleteInDfc)
sl@0
   307
		pQ=&iTickQ[i].iDfcQ;
sl@0
   308
	else
sl@0
   309
		pQ=&iTickQ[i].iIntQ;
sl@0
   310
	iPresent |= (1<<i);
sl@0
   311
	aTimer->iState=NTimer::EFinal;
sl@0
   312
	pQ->Add(aTimer);
sl@0
   313
	}
sl@0
   314
sl@0
   315
void NTimerQ::DfcFn(TAny* aPtr)
sl@0
   316
	{
sl@0
   317
	((NTimerQ*)aPtr)->Dfc();
sl@0
   318
	}
sl@0
   319
sl@0
   320
void NTimerQ::Dfc()
sl@0
   321
//
sl@0
   322
// Do deferred timer queue processing and/or DFC completions
sl@0
   323
//
sl@0
   324
	{
sl@0
   325
	TInt irq;
sl@0
   326
sl@0
   327
	// First transfer entries on the Ordered queue to the Final queues
sl@0
   328
	FOREVER
sl@0
   329
		{
sl@0
   330
		irq=NKern::DisableAllInterrupts();
sl@0
   331
		if (iOrderedQ.IsEmpty())
sl@0
   332
			break;
sl@0
   333
		NTimer* pC=(NTimer*)iOrderedQ.First();
sl@0
   334
		TInt remain=pC->iTriggerTime-iMsCount;
sl@0
   335
		if (remain>=ENumTimerQueues)
sl@0
   336
			break;
sl@0
   337
sl@0
   338
		// If remaining time <32 ticks, add it to final queue;
sl@0
   339
		// also if remain < 0 we've 'missed it' so add to final queue.
sl@0
   340
		pC->Deque();
sl@0
   341
		AddFinal(pC);
sl@0
   342
		NKern::RestoreInterrupts(irq);
sl@0
   343
		__DEBUG_CALLBACK(0);
sl@0
   344
		}
sl@0
   345
	NKern::RestoreInterrupts(irq);
sl@0
   346
	__DEBUG_CALLBACK(1);
sl@0
   347
sl@0
   348
	// Next transfer entries on the Holding queue to the Ordered queue or final queue
sl@0
   349
	FOREVER
sl@0
   350
		{
sl@0
   351
		irq=NKern::DisableAllInterrupts();
sl@0
   352
		if (iHoldingQ.IsEmpty())
sl@0
   353
			break;
sl@0
   354
		NTimer* pC=(NTimer*)iHoldingQ.First();
sl@0
   355
		pC->Deque();
sl@0
   356
		pC->iState=NTimer::ETransferring;
sl@0
   357
		iTransferringCancelled=FALSE;
sl@0
   358
		TUint32 trigger=pC->iTriggerTime;
sl@0
   359
		if (TInt(trigger-iMsCount)<ENumTimerQueues)
sl@0
   360
			{
sl@0
   361
			// <32ms remaining so put it on final queue
sl@0
   362
			AddFinal(pC);
sl@0
   363
			}
sl@0
   364
		else
sl@0
   365
			{
sl@0
   366
			FOREVER
sl@0
   367
				{
sl@0
   368
				NKern::RestoreInterrupts(irq);
sl@0
   369
				__DEBUG_CALLBACK(2);
sl@0
   370
sl@0
   371
				// we now need to walk ordered queue to find correct position for pC
sl@0
   372
				SDblQueLink* anchor=&iOrderedQ.iA;
sl@0
   373
				iCriticalCancelled=FALSE;
sl@0
   374
				irq=NKern::DisableAllInterrupts();
sl@0
   375
				NTimer* pN=(NTimer*)iOrderedQ.First();
sl@0
   376
				while (pN!=anchor && !iTransferringCancelled)
sl@0
   377
					{
sl@0
   378
					if ((pN->iTriggerTime-trigger)<0x80000000u)
sl@0
   379
						break;	// insert before pN
sl@0
   380
					pN->iState=NTimer::ECritical;
sl@0
   381
					NKern::RestoreInterrupts(irq);
sl@0
   382
					__DEBUG_CALLBACK(3);
sl@0
   383
					irq=NKern::DisableAllInterrupts();
sl@0
   384
					if (iCriticalCancelled)
sl@0
   385
						break;
sl@0
   386
					pN->iState=NTimer::EOrdered;
sl@0
   387
					pN=(NTimer*)pN->iNext;
sl@0
   388
					}
sl@0
   389
sl@0
   390
				if (iTransferringCancelled)
sl@0
   391
					break;		// this one has been cancelled, go on to next one
sl@0
   392
				if (!iCriticalCancelled)
sl@0
   393
					{
sl@0
   394
					pC->InsertBefore(pN);
sl@0
   395
					pC->iState=NTimer::EOrdered;
sl@0
   396
					break;		// done this one
sl@0
   397
					}
sl@0
   398
				}
sl@0
   399
			}
sl@0
   400
		NKern::RestoreInterrupts(irq);
sl@0
   401
		__DEBUG_CALLBACK(4);
sl@0
   402
		}
sl@0
   403
	NKern::RestoreInterrupts(irq);
sl@0
   404
	__DEBUG_CALLBACK(5);
sl@0
   405
sl@0
   406
	// Finally do call backs for timers which requested DFC callback
sl@0
   407
	FOREVER
sl@0
   408
		{
sl@0
   409
		irq=NKern::DisableAllInterrupts();
sl@0
   410
		if (iCompletedQ.IsEmpty())
sl@0
   411
			break;
sl@0
   412
		NTimer* pC=(NTimer*)iCompletedQ.First();
sl@0
   413
		pC->Deque();
sl@0
   414
		pC->iState=NTimer::EIdle;
sl@0
   415
		TAny* p=pC->iPtr;
sl@0
   416
		NTimerFn f=pC->iFunction;
sl@0
   417
		NKern::RestoreInterrupts(irq);
sl@0
   418
		__DEBUG_CALLBACK(7);
sl@0
   419
		(*f)(p);
sl@0
   420
		}
sl@0
   421
	NKern::RestoreInterrupts(irq);
sl@0
   422
	}
sl@0
   423
sl@0
   424
sl@0
   425
/** Tick over the nanokernel timer queue.
sl@0
   426
	This function should be called by the base port in the system tick timer ISR.
sl@0
   427
	It should not be called at any other time.
sl@0
   428
	The value of 'this' to pass is the value returned by NTimerQ::TimerAddress().
sl@0
   429
sl@0
   430
	@see NTimerQ::TimerAddress()
sl@0
   431
 */
sl@0
   432
EXPORT_C void NTimerQ::Tick()
sl@0
   433
	{
sl@0
   434
#ifdef _DEBUG
sl@0
   435
	// If there are threads waiting to be released by the tick, enqueue the dfc
sl@0
   436
	if (!TheScheduler.iDelayedQ.IsEmpty())
sl@0
   437
		TheScheduler.iDelayDfc.Add();
sl@0
   438
#endif
sl@0
   439
	TheScheduler.TimesliceTick();
sl@0
   440
	TInt irq=NKern::DisableAllInterrupts();
sl@0
   441
	TInt i=iMsCount & ETimerQMask;
sl@0
   442
	iMsCount++;
sl@0
   443
	STimerQ* pQ=iTickQ+i;
sl@0
   444
	iPresent &= ~(1<<i);
sl@0
   445
	TBool doDfc=FALSE;
sl@0
   446
	if (!pQ->iDfcQ.IsEmpty())
sl@0
   447
		{
sl@0
   448
		// transfer DFC completions to completed queue and queue DFC
sl@0
   449
		iCompletedQ.MoveFrom(&pQ->iDfcQ);
sl@0
   450
		doDfc=TRUE;
sl@0
   451
		}
sl@0
   452
	if ((i&(ETimerQMask>>1))==0)
sl@0
   453
		{
sl@0
   454
		// Every 16 ticks we check if a DFC is required.
sl@0
   455
		// This allows a DFC latency of up to 16 ticks before timers are missed.
sl@0
   456
		if (!iHoldingQ.IsEmpty())
sl@0
   457
			doDfc=TRUE;				// if holding queue nonempty, queue DFC to sort
sl@0
   458
		else if (!iOrderedQ.IsEmpty())
sl@0
   459
			{
sl@0
   460
			// if first ordered queue entry expires in <32ms, queue the DFC to transfer
sl@0
   461
			NTimer* pC=(NTimer*)iOrderedQ.First();
sl@0
   462
#ifdef __EPOC32__
sl@0
   463
			__ASSERT_WITH_MESSAGE_DEBUG(iMsCount<=pC->iTriggerTime, "iMsCount has exceeded pC->iTriggerTime; function called later than expected ","NTimerQ::Tick()");
sl@0
   464
#endif
sl@0
   465
			if (TInt(pC->iTriggerTime-iMsCount)<ENumTimerQueues)
sl@0
   466
				doDfc=TRUE;
sl@0
   467
			}
sl@0
   468
		}
sl@0
   469
	if (!pQ->iIntQ.IsEmpty())
sl@0
   470
		{
sl@0
   471
		// transfer ISR completions to a temporary queue
sl@0
   472
		// careful here - higher priority interrupts could dequeue timers!
sl@0
   473
		SDblQue q(&pQ->iIntQ,0);
sl@0
   474
		while(!q.IsEmpty())
sl@0
   475
			{
sl@0
   476
			NTimer* pC=(NTimer*)q.First();
sl@0
   477
			pC->Deque();
sl@0
   478
			pC->iState=NTimer::EIdle;
sl@0
   479
			NKern::RestoreInterrupts(irq);
sl@0
   480
			if (pC->iFunction)
sl@0
   481
				(*pC->iFunction)(pC->iPtr);
sl@0
   482
			else
sl@0
   483
				((TDfc*)(pC->iPtr))->Add();
sl@0
   484
			irq=NKern::DisableAllInterrupts();
sl@0
   485
			}
sl@0
   486
		}
sl@0
   487
	NKern::RestoreInterrupts(irq);
sl@0
   488
	if (doDfc)
sl@0
   489
		iDfc.Add();
sl@0
   490
	}
sl@0
   491
sl@0
   492
sl@0
   493
/** Return the number of ticks before the next nanokernel timer expiry.
sl@0
   494
	May on occasion return a pessimistic estimate (i.e. too low).
sl@0
   495
	Used by base port to disable the system tick interrupt when the system
sl@0
   496
	is idle.
sl@0
   497
sl@0
   498
	@return	The number of ticks before the next nanokernel timer expiry.
sl@0
   499
	
sl@0
   500
	@pre	Interrupts must be disabled.
sl@0
   501
	
sl@0
   502
	@post	Interrupts are disabled.
sl@0
   503
 */
sl@0
   504
EXPORT_C TInt NTimerQ::IdleTime()
sl@0
   505
	{
sl@0
   506
	CHECK_PRECONDITIONS(MASK_INTERRUPTS_DISABLED,"NTimerQ::IdleTime");	
sl@0
   507
#ifdef _DEBUG
sl@0
   508
	// If there are threads waiting to be released by the tick we can't idle
sl@0
   509
	if (!TheScheduler.iDelayedQ.IsEmpty())
sl@0
   510
		return 1;
sl@0
   511
#endif
sl@0
   512
	NTimerQ& m=TheTimerQ;
sl@0
   513
	TUint32 next=m.iMsCount;	// number of next tick
sl@0
   514
	TUint32 p=m.iPresent;
sl@0
   515
	TInt r=KMaxTInt;
sl@0
   516
	if (p)
sl@0
   517
		{
sl@0
   518
		// Final queues nonempty
sl@0
   519
		TInt nx=next&0x1f;				// number of next tick modulo 32
sl@0
   520
		p=(p>>nx)|(p<<(32-nx));			// rotate p right by nx (so lsb corresponds to next tick)
sl@0
   521
		r=__e32_find_ls1_32(p);			// find number of zeros before LS 1
sl@0
   522
		}
sl@0
   523
	if (!m.iHoldingQ.IsEmpty())
sl@0
   524
		{
sl@0
   525
		// Sort operation required - need to process next tick divisible by 16
sl@0
   526
		TInt nx=next&0x0f;				// number of next tick modulo 16
sl@0
   527
		TInt r2=nx?(16-nx):0;			// number of ticks before next divisible by 16
sl@0
   528
		if (r2<r)
sl@0
   529
			r=r2;
sl@0
   530
		}
sl@0
   531
	if (!m.iOrderedQ.IsEmpty())
sl@0
   532
		{
sl@0
   533
		// Timers present on ordered queue
sl@0
   534
		NTimer* pC=(NTimer*)m.iOrderedQ.First();
sl@0
   535
		TUint32 tt=pC->iTriggerTime;
sl@0
   536
		tt=(tt&~0x0f)-16;				// time at which transfer to final queue would occur
sl@0
   537
		TInt r3=(TInt)(tt-next);
sl@0
   538
		if (r3<r)
sl@0
   539
			r=r3;
sl@0
   540
		}
sl@0
   541
	return r;
sl@0
   542
	}
sl@0
   543
#endif
sl@0
   544
sl@0
   545
sl@0
   546
/** Advance the nanokernel timer queue by the specified number of ticks.
sl@0
   547
	It is assumed that no timers expire as a result of this.
sl@0
   548
	Used by base port when system comes out of idle mode after disabling the
sl@0
   549
	system tick interrupt to bring the timer queue up to date.
sl@0
   550
sl@0
   551
	@param	aTicks Number of ticks skipped due to tick suppression
sl@0
   552
sl@0
   553
	@pre	Interrupts must be disabled.
sl@0
   554
sl@0
   555
	@post	Interrupts are disabled.
sl@0
   556
 */
sl@0
   557
EXPORT_C void NTimerQ::Advance(TInt aTicks)
sl@0
   558
	{
sl@0
   559
	CHECK_PRECONDITIONS(MASK_INTERRUPTS_DISABLED,"NTimerQ::Advance");	
sl@0
   560
	TheTimerQ.iMsCount+=(TUint32)aTicks;
sl@0
   561
	}
sl@0
   562
sl@0
   563
sl@0
   564
/**	Returns the period of the nanokernel timer.
sl@0
   565
	@return Period in microseconds
sl@0
   566
	@pre any context
sl@0
   567
	@see NTimer
sl@0
   568
 */
sl@0
   569
EXPORT_C TInt NKern::TickPeriod()
sl@0
   570
	{
sl@0
   571
	return TheTimerQ.iTickPeriod;
sl@0
   572
	}
sl@0
   573
sl@0
   574
sl@0
   575
/**	Converts a time interval to timer ticks.
sl@0
   576
sl@0
   577
	@param aMilliseconds time interval in milliseconds.
sl@0
   578
	@return Number of nanokernel timer ticks.  Non-integral results are rounded up.
sl@0
   579
sl@0
   580
 	@pre aMilliseconds should be <=2147483 to avoid integer overflow.
sl@0
   581
	@pre any context
sl@0
   582
 */
sl@0
   583
EXPORT_C TInt NKern::TimerTicks(TInt aMilliseconds)
sl@0
   584
	{
sl@0
   585
	__ASSERT_WITH_MESSAGE_DEBUG(aMilliseconds<=2147483,"aMilliseconds should be <=2147483","NKern::TimerTicks");
sl@0
   586
	TUint32 msp=TheTimerQ.iTickPeriod;
sl@0
   587
	if (msp==1000)	// will be true except on pathological hardware
sl@0
   588
		return aMilliseconds;
sl@0
   589
	TUint32 us=(TUint32)aMilliseconds*1000;
sl@0
   590
	return (us+msp-1)/msp;
sl@0
   591
	}
sl@0
   592