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