1 /* $Id: timer.c,v 1.27 2000/02/14 10:27:40 ariadne 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>
23 #include <internal/string.h>
25 #include <internal/ke.h>
28 #include <internal/debug.h>
30 /* TYPES *****************************************************************/
34 /* GLOBALS ****************************************************************/
36 #define IDMAP_BASE (0xd0000000)
39 * Return a linear address which can be used to access the physical memory
42 extern inline unsigned int physical_to_linear(unsigned int x
)
47 extern inline unsigned int linear_to_physical(unsigned int x
)
55 static unsigned long long boot_time
= 0;
56 static unsigned long long system_time
= 0;
59 * Number of timer interrupts since initialisation
61 volatile ULONGLONG KiTimerTicks
;
64 * The increment in the system clock every timer tick (in system time units)
70 #define CLOCK_INCREMENT (549450)
73 * PURPOSE: List of timers
75 static LIST_ENTRY TimerListHead
;
76 static KSPIN_LOCK TimerListLock
;
78 /* must raise IRQL to HIGH_LEVEL and grab spin lock there, to sync with ISR */
80 extern ULONG PiNrRunnableThreads
;
82 #define MICROSECONDS_PER_TICK (54945)
83 #define TICKS_TO_CALIBRATE (1)
84 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
85 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
87 static BOOLEAN TimerInitDone
= FALSE
;
89 /* FUNCTIONS **************************************************************/
92 NTSTATUS STDCALL
NtQueryTimerResolution(OUT PULONG MinimumResolution
,
93 OUT PULONG MaximumResolution
,
94 OUT PULONG ActualResolution
)
100 NTSTATUS STDCALL
NtSetTimerResolution(IN ULONG RequestedResolution
,
102 OUT PULONG ActualResolution
)
108 NTSTATUS STDCALL
NtQueryPerformanceCounter (IN PLARGE_INTEGER Counter
,
109 IN PLARGE_INTEGER Frequency
)
115 NTSTATUS
KeAddThreadTimeout(PKTHREAD Thread
, PLARGE_INTEGER Interval
)
117 assert(Thread
!= NULL
);
118 assert(Interval
!= NULL
);
120 DPRINT("KeAddThreadTimeout(Thread %x, Interval %x)\n",Thread
,Interval
);
122 KeInitializeTimer(&(Thread
->Timer
));
123 KeSetTimer( &(Thread
->Timer
), *Interval
, &Thread
->TimerDpc
);
125 DPRINT("Thread->Timer.entry.Flink %x\n",
126 Thread
->Timer
.TimerListEntry
.Flink
);
128 return STATUS_SUCCESS
;
132 NTSTATUS STDCALL
NtDelayExecution(IN ULONG Alertable
,
136 PLARGE_INTEGER IntervalP
;
138 IntervalP
= (PLARGE_INTEGER
)Interval
;
140 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
141 Alertable
, Internal
, IntervalP
);
143 Status
= KeDelayExecutionThread(UserMode
, Alertable
, IntervalP
);
148 NTSTATUS STDCALL
KeDelayExecutionThread(KPROCESSOR_MODE WaitMode
,
150 PLARGE_INTEGER Interval
)
152 * FUNCTION: Puts the current thread into an alertable or nonalertable
153 * wait state for a given internal
155 * WaitMode = Processor mode in which the caller is waiting
156 * Altertable = Specifies if the wait is alertable
157 * Interval = Specifies the interval to wait
161 PKTHREAD CurrentThread
= KeGetCurrentThread();
162 KeAddThreadTimeout(CurrentThread
, Interval
);
163 return (KeWaitForSingleObject(&(CurrentThread
->Timer
),
170 LARGE_INTEGER
KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq
)
172 * FUNCTION: Queries the finest grained running count avaiable in the system
174 * PerformanceFreq (OUT) = The routine stores the number of
175 * performance counters tick per second here
176 * RETURNS: The performance counter value in HERTZ
177 * NOTE: Returns the system tick count or the time-stamp on the pentium
180 if (PerformanceFreq
!= NULL
)
182 PerformanceFreq
->QuadPart
= 0;
185 return *PerformanceFreq
;
188 ULONG
KeQueryTimeIncrement(VOID
)
190 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
191 * the system clock every time the clock interrupts
192 * RETURNS: The increment
195 return(CLOCK_INCREMENT
);
198 VOID
KeQuerySystemTime(PLARGE_INTEGER CurrentTime
)
200 * FUNCTION: Gets the current system time
202 * CurrentTime (OUT) = The routine stores the current time here
203 * NOTE: The time is the number of 100-nanosecond intervals since the
204 * 1st of January, 1601.
207 CurrentTime
->QuadPart
= system_time
;
211 NTSTATUS STDCALL
NtGetTickCount (PULONG UpTime
)
213 LARGE_INTEGER TickCount
;
214 if ( UpTime
== NULL
)
215 return(STATUS_INVALID_PARAMETER
);
216 KeQueryTickCount(&TickCount
);
217 *UpTime
= TickCount
.u
.LowPart
;
218 return (STATUS_SUCCESS
);
222 BOOLEAN
KeSetTimer(PKTIMER Timer
, LARGE_INTEGER DueTime
, PKDPC Dpc
)
224 * FUNCTION: Sets the absolute or relative interval at which a timer object
225 * is to be set to the signaled state and optionally supplies a
226 * CustomTimerDpc to be executed when the timer expires.
228 * Timer = Points to a previously initialized timer object
229 * DueTimer = If positive then absolute time to expire at
230 * If negative then the relative time to expire at
231 * Dpc = If non-NULL then a dpc to be called when the timer expires
232 * RETURNS: True if the timer was already in the system timer queue
236 return(KeSetTimerEx(Timer
, DueTime
, 0, Dpc
));
239 BOOLEAN
KeSetTimerEx(PKTIMER Timer
, LARGE_INTEGER DueTime
, LONG Period
,
242 * FUNCTION: Sets the absolute or relative interval at which a timer object
243 * is to be set to the signaled state and optionally supplies a
244 * CustomTimerDpc to be executed when the timer expires.
246 * Timer = Points to a previously initialized timer object
247 * DueTimer = If positive then absolute time to expire at
248 * If negative then the relative time to expire at
249 * Dpc = If non-NULL then a dpc to be called when the timer expires
250 * RETURNS: True if the timer was already in the system timer queue
256 DPRINT("KeSetTimerEx(Timer %x)\n",Timer
);
258 KeRaiseIrql( HIGH_LEVEL
, &oldlvl
);
259 KeAcquireSpinLockAtDpcLevel(&TimerListLock
);
262 if (DueTime
.QuadPart
< 0)
264 Timer
->DueTime
.QuadPart
= system_time
+ (-(DueTime
.QuadPart
));
268 Timer
->DueTime
.QuadPart
= DueTime
.QuadPart
;
270 Timer
->Period
= Period
;
271 Timer
->Header
.SignalState
= FALSE
;
272 if (Timer
->TimerListEntry
.Flink
!= NULL
)
274 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
277 InsertTailList(&TimerListHead
,&Timer
->TimerListEntry
);
278 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
283 BOOLEAN
KeCancelTimer(PKTIMER Timer
)
285 * FUNCTION: Removes a timer from the system timer list
287 * Timer = timer to cancel
288 * RETURNS: True if the timer was running
294 DPRINT("KeCancelTimer(Timer %x)\n",Timer
);
296 KeRaiseIrql( HIGH_LEVEL
, &oldlvl
);
297 KeAcquireSpinLockAtDpcLevel( &TimerListLock
);
299 if (Timer
->TimerListEntry
.Flink
== NULL
)
301 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
304 RemoveEntryList(&Timer
->TimerListEntry
);
305 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
306 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
311 BOOLEAN
KeReadStateTimer(PKTIMER Timer
)
313 return(Timer
->Header
.SignalState
);
316 VOID
KeInitializeTimer(PKTIMER Timer
)
318 * FUNCTION: Initalizes a kernel timer object
320 * Timer = caller supplied storage for the timer
321 * NOTE: This function initializes a notification timer
324 KeInitializeTimerEx(Timer
,NotificationTimer
);
327 VOID
KeInitializeTimerEx(PKTIMER Timer
, TIMER_TYPE Type
)
329 * FUNCTION: Initializes a kernel timer object
331 * Timer = caller supplied storage for the timer
332 * Type = the type of timer (notification or synchronization)
333 * NOTE: When a notification type expires all waiting threads are released
334 * and the timer remains signalled until it is explicitly reset. When a
335 * syncrhonization timer expires its state is set to signalled until a
336 * single waiting thread is released and then the timer is reset.
341 if (Type
== NotificationTimer
)
343 IType
= InternalNotificationTimer
;
345 else if (Type
== SynchronizationTimer
)
347 IType
= InternalSynchronizationTimer
;
355 KeInitializeDispatcherHeader(&Timer
->Header
,
357 sizeof(KTIMER
) / sizeof(ULONG
),
359 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
362 VOID
KeQueryTickCount(PLARGE_INTEGER TickCount
)
364 * FUNCTION: Returns the number of ticks since the system was booted
366 * TickCount (OUT) = Points to storage for the number of ticks
369 TickCount
->QuadPart
= KiTimerTicks
;
372 static void HandleExpiredTimer(PKTIMER current
)
374 DPRINT("HandleExpiredTime(current %x)\n",current
);
375 if (current
->Dpc
!= NULL
)
377 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
378 current
->Dpc
, current
->Dpc
->DeferredRoutine
);
379 KeInsertQueueDpc(current
->Dpc
,
382 DPRINT("Finished dpc routine\n");
384 current
->Header
.SignalState
= TRUE
;
385 if (current
->Period
!= 0)
387 current
->DueTime
.QuadPart
+=
388 current
->Period
* SYSTEM_TIME_UNITS_PER_MSEC
;
392 RemoveEntryList(¤t
->TimerListEntry
);
393 current
->TimerListEntry
.Flink
= current
->TimerListEntry
.Blink
= NULL
;
397 VOID
KeExpireTimers(VOID
)
399 PLIST_ENTRY current_entry
= NULL
;
400 PKTIMER current
= NULL
;
403 DPRINT("KeExpireTimers()\n");
405 current_entry
= TimerListHead
.Flink
;
407 // DPRINT("&TimerListHead %x\n",&TimerListHead);
408 // DPRINT("current_entry %x\n",current_entry);
409 // DPRINT("current_entry->Flink %x\n",current_entry->Flink);
410 // DPRINT("current_entry->Flink->Flink %x\n",current_entry->Flink->Flink);
412 KeRaiseIrql(HIGH_LEVEL
, &oldlvl
);
413 KeAcquireSpinLockAtDpcLevel(&TimerListLock
);
415 while (current_entry
!=(&TimerListHead
))
417 current
= CONTAINING_RECORD(current_entry
, KTIMER
, TimerListEntry
);
419 current_entry
= current_entry
->Flink
;
421 if (system_time
>= current
->DueTime
.QuadPart
)
423 HandleExpiredTimer(current
);
427 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
428 // DPRINT("Finished KeExpireTimers()\n");
432 VOID
KiTimerInterrupt(VOID
)
434 * FUNCTION: Handles a timer interrupt
438 char* vidmem
=(char *)physical_to_linear(0xb8000 + 160 - 36);
441 // extern ULONG EiNrUsedBlocks;
442 extern unsigned int EiFreeNonPagedPool
;
443 extern unsigned int EiUsedNonPagedPool
;
444 extern ULONG PiNrThreads
;
445 // extern ULONG MiNrFreePages;
447 if (TimerInitDone
== FALSE
)
452 * Increment the number of timers ticks
455 system_time
= system_time
+ CLOCK_INCREMENT
;
458 * Display the tick count in the top left of the screen as a debugging
461 // sprintf(str,"%.8u %.8u",nr_used_blocks,ticks);
462 if ((EiFreeNonPagedPool
+ EiUsedNonPagedPool
) == 0)
468 x
= (EiFreeNonPagedPool
* 100) /
469 (EiFreeNonPagedPool
+ EiUsedNonPagedPool
);
470 y
= (EiUsedNonPagedPool
* 100) /
471 (EiFreeNonPagedPool
+ EiUsedNonPagedPool
);
473 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,ticks);
474 memset(str
, 0, sizeof(str
));
475 // sprintf(str,"%.8u %.8u",(unsigned int)EiNrUsedBlocks,
476 // (unsigned int)EiFreeNonPagedPool);
477 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,EiUsedNonPagedPool);
478 sprintf(str
,"%.8u %.8u",(unsigned int)PiNrRunnableThreads
,
479 (unsigned int)PiNrThreads
);
480 // sprintf(str,"%.8u %.8u", (unsigned int)PiNrRunnableThreads,
481 // (unsigned int)MiNrFreePages);
493 VOID
KeInitializeTimerImpl(VOID
)
495 * FUNCTION: Initializes timer irq handling
496 * NOTE: This is only called once from main()
499 TIME_FIELDS TimeFields
;
500 LARGE_INTEGER SystemBootTime
;
502 DPRINT("KeInitializeTimerImpl()\n");
504 InitializeListHead(&TimerListHead
);
505 KeInitializeSpinLock(&TimerListLock
);
507 TimerInitDone
= TRUE
;
510 * Calculate the starting time for the system clock
512 HalQueryRealTimeClock(&TimeFields
);
513 RtlTimeFieldsToTime(&TimeFields
, &SystemBootTime
);
514 boot_time
=SystemBootTime
.QuadPart
;
515 system_time
=boot_time
;
517 DPRINT("Finished KeInitializeTimerImpl()\n");