branch update for HEAD-2003050101
[reactos.git] / ntoskrnl / nt / nttimer.c
1 /* $Id$
2  *
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS kernel
5  * FILE:            ntoskrnl/nt/nttimer.c
6  * PURPOSE:         User-mode timers
7  * PROGRAMMER:      David Welch (welch@mcmail.com)
8  * UPDATE HISTORY:
9  *                  Created 22/05/98
10  */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <ntos/synch.h>
16 #include <internal/ke.h>
17 #include <limits.h>
18 #include <internal/pool.h>
19 #include <internal/safe.h>
20
21 #include <internal/debug.h>
22
23
24 /* TYPES ********************************************************************/
25
26 typedef struct _NTTIMER
27 {
28    KTIMER Timer;
29    KDPC Dpc;
30    KAPC Apc;
31    BOOLEAN Running;
32 } NTTIMER, *PNTTIMER;
33
34
35 /* GLOBALS ******************************************************************/
36
37 POBJECT_TYPE ExTimerType = NULL;
38
39 static GENERIC_MAPPING ExpTimerMapping = {
40         STANDARD_RIGHTS_READ | TIMER_QUERY_STATE,
41         STANDARD_RIGHTS_WRITE | TIMER_MODIFY_STATE,
42         STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
43         TIMER_ALL_ACCESS};
44
45
46 /* FUNCTIONS *****************************************************************/
47
48 NTSTATUS STDCALL
49 NtpCreateTimer(PVOID ObjectBody,
50                PVOID Parent,
51                PWSTR RemainingPath,
52                POBJECT_ATTRIBUTES ObjectAttributes)
53 {
54   DPRINT("NtpCreateTimer(ObjectBody %x, Parent %x, RemainingPath %S)\n",
55          ObjectBody, Parent, RemainingPath);
56
57   if (RemainingPath != NULL && wcschr(RemainingPath+1, '\\') != NULL)
58     {
59       return(STATUS_UNSUCCESSFUL);
60     }
61
62   return(STATUS_SUCCESS);
63 }
64
65
66 VOID STDCALL
67 NtpDeleteTimer(PVOID ObjectBody)
68 {
69    KIRQL OldIrql;
70    PNTTIMER Timer = ObjectBody;
71
72    DPRINT("NtpDeleteTimer()\n");
73
74    OldIrql = KeRaiseIrqlToDpcLevel();
75
76    KeCancelTimer(&Timer->Timer);
77    KeRemoveQueueDpc(&Timer->Dpc);
78    KeRemoveQueueApc(&Timer->Apc);
79    Timer->Running = FALSE;
80
81    KeLowerIrql(OldIrql);
82 }
83
84
85 VOID
86 NtpTimerDpcRoutine(PKDPC Dpc,
87                    PVOID DeferredContext,
88                    PVOID SystemArgument1,
89                    PVOID SystemArgument2)
90 {
91    PNTTIMER Timer;
92
93    DPRINT("NtpTimerDpcRoutine()\n");
94
95    Timer = (PNTTIMER)DeferredContext;
96
97    if ( Timer->Running )
98      {
99         KeInsertQueueApc(&Timer->Apc,
100                          SystemArgument1,
101                          SystemArgument2,
102                          KernelMode);
103      }
104 }
105
106
107 VOID
108 NtpTimerApcKernelRoutine(PKAPC Apc,
109                          PKNORMAL_ROUTINE* NormalRoutine,
110                          PVOID* NormalContext,
111                          PVOID* SystemArgument1,
112                          PVOID* SystemArguemnt2)
113 {
114    DPRINT("NtpTimerApcKernelRoutine()\n");
115
116 }
117
118
119 VOID NtInitializeTimerImplementation(VOID)
120 {
121    ExTimerType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE));
122
123    RtlCreateUnicodeString(&ExTimerType->TypeName, L"Timer");
124
125    ExTimerType->Tag = TAG('T', 'I', 'M', 'T');
126    ExTimerType->MaxObjects = ULONG_MAX;
127    ExTimerType->MaxHandles = ULONG_MAX;
128    ExTimerType->TotalObjects = 0;
129    ExTimerType->TotalHandles = 0;
130    ExTimerType->PagedPoolCharge = 0;
131    ExTimerType->NonpagedPoolCharge = sizeof(NTTIMER);
132    ExTimerType->Mapping = &ExpTimerMapping;
133    ExTimerType->Dump = NULL;
134    ExTimerType->Open = NULL;
135    ExTimerType->Close = NULL;
136    ExTimerType->Delete = NtpDeleteTimer;
137    ExTimerType->Parse = NULL;
138    ExTimerType->Security = NULL;
139    ExTimerType->QueryName = NULL;
140    ExTimerType->OkayToClose = NULL;
141    ExTimerType->Create = NtpCreateTimer;
142    ExTimerType->DuplicationNotify = NULL;
143 }
144
145
146 NTSTATUS STDCALL
147 NtCancelTimer(IN HANDLE TimerHandle,
148               OUT PBOOLEAN CurrentState OPTIONAL)
149 {
150    PNTTIMER Timer;
151    NTSTATUS Status;
152    BOOLEAN State;
153    KIRQL OldIrql;
154
155    DPRINT("NtCancelTimer()\n");
156    Status = ObReferenceObjectByHandle(TimerHandle,
157                                       TIMER_ALL_ACCESS,
158                                       ExTimerType,
159                                       UserMode,
160                                       (PVOID*)&Timer,
161                                       NULL);
162    if (!NT_SUCCESS(Status))
163      return Status;
164
165    OldIrql = KeRaiseIrqlToDpcLevel();
166
167    State = KeCancelTimer(&Timer->Timer);
168    KeRemoveQueueDpc(&Timer->Dpc);
169    KeRemoveQueueApc(&Timer->Apc);
170    Timer->Running = FALSE;
171
172    KeLowerIrql(OldIrql);
173    ObDereferenceObject(Timer);
174
175    if (CurrentState != NULL)
176      {
177         *CurrentState = State;
178      }
179
180    return STATUS_SUCCESS;
181 }
182
183
184 NTSTATUS STDCALL
185 NtCreateTimer(OUT PHANDLE TimerHandle,
186               IN ACCESS_MASK DesiredAccess,
187               IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
188               IN TIMER_TYPE TimerType)
189 {
190    PNTTIMER Timer;
191    NTSTATUS Status;
192
193    DPRINT("NtCreateTimer()\n");
194    Status = ObCreateObject(TimerHandle,
195                            DesiredAccess,
196                            ObjectAttributes,
197                            ExTimerType,
198                            (PVOID*)&Timer);
199    if (!NT_SUCCESS(Status))
200      return Status;
201
202    KeInitializeTimerEx(&Timer->Timer,
203                        TimerType);
204
205    KeInitializeDpc (&Timer->Dpc,
206                     (PKDEFERRED_ROUTINE)NtpTimerDpcRoutine,
207                     (PVOID)Timer);
208
209    Timer->Running = FALSE;
210
211    ObDereferenceObject(Timer);
212
213    return(STATUS_SUCCESS);
214 }
215
216
217 NTSTATUS STDCALL
218 NtOpenTimer(OUT PHANDLE TimerHandle,
219             IN ACCESS_MASK DesiredAccess,
220             IN POBJECT_ATTRIBUTES ObjectAttributes)
221 {
222    NTSTATUS Status;
223
224    Status = ObOpenObjectByName(ObjectAttributes,
225                                ExTimerType,
226                                NULL,
227                                UserMode,
228                                DesiredAccess,
229                                NULL,
230                                TimerHandle);
231    return Status;
232 }
233
234
235 NTSTATUS STDCALL
236 NtQueryTimer(IN HANDLE TimerHandle,
237              IN CINT TimerInformationClass,
238              OUT PVOID UnsafeTimerInformation,
239              IN ULONG Length,
240              OUT PULONG UnsafeResultLength)
241 {
242   PNTTIMER Timer;
243   TIMER_BASIC_INFORMATION TimerInformation;
244   ULONG ResultLength;
245   NTSTATUS Status;
246
247   Status = ObReferenceObjectByHandle(TimerHandle,
248                                      TIMER_QUERY_STATE,
249                                      ExTimerType,
250                                      KeGetPreviousMode(),
251                                      (PVOID*)&Timer,
252                                      NULL);
253   if (!NT_SUCCESS(Status))
254     {
255       return(Status);
256     }
257
258   if (TimerInformationClass != TimerBasicInformation)
259     {
260       ObDereferenceObject(Timer);
261       return(STATUS_INVALID_INFO_CLASS);
262     }
263   if (Length < sizeof(TIMER_BASIC_INFORMATION))
264     {
265       ObDereferenceObject(Timer);
266       return(STATUS_INFO_LENGTH_MISMATCH);
267     }
268
269   memcpy(&TimerInformation.TimeRemaining, &Timer->Timer.DueTime,
270          sizeof(LARGE_INTEGER));
271   TimerInformation.SignalState = Timer->Timer.Header.SignalState;
272   ResultLength = sizeof(TIMER_BASIC_INFORMATION);
273
274   Status = MmCopyToCaller(UnsafeTimerInformation, &TimerInformation,
275                           sizeof(TIMER_BASIC_INFORMATION));
276   if (!NT_SUCCESS(Status))
277     {
278       ObDereferenceObject(Timer);
279       return(Status);
280     }
281
282   if (UnsafeResultLength != NULL)
283     {
284       Status = MmCopyToCaller(UnsafeResultLength, &ResultLength,
285                               sizeof(ULONG));
286       if (!NT_SUCCESS(Status))
287         {
288           ObDereferenceObject(Timer);
289           return(Status);
290         }
291     }
292   ObDereferenceObject(Timer);
293   return(STATUS_SUCCESS);
294 }
295
296
297 NTSTATUS STDCALL
298 NtSetTimer(IN HANDLE TimerHandle,
299            IN PLARGE_INTEGER DueTime,
300            IN PTIMERAPCROUTINE TimerApcRoutine,
301            IN PVOID TimerContext,
302            IN BOOL WakeTimer,
303            IN ULONG Period OPTIONAL,
304            OUT PBOOLEAN PreviousState OPTIONAL)
305 {
306    PNTTIMER Timer;
307    NTSTATUS Status;
308    BOOLEAN Result;
309    KIRQL OldIrql;
310    BOOLEAN State;
311
312    DPRINT("NtSetTimer()\n");
313
314    Status = ObReferenceObjectByHandle(TimerHandle,
315                                       TIMER_ALL_ACCESS,
316                                       ExTimerType,
317                                       KeGetPreviousMode(),
318                                       (PVOID*)&Timer,
319                                       NULL);
320    if (!NT_SUCCESS(Status))
321      return Status;
322
323    State = KeReadStateTimer(&Timer->Timer);
324
325    if (Timer->Running == TRUE)
326      {
327         /* cancel running timer */
328         OldIrql = KeRaiseIrqlToDpcLevel();
329         KeCancelTimer(&Timer->Timer);
330         KeRemoveQueueDpc(&Timer->Dpc);
331         KeRemoveQueueApc(&Timer->Apc);
332         Timer->Running = FALSE;
333         KeLowerIrql(OldIrql);
334      }
335    if( TimerApcRoutine )
336       KeInitializeApc(&Timer->Apc,
337                       KeGetCurrentThread(),
338                       0,
339                       (PKKERNEL_ROUTINE)NtpTimerApcKernelRoutine,
340                       (PKRUNDOWN_ROUTINE)NULL,
341                       (PKNORMAL_ROUTINE)TimerApcRoutine,
342                       KeGetPreviousMode(),
343                       TimerContext);
344
345    Result = KeSetTimerEx (&Timer->Timer,
346                           *DueTime,
347                           Period,
348                           TimerApcRoutine ? &Timer->Dpc : 0 );
349    if (Result == TRUE)
350      {
351         ObDereferenceObject(Timer);
352         DPRINT1( "KeSetTimer says the timer was already running, this shouldn't be\n" );
353         return STATUS_UNSUCCESSFUL;
354      }
355
356    Timer->Running = TRUE;
357
358    ObDereferenceObject(Timer);
359
360    if (PreviousState != NULL)
361      {
362         *PreviousState = State;
363      }
364
365    return STATUS_SUCCESS;
366 }
367
368 /* EOF */