Cleaned up code a bit
[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 /*
35 * Current time
36 */
37 static unsigned long long system_time = 0;
38
39 /*
40 * Number of timer interrupts since initialisation
41 */
42 volatile ULONGLONG KiTimerTicks;
43
44 /*
45 * The increment in the system clock every timer tick (in system time units)
46 *
47 * = (1/18.2)*10^9
48 *
49 * RJJ was 54945055
50 */
51 #define CLOCK_INCREMENT (549450)
52
53 /*
54 * PURPOSE: List of timers
55 */
56 static LIST_ENTRY timer_list_head = {NULL,NULL};
57 static KSPIN_LOCK timer_list_lock = {0,};
58
59
60
61 #define MICROSECONDS_PER_TICK (54945)
62 #define TICKS_TO_CALIBRATE (1)
63 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
64 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
65
66 static unsigned int loops_per_microsecond = 100;
67
68 /* FUNCTIONS **************************************************************/
69
70 VOID KeCalibrateTimerLoop(VOID)
71 {
72 unsigned int start_tick;
73 // unsigned int end_tick;
74 // unsigned int nr_ticks;
75 unsigned int i;
76 unsigned int microseconds;
77
78 for (i=0;i<20;i++)
79 {
80
81 start_tick = KiTimerTicks;
82 microseconds = 0;
83 while (start_tick == KiTimerTicks);
84 while (KiTimerTicks == (start_tick+TICKS_TO_CALIBRATE))
85 {
86 KeStallExecutionProcessor(1);
87 microseconds++;
88 };
89
90 // DbgPrint("microseconds %d\n",microseconds);
91
92 if (microseconds > (CALIBRATE_PERIOD+1000))
93 {
94 loops_per_microsecond = loops_per_microsecond + 1;
95 }
96 if (microseconds < (CALIBRATE_PERIOD-1000))
97 {
98 loops_per_microsecond = loops_per_microsecond - 1;
99 }
100 // DbgPrint("loops_per_microsecond %d\n",loops_per_microsecond);
101 }
102 // for(;;);
103 }
104
105
106 NTSTATUS STDCALL NtQueryTimerResolution (OUT PULONG MinimumResolution,
107 OUT PULONG MaximumResolution,
108 OUT PULONG ActualResolution)
109 {
110 return(ZwQueryTimerResolution(MinimumResolution,MaximumResolution,
111 ActualResolution));
112 }
113
114 NTSTATUS STDCALL ZwQueryTimerResolution (OUT PULONG MinimumResolution,
115 OUT PULONG MaximumResolution,
116 OUT PULONG ActualResolution)
117 {
118 UNIMPLEMENTED;
119 }
120
121 NTSTATUS STDCALL NtSetTimerResolution(IN ULONG RequestedResolution,
122 IN BOOL SetOrUnset,
123 OUT PULONG ActualResolution)
124 {
125 return(ZwSetTimerResolution(RequestedResolution,
126 SetOrUnset,
127 ActualResolution));
128 }
129
130 NTSTATUS STDCALL ZwSetTimerResolution(IN ULONG RequestedResolution,
131 IN BOOL SetOrUnset,
132 OUT PULONG ActualResolution)
133 {
134 UNIMPLEMENTED;
135 }
136
137 NTSTATUS STDCALL NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter,
138 IN PLARGE_INTEGER Frequency)
139 {
140 return(ZwQueryPerformanceCounter(Counter,
141 Frequency));
142 }
143
144 NTSTATUS STDCALL ZwQueryPerformanceCounter(IN PLARGE_INTEGER Counter,
145 IN PLARGE_INTEGER Frequency)
146 {
147 UNIMPLEMENTED;
148 }
149
150
151 NTSTATUS KeAddThreadTimeout(PKTHREAD Thread, PLARGE_INTEGER Interval)
152 {
153 assert(Thread != NULL);
154 assert(Interval != NULL);
155
156 DPRINT("KeAddThreadTimeout(Thread %x, Interval %x)\n",Thread,Interval);
157
158 KeInitializeTimer(&(Thread->TimerBlock));
159 KeSetTimer(&(Thread->TimerBlock),*Interval,NULL);
160
161 DPRINT("Thread->TimerBlock.entry.Flink %x\n",
162 Thread->TimerBlock.entry.Flink);
163
164 return STATUS_SUCCESS;
165 }
166
167
168 NTSTATUS STDCALL NtDelayExecution(IN BOOLEAN Alertable,
169 IN TIME *Interval)
170 {
171 return(ZwDelayExecution(Alertable,Interval));
172 }
173
174 NTSTATUS STDCALL ZwDelayExecution(IN BOOLEAN Alertable,
175 IN TIME *Interval)
176 {
177 KeBugCheck(0);
178 return(STATUS_UNSUCCESSFUL);
179 }
180
181 NTSTATUS KeDelayExecutionThread(KPROCESSOR_MODE WaitMode,
182 BOOLEAN Alertable,
183 PLARGE_INTEGER Interval)
184 /*
185 * FUNCTION: Puts the current thread into an alertable or nonalertable
186 * wait state for a given internal
187 * ARGUMENTS:
188 * WaitMode = Processor mode in which the caller is waiting
189 * Altertable = Specifies if the wait is alertable
190 * Interval = Specifies the interval to wait
191 * RETURNS: Status
192 */
193 {
194 PKTHREAD CurrentThread = KeGetCurrentThread();
195 KeAddThreadTimeout(CurrentThread,Interval);
196 return(KeWaitForSingleObject(&(CurrentThread->TimerBlock),Executive,
197 KernelMode,Alertable,NULL));
198 }
199
200 VOID KeStallExecutionProcessor(ULONG MicroSeconds)
201 {
202 unsigned int i;
203 for (i=0; i<(loops_per_microsecond*MicroSeconds) ;i++)
204 {
205 __asm__("nop\n\t");
206 }
207 }
208
209 #if 0
210 static inline void ULLToLargeInteger(unsigned long long src,
211 PLARGE_INTEGER dest)
212 {
213 dest->LowPart = src & 0xffffffff;
214 dest->HighPart = (src>>32);
215 }
216
217 static inline void SLLToLargeInteger(signed long long src,
218 PLARGE_INTEGER dest)
219 {
220 if (src > 0)
221 {
222 dest->LowPart = src & 0xffffffff;
223 dest->HighPart = (src>>32);
224 }
225 else
226 {
227 src = -src;
228 dest->LowPart = src & 0xffffffff;
229 dest->HighPart = -(src>>32);
230 }
231 }
232
233 static inline signed long long LargeIntegerToSLL(PLARGE_INTEGER src)
234 {
235 signed long long r;
236
237 r = src->LowPart;
238 if (src->HighPart >= 0)
239 {
240 r = r | (((unsigned long long)src->HighPart)<<32);
241 }
242 else
243 {
244 r = r | (((unsigned long long)(-(src->HighPart)))<<32);
245 r = -r;
246 }
247 return(r);
248 }
249
250 #endif
251
252
253 LARGE_INTEGER KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq)
254 /*
255 * FUNCTION: Queries the finest grained running count avaiable in the system
256 * ARGUMENTS:
257 * PerformanceFreq (OUT) = The routine stores the number of
258 * performance counters tick per second here
259 * RETURNS: The performance counter value in HERTZ
260 * NOTE: Returns the system tick count or the time-stamp on the pentium
261 */
262 {
263 if (PerformanceFreq != NULL)
264 {
265 LARGE_INTEGER_QUAD_PART(*PerformanceFreq) = 0;
266 }
267
268 return *PerformanceFreq;
269 }
270
271 ULONG KeQueryTimeIncrement(VOID)
272 /*
273 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
274 * the system clock every time the clock interrupts
275 * RETURNS: The increment
276 */
277 {
278 return(CLOCK_INCREMENT);
279 }
280
281 VOID KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
282 /*
283 * FUNCTION: Gets the current system time
284 * ARGUMENTS:
285 * CurrentTime (OUT) = The routine stores the current time here
286 * NOTE: The time is the number of 100-nanosecond intervals since the
287 * 1st of January, 1601.
288 */
289 {
290 LARGE_INTEGER_QUAD_PART(*CurrentTime) = system_time;
291 }
292
293 NTSTATUS STDCALL NtGetTickCount(PULONG UpTime)
294 {
295 return(ZwGetTickCount(UpTime));
296 }
297
298 NTSTATUS STDCALL ZwGetTickCount(PULONG UpTime)
299 {
300 UNIMPLEMENTED;
301 }
302
303 BOOLEAN KeSetTimer(PKTIMER Timer, LARGE_INTEGER DueTime, PKDPC Dpc)
304 /*
305 * FUNCTION: Sets the absolute or relative interval at which a timer object
306 * is to be set to the signaled state and optionally supplies a
307 * CustomTimerDpc to be executed when the timer expires.
308 * ARGUMENTS:
309 * Timer = Points to a previously initialized timer object
310 * DueTimer = If positive then absolute time to expire at
311 * If negative then the relative time to expire at
312 * Dpc = If non-NULL then a dpc to be called when the timer expires
313 * RETURNS: True if the timer was already in the system timer queue
314 * False otherwise
315 */
316 {
317 return(KeSetTimerEx(Timer,DueTime,0,Dpc));
318 }
319
320 BOOLEAN KeSetTimerEx(PKTIMER Timer, LARGE_INTEGER DueTime, LONG Period,
321 PKDPC Dpc)
322 /*
323 * FUNCTION: Sets the absolute or relative interval at which a timer object
324 * is to be set to the signaled state and optionally supplies a
325 * CustomTimerDpc to be executed when the timer expires.
326 * ARGUMENTS:
327 * Timer = Points to a previously initialized timer object
328 * DueTimer = If positive then absolute time to expire at
329 * If negative then the relative time to expire at
330 * Dpc = If non-NULL then a dpc to be called when the timer expires
331 * RETURNS: True if the timer was already in the system timer queue
332 * False otherwise
333 */
334 {
335 KIRQL oldlvl;
336
337 DPRINT("KeSetTimerEx(Timer %x)\n",Timer);
338
339 KeAcquireSpinLock(&timer_list_lock,&oldlvl);
340
341 Timer->dpc=Dpc;
342 Timer->period=Period;
343 Timer->expire_time = (*(long long int *)&DueTime);
344 if (Timer->expire_time < 0)
345 {
346 Timer->expire_time = system_time + (-Timer->expire_time);
347 }
348 DPRINT("System:%ld:%ld Expire:%d:%d Period:%d\n",
349 (unsigned long) (system_time & 0xffffffff),
350 (unsigned long) ((system_time >> 32) & 0xffffffff),
351 (unsigned long) (Timer->expire_time & 0xffffffff),
352 (unsigned long) ((Timer->expire_time >> 32) & 0xffffffff),
353 Timer->period);
354 Timer->signaled = FALSE;
355 if (Timer->running)
356 {
357 KeReleaseSpinLock(&timer_list_lock,oldlvl);
358 return TRUE;
359 }
360 DPRINT("Inserting %x in list\n",&Timer->entry);
361 DPRINT("Timer->entry.Flink %x\n",Timer->entry.Flink);
362 Timer->running=TRUE;
363 InsertTailList(&timer_list_head,&Timer->entry);
364 KeReleaseSpinLock(&timer_list_lock,oldlvl);
365
366 return FALSE;
367 }
368
369 BOOLEAN KeCancelTimer(PKTIMER Timer)
370 /*
371 * FUNCTION: Removes a timer from the system timer list
372 * ARGUMENTS:
373 * Timer = timer to cancel
374 * RETURNS: True if the timer was running
375 * False otherwise
376 */
377 {
378 KIRQL oldlvl;
379
380 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
381
382 KeAcquireSpinLock(&timer_list_lock,&oldlvl);
383
384 if (!Timer->running)
385 {
386 KeReleaseSpinLock(&timer_list_lock,oldlvl);
387 return FALSE;
388 }
389 RemoveEntryList(&Timer->entry);
390 Timer->running = FALSE;
391 KeReleaseSpinLock(&timer_list_lock,oldlvl);
392
393 return TRUE;
394 }
395
396 BOOLEAN KeReadStateTimer(PKTIMER Timer)
397 {
398 return Timer->signaled;
399 }
400
401 VOID KeInitializeTimer(PKTIMER Timer)
402 /*
403 * FUNCTION: Initalizes a kernel timer object
404 * ARGUMENTS:
405 * Timer = caller supplied storage for the timer
406 * NOTE: This function initializes a notification timer
407 */
408 {
409 KeInitializeTimerEx(Timer,NotificationTimer);
410 }
411
412 VOID KeInitializeTimerEx(PKTIMER Timer, TIMER_TYPE Type)
413 /*
414 * FUNCTION: Initializes a kernel timer object
415 * ARGUMENTS:
416 * Timer = caller supplied storage for the timer
417 * Type = the type of timer (notification or synchronization)
418 * NOTE: When a notification type expires all waiting threads are released
419 * and the timer remains signalled until it is explicitly reset. When a
420 * syncrhonization timer expires its state is set to signalled until a
421 * single waiting thread is released and then the timer is reset.
422 */
423 {
424 Timer->running=FALSE;
425 Timer->type=Type;
426 Timer->signaled=FALSE;
427 }
428
429 VOID KeQueryTickCount(PLARGE_INTEGER TickCount)
430 /*
431 * FUNCTION: Returns the number of ticks since the system was booted
432 * ARGUMENTS:
433 * TickCount (OUT) = Points to storage for the number of ticks
434 */
435 {
436 LARGE_INTEGER_QUAD_PART(*TickCount) = KiTimerTicks;
437 }
438
439 static void HandleExpiredTimer(PKTIMER current)
440 {
441 DPRINT("HandleExpiredTime(current %x)\n",current);
442 if (current->dpc!=NULL)
443 {
444 DPRINT("current->dpc->DeferredRoutine %x\n",
445 current->dpc->DeferredRoutine);
446 current->dpc->DeferredRoutine(current->dpc,
447 current->dpc->DeferredContext,
448 current->dpc->SystemArgument1,
449 current->dpc->SystemArgument2);
450 DPRINT("Finished dpc routine\n");
451 }
452 current->signaled=TRUE;
453 if (current->period != 0)
454 {
455 DPRINT("System:%ld:%ld Expire:%d:%d Period:%d\n",
456 (unsigned long) (system_time & 0xffffffff),
457 (unsigned long) ((system_time >> 32) & 0xffffffff),
458 (unsigned long) (current->expire_time & 0xffffffff),
459 (unsigned long) ((current->expire_time >> 32) & 0xffffffff),
460 current->period);
461 current->expire_time += current->period * SYSTEM_TIME_UNITS_PER_MSEC;
462 }
463 else
464 {
465 RemoveEntryList(&current->entry);
466 current->running=FALSE;
467 }
468 }
469
470 void KeExpireTimers(void)
471 {
472 PLIST_ENTRY current_entry = timer_list_head.Flink;
473 PKTIMER current = CONTAINING_RECORD(current_entry,KTIMER,entry);
474 KIRQL oldlvl;
475
476 DPRINT("KeExpireTimers()\n");
477 DPRINT("&timer_list_head %x\n",&timer_list_head);
478 DPRINT("current_entry %x\n",current_entry);
479 DPRINT("current_entry->Flink %x\n",current_entry->Flink);
480 DPRINT("current_entry->Flink->Flink %x\n",current_entry->Flink->Flink);
481
482 KeAcquireSpinLock(&timer_list_lock,&oldlvl);
483
484 while (current_entry!=(&timer_list_head))
485 {
486 if (system_time >= current->expire_time)
487 {
488 HandleExpiredTimer(current);
489 }
490
491 current_entry = current_entry->Flink;
492 current = CONTAINING_RECORD(current_entry,KTIMER,entry);
493 }
494 KeReleaseSpinLock(&timer_list_lock,oldlvl);
495 DPRINT("Finished KeExpireTimers()\n");
496 }
497
498
499 BOOLEAN KiTimerInterrupt(VOID)
500 /*
501 * FUNCTION: Handles a timer interrupt
502 */
503 {
504 char str[36];
505 char* vidmem=(char *)physical_to_linear(0xb8000 + 160 - 36);
506 int i;
507 int x,y;
508 extern ULONG EiNrUsedBlocks;
509 extern unsigned int EiFreeNonPagedPool;
510 extern unsigned int EiUsedNonPagedPool;
511
512 /*
513 * Increment the number of timers ticks
514 */
515 KiTimerTicks++;
516 system_time = system_time + CLOCK_INCREMENT;
517
518 /*
519 * Display the tick count in the top left of the screen as a debugging
520 * aid
521 */
522 // sprintf(str,"%.8u %.8u",nr_used_blocks,ticks);
523 if ((EiFreeNonPagedPool + EiUsedNonPagedPool) == 0)
524 {
525 x = y = 0;
526 }
527 else
528 {
529 x = (EiFreeNonPagedPool * 100) /
530 (EiFreeNonPagedPool + EiUsedNonPagedPool);
531 y = (EiUsedNonPagedPool * 100) /
532 (EiFreeNonPagedPool + EiUsedNonPagedPool);
533 }
534 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,ticks);
535 memset(str, 0, sizeof(str));
536 sprintf(str,"%.8u %.8u",(unsigned int)EiNrUsedBlocks,
537 (unsigned int)EiFreeNonPagedPool);
538 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,EiUsedNonPagedPool);
539 // sprintf(str,"%.8u %.8u",PiNrThreads,KiTimerTicks);
540 for (i=0;i<17;i++)
541 {
542 *vidmem=str[i];
543 vidmem++;
544 *vidmem=0x7;
545 vidmem++;
546 }
547
548 return TRUE;
549 }
550
551
552 VOID KeInitializeTimerImpl(VOID)
553 /*
554 * FUNCTION: Initializes timer irq handling
555 * NOTE: This is only called once from main()
556 */
557 {
558 InitializeListHead(&timer_list_head);
559 KeInitializeSpinLock(&timer_list_lock);
560
561 /*
562 * Calculate the starting time for the system clock
563 */
564 }