Improved time functions.
[reactos.git] / reactos / ntoskrnl / ke / timer.c
1 /*
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)
7 * UPDATE HISTORY:
8 * 28/05/98: Created
9 */
10
11 /* NOTES ******************************************************************/
12 /*
13 * System time units are 100-nanosecond intervals
14 */
15
16 /* INCLUDES ***************************************************************/
17
18 #include <limits.h>
19 #include <ddk/ntddk.h>
20 #include <string.h>
21 #include <internal/string.h>
22 #include <stdio.h>
23
24 #define NDEBUG
25 #include <internal/debug.h>
26
27 /* TYPES *****************************************************************/
28
29 #define TIMER_IRQ 0
30
31
32 /* GLOBALS ****************************************************************/
33
34 #define IDMAP_BASE (0xd0000000)
35
36 /*
37 * Return a linear address which can be used to access the physical memory
38 * starting at x
39 */
40 extern inline unsigned int physical_to_linear(unsigned int x)
41 {
42 return(x+IDMAP_BASE);
43 }
44
45 extern inline unsigned int linear_to_physical(unsigned int x)
46 {
47 return(x-IDMAP_BASE);
48 }
49
50 /*
51 * Current time
52 */
53 static unsigned long long boot_time = 0;
54 static unsigned long long system_time = 0;
55
56 /*
57 * Number of timer interrupts since initialisation
58 */
59 volatile ULONGLONG KiTimerTicks;
60
61 /*
62 * The increment in the system clock every timer tick (in system time units)
63 *
64 * = (1/18.2)*10^9
65 *
66 * RJJ was 54945055
67 */
68 #define CLOCK_INCREMENT (549450)
69
70 /*
71 * PURPOSE: List of timers
72 */
73 static LIST_ENTRY timer_list_head = {NULL,NULL};
74 static KSPIN_LOCK timer_list_lock = {0,};
75
76
77
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)
82
83 static unsigned int loops_per_microsecond = 100;
84
85 /* FUNCTIONS **************************************************************/
86
87 VOID KeCalibrateTimerLoop(VOID)
88 {
89 unsigned int start_tick;
90 // unsigned int end_tick;
91 // unsigned int nr_ticks;
92 unsigned int i;
93 unsigned int microseconds;
94
95 for (i=0;i<20;i++)
96 {
97
98 start_tick = KiTimerTicks;
99 microseconds = 0;
100 while (start_tick == KiTimerTicks);
101 while (KiTimerTicks == (start_tick+TICKS_TO_CALIBRATE))
102 {
103 KeStallExecutionProcessor(1);
104 microseconds++;
105 };
106
107 // DbgPrint("microseconds %d\n",microseconds);
108
109 if (microseconds > (CALIBRATE_PERIOD+1000))
110 {
111 loops_per_microsecond = loops_per_microsecond + 1;
112 }
113 if (microseconds < (CALIBRATE_PERIOD-1000))
114 {
115 loops_per_microsecond = loops_per_microsecond - 1;
116 }
117 // DbgPrint("loops_per_microsecond %d\n",loops_per_microsecond);
118 }
119 // for(;;);
120 }
121
122
123 NTSTATUS STDCALL NtQueryTimerResolution (OUT PULONG MinimumResolution,
124 OUT PULONG MaximumResolution,
125 OUT PULONG ActualResolution)
126 {
127 return(ZwQueryTimerResolution(MinimumResolution,MaximumResolution,
128 ActualResolution));
129 }
130
131 NTSTATUS STDCALL ZwQueryTimerResolution (OUT PULONG MinimumResolution,
132 OUT PULONG MaximumResolution,
133 OUT PULONG ActualResolution)
134 {
135 UNIMPLEMENTED;
136 }
137
138 NTSTATUS STDCALL NtSetTimerResolution(IN ULONG RequestedResolution,
139 IN BOOL SetOrUnset,
140 OUT PULONG ActualResolution)
141 {
142 return(ZwSetTimerResolution(RequestedResolution,
143 SetOrUnset,
144 ActualResolution));
145 }
146
147 NTSTATUS STDCALL ZwSetTimerResolution(IN ULONG RequestedResolution,
148 IN BOOL SetOrUnset,
149 OUT PULONG ActualResolution)
150 {
151 UNIMPLEMENTED;
152 }
153
154 NTSTATUS STDCALL NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter,
155 IN PLARGE_INTEGER Frequency)
156 {
157 return(ZwQueryPerformanceCounter(Counter,
158 Frequency));
159 }
160
161 NTSTATUS STDCALL ZwQueryPerformanceCounter(IN PLARGE_INTEGER Counter,
162 IN PLARGE_INTEGER Frequency)
163 {
164 UNIMPLEMENTED;
165 }
166
167
168 NTSTATUS KeAddThreadTimeout(PKTHREAD Thread, PLARGE_INTEGER Interval)
169 {
170 assert(Thread != NULL);
171 assert(Interval != NULL);
172
173 DPRINT("KeAddThreadTimeout(Thread %x, Interval %x)\n",Thread,Interval);
174
175 KeInitializeTimer(&(Thread->TimerBlock));
176 KeSetTimer(&(Thread->TimerBlock),*Interval,NULL);
177
178 DPRINT("Thread->TimerBlock.entry.Flink %x\n",
179 Thread->TimerBlock.entry.Flink);
180
181 return STATUS_SUCCESS;
182 }
183
184
185 NTSTATUS STDCALL NtDelayExecution(IN BOOLEAN Alertable,
186 IN TIME *Interval)
187 {
188 return(ZwDelayExecution(Alertable,Interval));
189 }
190
191 NTSTATUS STDCALL ZwDelayExecution(IN BOOLEAN Alertable,
192 IN TIME *Interval)
193 {
194 KeBugCheck(0);
195 return(STATUS_UNSUCCESSFUL);
196 }
197
198 NTSTATUS KeDelayExecutionThread(KPROCESSOR_MODE WaitMode,
199 BOOLEAN Alertable,
200 PLARGE_INTEGER Interval)
201 /*
202 * FUNCTION: Puts the current thread into an alertable or nonalertable
203 * wait state for a given internal
204 * ARGUMENTS:
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
208 * RETURNS: Status
209 */
210 {
211 PKTHREAD CurrentThread = KeGetCurrentThread();
212 KeAddThreadTimeout(CurrentThread,Interval);
213 return(KeWaitForSingleObject(&(CurrentThread->TimerBlock),Executive,
214 KernelMode,Alertable,NULL));
215 }
216
217 VOID KeStallExecutionProcessor(ULONG MicroSeconds)
218 {
219 unsigned int i;
220 for (i=0; i<(loops_per_microsecond*MicroSeconds) ;i++)
221 {
222 __asm__("nop\n\t");
223 }
224 }
225
226 #if 0
227 static inline void ULLToLargeInteger(unsigned long long src,
228 PLARGE_INTEGER dest)
229 {
230 dest->LowPart = src & 0xffffffff;
231 dest->HighPart = (src>>32);
232 }
233
234 static inline void SLLToLargeInteger(signed long long src,
235 PLARGE_INTEGER dest)
236 {
237 if (src > 0)
238 {
239 dest->LowPart = src & 0xffffffff;
240 dest->HighPart = (src>>32);
241 }
242 else
243 {
244 src = -src;
245 dest->LowPart = src & 0xffffffff;
246 dest->HighPart = -(src>>32);
247 }
248 }
249
250 static inline signed long long LargeIntegerToSLL(PLARGE_INTEGER src)
251 {
252 signed long long r;
253
254 r = src->LowPart;
255 if (src->HighPart >= 0)
256 {
257 r = r | (((unsigned long long)src->HighPart)<<32);
258 }
259 else
260 {
261 r = r | (((unsigned long long)(-(src->HighPart)))<<32);
262 r = -r;
263 }
264 return(r);
265 }
266
267 #endif
268
269
270 LARGE_INTEGER KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq)
271 /*
272 * FUNCTION: Queries the finest grained running count avaiable in the system
273 * ARGUMENTS:
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
278 */
279 {
280 if (PerformanceFreq != NULL)
281 {
282 PerformanceFreq->QuadPart = 0;
283 }
284
285 return *PerformanceFreq;
286 }
287
288 ULONG KeQueryTimeIncrement(VOID)
289 /*
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
293 */
294 {
295 return(CLOCK_INCREMENT);
296 }
297
298 VOID KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
299 /*
300 * FUNCTION: Gets the current system time
301 * ARGUMENTS:
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.
305 */
306 {
307 CurrentTime->QuadPart = system_time;
308 }
309
310 NTSTATUS STDCALL NtGetTickCount(PULONG UpTime)
311 {
312 return(ZwGetTickCount(UpTime));
313 }
314
315 NTSTATUS STDCALL ZwGetTickCount(PULONG UpTime)
316 {
317 UNIMPLEMENTED;
318 }
319
320 BOOLEAN KeSetTimer(PKTIMER Timer, LARGE_INTEGER DueTime, PKDPC Dpc)
321 /*
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.
325 * ARGUMENTS:
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
331 * False otherwise
332 */
333 {
334 return(KeSetTimerEx(Timer,DueTime,0,Dpc));
335 }
336
337 BOOLEAN KeSetTimerEx(PKTIMER Timer, LARGE_INTEGER DueTime, LONG Period,
338 PKDPC Dpc)
339 /*
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.
343 * ARGUMENTS:
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
349 * False otherwise
350 */
351 {
352 KIRQL oldlvl;
353
354 DPRINT("KeSetTimerEx(Timer %x)\n",Timer);
355
356 KeAcquireSpinLock(&timer_list_lock,&oldlvl);
357
358 Timer->dpc=Dpc;
359 Timer->period=Period;
360 Timer->expire_time = (*(long long int *)&DueTime);
361 if (Timer->expire_time < 0)
362 {
363 Timer->expire_time = system_time + (-Timer->expire_time);
364 }
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),
370 Timer->period);
371 Timer->signaled = FALSE;
372 if (Timer->running)
373 {
374 KeReleaseSpinLock(&timer_list_lock,oldlvl);
375 return TRUE;
376 }
377 DPRINT("Inserting %x in list\n",&Timer->entry);
378 DPRINT("Timer->entry.Flink %x\n",Timer->entry.Flink);
379 Timer->running=TRUE;
380 InsertTailList(&timer_list_head,&Timer->entry);
381 KeReleaseSpinLock(&timer_list_lock,oldlvl);
382
383 return FALSE;
384 }
385
386 BOOLEAN KeCancelTimer(PKTIMER Timer)
387 /*
388 * FUNCTION: Removes a timer from the system timer list
389 * ARGUMENTS:
390 * Timer = timer to cancel
391 * RETURNS: True if the timer was running
392 * False otherwise
393 */
394 {
395 KIRQL oldlvl;
396
397 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
398
399 KeAcquireSpinLock(&timer_list_lock,&oldlvl);
400
401 if (!Timer->running)
402 {
403 KeReleaseSpinLock(&timer_list_lock,oldlvl);
404 return FALSE;
405 }
406 RemoveEntryList(&Timer->entry);
407 Timer->running = FALSE;
408 KeReleaseSpinLock(&timer_list_lock,oldlvl);
409
410 return TRUE;
411 }
412
413 BOOLEAN KeReadStateTimer(PKTIMER Timer)
414 {
415 return Timer->signaled;
416 }
417
418 VOID KeInitializeTimer(PKTIMER Timer)
419 /*
420 * FUNCTION: Initalizes a kernel timer object
421 * ARGUMENTS:
422 * Timer = caller supplied storage for the timer
423 * NOTE: This function initializes a notification timer
424 */
425 {
426 KeInitializeTimerEx(Timer,NotificationTimer);
427 }
428
429 VOID KeInitializeTimerEx(PKTIMER Timer, TIMER_TYPE Type)
430 /*
431 * FUNCTION: Initializes a kernel timer object
432 * ARGUMENTS:
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.
439 */
440 {
441 Timer->running=FALSE;
442 Timer->type=Type;
443 Timer->signaled=FALSE;
444 }
445
446 VOID KeQueryTickCount(PLARGE_INTEGER TickCount)
447 /*
448 * FUNCTION: Returns the number of ticks since the system was booted
449 * ARGUMENTS:
450 * TickCount (OUT) = Points to storage for the number of ticks
451 */
452 {
453 TickCount->QuadPart = KiTimerTicks;
454 }
455
456 static void HandleExpiredTimer(PKTIMER current)
457 {
458 DPRINT("HandleExpiredTime(current %x)\n",current);
459 if (current->dpc!=NULL)
460 {
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");
468 }
469 current->signaled=TRUE;
470 if (current->period != 0)
471 {
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),
477 current->period);
478 current->expire_time += current->period * SYSTEM_TIME_UNITS_PER_MSEC;
479 }
480 else
481 {
482 RemoveEntryList(&current->entry);
483 current->running=FALSE;
484 }
485 }
486
487 void KeExpireTimers(void)
488 {
489 PLIST_ENTRY current_entry = timer_list_head.Flink;
490 PKTIMER current = CONTAINING_RECORD(current_entry,KTIMER,entry);
491 KIRQL oldlvl;
492
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);
498
499 KeAcquireSpinLock(&timer_list_lock,&oldlvl);
500
501 while (current_entry!=(&timer_list_head))
502 {
503 if (system_time >= current->expire_time)
504 {
505 HandleExpiredTimer(current);
506 }
507
508 current_entry = current_entry->Flink;
509 current = CONTAINING_RECORD(current_entry,KTIMER,entry);
510 }
511 KeReleaseSpinLock(&timer_list_lock,oldlvl);
512 DPRINT("Finished KeExpireTimers()\n");
513 }
514
515
516 BOOLEAN KiTimerInterrupt(VOID)
517 /*
518 * FUNCTION: Handles a timer interrupt
519 */
520 {
521 char str[36];
522 char* vidmem=(char *)physical_to_linear(0xb8000 + 160 - 36);
523 int i;
524 int x,y;
525 extern ULONG EiNrUsedBlocks;
526 extern unsigned int EiFreeNonPagedPool;
527 extern unsigned int EiUsedNonPagedPool;
528
529 /*
530 * Increment the number of timers ticks
531 */
532 KiTimerTicks++;
533 system_time = system_time + CLOCK_INCREMENT;
534
535 /*
536 * Display the tick count in the top left of the screen as a debugging
537 * aid
538 */
539 // sprintf(str,"%.8u %.8u",nr_used_blocks,ticks);
540 if ((EiFreeNonPagedPool + EiUsedNonPagedPool) == 0)
541 {
542 x = y = 0;
543 }
544 else
545 {
546 x = (EiFreeNonPagedPool * 100) /
547 (EiFreeNonPagedPool + EiUsedNonPagedPool);
548 y = (EiUsedNonPagedPool * 100) /
549 (EiFreeNonPagedPool + EiUsedNonPagedPool);
550 }
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);
557 for (i=0;i<17;i++)
558 {
559 *vidmem=str[i];
560 vidmem++;
561 *vidmem=0x7;
562 vidmem++;
563 }
564
565 return TRUE;
566 }
567
568
569 VOID KeInitializeTimerImpl(VOID)
570 /*
571 * FUNCTION: Initializes timer irq handling
572 * NOTE: This is only called once from main()
573 */
574 {
575 TIME_FIELDS TimeFields;
576 LARGE_INTEGER SystemBootTime;
577
578 InitializeListHead(&timer_list_head);
579 KeInitializeSpinLock(&timer_list_lock);
580
581 /*
582 * Calculate the starting time for the system clock
583 */
584 HalQueryRealTimeClock(&TimeFields);
585 DbgPrint("Date: %d.%d.%d\nTime: %d:%d:%d\n",
586 TimeFields.Day,
587 TimeFields.Month,
588 TimeFields.Year,
589 TimeFields.Hour,
590 TimeFields.Minute,
591 TimeFields.Second);
592 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
593 boot_time=SystemBootTime.QuadPart;
594 system_time=boot_time;
595 }