谢谢各位的帮助,针对这个问题,我下面做一个总结,以便后来者参考,不对的地方还请各位及时指出,以免我个人毒害后来者
EFI_EVENT
EfiLibCreateProtocolNotifyEvent (
IN EFI_GUID *ProtocolGuid,
IN EFI_TPL NotifyTpl,
IN EFI_EVENT_NOTIFY NotifyFunction,
IN VOID *NotifyContext,
OUT VOID *Registration
)
{
EFI_STATUS Status;
EFI_EVENT Event;
//
// Create the event
//
Status = gBS->CreateEvent (
EFI_EVENT_NOTIFY_SIGNAL,
NotifyTpl,
NotifyFunction,
NotifyContext,
&Event
);
ASSERT (!EFI_ERROR (Status));
//
// Register for protocol notifactions on this event
//
Status = gBS->RegisterProtocolNotify (
ProtocolGuid,
Event,
Registration
);
ASSERT (!EFI_ERROR (Status));
//
// Kick the event so we will perform an initial pass of
// current installed drivers
//
gBS->SignalEvent (Event);
return Event;
}
调用这个Function时,我们不知道对应的ProtocolGuid是否已经在GUIDDatabase中,下面分两种情况讨论:
1.这个Event被Create并RegisterProtocolNotify时,对应的ProtocolGuid已经被Install
一旦SignalEvent (Event),毫无疑问,这个NotifyFunction会被立即执行(具体代码参考EDK的
CoreDispatchEventNotifies的Event->NotifyFunction (Event, Event->NotifyContext)语句。
2.这个Event被Create并RegisterProtocolNotify时,对应的ProtocolGuid没有被Install
这个时候使用SignalEvent (Event),NotifyFunction也会被Dispatch并予以执行,这个时候问题就来了,一旦这个对应的ProtocolGuid在后面被Install,这个NotifyFunction岂不是又要被Dispatch并且执行?为了避免这个NotifyFunction被执行两遍,在NotifyFunction里面就需要做这么一件事:
VOID
EFIAPI
NotifyFunction (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_EVENT XXEvent;
……
Status = gBS->LocateProtocol (
&rotocolGuid,
NULL,
&XXEvent
);
if (EFI_ERROR (Status)) {
return ;
}
……
}
目的很明显,就是为了在第二种情况下,第一遍Dispatch到该NotifyFunction时直接返回。等到对应的ProtocolGuid被Install到GUIDDatabase的时候,第二次Dispatch到该NotifyFunction并真正得到执行。也就是Keashine在上楼说的"因为不确定Protocol到底是不是已经装好了。如果还没装的话,Event机制就会起作用了。在这种情况下NotificationFunction会被执行两次,第一次是在EfiLibCreateProtocolNotifyEvent中调用,失败,第二次在特定的Protocol装好以后回调一次,成功。"
现在再来分析EfiLibNamedEventListen和EfiLibCreateProtocolNotifyEvent就简单了。
EFI_STATUS
EfiLibNamedEventListen (
IN EFI_GUID * Name,
IN EFI_TPL NotifyTpl,
IN EFI_EVENT_NOTIFY NotifyFunction,
IN VOID *NotifyContext
)
{
EFI_STATUS Status;
EFI_EVENT Event;
VOID *RegistrationLocal;
//
// Create event
//
Status = gBS->CreateEvent (
EFI_EVENT_NOTIFY_SIGNAL,
NotifyTpl,
NotifyFunction,
NotifyContext,
&Event
);
ASSERT_EFI_ERROR (Status);
Status = gBS->RegisterProtocolNotify (
Name,
Event,
&RegistrationLocal
);
ASSERT_EFI_ERROR (Status);
return EFI_SUCCESS;
}
这个函数就是一个监听作用,一旦对应的Name,也就是ProtocolGuid被Install,对应的NotifyFunction就会被Dispatch并执行。这个函数用于上述的第二种情况,并且对User来说是已知的。
这里要注意系统中安装某个Protocol的多个Instance是可能的,这种情况下,Notification函数会被多次调用到(包括Signal Evnet触发的第一次)。Notification函数里可以:
用LocateHandle()/LocateHandleBuffer()配上ByRegisterNotify searchtype和RegisterProtocolNotify()返回的Registration值去找上一次Notification被触发以来的新安装Protocol interface的handle。一次返回一个Handle,所以要用循环枚举。
或者用LocateProtocol() 配上RegisterProtocolNotify()返回的Registration值去找上一次Notification被触发以来新安装的Protocol interface。LocateProtocol() 一次只能返回一个protocol interface,所以要用循环枚举。
转自:http://www.biosren.com/viewthread.php?action=printable&tid=1557