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