1 /* $Id: timer.c,v 1.24 1999/12/13 22:04:36 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>
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 BOOLEAN Alertable
,
137 Status
= KeDelayExecutionThread(UserMode
, Alertable
,
138 (PLARGE_INTEGER
)Internal
);
143 NTSTATUS STDCALL
KeDelayExecutionThread(KPROCESSOR_MODE WaitMode
,
145 PLARGE_INTEGER Interval
)
147 * FUNCTION: Puts the current thread into an alertable or nonalertable
148 * wait state for a given internal
150 * WaitMode = Processor mode in which the caller is waiting
151 * Altertable = Specifies if the wait is alertable
152 * Interval = Specifies the interval to wait
156 PKTHREAD CurrentThread
= KeGetCurrentThread();
157 KeAddThreadTimeout(CurrentThread
, Interval
);
158 return (KeWaitForSingleObject(&(CurrentThread
->Timer
),
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 if (PerformanceFreq
!= NULL
)
177 PerformanceFreq
->QuadPart
= 0;
180 return *PerformanceFreq
;
183 ULONG
KeQueryTimeIncrement(VOID
)
185 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
186 * the system clock every time the clock interrupts
187 * RETURNS: The increment
190 return(CLOCK_INCREMENT
);
193 VOID
KeQuerySystemTime(PLARGE_INTEGER CurrentTime
)
195 * FUNCTION: Gets the current system time
197 * CurrentTime (OUT) = The routine stores the current time here
198 * NOTE: The time is the number of 100-nanosecond intervals since the
199 * 1st of January, 1601.
202 CurrentTime
->QuadPart
= system_time
;
206 NTSTATUS STDCALL
NtGetTickCount (PULONG UpTime
)
212 BOOLEAN
KeSetTimer(PKTIMER Timer
, LARGE_INTEGER DueTime
, PKDPC Dpc
)
214 * FUNCTION: Sets the absolute or relative interval at which a timer object
215 * is to be set to the signaled state and optionally supplies a
216 * CustomTimerDpc to be executed when the timer expires.
218 * Timer = Points to a previously initialized timer object
219 * DueTimer = If positive then absolute time to expire at
220 * If negative then the relative time to expire at
221 * Dpc = If non-NULL then a dpc to be called when the timer expires
222 * RETURNS: True if the timer was already in the system timer queue
226 return(KeSetTimerEx(Timer
, DueTime
, 0, Dpc
));
229 BOOLEAN
KeSetTimerEx(PKTIMER Timer
, LARGE_INTEGER DueTime
, LONG Period
,
232 * FUNCTION: Sets the absolute or relative interval at which a timer object
233 * is to be set to the signaled state and optionally supplies a
234 * CustomTimerDpc to be executed when the timer expires.
236 * Timer = Points to a previously initialized timer object
237 * DueTimer = If positive then absolute time to expire at
238 * If negative then the relative time to expire at
239 * Dpc = If non-NULL then a dpc to be called when the timer expires
240 * RETURNS: True if the timer was already in the system timer queue
246 DPRINT("KeSetTimerEx(Timer %x)\n",Timer
);
248 KeRaiseIrql( HIGH_LEVEL
, &oldlvl
);
249 KeAcquireSpinLockAtDpcLevel(&TimerListLock
);
252 if (DueTime
.QuadPart
< 0)
254 Timer
->DueTime
.QuadPart
= system_time
+ (-(DueTime
.QuadPart
));
258 Timer
->DueTime
.QuadPart
= DueTime
.QuadPart
;
260 Timer
->Period
= Period
;
261 Timer
->Header
.SignalState
= FALSE
;
262 if (Timer
->TimerListEntry
.Flink
!= NULL
)
264 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
267 InsertTailList(&TimerListHead
,&Timer
->TimerListEntry
);
268 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
273 BOOLEAN
KeCancelTimer(PKTIMER Timer
)
275 * FUNCTION: Removes a timer from the system timer list
277 * Timer = timer to cancel
278 * RETURNS: True if the timer was running
284 DPRINT("KeCancelTimer(Timer %x)\n",Timer
);
286 KeRaiseIrql( HIGH_LEVEL
, &oldlvl
);
287 KeAcquireSpinLockAtDpcLevel( &TimerListLock
);
289 if (Timer
->TimerListEntry
.Flink
== NULL
)
291 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
294 RemoveEntryList(&Timer
->TimerListEntry
);
295 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
296 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
301 BOOLEAN
KeReadStateTimer(PKTIMER Timer
)
303 return(Timer
->Header
.SignalState
);
306 VOID
KeInitializeTimer(PKTIMER Timer
)
308 * FUNCTION: Initalizes a kernel timer object
310 * Timer = caller supplied storage for the timer
311 * NOTE: This function initializes a notification timer
314 KeInitializeTimerEx(Timer
,NotificationTimer
);
317 VOID
KeInitializeTimerEx(PKTIMER Timer
, TIMER_TYPE Type
)
319 * FUNCTION: Initializes a kernel timer object
321 * Timer = caller supplied storage for the timer
322 * Type = the type of timer (notification or synchronization)
323 * NOTE: When a notification type expires all waiting threads are released
324 * and the timer remains signalled until it is explicitly reset. When a
325 * syncrhonization timer expires its state is set to signalled until a
326 * single waiting thread is released and then the timer is reset.
331 if (Type
== NotificationTimer
)
333 IType
= InternalNotificationTimer
;
335 else if (Type
== SynchronizationTimer
)
337 IType
= InternalSynchronizationTimer
;
345 KeInitializeDispatcherHeader(&Timer
->Header
,
347 sizeof(KTIMER
) / sizeof(ULONG
),
349 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
352 VOID
KeQueryTickCount(PLARGE_INTEGER TickCount
)
354 * FUNCTION: Returns the number of ticks since the system was booted
356 * TickCount (OUT) = Points to storage for the number of ticks
359 TickCount
->QuadPart
= KiTimerTicks
;
362 static void HandleExpiredTimer(PKTIMER current
)
364 DPRINT("HandleExpiredTime(current %x)\n",current
);
365 if (current
->Dpc
!= NULL
)
367 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
368 current
->Dpc
, current
->Dpc
->DeferredRoutine
);
369 KeInsertQueueDpc(current
->Dpc
,
372 DPRINT("Finished dpc routine\n");
374 current
->Header
.SignalState
= TRUE
;
375 if (current
->Period
!= 0)
377 current
->DueTime
.QuadPart
+=
378 current
->Period
* SYSTEM_TIME_UNITS_PER_MSEC
;
382 RemoveEntryList(¤t
->TimerListEntry
);
383 current
->TimerListEntry
.Flink
= current
->TimerListEntry
.Blink
= NULL
;
387 VOID
KeExpireTimers(VOID
)
389 PLIST_ENTRY current_entry
= NULL
;
390 PKTIMER current
= NULL
;
393 DPRINT("KeExpireTimers()\n");
395 current_entry
= TimerListHead
.Flink
;
397 // DPRINT("&TimerListHead %x\n",&TimerListHead);
398 // DPRINT("current_entry %x\n",current_entry);
399 // DPRINT("current_entry->Flink %x\n",current_entry->Flink);
400 // DPRINT("current_entry->Flink->Flink %x\n",current_entry->Flink->Flink);
402 KeRaiseIrql(HIGH_LEVEL
, &oldlvl
);
403 KeAcquireSpinLockAtDpcLevel(&TimerListLock
);
405 while (current_entry
!=(&TimerListHead
))
407 current
= CONTAINING_RECORD(current_entry
, KTIMER
, TimerListEntry
);
409 current_entry
= current_entry
->Flink
;
411 if (system_time
>= current
->DueTime
.QuadPart
)
413 HandleExpiredTimer(current
);
417 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
418 // DPRINT("Finished KeExpireTimers()\n");
422 VOID
KiTimerInterrupt(VOID
)
424 * FUNCTION: Handles a timer interrupt
428 char* vidmem
=(char *)physical_to_linear(0xb8000 + 160 - 36);
431 // extern ULONG EiNrUsedBlocks;
432 extern unsigned int EiFreeNonPagedPool
;
433 extern unsigned int EiUsedNonPagedPool
;
434 extern ULONG PiNrThreads
;
435 // extern ULONG MiNrFreePages;
437 if (TimerInitDone
== FALSE
)
442 * Increment the number of timers ticks
445 system_time
= system_time
+ CLOCK_INCREMENT
;
448 * Display the tick count in the top left of the screen as a debugging
451 // sprintf(str,"%.8u %.8u",nr_used_blocks,ticks);
452 if ((EiFreeNonPagedPool
+ EiUsedNonPagedPool
) == 0)
458 x
= (EiFreeNonPagedPool
* 100) /
459 (EiFreeNonPagedPool
+ EiUsedNonPagedPool
);
460 y
= (EiUsedNonPagedPool
* 100) /
461 (EiFreeNonPagedPool
+ EiUsedNonPagedPool
);
463 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,ticks);
464 memset(str
, 0, sizeof(str
));
465 // sprintf(str,"%.8u %.8u",(unsigned int)EiNrUsedBlocks,
466 // (unsigned int)EiFreeNonPagedPool);
467 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,EiUsedNonPagedPool);
468 sprintf(str
,"%.8u %.8u",(unsigned int)PiNrRunnableThreads
,
469 (unsigned int)PiNrThreads
);
470 // sprintf(str,"%.8u %.8u", (unsigned int)PiNrRunnableThreads,
471 // (unsigned int)MiNrFreePages);
483 VOID
KeInitializeTimerImpl(VOID
)
485 * FUNCTION: Initializes timer irq handling
486 * NOTE: This is only called once from main()
489 TIME_FIELDS TimeFields
;
490 LARGE_INTEGER SystemBootTime
;
492 DPRINT("KeInitializeTimerImpl()\n");
494 InitializeListHead(&TimerListHead
);
495 KeInitializeSpinLock(&TimerListLock
);
497 TimerInitDone
= TRUE
;
500 * Calculate the starting time for the system clock
502 HalQueryRealTimeClock(&TimeFields
);
503 RtlTimeFieldsToTime(&TimeFields
, &SystemBootTime
);
504 boot_time
=SystemBootTime
.QuadPart
;
505 system_time
=boot_time
;
507 DPRINT("Finished KeInitializeTimerImpl()\n");