- Fixed the freeing of memory from boot load drivers.
[reactos.git] / reactos / ntoskrnl / nt / nttimer.c
1 /* $Id: nttimer.c,v 1.22 2003/10/12 17:05:48 hbirr 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
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
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 ExTimerType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE));
125
126 RtlCreateUnicodeString(&ExTimerType->TypeName, L"Timer");
127
128 ExTimerType->Tag = TAG('T', 'I', 'M', 'T');
129 ExTimerType->MaxObjects = ULONG_MAX;
130 ExTimerType->MaxHandles = ULONG_MAX;
131 ExTimerType->TotalObjects = 0;
132 ExTimerType->TotalHandles = 0;
133 ExTimerType->PagedPoolCharge = 0;
134 ExTimerType->NonpagedPoolCharge = sizeof(NTTIMER);
135 ExTimerType->Mapping = &ExpTimerMapping;
136 ExTimerType->Dump = NULL;
137 ExTimerType->Open = NULL;
138 ExTimerType->Close = NULL;
139 ExTimerType->Delete = NtpDeleteTimer;
140 ExTimerType->Parse = NULL;
141 ExTimerType->Security = NULL;
142 ExTimerType->QueryName = NULL;
143 ExTimerType->OkayToClose = NULL;
144 ExTimerType->Create = NtpCreateTimer;
145 ExTimerType->DuplicationNotify = NULL;
146
147 ObpCreateTypeObject(ExTimerType);
148 }
149
150
151 NTSTATUS STDCALL
152 NtCancelTimer(IN HANDLE TimerHandle,
153 OUT PBOOLEAN CurrentState OPTIONAL)
154 {
155 PNTTIMER Timer;
156 NTSTATUS Status;
157 BOOLEAN State;
158 KIRQL OldIrql;
159
160 DPRINT("NtCancelTimer()\n");
161 Status = ObReferenceObjectByHandle(TimerHandle,
162 TIMER_ALL_ACCESS,
163 ExTimerType,
164 UserMode,
165 (PVOID*)&Timer,
166 NULL);
167 if (!NT_SUCCESS(Status))
168 return Status;
169
170 OldIrql = KeRaiseIrqlToDpcLevel();
171
172 State = KeCancelTimer(&Timer->Timer);
173 KeRemoveQueueDpc(&Timer->Dpc);
174 KeRemoveQueueApc(&Timer->Apc);
175 Timer->Running = FALSE;
176
177 KeLowerIrql(OldIrql);
178 ObDereferenceObject(Timer);
179
180 if (CurrentState != NULL)
181 {
182 *CurrentState = State;
183 }
184
185 return STATUS_SUCCESS;
186 }
187
188
189 NTSTATUS STDCALL
190 NtCreateTimer(OUT PHANDLE TimerHandle,
191 IN ACCESS_MASK DesiredAccess,
192 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
193 IN TIMER_TYPE TimerType)
194 {
195 PNTTIMER Timer;
196 NTSTATUS Status;
197
198 DPRINT("NtCreateTimer()\n");
199 Status = ObCreateObject(ExGetPreviousMode(),
200 ExTimerType,
201 ObjectAttributes,
202 ExGetPreviousMode(),
203 NULL,
204 sizeof(NTTIMER),
205 0,
206 0,
207 (PVOID*)&Timer);
208 if (!NT_SUCCESS(Status))
209 return Status;
210
211 KeInitializeTimerEx(&Timer->Timer,
212 TimerType);
213
214 KeInitializeDpc (&Timer->Dpc,
215 (PKDEFERRED_ROUTINE)NtpTimerDpcRoutine,
216 (PVOID)Timer);
217
218 Timer->Running = FALSE;
219
220 Status = ObInsertObject ((PVOID)Timer,
221 NULL,
222 DesiredAccess,
223 0,
224 NULL,
225 TimerHandle);
226
227 ObDereferenceObject(Timer);
228
229 return Status;
230 }
231
232
233 NTSTATUS STDCALL
234 NtOpenTimer(OUT PHANDLE TimerHandle,
235 IN ACCESS_MASK DesiredAccess,
236 IN POBJECT_ATTRIBUTES ObjectAttributes)
237 {
238 NTSTATUS Status;
239
240 Status = ObOpenObjectByName(ObjectAttributes,
241 ExTimerType,
242 NULL,
243 UserMode,
244 DesiredAccess,
245 NULL,
246 TimerHandle);
247 return Status;
248 }
249
250
251 NTSTATUS STDCALL
252 NtQueryTimer(IN HANDLE TimerHandle,
253 IN CINT TimerInformationClass,
254 OUT PVOID UnsafeTimerInformation,
255 IN ULONG Length,
256 OUT PULONG UnsafeResultLength)
257 {
258 PNTTIMER Timer;
259 TIMER_BASIC_INFORMATION TimerInformation;
260 ULONG ResultLength;
261 NTSTATUS Status;
262
263 Status = ObReferenceObjectByHandle(TimerHandle,
264 TIMER_QUERY_STATE,
265 ExTimerType,
266 KeGetPreviousMode(),
267 (PVOID*)&Timer,
268 NULL);
269 if (!NT_SUCCESS(Status))
270 {
271 return(Status);
272 }
273
274 if (TimerInformationClass != TimerBasicInformation)
275 {
276 ObDereferenceObject(Timer);
277 return(STATUS_INVALID_INFO_CLASS);
278 }
279 if (Length < sizeof(TIMER_BASIC_INFORMATION))
280 {
281 ObDereferenceObject(Timer);
282 return(STATUS_INFO_LENGTH_MISMATCH);
283 }
284
285 memcpy(&TimerInformation.TimeRemaining, &Timer->Timer.DueTime,
286 sizeof(LARGE_INTEGER));
287 TimerInformation.SignalState = Timer->Timer.Header.SignalState;
288 ResultLength = sizeof(TIMER_BASIC_INFORMATION);
289
290 Status = MmCopyToCaller(UnsafeTimerInformation, &TimerInformation,
291 sizeof(TIMER_BASIC_INFORMATION));
292 if (!NT_SUCCESS(Status))
293 {
294 ObDereferenceObject(Timer);
295 return(Status);
296 }
297
298 if (UnsafeResultLength != NULL)
299 {
300 Status = MmCopyToCaller(UnsafeResultLength, &ResultLength,
301 sizeof(ULONG));
302 if (!NT_SUCCESS(Status))
303 {
304 ObDereferenceObject(Timer);
305 return(Status);
306 }
307 }
308 ObDereferenceObject(Timer);
309 return(STATUS_SUCCESS);
310 }
311
312
313 NTSTATUS STDCALL
314 NtSetTimer(IN HANDLE TimerHandle,
315 IN PLARGE_INTEGER DueTime,
316 IN PTIMERAPCROUTINE TimerApcRoutine,
317 IN PVOID TimerContext,
318 IN BOOL WakeTimer,
319 IN ULONG Period OPTIONAL,
320 OUT PBOOLEAN PreviousState OPTIONAL)
321 {
322 PNTTIMER Timer;
323 NTSTATUS Status;
324 BOOLEAN Result;
325 KIRQL OldIrql;
326 BOOLEAN State;
327
328 DPRINT("NtSetTimer()\n");
329
330 Status = ObReferenceObjectByHandle(TimerHandle,
331 TIMER_ALL_ACCESS,
332 ExTimerType,
333 KeGetPreviousMode(),
334 (PVOID*)&Timer,
335 NULL);
336 if (!NT_SUCCESS(Status))
337 return Status;
338
339 State = KeReadStateTimer(&Timer->Timer);
340
341 if (Timer->Running == TRUE)
342 {
343 /* cancel running timer */
344 OldIrql = KeRaiseIrqlToDpcLevel();
345 KeCancelTimer(&Timer->Timer);
346 KeRemoveQueueDpc(&Timer->Dpc);
347 KeRemoveQueueApc(&Timer->Apc);
348 Timer->Running = FALSE;
349 KeLowerIrql(OldIrql);
350 }
351 if( TimerApcRoutine )
352 KeInitializeApc(&Timer->Apc,
353 KeGetCurrentThread(),
354 OriginalApcEnvironment,
355 (PKKERNEL_ROUTINE)NtpTimerApcKernelRoutine,
356 (PKRUNDOWN_ROUTINE)NULL,
357 (PKNORMAL_ROUTINE)TimerApcRoutine,
358 KeGetPreviousMode(),
359 TimerContext);
360
361 Result = KeSetTimerEx (&Timer->Timer,
362 *DueTime,
363 Period,
364 TimerApcRoutine ? &Timer->Dpc : 0 );
365 if (Result == TRUE)
366 {
367 ObDereferenceObject(Timer);
368 DPRINT1( "KeSetTimer says the timer was already running, this shouldn't be\n" );
369 return STATUS_UNSUCCESSFUL;
370 }
371
372 Timer->Running = TRUE;
373
374 ObDereferenceObject(Timer);
375
376 if (PreviousState != NULL)
377 {
378 *PreviousState = State;
379 }
380
381 return STATUS_SUCCESS;
382 }
383
384 /* EOF */