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