Home | History | Annotate | Download | only in Observable
      1 /*++
      2   This file contains 'Framework Code' and is licensed as such
      3   under the terms of your license agreement with Intel or your
      4   vendor.  This file may not be modified, except as allowed by
      5   additional terms of your license agreement.
      6 --*/
      7 /*++
      8 
      9 Copyright (c)  2010  - 2014, Intel Corporation. All rights reserved
     10 
     11   This program and the accompanying materials are licensed and made available under
     13   the terms and conditions of the BSD License that accompanies this distribution.
     15   The full text of the license may be found at
     17   http://opensource.org/licenses/bsd-license.php.
     19 
     21   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     23   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     25 
     27 
     29 
     30 Module Name:
     31 
     32   Observable.c
     33 
     34 Abstract:
     35 
     36   The following contains all of the implementation for the Observable protocol. The
     37   protocol uses the observer design pattern to provide a way to publish events and
     38   to subscribe to those events so that a callback will be performed at the time of
     39   the event. The observables and subscribers are maintained by the static tree,
     40   mObservableDb. The difference between this protocol and the existing event protocol
     41   that exists within the EFI framework is that this protocol allows for parameters
     42   to be passed to the subscribed callbacks that can contain up to date context.
     43 
     44 --*/
     45 
     46 #include "Observable.h"
     47 
     48 static OBS_TREE*                mObservableDb = NULL;
     49 static EFI_HANDLE               mObservableHandle = NULL;
     50 static OBS_OBSERVABLE_PROTOCOL  mObservable = {
     51   AddObservable,
     52   RemoveObservable,
     53   Subscribe,
     54   Unsubscribe,
     55   Publish,
     56   RemoveAllObservables
     57 };
     58 
     59 /** Install observable protocol.
     60  *
     61  * Install interface and initialize the observable protocol.
     62  *
     63  * @param   VOID          No parameters.
     64  *
     65  * @return  EFI_SUCCESS   Successfully installed and initialized the protocol.
     66  **/
     67 EFI_STATUS
     68 InitializeObservableProtocol(
     69   VOID
     70   )
     71 {
     72   EFI_STATUS  Status;
     73 
     74   //
     75   // Install protocol.
     76   //
     77   Status = gBS->InstallProtocolInterface (
     78                   &mObservableHandle,
     79                   &gObservableProtocolGuid,
     80                   EFI_NATIVE_INTERFACE,
     81                   &mObservable
     82                   );
     83 
     84   return Status;
     85 }
     86 
     87 /** Deletes a subscriber
     88  *
     89  * This function removes the subscriber pointed to by Head.
     90  *
     91  * @param   OBS_TREE*     Head    Points to the current subscriber.
     92  *
     93  * @return  OBS_TREE*     Returns the tree after successfully removing the subscriber.
     94  **/
     95 OBS_LEAF*
     96 DeleteSubscriber(
     97   OBS_LEAF* Head
     98   )
     99 {
    100   OBS_LEAF* Temp;
    101 
    102   if (Head) {
    103     Temp = Head;
    104     Head = Head->Next;
    105     gBS->FreePool(Temp);
    106   }
    107 
    108   return Head;
    109 }
    110 
    111 /** Finds and deletes all subscribers
    112  *
    113  * This function iterates recursively through the existing subscribers and delets them all.
    114  *
    115  * @param   OBS_TREE*     Head    Points to the current subscriber.
    116  *
    117  * @return  OBS_TREE*     Returns the tree after successfully removing the subscribers.
    118  **/
    119 OBS_LEAF*
    120 DeleteAllSubscribers(
    121   OBS_LEAF* Head
    122   )
    123 {
    124   if (Head) {
    125     if (Head->Next) {
    126       //
    127       // We aren't at the end of the list yet.
    128       //
    129       Head->Next = DeleteAllSubscribers(Head->Next);
    130     }
    131 
    132     //
    133     // At the end, so delete the subscriber.
    134     //
    135     Head = DeleteSubscriber(Head);
    136   }
    137 
    138   return Head;
    139 }
    140 
    141 /** Deletes an observable
    142  *
    143  * This function removes the observable pointed to by Head.
    144  *
    145  * @param   OBS_TREE*     Head    Points to the current observable.
    146  *
    147  * @return  OBS_TREE*     Returns the tree after successfully removing the observable.
    148  **/
    149 OBS_TREE*
    150 DeleteObservable(
    151   OBS_TREE* Head
    152   )
    153 {
    154   OBS_TREE* Temp;
    155 
    156   if (Head) {
    157     Temp = Head;
    158     Head = Head->Next;
    159     gBS->FreePool(Temp);
    160   }
    161 
    162   return Head;
    163 }
    164 
    165 /** Finds and deletes all observables
    166  *
    167  * This function iterates recursively through the existing observables database and, starting with
    168  * the last most observable, deletes all of its subscribers, then deletes the observable itself.
    169  *
    170  * @param   OBS_TREE*     Head    Points to the current observable.
    171  *
    172  * @return  OBS_TREE*     Returns the tree after successfully removing the observables.
    173  **/
    174 OBS_TREE*
    175 DeleteAllObservables(
    176   OBS_TREE* Head
    177   )
    178 {
    179   if (Head) {
    180     if (Head->Next) {
    181       //
    182       // We aren't at the end of the list yet.
    183       //
    184       Head->Next = DeleteAllObservables(Head->Next);
    185     }
    186 
    187     //
    188     // This is the end of the list of observables.
    189     //
    190     Head->Leaf = DeleteAllSubscribers(Head->Leaf);
    191 
    192     //
    193     // Subscribers are deleted, so now delete the observable.
    194     //
    195     Head = DeleteObservable(Head);
    196   }
    197 
    198   return Head;
    199 }
    200 
    201 /** Finds and deletes observable
    202  *
    203  * This function iterates recursively through the existing observable database in order to find the one
    204  * specified by ReferenceGuid so that it can be deleted. If the requested observable is found, before it
    205  * is deleted, all of the subscribers that are listening to this observable are deleted.
    206  *
    207  * @param   OBS_TREE*     Head              Points to the current observable.
    208  *          EFI_GUID      ReferenceGuid     Corresponds to the observable that we're looking for.
    209  *
    210  * @return  OBS_TREE*     Returns the tree after successfully removing (or not finding) the observable.
    211  **/
    212 OBS_TREE*
    213 FindAndDeleteObservable(
    214   OBS_TREE* Head,
    215   EFI_GUID  ReferenceGuid
    216   )
    217 {
    218   if (Head) {
    219     if (CompareMem(&(Head->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {
    220       //
    221       // We found the observable. Delete all of it's subscribers, first.
    222       //
    223       Head->Leaf = DeleteAllSubscribers(Head->Leaf);
    224       //
    225       // Now we can safely remove the observable.
    226       //
    227       Head = DeleteObservable(Head);
    228     } else {
    229       //
    230       // Not found. Keep searching.
    231       //
    232       Head->Next = FindAndDeleteObservable(Head->Next, ReferenceGuid);
    233     }
    234   }
    235 
    236   return Head;
    237 }
    238 
    239 /** Finds and deletes subscriber
    240  *
    241  * This function iterates recursively through the existing subscribers that are listening to the
    242  * observable that was found when this function was called.
    243  *
    244  * @param   OBS_TREE*     Head              Points to the current subscriber.
    245  *          OBS_CALLBACK  CallbackInterface This is the subscriber that is requested be removed.
    246  *
    247  * @return  OBS_TREE*     Returns the tree after successfully removing (or not finding) the subscriber.
    248  **/
    249 OBS_LEAF*
    250 _FindAndDeleteSubscriber(
    251   OBS_LEAF*     Head,
    252   OBS_CALLBACK  CallbackInterface
    253   )
    254 {
    255   if (Head) {
    256     if (Head->Observer == CallbackInterface) {
    257       //
    258       // Found it. Now let's delete it.
    259       //
    260       Head = DeleteSubscriber(Head);
    261     } else {
    262       //
    263       // Not found. Keep searching.
    264       //
    265       Head->Next = _FindAndDeleteSubscriber(Head->Next, CallbackInterface);
    266     }
    267   }
    268 
    269   return Head;
    270 }
    271 
    272 /** Finds and deletes subscriber
    273  *
    274  * This function iterates recursively through the existing observables database until it either finds
    275  * a matching guid or reaches the end of the list. After finding a match, it calls a helper function,
    276  * _FindAndDeleteSubscriber. At this point, all responsibility for finding and deleting the subscriber
    277  * lies on the helper function.
    278  *
    279  * @param   OBS_TREE*     Head              Points to the current observable.
    280  *          EFI_GUID      ReferenceGuid     Corresponds to the observable that we're looking for.
    281  *          OBS_CALLBACK  CallbackInterface This is the subscriber that is requested be removed.
    282  *
    283  * @return  OBS_TREE*     Returns the tree after successfully removing (or not finding) the subscriber.
    284  **/
    285 OBS_TREE*
    286 FindAndDeleteSubscriber(
    287   IN  OUT OBS_TREE*     Head,
    288   IN      EFI_GUID      ReferenceGuid,
    289   IN      OBS_CALLBACK  CallbackInterface
    290   )
    291 {
    292   if (Head) {
    293     if (CompareMem(&(Head->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {
    294       //
    295       // We found the observer that matches ReferenceGuid. Find and delete the subscriber that is
    296       // listening to it.
    297       //
    298       Head->Leaf = _FindAndDeleteSubscriber(Head->Leaf, CallbackInterface);
    299     } else {
    300       //
    301       // Not found. Keep searching.
    302       //
    303       Head->Next = FindAndDeleteSubscriber(Head->Next, ReferenceGuid, CallbackInterface);
    304     }
    305   }
    306 
    307   return Head;
    308 }
    309 
    310 /** Remove all observables.
    311  *
    312  * Remove all observable guids and all interfaces subscribed to them.
    313  *
    314  * @param   VOID          No parameters.
    315  *
    316  * @return  EFI_SUCCESS   Successfully removed all observables and subscribed interfaces.
    317  **/
    318 EFI_STATUS
    319 EFIAPI
    320 RemoveAllObservables(
    321   VOID
    322   )
    323 {
    324   mObservableDb = DeleteAllObservables(mObservableDb);
    325 
    326   return EFI_SUCCESS;
    327 }
    328 
    329 /** Subscribe an interface with an observable guid.
    330  *
    331  * Use this to register a callback function with a guid. The function provided by CallbackInterface will be executed
    332  * whenever the appropriate observable instance specified by ReferenceGuid calls Publish.
    333  *
    334  * @param   EFI_GUID              ReferenceGuid       The observable guid that the callback interface will subscribe to.
    335  *          OBS_CASLLBACK         CallbackInterface   A pointer to the function that is subscribing to the observable.
    336  *
    337  * @return  EFI_SUCCESS           Successfully subscribed the interface to the observable guid.
    338  *          EFI_NOT_FOUND         No match could be found between the provided guid and existing observables.
    339  *          EFI_OUT_OF_RESOURCES  Could not subscribe to this observer due to resource limitations.
    340  *          EFI_INVALID_PARAMETER Interface is already subscribed to this observer.
    341  **/
    342 EFI_STATUS
    343 EFIAPI
    344 Subscribe (
    345   IN      EFI_GUID        ReferenceGuid,
    346   IN      OBS_CALLBACK    CallbackInterface
    347   )
    348 {
    349   EFI_STATUS  Status    = EFI_SUCCESS;
    350   OBS_TREE*   TempTree  = NULL;
    351   OBS_LEAF*   Last      = NULL;
    352   OBS_LEAF*   TempLeaf  = NULL;
    353   OBS_LEAF*   NewLeaf   = NULL;
    354   BOOLEAN     Found     = FALSE;
    355 
    356   if (mObservableDb != NULL) {
    357     //
    358     // Find the observable guid that we're looking for.
    359     //
    360     for (TempTree = mObservableDb; TempTree != NULL; TempTree = TempTree->Next) {
    361       if (CompareMem(&(TempTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {
    362         Found = TRUE;
    363         break;
    364       }
    365     }
    366     if (Found) {
    367       //
    368       // Prepare to add a new leaf.
    369       //
    370       NewLeaf = AllocateZeroPool(sizeof(OBS_LEAF));
    371       if (!NewLeaf) {
    372         Status = EFI_OUT_OF_RESOURCES;
    373       } else {
    374         NewLeaf->Next = NULL;
    375         NewLeaf->Observer = CallbackInterface;
    376         //
    377         // Go to the end of the list of observers.
    378         //
    379         if (TempTree->Leaf != NULL) {
    380           //
    381           // First check to see if this is a duplicate observer.
    382           //
    383           Found = FALSE;
    384           TempLeaf = TempTree->Leaf;
    385           do {
    386             Last = TempLeaf;
    387             if (TempLeaf->Observer == CallbackInterface) {
    388               //
    389               // It is, so let's abort this process.
    390               //
    391               Found = TRUE;
    392               break;
    393             }
    394             TempLeaf = TempLeaf->Next;
    395           } while (TempLeaf != NULL);
    396           TempLeaf = Last;
    397 
    398           //
    399           // Check for duplicates.
    400           //
    401           if (Found) {
    402             gBS->FreePool(NewLeaf);
    403             Status = EFI_INVALID_PARAMETER;
    404           } else {
    405             //
    406             // At this point, TempLeaf->Next will be the end of the list.
    407             //
    408             TempLeaf->Next = NewLeaf;
    409           }
    410         } else {
    411           //
    412           // There are no observers listening to this guid. Start a new list.
    413           //
    414           TempTree->Leaf = NewLeaf;
    415         }
    416       }
    417     } else {
    418       Status = EFI_NOT_FOUND;
    419     }
    420   } else {
    421     Status = EFI_NOT_FOUND;
    422   }
    423 
    424   return Status;
    425 }
    426 
    427 /** Unsubscribe an interface with an observable guid.
    428  *
    429  * Use this to remove an interface from the callback list associated with an observable guid.
    430  *
    431  * @param   EFI_GUID                ReferenceGuid   The observable guid to unsubscribe the interface from.
    432  *          OBS_NOTIFY_INTERFACE    NotifyCallback  A pointer to the interface that is being unsubscribed.
    433  *
    434  * @return  EFI_SUCCESS           Successfully unsubscribed the interface from the observable guid.
    435  **/
    436 EFI_STATUS
    437 EFIAPI
    438 Unsubscribe (
    439   IN      EFI_GUID        ReferenceGuid,
    440   IN      OBS_CALLBACK    CallbackInterface
    441   )
    442 {
    443   mObservableDb = FindAndDeleteSubscriber(mObservableDb, ReferenceGuid, CallbackInterface);
    444 
    445   return EFI_SUCCESS;
    446 }
    447 
    448 /** Notify observing functions.
    449  *
    450  * Use this to notify all functions who are subscribed to the guid specified by ReferenceGuid.
    451  *
    452  * @param   EFI_GUID          ReferenceGuid   The observable guid that contains the the list of interfaces to be notified.
    453  *          VOID*             Data            Parameter context to be passed to the notification function.
    454  *
    455  * @return  EFI_SUCCESS       Successfully notified all observers listening to this guid.
    456  *          EFI_NOT_FOUND     No match could be found between the provided guid and existing observables.
    457  **/
    458 EFI_STATUS
    459 EFIAPI
    460 Publish (
    461   IN      EFI_GUID                  ReferenceGuid,
    462   IN  OUT VOID*                     Data
    463   )
    464 {
    465   EFI_STATUS  Status    = EFI_SUCCESS;
    466   OBS_TREE*   TempTree  = NULL;
    467   OBS_LEAF*   TempLeaf  = NULL;
    468   BOOLEAN     Found     = FALSE;
    469 
    470   if (mObservableDb != NULL) {
    471     //
    472     // Find the observable guid that we're looking for.
    473     //
    474     for (TempTree = mObservableDb; TempTree != NULL; TempTree = TempTree->Next) {
    475       if (CompareMem(&(TempTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {
    476         Found = TRUE;
    477         break;
    478       }
    479     }
    480     if (Found) {
    481       //
    482       // Notify every listener by performing each provided callback.
    483       //
    484       for (TempLeaf = TempTree->Leaf; TempLeaf != NULL; TempLeaf = TempLeaf->Next) {
    485         if (TempLeaf->Observer != NULL) {
    486           //
    487           // Execute the callback.
    488           //
    489           TempLeaf->Observer(Data);
    490         }
    491       }
    492     } else {
    493       Status = EFI_NOT_FOUND;
    494     }
    495   } else {
    496     Status = EFI_NOT_FOUND;
    497   }
    498 
    499   return Status;
    500 }
    501 
    502 /** Creates a new observable.
    503  *
    504  * Create a new observable that can be observed with the use of Subscribe function.
    505  *
    506  * @param   EFI_GUID              ReferenceGuid   The observable guid to add.
    507  *
    508  * @return  EFI_SUCCESS           Successfully added observable.
    509  *          EFI_INVALID_PARAMETER Observable already exists.
    510  **/
    511 EFI_STATUS
    512 EFIAPI
    513 AddObservable (
    514   IN      EFI_GUID                  ReferenceGuid
    515   )
    516 {
    517   EFI_STATUS  Status    = EFI_SUCCESS;
    518   OBS_TREE*   TempTree  = NULL;
    519   OBS_TREE*   Last      = NULL;
    520   OBS_TREE*   NewTree   = NULL;
    521   BOOLEAN     Found     = FALSE;
    522 
    523   if (mObservableDb != NULL) {
    524     if (mObservableDb->Next != NULL) {
    525       //
    526       // Iterate to the end of the observable list while checking to see if we aren't creating a duplicate.
    527       //
    528       TempTree = mObservableDb->Next;
    529       do {
    530         Last = TempTree;
    531         if (CompareMem(&(TempTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {
    532           Found = TRUE;
    533           break;
    534         }
    535         TempTree = TempTree->Next;
    536       } while (TempTree != NULL);
    537       TempTree = Last;
    538     } else {
    539       TempTree = mObservableDb;
    540     }
    541     if (Found) {
    542       //
    543       // Duplicate, so reject the parameter.
    544       //
    545       Status = EFI_INVALID_PARAMETER;
    546     } else {
    547       //
    548       // TempTree->Next is our target. Prepare to add a new tree link.
    549       //
    550       NewTree = AllocateZeroPool(sizeof(OBS_TREE));
    551       if (NewTree) {
    552         NewTree->Next = NULL;
    553         NewTree->Leaf = NULL;
    554         CopyMem(&(NewTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid));
    555         TempTree->Next = NewTree;
    556       } else {
    557         Status = EFI_OUT_OF_RESOURCES;
    558       }
    559     }
    560   } else {
    561     //
    562     // mObservableDb has not been created yet. Let's do that.
    563     //
    564     NewTree = AllocateZeroPool(sizeof(OBS_TREE));
    565     if (NewTree) {
    566       NewTree->Next = NULL;
    567       NewTree->Leaf = NULL;
    568       CopyMem(&(NewTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid));
    569       mObservableDb = NewTree;
    570     } else {
    571       Status = EFI_OUT_OF_RESOURCES;
    572     }
    573   }
    574 
    575   return Status;
    576 }
    577 
    578 /** Remove an observable.
    579  *
    580  * Remove an observable so that it can no longer be subscribed to. In addition, unsubscribe any functions
    581  * that are subscribed to this guid.
    582  *
    583  * @param   EFI_GUID              ReferenceGuid   The observable guid to remove.
    584  *
    585  * @return  EFI_SUCCESS           Successfully removed observable.
    586  **/
    587 EFI_STATUS
    588 EFIAPI
    589 RemoveObservable (
    590   IN      EFI_GUID        ReferenceGuid
    591   )
    592 {
    593   mObservableDb = FindAndDeleteObservable(mObservableDb, ReferenceGuid);
    594 
    595   return EFI_SUCCESS;
    596 }
    597