--- /dev/null
+/*++\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