Initial revision
[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
20 #include <internal/stddef.h>
21 #include <internal/kernel.h>
22 #include <internal/mm.h>
23 #include <internal/hal/page.h>
24 #include <internal/string.h>
25 #include <ddk/ntddk.h>
26
27 #include <internal/debug.h>
28
29 /* TYPES *****************************************************************/
30
31 #define TIMER_IRQ 0
32
33
34 /* GLOBALS ****************************************************************/
35
36 /*
37 * Current time
38 */
39 static unsigned long long system_time = 0;
40
41 /*
42 * Number of timer interrupts since initialisation
43 */
44 static volatile unsigned long long ticks=0;
45
46 /*
47 * The increment in the system clock every timer tick (in system time units)
48 *
49 * = (1/18.2)*10^9
50 *
51 */
52 #define CLOCK_INCREMENT (54945055)
53
54 /*
55 * PURPOSE: List of timers
56 */
57 static LIST_ENTRY timer_list_head = {NULL,NULL};
58 static KSPIN_LOCK timer_list_lock;
59
60
61 #define MICROSECONDS_TO_CALIBRATE (1000000)
62 #define MICROSECONDS_PER_TICK (54945)
63 #define MICROSECONDS_IN_A_SECOND (10000000)
64 #define TICKS_PER_SECOND_APPROX (18)
65
66 static unsigned int loops_per_microsecond = 17;
67
68 /* FUNCTIONS **************************************************************/
69
70 void KeCalibrateTimerLoop()
71 {
72 unsigned int start_tick;
73 unsigned int end_tick;
74 unsigned int nr_ticks;
75 unsigned int i;
76
77 return;
78
79 for (i=0;i<5;i++)
80 {
81
82 start_tick = ticks;
83 while (start_tick==ticks);
84 KeStallExecutionProcessor(MICROSECONDS_TO_CALIBRATE);
85 end_tick = ticks;
86 while (end_tick==ticks);
87
88 nr_ticks = end_tick - start_tick;
89 loops_per_microsecond = (loops_per_microsecond * MICROSECONDS_TO_CALIBRATE)
90 / (nr_ticks*MICROSECONDS_PER_TICK);
91
92 printk("nr_ticks %d\n",nr_ticks);
93 printk("loops_per_microsecond %d\n",loops_per_microsecond);
94 printk("Processor speed (approx) %d\n",
95 (6*loops_per_microsecond)/1000);
96
97 if (nr_ticks == (TICKS_PER_SECOND_APPROX * MICROSECONDS_TO_CALIBRATE)
98 / MICROSECONDS_IN_A_SECOND)
99 {
100 printk("Testing loop\n");
101 KeStallExecutionProcessor(10000);
102 printk("Finished loop\n");
103 return;
104 }
105 }
106 }
107
108 NTSTATUS KeDelayExecutionThread(KPROCESSOR_MODE WaitMode,
109 BOOLEAN Alertable,
110 PLARGE_INTEGER Interval)
111 {
112 UNIMPLEMENTED;
113 }
114
115 VOID KeStallExecutionProcessor(ULONG MicroSeconds)
116 {
117 unsigned int i;
118 for (i=0; i<(loops_per_microsecond*MicroSeconds) ;i++)
119 {
120 __asm__("nop\n\t");
121 }
122 }
123
124 static inline void ULLToLargeInteger(unsigned long long src,
125 PLARGE_INTEGER dest)
126 {
127 dest->LowPart = src & 0xffffffff;
128 dest->HighPart = (src>>32);
129 }
130
131 static inline void SLLToLargeInteger(signed long long src,
132 PLARGE_INTEGER dest)
133 {
134 if (src > 0)
135 {
136 dest->LowPart = src & 0xffffffff;
137 dest->HighPart = (src>>32);
138 }
139 else
140 {
141 src = -src;
142 dest->LowPart = src & 0xffffffff;
143 dest->HighPart = -(src>>32);
144 }
145 }
146
147 static inline signed long long LargeIntegerToSLL(PLARGE_INTEGER src)
148 {
149 signed long long r;
150
151 r = src->LowPart;
152 if (src->HighPart >= 0)
153 {
154 r = r | (((unsigned long long)src->HighPart)<<32);
155 }
156 else
157 {
158 r = r | (((unsigned long long)(-(src->HighPart)))<<32);
159 r = -r;
160 }
161 return(r);
162 }
163
164
165 LARGE_INTEGER KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq)
166 /*
167 * FUNCTION: Queries the finest grained running count avaiable in the system
168 * ARGUMENTS:
169 * PerformanceFreq (OUT) = The routine stores the number of
170 * performance counters tick per second here
171 * RETURNS: The performance counter value in HERTZ
172 * NOTE: Returns the system tick count or the time-stamp on the pentium
173 */
174 {
175 PerformanceFreq->HighPart=0;
176 PerformanceFreq->LowPart=0;
177 }
178
179 ULONG KeQueryTimeIncrement(VOID)
180 /*
181 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
182 * the system clock every time the clock interrupts
183 * RETURNS: The increment
184 */
185 {
186 return(CLOCK_INCREMENT);
187 }
188
189 VOID KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
190 /*
191 * FUNCTION: Gets the current system time
192 * ARGUMENTS:
193 * CurrentTime (OUT) = The routine stores the current time here
194 * NOTE: The time is the number of 100-nanosecond intervals since the
195 * 1st of January, 1601.
196 */
197 {
198 ULLToLargeInteger(system_time,CurrentTime);
199 }
200
201
202 BOOLEAN KeSetTimer(PKTIMER Timer, LARGE_INTEGER DueTime, PKDPC Dpc)
203 /*
204 * FUNCTION: Sets the absolute or relative interval at which a timer object
205 * is to be set to the signaled state and optionally supplies a
206 * CustomTimerDpc to be executed when the timer expires.
207 * ARGUMENTS:
208 * Timer = Points to a previously initialized timer object
209 * DueTimer = If positive then absolute time to expire at
210 * If negative then the relative time to expire at
211 * Dpc = If non-NULL then a dpc to be called when the timer expires
212 * RETURNS: True if the timer was already in the system timer queue
213 * False otherwise
214 */
215 {
216 return(KeSetTimerEx(Timer,DueTime,0,Dpc));
217 }
218
219 BOOLEAN KeSetTimerEx(PKTIMER Timer, LARGE_INTEGER DueTime, 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 KeAcquireSpinLock(&timer_list_lock,&oldlvl);
237
238 Timer->dpc=Dpc;
239 Timer->period=Period;
240 Timer->expire_time = LargeIntegerToSLL(&DueTime);
241 if (Timer->expire_time < 0)
242 {
243 Timer->expire_time = system_time - Timer->expire_time;
244 Timer->signaled = FALSE;
245 }
246 if (Timer->running)
247 {
248 KeReleaseSpinLock(&timer_list_lock,oldlvl);
249 return(TRUE);
250 }
251 InsertTailList(&timer_list_head,&Timer->entry);
252 KeReleaseSpinLock(&timer_list_lock,oldlvl);
253 return(FALSE);
254 }
255
256 BOOLEAN KeCancelTimer(PKTIMER Timer)
257 /*
258 * FUNCTION: Removes a timer from the system timer list
259 * ARGUMENTS:
260 * Timer = timer to cancel
261 * RETURNS: True if the timer was running
262 * False otherwise
263 */
264 {
265 KIRQL oldlvl;
266
267 KeAcquireSpinLock(&timer_list_lock,&oldlvl);
268
269 if (!Timer->running)
270 {
271 return(FALSE);
272 }
273 RemoveEntryFromList(&timer_list_head,&Timer->entry);
274 KeReleaseSpinLock(&timer_list_lock,oldlvl);
275 return(TRUE);
276 }
277
278 BOOLEAN KeReadStateTimer(PKTIMER Timer)
279 {
280 return(Timer->signaled);
281 }
282
283 VOID KeInitializeTimer(PKTIMER Timer)
284 /*
285 * FUNCTION: Initalizes a kernel timer object
286 * ARGUMENTS:
287 * Timer = caller supplied storage for the timer
288 * NOTE: This function initializes a notification timer
289 */
290 {
291 KeInitializeTimerEx(Timer,NotificationTimer);
292 }
293
294 VOID KeInitializeTimerEx(PKTIMER Timer, TIMER_TYPE Type)
295 /*
296 * FUNCTION: Initializes a kernel timer object
297 * ARGUMENTS:
298 * Timer = caller supplied storage for the timer
299 * Type = the type of timer (notification or synchronization)
300 * NOTE: When a notification type expires all waiting threads are released
301 * and the timer remains signalled until it is explicitly reset. When a
302 * syncrhonization timer expires its state is set to signalled until a
303 * single waiting thread is released and then the timer is reset.
304 */
305 {
306 Timer->running=FALSE;
307 Timer->type=Type;
308 Timer->signaled=FALSE;
309 }
310
311 VOID KeQueryTickCount(PLARGE_INTEGER TickCount)
312 /*
313 * FUNCTION: Returns the number of ticks since the system was booted
314 * ARGUMENTS:
315 * TickCount (OUT) = Points to storage for the number of ticks
316 */
317 {
318 ULLToLargeInteger(ticks,TickCount);
319 }
320
321 static void HandleExpiredTimer(PKTIMER current)
322 {
323 if (current->dpc!=NULL)
324 {
325 current->dpc->DeferredRoutine(current->dpc,
326 current->dpc->DeferredContext,
327 current->dpc->SystemArgument1,
328 current->dpc->SystemArgument2);
329 }
330 current->signaled=TRUE;
331 if (current->period !=0)
332 {
333 current->expire_time = current->expire_time + current->period;
334 }
335 else
336 {
337 RemoveEntryFromList(&timer_list_head,&current->entry);
338 current->running=FALSE;
339 }
340 }
341
342 void KeExpireTimers(void)
343 {
344 PLIST_ENTRY current_entry = timer_list_head.Flink;
345 PKTIMER current = CONTAINING_RECORD(current_entry,KTIMER,entry);
346 KIRQL oldlvl;
347
348 KeAcquireSpinLock(&timer_list_lock,&oldlvl);
349
350 while (current_entry!=NULL)
351 {
352 if (system_time == current->expire_time)
353 {
354 HandleExpiredTimer(current);
355 }
356
357 current_entry = current_entry->Flink;
358 current = CONTAINING_RECORD(current_entry,KTIMER,entry);
359 }
360 KeReleaseSpinLock(&timer_list_lock,oldlvl);
361 }
362
363 VOID KeTimerInterrupt(VOID)
364 /*
365 * FUNCTION: Handles a timer interrupt
366 */
367 {
368 char str[16];
369 char* vidmem=(char *)physical_to_linear(0xb8000 + 160 - 16);
370 int i;
371
372 /*
373 * Increment the number of timers ticks
374 */
375 ticks++;
376 system_time = system_time + CLOCK_INCREMENT;
377
378 /*
379 * Display the tick count in the top left of the screen as a debugging
380 * aid
381 */
382 sprintf(str,"%.8u",ticks);
383 for (i=0;i<8;i++)
384 {
385 *vidmem=str[i];
386 vidmem++;
387 *vidmem=0x7;
388 vidmem++;
389 }
390
391 return(TRUE);
392 }
393
394
395 void InitializeTimer(void)
396 /*
397 * FUNCTION: Initializes timer irq handling
398 * NOTE: This is only called once from main()
399 */
400 {
401 InitializeListHead(&timer_list_head);
402 KeInitializeSpinLock(&timer_list_lock);
403
404 /*
405 * Calculate the starting time for the system clock
406 */
407 }