关闭

IoRegisterPlugPlayNotification源码分析

标签: windows
716人阅读 评论(1) 收藏 举报
分类:

    IoRegisterPlugPlayNotification函数的作用是为指定guid的设备注册一个回调函数,当进入设备驱动并调用IoSetDeviceInterfaceState使能设备访问接口后,让系统调用之前注册的回调函数,以实现通知的目的.wrk1.2源码中并没有IO管理器的实现,因此,只能退而求其次,参考ReactOS中关于IoRegisterPlugPlayNotification的实现.

    ReactOS在系统初始化阶段调用PoInitSystem初始化电源管理器时,会以

        IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
                                       0, /* The registry has not been initialized yet */
                                       (PVOID)&GUID_DEVICE_SYS_BUTTON,
                                       IopRootDeviceNode->
                                       PhysicalDeviceObject->DriverObject,
                                       PopAddRemoveSysCapsCallback,
                                       NULL,
                                       &NotificationEntry);
的形式为集成在主板按键上的调用IoRegisterPlugPlayNotification.

    参数1 EventCategoryDeviceInterfaceChange表明Pnp管理器在接到设备接口状态变化(Enable/Disable)时调用先前注册的回调函数;

    参数3 GUID_DEVICE_SYS_BUTTON是指被IoSetDeviceInterfaceState操作的接口;

    参数5和6是回调函数的入口地址和参数;

    不得不说,系统中注册回调函数都有一定的相似性,步骤可归纳为2步:1).为一个Entry分配pool内存,并用回调函数入口地址和参数Context初始化Entry,并初始化其他域(如回调条件);2).将刚分配的Entry结构以互斥的方式加入到内核中相应的队列中.IoRegisterPlugPlayNotification也是如此:

{
//分配池内存
Entry = ExAllocatePoolWithTag(
		NonPagedPool,
		sizeof(PNP_NOTIFY_ENTRY),
		TAG_PNP_NOTIFY);
//从interface获得设备的名字
Status = IoGetDeviceInterfaces(
			(LPGUID)EventCategoryData,
			NULL, /* PhysicalDeviceObject OPTIONAL */
			0, /* Flags */
			&SymbolicLinkList);
NotificationInfos.Version = 1;//以下是初始化Entry结构
NotificationInfos.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
RtlCopyMemory(&NotificationInfos.Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID));
RtlCopyMemory(&NotificationInfos.InterfaceClassGuid, EventCategoryData, sizeof(GUID));


Entry->PnpNotificationProc = CallbackRoutine;
	Entry->EventCategory = EventCategory;
	Entry->Context = Context;
//最后,获得互斥锁,并插入PnpNotifyListHead
KeAcquireGuardedMutex(&PnpNotifyListLock);
InsertHeadList(&PnpNotifyListHead,
		&Entry->PnpNotifyList);
KeReleaseGuardedMutex(&PnpNotifyListLock);
}
    回调函数注册后,将在什么情况下才会被调用?通过搜索对PnpNotifyListHead的引用,可以发现端倪:

Pnpnotify.c!IopNotifyPlugPlayNotification中遍历PnpNotifyListHead:
	ListEntry = PnpNotifyListHead.Flink;
	while (ListEntry != &PnpNotifyListHead)
	{
		ChangeEntry = CONTAINING_RECORD(ListEntry, PNP_NOTIFY_ENTRY, 
PnpNotifyList);
		ListEntry = ListEntry->Flink;
	}
这是典型的遍历队列操作啊,有必要介绍一下IopNotifyPlugPlayNotification的流程:

一般IoSetDeviceInterfaceState会以下列方式调用IopNotifyPlugPlayNotification

    EventGuid = Enable ? &GUID_DEVICE_INTERFACE_ARRIVAL : &GUID_DEVICE_INTERFACE_REMOVAL;
    IopNotifyPlugPlayNotification(
        PhysicalDeviceObject,
        EventCategoryDeviceInterfaceChange,
        EventGuid,
        &GuidString,
        (PVOID)SymbolicLinkName);
    当IopNotifyPlugPlayNotification被调用时,它搜索PnpNotifyListHead链表,逐项比对已注册在PnpNotifyListHead队列中的PNP_NOTIFY_ENTRY各项,如果PNP_NOTIFY_ENTRY中接口GUID和产生设备插拔事件的接口GUID相同,则调用PNP_NOTIFY_ENTRY中的回调函数,去完成一些挂起的IRP请求.


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:189772次
    • 积分:3644
    • 等级:
    • 排名:第9266名
    • 原创:163篇
    • 转载:104篇
    • 译文:2篇
    • 评论:31条
    博客专栏
    文章分类
    最新评论