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
12 This program and the accompanying materials are licensed and made available under
13
14 the terms and conditions of the BSD License that accompanies this distribution.
15
16 The full text of the license may be found at
17
18 http://opensource.org/licenses/bsd-license.php.
19
20
21
22 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
23
24 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
25
26
27
28
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
InitializeObservableProtocol(VOID)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
DeleteSubscriber(OBS_LEAF * Head)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
DeleteAllSubscribers(OBS_LEAF * Head)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
DeleteObservable(OBS_TREE * Head)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
DeleteAllObservables(OBS_TREE * Head)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
FindAndDeleteObservable(OBS_TREE * Head,EFI_GUID ReferenceGuid)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 *
_FindAndDeleteSubscriber(OBS_LEAF * Head,OBS_CALLBACK CallbackInterface)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
FindAndDeleteSubscriber(IN OUT OBS_TREE * Head,IN EFI_GUID ReferenceGuid,IN OBS_CALLBACK CallbackInterface)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.
RemoveAllObservables(VOID)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.
Subscribe(IN EFI_GUID ReferenceGuid,IN OBS_CALLBACK CallbackInterface)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 *
Unsubscribe(IN EFI_GUID ReferenceGuid,IN OBS_CALLBACK CallbackInterface)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.
Publish(IN EFI_GUID ReferenceGuid,IN OUT VOID * Data)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 *
AddObservable(IN EFI_GUID ReferenceGuid)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 *
RemoveObservable(IN EFI_GUID ReferenceGuid)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