:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[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
67 NtpTimerDpcRoutine(PKDPC Dpc,
68                    PVOID DeferredContext,
69                    PVOID SystemArgument1,
70                    PVOID SystemArgument2)
71 {
72    PNTTIMER Timer;
73    
74    DPRINT("NtpTimerDpcRoutine()\n");
75    
76    Timer = (PNTTIMER)DeferredContext;
77    
78    if ( Timer->Running )
79      {
80         KeInsertQueueApc(&Timer->Apc,
81                          SystemArgument1,
82                          SystemArgument2,
83                          KernelMode);
84      }
85 }
86
87
88 VOID
89 NtpTimerApcKernelRoutine(PKAPC Apc,
90                          PKNORMAL_ROUTINE* NormalRoutine,
91                          PVOID* NormalContext,
92                          PVOID* SystemArgument1,
93                          PVOID* SystemArguemnt2)
94 {
95    DPRINT("NtpTimerApcKernelRoutine()\n");
96
97 }
98
99
100 VOID NtInitializeTimerImplementation(VOID)
101 {
102    ExTimerType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE));
103    
104    RtlCreateUnicodeString(&ExTimerType->TypeName, L"Timer");
105    
106    ExTimerType->Tag = TAG('T', 'I', 'M', 'T');
107    ExTimerType->MaxObjects = ULONG_MAX;
108    ExTimerType->MaxHandles = ULONG_MAX;
109    ExTimerType->TotalObjects = 0;
110    ExTimerType->TotalHandles = 0;
111    ExTimerType->PagedPoolCharge = 0;
112    ExTimerType->NonpagedPoolCharge = sizeof(NTTIMER);
113    ExTimerType->Mapping = &ExpTimerMapping;
114    ExTimerType->Dump = NULL;
115    ExTimerType->Open = NULL;
116    ExTimerType->Close = NULL;
117    ExTimerType->Delete = NULL;
118    ExTimerType->Parse = NULL;
119    ExTimerType->Security = NULL;
120    ExTimerType->QueryName = NULL;
121    ExTimerType->OkayToClose = NULL;
122    ExTimerType->Create = NtpCreateTimer;
123    ExTimerType->DuplicationNotify = NULL;
124 }
125
126
127 NTSTATUS STDCALL
128 NtCancelTimer(IN HANDLE TimerHandle,
129               OUT PBOOLEAN CurrentState OPTIONAL)
130 {
131    PNTTIMER Timer;
132    NTSTATUS Status;
133    BOOLEAN State;
134    KIRQL OldIrql;
135
136    DPRINT("NtCancelTimer()\n");
137    Status = ObReferenceObjectByHandle(TimerHandle,
138                                       TIMER_ALL_ACCESS,
139                                       ExTimerType,
140                                       UserMode,
141                                       (PVOID*)&Timer,
142                                       NULL);
143    if (!NT_SUCCESS(Status))
144      return Status;
145    
146    OldIrql = KeRaiseIrqlToDpcLevel();
147    
148    State = KeCancelTimer(&Timer->Timer);
149    KeRemoveQueueDpc(&Timer->Dpc);
150    KeRemoveQueueApc(&Timer->Apc);
151    Timer->Running = FALSE;
152   
153    KeLowerIrql(OldIrql);
154    ObDereferenceObject(Timer);
155    
156    if (CurrentState != NULL)
157      {
158         *CurrentState = State;
159      }
160    
161    return STATUS_SUCCESS;
162 }
163
164
165 NTSTATUS STDCALL
166 NtCreateTimer(OUT PHANDLE TimerHandle,
167               IN ACCESS_MASK DesiredAccess,
168               IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
169               IN TIMER_TYPE TimerType)
170 {
171    PNTTIMER Timer;
172    NTSTATUS Status;
173    
174    DPRINT("NtCreateTimer()\n");
175    Status = ObCreateObject(TimerHandle,
176                            DesiredAccess,
177                            ObjectAttributes,
178                            ExTimerType,
179                            (PVOID*)&Timer);
180    if (!NT_SUCCESS(Status))
181      return Status;
182    
183    KeInitializeTimerEx(&Timer->Timer,
184                        TimerType);
185    
186    KeInitializeDpc (&Timer->Dpc,
187                     (PKDEFERRED_ROUTINE)NtpTimerDpcRoutine,
188                     (PVOID)Timer);
189    
190    Timer->Running = FALSE;
191    
192    ObDereferenceObject(Timer);
193    
194    return(STATUS_SUCCESS);
195 }
196
197
198 NTSTATUS STDCALL
199 NtOpenTimer(OUT PHANDLE TimerHandle,
200             IN ACCESS_MASK DesiredAccess,
201             IN POBJECT_ATTRIBUTES ObjectAttributes)
202 {
203    NTSTATUS Status;
204    
205    Status = ObOpenObjectByName(ObjectAttributes,
206                                ExTimerType,
207                                NULL,
208                                UserMode,
209                                DesiredAccess,
210                                NULL,
211                                TimerHandle);
212    return Status;
213 }
214
215
216 NTSTATUS STDCALL
217 NtQueryTimer(IN HANDLE TimerHandle,
218              IN CINT TimerInformationClass,
219              OUT PVOID UnsafeTimerInformation,
220              IN ULONG Length,
221              OUT PULONG UnsafeResultLength)
222 {
223   PNTTIMER Timer;
224   TIMER_BASIC_INFORMATION TimerInformation;
225   ULONG ResultLength;
226   NTSTATUS Status;
227
228   Status = ObReferenceObjectByHandle(TimerHandle,
229                                      TIMER_QUERY_STATE,
230                                      ExTimerType,
231                                      KeGetPreviousMode(),
232                                      (PVOID*)&Timer,
233                                      NULL);
234   if (!NT_SUCCESS(Status))
235     {
236       return(Status); 
237     }
238
239   if (TimerInformationClass != TimerBasicInformation)
240     {
241       ObDereferenceObject(Timer);
242       return(STATUS_INVALID_INFO_CLASS);
243     }
244   if (Length < sizeof(TIMER_BASIC_INFORMATION))
245     {
246       ObDereferenceObject(Timer);
247       return(STATUS_INFO_LENGTH_MISMATCH);
248     }
249   
250   memcpy(&TimerInformation.TimeRemaining, &Timer->Timer.DueTime,
251          sizeof(LARGE_INTEGER));
252   TimerInformation.SignalState = Timer->Timer.Header.SignalState;
253   ResultLength = sizeof(TIMER_BASIC_INFORMATION);
254   
255   Status = MmCopyToCaller(UnsafeTimerInformation, &TimerInformation,
256                           sizeof(TIMER_BASIC_INFORMATION));
257   if (!NT_SUCCESS(Status))
258     {
259       ObDereferenceObject(Timer);
260       return(Status);
261     }
262   
263   if (UnsafeResultLength != NULL)
264     {
265       Status = MmCopyToCaller(UnsafeResultLength, &ResultLength,
266                               sizeof(ULONG));
267       if (!NT_SUCCESS(Status))
268         {
269           ObDereferenceObject(Timer);
270           return(Status);
271         }
272     }
273   ObDereferenceObject(Timer);
274   return(STATUS_SUCCESS);
275 }
276
277
278 NTSTATUS STDCALL
279 NtSetTimer(IN HANDLE TimerHandle,
280            IN PLARGE_INTEGER DueTime,
281            IN PTIMERAPCROUTINE TimerApcRoutine,
282            IN PVOID TimerContext,
283            IN BOOL WakeTimer,
284            IN ULONG Period OPTIONAL,
285            OUT PBOOLEAN PreviousState OPTIONAL)
286 {
287    PNTTIMER Timer;
288    NTSTATUS Status;
289    BOOLEAN Result;
290    KIRQL OldIrql;
291    BOOLEAN State;
292
293    DPRINT("NtSetTimer()\n");
294
295    Status = ObReferenceObjectByHandle(TimerHandle,
296                                       TIMER_ALL_ACCESS,
297                                       ExTimerType,
298                                       KeGetPreviousMode(),
299                                       (PVOID*)&Timer,
300                                       NULL);
301    if (!NT_SUCCESS(Status))
302      return Status;
303
304    State = KeReadStateTimer(&Timer->Timer);
305
306    if (Timer->Running == TRUE)
307      {
308         /* cancel running timer */
309         OldIrql = KeRaiseIrqlToDpcLevel();
310         KeCancelTimer(&Timer->Timer);
311         KeRemoveQueueDpc(&Timer->Dpc);
312         KeRemoveQueueApc(&Timer->Apc);
313         Timer->Running = FALSE;
314         KeLowerIrql(OldIrql);
315      }
316    if( TimerApcRoutine )
317       KeInitializeApc(&Timer->Apc,
318                       KeGetCurrentThread(),
319                       0,
320                       (PKKERNEL_ROUTINE)NtpTimerApcKernelRoutine,
321                       (PKRUNDOWN_ROUTINE)NULL,
322                       (PKNORMAL_ROUTINE)TimerApcRoutine,
323                       KeGetPreviousMode(),
324                       TimerContext);
325
326    Result = KeSetTimerEx (&Timer->Timer,
327                           *DueTime,
328                           Period,
329                           TimerApcRoutine ? &Timer->Dpc : 0 );
330    if (Result == TRUE)
331      {
332         ObDereferenceObject(Timer);
333         DPRINT1( "KeSetTimer says the timer was already running, this shouldn't be\n" );
334         return STATUS_UNSUCCESSFUL;
335      }
336
337    Timer->Running = TRUE;
338
339    ObDereferenceObject(Timer);
340
341    if (PreviousState != NULL)
342      {
343         *PreviousState = State;
344      }
345
346    return STATUS_SUCCESS;
347 }
348
349 /* EOF */