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