* include/ddk/zw.h: Corrected declarations of NtCreateProfile,
[reactos.git] / reactos / ntoskrnl / ke / timer.c
1 /* $Id: timer.c,v 1.42 2001/03/16 16:05:33 dwelch 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 <internal/ke.h>
23 #include <internal/id.h>
24 #include <internal/ps.h>
25
26 #define NDEBUG
27 #include <internal/debug.h>
28
29 /* TYPES *****************************************************************/
30
31 #define TIMER_IRQ 0
32
33 /* GLOBALS ****************************************************************/
34
35 #define IDMAP_BASE (0xd0000000)
36
37 /*
38 * Return a linear address which can be used to access the physical memory
39 * starting at x
40 */
41 unsigned int physical_to_linear(unsigned int x)
42 {
43 return(x+IDMAP_BASE);
44 }
45
46 unsigned int linear_to_physical(unsigned int x)
47 {
48 return(x-IDMAP_BASE);
49 }
50
51 /*
52 * Current time
53 */
54 static unsigned long long boot_time = 0;
55 static unsigned long long system_time = 0;
56
57 /*
58 * Number of timer interrupts since initialisation
59 */
60 volatile ULONGLONG KiTimerTicks;
61 volatile ULONG KiRawTicks = 0;
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 (100000)
71
72 /*
73 * PURPOSE: List of timers
74 */
75 static LIST_ENTRY TimerListHead;
76 static KSPIN_LOCK TimerListLock;
77 static KDPC ExpireTimerDpc;
78
79 /* must raise IRQL to HIGH_LEVEL and grab spin lock there, to sync with ISR */
80
81 extern ULONG PiNrRunnableThreads;
82
83 #define MICROSECONDS_PER_TICK (10000)
84 #define TICKS_TO_CALIBRATE (1)
85 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
86 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
87
88 static BOOLEAN TimerInitDone = FALSE;
89
90 /* FUNCTIONS **************************************************************/
91
92
93 NTSTATUS STDCALL NtQueryTimerResolution(OUT PULONG MinimumResolution,
94 OUT PULONG MaximumResolution,
95 OUT PULONG ActualResolution)
96 {
97 UNIMPLEMENTED;
98 }
99
100
101 NTSTATUS STDCALL NtSetTimerResolution(IN ULONG RequestedResolution,
102 IN BOOL SetOrUnset,
103 OUT PULONG ActualResolution)
104 {
105 UNIMPLEMENTED;
106 }
107
108
109 NTSTATUS STDCALL NtQueryPerformanceCounter (IN PLARGE_INTEGER Counter,
110 IN PLARGE_INTEGER Frequency)
111 {
112 UNIMPLEMENTED;
113 }
114
115
116 NTSTATUS KeAddThreadTimeout(PKTHREAD Thread, PLARGE_INTEGER Interval)
117 {
118 assert(Thread != NULL);
119 assert(Interval != NULL);
120
121 DPRINT("KeAddThreadTimeout(Thread %x, Interval %x)\n",Thread,Interval);
122
123 KeInitializeTimer(&Thread->Timer);
124 KeSetTimer(&Thread->Timer, *Interval, &Thread->TimerDpc);
125
126 DPRINT("Thread->Timer.entry.Flink %x\n",
127 Thread->Timer.TimerListEntry.Flink);
128
129 return STATUS_SUCCESS;
130 }
131
132
133 NTSTATUS STDCALL NtDelayExecution(IN ULONG Alertable,
134 IN TIME* Interval)
135 {
136 NTSTATUS Status;
137 LARGE_INTEGER Timeout;
138
139 Timeout = *((PLARGE_INTEGER)Interval);
140 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
141 Alertable, Internal, Timeout);
142
143 DPRINT("Execution delay is %d/%d\n",
144 Timeout.u.Highpart, Timeout.u.LowPart);
145 Status = KeDelayExecutionThread(UserMode, Alertable, &Timeout);
146 return(Status);
147 }
148
149
150 NTSTATUS STDCALL
151 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode,
152 BOOLEAN Alertable,
153 PLARGE_INTEGER Interval)
154 /*
155 * FUNCTION: Puts the current thread into an alertable or nonalertable
156 * wait state for a given internal
157 * ARGUMENTS:
158 * WaitMode = Processor mode in which the caller is waiting
159 * Altertable = Specifies if the wait is alertable
160 * Interval = Specifies the interval to wait
161 * RETURNS: Status
162 */
163 {
164 PKTHREAD CurrentThread = KeGetCurrentThread();
165 KeAddThreadTimeout(CurrentThread, Interval);
166 return (KeWaitForSingleObject(&CurrentThread->Timer,
167 Executive,
168 UserMode,
169 Alertable,
170 NULL));
171 }
172
173 ULONG STDCALL
174 KeQueryTimeIncrement (VOID)
175 /*
176 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
177 * the system clock every time the clock interrupts
178 * RETURNS: The increment
179 */
180 {
181 return(CLOCK_INCREMENT);
182 }
183
184 VOID STDCALL
185 KeQuerySystemTime (PLARGE_INTEGER CurrentTime)
186 /*
187 * FUNCTION: Gets the current system time
188 * ARGUMENTS:
189 * CurrentTime (OUT) = The routine stores the current time here
190 * NOTE: The time is the number of 100-nanosecond intervals since the
191 * 1st of January, 1601.
192 */
193 {
194 CurrentTime->QuadPart = system_time;
195 }
196
197
198 NTSTATUS STDCALL
199 NtGetTickCount (PULONG UpTime)
200 {
201 LARGE_INTEGER TickCount;
202 if ( UpTime == NULL )
203 return(STATUS_INVALID_PARAMETER);
204 KeQueryTickCount(&TickCount);
205 *UpTime = TickCount.u.LowPart;
206 return (STATUS_SUCCESS);
207 }
208
209
210 BOOLEAN STDCALL
211 KeSetTimer (PKTIMER Timer,
212 LARGE_INTEGER DueTime,
213 PKDPC Dpc)
214 /*
215 * FUNCTION: Sets the absolute or relative interval at which a timer object
216 * is to be set to the signaled state and optionally supplies a
217 * CustomTimerDpc to be executed when the timer expires.
218 * ARGUMENTS:
219 * Timer = Points to a previously initialized timer object
220 * DueTimer = If positive then absolute time to expire at
221 * If negative then the relative time to expire at
222 * Dpc = If non-NULL then a dpc to be called when the timer expires
223 * RETURNS: True if the timer was already in the system timer queue
224 * False otherwise
225 */
226 {
227 return(KeSetTimerEx(Timer, DueTime, 0, Dpc));
228 }
229
230 BOOLEAN STDCALL
231 KeSetTimerEx (PKTIMER Timer,
232 LARGE_INTEGER DueTime,
233 LONG Period,
234 PKDPC Dpc)
235 /*
236 * FUNCTION: Sets the absolute or relative interval at which a timer object
237 * is to be set to the signaled state and optionally supplies a
238 * CustomTimerDpc to be executed when the timer expires.
239 * ARGUMENTS:
240 * Timer = Points to a previously initialized timer object
241 * DueTimer = If positive then absolute time to expire at
242 * If negative then the relative time to expire at
243 * Dpc = If non-NULL then a dpc to be called when the timer expires
244 * RETURNS: True if the timer was already in the system timer queue
245 * False otherwise
246 */
247 {
248 KIRQL oldlvl;
249
250 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer);
251 KeAcquireSpinLock( &TimerListLock, &oldlvl );
252
253 Timer->Dpc = Dpc;
254 if (DueTime.QuadPart < 0)
255 {
256 Timer->DueTime.QuadPart = system_time - DueTime.QuadPart;
257 }
258 else
259 {
260 Timer->DueTime.QuadPart = DueTime.QuadPart;
261 }
262 Timer->Period = Period;
263 Timer->Header.SignalState = FALSE;
264 if (Timer->TimerListEntry.Flink != NULL)
265 {
266 KeReleaseSpinLock(&TimerListLock, oldlvl);
267 return(TRUE);
268 }
269 InsertTailList(&TimerListHead,&Timer->TimerListEntry);
270 KeReleaseSpinLock(&TimerListLock, oldlvl);
271
272 return FALSE;
273 }
274
275 BOOLEAN STDCALL
276 KeCancelTimer (PKTIMER Timer)
277 /*
278 * FUNCTION: Removes a timer from the system timer list
279 * ARGUMENTS:
280 * Timer = timer to cancel
281 * RETURNS: True if the timer was running
282 * False otherwise
283 */
284 {
285 KIRQL oldlvl;
286
287 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
288
289 KeRaiseIrql( HIGH_LEVEL, &oldlvl );
290 KeAcquireSpinLockAtDpcLevel( &TimerListLock );
291
292 if (Timer->TimerListEntry.Flink == NULL)
293 {
294 KeReleaseSpinLock(&TimerListLock, oldlvl);
295 return(FALSE);
296 }
297 RemoveEntryList(&Timer->TimerListEntry);
298 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
299 KeReleaseSpinLock(&TimerListLock, oldlvl);
300
301 return(TRUE);
302 }
303
304 BOOLEAN STDCALL
305 KeReadStateTimer (PKTIMER Timer)
306 {
307 return(Timer->Header.SignalState);
308 }
309
310 VOID STDCALL
311 KeInitializeTimer (PKTIMER Timer)
312 /*
313 * FUNCTION: Initalizes a kernel timer object
314 * ARGUMENTS:
315 * Timer = caller supplied storage for the timer
316 * NOTE: This function initializes a notification timer
317 */
318 {
319 KeInitializeTimerEx(Timer,NotificationTimer);
320 }
321
322 VOID STDCALL
323 KeInitializeTimerEx (PKTIMER Timer,
324 TIMER_TYPE Type)
325 /*
326 * FUNCTION: Initializes a kernel timer object
327 * ARGUMENTS:
328 * Timer = caller supplied storage for the timer
329 * Type = the type of timer (notification or synchronization)
330 * NOTE: When a notification type expires all waiting threads are released
331 * and the timer remains signalled until it is explicitly reset. When a
332 * syncrhonization timer expires its state is set to signalled until a
333 * single waiting thread is released and then the timer is reset.
334 */
335 {
336 ULONG IType;
337
338 if (Type == NotificationTimer)
339 {
340 IType = InternalNotificationTimer;
341 }
342 else if (Type == SynchronizationTimer)
343 {
344 IType = InternalSynchronizationTimer;
345 }
346 else
347 {
348 assert(FALSE);
349 return;
350 }
351
352 KeInitializeDispatcherHeader(&Timer->Header,
353 IType,
354 sizeof(KTIMER) / sizeof(ULONG),
355 FALSE);
356 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
357 }
358
359 VOID STDCALL
360 KeQueryTickCount (PLARGE_INTEGER TickCount)
361 /*
362 * FUNCTION: Returns the number of ticks since the system was booted
363 * ARGUMENTS:
364 * TickCount (OUT) = Points to storage for the number of ticks
365 */
366 {
367 TickCount->QuadPart = KiTimerTicks;
368 }
369
370 static void HandleExpiredTimer(PKTIMER current)
371 {
372 DPRINT("HandleExpiredTime(current %x)\n",current);
373 if (current->Dpc != NULL)
374 {
375 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
376 current->Dpc, current->Dpc->DeferredRoutine);
377 KeInsertQueueDpc(current->Dpc,
378 NULL,
379 NULL);
380 DPRINT("Finished dpc routine\n");
381 }
382 KeAcquireDispatcherDatabaseLock(FALSE);
383 current->Header.SignalState = TRUE;
384 KeDispatcherObjectWake(&current->Header);
385 KeReleaseDispatcherDatabaseLock(FALSE);
386 if (current->Period != 0)
387 {
388 current->DueTime.QuadPart +=
389 current->Period * SYSTEM_TIME_UNITS_PER_MSEC;
390 }
391 else
392 {
393 RemoveEntryList(&current->TimerListEntry);
394 current->TimerListEntry.Flink = current->TimerListEntry.Blink = NULL;
395 }
396 }
397
398 VOID
399 KeExpireTimers(PKDPC Dpc,
400 PVOID Context1,
401 PVOID Arg1,
402 PVOID Arg2)
403 {
404 PLIST_ENTRY current_entry = NULL;
405 PKTIMER current = NULL;
406 ULONG Eip = (ULONG)Arg1;
407
408 DPRINT("KeExpireTimers()\n");
409
410 current_entry = TimerListHead.Flink;
411
412 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
413
414 while (current_entry != &TimerListHead)
415 {
416 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
417
418 current_entry = current_entry->Flink;
419
420 if (system_time >= current->DueTime.QuadPart)
421 {
422 HandleExpiredTimer(current);
423 }
424 }
425
426 KiAddProfileEvent(ProfileTime, Eip);
427
428 KeReleaseSpinLockFromDpcLevel(&TimerListLock);
429 }
430
431
432 VOID
433 KiUpdateSystemTime (KIRQL oldIrql, ULONG Eip)
434 /*
435 * FUNCTION: Handles a timer interrupt
436 */
437 {
438 char* vidmem=(char *)physical_to_linear(0xb8000 + 160 - 2);
439
440 KiRawTicks++;
441
442 if (TimerInitDone == FALSE)
443 {
444 return;
445 }
446 /*
447 * Increment the number of timers ticks
448 */
449 KiTimerTicks++;
450 system_time = system_time + CLOCK_INCREMENT;
451
452 /*
453 * Display the tick count in the top left of the screen as a debugging
454 * aid
455 */
456 vidmem[0] = ' ';
457 if (oldIrql < DISPATCH_LEVEL)
458 {
459 vidmem[1] = 0x17;
460 }
461 else
462 {
463 vidmem[1] = 0x27;
464 }
465
466 /*
467 * Queue a DPC that will expire timers
468 */
469 KeInsertQueueDpc(&ExpireTimerDpc, (PVOID)Eip, 0);
470 }
471
472
473 VOID
474 KeInitializeTimerImpl(VOID)
475 /*
476 * FUNCTION: Initializes timer irq handling
477 * NOTE: This is only called once from main()
478 */
479 {
480 TIME_FIELDS TimeFields;
481 LARGE_INTEGER SystemBootTime;
482 extern VOID HalpCalibrateStallExecution (VOID);
483
484 DPRINT("KeInitializeTimerImpl()\n");
485
486 HalpCalibrateStallExecution ();
487
488 InitializeListHead(&TimerListHead);
489 KeInitializeSpinLock(&TimerListLock);
490 KeInitializeDpc(&ExpireTimerDpc, KeExpireTimers, 0);
491 TimerInitDone = TRUE;
492
493 /*
494 * Calculate the starting time for the system clock
495 */
496 HalQueryRealTimeClock(&TimeFields);
497 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
498 boot_time=SystemBootTime.QuadPart;
499 system_time=boot_time;
500
501 DPRINT("Finished KeInitializeTimerImpl()\n");
502 }