- Callbacks implementation. A little modified patch by Alex Ionescu (alex@relsoft...
authorFilip Navara <filip.navara@gmail.com>
Sat, 19 Jun 2004 08:48:01 +0000 (08:48 +0000)
committerFilip Navara <filip.navara@gmail.com>
Sat, 19 Jun 2004 08:48:01 +0000 (08:48 +0000)
svn path=/trunk/; revision=9723

reactos/ntoskrnl/ex/callback.c
reactos/ntoskrnl/include/internal/callbacks.h [new file with mode: 0644]
reactos/ntoskrnl/include/internal/ex.h
reactos/ntoskrnl/ke/main.c

index a822ed8..e4ec4bf 100644 (file)
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-/* $Id: callback.c,v 1.10 2003/07/10 06:27:13 royce Exp $
+/* $Id: callback.c,v 1.11 2004/06/19 08:48:00 navaraf Exp $
  *
  * PROJECT:         ReactOS kernel
  * FILE:            ntoskrnl/ex/callback.c
  * PURPOSE:         Executive callbacks
  * PROGRAMMER:      David Welch (welch@mcmail.com)
+ *                  Alex Ionescu (alex@relsoft.net)
  * PORTABILITY:     Checked.
  * UPDATE HISTORY:
+ *                  Added all functions 30/05/04
  *                  Created 22/05/98
- */
-
-/*
  * NOTE:
- *     These funtions are not implemented in NT4.
- *     They are implemented in Win2k.
+ *                  These funtions are not implemented in NT4, but
+ *                  they are implemented in Win2k.
  */
 
 /* INCLUDES *****************************************************************/
 
 #include <ddk/ntddk.h>
-
+#include <rosrtl/string.h>
 #include <internal/debug.h>
+#include <internal/ob.h>
+#include <internal/callbacks.h>
 
 /* FUNCTIONS *****************************************************************/
 
 /*
- * @unimplemented
+ * ExpInitializeCallbacks
+ *
+ * FUNCTION:
+ * Creates the Callback Object as a valid Object Type in the Kernel.
+ *
+ * ARGUMENTS:
+ * None
+ *
+ * RETURNS:
+ * TRUE if the Callback Object Type was successfully created.
+ */
+BOOLEAN
+ExpInitializeCallbacks(VOID)
+{
+   OBJECT_ATTRIBUTES ObjectAttributes;
+   NTSTATUS Status;
+   UNICODE_STRING DirName;
+   UNICODE_STRING CallbackName;
+   HANDLE DirectoryHandle;
+   ULONG i;
+
+   /* Allocate memory for Object */
+   ExCallbackObjectType = ExAllocatePoolWithTag(NonPagedPool, sizeof(OBJECT_TYPE), CALLBACK_TAG);
+
+   /* Initialize name */
+   RtlRosInitUnicodeStringFromLiteral(&ExCallbackObjectType->TypeName,L"Callback");
+
+   /* Create the Object Type */
+   ExCallbackObjectType->Tag = CALLBACK_TAG;
+   ExCallbackObjectType->TotalObjects = 0;
+   ExCallbackObjectType->TotalHandles = 0;
+   ExCallbackObjectType->MaxObjects = 0xFFFFFFFF;
+   ExCallbackObjectType->MaxHandles = 0xFFFFFFFF;
+   ExCallbackObjectType->PagedPoolCharge = 0;
+   ExCallbackObjectType->Dump = NULL;
+   ExCallbackObjectType->Open = NULL;
+   ExCallbackObjectType->Close = NULL;
+   ExCallbackObjectType->Delete = NULL;
+   ExCallbackObjectType->Parse = NULL;
+   ExCallbackObjectType->Security = NULL;
+   ExCallbackObjectType->QueryName = NULL;
+   ExCallbackObjectType->DuplicationNotify = NULL;
+   ExCallbackObjectType->OkayToClose = NULL;
+   ExCallbackObjectType->Create = NULL;
+   ExCallbackObjectType->Mapping = &ExpCallbackMapping;
+   ExCallbackObjectType->NonpagedPoolCharge = sizeof(_INT_CALLBACK_OBJECT);
+   Status = ObpCreateTypeObject(ExCallbackObjectType);
+
+   /* Fail if it wasn't created successfully */
+   if (!NT_SUCCESS(Status))
+   {
+      return FALSE;
+   }
+
+   /* Initialize the Object */
+   RtlRosInitUnicodeStringFromLiteral(&DirName, L"\\Callback" );
+   InitializeObjectAttributes(
+      &ObjectAttributes,
+      &DirName,
+      OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
+      NULL,
+      NULL
+   );
+
+   /* Create the Object Directory */
+   Status = NtCreateDirectoryObject(
+               &DirectoryHandle,
+               DIRECTORY_ALL_ACCESS,
+               &ObjectAttributes
+            );
+
+   /* Fail if couldn't create */
+   if (!NT_SUCCESS(Status))
+   {
+      return FALSE;
+   }
+
+   /* Close Handle... */
+   NtClose(DirectoryHandle);
+
+   /* Initialize Event used when unregistering */
+   KeInitializeEvent(&ExpCallbackEvent, NotificationEvent, 0);
+
+   /* Default NT Kernel Callbacks. */
+   for (i=0; ExpInitializeCallback[i].CallbackObject; i++)
+   {
+      /* Create the name from the structure */
+      RtlInitUnicodeString(&CallbackName, ExpInitializeCallback[i].Name);
+
+      /* Initialize the Object Attributes Structure */
+      InitializeObjectAttributes(
+         &ObjectAttributes,
+         &CallbackName,
+         OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
+         NULL,
+         NULL
+      );
+
+      /* Create the Callback Object */
+      Status = ExCreateCallback(
+                  (PCALLBACK_OBJECT*)&(ExpInitializeCallback[i].CallbackObject),
+                  &ObjectAttributes,
+                  TRUE,
+                  TRUE
+               );
+
+      /* Make sure Global Callbacks have been created */
+      if (!NT_SUCCESS(Status))
+      {
+         return FALSE;
+      }
+   }
+   /* Everything successful */
+   return TRUE;
+}
+
+/*
+ * ExCreateCallback
+ *
+ * FUNCTION:
+ * Opens or creates a Callback Object. Creates only if Create is true.
+ *  Allows multiple Callback Functions to be registred only if AllowMultipleCallbacks
+ *  is true.
+ *
+ * ARGUMENTS:
+ * CallbackObject = Pointer that will receive the Callback Object.
+ * CallbackName = Name of Callback
+ * Create = Determines if the object will be created if it doesn't exit
+ * AllowMultipleCallbacks = Determines if more then one registered callback function
+ *  can be attached to this Callback Object.
+ *
+ * RETURNS:
+ * STATUS_SUCESS if not failed.
+ *
+ * @implemented
  */
 NTSTATUS
 STDCALL
-ExCreateCallback (
-       OUT     PCALLBACK_OBJECT        * CallbackObject,
-       IN      POBJECT_ATTRIBUTES      ObjectAttributes,
-       IN      BOOLEAN                 Create,
-       IN      BOOLEAN                 AllowMultipleCallbacks
-       )
+ExCreateCallback(
+   OUT PCALLBACK_OBJECT *CallbackObject,
+   IN POBJECT_ATTRIBUTES ObjectAttributes,
+   IN BOOLEAN Create,
+   IN BOOLEAN AllowMultipleCallbacks
+)
 {
-       return STATUS_NOT_IMPLEMENTED;
+   PINT_CALLBACK_OBJECT Callback;
+   NTSTATUS    Status;
+   HANDLE     Handle;
+
+   /* Open a handle to the callback if it exists */
+   if (ObjectAttributes->ObjectName)
+   {
+      Status = ObOpenObjectByName(ObjectAttributes,
+                                  ExCallbackObjectType,
+                                  NULL,
+                                  KernelMode,
+                                  0,
+                                  NULL,
+                                  &Handle);
+   }
+   else
+   {
+      Status = STATUS_UNSUCCESSFUL;
+   }
+
+   /* We weren't able to open it...should we create it? */
+   if(!NT_SUCCESS(Status) && Create )
+   {
+      Status = ObCreateObject(KernelMode,
+                              ExCallbackObjectType,
+                              ObjectAttributes,
+                              KernelMode,
+                              NULL,
+                              sizeof(_INT_CALLBACK_OBJECT),
+                              0,
+                              0,
+                              (PVOID *)&Callback );
+
+      /* We Created it...let's initialize the structure now */
+      if(NT_SUCCESS(Status))
+      {
+         KeInitializeSpinLock (&Callback->Lock);      /*  SpinLock   */
+         InitializeListHead(&Callback->RegisteredCallbacks);   /*  Callback Entries */
+         Callback->AllowMultipleCallbacks = AllowMultipleCallbacks; /*  Multiple Callbacks */
+         Status = ObInsertObject (         /*  Create the object */
+                     Callback,
+                     NULL,
+                     FILE_READ_DATA,
+                     0,
+                     NULL,
+                     &Handle );
+      }
+   }
+   if(NT_SUCCESS(Status))
+   {
+
+      /* Get a pointer to the new object from the handle we just got */
+      Status = ObReferenceObjectByHandle (
+                  Handle,
+                  0,
+                  ExCallbackObjectType,
+                  KernelMode,
+                  (PVOID)&Callback,
+                  NULL
+               );
+      /* Close the Handle, since we now have the pointer */
+      ZwClose(Handle);
+   }
+
+   /* Everything went fine, so return a pointer to the Object */
+   if (NT_SUCCESS(Status))
+   {
+      *CallbackObject = (PCALLBACK_OBJECT)Callback;
+   }
+   return Status;
 }
 
 /*
- * @unimplemented
+ * ExNotifyCallback
+ *
+ * FUNCTION:
+ * Calls a function pointer (a registered callback)
+ *
+ * ARGUMENTS:
+ * CallbackObject - Which callback to call
+ * Argument1 - Pointer/data to send to callback function
+ * Argument2 - Pointer/data to send to callback function
+ *
+ * RETURNS:
+ * Nothing
+ *
+ * @implemented
  */
 VOID
 STDCALL
-ExNotifyCallback (
-       IN      PCALLBACK_OBJECT        CallbackObject,
-       IN      PVOID   Argument1,
-       IN      PVOID   Argument2
-       )
+ExNotifyCallback(
+   IN PCALLBACK_OBJECT OpaqueCallbackObject,
+   IN PVOID Argument1,
+   IN PVOID Argument2
+)
 {
-       return;
+   PINT_CALLBACK_OBJECT    CallbackObject = (PINT_CALLBACK_OBJECT)OpaqueCallbackObject;
+   PLIST_ENTRY             RegisteredCallbacks;
+   PCALLBACK_REGISTRATION  CallbackRegistration;
+   KIRQL                   OldIrql;
+
+   /* Acquire the Lock */
+   OldIrql = KfAcquireSpinLock(&CallbackObject->Lock);
+
+   /* Enumerate through all the registered functions */
+   for (RegisteredCallbacks = CallbackObject->RegisteredCallbacks.Flink;
+         RegisteredCallbacks != &CallbackObject->RegisteredCallbacks;
+         RegisteredCallbacks = RegisteredCallbacks->Flink)
+   {
+
+      /* Get a pointer to a Callback Registration from the List Entries */
+      CallbackRegistration = CONTAINING_RECORD ( RegisteredCallbacks,
+                             CALLBACK_REGISTRATION,
+                             RegisteredCallbacks);
+
+      /* Don't bother doing Callback Notification if it's pending to be deleted */
+      if (!CallbackRegistration->PendingDeletion)
+      {
+
+         /* Mark the Callback in use, so it won't get deleted while we are calling it */
+         CallbackRegistration->InUse += 1;
+
+         /* Release the Spinlock before making the call */
+         KfReleaseSpinLock(&CallbackObject->Lock, OldIrql);
+
+         /* Call the Registered Function */
+         CallbackRegistration->CallbackFunction (
+            CallbackRegistration->CallbackContext,
+            Argument1,
+            Argument2
+         );
+
+         /* Get SpinLock back */
+         OldIrql = KfAcquireSpinLock(&CallbackObject->Lock);
+
+         /* We are not in use anymore */
+         CallbackRegistration->InUse -= 1;
+
+         /* If another instance of this function isn't running and deletion is pending, signal the event */
+         if (CallbackRegistration->PendingDeletion  && CallbackRegistration->InUse == 0)
+         {
+            KeSetEvent(&ExpCallbackEvent, 0, FALSE);
+         }
+      }
+   }
+   /* Unsynchronize and release the Callback Object */
+   KfReleaseSpinLock(&CallbackObject->Lock, OldIrql);
 }
 
 /*
- * @unimplemented
+ * ExRegisterCallback
+ *
+ * FUNCTION:
+ * Allows a function to associate a callback pointer (Function) 
+ *  to a created Callback object
+ *
+ * ARGUMENTS:
+ * CallbackObject = The Object Created with ExCreateCallBack
+ * CallBackFunction = Pointer to the function to be called back
+ * CallBackContext = Block of memory that can contain user-data
+ *  which will be passed on to the callback
+ *
+ * RETURNS:
+ * A handle to a Callback Registration Structure (MSDN Documentation)
+ *
+ * @implemented
  */
 PVOID
 STDCALL
-ExRegisterCallback (
-       IN      PCALLBACK_OBJECT        CallbackObject,
-       IN      PCALLBACK_FUNCTION      CallbackFunction,
-       IN      PVOID                   CallbackContext
-       )
+ExRegisterCallback(
+   IN PCALLBACK_OBJECT OpaqueCallbackObject,
+   IN PCALLBACK_FUNCTION CallbackFunction,
+   IN PVOID CallbackContext
+)
 {
-       return NULL;
+   PINT_CALLBACK_OBJECT CallbackObject = (PINT_CALLBACK_OBJECT)OpaqueCallbackObject;
+   PCALLBACK_REGISTRATION  CallbackRegistration = NULL;
+   KIRQL     OldIrql;
+
+   /* Create reference to Callback Object */
+   ObReferenceObject (CallbackObject);
+
+   /* Allocate memory for the structure */
+   CallbackRegistration = ExAllocatePoolWithTag(
+                             NonPagedPool,
+                             sizeof(CallbackRegistration),
+                             CALLBACK_TAG
+                          );
+   /* Fail if memory allocation failed */
+   if(!CallbackRegistration)
+   {
+      ObDereferenceObject (CallbackObject);
+      return NULL;
+   }
+
+   /* Create Callback Registration */
+   CallbackRegistration->CallbackObject = CallbackObject; /* When unregistering, drivers send a handle to the Registration, not the object...     */
+   CallbackRegistration->CallbackFunction = CallbackFunction; /* NotifyCallback uses Objects, so this needs to be here in order to call the registered functions */
+   CallbackRegistration->CallbackContext = CallbackContext; /* The documented NotifyCallback returns the Context, so we must save this somewhere    */
+
+   /* Acquire SpinLock */
+   OldIrql = KfAcquireSpinLock (&CallbackObject->Lock);
+
+   /* Add Callback if 1) No Callbacks registered or 2) Multiple Callbacks allowed */
+   if(CallbackObject->AllowMultipleCallbacks || IsListEmpty(&CallbackObject->RegisteredCallbacks))
+   {
+      InsertTailList(&CallbackObject->RegisteredCallbacks,&CallbackRegistration->RegisteredCallbacks)
+   }
+   else
+   {
+      ExFreePool(CallbackRegistration);
+      CallbackRegistration = NULL;
+   }
+
+   /* Release SpinLock */
+   KfReleaseSpinLock(&CallbackObject->Lock, OldIrql);
+
+   /* Return handle to Registration Object */
+   return (PVOID) CallbackRegistration;
 }
 
 /*
- * @unimplemented
+ * ExUnregisterCallback
+ *
+ * FUNCTION:
+ * Deregisters a CallBack
+ *
+ * ARGUMENTS:
+ * CallbackRegistration = Callback Registration Handle
+ *
+ * RETURNS:
+ * Nothing
+ *
+ * @implemented
  */
-VOID
-STDCALL
-ExUnregisterCallback (
-       IN      PVOID   CallbackRegistration
-       )
+VOID STDCALL
+ExUnregisterCallback(
+   IN PVOID CallbackRegistrationHandle
+)
 {
-       return;
+   PCALLBACK_REGISTRATION  CallbackRegistration;
+   PINT_CALLBACK_OBJECT    CallbackObject;
+   KIRQL                   OldIrql;
+
+   /* Convert Handle to valid Structure Pointer */
+   CallbackRegistration = (PCALLBACK_REGISTRATION) CallbackRegistrationHandle;
+
+   /* Get the Callback Object */
+   CallbackObject = CallbackRegistration->CallbackObject;
+
+   /* Lock the Object */
+   OldIrql = KfAcquireSpinLock (&CallbackObject->Lock);
+
+   /* We can't Delete the Callback if it's in use, because this would create a call towards a null pointer => crash */
+   while (CallbackRegistration->InUse)
+   {
+
+      /* Similarly, we also don't want to wait ages for all pending callbacks to be called */
+      CallbackRegistration->PendingDeletion = TRUE;
+
+      /* We are going to wait for the event, so the Lock isn't necessary */
+      KfReleaseSpinLock (&CallbackObject->Lock, OldIrql);
+
+      /* Make sure the event is cleared */
+      KeClearEvent (&ExpCallbackEvent);
+
+      /* Wait for the Event */
+      KeWaitForSingleObject (
+         &ExpCallbackEvent,
+         Executive,
+         KernelMode,
+         FALSE,
+         NULL
+      );
+
+      /* We need the Lock again */
+      OldIrql = KfAcquireSpinLock(&CallbackObject->Lock);
+   }
+
+   /* Remove the Callback */
+   RemoveEntryList(&CallbackRegistration->RegisteredCallbacks);
+
+   /* It's now safe to release the lock */
+   KfReleaseSpinLock(&CallbackObject->Lock, OldIrql);
+
+   /* Delete this registration */
+   ExFreePool(CallbackRegistration);
+
+   /* Remove the reference */
+   ObDereferenceObject(CallbackObject);
 }
 
 /* EOF */
diff --git a/reactos/ntoskrnl/include/internal/callbacks.h b/reactos/ntoskrnl/include/internal/callbacks.h
new file mode 100644 (file)
index 0000000..151e7f8
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *  ReactOS kernel
+ *  Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * PROJECT:         ReactOS kernel
+ * FILE:            ntoskrnl/ex/callback.h
+ * PURPOSE:         Executive callbacks Header
+ * PROGRAMMER:      Alex Ionescu (alex@relsoft.net)
+ * PORTABILITY:     Checked.
+ * UPDATE HISTORY:
+ *                  Created 30/05/04
+ */
+
+#ifndef __INCLUDE_INTERNAL_CALLBACKS_H
+#define __INCLUDE_INTERNAL_CALLBACKS_H
+
+/* Tag */
+#define CALLBACK_TAG   TAG('C','L','B','K')
+/* ROS Callback Object */
+typedef struct _INT_CALLBACK_OBJECT {
+   KSPIN_LOCK Lock;
+   LIST_ENTRY RegisteredCallbacks;
+   ULONG AllowMultipleCallbacks;
+} _INT_CALLBACK_OBJECT , *PINT_CALLBACK_OBJECT;
+
+/* Structure used to hold Callbacks */
+typedef struct _CALLBACK_REGISTRATION {
+   LIST_ENTRY RegisteredCallbacks;
+   PINT_CALLBACK_OBJECT CallbackObject;
+   PCALLBACK_FUNCTION CallbackFunction;
+   PVOID CallbackContext;
+   ULONG InUse;
+   BOOLEAN PendingDeletion;
+} CALLBACK_REGISTRATION, *PCALLBACK_REGISTRATION;
+
+/* Add 0x1 flag to differentiate */
+#define CALLBACK_ALL_ACCESS            (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x0001)
+#define CALLBACK_EXECUTE               (STANDARD_RIGHTS_EXECUTE|SYNCHRONIZE|0x0001)
+#define CALLBACK_WRITE                 (STANDARD_RIGHTS_WRITE|SYNCHRONIZE|0x0001)
+#define CALLBACK_READ                  (STANDARD_RIGHTS_READ|SYNCHRONIZE|0x0001)
+
+/* Mapping for Callback Object */
+GENERIC_MAPPING ExpCallbackMapping = 
+{
+   CALLBACK_READ,
+   CALLBACK_WRITE,
+   CALLBACK_EXECUTE,
+   CALLBACK_ALL_ACCESS
+};
+
+/* Kernel Default Callbacks */
+PINT_CALLBACK_OBJECT SetSystemTimeCallback;
+PINT_CALLBACK_OBJECT SetSystemStateCallback;
+PINT_CALLBACK_OBJECT PowerStateCallback;
+
+typedef struct {
+    PINT_CALLBACK_OBJECT *CallbackObject;
+    PWSTR Name;
+} SYSTEM_CALLBACKS;
+
+SYSTEM_CALLBACKS ExpInitializeCallback[] = {
+   {&SetSystemTimeCallback, L"\\Callback\\SetSystemTime"},
+   {&SetSystemStateCallback, L"\\Callback\\SetSystemState"},
+   {&PowerStateCallback, L"\\Callback\\PowerState"},
+   {NULL, NULL}
+};
+
+/* Callback Event */
+KEVENT ExpCallbackEvent;
+
+/* Callback Object */
+POBJECT_TYPE ExCallbackObjectType;
+
+#endif /* __INCLUDE_INTERNAL_CALLBACKS_H */
+
+/* EOF */
index be5d814..b8b65eb 100644 (file)
@@ -81,6 +81,8 @@ VOID
 ExInitializeWorkerThreads(VOID);
 VOID
 ExpInitLookasideLists(VOID);
+VOID
+ExpInitializeCallbacks(VOID);
 
 /* OTHER FUNCTIONS **********************************************************/
 
index 7e910a1..750eed4 100644 (file)
@@ -16,7 +16,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-/* $Id: main.c,v 1.187 2004/05/29 22:10:05 hbirr Exp $
+/* $Id: main.c,v 1.188 2004/06/19 08:48:01 navaraf Exp $
  *
  * PROJECT:         ReactOS kernel
  * FILE:            ntoskrnl/ke/main.c
@@ -664,6 +664,9 @@ ExpInitializeExecutive(VOID)
 
   IoInit2();
 
+  /* Initialize Callbacks before drivers */
+  ExpInitializeCallbacks();
+
   /*
    * Load boot start drivers
    */