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