2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/timer.c
5 * PURPOSE: Handle timers
6 * PROGRAMMER: David Welch (welch@mcmail.com)
11 /* NOTES ******************************************************************/
13 * System time units are 100-nanosecond intervals
16 /* INCLUDES ***************************************************************/
20 #include <internal/stddef.h>
21 #include <internal/kernel.h>
22 #include <internal/mm.h>
23 #include <internal/hal/page.h>
24 #include <internal/string.h>
25 #include <ddk/ntddk.h>
27 #include <internal/debug.h>
29 /* TYPES *****************************************************************/
34 /* GLOBALS ****************************************************************/
39 static unsigned long long system_time
= 0;
42 * Number of timer interrupts since initialisation
44 static volatile unsigned long long ticks
=0;
47 * The increment in the system clock every timer tick (in system time units)
52 #define CLOCK_INCREMENT (54945055)
55 * PURPOSE: List of timers
57 static LIST_ENTRY timer_list_head
= {NULL
,NULL
};
58 static KSPIN_LOCK timer_list_lock
;
61 #define MICROSECONDS_TO_CALIBRATE (1000000)
62 #define MICROSECONDS_PER_TICK (54945)
63 #define MICROSECONDS_IN_A_SECOND (10000000)
64 #define TICKS_PER_SECOND_APPROX (18)
66 static unsigned int loops_per_microsecond
= 17;
68 /* FUNCTIONS **************************************************************/
70 void KeCalibrateTimerLoop()
72 unsigned int start_tick
;
73 unsigned int end_tick
;
74 unsigned int nr_ticks
;
83 while (start_tick
==ticks
);
84 KeStallExecutionProcessor(MICROSECONDS_TO_CALIBRATE
);
86 while (end_tick
==ticks
);
88 nr_ticks
= end_tick
- start_tick
;
89 loops_per_microsecond
= (loops_per_microsecond
* MICROSECONDS_TO_CALIBRATE
)
90 / (nr_ticks
*MICROSECONDS_PER_TICK
);
92 printk("nr_ticks %d\n",nr_ticks
);
93 printk("loops_per_microsecond %d\n",loops_per_microsecond
);
94 printk("Processor speed (approx) %d\n",
95 (6*loops_per_microsecond
)/1000);
97 if (nr_ticks
== (TICKS_PER_SECOND_APPROX
* MICROSECONDS_TO_CALIBRATE
)
98 / MICROSECONDS_IN_A_SECOND
)
100 printk("Testing loop\n");
101 KeStallExecutionProcessor(10000);
102 printk("Finished loop\n");
108 NTSTATUS
KeDelayExecutionThread(KPROCESSOR_MODE WaitMode
,
110 PLARGE_INTEGER Interval
)
115 VOID
KeStallExecutionProcessor(ULONG MicroSeconds
)
118 for (i
=0; i
<(loops_per_microsecond
*MicroSeconds
) ;i
++)
124 static inline void ULLToLargeInteger(unsigned long long src
,
127 dest
->LowPart
= src
& 0xffffffff;
128 dest
->HighPart
= (src
>>32);
131 static inline void SLLToLargeInteger(signed long long src
,
136 dest
->LowPart
= src
& 0xffffffff;
137 dest
->HighPart
= (src
>>32);
142 dest
->LowPart
= src
& 0xffffffff;
143 dest
->HighPart
= -(src
>>32);
147 static inline signed long long LargeIntegerToSLL(PLARGE_INTEGER src
)
152 if (src
->HighPart
>= 0)
154 r
= r
| (((unsigned long long)src
->HighPart
)<<32);
158 r
= r
| (((unsigned long long)(-(src
->HighPart
)))<<32);
165 LARGE_INTEGER
KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq
)
167 * FUNCTION: Queries the finest grained running count avaiable in the system
169 * PerformanceFreq (OUT) = The routine stores the number of
170 * performance counters tick per second here
171 * RETURNS: The performance counter value in HERTZ
172 * NOTE: Returns the system tick count or the time-stamp on the pentium
175 PerformanceFreq
->HighPart
=0;
176 PerformanceFreq
->LowPart
=0;
179 ULONG
KeQueryTimeIncrement(VOID
)
181 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
182 * the system clock every time the clock interrupts
183 * RETURNS: The increment
186 return(CLOCK_INCREMENT
);
189 VOID
KeQuerySystemTime(PLARGE_INTEGER CurrentTime
)
191 * FUNCTION: Gets the current system time
193 * CurrentTime (OUT) = The routine stores the current time here
194 * NOTE: The time is the number of 100-nanosecond intervals since the
195 * 1st of January, 1601.
198 ULLToLargeInteger(system_time
,CurrentTime
);
202 BOOLEAN
KeSetTimer(PKTIMER Timer
, LARGE_INTEGER DueTime
, PKDPC Dpc
)
204 * FUNCTION: Sets the absolute or relative interval at which a timer object
205 * is to be set to the signaled state and optionally supplies a
206 * CustomTimerDpc to be executed when the timer expires.
208 * Timer = Points to a previously initialized timer object
209 * DueTimer = If positive then absolute time to expire at
210 * If negative then the relative time to expire at
211 * Dpc = If non-NULL then a dpc to be called when the timer expires
212 * RETURNS: True if the timer was already in the system timer queue
216 return(KeSetTimerEx(Timer
,DueTime
,0,Dpc
));
219 BOOLEAN
KeSetTimerEx(PKTIMER Timer
, LARGE_INTEGER DueTime
, LONG Period
,
222 * FUNCTION: Sets the absolute or relative interval at which a timer object
223 * is to be set to the signaled state and optionally supplies a
224 * CustomTimerDpc to be executed when the timer expires.
226 * Timer = Points to a previously initialized timer object
227 * DueTimer = If positive then absolute time to expire at
228 * If negative then the relative time to expire at
229 * Dpc = If non-NULL then a dpc to be called when the timer expires
230 * RETURNS: True if the timer was already in the system timer queue
236 KeAcquireSpinLock(&timer_list_lock
,&oldlvl
);
239 Timer
->period
=Period
;
240 Timer
->expire_time
= LargeIntegerToSLL(&DueTime
);
241 if (Timer
->expire_time
< 0)
243 Timer
->expire_time
= system_time
- Timer
->expire_time
;
244 Timer
->signaled
= FALSE
;
248 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
251 InsertTailList(&timer_list_head
,&Timer
->entry
);
252 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
256 BOOLEAN
KeCancelTimer(PKTIMER Timer
)
258 * FUNCTION: Removes a timer from the system timer list
260 * Timer = timer to cancel
261 * RETURNS: True if the timer was running
267 KeAcquireSpinLock(&timer_list_lock
,&oldlvl
);
273 RemoveEntryFromList(&timer_list_head
,&Timer
->entry
);
274 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
278 BOOLEAN
KeReadStateTimer(PKTIMER Timer
)
280 return(Timer
->signaled
);
283 VOID
KeInitializeTimer(PKTIMER Timer
)
285 * FUNCTION: Initalizes a kernel timer object
287 * Timer = caller supplied storage for the timer
288 * NOTE: This function initializes a notification timer
291 KeInitializeTimerEx(Timer
,NotificationTimer
);
294 VOID
KeInitializeTimerEx(PKTIMER Timer
, TIMER_TYPE Type
)
296 * FUNCTION: Initializes a kernel timer object
298 * Timer = caller supplied storage for the timer
299 * Type = the type of timer (notification or synchronization)
300 * NOTE: When a notification type expires all waiting threads are released
301 * and the timer remains signalled until it is explicitly reset. When a
302 * syncrhonization timer expires its state is set to signalled until a
303 * single waiting thread is released and then the timer is reset.
306 Timer
->running
=FALSE
;
308 Timer
->signaled
=FALSE
;
311 VOID
KeQueryTickCount(PLARGE_INTEGER TickCount
)
313 * FUNCTION: Returns the number of ticks since the system was booted
315 * TickCount (OUT) = Points to storage for the number of ticks
318 ULLToLargeInteger(ticks
,TickCount
);
321 static void HandleExpiredTimer(PKTIMER current
)
323 if (current
->dpc
!=NULL
)
325 current
->dpc
->DeferredRoutine(current
->dpc
,
326 current
->dpc
->DeferredContext
,
327 current
->dpc
->SystemArgument1
,
328 current
->dpc
->SystemArgument2
);
330 current
->signaled
=TRUE
;
331 if (current
->period
!=0)
333 current
->expire_time
= current
->expire_time
+ current
->period
;
337 RemoveEntryFromList(&timer_list_head
,¤t
->entry
);
338 current
->running
=FALSE
;
342 void KeExpireTimers(void)
344 PLIST_ENTRY current_entry
= timer_list_head
.Flink
;
345 PKTIMER current
= CONTAINING_RECORD(current_entry
,KTIMER
,entry
);
348 KeAcquireSpinLock(&timer_list_lock
,&oldlvl
);
350 while (current_entry
!=NULL
)
352 if (system_time
== current
->expire_time
)
354 HandleExpiredTimer(current
);
357 current_entry
= current_entry
->Flink
;
358 current
= CONTAINING_RECORD(current_entry
,KTIMER
,entry
);
360 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
363 VOID
KeTimerInterrupt(VOID
)
365 * FUNCTION: Handles a timer interrupt
369 char* vidmem
=(char *)physical_to_linear(0xb8000 + 160 - 16);
373 * Increment the number of timers ticks
376 system_time
= system_time
+ CLOCK_INCREMENT
;
379 * Display the tick count in the top left of the screen as a debugging
382 sprintf(str
,"%.8u",ticks
);
395 void InitializeTimer(void)
397 * FUNCTION: Initializes timer irq handling
398 * NOTE: This is only called once from main()
401 InitializeListHead(&timer_list_head
);
402 KeInitializeSpinLock(&timer_list_lock
);
405 * Calculate the starting time for the system clock