impl. NtGetTickCount
[reactos.git] / reactos / ntoskrnl / ke / timer.c
1 /* $Id: timer.c,v 1.27 2000/02/14 10:27:40 ariadne Exp $
2 *
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)
8 * UPDATE HISTORY:
9 * 28/05/98: Created
10 * 12/3/99: Phillip Susi: enabled the timers, fixed spin lock
11 */
12
13 /* NOTES ******************************************************************/
14 /*
15 * System time units are 100-nanosecond intervals
16 */
17
18 /* INCLUDES ***************************************************************/
19
20 #include <limits.h>
21 #include <ddk/ntddk.h>
22 #include <string.h>
23 #include <internal/string.h>
24 #include <stdio.h>
25 #include <internal/ke.h>
26
27 #define NDEBUG
28 #include <internal/debug.h>
29
30 /* TYPES *****************************************************************/
31
32 #define TIMER_IRQ 0
33
34 /* GLOBALS ****************************************************************/
35
36 #define IDMAP_BASE (0xd0000000)
37
38 /*
39 * Return a linear address which can be used to access the physical memory
40 * starting at x
41 */
42 extern inline unsigned int physical_to_linear(unsigned int x)
43 {
44 return(x+IDMAP_BASE);
45 }
46
47 extern inline unsigned int linear_to_physical(unsigned int x)
48 {
49 return(x-IDMAP_BASE);
50 }
51
52 /*
53 * Current time
54 */
55 static unsigned long long boot_time = 0;
56 static unsigned long long system_time = 0;
57
58 /*
59 * Number of timer interrupts since initialisation
60 */
61 volatile ULONGLONG KiTimerTicks;
62
63 /*
64 * The increment in the system clock every timer tick (in system time units)
65 *
66 * = (1/18.2)*10^9
67 *
68 * RJJ was 54945055
69 */
70 #define CLOCK_INCREMENT (549450)
71
72 /*
73 * PURPOSE: List of timers
74 */
75 static LIST_ENTRY TimerListHead;
76 static KSPIN_LOCK TimerListLock;
77
78 /* must raise IRQL to HIGH_LEVEL and grab spin lock there, to sync with ISR */
79
80 extern ULONG PiNrRunnableThreads;
81
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)
86
87 static BOOLEAN TimerInitDone = FALSE;
88
89 /* FUNCTIONS **************************************************************/
90
91
92 NTSTATUS STDCALL NtQueryTimerResolution(OUT PULONG MinimumResolution,
93 OUT PULONG MaximumResolution,
94 OUT PULONG ActualResolution)
95 {
96 UNIMPLEMENTED;
97 }
98
99
100 NTSTATUS STDCALL NtSetTimerResolution(IN ULONG RequestedResolution,
101 IN BOOL SetOrUnset,
102 OUT PULONG ActualResolution)
103 {
104 UNIMPLEMENTED;
105 }
106
107
108 NTSTATUS STDCALL NtQueryPerformanceCounter (IN PLARGE_INTEGER Counter,
109 IN PLARGE_INTEGER Frequency)
110 {
111 UNIMPLEMENTED;
112 }
113
114
115 NTSTATUS KeAddThreadTimeout(PKTHREAD Thread, PLARGE_INTEGER Interval)
116 {
117 assert(Thread != NULL);
118 assert(Interval != NULL);
119
120 DPRINT("KeAddThreadTimeout(Thread %x, Interval %x)\n",Thread,Interval);
121
122 KeInitializeTimer(&(Thread->Timer));
123 KeSetTimer( &(Thread->Timer), *Interval, &Thread->TimerDpc );
124
125 DPRINT("Thread->Timer.entry.Flink %x\n",
126 Thread->Timer.TimerListEntry.Flink);
127
128 return STATUS_SUCCESS;
129 }
130
131
132 NTSTATUS STDCALL NtDelayExecution(IN ULONG Alertable,
133 IN TIME* Interval)
134 {
135 NTSTATUS Status;
136 PLARGE_INTEGER IntervalP;
137
138 IntervalP = (PLARGE_INTEGER)Interval;
139
140 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
141 Alertable, Internal, IntervalP);
142
143 Status = KeDelayExecutionThread(UserMode, Alertable, IntervalP);
144 return(Status);
145 }
146
147
148 NTSTATUS STDCALL KeDelayExecutionThread(KPROCESSOR_MODE WaitMode,
149 BOOLEAN Alertable,
150 PLARGE_INTEGER Interval)
151 /*
152 * FUNCTION: Puts the current thread into an alertable or nonalertable
153 * wait state for a given internal
154 * ARGUMENTS:
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
158 * RETURNS: Status
159 */
160 {
161 PKTHREAD CurrentThread = KeGetCurrentThread();
162 KeAddThreadTimeout(CurrentThread, Interval);
163 return (KeWaitForSingleObject(&(CurrentThread->Timer),
164 Executive,
165 KernelMode,
166 Alertable,
167 NULL));
168 }
169
170 LARGE_INTEGER KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq)
171 /*
172 * FUNCTION: Queries the finest grained running count avaiable in the system
173 * ARGUMENTS:
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
178 */
179 {
180 if (PerformanceFreq != NULL)
181 {
182 PerformanceFreq->QuadPart = 0;
183 }
184
185 return *PerformanceFreq;
186 }
187
188 ULONG KeQueryTimeIncrement(VOID)
189 /*
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
193 */
194 {
195 return(CLOCK_INCREMENT);
196 }
197
198 VOID KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
199 /*
200 * FUNCTION: Gets the current system time
201 * ARGUMENTS:
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.
205 */
206 {
207 CurrentTime->QuadPart = system_time;
208 }
209
210
211 NTSTATUS STDCALL NtGetTickCount (PULONG UpTime)
212 {
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);
219 }
220
221
222 BOOLEAN KeSetTimer(PKTIMER Timer, LARGE_INTEGER DueTime, PKDPC Dpc)
223 /*
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.
227 * ARGUMENTS:
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
233 * False otherwise
234 */
235 {
236 return(KeSetTimerEx(Timer, DueTime, 0, Dpc));
237 }
238
239 BOOLEAN KeSetTimerEx(PKTIMER Timer, LARGE_INTEGER DueTime, LONG Period,
240 PKDPC Dpc)
241 /*
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.
245 * ARGUMENTS:
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
251 * False otherwise
252 */
253 {
254 KIRQL oldlvl;
255
256 DPRINT("KeSetTimerEx(Timer %x)\n",Timer);
257
258 KeRaiseIrql( HIGH_LEVEL, &oldlvl );
259 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
260
261 Timer->Dpc = Dpc;
262 if (DueTime.QuadPart < 0)
263 {
264 Timer->DueTime.QuadPart = system_time + (-(DueTime.QuadPart));
265 }
266 else
267 {
268 Timer->DueTime.QuadPart = DueTime.QuadPart;
269 }
270 Timer->Period = Period;
271 Timer->Header.SignalState = FALSE;
272 if (Timer->TimerListEntry.Flink != NULL)
273 {
274 KeReleaseSpinLock(&TimerListLock, oldlvl);
275 return(TRUE);
276 }
277 InsertTailList(&TimerListHead,&Timer->TimerListEntry);
278 KeReleaseSpinLock(&TimerListLock, oldlvl);
279
280 return FALSE;
281 }
282
283 BOOLEAN KeCancelTimer(PKTIMER Timer)
284 /*
285 * FUNCTION: Removes a timer from the system timer list
286 * ARGUMENTS:
287 * Timer = timer to cancel
288 * RETURNS: True if the timer was running
289 * False otherwise
290 */
291 {
292 KIRQL oldlvl;
293
294 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
295
296 KeRaiseIrql( HIGH_LEVEL, &oldlvl );
297 KeAcquireSpinLockAtDpcLevel( &TimerListLock );
298
299 if (Timer->TimerListEntry.Flink == NULL)
300 {
301 KeReleaseSpinLock(&TimerListLock, oldlvl);
302 return(FALSE);
303 }
304 RemoveEntryList(&Timer->TimerListEntry);
305 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
306 KeReleaseSpinLock(&TimerListLock, oldlvl);
307
308 return(TRUE);
309 }
310
311 BOOLEAN KeReadStateTimer(PKTIMER Timer)
312 {
313 return(Timer->Header.SignalState);
314 }
315
316 VOID KeInitializeTimer(PKTIMER Timer)
317 /*
318 * FUNCTION: Initalizes a kernel timer object
319 * ARGUMENTS:
320 * Timer = caller supplied storage for the timer
321 * NOTE: This function initializes a notification timer
322 */
323 {
324 KeInitializeTimerEx(Timer,NotificationTimer);
325 }
326
327 VOID KeInitializeTimerEx(PKTIMER Timer, TIMER_TYPE Type)
328 /*
329 * FUNCTION: Initializes a kernel timer object
330 * ARGUMENTS:
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.
337 */
338 {
339 ULONG IType;
340
341 if (Type == NotificationTimer)
342 {
343 IType = InternalNotificationTimer;
344 }
345 else if (Type == SynchronizationTimer)
346 {
347 IType = InternalSynchronizationTimer;
348 }
349 else
350 {
351 assert(FALSE);
352 return;
353 }
354
355 KeInitializeDispatcherHeader(&Timer->Header,
356 IType,
357 sizeof(KTIMER) / sizeof(ULONG),
358 FALSE);
359 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
360 }
361
362 VOID KeQueryTickCount(PLARGE_INTEGER TickCount)
363 /*
364 * FUNCTION: Returns the number of ticks since the system was booted
365 * ARGUMENTS:
366 * TickCount (OUT) = Points to storage for the number of ticks
367 */
368 {
369 TickCount->QuadPart = KiTimerTicks;
370 }
371
372 static void HandleExpiredTimer(PKTIMER current)
373 {
374 DPRINT("HandleExpiredTime(current %x)\n",current);
375 if (current->Dpc != NULL)
376 {
377 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
378 current->Dpc, current->Dpc->DeferredRoutine);
379 KeInsertQueueDpc(current->Dpc,
380 NULL,
381 NULL);
382 DPRINT("Finished dpc routine\n");
383 }
384 current->Header.SignalState = TRUE;
385 if (current->Period != 0)
386 {
387 current->DueTime.QuadPart +=
388 current->Period * SYSTEM_TIME_UNITS_PER_MSEC;
389 }
390 else
391 {
392 RemoveEntryList(&current->TimerListEntry);
393 current->TimerListEntry.Flink = current->TimerListEntry.Blink = NULL;
394 }
395 }
396
397 VOID KeExpireTimers(VOID)
398 {
399 PLIST_ENTRY current_entry = NULL;
400 PKTIMER current = NULL;
401 KIRQL oldlvl;
402
403 DPRINT("KeExpireTimers()\n");
404
405 current_entry = TimerListHead.Flink;
406
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);
411
412 KeRaiseIrql(HIGH_LEVEL, &oldlvl);
413 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
414
415 while (current_entry!=(&TimerListHead))
416 {
417 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
418
419 current_entry = current_entry->Flink;
420
421 if (system_time >= current->DueTime.QuadPart)
422 {
423 HandleExpiredTimer(current);
424 }
425 }
426
427 KeReleaseSpinLock(&TimerListLock, oldlvl);
428 // DPRINT("Finished KeExpireTimers()\n");
429 }
430
431
432 VOID KiTimerInterrupt(VOID)
433 /*
434 * FUNCTION: Handles a timer interrupt
435 */
436 {
437 char str[36];
438 char* vidmem=(char *)physical_to_linear(0xb8000 + 160 - 36);
439 int i;
440 int x,y;
441 // extern ULONG EiNrUsedBlocks;
442 extern unsigned int EiFreeNonPagedPool;
443 extern unsigned int EiUsedNonPagedPool;
444 extern ULONG PiNrThreads;
445 // extern ULONG MiNrFreePages;
446
447 if (TimerInitDone == FALSE)
448 {
449 return;
450 }
451 /*
452 * Increment the number of timers ticks
453 */
454 KiTimerTicks++;
455 system_time = system_time + CLOCK_INCREMENT;
456
457 /*
458 * Display the tick count in the top left of the screen as a debugging
459 * aid
460 */
461 // sprintf(str,"%.8u %.8u",nr_used_blocks,ticks);
462 if ((EiFreeNonPagedPool + EiUsedNonPagedPool) == 0)
463 {
464 x = y = 0;
465 }
466 else
467 {
468 x = (EiFreeNonPagedPool * 100) /
469 (EiFreeNonPagedPool + EiUsedNonPagedPool);
470 y = (EiUsedNonPagedPool * 100) /
471 (EiFreeNonPagedPool + EiUsedNonPagedPool);
472 }
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);
482 for (i=0;i<17;i++)
483 {
484 *vidmem=str[i];
485 vidmem++;
486 *vidmem=0x7;
487 vidmem++;
488 }
489 KeExpireTimers();
490 }
491
492
493 VOID KeInitializeTimerImpl(VOID)
494 /*
495 * FUNCTION: Initializes timer irq handling
496 * NOTE: This is only called once from main()
497 */
498 {
499 TIME_FIELDS TimeFields;
500 LARGE_INTEGER SystemBootTime;
501
502 DPRINT("KeInitializeTimerImpl()\n");
503
504 InitializeListHead(&TimerListHead);
505 KeInitializeSpinLock(&TimerListLock);
506
507 TimerInitDone = TRUE;
508
509 /*
510 * Calculate the starting time for the system clock
511 */
512 HalQueryRealTimeClock(&TimeFields);
513 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
514 boot_time=SystemBootTime.QuadPart;
515 system_time=boot_time;
516
517 DPRINT("Finished KeInitializeTimerImpl()\n");
518 }