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()\n");

        /* 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\n");
        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 (ntoskrnl\io\iomgr):    IopNotifyPlugPlayNotification(
Io.h (ntoskrnl\include\internal):IopNotifyPlugPlayNotification(
Pnpnotify.c (ntoskrnl\io\pnpmgr):IopNotifyPlugPlayNotification(
Pnpnotify.c (ntoskrnl\io\pnpmgr):            DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED\n", EventCategory);
Pnpnotify.c (ntoskrnl\io\pnpmgr):                DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED\n", EventCategory);
---- IoSetDeviceInterfaceState Matches (12 in 6 files) ----
Deviface.c (ntoskrnl\io\iomgr): * @name IoSetDeviceInterfaceState
Deviface.c (ntoskrnl\io\iomgr):IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName,
Deviface.c (ntoskrnl\io\iomgr):    DPRINT("IoSetDeviceInterfaceState('%wZ', %d)\n", SymbolicLinkName, Enable);
Deviface.c (ntoskrnl\io\iomgr):        DPRINT("IoSetDeviceInterfaceState() returning STATUS_INVALID_PARAMETER_1\n");
Fdo.c (drivers\serial\serenum):	Status = IoSetDeviceInterfaceState(&DeviceExtension->SerenumInterfaceName, TRUE);
Fdo.c (drivers\serial\serenum):		DPRINT("IoSetDeviceInterfaceState() failed with status 0x%08lx\n", Status);
Ntoskrnl.def (ntoskrnl):[email protected]
Pdo.c (drivers\usb\usbhub):	Status = IoSetDeviceInterfaceState(&DeviceExtension->SymbolicLinkName, TRUE);
Pdo.c (drivers\usb\usbhub):		DPRINT("Usbhub: IoSetDeviceInterfaceState() failed with status 0x%08lx\n", Status);
Pnp.c (drivers\serial\serial):		IoSetDeviceInterfaceState(&DeviceExtension->SerialInterfaceName, FALSE);
Pnp.c (drivers\serial\serial):	IoSetDeviceInterfaceState(&DeviceExtension->SerialInterfaceName, TRUE);
Winddk.h (include\ddk):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\n");
        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\n");
        KEBUGCHECKEX(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
    }

    Status = IopCreateDeviceNode(NULL, Pdo, NULL, &IopRootDeviceNode);
    if (!NT_SUCCESS(Status))
    {
        CPRINT("Insufficient resources\n");
        KEBUGCHECKEX(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
    }

    if (!RtlCreateUnicodeString(&IopRootDeviceNode->InstancePath,
        L"HTREE\\ROOT\\0"))
    {
        CPRINT("Failed to create the instance path!\n");
        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)\n", 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\n", 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函数分配物理资源

时间: 2024-11-08 15:24:16

PNP管理器简析--基于ReactOS0.33的相关文章

Pnp管理器(2)

前一篇Pnp管理器(1)提到了总线上设备变化时,将产生Pnp消息并在Pnp管理器个组件间流动.消息传递的最终目的是通知某个组件加载驱动.本文看一下Pnp管理器收到消息后加载驱动的流程. 当PnpEventThread函数从等待阻塞中返回,判断如果是添加设备则创建一个DeviceInstallParams* Params变量 typedef struct { #ifdef HAVE_SLIST_ENTRY_IMPLEMENTED SLIST_ENTRY ListEntry; #else LIST_

光源 材质 简析——基于《real time rendering》第三版 第五章

对于真是世界的渲染,有三个重要的组成部分,光源,材质,以及摄像机.下面,我们一个一个来简单介绍一下. 光源:方向光,点光源,聚光灯.但是,在和物体表面交互的时候,光源对物体表面的影响是依赖光的辐照度(irradiance)EL的,也就是单位面积上通过光的能量,这个能量由受到光和表面的夹角以及光本身的能量来表示.通常我们用RGB向量三种颜色来表示不同颜色光的辐照度.那么,当光的方向和表面垂直时,单位面积上的辐照度时最大的.当光的方向和表面有夹角时,光的辐照度开始变弱.如下图 可以看出,光的密度一样

项目管理中PMC管理模式简析

一.了解PMC管理要点,充分发挥PMC的优势 PMC属于一种委托管理模式,在项目建设工作中应重点考虑如何发挥PMC管理的优势,为了能够在项目建设管理当中充分发挥PMC管理的优势: 1.技术优势 2.管理优势 3.人才优势 二.优化PMC管理内容,发挥项目管理团队在各阶段的管理优势,合理管理项目,确保PMC管理与业主管理实现有效衔接.互补,实现项目建设目标 天行健咨询认为采用合理的措施确保PMC管理与业主管理实现有效衔接.互补是工程建设管理的重要任务,应用PMC模式管理项目建设工作时,需要统筹执行

一个简单而强大的单片机内存管理器-不带内存碎片整理

单片机简单内存管理器 本代码基于无操作系统的STM32单片机开发,功能强大,可申请到地址空间连续的不同大小的内存空间,且用户接口简单,使用方便 转载请注明出处:http://blog.csdn.net/u011833609/article/details/46834203 memory.h #ifndef __MEMORY_H__ #define __MEMORY_H__ #include "stdio.h" #include "string.h" #include

Python核心技术与实战——二一|巧用上下文管理器和with语句精简代码

我们在Python中对于with的语句应该是不陌生的,特别是在文件的输入输出操作中,那在具体的使用过程中,是有什么引伸的含义呢?与之密切相关的上下文管理器(context manager)又是什么呢? 什么是上下文管理器 在任何一种编程语言里,文件的输入输出.数据库的建立连接和断开等操作,都是很常见的资源管理操作.但是资源是有限的,在写程序的时候,我们必须保证这些资源在使用后得到释放,不然就容易造成资源泄漏,轻者系统处理缓慢,重则系统崩溃. 我们看一个例子: for i in range(100

最简单的基于FFMPEG的转码程序分析 +ffmpga代码简析(转 +总结)

模块:  libavcodec    - 编码解码器         libavdevice   - 输入输出设备的支持         libavfilter   - 视音频滤镜支持         libavformat   - 视音频等格式的解析         libavutil     - 工具库         libpostproc   - 后期效果处理         libswscale    - 图像颜色.尺寸转换 1. ffmpga代码简析 1.1 av_log() av_

开发基于键值对形式的语言管理器LauguageManager

开发基于键值对形式的语言管理器LauguageManager 前言:在我们在开发UI框架的过程中,我们会用到语言的国际化,尤其是我们的游戏项目做大之后,公司不仅仅满足于国内的市场,有可能会发布到美国.日本等国家.在这个时候语言的国际化会显得尤为重要了,当然国际化不仅仅指的语言国家化,还包括音频的国际化等许多的方面.在这里我们主要讲解的是语言的国际化. 通常的话我们会将语言信息全部的存放在配置文件中,当我们在国内的市场中我们可以使用中文,当我们发布到美国的时候可以使用英文的配置文件.如图所示: 英

0002 - Spring MVC 拦截器源码简析:拦截器加载与执行

1.概述 Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理.例如通过拦截器可以进行权限验证.记录请求信息的日志.判断用户是否登录等. 2.简单示例 2.1.继承 HandlerInterceptorAdapter 抽象类实现一个拦截器.代码如下: public class DemoInterceptor extends HandlerInterceptorAdapter { @Override    pu

JUC锁简析(基于源码的详解后续会陆续发出)

张图说明下要分享的内容: 01. Lock接口 JUC包中的 Lock 接口支持那些语义不同(重入.公平等)的锁规则.所谓语义不同,是指锁可是有"公平机制的锁"."非公平机制的锁"."可重入的锁"等等. "公平机制"是指"不同线程获取锁的机制是公平的", 而"非公平机制"则是指"不同线程获取锁的机制是非公平的","可重入的锁"是指同一个锁能够被一个