2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ex/callback.c
5 * PURPOSE: Executive callbacks
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* TYPES *********************************************************************/
17 /* Mapping for Callback Object */
18 GENERIC_MAPPING ExpCallbackMapping
=
21 STANDARD_RIGHTS_WRITE
| CALLBACK_MODIFY_STATE
,
22 STANDARD_RIGHTS_EXECUTE
| SYNCHRONIZE
,
26 /* Kernel Default Callbacks */
27 PCALLBACK_OBJECT SetSystemTimeCallback
;
28 PCALLBACK_OBJECT SetSystemStateCallback
;
29 PCALLBACK_OBJECT PowerStateCallback
;
30 SYSTEM_CALLBACKS ExpInitializeCallback
[] =
32 {&SetSystemTimeCallback
, L
"\\Callback\\SetSystemTime"},
33 {&SetSystemStateCallback
, L
"\\Callback\\SetSystemState"},
34 {&PowerStateCallback
, L
"\\Callback\\PowerState"},
38 POBJECT_TYPE ExCallbackObjectType
;
39 KEVENT ExpCallbackEvent
;
40 EX_PUSH_LOCK ExpCallBackFlush
;
42 /* PRIVATE FUNCTIONS *********************************************************/
46 ExInitializeCallBack(IN OUT PEX_CALLBACK Callback
)
48 /* Initialize the fast reference */
49 ExInitializeFastReference(&Callback
->RoutineBlock
, NULL
);
52 PEX_CALLBACK_ROUTINE_BLOCK
54 ExAllocateCallBack(IN PEX_CALLBACK_FUNCTION Function
,
57 PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
;
59 /* Allocate a callback */
60 CallbackBlock
= ExAllocatePoolWithTag(PagedPool
,
61 sizeof(EX_CALLBACK_ROUTINE_BLOCK
),
62 TAG_CALLBACK_ROUTINE_BLOCK
);
66 CallbackBlock
->Function
= Function
;
67 CallbackBlock
->Context
= Context
;
68 ExInitializeRundownProtection(&CallbackBlock
->RundownProtect
);
77 ExFreeCallBack(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
)
79 /* Just free it from memory */
80 ExFreePoolWithTag(CallbackBlock
, TAG_CALLBACK_ROUTINE_BLOCK
);
85 ExWaitForCallBacks(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
)
87 /* Wait on the rundown */
88 ExWaitForRundownProtectionRelease(&CallbackBlock
->RundownProtect
);
93 ExGetCallBackBlockRoutine(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
)
95 /* Return the function */
96 return CallbackBlock
->Function
;
101 ExGetCallBackBlockContext(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
)
103 /* Return the context */
104 return CallbackBlock
->Context
;
109 ExDereferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack
,
110 IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
)
112 /* Release a fast reference */
113 if (!ExReleaseFastReference(&CallBack
->RoutineBlock
, CallbackBlock
))
116 ExReleaseRundownProtection(&CallbackBlock
->RundownProtect
);
120 PEX_CALLBACK_ROUTINE_BLOCK
122 ExReferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack
)
124 EX_FAST_REF OldValue
;
126 PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
;
128 /* Acquire a reference */
129 OldValue
= ExAcquireFastReference(&CallBack
->RoutineBlock
);
130 Count
= ExGetCountFastReference(OldValue
);
132 /* Fail if there isn't any object */
133 if (!ExGetObjectFastReference(OldValue
)) return NULL
;
135 /* Check if we don't have a reference */
139 DPRINT1("Unhandled callback race condition\n");
144 /* Get the callback block */
145 CallbackBlock
= ExGetObjectFastReference(OldValue
);
147 /* Check if this is the last reference */
150 /* Acquire rundown protection */
151 if (ExfAcquireRundownProtectionEx(&CallbackBlock
->RundownProtect
,
154 /* Insert references */
155 if (!ExInsertFastReference(&CallBack
->RoutineBlock
, CallbackBlock
))
157 /* Backdown the rundown acquire */
158 ExfReleaseRundownProtectionEx(&CallbackBlock
->RundownProtect
,
164 /* Return the callback block */
165 return CallbackBlock
;
170 ExCompareExchangeCallBack(IN OUT PEX_CALLBACK CallBack
,
171 IN PEX_CALLBACK_ROUTINE_BLOCK NewBlock
,
172 IN PEX_CALLBACK_ROUTINE_BLOCK OldBlock
)
174 EX_FAST_REF OldValue
;
175 PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
;
178 /* Check that we have a new block */
181 /* Acquire rundown */
182 if (!ExfAcquireRundownProtectionEx(&NewBlock
->RundownProtect
,
185 /* This should never happen */
186 ASSERTMSG("Callback block is already undergoing rundown", FALSE
);
192 OldValue
= ExCompareSwapFastReference(&CallBack
->RoutineBlock
,
196 /* Get the routine block */
197 CallbackBlock
= ExGetObjectFastReference(OldValue
);
198 Count
= ExGetCountFastReference(OldValue
);
200 /* Make sure the swap worked */
201 if (CallbackBlock
== OldBlock
)
203 /* Make sure we replaced a valid pointer */
206 /* Acquire the flush lock and immediately release it */
207 KeEnterCriticalRegion();
208 ExWaitOnPushLock(&ExpCallBackFlush
);
210 /* Release rundown protection */
211 KeLeaveCriticalRegion();
212 ExfReleaseRundownProtectionEx(&CallbackBlock
->RundownProtect
,
221 /* It failed, check if we had a block */
224 /* We did, remove the refernces that we had added */
225 ExfReleaseRundownProtectionEx(&NewBlock
->RundownProtect
,
236 ExpDeleteCallback(IN PVOID Object
)
239 ASSERT(IsListEmpty(&((PCALLBACK_OBJECT
)Object
)->RegisteredCallbacks
));
243 * @name ExpInitializeCallbacks
245 * Creates the Callback Object as a valid Object Type in the Kernel.
246 * Internal function, subject to further review
248 * @return TRUE if the Callback Object Type was successfully created.
256 ExpInitializeCallbacks(VOID
)
258 OBJECT_ATTRIBUTES ObjectAttributes
;
260 UNICODE_STRING DirName
= RTL_CONSTANT_STRING(L
"\\Callback");
261 UNICODE_STRING CallbackName
;
263 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer
;
264 HANDLE DirectoryHandle
;
267 /* Setup lightweight callback lock */
268 ExpCallBackFlush
.Value
= 0;
270 /* Initialize the Callback Object type */
271 RtlZeroMemory(&ObjectTypeInitializer
, sizeof(ObjectTypeInitializer
));
272 RtlInitUnicodeString(&Name
, L
"Callback");
273 ObjectTypeInitializer
.Length
= sizeof(ObjectTypeInitializer
);
274 ObjectTypeInitializer
.InvalidAttributes
= OBJ_OPENLINK
;
275 ObjectTypeInitializer
.GenericMapping
= ExpCallbackMapping
;
276 ObjectTypeInitializer
.PoolType
= NonPagedPool
;
277 ObjectTypeInitializer
.DeleteProcedure
= ExpDeleteCallback
;
278 ObjectTypeInitializer
.ValidAccessMask
= CALLBACK_ALL_ACCESS
;
279 Status
= ObCreateObjectType(&Name
,
280 &ObjectTypeInitializer
,
282 &ExCallbackObjectType
);
283 if (!NT_SUCCESS(Status
)) return FALSE
;
285 /* Initialize the Object */
286 InitializeObjectAttributes(&ObjectAttributes
,
288 OBJ_CASE_INSENSITIVE
| OBJ_PERMANENT
,
292 /* Create the Object Directory */
293 Status
= NtCreateDirectoryObject(&DirectoryHandle
,
294 DIRECTORY_ALL_ACCESS
,
296 if (!NT_SUCCESS(Status
)) return FALSE
;
298 /* Close Handle... */
299 NtClose(DirectoryHandle
);
301 /* Initialize Event used when unregistering */
302 KeInitializeEvent(&ExpCallbackEvent
, NotificationEvent
, 0);
304 /* Default NT Kernel Callbacks. */
305 for (i
= 0; ExpInitializeCallback
[i
].CallbackObject
; i
++)
307 /* Create the name from the structure */
308 RtlInitUnicodeString(&CallbackName
, ExpInitializeCallback
[i
].Name
);
310 /* Initialize the Object Attributes Structure */
311 InitializeObjectAttributes(&ObjectAttributes
,
313 OBJ_PERMANENT
| OBJ_CASE_INSENSITIVE
,
317 /* Create the Callback Object */
318 Status
= ExCreateCallback(ExpInitializeCallback
[i
].CallbackObject
,
322 if (!NT_SUCCESS(Status
)) return FALSE
;
325 /* Everything successful */
329 /* PUBLIC FUNCTIONS **********************************************************/
332 * @name ExCreateCallback
335 * Opens or creates a Callback Object. Creates only if Create is true.
336 * Allows multiple Callback Functions to be registred only if
337 * AllowMultipleCallbacks is true.
338 * See: http://www.osronline.com/ddkx/kmarch/k102_967m.htm
339 * http://www.osronline.com/article.cfm?id=24
341 * @param CallbackObject
342 * Pointer that will receive the Callback Object.
344 * @param CallbackName
348 * Determines if the object will be created if it doesn't exit
350 * @param AllowMultipleCallbacks
351 * Determines if more then one registered callback function
352 * can be attached to this Callback Object.
354 * @return STATUS_SUCESS if not failed.
356 * @remarks Must be called at IRQL = PASSIVE_LEVEL
361 ExCreateCallback(OUT PCALLBACK_OBJECT
*CallbackObject
,
362 IN POBJECT_ATTRIBUTES ObjectAttributes
,
364 IN BOOLEAN AllowMultipleCallbacks
)
366 PCALLBACK_OBJECT Callback
= NULL
;
368 HANDLE Handle
= NULL
;
371 /* Open a handle to the callback if it exists */
372 if (ObjectAttributes
->ObjectName
)
374 /* Open the handle */
375 Status
= ObOpenObjectByName(ObjectAttributes
,
376 ExCallbackObjectType
,
385 /* Otherwise, fail */
386 Status
= STATUS_UNSUCCESSFUL
;
389 /* We weren't able to open it...should we create it? */
390 if (!(NT_SUCCESS(Status
)) && (Create
))
392 /* Create the object */
393 Status
= ObCreateObject(KernelMode
,
394 ExCallbackObjectType
,
398 sizeof(CALLBACK_OBJECT
),
402 if (NT_SUCCESS(Status
))
405 Callback
->Signature
= 'llaC';
406 KeInitializeSpinLock(&Callback
->Lock
);
407 InitializeListHead(&Callback
->RegisteredCallbacks
);
408 Callback
->AllowMultipleCallbacks
= AllowMultipleCallbacks
;
410 /* Insert the object into the object namespace */
411 Status
= ObInsertObject(Callback
,
420 /* Check if we have success until here */
421 if (NT_SUCCESS(Status
))
423 /* Get a pointer to the new object from the handle we just got */
424 Status
= ObReferenceObjectByHandle(Handle
,
426 ExCallbackObjectType
,
431 /* Close the Handle, since we now have the pointer */
435 /* Everything went fine, so return a pointer to the Object */
436 if (NT_SUCCESS(Status
))
438 *CallbackObject
= Callback
;
444 * @name ExNotifyCallback
447 * Calls a function pointer (a registered callback)
448 * See: http://www.osronline.com/ddkx/kmarch/k102_2f5e.htm
449 * http://vmsone.com/~decuslib/vmssig/vmslt99b/nt/wdm-callback.txt
451 * @param CallbackObject
452 * Which callback to call
455 * Pointer/data to send to callback function
458 * Pointer/data to send to callback function
467 ExNotifyCallback(IN PCALLBACK_OBJECT CallbackObject
,
471 PLIST_ENTRY RegisteredCallbacks
;
472 PCALLBACK_REGISTRATION CallbackRegistration
;
475 /* Check if we don't have an object or registrations */
476 if (!(CallbackObject
) ||
477 (IsListEmpty(&CallbackObject
->RegisteredCallbacks
)))
483 /* Acquire the Lock */
484 KeAcquireSpinLock(&CallbackObject
->Lock
, &OldIrql
);
486 /* Enumerate through all the registered functions */
487 for (RegisteredCallbacks
= CallbackObject
->RegisteredCallbacks
.Flink
;
488 RegisteredCallbacks
!= &CallbackObject
->RegisteredCallbacks
;
489 RegisteredCallbacks
= RegisteredCallbacks
->Flink
)
491 /* Get a pointer to a Callback Registration from the List Entries */
492 CallbackRegistration
= CONTAINING_RECORD(RegisteredCallbacks
,
493 CALLBACK_REGISTRATION
,
496 /* Don't bother doing notification if it's pending to be deleted */
497 if (!CallbackRegistration
->UnregisterWaiting
)
499 /* Mark the Callback in use, so it won't get deleted */
500 CallbackRegistration
->Busy
+= 1;
502 /* Release the Spinlock before making the call */
503 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
505 /* Call the Registered Function */
506 CallbackRegistration
->CallbackFunction(CallbackRegistration
->
511 /* Get SpinLock back */
512 KeAcquireSpinLock(&CallbackObject
->Lock
, &OldIrql
);
514 /* We are not in use anymore */
515 CallbackRegistration
->Busy
-= 1;
517 /* Check if removal is pending and we're not active */
518 if ((CallbackRegistration
->UnregisterWaiting
) &&
519 !(CallbackRegistration
->Busy
))
521 /* Signal the callback event */
522 KeSetEvent(&ExpCallbackEvent
, 0, FALSE
);
527 /* Release the Callback Object */
528 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
532 * @name ExRegisterCallback
535 * Allows a function to associate a callback pointer (Function) to
536 * a created Callback object
537 * See: DDK, OSR, links in ExNotifyCallback
539 * @param CallbackObject
540 * The Object Created with ExCreateCallBack
542 * @param CallBackFunction
543 * Pointer to the function to be called back
545 * @param CallBackContext
546 * Block of memory that can contain user-data which will be
547 * passed on to the callback
549 * @return A handle to a Callback Registration Structure (MSDN Documentation)
556 ExRegisterCallback(IN PCALLBACK_OBJECT CallbackObject
,
557 IN PCALLBACK_FUNCTION CallbackFunction
,
558 IN PVOID CallbackContext
)
560 PCALLBACK_REGISTRATION CallbackRegistration
= NULL
;
564 ASSERT(CallbackFunction
);
565 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
567 /* Create reference to Callback Object */
568 ObReferenceObject(CallbackObject
);
570 /* Allocate memory for the structure */
571 CallbackRegistration
= ExAllocatePoolWithTag(NonPagedPool
,
572 sizeof(CALLBACK_REGISTRATION
),
573 TAG_CALLBACK_REGISTRATION
);
574 if (!CallbackRegistration
)
576 /* Dereference and fail */
577 ObDereferenceObject (CallbackObject
);
581 /* Create Callback Registration */
582 CallbackRegistration
->CallbackObject
= CallbackObject
;
583 CallbackRegistration
->CallbackFunction
= CallbackFunction
;
584 CallbackRegistration
->CallbackContext
= CallbackContext
;
585 CallbackRegistration
->Busy
= 0;
586 CallbackRegistration
->UnregisterWaiting
= FALSE
;
588 /* Acquire SpinLock */
589 KeAcquireSpinLock(&CallbackObject
->Lock
, &OldIrql
);
591 /* Check if 1) No Callbacks registered or 2) Multiple Callbacks allowed */
592 if ((CallbackObject
->AllowMultipleCallbacks
) ||
593 (IsListEmpty(&CallbackObject
->RegisteredCallbacks
)))
595 /* Register the callback */
596 InsertTailList(&CallbackObject
->RegisteredCallbacks
,
597 &CallbackRegistration
->Link
);
599 /* Release SpinLock */
600 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
604 /* Release SpinLock */
605 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
607 /* Free the registration */
608 ExFreePoolWithTag(CallbackRegistration
, TAG_CALLBACK_REGISTRATION
);
609 CallbackRegistration
= NULL
;
611 /* Dereference the object */
612 ObDereferenceObject(CallbackObject
);
615 /* Return handle to Registration Object */
616 return (PVOID
)CallbackRegistration
;
620 * @name ExUnregisterCallback
623 * Deregisters a CallBack
624 * See: DDK, OSR, links in ExNotifyCallback
626 * @param CallbackRegistration
627 * Callback Registration Handle
636 ExUnregisterCallback(IN PVOID CallbackRegistrationHandle
)
638 PCALLBACK_REGISTRATION CallbackRegistration
;
639 PCALLBACK_OBJECT CallbackObject
;
641 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
643 /* Convert Handle to valid Structure Pointer */
644 CallbackRegistration
= (PCALLBACK_REGISTRATION
)CallbackRegistrationHandle
;
646 /* Get the Callback Object */
647 CallbackObject
= CallbackRegistration
->CallbackObject
;
649 /* Lock the Object */
650 KeAcquireSpinLock (&CallbackObject
->Lock
, &OldIrql
);
652 /* We can't Delete the Callback if it's in use */
653 while (CallbackRegistration
->Busy
)
655 /* Let everyone else know we're unregistering */
656 CallbackRegistration
->UnregisterWaiting
= TRUE
;
658 /* We are going to wait for the event, so the Lock isn't necessary */
659 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
661 /* Make sure the event is cleared */
662 KeClearEvent(&ExpCallbackEvent
);
664 /* Wait for the Event */
665 KeWaitForSingleObject(&ExpCallbackEvent
,
671 /* We need the Lock again */
672 KeAcquireSpinLock(&CallbackObject
->Lock
, &OldIrql
);
675 /* Remove the Callback */
676 RemoveEntryList(&CallbackRegistration
->Link
);
678 /* It's now safe to release the lock */
679 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
681 /* Delete this registration */
682 ExFreePoolWithTag(CallbackRegistration
, TAG_CALLBACK_REGISTRATION
);
684 /* Remove the reference */
685 ObDereferenceObject(CallbackObject
);