PNP技术是由Microsoft提出的,英文Plug and play的缩写,中译即插即用,意思是系统自动侦测周边设备和板卡并自动安装设备驱动程序,做到插上就能用,无须人工干预,是Windows自带的一项技术。所谓即插即用是指将符合PNP标准的PC插卡等外围设备安装到电脑时,操作系统自动设定系统结构的技术。这就是说,当用户安装新的硬件时,不必设置任何跳线器开关,也不必用软件配置中断请求、内存地址或直接存储器存取(DMA)通道,Windows会向应用程序通知硬件设备的新变化,并会自动协调IRQ、内存地址和DMA通道之间的冲突。
PnP 管理器包含两个部分:内核模式 PnP 管理器和用户模式 PnP 管理器。 内核模式 PnP 管理器与操作系统组件和驱动程序交互,以配置、管理和维护设备。 用户模式 PnP 管理器与用户模式安装组件(如类安装程序)交互,以配置和安装设备。 用户模式 PnP 管理器还与应用程序交互,例如,注册应用程序以通知设备更改,并在发生设备事件时通知应用程序,它们之间的协作如下图:
PnP 驱动程序支持计算机上的物理、逻辑和虚拟设备。 术语"PnP 驱动程序"是指支持PNP接口的任何 Windows 驱动程序。 虽然大多数 PnP 驱动程序也是 WDM 驱动程序,因此跨 Windows 平台与源兼容,但一些驱动程序支持 PnP,而无需完全实现 WDM。
所有驱动程序都应支持 PnP 和电源管理。 如果单个驱动程序不支持 PnP 和电源管理,它将限制整个系统的 PnP 和电源管理支持。
若要支持 PnP,驱动程序必须遵循以下准则:
- 它必须包含 DispatchPnP 例程;
- 此调度例程必须处理 IRP_MJ_PNP 请求和关联的次要函数代码。 有关详细信息,请参阅 DispatchPnP 例程;
- 它不得搜索硬件;
- PnP 管理器负责确定硬件设备是否存在。 当 PnP 管理器检测到设备时,它会通过调用其 AddDevice 例程通知驱动程序。 当系统启动时,或者用户向正在运行的系统添加设备或从中删除设备时,都可以检测到硬件;
- 它不得分配硬件资源;
- PnP 驱动程序必须为 PnP 管理器提供设备可能使用的资源列表。 PnP 管理器负责将资源分配给每个设备,并在驱动程序发送 IRP_MN_START_DEVICE 请求时通知每个设备的分配。 因此,驱动程序必须能够处理各种硬件资源配置;
PnP设备的状态转换
在PnP 系统上,设备在配置、启动、可能停止以重新平衡资源以及可能被删除时会转换各种PnP状态。下图显示了设备的PnP 状态,以及设备如何从一种状态转换为另一种状态:
从上图左上角开始,PnP设备实际存在于系统中,因为用户刚刚插入了设备,或者设备在启动时存在,系统软件尚不知道该设备。
若要开始设备的软件配置,PnP 管理器和父总线驱动程序会枚举设备。 PnP 管理器(可能来自用户模式组件的帮助)标识设备的驱动程序,包括函数驱动程序和任何可选的Filter驱动程序。 如果尚未加载驱动程序,PnP 管理器会调用每个驱动程序的 DriverEntry 例程。
初始化驱动程序后,它必须准备好初始化其设备。 PnP 管理器为驱动程序控制的每个设备调用驱动程序的 AddDevice 例程。
当驱动程序收到来自 PnP 管理器 的IRP_MN_START_DEVICE 请求时,驱动程序会启动设备并准备好处理设备的 I/O 请求。
如果 PnP 管理器必须重新配置活动设备的硬件资源,则会向设备的驱动程序发送 IRP_MN_QUERY_STOP_DEVICE 和 IRP_MN_STOP_DEVICE 请求。 重新配置硬件资源后,PnP 管理器会通过发送 IRP_MN_START_DEVICE 请求来指示驱动程序重启设备。启动配置设备的驱动程序可以在设备启动之前接收 IRP_MN_QUERY_STOP_DEVICE 和 IRP_MN_STOP_DEVICE 请求,尽管上图中未显示此步骤。
当 PnP 设备正在从系统中物理删除或已被删除时,PnP 管理器会将各种删除 IRP 发送到设备的驱动程序,指示它们删除设备的软件表示,包括设备对象等 。
在删除驱动程序的所有设备之后的某个时刻,PnP 管理器会调用驱动程序的 Unload 例程并卸载驱动程序。
PNP设备加入系统
下图显示了配置设备的第一步,从用户将硬件插入计算机开始。
以下注释对应于上图中带圆圈的数字:
- 用户将PnP 设备插入PnP 总线上的可用槽。在此示例中,用户将PnP USB 游戏杆插入 USB 主机控制器上的集线器。 USB 集线器是PnP 总线设备,因为子设备可以连接到它;
- 总线设备的功能驱动程序确定新设备在其总线上。驱动程序如何确定这一点取决于总线体系结构。 对于某些总线,总线功能驱动程序会收到新设备的热插拔通知。 如果总线不支持热插拔通知,则用户必须在控制面板中采取适当的操作,以枚举总线。在此示例中,USB 总线支持热插拔通知,因此 USB 总线的功能驱动程序会收到其子级已更改的通知;
- 总线设备的功能驱动程序通知PnP 管理器其子设备集已更改。函数驱动程序使用 BusRelations 类型调用 IoInvalidateDeviceRelations 来通知PnP管理器;
- PnP 管理器查询总线的驱动程序,以获取总线上的当前设备列表。PnP 管理器将 IRP_MN_QUERY_DEVICE_RELATIONS 请求发送到总线的设备堆栈, Parameters.QueryDeviceRelations.Type 值为 BusRelations,表示PnP 管理器正在请求总线上存在的当前设备列表 (根据总线关系) 。PnP 管理器将IRP发送到总线的设备堆栈中的顶部驱动程序。 根据PnPIRP的规则,堆栈中的每个驱动程序处理 IRP,并将IRP向下传递到下一个驱动程序;
- 总线设备的函数驱动程序处理 IRP。在此示例中,USB 集线器驱动程序处理中心 FDO 的此 IRP。 中心驱动程序为游戏杆设备创建 PDO ,并在使用IRP返回的子设备列表中包括指向游戏杆 PDO 的引用指针。当 USB 中心的父总线驱动程序 (USB 主机控制器类/微型类驱动程序对) 完成IRP时,IRP 将通过中心驱动程序注册的任何 IoCompletion 例程来备份设备堆栈;
请注意,总线函数驱动程序通过请求PnP 管理器查询其子设备列表来报告其子设备列表中的更改。 总线设备的所有驱动程序都可以看到生成的 IRP_MN_QUERY_DEVICE_RELATIONS 请求。 通常,总线函数驱动程序是处理IRP和报表子级的唯一驱动程序。 在某些设备堆栈中,存在总线Filter驱动程序,并参与构建总线关系列表。 一个示例是 ACPI,它附加为 ACPI 设备的总线Filter驱动程序。在某些设备堆栈中,非总线Filter驱动程序处理 IRP_MN_QUERY_DEVICE_RELATIONS 请求,但这并不典型。
此时,PnP 管理器在总线上具有当前设备列表。 然后,PnP 管理器确定任何设备是新到达设备还是已删除设备。 在此示例中,有一个新设备。 下图显示了为新设备创建开发节点并开始配置设备的 PnP 管理器:
以下注释对应于上图中带圆圈的数字:
1. PnP 管理器为总线上的任何新子设备创建开发节点。PnP 管理器将 IRP_MN_QUERY_DEVICE_RELATIONS IRP 中返回的总线关系列表与当前记录在 PnP 设备树中的总线的子级列表进行比较。 PnP 管理器为每个新设备创建一个开发节点,并为已删除的任何设备启动删除处理。
在此示例中,有一个新设备 (游戏杆) ,因此 PnP 管理器为游戏杆创建开发节点。 此时,为游戏杆配置的唯一驱动程序是父 USB 集线器总线驱动程序,该驱动程序创建了游戏杆的 PDO。 设备堆栈中也会存在任何可选的总线Filter驱动程序,但为简单起见,本示例省略了总线Filter驱动程序。上图中两个开发节点之间的宽箭头指示游戏杆开发节点是 USB 集线器开发节点的子级。
2. PnP 管理器收集有关新设备的信息,并开始配置设备。PnP 管理器将一系列 IRP 发送到设备堆栈,以收集有关设备的信息。 此时,设备堆栈仅包含设备的父总线驱动程序创建的 PDO,以及任何可选总线Filter驱动程序的筛选 DO。 因此,总线驱动程序和总线Filter驱动程序是唯一响应这些 IRP 的驱动程序。 在此示例中,游戏杆设备堆栈中唯一的驱动程序是父总线驱动程序,即 USB 集线器驱动程序。
PnP 管理器通过向设备堆栈发送 IRP 来收集有关新设备的信息。 这些 IRP 包括以下内容:
- IRP_MN_QUERY_ID为以下每种硬件 ID 类型的单独 IRP:BusQueryDeviceID、BusQueryInstanceID、BusQueryHardwareIDs、BusQueryCompatibleIDs、BusQueryContainerID
- IRP_MN_QUERY_CAPABILITIES
- IRP_MN_QUERY_DEVICE_TEXT,为以下每个项创建单独的 IRP:DeviceTextDescription、DeviceTextLocationInformation
- IRP_MN_QUERY_BUS_INFORMATION
- IRP_MN_QUERY_RESOURCES
- IRP_MN_QUERY_RESOURCE_REQUIREMENTS
在处理新 PnP 设备的这一阶段,PnP 管理器发送上面列出的 IRP,但不一定按列出的顺序发送,因此不应假设 IRP 的发送顺序。 此外,不应假定 PnP 管理器仅发送上面列出的 IRP。
PnP 管理器检查注册表以确定设备以前是否已安装在此计算机上。 PnP 管理器在 Enum 分支下检查<设备的枚举器>\<deviceID> 子项。 在此示例中,该设备是新设备,必须"从头开始"进行配置。
3. PnP 管理器将有关设备的信息存储在注册表中。
注册表的Enum分支保留供操作系统组件使用,其布局可能会更改。 驱动程序编写器必须使用系统例程来提取与驱动程序相关的信息。 请勿直接从驱动程序访问Enum分支。 列出的以下 枚举 信息仅用于调试目的:
a. PnP 管理器在该设备枚举器的键下为设备创建子项:PnP 管理器创建名为 HKLM\System\CurrentControlSet\Enum\<enumeratordeviceID> 的enumeratordeviceID子项。 如果子项尚不存在,则创建该子项。枚举器是基于 PnP 硬件标准发现 PnP 设备的组件。 枚举器的任务由 PnP 总线驱动程序与 PnP 管理器合作执行。
设备通常由其父总线驱动程序(例如 PCI 或 PCMCIA)枚举。 某些设备由总线Filter驱动程序(如 ACPI)枚举。
b. PnP 管理器为设备的此实例创建子项:如果 Capabilities.UniqueID 为 IRP_MN_QUERY_CAPABILITIES 返回为 TRUE,则设备的唯一 ID 在系统中是唯一的。 如果没有,PnP 管理器会修改 ID,使其在系统范围内是唯一的。
PnP 管理器创建名为 HKLM\System\CurrentControlSet\Enum\<enumerator><\deviceID>\<instanceID> 的子项。
PnP 管理器将有关设备的信息写入设备实例的子项。
c. PnP 管理器存储信息,包括以下设备提供的内容:
- DeviceDesc ,来自 IRP_MN_QUERY_DEVICE_TEXT
- Location,来自 IRP_MN_QUERY_DEVICE_TEXT
- Capabilities ,来自 IRP_MN_QUERY_CAPABILITIES 的标志
- UINumber,来自 IRP_MN_QUERY_CAPABILITIES
- HardwareID,来自 IRP_MN_QUERY_ID
- CompatibleIDs,来自 IRP_MN_QUERY_ID
- ContainerID,来自 IRP_MN_QUERY_ID
- LogConf\BootConfig,来自 IRP_MN_QUERY_RESOURCES
- LogConf\BasicConfigVector,来自 IRP_MN_QUERY_RESOURCE_REQUIREMENTS
此时,PnP 管理器已准备好查找设备的功能驱动程序和Filter驱动程序。 请参阅下图:
以下注释对应于上图中的编号圆圈:
- 内核模式 PnP 管理器与用户模式 PnP 管理器和用户模式安装程序组件进行协调,以查找设备的功能和Filter驱动程序。内核模式 PnP 管理器将事件排队到用户模式 PnP 管理器,标识需要安装的设备。 特权用户登录后,用户模式组件将继续查找驱动程序。
- 用户模式安装程序组件指示内核模式 PnP 管理器加载功能和Filter驱动程序,用户模式组件调用回内核模式以加载驱动程序,从而导致调用其 AddDevice 例程。
下图显示了 PnP 管理器加载适用的驱动程序,调用其 AddDevice 例程,并指示驱动程序启动设备:
以下注释对应于上图中的编号圆圈:
1. 底层的Filter驱动程序
在功能驱动程序附加到设备堆栈之前,PnP 管理器会处理任何低Filter驱动程序。 对于每个低Filter驱动程序,如果驱动程序尚未加载,PnP 管理器会调用驱动程序的 DriverEntry 例程。 然后PnP 管理器调用驱动程序的 AddDevice 例程。 在其 AddDevice 例程中,Filter驱动程序 (Filter DO) 创建Filter设备对象,并将其附加到设备堆栈 (IoAttachDeviceToDeviceStack) 。 将设备对象附加到设备堆栈后,驱动程序将作为设备的驱动程序使用。
在 USB 游戏杆示例中,有一个适用于设备的低Filter驱动程序。
2. 功能驱动程序
附加任何较低的Filter后,PnP 管理器将处理功能驱动程序。 如果尚未加载驱动程序,则PnP 管理器会调用功能驱动程序的 DriverEntry 例程,并调用功能驱动程序的 AddDevice 例程。 功能驱动程序 (FDO) 创建函数设备对象,并将其附加到设备堆栈。
在此示例中,USB 游戏杆的功能驱动程序实际上是一对驱动程序:HID 类驱动程序和 HID 微类驱动程序。 这两个驱动程序协同工作,充当功能驱动程序。 驱动程序对仅创建一个 FDO 并将其附加到设备堆栈。
3. 上层Filter驱动程序
附加功能驱动程序后,PnP 管理器将处理任何上层Filter驱动程序。
在此示例中,有一个适用于设备的上层Filter驱动程序。
4. 分配资源和启动设备
如果需要,PnP 管理器会向设备分配资源,并发出IRP来启动设备。
- a.分配资源
- 在配置过程的早期,PnP 管理器从设备的父总线驱动程序中收集了设备的硬件资源要求。 为设备加载完整的驱动程序集后,PnP 管理器会向设备堆栈发送 IRP_MN_FILTER_RESOURCE_REQUIREMENTS 请求。 堆栈中的所有驱动程序都有机会处理此IRP并在必要时修改设备的资源要求列表。
- 如果设备需要资源,则PnP 管理器会根据设备的要求和当前可用的资源将资源分配给设备。
- PnP 管理器可能需要重新排列现有设备的资源分配以满足新设备的需求。 这种资源重新分配称为"重新均衡"。现有设备的驱动程序在重新平衡期间接收一系列停止和启动 IRP,但重新平衡必须对用户透明。
- 在 USB 游戏杆示例中,USB 设备不需要硬件资源,因此PnP 管理器将资源列表设置为 NULL。
- b.启动设备 (IRP_MN_START_DEVICE)
- PnP 管理器将资源分配给设备后,会将 IRP_MN_START_DEVICEIRP发送到设备堆栈,以指示驱动程序启动设备。
启动设备后,PnP 管理器再向设备的驱动程序发送三个 IRP:
- IRP_MN_QUERY_CAPABILITIES:启动IRP成功完成后,PnP 管理器将另一 个IRP_MN_QUERY_CAPABILITIESIRP发送到设备堆栈。 设备的所有驱动程序都可以选择处理 IRP。PnP 管理器此时会在附加所有驱动程序并启动设备后发送此 IRP,因为函数或Filter驱动程序可能需要访问设备来收集功能信息。
- IRP_MN_QUERY_PNP_DEVICE_STATE:例如,此IRP使驱动程序有机会报告设备不应显示在用户界面(如 设备管理器 和 Hotplug 程序)中。 这对于系统上存在但在当前配置中不可用的设备(例如,在取消停靠笔记本电脑时无法使用的笔记本电脑上的游戏端口)非常有用。
- 总线关系的IRP_MN_QUERY_DEVICE_RELATIONSPnP 管理器发送此IRP以确定设备是否具有任何子设备。 如果是,则PnP 管理器将配置每个子设备。
注意:
GUID_PNP_LOCATION_INTERFACE 接口为设备提供 SPDRP_LOCATION_PATHS 即插即用 (PnP) 设备属性。
若要在驱动程序中实现此接口,请处理 interfaceType = GUID_PNP_LOCATION_INTERFACE IRP_MN_QUERY_INTERFACE IRP,驱动程序提供指向PNP_LOCATION_INTERFACE结构的指针,该结构包含指向接口的各个例程的指针, PnpGetLocationString 例程提供设备SPDRP_LOCATION_PATHS属性中特定于设备的部分。