PNP管理器简析--基于ReactOS0.33

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

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

static DWORD WINAPI
PnpEventThread(LPVOID lpParameter)
{
 PPLUGPLAY_EVENT_BLOCK PnpEvent;
    ...
    PnpEvent = HeapAlloc(GetProcessHeap(),PnpEventSize);
    ...
    for (;;)
 {
 DPRINT("Calling NtGetPlugPlayEvent()\n");

 /* Wait for the next pnp event */
  //这是个等待操作,等待PnpEvent有信号
 Status = NtGetPlugPlayEvent(0,PnpEvent,PnpEventSize);
        ....
        NtPlugPlayControl(PlugPlayControlUserResponse,NULL,0);
        ...
    }
    HeapFree(GetProcessHeap(),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;
}
代码有点TCP服务器accept事件通知模型:等待事件发生,然后分类处理事件。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);
 }
}

/* GLOBALS *******************************************************************/

static LIST_ENTRY IopPnpEventQueueHead;
static KEVENT IopPnpNotifyEvent;

/* FUNCTIONS *****************************************************************/
//上面提到的重要的函数的实现
NTSTATUS INIT_FUNCTION
IopInitPlugPlayEvents(VOID)
{
 InitializeListHead(&IopPnpEventQueueHead);

 KeInitializeEvent(&IopPnpNotifyEvent,SynchronizationEvent,FALSE);

 return STATUS_SUCCESS;
}
到这,PNP管理器这个服务器模型的骨架部分已经清晰了,后面循着骨架找齐完整的(相对完整的)结构。

总有些项目希望得到这样的功能:当插入设备的时候获得通知。界面开发者可能会这么做:添加一个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):IoSetDeviceInterfaceState@8
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,&IopRootDriverObject);
 if (!NT_SUCCESS(Status))
 {
 CPRINT("IoCreateDriverObject() Failed\n");
 KEBUGCHECKEX(PHASE1_INITIALIZATION_Failed,0);
 }

 Status = IoCreateDevice(IopRootDriverObject,FILE_DEVICE_CONTROLLER,&Pdo);
 if (!NT_SUCCESS(Status))
 {
 CPRINT("IoCreateDevice() Failed\n");
 KEBUGCHECKEX(PHASE1_INITIALIZATION_Failed,0);
 }

 Status = IopCreateDeviceNode(NULL,Pdo,&IopRootDeviceNode);
 if (!NT_SUCCESS(Status))
 {
 CPRINT("Insufficient resources\n");
 KEBUGCHECKEX(PHASE1_INITIALIZATION_Failed,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);
 }

 /* 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),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_NODEIopRootDeviceNode和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),FILE_AUTOGENERATED_DEVICE_NAME,&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差不多完整了,但是还差一步,设备毕竟运行需要中断,端口等资源来配合完成。因此枚举设备后运行设备前必然要满足设备资源请求。这个在IopActionInterrogateDeviceStack函数中完成。

NTSTATUS
IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,PVOID Context)
{
    ...
    Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,IRP_MN_QUERY_ID,&Stack);
    ...
    Status = IopQueryDeviceCapabilities(DeviceNode,&DeviceCapabilities);
    ...
    Stack.Parameters.QueryId.IdType = BusQueryInstanceID;
  Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,&Stack);
    ...
    Status = IopInitiatePnpIrp(
 DeviceNode->PhysicalDeviceObject,IRP_MN_QUERY_BUS_informatION,NULL);
    ...
    //请求资源
    Status = IopInitiatePnpIrp(
 DeviceNode->PhysicalDeviceObject,IRP_MN_QUERY_RESOURCE_REQUIREMENTS,NULL);
}
对于请求的资源存放在一个资源列表中:
DeviceNode->ResourceRequirements =
         (PIO_RESOURCE_REQUIREMENTS_LIST)IoStatusBlock.information;
然后由IopAssignDeviceResources和IopTranslateDeviceResources遍历资源队列,并调用相应的HAL函数分配物理资源

相关文章

react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接...
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc ...