Standardize comment headers. Patch by Trevor McCort
[reactos.git] / reactos / ntoskrnl / ex / timer.c
1 /* $Id: nttimer.c 12779 2005-01-04 04:45:00Z gdalsnes $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ex/timer.c
6 * PURPOSE: User-mode timers
7 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #include <internal/debug.h>
15
16
17 /* TYPES ********************************************************************/
18
19 typedef struct _NTTIMER
20 {
21 KTIMER Timer;
22 KDPC Dpc;
23 KAPC Apc;
24 BOOLEAN Running;
25 } NTTIMER, *PNTTIMER;
26
27
28 /* GLOBALS ******************************************************************/
29
30 POBJECT_TYPE ExTimerType = NULL;
31
32 static GENERIC_MAPPING ExpTimerMapping = {
33 STANDARD_RIGHTS_READ | TIMER_QUERY_STATE,
34 STANDARD_RIGHTS_WRITE | TIMER_MODIFY_STATE,
35 STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
36 TIMER_ALL_ACCESS};
37
38 static const INFORMATION_CLASS_INFO ExTimerInfoClass[] =
39 {
40 ICI_SQ_SAME( sizeof(TIMER_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* TimerBasicInformation */
41 };
42
43 /* FUNCTIONS *****************************************************************/
44
45 NTSTATUS STDCALL
46 ExpCreateTimer(PVOID ObjectBody,
47 PVOID Parent,
48 PWSTR RemainingPath,
49 POBJECT_ATTRIBUTES ObjectAttributes)
50 {
51 DPRINT("ExpCreateTimer(ObjectBody %x, Parent %x, RemainingPath %S)\n",
52 ObjectBody, Parent, RemainingPath);
53
54 if (RemainingPath != NULL && wcschr(RemainingPath+1, '\\') != NULL)
55 {
56 return(STATUS_UNSUCCESSFUL);
57 }
58
59 return(STATUS_SUCCESS);
60 }
61
62
63 VOID STDCALL
64 ExpDeleteTimer(PVOID ObjectBody)
65 {
66 KIRQL OldIrql;
67 PNTTIMER Timer = ObjectBody;
68
69 DPRINT("ExpDeleteTimer()\n");
70
71 OldIrql = KeRaiseIrqlToDpcLevel();
72
73 KeCancelTimer(&Timer->Timer);
74 KeRemoveQueueDpc(&Timer->Dpc);
75 KeRemoveQueueApc(&Timer->Apc);
76 Timer->Running = FALSE;
77
78 KeLowerIrql(OldIrql);
79 }
80
81
82 VOID STDCALL
83 ExpTimerDpcRoutine(PKDPC Dpc,
84 PVOID DeferredContext,
85 PVOID SystemArgument1,
86 PVOID SystemArgument2)
87 {
88 PNTTIMER Timer;
89
90 DPRINT("ExpTimerDpcRoutine()\n");
91
92 Timer = (PNTTIMER)DeferredContext;
93
94 if ( Timer->Running )
95 {
96 KeInsertQueueApc(&Timer->Apc,
97 SystemArgument1,
98 SystemArgument2,
99 IO_NO_INCREMENT);
100 }
101 }
102
103
104 VOID STDCALL
105 ExpTimerApcKernelRoutine(PKAPC Apc,
106 PKNORMAL_ROUTINE* NormalRoutine,
107 PVOID* NormalContext,
108 PVOID* SystemArgument1,
109 PVOID* SystemArguemnt2)
110 {
111 DPRINT("ExpTimerApcKernelRoutine()\n");
112
113 }
114
115
116 VOID INIT_FUNCTION
117 ExpInitializeTimerImplementation(VOID)
118 {
119 ASSERT(!ExTimerType)
120 ExTimerType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE));
121
122 RtlCreateUnicodeString(&ExTimerType->TypeName, L"Timer");
123
124 ExTimerType->Tag = TAG('T', 'I', 'M', 'T');
125 ExTimerType->PeakObjects = 0;
126 ExTimerType->PeakHandles = 0;
127 ExTimerType->TotalObjects = 0;
128 ExTimerType->TotalHandles = 0;
129 ExTimerType->PagedPoolCharge = 0;
130 ExTimerType->NonpagedPoolCharge = sizeof(NTTIMER);
131 ExTimerType->Mapping = &ExpTimerMapping;
132 ExTimerType->Dump = NULL;
133 ExTimerType->Open = NULL;
134 ExTimerType->Close = NULL;
135 ExTimerType->Delete = ExpDeleteTimer;
136 ExTimerType->Parse = NULL;
137 ExTimerType->Security = NULL;
138 ExTimerType->QueryName = NULL;
139 ExTimerType->OkayToClose = NULL;
140 ExTimerType->Create = ExpCreateTimer;
141 ExTimerType->DuplicationNotify = NULL;
142
143 ObpCreateTypeObject(ExTimerType);
144 }
145
146
147 NTSTATUS STDCALL
148 NtCancelTimer(IN HANDLE TimerHandle,
149 OUT PBOOLEAN CurrentState OPTIONAL)
150 {
151 PNTTIMER Timer;
152 KPROCESSOR_MODE PreviousMode;
153 NTSTATUS Status = STATUS_SUCCESS;
154
155 PreviousMode = ExGetPreviousMode();
156
157 DPRINT("NtCancelTimer(0x%x, 0x%x)\n", TimerHandle, CurrentState);
158
159 if(CurrentState != NULL && PreviousMode != KernelMode)
160 {
161 _SEH_TRY
162 {
163 ProbeForWrite(CurrentState,
164 sizeof(BOOLEAN),
165 sizeof(BOOLEAN));
166 }
167 _SEH_HANDLE
168 {
169 Status = _SEH_GetExceptionCode();
170 }
171 _SEH_END;
172
173 if(!NT_SUCCESS(Status))
174 {
175 return Status;
176 }
177 }
178
179 Status = ObReferenceObjectByHandle(TimerHandle,
180 TIMER_ALL_ACCESS,
181 ExTimerType,
182 PreviousMode,
183 (PVOID*)&Timer,
184 NULL);
185 if(NT_SUCCESS(Status))
186 {
187 BOOLEAN State;
188 KIRQL OldIrql = KeRaiseIrqlToDpcLevel();
189
190 State = KeCancelTimer(&Timer->Timer);
191 KeRemoveQueueDpc(&Timer->Dpc);
192 KeRemoveQueueApc(&Timer->Apc);
193 Timer->Running = FALSE;
194
195 KeLowerIrql(OldIrql);
196 ObDereferenceObject(Timer);
197
198 if(CurrentState != NULL)
199 {
200 _SEH_TRY
201 {
202 *CurrentState = State;
203 }
204 _SEH_HANDLE
205 {
206 Status = _SEH_GetExceptionCode();
207 }
208 _SEH_END;
209 }
210 }
211
212 return Status;
213 }
214
215
216 NTSTATUS STDCALL
217 NtCreateTimer(OUT PHANDLE TimerHandle,
218 IN ACCESS_MASK DesiredAccess,
219 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
220 IN TIMER_TYPE TimerType)
221 {
222 PNTTIMER Timer;
223 HANDLE hTimer;
224 KPROCESSOR_MODE PreviousMode;
225 NTSTATUS Status = STATUS_SUCCESS;
226
227 DPRINT("NtCreateTimer()\n");
228
229 PreviousMode = ExGetPreviousMode();
230
231 if(PreviousMode != KernelMode)
232 {
233 _SEH_TRY
234 {
235 ProbeForWrite(TimerHandle,
236 sizeof(HANDLE),
237 sizeof(ULONG));
238 }
239 _SEH_HANDLE
240 {
241 Status = _SEH_GetExceptionCode();
242 }
243 _SEH_END;
244
245 if(!NT_SUCCESS(Status))
246 {
247 return Status;
248 }
249 }
250
251 Status = ObCreateObject(PreviousMode,
252 ExTimerType,
253 ObjectAttributes,
254 PreviousMode,
255 NULL,
256 sizeof(NTTIMER),
257 0,
258 0,
259 (PVOID*)&Timer);
260 if(NT_SUCCESS(Status))
261 {
262 KeInitializeTimerEx(&Timer->Timer,
263 TimerType);
264
265 KeInitializeDpc(&Timer->Dpc,
266 &ExpTimerDpcRoutine,
267 Timer);
268
269 Timer->Running = FALSE;
270
271 Status = ObInsertObject ((PVOID)Timer,
272 NULL,
273 DesiredAccess,
274 0,
275 NULL,
276 &hTimer);
277 ObDereferenceObject(Timer);
278
279 if(NT_SUCCESS(Status))
280 {
281 _SEH_TRY
282 {
283 *TimerHandle = hTimer;
284 }
285 _SEH_HANDLE
286 {
287 Status = _SEH_GetExceptionCode();
288 }
289 _SEH_END;
290 }
291 }
292
293 return Status;
294 }
295
296
297 NTSTATUS STDCALL
298 NtOpenTimer(OUT PHANDLE TimerHandle,
299 IN ACCESS_MASK DesiredAccess,
300 IN POBJECT_ATTRIBUTES ObjectAttributes)
301 {
302 HANDLE hTimer;
303 KPROCESSOR_MODE PreviousMode;
304 NTSTATUS Status = STATUS_SUCCESS;
305
306 DPRINT("NtOpenTimer()\n");
307
308 PreviousMode = ExGetPreviousMode();
309
310 if(PreviousMode != KernelMode)
311 {
312 _SEH_TRY
313 {
314 ProbeForWrite(TimerHandle,
315 sizeof(HANDLE),
316 sizeof(ULONG));
317 }
318 _SEH_HANDLE
319 {
320 Status = _SEH_GetExceptionCode();
321 }
322 _SEH_END;
323
324 if(!NT_SUCCESS(Status))
325 {
326 return Status;
327 }
328 }
329
330 Status = ObOpenObjectByName(ObjectAttributes,
331 ExTimerType,
332 NULL,
333 PreviousMode,
334 DesiredAccess,
335 NULL,
336 &hTimer);
337 if(NT_SUCCESS(Status))
338 {
339 _SEH_TRY
340 {
341 *TimerHandle = hTimer;
342 }
343 _SEH_HANDLE
344 {
345 Status = _SEH_GetExceptionCode();
346 }
347 _SEH_END;
348 }
349
350 return Status;
351 }
352
353
354 NTSTATUS STDCALL
355 NtQueryTimer(IN HANDLE TimerHandle,
356 IN TIMER_INFORMATION_CLASS TimerInformationClass,
357 OUT PVOID TimerInformation,
358 IN ULONG TimerInformationLength,
359 OUT PULONG ReturnLength OPTIONAL)
360 {
361 PNTTIMER Timer;
362 KPROCESSOR_MODE PreviousMode;
363 NTSTATUS Status = STATUS_SUCCESS;
364
365 PreviousMode = ExGetPreviousMode();
366
367 DefaultQueryInfoBufferCheck(TimerInformationClass,
368 ExTimerInfoClass,
369 TimerInformation,
370 TimerInformationLength,
371 ReturnLength,
372 PreviousMode,
373 &Status);
374 if(!NT_SUCCESS(Status))
375 {
376 DPRINT1("NtQueryTimer() failed, Status: 0x%x\n", Status);
377 return Status;
378 }
379
380 Status = ObReferenceObjectByHandle(TimerHandle,
381 TIMER_QUERY_STATE,
382 ExTimerType,
383 PreviousMode,
384 (PVOID*)&Timer,
385 NULL);
386 if(NT_SUCCESS(Status))
387 {
388 switch(TimerInformationClass)
389 {
390 case TimerBasicInformation:
391 {
392 PTIMER_BASIC_INFORMATION BasicInfo = (PTIMER_BASIC_INFORMATION)TimerInformation;
393
394 _SEH_TRY
395 {
396 /* FIXME - interrupt correction */
397 BasicInfo->TimeRemaining.QuadPart = Timer->Timer.DueTime.QuadPart;
398 BasicInfo->SignalState = (BOOLEAN)Timer->Timer.Header.SignalState;
399
400 if(ReturnLength != NULL)
401 {
402 *ReturnLength = sizeof(TIMER_BASIC_INFORMATION);
403 }
404 }
405 _SEH_HANDLE
406 {
407 Status = _SEH_GetExceptionCode();
408 }
409 _SEH_END;
410 break;
411 }
412
413 default:
414 Status = STATUS_NOT_IMPLEMENTED;
415 break;
416 }
417
418 ObDereferenceObject(Timer);
419 }
420
421 return Status;
422 }
423
424
425 NTSTATUS STDCALL
426 NtSetTimer(IN HANDLE TimerHandle,
427 IN PLARGE_INTEGER DueTime,
428 IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL,
429 IN PVOID TimerContext OPTIONAL,
430 IN BOOLEAN ResumeTimer,
431 IN LONG Period OPTIONAL,
432 OUT PBOOLEAN PreviousState OPTIONAL)
433 {
434 PNTTIMER Timer;
435 BOOLEAN Result;
436 BOOLEAN State;
437 LARGE_INTEGER TimerDueTime;
438 KPROCESSOR_MODE PreviousMode;
439 NTSTATUS Status = STATUS_SUCCESS;
440
441 DPRINT("NtSetTimer()\n");
442
443 PreviousMode = ExGetPreviousMode();
444
445 if(PreviousMode != KernelMode)
446 {
447 _SEH_TRY
448 {
449 ProbeForRead(DueTime,
450 sizeof(LARGE_INTEGER),
451 sizeof(ULONG));
452 TimerDueTime = *DueTime;
453
454 if(PreviousState != NULL)
455 {
456 ProbeForWrite(PreviousState,
457 sizeof(BOOLEAN),
458 sizeof(BOOLEAN));
459 }
460 }
461 _SEH_HANDLE
462 {
463 Status = _SEH_GetExceptionCode();
464 }
465 _SEH_END;
466
467 if(!NT_SUCCESS(Status))
468 {
469 return Status;
470 }
471 }
472
473 Status = ObReferenceObjectByHandle(TimerHandle,
474 TIMER_ALL_ACCESS,
475 ExTimerType,
476 PreviousMode,
477 (PVOID*)&Timer,
478 NULL);
479 if (!NT_SUCCESS(Status))
480 {
481 return Status;
482 }
483
484 State = KeReadStateTimer(&Timer->Timer);
485
486 if (Timer->Running == TRUE)
487 {
488 /* cancel running timer */
489 const KIRQL OldIrql = KeRaiseIrqlToDpcLevel();
490 KeCancelTimer(&Timer->Timer);
491 KeRemoveQueueDpc(&Timer->Dpc);
492 KeRemoveQueueApc(&Timer->Apc);
493 Timer->Running = FALSE;
494 KeLowerIrql(OldIrql);
495 }
496
497 if (TimerApcRoutine)
498 {
499 KeInitializeApc(&Timer->Apc,
500 KeGetCurrentThread(),
501 OriginalApcEnvironment,
502 &ExpTimerApcKernelRoutine,
503 (PKRUNDOWN_ROUTINE)NULL,
504 (PKNORMAL_ROUTINE)TimerApcRoutine,
505 PreviousMode,
506 TimerContext);
507 }
508
509 Result = KeSetTimerEx(&Timer->Timer,
510 TimerDueTime,
511 Period,
512 TimerApcRoutine ? &Timer->Dpc : 0 );
513 if (Result)
514 {
515 ObDereferenceObject(Timer);
516 DPRINT1( "KeSetTimer says the timer was already running, this shouldn't be\n" );
517 return STATUS_UNSUCCESSFUL;
518 }
519
520 Timer->Running = TRUE;
521
522 ObDereferenceObject(Timer);
523
524 if (PreviousState != NULL)
525 {
526 _SEH_TRY
527 {
528 *PreviousState = State;
529 }
530 _SEH_HANDLE
531 {
532 Status = _SEH_GetExceptionCode();
533 }
534 _SEH_END;
535 }
536
537 return Status;
538 }
539
540 /* EOF */