zoukankan      html  css  js  c++  java
  • PNP管理器简析--基于ReactOS0.33

        CSDN上转悠了一圈发现关于PNP管理的文章不多。那就由我献个丑,记录自己对PNP管理器的看法。

        pnp管理器被描写叙述为向内核和应用程序提供关于设备拔插的通知,凭感觉,pnp管理器应该是个线程函数等待设备通知。搜索ReactOS发现有这么个函数符合这个功能:

    static DWORD WINAPI
    PnpEventThread(LPVOID lpParameter)
    {
        PPLUGPLAY_EVENT_BLOCK PnpEvent;
        ...
        PnpEvent = HeapAlloc(GetProcessHeap(), 0, PnpEventSize);
        ...
        for (;;)
        {
            DPRINT("Calling NtGetPlugPlayEvent()
    ");
    
            /* Wait for the next pnp event */
            //这是个等待操作,等待PnpEvent有信号
            Status = NtGetPlugPlayEvent(0, 0, PnpEvent, PnpEventSize);
            ....
            NtPlugPlayControl(PlugPlayControlUserResponse, NULL, 0);
            ...
        }
        HeapFree(GetProcessHeap(), 0, PnpEvent);
    
        return ERROR_SUCCESS;
    }
    
    /*
    V操作 等待IopPnpNotifyEvent有信号 然后从IopPnpEventQueueHead队列中取pnp事件
    */
    NTSTATUS STDCALL
    NtGetPlugPlayEvent(IN ULONG Reserved1,
                       IN ULONG Reserved2,
                       OUT PPLUGPLAY_EVENT_BLOCK Buffer,
                       IN ULONG BufferSize)
    {
        PPNP_EVENT_ENTRY Entry;
        NTSTATUS Status;
        ...
        Status = KeWaitForSingleObject(&IopPnpNotifyEvent,
                                     UserRequest,
                                     KernelMode,
                                     FALSE,
                                     NULL);
        /* Get entry from the tail of the queue */
        Entry = CONTAINING_RECORD(IopPnpEventQueueHead.Blink,
                                PNP_EVENT_ENTRY,
                                ListEntry);
        memcpy(Buffer,
             &Entry->Event,
             Entry->Event.TotalSize);
    
        return STATUS_SUCCESS;
    }
    
    NTSTATUS STDCALL
    NtPlugPlayControl(IN PLUGPLAY_CONTROL_CLASS PlugPlayControlClass,
                      IN OUT PVOID Buffer,
                      IN ULONG BufferLength)
    {
        ...
        switch (PlugPlayControlClass)
        {
            case PlugPlayControlUserResponse:
                if (Buffer || BufferLength != 0)
                    return STATUS_INVALID_PARAMETER;
                return IopRemovePlugPlayEvent();
    
            case PlugPlayControlProperty:
                if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA))
                    return STATUS_INVALID_PARAMETER;
                return IopGetDeviceProperty((PPLUGPLAY_CONTROL_PROPERTY_DATA)Buffer);
    
            case PlugPlayControlGetRelatedDevice:
                if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA))
                    return STATUS_INVALID_PARAMETER;
                return IopGetRelatedDevice((PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA)Buffer);
    
            case PlugPlayControlDeviceStatus:
                if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_STATUS_DATA))
                    return STATUS_INVALID_PARAMETER;
                return IopDeviceStatus((PPLUGPLAY_CONTROL_STATUS_DATA)Buffer);
    
            case PlugPlayControlGetDeviceDepth:
                if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEPTH_DATA))
                    return STATUS_INVALID_PARAMETER;
                return IopGetDeviceDepth((PPLUGPLAY_CONTROL_DEPTH_DATA)Buffer);
    
            case PlugPlayControlResetDevice:
                if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RESET_DEVICE_DATA))
                    return STATUS_INVALID_PARAMETER;
                return IopResetDevice((PPLUGPLAY_CONTROL_RESET_DEVICE_DATA)Buffer);
    
            default:
                return STATUS_NOT_IMPLEMENTED;
        }
    
        return STATUS_NOT_IMPLEMENTED;
    }
    
        段代码有点TCPserveraccept事件通知模型:等待事件发生,然后分类处理事件。NtGetPlugPlayEvent函数中用到了两个重要的变量:IopPnpNotifyEvent和IopPnpEventQueueHead。

    内核将新的pnp事件插入IopPnpEventQueueHead队列。然后唤醒应用层线程,IopPnpNotifyEvent就是内核和用户态PV操作的信号量。

        这两个变量在系统启动时被初始化:

    BOOLEAN
    INIT_FUNCTION
    NTAPI
    IoInitSystem(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
    {
        KeInitializeGuardedMutex(&PnpNotifyListLock);
        ...
        InitializeListHead(&PnpNotifyListHead);
        ...
        PnpInit();
        ...
    }
    
    VOID INIT_FUNCTION
    PnpInit(VOID)
    {
       /* Initialize PnP-Event notification support */
        Status = IopInitPlugPlayEvents();
        //这个函数够重要的 调用失败就直接BUGCHECK了
        if (!NT_SUCCESS(Status))
        {
            CPRINT("IopInitPlugPlayEvents() failed
    ");
            KEBUGCHECKEX(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
        }
    }
    
    /* GLOBALS *******************************************************************/
    
    static LIST_ENTRY IopPnpEventQueueHead;
    static KEVENT IopPnpNotifyEvent;
    
    /* FUNCTIONS *****************************************************************/
    //上面提到的重要的函数的实现
    NTSTATUS INIT_FUNCTION
    IopInitPlugPlayEvents(VOID)
    {
        InitializeListHead(&IopPnpEventQueueHead);
    
        KeInitializeEvent(&IopPnpNotifyEvent,
                          SynchronizationEvent,
                          FALSE);
    
        return STATUS_SUCCESS;
    }
        到这。PNP管理器这个server模型的骨架部分已经清晰了,后面循着骨架找齐完整的(相对完整的)结构。

        总有些项目希望得到这种功能:当插入设备的时候获得通知。

    界面开发人员可能会这么做:加入一个WM_DEVICECHANGE事件的回调函数

    BEGIN_MESSAGE_MAP(CDeviceMonitorDlg, CDialog)
    //}}AFX_MSG_MAP
    ON_WM_DEVICECHANGE()
    END_MESSAGE_MAP()
        这个WM_DEVICECHANGE消息可能由WIN发出。也可能由某些后台程序监測到设备拔插后发出。为了监測某个设备拔插事件,就得注冊回调函数。就像为了观察北极气候就建立一个北极观測站一样。总线驱动程序假设想捕获到相关设备插入拔出事件,能够在其IRP_MN_START_DEVICE结束处用IoRegisterPlugPlayNotification注冊一回调函数。这个设备拔插事件回调函数注冊/注销接口例如以下:

    IoRegisterPlugPlayNotification/IoUnregisterPlugPlayNotificatio
    
    STDCALL
    IoRegisterPlugPlayNotification(
    	IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
    	IN ULONG EventCategoryFlags,
    	IN PVOID EventCategoryData OPTIONAL,
    	IN PDRIVER_OBJECT DriverObject,
    	IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine,
    	IN PVOID Context,
    	OUT PVOID *NotificationEntry)
    {
        /*
        windows内核中注冊回调事件都有相似的模式:创建一个回调事件项,
        然后把各种參数填入,最后把这个新建项挂入队列
        */
        PPNP_NOTIFY_ENTRY Entry;
        ...
        Entry = ExAllocatePoolWithTag(
            NonPagedPool,
            sizeof(PNP_NOTIFY_ENTRY),
            TAG_PNP_NOTIFY);
        ...
        Entry->PnpNotificationProc = CallbackRoutine;
        Entry->EventCategory = EventCategory;
        Entry->Context = Context;
        ...
        KeAcquireGuardedMutex(&PnpNotifyListLock);
        InsertHeadList(&PnpNotifyListHead,
            &Entry->PnpNotifyList);
        KeReleaseGuardedMutex(&PnpNotifyListLock);
        return STATUS_SUCCESS;
    }
    

    当中的通知队列和通知相互排斥锁:PnpNotifyListHead/PnpNotifyListLock在前面IoInitSystem时已经被初始化。接着看下这个监測站怎么工作:

    前面往回调事件队列中插入了回调函数。对应的。内核中有个遍历队列并调用回调函数的接口:

    VOID
    IopNotifyPlugPlayNotification(
    	IN PDEVICE_OBJECT DeviceObject,
    	IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
    	IN LPCGUID Event,
    	IN PVOID EventCategoryData1,
    	IN PVOID EventCategoryData2)
    {
        PPNP_NOTIFY_ENTRY ChangeEntry;
        PLIST_ENTRY ListEntry;
        PVOID NotificationStructure;
        ...
        ListEntry = PnpNotifyListHead.Flink;
        while (ListEntry != &PnpNotifyListHead)
        {
            ChangeEntry = CONTAINING_RECORD(ListEntry, PNP_NOTIFY_ENTRY, PnpNotifyList);
            CallCurrentEntry = FALSE;
    
            switch (EventCategory)
            {
                case EventCategoryDeviceInterfaceChange:
                {
                    if (ChangeEntry->EventCategory == EventCategory
                        && RtlCompareUnicodeString(&ChangeEntry->Guid, (PUNICODE_STRING)EventCategoryData1, FALSE) == 0)
                    {
                        CallCurrentEntry = TRUE;
                    }
                    break;
                }
                case EventCategoryHardwareProfileChange:
                {
                    CallCurrentEntry = TRUE;
                    break;
                }
                case EventCategoryTargetDeviceChange:
                {
                    if (ChangeEntry->FileObject == (PFILE_OBJECT)EventCategoryData1)
                        CallCurrentEntry = TRUE;
                }
                default:
                {
                    break;
                }
            }
    
            /* Move to the next element now, as callback may unregister itself */
            ListEntry = ListEntry->Flink;
            //调用注入的回调函数
            if (CallCurrentEntry)
            {
                /* Call entry into new allocated memory */
    
                KeReleaseGuardedMutex(&PnpNotifyListLock);
                (ChangeEntry->PnpNotificationProc)(
                    NotificationStructure,
                    ChangeEntry->Context);
                KeAcquireGuardedMutex(&PnpNotifyListLock);
            }
        }
    }
    看下ReactOS中谁会调用这个函数:

    ---- IopNotifyPlugPlayNotification Matches (6 in 3 files) ----
    Deviface.c (ntoskrnlioiomgr):    IopNotifyPlugPlayNotification(
    Io.h (ntoskrnlincludeinternal):IopNotifyPlugPlayNotification(
    Pnpnotify.c (ntoskrnliopnpmgr):IopNotifyPlugPlayNotification(
    Pnpnotify.c (ntoskrnliopnpmgr):            DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED
    ", EventCategory);
    Pnpnotify.c (ntoskrnliopnpmgr):                DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED
    ", EventCategory);
    

    ---- IoSetDeviceInterfaceState Matches (12 in 6 files) ----
    Deviface.c (ntoskrnlioiomgr): * @name IoSetDeviceInterfaceState
    Deviface.c (ntoskrnlioiomgr):IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName,
    Deviface.c (ntoskrnlioiomgr):    DPRINT("IoSetDeviceInterfaceState('%wZ', %d)
    ", SymbolicLinkName, Enable);
    Deviface.c (ntoskrnlioiomgr):        DPRINT("IoSetDeviceInterfaceState() returning STATUS_INVALID_PARAMETER_1
    ");
    Fdo.c (driversserialserenum):	Status = IoSetDeviceInterfaceState(&DeviceExtension->SerenumInterfaceName, TRUE);
    Fdo.c (driversserialserenum):		DPRINT("IoSetDeviceInterfaceState() failed with status 0x%08lx
    ", Status);
    Ntoskrnl.def (ntoskrnl):IoSetDeviceInterfaceState@8
    Pdo.c (driversusbusbhub):	Status = IoSetDeviceInterfaceState(&DeviceExtension->SymbolicLinkName, TRUE);
    Pdo.c (driversusbusbhub):		DPRINT("Usbhub: IoSetDeviceInterfaceState() failed with status 0x%08lx
    ", Status);
    Pnp.c (driversserialserial):		IoSetDeviceInterfaceState(&DeviceExtension->SerialInterfaceName, FALSE);
    Pnp.c (driversserialserial):	IoSetDeviceInterfaceState(&DeviceExtension->SerialInterfaceName, TRUE);
    Winddk.h (includeddk):IoSetDeviceInterfaceState(
    
    从这些输出来看:当用户拔插串口。usb时都会使IoRegisterDeviceInterface注冊的回调函数得到运行。

    假设把通知机制比方血液循环系统,那么到这,PNP管理器的循环系统也建立起来了。接着往下看其它脏器的组织结构,pnp管理器中管理的主要对象:设备驱动。

        整个windows的设备驱动都是从根设备对象出发。层层堆叠形成一颗树结构(尽管说legacy设备不形成堆叠。可是windows还是把他纳入到堆叠结构。仅仅是其堆叠的PDO是个虚拟的设备对象)。附带说一下,看ReactOS源代码时,总认为PNP管理器跟linux的sysfs概念有点接近,大家都用虚拟的驱动/设备形成一个自上至下的树结构。

    既然根设备是整个系统中设备驱动的基石,他的出场一定比較靠前--至少要在系统创建其它设备之前已经有了吧?来看下传说中根设备出场函数:

    //创建IopRootDriverObject,IopRootDeviceNode,PnpRootDeviceOBject,并建立三者关系
    VOID INIT_FUNCTION
    PnpInit(VOID)
    {
        ...
        Status = IopCreateDriver(NULL, PnpDriverInitializeEmpty, NULL, 0, 0, &IopRootDriverObject);
        if (!NT_SUCCESS(Status))
        {
            CPRINT("IoCreateDriverObject() failed
    ");
            KEBUGCHECKEX(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
        }
    
        Status = IoCreateDevice(IopRootDriverObject, 0, NULL, FILE_DEVICE_CONTROLLER,
            0, FALSE, &Pdo);
        if (!NT_SUCCESS(Status))
        {
            CPRINT("IoCreateDevice() failed
    ");
            KEBUGCHECKEX(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
        }
    
        Status = IopCreateDeviceNode(NULL, Pdo, NULL, &IopRootDeviceNode);
        if (!NT_SUCCESS(Status))
        {
            CPRINT("Insufficient resources
    ");
            KEBUGCHECKEX(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
        }
    
        if (!RtlCreateUnicodeString(&IopRootDeviceNode->InstancePath,
            L"HTREE\ROOT\0"))
        {
            CPRINT("Failed to create the instance path!
    ");
            KEBUGCHECKEX(PHASE1_INITIALIZATION_FAILED, STATUS_NO_MEMORY, 0, 0, 0);
        }
    
        /* Report the device to the user-mode pnp manager */
        IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
            &IopRootDeviceNode->InstancePath);
        PnpRootDriverEntry(IopRootDriverObject, NULL);
        IopRootDeviceNode->PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
        //竟然自动调用AddDevice,而不是通过内核调用IopInitializeDevice
        IopRootDriverObject->DriverExtension->AddDevice(
            IopRootDriverObject,
            IopRootDeviceNode->PhysicalDeviceObject);
        ...
    }
    
        PnpInit调用了PnpRootDriver的驱动入口和AddDevice函数。这么看来PnpRootDriver也算得上是Pnp设备了。

    跟进PnpRootDriver和AddDevice函数一瞧到底:

    NTSTATUS NTAPI
    PnpRootDriverEntry(
      IN PDRIVER_OBJECT DriverObject,
      IN PUNICODE_STRING RegistryPath)
    {
      DPRINT("PnpRootDriverEntry(%p %wZ)
    ", DriverObject, RegistryPath);
    	//内核载入驱动后在IopInitializeDevice中调用AddDevice
      DriverObject->DriverExtension->AddDevice = PnpRootAddDevice;
    
      DriverObject->MajorFunction[IRP_MJ_PNP] = PnpRootPnpControl;
      //DriverObject->MajorFunction[IRP_MJ_POWER] = PnpRootPowerControl;
    
      return STATUS_SUCCESS;
    }
    

    PnpRootDriver应该算是一个最简单的DriverEntry入口函数了。如毛德操书中所述,Pnp设备至少要提供PNP派遣函数,PnpRootDriver真的仅仅做了这么件事!

    NTSTATUS
    STDCALL
    PnpRootAddDevice(
      IN PDRIVER_OBJECT DriverObject,
      IN PDEVICE_OBJECT PhysicalDeviceObject)
    {
        Status = IoCreateDevice(
        DriverObject,
        sizeof(PNPROOT_FDO_DEVICE_EXTENSION),
        NULL,
        FILE_DEVICE_BUS_EXTENDER,
        FILE_DEVICE_SECURE_OPEN,
        TRUE,
        &PnpRootDeviceObject);
        ...
        DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)PnpRootDeviceObject->DeviceExtension;
        RtlZeroMemory(DeviceExtension, sizeof(PNPROOT_FDO_DEVICE_EXTENSION));
    
        InitializeListHead(&DeviceExtension->DeviceListHead);
        DeviceExtension->DeviceListCount = 0;
        KeInitializeGuardedMutex(&DeviceExtension->DeviceListLock);
    
        Status = IoAttachDeviceToDeviceStackSafe(
        PnpRootDeviceObject,
        PhysicalDeviceObject,
        &DeviceExtension->Ldo);
    }
         内核中维持一颗已装载的设备驱动模块树。树根是一个虚拟节点IopRootDeviceNode,节点在PnpInit函数中创建。

    这个树根下挂满了各类总线设备和Legacy设备。形成设备树中的节点是结构:

    typedef struct _DEVICE_NODE
    {
        struct _DEVICE_NODE *Parent;
        struct _DEVICE_NODE *NextSibling;
        struct _DEVICE_NODE *Child;
        ...
        PDEVICE_OBJECT PhysicalDeviceObject;
        PCM_RESOURCE_LIST ResourceList;
        ...
    }
    
        对于根设备IopRootDeviceNode,从PnpInit中能够看到其PhysicalDeviceObject指向IopRootDriverObject创建的Pdo。这个重要的Pdo是个无名设备,默默无闻的提供堆叠功能,真是居功至伟!PnpInit函数完毕了DEVICE_NODE IopRootDeviceNode和DriverObject IopRootDriverObject的创建之后立即调用IopRootDriverObject的AddDevice函数完毕设备堆叠。

        结合PnpRootAddDevice的代码,能够看到PnpRootDeviceObject的驱动对象是IopRootDriverObject,而且堆叠在PnpInit创建的无名设备对象Pdo上。至此三个重要的对象之间的关系已经确立:IopRootDeviceNode是系统设备堆叠的根,其物理设备是一个无名的设备,并被堆叠了一个以IopRootDriverObject为驱动对象的设备PnpRootDeviceObject。这三个变量算构成PNP管理器的神经中枢。特别是PnpRootDeviceObject,其自己定义设备扩展域DeviceExtension,维护了一个PNPROOT_DEVICE结构。为什么这是个特别的域?以下这段代码做出了解释:

    /*
    这个函数主要干了2件事:创建PNPROOT_DEVICE节点和DEVICE_OBJECT设备对象
    由于这个函数仅仅可能因NtLoadDriver创建非pnp设备而间接调用。为了维持
    pnp设备树。NtLoadDriver将创建DEVICE_NODE节点。并在PnpRootCreateDevice这个函数
    中创建PNPROOT_DEVICE节点和DEVICE_OBJECT pdo对象。
    看结构。PNPROOT_DEVICE更像是一个pnp描写叙述符,描写叙述了pdo对象的附加信息。
    最基本的。由于创建的DEVICE_NODE挂在IopRootDeviceNode下,因此把这个新
    创建的PNPROOT_DEVICE节点挂在IopRootDeviceNode的链表下,这是在表明pnp
    根节点下第一层子节点的意思吗?
    */
    NTSTATUS
    PnpRootCreateDevice(
      IN PUNICODE_STRING ServiceName,
      IN PDEVICE_OBJECT *PhysicalDeviceObject)
    {
        ...
        DeviceExtension = PnpRootDeviceObject->DeviceExtension;
        KeAcquireGuardedMutex(&DeviceExtension->DeviceListLock);
        ...
        Device = ExAllocatePoolWithTag(PagedPool, sizeof(PNPROOT_DEVICE), TAG_PNP_ROOT);
        ...
        RtlZeroMemory(Device, sizeof(PNPROOT_DEVICE));
        ...
        /*
        创建一个用于给legacy设备进行堆叠的pnp  root根下第一层pdo对象,
        尽管legacy不提供堆叠可是也被纳入pnp树的管理范畴,因此创建
        这么个父设备对象。
        从IoCreateDevice的參数PnpRootDeviceObject->DriverObject也能够看出创建的pdo
        并非给NtLoadDriver用的,是根节点驱动的设备对象
        */
      Status = IoCreateDevice(
        PnpRootDeviceObject->DriverObject,
        sizeof(PNPROOT_PDO_DEVICE_EXTENSION),
        NULL,
        FILE_DEVICE_CONTROLLER,
        FILE_AUTOGENERATED_DEVICE_NAME,
        FALSE,
        &Device->Pdo);
        /*
        上面创建PnpRootDeviceObject设备对象。属于IopRootDriverObject驱动的设备对象
        以下将全部属于IopRootDriverObject驱动的设备对象增加链表DeviceExtension->DeviceListHead中
        */
        PdoDeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension;
        RtlZeroMemory(PdoDeviceExtension, sizeof(PNPROOT_PDO_DEVICE_EXTENSION));
        PdoDeviceExtension->Common.IsFDO = FALSE;
        PdoDeviceExtension->DeviceInfo = Device;
        ...
        InsertTailList(
        &DeviceExtension->DeviceListHead,
        &Device->ListEntry);
        DeviceExtension->DeviceListCount++;
    
        *PhysicalDeviceObject = Device->Pdo;
    }
    
        如我的凝视所诉,PnpRootDeviceObject!DeviceExtension!DeviceListHead用于存放全部根节点下的设备,设备枚举时通过这个列表遍历各个设备。本以为通过NtLoadDriver装载驱动时会枚举设备请求资源,只是非常不幸。我没在ReactOS中找到相关的代码。仅仅在IoInitSystem时看到相关代码。

        IoInitSystem调用IoSynchronousInvalidateDeviceRelations,这个函数名字真长按字面意思理解是"同步无效的设备关系"。怎么同步?

    VOID
    NTAPI
    IoSynchronousInvalidateDeviceRelations(
        IN PDEVICE_OBJECT DeviceObject,
        IN DEVICE_RELATION_TYPE Type)
    {
        ...
        Status = IopInitiatePnpIrp(
            DeviceObject,
            &IoStatusBlock,
            IRP_MN_QUERY_DEVICE_RELATIONS,
            &Stack);
        if (!NT_SUCCESS(Status))
        {
            DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx
    ", Status);
            return;
        }
    
        DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
        ...
    }
        向DeviceObject发送主功能号为PNP次功能号为IRP_MN_QUERY_DEVICE_RELATIONS的IRP请求,请求结果存放在IoStatusBlock.Information中。这里的DeviceObject即为IopRootDeviceNode->PhysicalDeviceObject,这个设备对象相应的PNP回调函数为:

    PnpRootFdoPnpControl!FdoQueryDeviceRelations!PnpRootQueryDeviceRelations
        这个函数调用EnumerateDevices,枚举注冊表Root下的子健。每一个子健代表一个设备项(应该是安装驱动时由inf文件设置),对于每一个被发现的子健创建PNPROOT_DEVICE结构的节点并挂入IopRootDevice扩展部的DeviceListHead队列中。当EnumerateDevices返回。为枚举到的每一个设备创建驱动对象和设备对象,这样整个设备对象树逐渐变得枝繁叶茂起来。

        到这里,整个PNP差点儿相同完整了。可是还差一步,设备毕竟执行须要中断,port等资源来配合完毕。因此枚举设备后执行设备前必定要满足设备资源请求。这个在IopActionInterrogateDeviceStack函数中完毕。

    NTSTATUS
    IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
                                    PVOID Context)
    {
        ...
        Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
                                  &IoStatusBlock,
                                  IRP_MN_QUERY_ID,
                                  &Stack);
        ...
        Status = IopQueryDeviceCapabilities(DeviceNode, &DeviceCapabilities);
        ...
        Stack.Parameters.QueryId.IdType = BusQueryInstanceID;
        Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
                                  &IoStatusBlock,
                                  IRP_MN_QUERY_ID,
                                  &Stack);
        ...
        Status = IopInitiatePnpIrp(
          DeviceNode->PhysicalDeviceObject,
          &IoStatusBlock,
          IRP_MN_QUERY_BUS_INFORMATION,
          NULL);
        ...
        //请求资源
        Status = IopInitiatePnpIrp(
          DeviceNode->PhysicalDeviceObject,
          &IoStatusBlock,
          IRP_MN_QUERY_RESOURCE_REQUIREMENTS,
          NULL);
    }
    
    对于请求的资源存放在一个资源列表中:

    DeviceNode->ResourceRequirements =
             (PIO_RESOURCE_REQUIREMENTS_LIST)IoStatusBlock.Information;
    然后由IopAssignDeviceResources和IopTranslateDeviceResources遍历资源队列。并调用对应的HAL函数分配物理资源




  • 相关阅读:
    关于SpringBoot的外部化配置使用记录
    深入理解Mybatis插件
    MySQL JDBC Driver 8.0+设置服务器时区
    重新认识Java注解
    深入理解Java枚举
    Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇
    Spring Cloud系列教程第九篇-Eureka自我保护机制
    安装vsftp服务器的时候遇到的问题
    spring cloud系列教程第八篇-修改服务名称及获取注册中心注册者的信息
    idea 启动命令行的时候提示不能创建PTY
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/7280280.html
Copyright © 2011-2022 走看看