1 /* $Id: timer.c,v 1.42 2001/03/16 16:05:33 dwelch Exp $
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ke/timer.c
6 * PURPOSE: Handle timers
7 * PROGRAMMER: David Welch (welch@mcmail.com)
10 * 12/3/99: Phillip Susi: enabled the timers, fixed spin lock
13 /* NOTES ******************************************************************/
15 * System time units are 100-nanosecond intervals
18 /* INCLUDES ***************************************************************/
21 #include <ddk/ntddk.h>
22 #include <internal/ke.h>
23 #include <internal/id.h>
24 #include <internal/ps.h>
27 #include <internal/debug.h>
29 /* TYPES *****************************************************************/
33 /* GLOBALS ****************************************************************/
35 #define IDMAP_BASE (0xd0000000)
38 * Return a linear address which can be used to access the physical memory
41 unsigned int physical_to_linear(unsigned int x
)
46 unsigned int linear_to_physical(unsigned int x
)
54 static unsigned long long boot_time
= 0;
55 static unsigned long long system_time
= 0;
58 * Number of timer interrupts since initialisation
60 volatile ULONGLONG KiTimerTicks
;
61 volatile ULONG KiRawTicks
= 0;
64 * The increment in the system clock every timer tick (in system time units)
70 #define CLOCK_INCREMENT (100000)
73 * PURPOSE: List of timers
75 static LIST_ENTRY TimerListHead
;
76 static KSPIN_LOCK TimerListLock
;
77 static KDPC ExpireTimerDpc
;
79 /* must raise IRQL to HIGH_LEVEL and grab spin lock there, to sync with ISR */
81 extern ULONG PiNrRunnableThreads
;
83 #define MICROSECONDS_PER_TICK (10000)
84 #define TICKS_TO_CALIBRATE (1)
85 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
86 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
88 static BOOLEAN TimerInitDone
= FALSE
;
90 /* FUNCTIONS **************************************************************/
93 NTSTATUS STDCALL
NtQueryTimerResolution(OUT PULONG MinimumResolution
,
94 OUT PULONG MaximumResolution
,
95 OUT PULONG ActualResolution
)
101 NTSTATUS STDCALL
NtSetTimerResolution(IN ULONG RequestedResolution
,
103 OUT PULONG ActualResolution
)
109 NTSTATUS STDCALL
NtQueryPerformanceCounter (IN PLARGE_INTEGER Counter
,
110 IN PLARGE_INTEGER Frequency
)
116 NTSTATUS
KeAddThreadTimeout(PKTHREAD Thread
, PLARGE_INTEGER Interval
)
118 assert(Thread
!= NULL
);
119 assert(Interval
!= NULL
);
121 DPRINT("KeAddThreadTimeout(Thread %x, Interval %x)\n",Thread
,Interval
);
123 KeInitializeTimer(&Thread
->Timer
);
124 KeSetTimer(&Thread
->Timer
, *Interval
, &Thread
->TimerDpc
);
126 DPRINT("Thread->Timer.entry.Flink %x\n",
127 Thread
->Timer
.TimerListEntry
.Flink
);
129 return STATUS_SUCCESS
;
133 NTSTATUS STDCALL
NtDelayExecution(IN ULONG Alertable
,
137 LARGE_INTEGER Timeout
;
139 Timeout
= *((PLARGE_INTEGER
)Interval
);
140 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
141 Alertable
, Internal
, Timeout
);
143 DPRINT("Execution delay is %d/%d\n",
144 Timeout
.u
.Highpart
, Timeout
.u
.LowPart
);
145 Status
= KeDelayExecutionThread(UserMode
, Alertable
, &Timeout
);
151 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode
,
153 PLARGE_INTEGER Interval
)
155 * FUNCTION: Puts the current thread into an alertable or nonalertable
156 * wait state for a given internal
158 * WaitMode = Processor mode in which the caller is waiting
159 * Altertable = Specifies if the wait is alertable
160 * Interval = Specifies the interval to wait
164 PKTHREAD CurrentThread
= KeGetCurrentThread();
165 KeAddThreadTimeout(CurrentThread
, Interval
);
166 return (KeWaitForSingleObject(&CurrentThread
->Timer
,
174 KeQueryTimeIncrement (VOID
)
176 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
177 * the system clock every time the clock interrupts
178 * RETURNS: The increment
181 return(CLOCK_INCREMENT
);
185 KeQuerySystemTime (PLARGE_INTEGER CurrentTime
)
187 * FUNCTION: Gets the current system time
189 * CurrentTime (OUT) = The routine stores the current time here
190 * NOTE: The time is the number of 100-nanosecond intervals since the
191 * 1st of January, 1601.
194 CurrentTime
->QuadPart
= system_time
;
199 NtGetTickCount (PULONG UpTime
)
201 LARGE_INTEGER TickCount
;
202 if ( UpTime
== NULL
)
203 return(STATUS_INVALID_PARAMETER
);
204 KeQueryTickCount(&TickCount
);
205 *UpTime
= TickCount
.u
.LowPart
;
206 return (STATUS_SUCCESS
);
211 KeSetTimer (PKTIMER Timer
,
212 LARGE_INTEGER DueTime
,
215 * FUNCTION: Sets the absolute or relative interval at which a timer object
216 * is to be set to the signaled state and optionally supplies a
217 * CustomTimerDpc to be executed when the timer expires.
219 * Timer = Points to a previously initialized timer object
220 * DueTimer = If positive then absolute time to expire at
221 * If negative then the relative time to expire at
222 * Dpc = If non-NULL then a dpc to be called when the timer expires
223 * RETURNS: True if the timer was already in the system timer queue
227 return(KeSetTimerEx(Timer
, DueTime
, 0, Dpc
));
231 KeSetTimerEx (PKTIMER Timer
,
232 LARGE_INTEGER DueTime
,
236 * FUNCTION: Sets the absolute or relative interval at which a timer object
237 * is to be set to the signaled state and optionally supplies a
238 * CustomTimerDpc to be executed when the timer expires.
240 * Timer = Points to a previously initialized timer object
241 * DueTimer = If positive then absolute time to expire at
242 * If negative then the relative time to expire at
243 * Dpc = If non-NULL then a dpc to be called when the timer expires
244 * RETURNS: True if the timer was already in the system timer queue
250 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer
);
251 KeAcquireSpinLock( &TimerListLock
, &oldlvl
);
254 if (DueTime
.QuadPart
< 0)
256 Timer
->DueTime
.QuadPart
= system_time
- DueTime
.QuadPart
;
260 Timer
->DueTime
.QuadPart
= DueTime
.QuadPart
;
262 Timer
->Period
= Period
;
263 Timer
->Header
.SignalState
= FALSE
;
264 if (Timer
->TimerListEntry
.Flink
!= NULL
)
266 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
269 InsertTailList(&TimerListHead
,&Timer
->TimerListEntry
);
270 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
276 KeCancelTimer (PKTIMER Timer
)
278 * FUNCTION: Removes a timer from the system timer list
280 * Timer = timer to cancel
281 * RETURNS: True if the timer was running
287 DPRINT("KeCancelTimer(Timer %x)\n",Timer
);
289 KeRaiseIrql( HIGH_LEVEL
, &oldlvl
);
290 KeAcquireSpinLockAtDpcLevel( &TimerListLock
);
292 if (Timer
->TimerListEntry
.Flink
== NULL
)
294 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
297 RemoveEntryList(&Timer
->TimerListEntry
);
298 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
299 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
305 KeReadStateTimer (PKTIMER Timer
)
307 return(Timer
->Header
.SignalState
);
311 KeInitializeTimer (PKTIMER Timer
)
313 * FUNCTION: Initalizes a kernel timer object
315 * Timer = caller supplied storage for the timer
316 * NOTE: This function initializes a notification timer
319 KeInitializeTimerEx(Timer
,NotificationTimer
);
323 KeInitializeTimerEx (PKTIMER Timer
,
326 * FUNCTION: Initializes a kernel timer object
328 * Timer = caller supplied storage for the timer
329 * Type = the type of timer (notification or synchronization)
330 * NOTE: When a notification type expires all waiting threads are released
331 * and the timer remains signalled until it is explicitly reset. When a
332 * syncrhonization timer expires its state is set to signalled until a
333 * single waiting thread is released and then the timer is reset.
338 if (Type
== NotificationTimer
)
340 IType
= InternalNotificationTimer
;
342 else if (Type
== SynchronizationTimer
)
344 IType
= InternalSynchronizationTimer
;
352 KeInitializeDispatcherHeader(&Timer
->Header
,
354 sizeof(KTIMER
) / sizeof(ULONG
),
356 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
360 KeQueryTickCount (PLARGE_INTEGER TickCount
)
362 * FUNCTION: Returns the number of ticks since the system was booted
364 * TickCount (OUT) = Points to storage for the number of ticks
367 TickCount
->QuadPart
= KiTimerTicks
;
370 static void HandleExpiredTimer(PKTIMER current
)
372 DPRINT("HandleExpiredTime(current %x)\n",current
);
373 if (current
->Dpc
!= NULL
)
375 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
376 current
->Dpc
, current
->Dpc
->DeferredRoutine
);
377 KeInsertQueueDpc(current
->Dpc
,
380 DPRINT("Finished dpc routine\n");
382 KeAcquireDispatcherDatabaseLock(FALSE
);
383 current
->Header
.SignalState
= TRUE
;
384 KeDispatcherObjectWake(¤t
->Header
);
385 KeReleaseDispatcherDatabaseLock(FALSE
);
386 if (current
->Period
!= 0)
388 current
->DueTime
.QuadPart
+=
389 current
->Period
* SYSTEM_TIME_UNITS_PER_MSEC
;
393 RemoveEntryList(¤t
->TimerListEntry
);
394 current
->TimerListEntry
.Flink
= current
->TimerListEntry
.Blink
= NULL
;
399 KeExpireTimers(PKDPC Dpc
,
404 PLIST_ENTRY current_entry
= NULL
;
405 PKTIMER current
= NULL
;
406 ULONG Eip
= (ULONG
)Arg1
;
408 DPRINT("KeExpireTimers()\n");
410 current_entry
= TimerListHead
.Flink
;
412 KeAcquireSpinLockAtDpcLevel(&TimerListLock
);
414 while (current_entry
!= &TimerListHead
)
416 current
= CONTAINING_RECORD(current_entry
, KTIMER
, TimerListEntry
);
418 current_entry
= current_entry
->Flink
;
420 if (system_time
>= current
->DueTime
.QuadPart
)
422 HandleExpiredTimer(current
);
426 KiAddProfileEvent(ProfileTime
, Eip
);
428 KeReleaseSpinLockFromDpcLevel(&TimerListLock
);
433 KiUpdateSystemTime (KIRQL oldIrql
, ULONG Eip
)
435 * FUNCTION: Handles a timer interrupt
438 char* vidmem
=(char *)physical_to_linear(0xb8000 + 160 - 2);
442 if (TimerInitDone
== FALSE
)
447 * Increment the number of timers ticks
450 system_time
= system_time
+ CLOCK_INCREMENT
;
453 * Display the tick count in the top left of the screen as a debugging
457 if (oldIrql
< DISPATCH_LEVEL
)
467 * Queue a DPC that will expire timers
469 KeInsertQueueDpc(&ExpireTimerDpc
, (PVOID
)Eip
, 0);
474 KeInitializeTimerImpl(VOID
)
476 * FUNCTION: Initializes timer irq handling
477 * NOTE: This is only called once from main()
480 TIME_FIELDS TimeFields
;
481 LARGE_INTEGER SystemBootTime
;
482 extern VOID
HalpCalibrateStallExecution (VOID
);
484 DPRINT("KeInitializeTimerImpl()\n");
486 HalpCalibrateStallExecution ();
488 InitializeListHead(&TimerListHead
);
489 KeInitializeSpinLock(&TimerListLock
);
490 KeInitializeDpc(&ExpireTimerDpc
, KeExpireTimers
, 0);
491 TimerInitDone
= TRUE
;
494 * Calculate the starting time for the system clock
496 HalQueryRealTimeClock(&TimeFields
);
497 RtlTimeFieldsToTime(&TimeFields
, &SystemBootTime
);
498 boot_time
=SystemBootTime
.QuadPart
;
499 system_time
=boot_time
;
501 DPRINT("Finished KeInitializeTimerImpl()\n");