466e8958a02d86905d4d4b9ca7a074d005f2597b
[reactos.git] / reactos / ntoskrnl / ke / timer.c
1 /* $Id: timer.c,v 1.47 2001/08/27 01:21:50 ekohl 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 KeTickCount;
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
117 NTSTATUS STDCALL NtDelayExecution(IN ULONG Alertable,
118 IN TIME* Interval)
119 {
120 NTSTATUS Status;
121 LARGE_INTEGER Timeout;
122
123 Timeout = *((PLARGE_INTEGER)Interval);
124 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
125 Alertable, Internal, Timeout);
126
127 DPRINT("Execution delay is %d/%d\n",
128 Timeout.u.HighPart, Timeout.u.LowPart);
129 Status = KeDelayExecutionThread(UserMode, Alertable, &Timeout);
130 return(Status);
131 }
132
133
134 NTSTATUS STDCALL
135 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode,
136 BOOLEAN Alertable,
137 PLARGE_INTEGER Interval)
138 /*
139 * FUNCTION: Puts the current thread into an alertable or nonalertable
140 * wait state for a given internal
141 * ARGUMENTS:
142 * WaitMode = Processor mode in which the caller is waiting
143 * Altertable = Specifies if the wait is alertable
144 * Interval = Specifies the interval to wait
145 * RETURNS: Status
146 */
147 {
148 PKTHREAD Thread = KeGetCurrentThread();
149
150 KeInitializeTimer(&Thread->Timer);
151 KeSetTimer(&Thread->Timer, *Interval, NULL);
152 return (KeWaitForSingleObject(&Thread->Timer,
153 Executive,
154 UserMode,
155 Alertable,
156 NULL));
157 }
158
159 ULONG STDCALL
160 KeQueryTimeIncrement (VOID)
161 /*
162 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
163 * the system clock every time the clock interrupts
164 * RETURNS: The increment
165 */
166 {
167 return(CLOCK_INCREMENT);
168 }
169
170 VOID STDCALL
171 KeQuerySystemTime (PLARGE_INTEGER CurrentTime)
172 /*
173 * FUNCTION: Gets the current system time
174 * ARGUMENTS:
175 * CurrentTime (OUT) = The routine stores the current time here
176 * NOTE: The time is the number of 100-nanosecond intervals since the
177 * 1st of January, 1601.
178 */
179 {
180 CurrentTime->QuadPart = system_time;
181 }
182
183
184 NTSTATUS STDCALL
185 NtGetTickCount (PULONG UpTime)
186 {
187 LARGE_INTEGER TickCount;
188 if ( UpTime == NULL )
189 return(STATUS_INVALID_PARAMETER);
190 KeQueryTickCount(&TickCount);
191 *UpTime = TickCount.u.LowPart;
192 return (STATUS_SUCCESS);
193 }
194
195
196 BOOLEAN STDCALL
197 KeSetTimer (PKTIMER Timer,
198 LARGE_INTEGER DueTime,
199 PKDPC Dpc)
200 /*
201 * FUNCTION: Sets the absolute or relative interval at which a timer object
202 * is to be set to the signaled state and optionally supplies a
203 * CustomTimerDpc to be executed when the timer expires.
204 * ARGUMENTS:
205 * Timer = Points to a previously initialized timer object
206 * DueTimer = If positive then absolute time to expire at
207 * If negative then the relative time to expire at
208 * Dpc = If non-NULL then a dpc to be called when the timer expires
209 * RETURNS: True if the timer was already in the system timer queue
210 * False otherwise
211 */
212 {
213 return(KeSetTimerEx(Timer, DueTime, 0, Dpc));
214 }
215
216 BOOLEAN STDCALL
217 KeSetTimerEx (PKTIMER Timer,
218 LARGE_INTEGER DueTime,
219 LONG Period,
220 PKDPC Dpc)
221 /*
222 * FUNCTION: Sets the absolute or relative interval at which a timer object
223 * is to be set to the signaled state and optionally supplies a
224 * CustomTimerDpc to be executed when the timer expires.
225 * ARGUMENTS:
226 * Timer = Points to a previously initialized timer object
227 * DueTimer = If positive then absolute time to expire at
228 * If negative then the relative time to expire at
229 * Dpc = If non-NULL then a dpc to be called when the timer expires
230 * RETURNS: True if the timer was already in the system timer queue
231 * False otherwise
232 */
233 {
234 KIRQL oldlvl;
235
236 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer);
237 KeAcquireSpinLock( &TimerListLock, &oldlvl );
238
239 Timer->Dpc = Dpc;
240 if (DueTime.QuadPart < 0)
241 {
242 Timer->DueTime.QuadPart = system_time - DueTime.QuadPart;
243 }
244 else
245 {
246 Timer->DueTime.QuadPart = DueTime.QuadPart;
247 }
248 Timer->Period = Period;
249 Timer->Header.SignalState = FALSE;
250 if (Timer->TimerListEntry.Flink != NULL)
251 {
252 KeReleaseSpinLock(&TimerListLock, oldlvl);
253 return(TRUE);
254 }
255 InsertTailList(&TimerListHead,&Timer->TimerListEntry);
256 KeReleaseSpinLock(&TimerListLock, oldlvl);
257
258 return FALSE;
259 }
260
261 BOOLEAN STDCALL
262 KeCancelTimer (PKTIMER Timer)
263 /*
264 * FUNCTION: Removes a timer from the system timer list
265 * ARGUMENTS:
266 * Timer = timer to cancel
267 * RETURNS: True if the timer was running
268 * False otherwise
269 */
270 {
271 KIRQL oldlvl;
272
273 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
274
275 KeRaiseIrql(HIGH_LEVEL, &oldlvl);
276 KeAcquireSpinLockAtDpcLevel( &TimerListLock );
277
278 if (Timer->TimerListEntry.Flink == NULL)
279 {
280 KeReleaseSpinLock(&TimerListLock, oldlvl);
281 return(FALSE);
282 }
283 RemoveEntryList(&Timer->TimerListEntry);
284 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
285 KeReleaseSpinLock(&TimerListLock, oldlvl);
286
287 return(TRUE);
288 }
289
290 BOOLEAN STDCALL
291 KeReadStateTimer (PKTIMER Timer)
292 {
293 return(Timer->Header.SignalState);
294 }
295
296 VOID STDCALL
297 KeInitializeTimer (PKTIMER Timer)
298 /*
299 * FUNCTION: Initalizes a kernel timer object
300 * ARGUMENTS:
301 * Timer = caller supplied storage for the timer
302 * NOTE: This function initializes a notification timer
303 */
304 {
305 KeInitializeTimerEx(Timer,NotificationTimer);
306 }
307
308 VOID STDCALL
309 KeInitializeTimerEx (PKTIMER Timer,
310 TIMER_TYPE Type)
311 /*
312 * FUNCTION: Initializes a kernel timer object
313 * ARGUMENTS:
314 * Timer = caller supplied storage for the timer
315 * Type = the type of timer (notification or synchronization)
316 * NOTE: When a notification type expires all waiting threads are released
317 * and the timer remains signalled until it is explicitly reset. When a
318 * syncrhonization timer expires its state is set to signalled until a
319 * single waiting thread is released and then the timer is reset.
320 */
321 {
322 ULONG IType;
323
324 if (Type == NotificationTimer)
325 {
326 IType = InternalNotificationTimer;
327 }
328 else if (Type == SynchronizationTimer)
329 {
330 IType = InternalSynchronizationTimer;
331 }
332 else
333 {
334 assert(FALSE);
335 return;
336 }
337
338 KeInitializeDispatcherHeader(&Timer->Header,
339 IType,
340 sizeof(KTIMER) / sizeof(ULONG),
341 FALSE);
342 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
343 }
344
345 VOID STDCALL
346 KeQueryTickCount (PLARGE_INTEGER TickCount)
347 /*
348 * FUNCTION: Returns the number of ticks since the system was booted
349 * ARGUMENTS:
350 * TickCount (OUT) = Points to storage for the number of ticks
351 */
352 {
353 TickCount->QuadPart = KeTickCount;
354 }
355
356 STATIC VOID
357 HandleExpiredTimer(PKTIMER current)
358 {
359 DPRINT("HandleExpiredTime(current %x)\n",current);
360 if (current->Dpc != NULL)
361 {
362 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
363 current->Dpc, current->Dpc->DeferredRoutine);
364 KeInsertQueueDpc(current->Dpc,
365 NULL,
366 NULL);
367 DPRINT("Finished dpc routine\n");
368 }
369 KeAcquireDispatcherDatabaseLock(FALSE);
370 current->Header.SignalState = TRUE;
371 KeDispatcherObjectWake(&current->Header);
372 KeReleaseDispatcherDatabaseLock(FALSE);
373 if (current->Period != 0)
374 {
375 current->DueTime.QuadPart +=
376 current->Period * SYSTEM_TIME_UNITS_PER_MSEC;
377 }
378 else
379 {
380 RemoveEntryList(&current->TimerListEntry);
381 current->TimerListEntry.Flink = current->TimerListEntry.Blink = NULL;
382 }
383 }
384
385 VOID STDCALL
386 KeExpireTimers(PKDPC Dpc,
387 PVOID Context1,
388 PVOID Arg1,
389 PVOID Arg2)
390 {
391 PLIST_ENTRY current_entry = NULL;
392 PKTIMER current = NULL;
393 ULONG Eip = (ULONG)Arg1;
394
395 DPRINT("KeExpireTimers()\n");
396
397 current_entry = TimerListHead.Flink;
398
399 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
400
401 while (current_entry != &TimerListHead)
402 {
403 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
404
405 current_entry = current_entry->Flink;
406
407 if (system_time >= current->DueTime.QuadPart)
408 {
409 HandleExpiredTimer(current);
410 }
411 }
412
413 KiAddProfileEvent(ProfileTime, Eip);
414
415 KeReleaseSpinLockFromDpcLevel(&TimerListLock);
416 }
417
418
419 VOID
420 KiUpdateSystemTime (KIRQL oldIrql, ULONG Eip)
421 /*
422 * FUNCTION: Handles a timer interrupt
423 */
424 {
425 KiRawTicks++;
426
427 if (TimerInitDone == FALSE)
428 {
429 return;
430 }
431 /*
432 * Increment the number of timers ticks
433 */
434 KeTickCount++;
435 system_time = system_time + CLOCK_INCREMENT;
436
437 /*
438 * Queue a DPC that will expire timers
439 */
440 KeInsertQueueDpc(&ExpireTimerDpc, (PVOID)Eip, 0);
441 }
442
443
444 VOID
445 KeInitializeTimerImpl(VOID)
446 /*
447 * FUNCTION: Initializes timer irq handling
448 * NOTE: This is only called once from main()
449 */
450 {
451 TIME_FIELDS TimeFields;
452 LARGE_INTEGER SystemBootTime;
453
454 DPRINT("KeInitializeTimerImpl()\n");
455 InitializeListHead(&TimerListHead);
456 KeInitializeSpinLock(&TimerListLock);
457 KeInitializeDpc(&ExpireTimerDpc, KeExpireTimers, 0);
458 TimerInitDone = TRUE;
459 /*
460 * Calculate the starting time for the system clock
461 */
462 HalQueryRealTimeClock(&TimeFields);
463 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
464 boot_time=SystemBootTime.QuadPart;
465 system_time=boot_time;
466
467 DPRINT("Finished KeInitializeTimerImpl()\n");
468 }