First public contribution.
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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32\nkern\arm\nctimer.cia
15 // Fast Millisecond Timer Implementation
23 #define ASM_KILL_LINK(rp,rs) asm("mov "#rs", #0xdf ");\
24 asm("orr "#rs", "#rs", "#rs", lsl #8 ");\
25 asm("orr "#rs", "#rs", "#rs", lsl #16 ");\
26 asm("str "#rs", ["#rp"] ");\
27 asm("str "#rs", ["#rp", #4] ");
29 #define ASM_KILL_LINK_OFFSET(rp,rs,offset) asm("mov "#rs", #0xdf ");\
30 asm("orr "#rs", "#rs", "#rs", lsl #8 ");\
31 asm("orr "#rs", "#rs", "#rs", lsl #16 ");\
32 asm("str "#rs", ["#rp", #"#offset"] ");\
33 asm("str "#rs", ["#rp", #"#offset"+4] ");
35 #define ASM_KILL_LINK(rp,rs)
36 #define ASM_KILL_LINK_OFFSET(rp,rs,offset)
39 #ifdef __MSTIM_MACHINE_CODED__
42 #define __DEBUG_CALLBACK(n) asm("stmfd sp!, {r0-r3,r12,lr} "); \
43 asm("ldr r0, __TheTimerQ "); \
44 asm("ldr r12, [r0, #%a0]!" : : "i" _FOFF(NTimerQ,iDebugFn)); \
45 asm("cmp r12, #0 "); \
46 asm("movne r1, #" #n ); \
47 asm("ldrne r0, [r0, #4] "); \
48 asm("movne lr, pc "); \
50 asm("ldmfd sp!, {r0-r3,r12,lr} ")
52 #define __DEBUG_CALLBACK(n)
56 /** Start a nanokernel timer in zero-drift periodic mode with ISR or DFC callback.
57 Queues the timer to expire in the specified number of nanokernel ticks,
58 measured from the time at which it last expired. This allows exact periodic
59 timers to be implemented with no drift caused by delays in requeueing the
61 The expiry handler will be called in the same context as the previous timer
62 expiry. Generally the way this is used is that NTimer::OneShot() is used to start
63 the first time interval and this specifies whether the callback is in ISR context
64 or in the context of the nanokernel timer thread (DfcThread1) or other Dfc thread.
65 The expiry handler then uses NTimer::Again() to requeue the timer.
67 @param aTime Timeout in nanokernel ticks
68 @return KErrNone if no error
69 @return KErrInUse if timer is already active
70 @return KErrArgument if the requested expiry time is in the past
73 __NAKED__ EXPORT_C TInt NTimer::Again(TInt /*aTime*/)
75 asm("mrs r12, cpsr ");
76 INTS_OFF(r3, r12, INTS_ALL_OFF); // all interrupts off
77 asm("ldrb r3, [r0, #%a0]" : : "i" _FOFF(NTimer,iState)); // r3=iState
78 asm("ldr r2, __TheTimerQ ");
79 asm("cmp r3, #%a0" : : "i" ((TInt)EIdle));
80 asm("ldreq r3, [r0, #%a0]" : : "i" _FOFF(NTimer,iTriggerTime)); // r3=iTriggerTime
81 asm("bne add_mscb_in_use "); // if already queued return KErrInUse
82 asm("add r3, r3, r1 "); // add requested time interval
83 asm("ldr r1, [r2, #%a0]" : : "i" _FOFF(NTimerQ,iMsCount)); // r1=iMsCount
84 asm("subs r1, r3, r1 "); // r1=trigger time-next tick time
85 asm("strpl r3, [r0, #%a0]" : : "i" _FOFF(NTimer,iTriggerTime)); // iTriggerTime+=aTime
86 asm("bpl AddMsCallBack "); // if time interval positive, ok
87 asm("mov r0, #%a0" : : "i" ((TInt)KErrArgument)); // else return KErrArgument
92 /** Start a nanokernel timer in one-shot mode with ISR callback.
93 Queues the timer to expire in the specified number of nanokernel ticks. The
94 actual wait time will be at least that much and may be up to one tick more.
95 The expiry handler will be called in ISR context.
97 @param aTime Timeout in nanokernel ticks
98 @return KErrNone if no error
99 @return KErrInUse if timer is already active
102 __NAKED__ EXPORT_C TInt NTimer::OneShot(TInt /*aTime*/)
109 /** Start a nanokernel timer in one-shot mode with ISR or DFC callback.
110 Queues the timer to expire in the specified number of nanokernel ticks. The
111 actual wait time will be at least that much and may be up to one tick more.
112 The expiry handler will be called in either ISR context or in the context
113 of the nanokernel timer thread (DfcThread1).
115 @param aTime Timeout in nanokernel ticks
116 @param aDfc TRUE if DFC callback required, FALSE if ISR callback required.
117 @return KErrNone if no error
118 @return KErrInUse if timer is already active
121 __NAKED__ EXPORT_C TInt NTimer::OneShot(TInt /*aTime*/, TBool /*aDfc*/)
123 asm("mrs r12, cpsr ");
124 INTS_OFF(r3, r12, INTS_ALL_OFF); // all interrupts off
125 asm("ldrb r3, [r0, #%a0]" : : "i" _FOFF(NTimer,iState)); // r3=iState
126 asm("cmp r3, #%a0" : : "i" ((TInt)EIdle));
127 asm("bne add_mscb_in_use "); // if already queued return KErrInUse
128 asm("strb r2, [r0, #%a0]" : : "i" _FOFF(NTimer,iCompleteInDfc)); // iCompleteInDfc=aDfc
129 asm("ldr r2, __TheTimerQ ");
130 asm("ldr r3, [r2, #%a0]" : : "i" _FOFF(NTimerQ,iMsCount)); // r3=iMsCount
131 asm("add r3, r3, r1 ");
132 asm("str r3, [r0, #%a0]" : : "i" _FOFF(NTimer,iTriggerTime)); // iTriggerTime=ms count + aTime
134 // r0->CallBack, r2=TheTimerQ, r1=time interval, r3=trigger time
135 asm("AddMsCallBack: ");
136 asm("cmp r1, #32 "); // compare interval with 32ms
137 asm("bge add_mscb_holding "); // if >=32ms put it on holding queue
138 asm("ldrb r1, [r0, #%a0]" : : "i" _FOFF(NTimer,iCompleteInDfc)); // r1=iCompleteInDfc
139 asm("and r3, r3, #0x1f "); // r3=trigger time & 0x1f
141 asm("add r1, r2, r3, lsl #4 "); // r1->IntQ corresponding to trigger time
142 asm("addne r1, r1, #8 "); // if (iCompleteInDfc), r1 points to DfcQ
143 asm("ldr r3, [r1, #4] "); // r3=pQ->iA.iPrev
144 asm("str r0, [r1, #4] "); // pQ->iA.iPrev=pC
145 asm("str r0, [r3, #0] "); // pQ->iA.iPrev->iNext=pC
146 asm("stmia r0, {r1,r3} "); // pC->iNext=&pQ->iA, pC->iPrev=pQ->iA.iPrev
147 asm("mov r1, #%a0" : : "i" ((TInt)EFinal));
148 asm("strb r1, [r0, #%a0]" : : "i" _FOFF(NTimer,iState)); // iState=EFinal
149 asm("ldr r0, [r0, #%a0]" : : "i" _FOFF(NTimer,iTriggerTime)); // r0=iTriggerTime
150 asm("ldr r3, [r2, #%a0]" : : "i" _FOFF(NTimerQ,iPresent)); // r3=TheTimerQ->iPresent
151 asm("and r0, r0, #0x1f ");
153 asm("orr r3, r3, r1, lsl r0 "); // iPresent |= (1<<index)
154 asm("str r3, [r2, #%a0]" : : "i" _FOFF(NTimerQ,iPresent));
155 asm("mov r0, #0 "); // return KErrNone
156 asm("msr cpsr, r12 ");
159 asm("add_mscb_holding: ");
160 asm("ldr r3, [r2, #%a0]!" : : "i" _FOFF(NTimerQ,iHoldingQ.iA.iPrev)); // r3=pQ->iPrev, r2=&iHoldingQ.iA.iPrev
161 asm("mov r1, #%a0" : : "i" ((TInt)EHolding));
162 asm("strb r1, [r0, #%a0]" : : "i" _FOFF(NTimer,iState)); // iState=EHolding
163 asm("str r0, [r2], #-4 "); // pQ->iPrev=pC, r2=&iHoldingQ
164 asm("str r0, [r3, #0] "); // pQ->iPrev->iNext=pC
165 asm("stmia r0, {r2,r3} "); // pC->iNext=pQ, pC->iPrev=pQ->iPrev
166 asm("mov r0, #0 "); // return KErrNone
169 asm("msr cpsr, r12 ");
172 asm("add_mscb_in_use: ");
173 asm("mov r0, #%a0" : : "i" ((TInt)KErrInUse)); // return KErrInUse
174 asm("msr cpsr, r12 ");
177 asm("__TheTimerQ: ");
178 asm(".word TheTimerQ ");
182 /** Starts a nanokernel timer in one-shot mode with callback in dfc thread that provided DFC belongs to.
184 Queues the timer to expire in the specified number of nanokernel ticks. The
185 actual wait time will be at least that much and may be up to one tick more.
186 On expiry aDfc will be queued in ISR context.
188 Note that NKern::TimerTicks() can be used to convert milliseconds to ticks.
190 @param aTime Timeout in nanokernel ticks
191 @param aDfc - Dfc to be queued when the timer expires.
193 @return KErrNone if no error; KErrInUse if timer is already active.
197 @see NKern::TimerTicks()
200 __NAKED__ EXPORT_C TInt NTimer::OneShot(TInt /*aTime*/, TDfc& /*aDfc*/)
202 asm("mrs r12, cpsr ");
203 INTS_OFF(r3, r12, INTS_ALL_OFF); // all interrupts off
204 asm("ldrb r3, [r0, #%a0]" : : "i" _FOFF(NTimer,iState)); // r3=iState
205 asm("cmp r3, #%a0" : : "i" ((TInt)EIdle));
206 asm("bne add_mscb_in_use "); // if already queued return KErrInUse
208 asm("strb r3, [r0, #%a0]" : : "i" _FOFF(NTimer,iCompleteInDfc)); // iCompleteInDfc=0
209 asm("str r3, [r0, #%a0]" : : "i" _FOFF(NTimer,iFunction)); // iFunction=NULL
210 asm("str r2, [r0, #%a0]" : : "i" _FOFF(NTimer,iPtr)); // iPtr= &aDfc
211 asm("ldr r2, __TheTimerQ ");
212 asm("ldr r3, [r2, #%a0]" : : "i" _FOFF(NTimerQ,iMsCount)); // r3=iMsCount
213 asm("add r3, r3, r1 ");
214 asm("str r3, [r0, #%a0]" : : "i" _FOFF(NTimer,iTriggerTime)); // iTriggerTime=ms count + aTime
215 asm("b AddMsCallBack ");
219 /** Cancel a nanokernel timer.
220 Removes this timer from the nanokernel timer queue. Does nothing if the
221 timer is inactive or has already expired.
222 Note that if the timer was queued and DFC callback requested it is possible
223 for the expiry handler to run even after Cancel() has been called. This will
224 occur in the case where DfcThread1 is preempted just before calling the
225 expiry handler for this timer and the preempting thread/ISR/IDFC calls
226 Cancel() on the timer.
229 @return TRUE if timer was actually cancelled
230 @return FALSE if timer was not cancelled - this could be because it was not
231 active or because its expiry handler was already running on
232 another CPU or in the timer DFC.
234 EXPORT_C __NAKED__ TBool NTimer::Cancel()
236 asm("mrs r12, cpsr ");
237 INTS_OFF(r3, r12, INTS_ALL_OFF); // all interrupts off
238 asm("ldrb r3, [r0, #%a0]" : : "i" _FOFF(NTimer,iState));
240 asm("cmp r3, #%a0" : : "i" ((TInt)ETransferring));
241 asm("movcc r0, #0 "); // if EIdle, nothing to do, return FALSE
242 asm("bcc cancel_idle ");
243 asm("strb r1, [r0, #%a0]" : : "i" _FOFF(NTimer,iState)); // iState=EIdle
244 asm("beq cancel_xfer "); // if ETransferring, branch
245 asm("ldmia r0, {r1,r2} "); // If queued, dequeue - r1=next, r2=prev
246 asm("cmp r3, #%a0" : : "i" ((TInt)ECritical));
247 asm("str r1, [r2, #0] "); // if queued, prev->next=next
248 asm("str r2, [r1, #4] "); // and next->prev=prev
249 ASM_KILL_LINK(r0,r1);
250 asm("ldrcs r1, __TheTimerQ ");
251 asm("ldrhi r0, [r0, #%a0]" : : "i" _FOFF(NTimer,iTriggerTime)); // r0=iTriggerTime
252 asm("ldrhi r3, [r1, #%a0]" : : "i" _FOFF(NTimerQ,iMsCount)); // r3=iMsCount
253 asm("bcc cancel_done "); // if EHolding or EOrdered, finished
254 asm("beq cancel_critical "); // if ECritical, branch
255 // r1->ms timer, state was EFinal
256 asm("subs r3, r0, r3 "); // r3=trigger time - next tick
257 asm("bmi cancel_done "); // if trigger time already expired, don't touch iPresent (was on iCompletedQ)
258 asm("and r0, r0, #0x1f "); // r0=iTriggerTime&0x1f = queue index
260 asm("ldr r2, [r3, r0, lsl #4]! "); // r3->iIntQ for this timer, r2=iIntQ head pointer
262 asm("bne cancel_done "); // iIntQ not empty so finished
263 asm("ldr r2, [r3, #8]! "); // r2=iDfcQ head pointer
265 asm("bne cancel_done "); // iDfcQ not empty so finished
266 asm("ldr r2, [r1, #%a0]" : : "i" _FOFF(NTimerQ,iPresent)); // r2=TheTimerQ->iPresent
268 asm("bic r2, r2, r3, lsl r0 "); // iPresent &= ~(1<<index)
269 asm("str r2, [r1, #%a0]" : : "i" _FOFF(NTimerQ,iPresent));
271 asm("cancel_done: ");
272 asm("mov r0, #1 "); // return TRUE
274 asm("cancel_idle: ");
275 asm("msr cpsr, r12 ");
278 asm("cancel_xfer: ");
279 asm("ldr r1, __TheTimerQ "); // r1->ms timer, state was ETransferring
280 asm("strb r3, [r1, #%a0]" : : "i" _FOFF(NTimerQ,iTransferringCancelled));
281 asm("msr cpsr, r12 ");
282 asm("mov r0, #1 "); // return TRUE
285 asm("cancel_critical: "); // r1->ms timer, state was ECritical
286 asm("strb r3, [r1, #%a0]" : : "i" _FOFF(NTimerQ,iCriticalCancelled));
287 asm("msr cpsr, r12 ");
288 asm("mov r0, #1 "); // return TRUE
293 /** Return the number of ticks before the next nanokernel timer expiry.
294 May on occasion return a pessimistic estimate (i.e. too low).
295 Used by base port to disable the system tick interrupt when the system
298 @return The number of ticks before the next nanokernel timer expiry.
300 @pre Interrupts must be disabled.
302 @post Interrupts are disabled.
304 EXPORT_C __NAKED__ TInt NTimerQ::IdleTime()
306 ASM_CHECK_PRECONDITIONS(MASK_INTERRUPTS_DISABLED);
308 asm("ldr r1, __TheScheduler ");
309 asm("ldr r2, [r1, #%a0]! " : : "i" _FOFF(TScheduler,iDelayedQ));
310 // r2 = iA.iNext, r1 = iA
312 asm("movne r0, #1 "); // if there are delayed threads, prevent idle
315 asm("ldr r12, __TheTimerQ ");
316 asm("mvn r0, #0x80000000 "); // set r0=KMaxTInt initially
317 asm("ldr r2, [r12, #%a0]!" : : "i" _FOFF(NTimerQ,iOrderedQ)); // r12->iOrderedQ, r2=iOrderedQ first
318 asm("ldr r3, [r12, #-12] "); // r3=next tick number
319 asm("cmp r2, r12 "); // check if iOrderedQ empty
320 asm("ldrne r0, [r2, #%a0]" : : "i" _FOFF(NTimer,iTriggerTime)); // if not, r0=ordered Q first->trigger time
321 asm("ldr r1, [r12, #-8]! "); // r1=iHoldingQ first, r12->iHoldingQ
322 asm("bicne r0, r0, #0x0f ");
323 asm("subne r0, r0, #16 "); // r0=tick at which transfer to final queue would occur
324 asm("subne r0, r0, r3 "); // return value = trigger time - iMsCount
325 asm("cmp r1, r12 "); // holding Q empty?
326 asm("ldr r1, [r12, #-8] "); // r1=iPresent
327 asm("and r12, r3, #0x1f "); // r12=next tick mod 32
328 asm("beq 1f "); // branch if holding Q empty
329 asm("ands r2, r3, #0x0f "); // else r2=next tick no. mod 16
330 asm("rsbne r2, r2, #16 "); // if nonzero, subtract from 16 to give #ticks before next multiple of 16
332 asm("movlt r0, r2 "); // update result if necessary
334 asm("movs r1, r1, ror r12 "); // r1=iPresent rotated so that LSB corresponds to next tick
335 __JUMP(eq,lr); // if iPresent=0, finished
336 asm("mov r3, #0 "); // r3 will accumulate bit number of least significant 1
337 asm("movs r2, r1, lsl #16 ");
338 asm("movne r1, r2 ");
339 asm("addeq r3, r3, #16 ");
340 asm("movs r2, r1, lsl #8 ");
341 asm("movne r1, r2 ");
342 asm("addeq r3, r3, #8 ");
343 asm("movs r2, r1, lsl #4 ");
344 asm("movne r1, r2 ");
345 asm("addeq r3, r3, #4 ");
346 asm("movs r2, r1, lsl #2 ");
347 asm("movne r1, r2 ");
348 asm("addeq r3, r3, #2 ");
349 asm("movs r2, r1, lsl #1 ");
350 asm("addeq r3, r3, #1 ");
352 asm("movlt r0, r3 "); // update result if necessary
357 /** Tick over the nanokernel timer queue.
358 This function should be called by the base port in the system tick timer ISR.
359 It should not be called at any other time.
360 The value of 'this' to pass is the value returned by NTimerQ::TimerAddress().
362 @see NTimerQ::TimerAddress()
364 __NAKED__ EXPORT_C void NTimerQ::Tick()
369 asm("ldr r1, __TheScheduler ");
370 asm("ldr r2, [r1, #%a0]! " : : "i" _FOFF(TScheduler,iDelayedQ));
371 // r2 = iA.iNext, r1 = iA
373 asm("beq 1f "); // no delayed threads, don't queue dfc
374 asm("stmfd sp!, {r0,lr} ");
375 asm("ldr r1, __TheScheduler ");
376 asm("add r0, r1, #%a0 " : : "i" _FOFF(TScheduler,iDelayDfc));
377 asm("bl " CSM_ZN4TDfc3AddEv);
378 asm("ldmfd sp!, {r0,lr} ");
382 // Enter with r0 pointing to NTimerQ
383 asm("ldr r1, __TheScheduler ");
384 asm("mrs r12, cpsr ");
386 // do the timeslice tick - on ARM __SCHEDULER_MACHINE_CODED is mandatory
387 asm("ldr r2, [r1, #%a0]" : : "i" _FOFF(TScheduler,iCurrentThread));
388 asm("ldr r3, [r2, #%a0]" : : "i" _FOFF(NThread,iTime));
389 asm("subs r3, r3, #1 ");
390 asm("strge r3, [r2, #%a0]" : : "i" _FOFF(NThread,iTime));
391 asm("streqb r12, [r1, #%a0]" : : "i" _FOFF(TScheduler,iRescheduleNeededFlag)); // r12 lower byte is never 0
392 INTS_OFF(r3, r12, INTS_ALL_OFF); // disable all interrupts
394 asm("ldr r1, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iMsCount)); // r1=iMsCount
395 asm("ldr r3, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iPresent)); // r3=iPresent
396 asm("and r2, r1, #0x1f "); // r2=iMsCount & 0x1f
397 asm("add r1, r1, #1 ");
398 asm("str r1, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iMsCount)); // iMsCount++
400 asm("tst r3, r1, lsl r2 "); // test iPresent bit for this tick
401 asm("bic r1, r3, r1, lsl r2 "); // clear iPresent bit
402 asm("strne r1, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iPresent)); // update iPresent if necessary
403 asm("bne mstim_tick_1 "); // if bit was set, we have work to do
404 asm("tst r2, #0x0f "); // else test for tick 0 or 16
405 __MSR_CPSR_C(ne, r12); // if neither return
408 asm("mstim_tick_1: "); // get here if timers complete this tick
409 asm("stmfd sp!, {r4-r6,lr} ");
410 asm("add r1, r0, r2, lsl #4 "); // r1->IntQ for this tick
411 asm("ldr r3, [r1, #8]! "); // r1->DfcQ and r3=DfcQ first
412 asm("mov r5, #0 "); // r5=doDfc=FALSE
414 asm("beq mstim_tick_2 "); // skip if DfcQ empty
416 // Move DFC completions from iDfcQ to iCompletedQ
417 asm("ldr lr, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iCompletedQ.iA.iPrev)); // lr=last completed
418 asm("ldr r4, [r1, #4] "); // r4=DfcQ last
419 asm("add r5, r0, #%a0" : : "i" _FOFF(NTimerQ,iDfc)); // doDfc=TRUE
420 asm("str r3, [lr, #0] "); // old last pending->next = DfcQ first
421 asm("str r4, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iCompletedQ.iA.iPrev)); // last pending=DfcQ last
422 asm("str lr, [r3, #4] "); // DfcQ first->prev = old last pending
423 asm("add lr, r0, #%a0" : : "i" _FOFF(NTimerQ,iCompletedQ)); // lr=&iCompletedQ.iA
424 asm("str lr, [r4, #0] "); // DfcQ last->next=&iPending
425 asm("str r1, [r1, #0] "); // DfcQ first=&DfcQ
426 asm("str r1, [r1, #4] "); // DfcQ last=&DfcQ
428 asm("mstim_tick_2: ");
429 asm("tst r2, #0x0f "); // check for tick 0 or 16
430 asm("bne mstim_tick_3 "); // skip if not
432 // Tick 0 or 16 - must check holding queue and ordered queue
433 asm("ldr r3, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iHoldingQ)); // r3=iHoldingQ first
434 asm("add r6, r0, #%a0" : : "i" _FOFF(NTimerQ,iHoldingQ)); // r6=&iHoldingQ
436 asm("addne r5, r0, #%a0" : : "i" _FOFF(NTimerQ,iDfc)); // if iHoldingQ nonempty, doDfc=TRUE and skip ordered queue check
437 asm("bne mstim_tick_3 "); // skip if iHoldingQ nonempty
438 asm("ldr r3, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iOrderedQ)); // r3=iOrderedQ first
439 asm("add r6, r0, #%a0" : : "i" _FOFF(NTimerQ,iOrderedQ)); // r6=&iOrderedQ
441 asm("beq mstim_tick_3 "); // skip if iOrderedQ empty
442 asm("ldr r4, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iMsCount)); // else r4=iMsCount
443 asm("ldr r3, [r3, #%a0]" : : "i" _FOFF(NTimer,iTriggerTime)); // and r3=trigger time of first on ordered queue
444 asm("sub r3, r3, r4 "); // r3=trigger time-iMsCount
446 asm("addls r5, r0, #%a0" : : "i" _FOFF(NTimerQ,iDfc)); // if first expiry in <=31ms, doDfc=TRUE
449 asm("mstim_tick_3: ");
450 asm("ldr r3, [r1, #-8]! "); // r1->iIntQ, r3=iIntQ first
451 asm("mov r6, r12 "); // r6=original cpsr
452 asm("cmp r3, r1 "); // test if iIntQ empty
453 asm("beq mstim_tick_4 "); // branch if it is
455 // Transfer iIntQ to a temporary queue
456 asm("ldr r4, [r1, #4] "); // r4=iIntQ last
457 asm("str r1, [r1, #0] "); // clear iIntQ
458 asm("str r1, [r1, #4] ");
459 asm("stmfd sp!, {r3,r4} "); // copy queue onto stack
460 asm("str sp, [r4, #0] "); // iIntQ last->next=sp
461 asm("str sp, [r3, #4] "); // iIntQ first->prev=sp
462 INTS_OFF_1(r4, r6, INTS_ALL_OFF); // r4=cpsr with all interrupts off
464 // Walk the temporary queue and complete timers
465 asm("mstim_tick_5: ");
466 INTS_OFF_2(r4, r6, INTS_ALL_OFF); // all interrupts off
467 asm("ldr r0, [sp, #0] "); // r0=q.iNext
468 asm("mov r3, #%a0" : : "i" ((TInt)NTimer::EIdle));
469 asm("cmp r0, sp "); // end of queue?
470 asm("beq mstim_tick_6 "); // if so, branch out
471 asm("ldmia r0!, {r1,r2} "); // r1=next r2=prev, r0->iPtr
472 asm("strb r3, [r0, #%a0]" : : "i" (_FOFF(NTimer,iState)-8)); // iState=EIdle
473 ASM_KILL_LINK_OFFSET(r0,r12,-8);
474 asm("ldmia r0, {r0,r12} "); // r0=iPtr, r12=iFunction
475 asm("str r1, [r2, #0] "); // prev->next=next
476 asm("str r2, [r1, #4] "); // next->prev=prev
477 asm("adr lr, mstim_tick_5 "); // return to mstim_tick_5
478 asm("msr cpsr, r6 "); // restore interrupts
479 asm("cmp r12, #0 "); // iFunction==NULL ?
480 asm("beq mstim_tick_7 "); // if so queue Dfc (iPtr is a pointer to TDfc )
481 __JUMP(,r12); // call timer callback with r0=iPtr
482 asm("b mstim_tick_6 "); // skip queuing of Dfc
484 asm("mstim_tick_7: ");
485 asm("b " CSM_ZN4TDfc3AddEv); // add the DFC with r0=iPtr - a pointer to TDfc
487 asm("mstim_tick_6: ");
488 asm("add sp, sp, #8 "); // take temporary queue off stack
490 asm("mstim_tick_4: ");
491 asm("msr cpsr, r6 "); // restore original interrupt state
492 asm("movs r0, r5 "); // DFC needed? if so, r0->iDfc
493 asm("ldmfd sp!, {r4-r6,lr} "); // restore registers
494 asm("bne " CSM_ZN4TDfc3AddEv); // add the DFC if required
495 __JUMP(,lr); // if no DFC needed, return
497 asm("__TheScheduler: ");
498 asm(".word TheScheduler ");
501 __NAKED__ void NTimerQ::DfcFn(TAny* /*aPtr*/)
503 // Enter with r0 pointing to NTimerQ
504 asm("stmfd sp!, {r7-r11,lr} ");
505 SET_INTS_1(r11, MODE_SVC, INTS_ALL_ON); // always called from SVC mode
506 SET_INTS_1(r10, MODE_SVC, INTS_ALL_OFF); // with interruts enabled
508 // First transfer entries on the Ordered queue to the Final queues
509 asm("mstim_dfc_0: ");
510 SET_INTS_2(r10, MODE_SVC, INTS_ALL_OFF); // disable interrupts
511 asm("ldr r1, [r0, #%a0]!" : : "i" _FOFF(NTimerQ,iOrderedQ)); // r0->iOrderedQ, r1=orderedQ first
513 asm("beq mstim_dfc_1 "); // ordered Q empty so move to next stage
514 asm("ldr r2, [r1, #%a0]" : : "i" _FOFF(NTimer,iTriggerTime)); // r2=r1->trigger time
515 asm("ldr r3, [r0, #-12] "); // r3=iMsCount
516 asm("subs r3, r2, r3 "); // r3=trigger time-iMsCount
517 asm("cmp r3, #31 "); // test if remaining time <32ms or has already passed
518 asm("bgt mstim_dfc_1 "); // if >31ms, move to next stage (signed comparison to catch already passed case)
519 asm("sub r0, r0, #%a0" : : "i" _FOFF(NTimerQ,iOrderedQ)); // r0->NTimerQ
520 asm("bl dequeaddfinal "); // <=31ms, so deque and add to final queue
521 SET_INTS_2(r11, MODE_SVC, INTS_ALL_ON); // let interrupts in
523 asm("b mstim_dfc_0 ");
525 asm("mstim_dfc_1: ");
526 SET_INTS_2(r11, MODE_SVC, INTS_ALL_ON); // let interrupts in
527 asm("sub r0, r0, #%a0" : : "i" _FOFF(NTimerQ,iOrderedQ)); // r0->NTimerQ
530 // Next transfer entries on the Holding queue to the Ordered queue or final queue
531 asm("mstim_dfc_2: ");
532 SET_INTS_2(r10, MODE_SVC, INTS_ALL_OFF); // disable interrupts
533 asm("ldr r1, [r0, #%a0]!" : : "i" _FOFF(NTimerQ,iHoldingQ)); // r0->iHoldingQ, r1=holdingQ first
535 asm("beq mstim_dfc_3 "); // holding Q empty so move to next stage
536 asm("ldr r2, [r1, #%a0]" : : "i" _FOFF(NTimer,iTriggerTime)); // r2=r1->trigger time
537 asm("ldr r3, [r0, #-4] "); // r3=iMsCount
538 asm("sub r0, r0, #%a0" : : "i" _FOFF(NTimerQ,iHoldingQ)); // r0->NTimerQ
539 asm("subs r3, r2, r3 "); // r3=trigger time-iMsCount
540 asm("cmp r3, #31 "); // test if remaining time <32ms or has already passed
541 asm("bgt mstim_dfc_4 "); // if >31ms, need to put it on the ordered Q (signed comparison to catch late case)
542 asm("bl dequeaddfinal "); // <=31ms or already passed, so deque and add to final queue
544 asm("mstim_dfc_7: ");
545 SET_INTS_2(r11, MODE_SVC, INTS_ALL_ON); // let interrupts in
547 asm("b mstim_dfc_2 "); // process next holding Q entry
549 // need to put entry r1 trigger time r2 on the ordered Q
550 asm("mstim_dfc_4: ");
551 asm("ldmia r1, {r3,r12} "); // r3=r1->next, r12=r1->prev
553 asm("strb r9, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iTransferringCancelled)); // iTransferringCancelled=0
554 asm("str r3, [r12, #0] "); // prev->next=next
555 asm("str r12, [r3, #4] "); // next->prev=prev
556 asm("mov r3, #%a0" : : "i" ((TInt)NTimer::ETransferring));
557 asm("strb r3, [r1, #%a0]" : : "i" _FOFF(NTimer,iState)); // r1->iState=ETransferring
559 asm("mstim_dfc_5: ");
560 SET_INTS_2(r11, MODE_SVC, INTS_ALL_ON); // let interrupts in
561 asm("add lr, r0, #%a0" : : "i" _FOFF(NTimerQ,iOrderedQ)); // lr=&iOrderedQ.iA
563 SET_INTS_2(r10, MODE_SVC, INTS_ALL_OFF); // disable interrupts
565 asm("mstim_dfc_9: ");
566 asm("ldrb r12, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iTransferringCancelled));
567 asm("ldr r3, [lr, #0] "); // r3=iOrderedQ first
569 asm("bne mstim_dfc_7 "); // Entry r1 has been cancelled so move to next one
570 asm("strb r9, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iCriticalCancelled)); // iCriticalCancelled=0
572 // Walk iOrderedQ to find correct position for this entry
573 asm("mstim_dfc_6: ");
574 asm("cmp r3, lr "); // reached end of ordered Q?
575 asm("ldrne r12, [r3, #%a0]" : : "i" _FOFF(NTimer,iTriggerTime)); // if not, r12=r3->trigger time
576 asm("beq mstim_dfc_8 "); // branch if we have
577 asm("mov r8, #%a0" : : "i" ((TInt)NTimer::ECritical));
578 asm("subs r12, r12, r2 "); // r12=r3->trigger - r1->trigger
579 asm("bpl mstim_dfc_8 "); // branch if r3 expires after r1
580 asm("strb r8, [r3, #%a0]" : : "i" _FOFF(NTimer,iState)); // r3->iState=ECritical
581 SET_INTS_2(r11, MODE_SVC, INTS_ALL_ON); // let interrupts in
582 asm("mov r8, #%a0" : : "i" ((TInt)NTimer::EOrdered));
584 SET_INTS_2(r10, MODE_SVC, INTS_ALL_OFF); // disable interrupts
585 asm("ldr r12, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iTransferringCancelled));
586 asm("tst r12, #0xff00 "); // test iCriticalCancelled
587 asm("streqb r8, [r3, #%a0]" : : "i" _FOFF(NTimer,iState)); // if not set, r3->iState=EOrdered
588 asm("cmp r12, #0 "); // test iTransferringCancelled and iCriticalCancelled
589 asm("ldreq r3, [r3, #0] "); // if neither set r3=r3->iNext
590 asm("beq mstim_dfc_6 "); // and inspect next ordered Q entry
591 asm("b mstim_dfc_9 "); // if either set, start again from beginning of ordered Q
593 asm("mstim_dfc_8: "); // if we get to here we need to insert r1 before r3
594 asm("ldr r12, [r3, #4] "); // r12=r3->iPrev
595 asm("mov r8, #%a0" : : "i" ((TInt)NTimer::EOrdered));
596 asm("strb r8, [r1, #%a0]" : : "i" _FOFF(NTimer,iState)); // r1->iState=EOrdered
597 asm("str r1, [r3, #4] "); // r3->prev=r1
598 asm("str r1, [r12, #0] "); // r3->prev->next=r1
599 asm("stmia r1, {r3,r12} "); // r1->next=r3, r1->prev=r3->prev
600 SET_INTS_2(r11, MODE_SVC, INTS_ALL_ON); // let interrupts in
602 asm("b mstim_dfc_2 "); // process next holding Q entry
604 // Get here when all holding Q entries processed
605 asm("mstim_dfc_3: ");
606 SET_INTS_2(r11, MODE_SVC, INTS_ALL_ON); // let interrupts in
608 asm("add r8, r0, #16 "); // r8->iCompletedQ
610 // Finally do call backs for timers which requested DFC callback
611 asm("mstim_dfc_10: ");
612 SET_INTS_2(r10, MODE_SVC, INTS_ALL_OFF); // disable interrupts
613 asm("ldr r9, [r8, #0] "); // r9=completed Q first
614 asm("mov r3, #%a0" : : "i" ((TInt)NTimer::EIdle));
615 asm("cmp r9, r8 "); // Is completed Q empty?
616 asm("beq mstim_dfc_11 "); // branch out if it is
617 asm("ldmia r9!, {r1,r2} "); // r1=r9->next, r2=r9->prev, r9->iPtr of completed entry
618 asm("strb r3, [r9, #%a0]" : : "i" (_FOFF(NTimer,iState)-8)); // iState=EIdle for completed entry
619 asm("ldmia r9, {r0,r3} "); // r0=iPtr, r3=function address
620 ASM_KILL_LINK_OFFSET(r9,r12,-8);
621 asm("str r1, [r2, #0] "); // prev->next=next
622 asm("str r2, [r1, #4] "); // next->prev=prev
623 SET_INTS_2(r11, MODE_SVC, INTS_ALL_ON); // let interrupts in
625 asm("adr lr, mstim_dfc_10 "); // return to mstim_dfc_10
626 __JUMP(,r3); // call back with r0=iPtr
629 asm("mstim_dfc_11: ");
630 SET_INTS_2(r11, MODE_SVC, INTS_ALL_ON); // let interrupts in
631 asm("ldmfd sp!, {r7-r11,pc} "); // and return
633 // Subroutine dequeaddfinal
634 // Deque the NTimer pointed to by r1 and put it on its final queue
635 // Enter with r0->NTimerQ, r1->NTimer, r2=r1->iTriggerTime
636 // Enter and leave with interrupts disabled
637 // Can modify r1-r3,r8,r9,r12
638 asm("dequeaddfinal: ");
639 asm("ldmia r1, {r8,r9} "); // r8=r1->next, r9=r1->prev
640 asm("mov r3, #%a0" : : "i" ((TInt)NTimer::EFinal));
641 asm("strb r3, [r1, #%a0]" : : "i" _FOFF(NTimer,iState)); // iState=EFinal
642 asm("str r8, [r9, #0] "); // prev->next=next
643 asm("str r9, [r8, #4] "); // next->prev=prev
644 asm("ldr r12, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iPresent)); // r12=timer iPresent
645 asm("and r2, r2, #0x1f "); // r2=trigger time & 0x1f
647 asm("orr r12, r12, r3, lsl r2 "); // set bit in iPresent
648 asm("ldrb r3, [r1, #%a0]" : : "i" _FOFF(NTimer,iCompleteInDfc)); // r3=iCompleteInDfc
649 asm("str r12, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iPresent));
650 asm("add r2, r0, r2, lsl #4 "); // r2->iIntQ for this timer
652 asm("addne r2, r2, #8 "); // if iCompleteInDfc, r2->iDfcQ for this timer
653 asm("ldr r12, [r2, #4] "); // r12->last on queue
654 asm("str r1, [r2, #4] "); // queue->last=this
655 asm("str r1, [r12, #0] "); // last->next=this
656 asm("stmia r1, {r2,r12} "); // this->next=&queue, this->prev=last on queue