]> git.proxmox.com Git - mirror_edk2.git/blobdiff - Vlv2TbltDevicePkg/PlatformDxe/Observable/Observable.c
Upload BSD-licensed Vlv2TbltDevicePkg and Vlv2DeviceRefCodePkg to
[mirror_edk2.git] / Vlv2TbltDevicePkg / PlatformDxe / Observable / Observable.c
diff --git a/Vlv2TbltDevicePkg/PlatformDxe/Observable/Observable.c b/Vlv2TbltDevicePkg/PlatformDxe/Observable/Observable.c
new file mode 100644 (file)
index 0000000..c67862c
--- /dev/null
@@ -0,0 +1,587 @@
+/*++\r
+  This file contains 'Framework Code' and is licensed as such\r
+  under the terms of your license agreement with Intel or your\r
+  vendor.  This file may not be modified, except as allowed by\r
+  additional terms of your license agreement.\r
+--*/\r
+/*++\r
+\r
+Copyright (c)  2010  - 2014, Intel Corporation. All rights reserved\r
+                                                                                   \r\r
+  This program and the accompanying materials are licensed and made available under\r\r
+  the terms and conditions of the BSD License that accompanies this distribution.  \r\r
+  The full text of the license may be found at                                     \r\r
+  http://opensource.org/licenses/bsd-license.php.                                  \r\r
+                                                                                   \r\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,            \r\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.    \r\r
+                                                                                   \r\r
+\r
+\r
+Module Name:\r
+\r
+  Observable.c\r
+\r
+Abstract:\r
+\r
+  The following contains all of the implementation for the Observable protocol. The\r
+  protocol uses the observer design pattern to provide a way to publish events and\r
+  to subscribe to those events so that a callback will be performed at the time of\r
+  the event. The observables and subscribers are maintained by the static tree,\r
+  mObservableDb. The difference between this protocol and the existing event protocol\r
+  that exists within the EFI framework is that this protocol allows for parameters\r
+  to be passed to the subscribed callbacks that can contain up to date context.\r
+\r
+--*/\r
+\r
+#include "Observable.h"\r
+\r
+static OBS_TREE*                mObservableDb = NULL;\r
+static EFI_HANDLE               mObservableHandle = NULL;\r
+static OBS_OBSERVABLE_PROTOCOL  mObservable = {\r
+  AddObservable,\r
+  RemoveObservable,\r
+  Subscribe,\r
+  Unsubscribe,\r
+  Publish,\r
+  RemoveAllObservables\r
+};\r
+\r
+/** Install observable protocol.\r
+ *\r
+ * Install interface and initialize the observable protocol.\r
+ *\r
+ * @param   VOID          No parameters.\r
+ *\r
+ * @return  EFI_SUCCESS   Successfully installed and initialized the protocol.\r
+ **/\r
+EFI_STATUS\r
+InitializeObservableProtocol(\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  //\r
+  // Install protocol.\r
+  //\r
+  Status = gBS->InstallProtocolInterface (\r
+                  &mObservableHandle,\r
+                  &gObservableProtocolGuid,\r
+                  EFI_NATIVE_INTERFACE,\r
+                  &mObservable\r
+                  );\r
+\r
+  return Status;\r
+}\r
+\r
+/** Deletes a subscriber\r
+ *\r
+ * This function removes the subscriber pointed to by Head.\r
+ *\r
+ * @param   OBS_TREE*     Head    Points to the current subscriber.\r
+ *\r
+ * @return  OBS_TREE*     Returns the tree after successfully removing the subscriber.\r
+ **/\r
+OBS_LEAF*\r
+DeleteSubscriber(\r
+  OBS_LEAF* Head\r
+  )\r
+{\r
+  OBS_LEAF* Temp;\r
+\r
+  if (Head) {\r
+    Temp = Head;\r
+    Head = Head->Next;\r
+    gBS->FreePool(Temp);\r
+  }\r
+\r
+  return Head;\r
+}\r
+\r
+/** Finds and deletes all subscribers\r
+ *\r
+ * This function iterates recursively through the existing subscribers and delets them all.\r
+ *\r
+ * @param   OBS_TREE*     Head    Points to the current subscriber.\r
+ *\r
+ * @return  OBS_TREE*     Returns the tree after successfully removing the subscribers.\r
+ **/\r
+OBS_LEAF*\r
+DeleteAllSubscribers(\r
+  OBS_LEAF* Head\r
+  )\r
+{\r
+  if (Head) {\r
+    if (Head->Next) {\r
+      //\r
+      // We aren't at the end of the list yet.\r
+      //\r
+      Head->Next = DeleteAllSubscribers(Head->Next);\r
+    }\r
+\r
+    //\r
+    // At the end, so delete the subscriber.\r
+    //\r
+    Head = DeleteSubscriber(Head);\r
+  }\r
+\r
+  return Head;\r
+}\r
+\r
+/** Deletes an observable\r
+ *\r
+ * This function removes the observable pointed to by Head.\r
+ *\r
+ * @param   OBS_TREE*     Head    Points to the current observable.\r
+ *\r
+ * @return  OBS_TREE*     Returns the tree after successfully removing the observable.\r
+ **/\r
+OBS_TREE*\r
+DeleteObservable(\r
+  OBS_TREE* Head\r
+  )\r
+{\r
+  OBS_TREE* Temp;\r
+\r
+  if (Head) {\r
+    Temp = Head;\r
+    Head = Head->Next;\r
+    gBS->FreePool(Temp);\r
+  }\r
+\r
+  return Head;\r
+}\r
+\r
+/** Finds and deletes all observables\r
+ *\r
+ * This function iterates recursively through the existing observables database and, starting with\r
+ * the last most observable, deletes all of its subscribers, then deletes the observable itself.\r
+ *\r
+ * @param   OBS_TREE*     Head    Points to the current observable.\r
+ *\r
+ * @return  OBS_TREE*     Returns the tree after successfully removing the observables.\r
+ **/\r
+OBS_TREE*\r
+DeleteAllObservables(\r
+  OBS_TREE* Head\r
+  )\r
+{\r
+  if (Head) {\r
+    if (Head->Next) {\r
+      //\r
+      // We aren't at the end of the list yet.\r
+      //\r
+      Head->Next = DeleteAllObservables(Head->Next);\r
+    }\r
+\r
+    //\r
+    // This is the end of the list of observables.\r
+    //\r
+    Head->Leaf = DeleteAllSubscribers(Head->Leaf);\r
+\r
+    //\r
+    // Subscribers are deleted, so now delete the observable.\r
+    //\r
+    Head = DeleteObservable(Head);\r
+  }\r
+\r
+  return Head;\r
+}\r
+\r
+/** Finds and deletes observable\r
+ *\r
+ * This function iterates recursively through the existing observable database in order to find the one\r
+ * specified by ReferenceGuid so that it can be deleted. If the requested observable is found, before it\r
+ * is deleted, all of the subscribers that are listening to this observable are deleted.\r
+ *\r
+ * @param   OBS_TREE*     Head              Points to the current observable.\r
+ *          EFI_GUID      ReferenceGuid     Corresponds to the observable that we're looking for.\r
+ *\r
+ * @return  OBS_TREE*     Returns the tree after successfully removing (or not finding) the observable.\r
+ **/\r
+OBS_TREE*\r
+FindAndDeleteObservable(\r
+  OBS_TREE* Head,\r
+  EFI_GUID  ReferenceGuid\r
+  )\r
+{\r
+  if (Head) {\r
+    if (CompareMem(&(Head->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {\r
+      //\r
+      // We found the observable. Delete all of it's subscribers, first.\r
+      //\r
+      Head->Leaf = DeleteAllSubscribers(Head->Leaf);\r
+      //\r
+      // Now we can safely remove the observable.\r
+      //\r
+      Head = DeleteObservable(Head);\r
+    } else {\r
+      //\r
+      // Not found. Keep searching.\r
+      //\r
+      Head->Next = FindAndDeleteObservable(Head->Next, ReferenceGuid);\r
+    }\r
+  }\r
+\r
+  return Head;\r
+}\r
+\r
+/** Finds and deletes subscriber\r
+ *\r
+ * This function iterates recursively through the existing subscribers that are listening to the\r
+ * observable that was found when this function was called.\r
+ *\r
+ * @param   OBS_TREE*     Head              Points to the current subscriber.\r
+ *          OBS_CALLBACK  CallbackInterface This is the subscriber that is requested be removed.\r
+ *\r
+ * @return  OBS_TREE*     Returns the tree after successfully removing (or not finding) the subscriber.\r
+ **/\r
+OBS_LEAF*\r
+_FindAndDeleteSubscriber(\r
+  OBS_LEAF*     Head,\r
+  OBS_CALLBACK  CallbackInterface\r
+  )\r
+{\r
+  if (Head) {\r
+    if (Head->Observer == CallbackInterface) {\r
+      //\r
+      // Found it. Now let's delete it.\r
+      //\r
+      Head = DeleteSubscriber(Head);\r
+    } else {\r
+      //\r
+      // Not found. Keep searching.\r
+      //\r
+      Head->Next = _FindAndDeleteSubscriber(Head->Next, CallbackInterface);\r
+    }\r
+  }\r
+\r
+  return Head;\r
+}\r
+\r
+/** Finds and deletes subscriber\r
+ *\r
+ * This function iterates recursively through the existing observables database until it either finds\r
+ * a matching guid or reaches the end of the list. After finding a match, it calls a helper function,\r
+ * _FindAndDeleteSubscriber. At this point, all responsibility for finding and deleting the subscriber\r
+ * lies on the helper function.\r
+ *\r
+ * @param   OBS_TREE*     Head              Points to the current observable.\r
+ *          EFI_GUID      ReferenceGuid     Corresponds to the observable that we're looking for.\r
+ *          OBS_CALLBACK  CallbackInterface This is the subscriber that is requested be removed.\r
+ *\r
+ * @return  OBS_TREE*     Returns the tree after successfully removing (or not finding) the subscriber.\r
+ **/\r
+OBS_TREE*\r
+FindAndDeleteSubscriber(\r
+  IN  OUT OBS_TREE*     Head,\r
+  IN      EFI_GUID      ReferenceGuid,\r
+  IN      OBS_CALLBACK  CallbackInterface\r
+  )\r
+{\r
+  if (Head) {\r
+    if (CompareMem(&(Head->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {\r
+      //\r
+      // We found the observer that matches ReferenceGuid. Find and delete the subscriber that is\r
+      // listening to it.\r
+      //\r
+      Head->Leaf = _FindAndDeleteSubscriber(Head->Leaf, CallbackInterface);\r
+    } else {\r
+      //\r
+      // Not found. Keep searching.\r
+      //\r
+      Head->Next = FindAndDeleteSubscriber(Head->Next, ReferenceGuid, CallbackInterface);\r
+    }\r
+  }\r
+\r
+  return Head;\r
+}\r
+\r
+/** Remove all observables.\r
+ *\r
+ * Remove all observable guids and all interfaces subscribed to them.\r
+ *\r
+ * @param   VOID          No parameters.\r
+ *\r
+ * @return  EFI_SUCCESS   Successfully removed all observables and subscribed interfaces.\r
+ **/\r
+EFI_STATUS\r
+EFIAPI\r
+RemoveAllObservables(\r
+  VOID\r
+  )\r
+{\r
+  mObservableDb = DeleteAllObservables(mObservableDb);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/** Subscribe an interface with an observable guid.\r
+ *\r
+ * Use this to register a callback function with a guid. The function provided by CallbackInterface will be executed\r
+ * whenever the appropriate observable instance specified by ReferenceGuid calls Publish.\r
+ *\r
+ * @param   EFI_GUID              ReferenceGuid       The observable guid that the callback interface will subscribe to.\r
+ *          OBS_CASLLBACK         CallbackInterface   A pointer to the function that is subscribing to the observable.\r
+ *\r
+ * @return  EFI_SUCCESS           Successfully subscribed the interface to the observable guid.\r
+ *          EFI_NOT_FOUND         No match could be found between the provided guid and existing observables.\r
+ *          EFI_OUT_OF_RESOURCES  Could not subscribe to this observer due to resource limitations.\r
+ *          EFI_INVALID_PARAMETER Interface is already subscribed to this observer.\r
+ **/\r
+EFI_STATUS\r
+EFIAPI\r
+Subscribe (\r
+  IN      EFI_GUID        ReferenceGuid,\r
+  IN      OBS_CALLBACK    CallbackInterface\r
+  )\r
+{\r
+  EFI_STATUS  Status    = EFI_SUCCESS;\r
+  OBS_TREE*   TempTree  = NULL;\r
+  OBS_LEAF*   Last      = NULL;\r
+  OBS_LEAF*   TempLeaf  = NULL;\r
+  OBS_LEAF*   NewLeaf   = NULL;\r
+  BOOLEAN     Found     = FALSE;\r
+\r
+  if (mObservableDb != NULL) {\r
+    //\r
+    // Find the observable guid that we're looking for.\r
+    //\r
+    for (TempTree = mObservableDb; TempTree != NULL; TempTree = TempTree->Next) {\r
+      if (CompareMem(&(TempTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {\r
+        Found = TRUE;\r
+        break;\r
+      }\r
+    }\r
+    if (Found) {\r
+      //\r
+      // Prepare to add a new leaf.\r
+      //\r
+      NewLeaf = AllocateZeroPool(sizeof(OBS_LEAF));\r
+      if (!NewLeaf) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+      } else {\r
+        NewLeaf->Next = NULL;\r
+        NewLeaf->Observer = CallbackInterface;\r
+        //\r
+        // Go to the end of the list of observers.\r
+        //\r
+        if (TempTree->Leaf != NULL) {\r
+          //\r
+          // First check to see if this is a duplicate observer.\r
+          //\r
+          Found = FALSE;\r
+          TempLeaf = TempTree->Leaf;\r
+          do {\r
+            Last = TempLeaf;\r
+            if (TempLeaf->Observer == CallbackInterface) {\r
+              //\r
+              // It is, so let's abort this process.\r
+              //\r
+              Found = TRUE;\r
+              break;\r
+            }\r
+            TempLeaf = TempLeaf->Next;\r
+          } while (TempLeaf != NULL);\r
+          TempLeaf = Last;\r
+\r
+          //\r
+          // Check for duplicates.\r
+          //\r
+          if (Found) {\r
+            gBS->FreePool(NewLeaf);\r
+            Status = EFI_INVALID_PARAMETER;\r
+          } else {\r
+            //\r
+            // At this point, TempLeaf->Next will be the end of the list.\r
+            //\r
+            TempLeaf->Next = NewLeaf;\r
+          }\r
+        } else {\r
+          //\r
+          // There are no observers listening to this guid. Start a new list.\r
+          //\r
+          TempTree->Leaf = NewLeaf;\r
+        }\r
+      }\r
+    } else {\r
+      Status = EFI_NOT_FOUND;\r
+    }\r
+  } else {\r
+    Status = EFI_NOT_FOUND;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/** Unsubscribe an interface with an observable guid.\r
+ *\r
+ * Use this to remove an interface from the callback list associated with an observable guid.\r
+ *\r
+ * @param   EFI_GUID                ReferenceGuid   The observable guid to unsubscribe the interface from.\r
+ *          OBS_NOTIFY_INTERFACE    NotifyCallback  A pointer to the interface that is being unsubscribed.\r
+ *\r
+ * @return  EFI_SUCCESS           Successfully unsubscribed the interface from the observable guid.\r
+ **/\r
+EFI_STATUS\r
+EFIAPI\r
+Unsubscribe (\r
+  IN      EFI_GUID        ReferenceGuid,\r
+  IN      OBS_CALLBACK    CallbackInterface\r
+  )\r
+{\r
+  mObservableDb = FindAndDeleteSubscriber(mObservableDb, ReferenceGuid, CallbackInterface);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/** Notify observing functions.\r
+ *\r
+ * Use this to notify all functions who are subscribed to the guid specified by ReferenceGuid.\r
+ *\r
+ * @param   EFI_GUID          ReferenceGuid   The observable guid that contains the the list of interfaces to be notified.\r
+ *          VOID*             Data            Parameter context to be passed to the notification function.\r
+ *\r
+ * @return  EFI_SUCCESS       Successfully notified all observers listening to this guid.\r
+ *          EFI_NOT_FOUND     No match could be found between the provided guid and existing observables.\r
+ **/\r
+EFI_STATUS\r
+EFIAPI\r
+Publish (\r
+  IN      EFI_GUID                  ReferenceGuid,\r
+  IN  OUT VOID*                     Data\r
+  )\r
+{\r
+  EFI_STATUS  Status    = EFI_SUCCESS;\r
+  OBS_TREE*   TempTree  = NULL;\r
+  OBS_LEAF*   TempLeaf  = NULL;\r
+  BOOLEAN     Found     = FALSE;\r
+\r
+  if (mObservableDb != NULL) {\r
+    //\r
+    // Find the observable guid that we're looking for.\r
+    //\r
+    for (TempTree = mObservableDb; TempTree != NULL; TempTree = TempTree->Next) {\r
+      if (CompareMem(&(TempTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {\r
+        Found = TRUE;\r
+        break;\r
+      }\r
+    }\r
+    if (Found) {\r
+      //\r
+      // Notify every listener by performing each provided callback.\r
+      //\r
+      for (TempLeaf = TempTree->Leaf; TempLeaf != NULL; TempLeaf = TempLeaf->Next) {\r
+        if (TempLeaf->Observer != NULL) {\r
+          //\r
+          // Execute the callback.\r
+          //\r
+          TempLeaf->Observer(Data);\r
+        }\r
+      }\r
+    } else {\r
+      Status = EFI_NOT_FOUND;\r
+    }\r
+  } else {\r
+    Status = EFI_NOT_FOUND;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/** Creates a new observable.\r
+ *\r
+ * Create a new observable that can be observed with the use of Subscribe function.\r
+ *\r
+ * @param   EFI_GUID              ReferenceGuid   The observable guid to add.\r
+ *\r
+ * @return  EFI_SUCCESS           Successfully added observable.\r
+ *          EFI_INVALID_PARAMETER Observable already exists.\r
+ **/\r
+EFI_STATUS\r
+EFIAPI\r
+AddObservable (\r
+  IN      EFI_GUID                  ReferenceGuid\r
+  )\r
+{\r
+  EFI_STATUS  Status    = EFI_SUCCESS;\r
+  OBS_TREE*   TempTree  = NULL;\r
+  OBS_TREE*   Last      = NULL;\r
+  OBS_TREE*   NewTree   = NULL;\r
+  BOOLEAN     Found     = FALSE;\r
+\r
+  if (mObservableDb != NULL) {\r
+    if (mObservableDb->Next != NULL) {\r
+      //\r
+      // Iterate to the end of the observable list while checking to see if we aren't creating a duplicate.\r
+      //\r
+      TempTree = mObservableDb->Next;\r
+      do {\r
+        Last = TempTree;\r
+        if (CompareMem(&(TempTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {\r
+          Found = TRUE;\r
+          break;\r
+        }\r
+        TempTree = TempTree->Next;\r
+      } while (TempTree != NULL);\r
+      TempTree = Last;\r
+    } else {\r
+      TempTree = mObservableDb;\r
+    }\r
+    if (Found) {\r
+      //\r
+      // Duplicate, so reject the parameter.\r
+      //\r
+      Status = EFI_INVALID_PARAMETER;\r
+    } else {\r
+      //\r
+      // TempTree->Next is our target. Prepare to add a new tree link.\r
+      //\r
+      NewTree = AllocateZeroPool(sizeof(OBS_TREE));\r
+      if (NewTree) {\r
+        NewTree->Next = NULL;\r
+        NewTree->Leaf = NULL;\r
+        CopyMem(&(NewTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid));\r
+        TempTree->Next = NewTree;\r
+      } else {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+      }\r
+    }\r
+  } else {\r
+    //\r
+    // mObservableDb has not been created yet. Let's do that.\r
+    //\r
+    NewTree = AllocateZeroPool(sizeof(OBS_TREE));\r
+    if (NewTree) {\r
+      NewTree->Next = NULL;\r
+      NewTree->Leaf = NULL;\r
+      CopyMem(&(NewTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid));\r
+      mObservableDb = NewTree;\r
+    } else {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/** Remove an observable.\r
+ *\r
+ * Remove an observable so that it can no longer be subscribed to. In addition, unsubscribe any functions\r
+ * that are subscribed to this guid.\r
+ *\r
+ * @param   EFI_GUID              ReferenceGuid   The observable guid to remove.\r
+ *\r
+ * @return  EFI_SUCCESS           Successfully removed observable.\r
+ **/\r
+EFI_STATUS\r
+EFIAPI\r
+RemoveObservable (\r
+  IN      EFI_GUID        ReferenceGuid\r
+  )\r
+{\r
+  mObservableDb = FindAndDeleteObservable(mObservableDb, ReferenceGuid);\r
+\r
+  return EFI_SUCCESS;\r
+}\r