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 ***************************************************************/
19 #include <ddk/ntddk.h>
21 #include <internal/string.h>
25 #include <internal/debug.h>
27 /* TYPES *****************************************************************/
32 /* GLOBALS ****************************************************************/
34 #define IDMAP_BASE (0xd0000000)
37 * Return a linear address which can be used to access the physical memory
40 extern inline unsigned int physical_to_linear(unsigned int x
)
45 extern inline unsigned int linear_to_physical(unsigned int x
)
53 static unsigned long long boot_time
= 0;
54 static unsigned long long system_time
= 0;
57 * Number of timer interrupts since initialisation
59 volatile ULONGLONG KiTimerTicks
;
62 * The increment in the system clock every timer tick (in system time units)
68 #define CLOCK_INCREMENT (549450)
71 * PURPOSE: List of timers
73 static LIST_ENTRY timer_list_head
= {NULL
,NULL
};
74 static KSPIN_LOCK timer_list_lock
= {0,};
78 #define MICROSECONDS_PER_TICK (54945)
79 #define TICKS_TO_CALIBRATE (1)
80 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
81 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
83 static unsigned int loops_per_microsecond
= 100;
85 /* FUNCTIONS **************************************************************/
87 VOID
KeCalibrateTimerLoop(VOID
)
89 unsigned int start_tick
;
90 // unsigned int end_tick;
91 // unsigned int nr_ticks;
93 unsigned int microseconds
;
98 start_tick
= KiTimerTicks
;
100 while (start_tick
== KiTimerTicks
);
101 while (KiTimerTicks
== (start_tick
+TICKS_TO_CALIBRATE
))
103 KeStallExecutionProcessor(1);
107 // DbgPrint("microseconds %d\n",microseconds);
109 if (microseconds
> (CALIBRATE_PERIOD
+1000))
111 loops_per_microsecond
= loops_per_microsecond
+ 1;
113 if (microseconds
< (CALIBRATE_PERIOD
-1000))
115 loops_per_microsecond
= loops_per_microsecond
- 1;
117 // DbgPrint("loops_per_microsecond %d\n",loops_per_microsecond);
123 NTSTATUS STDCALL
NtQueryTimerResolution (OUT PULONG MinimumResolution
,
124 OUT PULONG MaximumResolution
,
125 OUT PULONG ActualResolution
)
127 return(ZwQueryTimerResolution(MinimumResolution
,MaximumResolution
,
131 NTSTATUS STDCALL
ZwQueryTimerResolution (OUT PULONG MinimumResolution
,
132 OUT PULONG MaximumResolution
,
133 OUT PULONG ActualResolution
)
138 NTSTATUS STDCALL
NtSetTimerResolution(IN ULONG RequestedResolution
,
140 OUT PULONG ActualResolution
)
142 return(ZwSetTimerResolution(RequestedResolution
,
147 NTSTATUS STDCALL
ZwSetTimerResolution(IN ULONG RequestedResolution
,
149 OUT PULONG ActualResolution
)
154 NTSTATUS STDCALL
NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter
,
155 IN PLARGE_INTEGER Frequency
)
157 return(ZwQueryPerformanceCounter(Counter
,
161 NTSTATUS STDCALL
ZwQueryPerformanceCounter(IN PLARGE_INTEGER Counter
,
162 IN PLARGE_INTEGER Frequency
)
168 NTSTATUS
KeAddThreadTimeout(PKTHREAD Thread
, PLARGE_INTEGER Interval
)
170 assert(Thread
!= NULL
);
171 assert(Interval
!= NULL
);
173 DPRINT("KeAddThreadTimeout(Thread %x, Interval %x)\n",Thread
,Interval
);
175 KeInitializeTimer(&(Thread
->TimerBlock
));
176 KeSetTimer(&(Thread
->TimerBlock
),*Interval
,NULL
);
178 DPRINT("Thread->TimerBlock.entry.Flink %x\n",
179 Thread
->TimerBlock
.entry
.Flink
);
181 return STATUS_SUCCESS
;
185 NTSTATUS STDCALL
NtDelayExecution(IN BOOLEAN Alertable
,
188 return(ZwDelayExecution(Alertable
,Interval
));
191 NTSTATUS STDCALL
ZwDelayExecution(IN BOOLEAN Alertable
,
195 return(STATUS_UNSUCCESSFUL
);
198 NTSTATUS
KeDelayExecutionThread(KPROCESSOR_MODE WaitMode
,
200 PLARGE_INTEGER Interval
)
202 * FUNCTION: Puts the current thread into an alertable or nonalertable
203 * wait state for a given internal
205 * WaitMode = Processor mode in which the caller is waiting
206 * Altertable = Specifies if the wait is alertable
207 * Interval = Specifies the interval to wait
211 PKTHREAD CurrentThread
= KeGetCurrentThread();
212 KeAddThreadTimeout(CurrentThread
,Interval
);
213 return(KeWaitForSingleObject(&(CurrentThread
->TimerBlock
),Executive
,
214 KernelMode
,Alertable
,NULL
));
217 VOID
KeStallExecutionProcessor(ULONG MicroSeconds
)
220 for (i
=0; i
<(loops_per_microsecond
*MicroSeconds
) ;i
++)
227 static inline void ULLToLargeInteger(unsigned long long src
,
230 dest
->LowPart
= src
& 0xffffffff;
231 dest
->HighPart
= (src
>>32);
234 static inline void SLLToLargeInteger(signed long long src
,
239 dest
->LowPart
= src
& 0xffffffff;
240 dest
->HighPart
= (src
>>32);
245 dest
->LowPart
= src
& 0xffffffff;
246 dest
->HighPart
= -(src
>>32);
250 static inline signed long long LargeIntegerToSLL(PLARGE_INTEGER src
)
255 if (src
->HighPart
>= 0)
257 r
= r
| (((unsigned long long)src
->HighPart
)<<32);
261 r
= r
| (((unsigned long long)(-(src
->HighPart
)))<<32);
270 LARGE_INTEGER
KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq
)
272 * FUNCTION: Queries the finest grained running count avaiable in the system
274 * PerformanceFreq (OUT) = The routine stores the number of
275 * performance counters tick per second here
276 * RETURNS: The performance counter value in HERTZ
277 * NOTE: Returns the system tick count or the time-stamp on the pentium
280 if (PerformanceFreq
!= NULL
)
282 PerformanceFreq
->QuadPart
= 0;
285 return *PerformanceFreq
;
288 ULONG
KeQueryTimeIncrement(VOID
)
290 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
291 * the system clock every time the clock interrupts
292 * RETURNS: The increment
295 return(CLOCK_INCREMENT
);
298 VOID
KeQuerySystemTime(PLARGE_INTEGER CurrentTime
)
300 * FUNCTION: Gets the current system time
302 * CurrentTime (OUT) = The routine stores the current time here
303 * NOTE: The time is the number of 100-nanosecond intervals since the
304 * 1st of January, 1601.
307 CurrentTime
->QuadPart
= system_time
;
310 NTSTATUS STDCALL
NtGetTickCount(PULONG UpTime
)
312 return(ZwGetTickCount(UpTime
));
315 NTSTATUS STDCALL
ZwGetTickCount(PULONG UpTime
)
320 BOOLEAN
KeSetTimer(PKTIMER Timer
, LARGE_INTEGER DueTime
, PKDPC Dpc
)
322 * FUNCTION: Sets the absolute or relative interval at which a timer object
323 * is to be set to the signaled state and optionally supplies a
324 * CustomTimerDpc to be executed when the timer expires.
326 * Timer = Points to a previously initialized timer object
327 * DueTimer = If positive then absolute time to expire at
328 * If negative then the relative time to expire at
329 * Dpc = If non-NULL then a dpc to be called when the timer expires
330 * RETURNS: True if the timer was already in the system timer queue
334 return(KeSetTimerEx(Timer
,DueTime
,0,Dpc
));
337 BOOLEAN
KeSetTimerEx(PKTIMER Timer
, LARGE_INTEGER DueTime
, LONG Period
,
340 * FUNCTION: Sets the absolute or relative interval at which a timer object
341 * is to be set to the signaled state and optionally supplies a
342 * CustomTimerDpc to be executed when the timer expires.
344 * Timer = Points to a previously initialized timer object
345 * DueTimer = If positive then absolute time to expire at
346 * If negative then the relative time to expire at
347 * Dpc = If non-NULL then a dpc to be called when the timer expires
348 * RETURNS: True if the timer was already in the system timer queue
354 DPRINT("KeSetTimerEx(Timer %x)\n",Timer
);
356 KeAcquireSpinLock(&timer_list_lock
,&oldlvl
);
359 Timer
->period
=Period
;
360 Timer
->expire_time
= (*(long long int *)&DueTime
);
361 if (Timer
->expire_time
< 0)
363 Timer
->expire_time
= system_time
+ (-Timer
->expire_time
);
365 DPRINT("System:%ld:%ld Expire:%d:%d Period:%d\n",
366 (unsigned long) (system_time
& 0xffffffff),
367 (unsigned long) ((system_time
>> 32) & 0xffffffff),
368 (unsigned long) (Timer
->expire_time
& 0xffffffff),
369 (unsigned long) ((Timer
->expire_time
>> 32) & 0xffffffff),
371 Timer
->signaled
= FALSE
;
374 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
377 DPRINT("Inserting %x in list\n",&Timer
->entry
);
378 DPRINT("Timer->entry.Flink %x\n",Timer
->entry
.Flink
);
380 InsertTailList(&timer_list_head
,&Timer
->entry
);
381 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
386 BOOLEAN
KeCancelTimer(PKTIMER Timer
)
388 * FUNCTION: Removes a timer from the system timer list
390 * Timer = timer to cancel
391 * RETURNS: True if the timer was running
397 DPRINT("KeCancelTimer(Timer %x)\n",Timer
);
399 KeAcquireSpinLock(&timer_list_lock
,&oldlvl
);
403 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
406 RemoveEntryList(&Timer
->entry
);
407 Timer
->running
= FALSE
;
408 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
413 BOOLEAN
KeReadStateTimer(PKTIMER Timer
)
415 return Timer
->signaled
;
418 VOID
KeInitializeTimer(PKTIMER Timer
)
420 * FUNCTION: Initalizes a kernel timer object
422 * Timer = caller supplied storage for the timer
423 * NOTE: This function initializes a notification timer
426 KeInitializeTimerEx(Timer
,NotificationTimer
);
429 VOID
KeInitializeTimerEx(PKTIMER Timer
, TIMER_TYPE Type
)
431 * FUNCTION: Initializes a kernel timer object
433 * Timer = caller supplied storage for the timer
434 * Type = the type of timer (notification or synchronization)
435 * NOTE: When a notification type expires all waiting threads are released
436 * and the timer remains signalled until it is explicitly reset. When a
437 * syncrhonization timer expires its state is set to signalled until a
438 * single waiting thread is released and then the timer is reset.
441 Timer
->running
=FALSE
;
443 Timer
->signaled
=FALSE
;
446 VOID
KeQueryTickCount(PLARGE_INTEGER TickCount
)
448 * FUNCTION: Returns the number of ticks since the system was booted
450 * TickCount (OUT) = Points to storage for the number of ticks
453 TickCount
->QuadPart
= KiTimerTicks
;
456 static void HandleExpiredTimer(PKTIMER current
)
458 DPRINT("HandleExpiredTime(current %x)\n",current
);
459 if (current
->dpc
!=NULL
)
461 DPRINT("current->dpc->DeferredRoutine %x\n",
462 current
->dpc
->DeferredRoutine
);
463 current
->dpc
->DeferredRoutine(current
->dpc
,
464 current
->dpc
->DeferredContext
,
465 current
->dpc
->SystemArgument1
,
466 current
->dpc
->SystemArgument2
);
467 DPRINT("Finished dpc routine\n");
469 current
->signaled
=TRUE
;
470 if (current
->period
!= 0)
472 DPRINT("System:%ld:%ld Expire:%d:%d Period:%d\n",
473 (unsigned long) (system_time
& 0xffffffff),
474 (unsigned long) ((system_time
>> 32) & 0xffffffff),
475 (unsigned long) (current
->expire_time
& 0xffffffff),
476 (unsigned long) ((current
->expire_time
>> 32) & 0xffffffff),
478 current
->expire_time
+= current
->period
* SYSTEM_TIME_UNITS_PER_MSEC
;
482 RemoveEntryList(¤t
->entry
);
483 current
->running
=FALSE
;
487 void KeExpireTimers(void)
489 PLIST_ENTRY current_entry
= timer_list_head
.Flink
;
490 PKTIMER current
= CONTAINING_RECORD(current_entry
,KTIMER
,entry
);
493 DPRINT("KeExpireTimers()\n");
494 DPRINT("&timer_list_head %x\n",&timer_list_head
);
495 DPRINT("current_entry %x\n",current_entry
);
496 DPRINT("current_entry->Flink %x\n",current_entry
->Flink
);
497 DPRINT("current_entry->Flink->Flink %x\n",current_entry
->Flink
->Flink
);
499 KeAcquireSpinLock(&timer_list_lock
,&oldlvl
);
501 while (current_entry
!=(&timer_list_head
))
503 if (system_time
>= current
->expire_time
)
505 HandleExpiredTimer(current
);
508 current_entry
= current_entry
->Flink
;
509 current
= CONTAINING_RECORD(current_entry
,KTIMER
,entry
);
511 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
512 DPRINT("Finished KeExpireTimers()\n");
516 BOOLEAN
KiTimerInterrupt(VOID
)
518 * FUNCTION: Handles a timer interrupt
522 char* vidmem
=(char *)physical_to_linear(0xb8000 + 160 - 36);
525 extern ULONG EiNrUsedBlocks
;
526 extern unsigned int EiFreeNonPagedPool
;
527 extern unsigned int EiUsedNonPagedPool
;
530 * Increment the number of timers ticks
533 system_time
= system_time
+ CLOCK_INCREMENT
;
536 * Display the tick count in the top left of the screen as a debugging
539 // sprintf(str,"%.8u %.8u",nr_used_blocks,ticks);
540 if ((EiFreeNonPagedPool
+ EiUsedNonPagedPool
) == 0)
546 x
= (EiFreeNonPagedPool
* 100) /
547 (EiFreeNonPagedPool
+ EiUsedNonPagedPool
);
548 y
= (EiUsedNonPagedPool
* 100) /
549 (EiFreeNonPagedPool
+ EiUsedNonPagedPool
);
551 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,ticks);
552 memset(str
, 0, sizeof(str
));
553 sprintf(str
,"%.8u %.8u",(unsigned int)EiNrUsedBlocks
,
554 (unsigned int)EiFreeNonPagedPool
);
555 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,EiUsedNonPagedPool);
556 // sprintf(str,"%.8u %.8u",PiNrThreads,KiTimerTicks);
569 VOID
KeInitializeTimerImpl(VOID
)
571 * FUNCTION: Initializes timer irq handling
572 * NOTE: This is only called once from main()
575 TIME_FIELDS TimeFields
;
576 LARGE_INTEGER SystemBootTime
;
578 InitializeListHead(&timer_list_head
);
579 KeInitializeSpinLock(&timer_list_lock
);
582 * Calculate the starting time for the system clock
584 HalQueryRealTimeClock(&TimeFields
);
585 DbgPrint("Date: %d.%d.%d\nTime: %d:%d:%d\n",
592 RtlTimeFieldsToTime(&TimeFields
, &SystemBootTime
);
593 boot_time
=SystemBootTime
.QuadPart
;
594 system_time
=boot_time
;