将 PnP 设备添加到正在运行的系统
本部分介绍系统配置用户已添加到正在运行的计算机的 PnP 设备时发生的事件序列。 本讨论重点介绍了 PnP 管理器、总线驱动程序以及函数和筛选器驱动程序在枚举和配置新设备时的角色。
此讨论的大部分内容也与配置启动计算机时存在的 PnP 设备相关。 具体而言,在 INF 文件中将驱动程序标记为SERVICE_DEMAND_START的设备实质上配置方式与设备是动态添加还是启动时存在的方式相同。
下图显示了配置设备的第一步,从用户将硬件插入计算机时开始。
以下注释对应于上图中带圆圈的数字:
用户将 PnP 设备插入到 PnP 总线上的可用槽。
在此示例中,用户将 PnP USB 游戏杆插入 USB 主机控制器上的集线器。 USB 集线器是 PnP 总线设备,因为子设备可以连接到它上面。
总线设备的功能驱动程序确定其总线上有新设备。
驱动程序如何确定这取决于总线体系结构。 对于某些总线,总线功能驱动程序接收新设备的热插拔通知。 如果总线不支持热插拔通知,则用户必须在控制面板中采取适当的操作来枚举总线。
在此示例中,USB 总线支持热插拔通知,因此 USB 总线的功能驱动程序会收到其子节点已更改的通知。
总线设备的功能驱动程序通知 PnP 管理器说,其子设备集合已发生变化。
功能驱动程序通过调用 IoInvalidateDeviceRelations,并以 Type 为 BusRelations 的方式通知 PnP 管理器。
PnP 管理器查询总线的驱动程序,以获取总线上设备的当前列表。
PnP 管理器向总线的设备堆栈发送一个 IRP_MN_QUERY_DEVICE_RELATIONS 请求。 Parameters.QueryDeviceRelations.Type 值是 BusRelations,指示 PnP 管理器要求当前总线上存在的设备列表(总线关系)。
PnP 管理器将 IRP 发送到总线设备堆栈中的顶部驱动程序。 根据 PnP IRP 的规则,堆栈中的每个驱动程序(如果适用)处理 IRP,并将 IRP 向下传递到下一个驱动程序。
总线设备的功能驱动程序处理 IRP。
有关处理此 IRP 的详细信息,请参阅 IRP_MN_QUERY_DEVICE_RELATIONS 的参考页面。
在此示例中,USB 集线器驱动程序处理集线器 FDO的 IRP。 中心驱动程序为游戏杆设备创建了 PDO,并在 IRP 返回的子设备列表中包含一个指向游戏杆 PDO 的引用指针。
当 USB 集线器的父总线驱动程序(USB 主机控制器类/微型类驱动程序对)完成 IRP 时,IRP 会通过集线器驱动程序注册的任何 IoCompletion 例程返回设备堆栈。
请注意,总线函数驱动程序通过请求 PnP 管理器查询其子设备列表来报告其子设备列表中的更改。 总线设备的所有驱动程序都可以看到由此产生的 IRP_MN_QUERY_DEVICE_RELATIONS 请求。 通常,总线功能驱动程序是处理 IRP 和报告子级的唯一驱动程序。 在某些设备堆栈中,存在总线筛选器驱动程序并参与构建总线关系列表。 一个示例是 ACPI,它作为 ACPI 设备的总线筛选器驱动程序附加。 在某些设备堆栈中,非总线筛选器驱动程序处理 IRP_MN_QUERY_DEVICE_RELATIONS 请求,但这并不常见。
此时,PnP 管理器具有总线上设备的最新列表。 然后,PnP 管理器确定是否有任何设备是新到达的或已被移除。 在此示例中,有一个新设备。 下图显示了 PnP 管理器为新设备创建 devnode 并开始配置该设备。
以下注释对应于上图中带圆圈的数字:
PnP 管理器为总线上的新子设备创建 devnode。
PnP 管理器将 IRP_MN_QUERY_DEVICE_RELATIONS IRP 中返回的总线关系列表与当前记录在 PnP 设备树中的总线的子设备列表进行对比。 PnP 管理器为每个新设备创建一个 devnode,并对已删除的任何设备启动删除处理。
在此示例中,有一个新设备(游戏杆),因此 PnP 管理器为游戏杆创建了一个 devnode。 此时,为摇杆配置的唯一驱动程序是上级 USB 集线器总线驱动程序,该驱动程序创建了摇杆的 PDO。 任何可选的总线筛选器驱动程序也会存在于设备堆栈中,但该示例省略总线筛选器驱动程序,以便于简单。
上图中两个 devnode 之间的宽箭头表示游戏杆设备节点是 USB 集线器 devnode 的子节点。
PnP 管理器收集有关新设备的信息,并开始配置设备。
PnP 管理器将一系列 IRP 发送到设备堆栈,以收集有关设备的信息。 此时,设备堆栈仅由设备的父总线驱动程序创建的 PDO 和任何可选总线筛选器驱动程序的筛选器 DO 组成。 因此,总线驱动程序和总线筛选器驱动程序是唯一响应这些 IRP 的驱动程序。 在此示例中,游戏杆设备堆栈中唯一的驱动程序是父总线驱动程序,即 USB 中心驱动程序。
PnP 管理器通过将 IRP 发送到设备堆栈来收集有关新设备的信息。 这些 IRP 包括以下内容:
IRP_MN_QUERY_ID,为以下每种类型的硬件 ID 提供单独的 IRP:
- BusQueryDeviceID
- BusQueryInstanceID
- BusQueryHardwareIDs
- BusQueryCompatibleIDs
- BusQueryContainerID
IRP_MN_QUERY_DEVICE_TEXT,以下每个项的单独 IRP:
- DeviceTextDescription
- DeviceTextLocationInformation
PnP 管理器在此阶段发送上面列出的 IRP 来处理新的 PnP 设备,但不一定按列出的顺序,因此不应假设 IRP 的发送顺序。 此外,不应假定 PnP 管理器仅发送上面列出的 IRP。
PnP 管理器检查注册表,以确定以前是否已在此计算机上安装设备。 PnP 管理器检查位于 Enum 分支下的设备的 <enumerator>\<deviceID> 子项。 在此示例中,设备是新的设备,必须“从头开始”进行配置。
PnP 管理器将有关设备的信息存储在注册表中。
注册表的 Enum 分支保留供操作系统组件使用,其布局可能会更改。 驱动程序编写器必须使用系统例程来提取与驱动程序相关的信息。 请勿从驱动程序直接访问 Enum 分支。 以下枚举信息仅出于调试目的列出。
PnP 管理器在设备枚举器的项下为该设备创建一个子项。
PnP 管理器创建一个名为 HKLM\System\CurrentControlSet\Enum\<enumerator>\<deviceID> 的子项。 如果 <enumerator> 子项不存在,它将创建该子项。
enumerator 是一个基于 PnP 硬件标准发现 PnP 设备的组件。 枚举器的任务由 PnP 总线驱动程序与 PnP 管理器合作执行。 设备通常由其父总线驱动程序(如 PCI 或 PCMCIA)枚举。 某些设备由总线筛选器驱动程序(如 ACPI)枚举。
PnP 管理器为此设备实例创建一个子项。
如果对于 IRP_MN_QUERY_CAPABILITIES,Capabilities.UniqueID 返回为 TRUE,则设备的唯一 ID 在整个系统中是唯一的。 否则,PnP 管理器将修改 ID,使其在整个系统内唯一。
PnP 管理器创建一个名为 HKLM\System\CurrentControlSet\Enum\<enumerator>\<deviceID>\<instanceID> 的子项。
PnP 管理器将有关设备的信息写入设备实例的子项。
PnP 管理器存储信息,包括以下内容(如果为设备提供的话):
- DeviceDesc - 来自 IRP_MN_QUERY_DEVICE_TEXT
- 位置 - 来自 IRP_MN_QUERY_DEVICE_TEXT
- 功能 - 来自 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 管理器已准备好找到设备的函数驱动程序并筛选驱动程序(如果有)。 (请参阅下图。
以下注释对应于上图中的带编号圆:
内核模式 PnP 管理器与用户模式 PnP 管理器和用户模式设置组件协调,以查找设备的功能驱动程序和筛选驱动程序(如果有)。
内核模式 PnP 管理器将事件排队到用户模式 PnP 管理器,标识需要安装的设备。 特权用户登录后,用户模式组件将继续查找驱动程序。 有关安装设备中的安装组件及其角色的信息,请参阅 设备和驱动程序安装。
用户模式设置组件指导内核模式 PnP 管理器加载功能和筛选驱动程序。
用户模式组件回调到内核模式以加载驱动程序,导致调用其 AddDevice 例程。
下图显示了 PnP 管理器如何加载(如果适用)驱动程序,随后调用其 AddDevice 例程,并指示驱动程序启动设备。
以下注释对应于上图中的带编号圆:
下层筛选驱动程序
在功能驱动程序附加到设备堆栈之前,PnP 管理器将处理任何下层筛选器驱动程序。 对于每个下层筛选器驱动程序,如果驱动程序尚未加载,PnP 管理器将调用驱动程序的 DriverEntry 例程。 然后,PnP 管理器调用驱动程序 AddDevice 例程。 在 AddDevice 例程中,筛选器驱动程序将创建一个筛选器设备对象(filter DO),并将其附加到设备堆栈(IoAttachDeviceToDeviceStack)。 将其设备对象附加到设备堆栈后,驱动程序便被作为该设备的驱动程序来使用。
在 USB 游戏杆示例中,设备有一个下层筛选器驱动程序。
函数驱动程序
附加任何下层筛选器后,PnP 管理器将处理功能驱动程序。 如果驱动程序尚未加载,PnP 管理器将调用函数驱动程序的 DriverEntry 例程,然后调用函数驱动程序的 AddDevice 例程。 函数驱动程序创建一个函数设备对象(FDO),并将其附加到设备堆栈。
在此示例中,USB 游戏杆的函数驱动程序实际上是一对驱动程序:HID 类驱动程序和 HID 微型类驱动程序。 这两个驱动程序协同工作,充当函数驱动程序。 驱动程序对只创建一个 FDO,并将其附加到设备堆栈。
上层筛选器驱动程序
附加函数驱动程序后,PnP 管理器将处理任何上层筛选器驱动程序。
在此示例中,设备有一个上层过滤驱动程序。
分配资源和启动设备
PnP 管理器根据需要将资源分配给设备,并发出 IRP 来启动设备。
分配资源
在配置过程中,PnP 管理器从设备的父总线驱动程序收集了设备的硬件资源要求。 为设备加载完整的驱动程序集后,PnP 管理器会将 IRP_MN_FILTER_RESOURCE_REQUIREMENTS 请求发送到设备堆栈。 堆栈中的所有驱动程序都有机会处理此 IRP,并在必要时修改设备的资源要求列表。
如果设备需要任何资源,则 PnP 管理器会根据设备的要求以及当前可用的资源将资源分配给设备。
PnP 管理器可能需要重新排列现有设备的资源分配,以满足新设备的需求。 此类资源重新分配称为“重新平衡”。现有设备的驱动程序在重新平衡过程中接收一系列停止和启动IRP,但重新平衡必须对用户透明。
在 USB 游戏杆示例中,USB 设备不需要硬件资源,因此 PnP 管理器将资源列表设置为 NULL。
启动设备(IRP_MN_START_DEVICE)
将资源分配给设备后,PnP 管理器会将 IRP_MN_START_DEVICE IRP 发送到设备堆栈,以指示驱动程序启动设备。
设备启动后,PnP 管理器再向设备的驱动程序发送三个 IRP:
-
启动 IRP 成功完成后,PnP 管理器向设备堆栈发送另一个 IRP_MN_QUERY_CAPABILITIES IRP。 设备的所有驱动程序都可以选择处理 IRP。 此时,PnP 管理器会在附加所有驱动程序并启动设备后发送此 IRP,因为函数或筛选器驱动程序可能需要访问设备以收集功能信息。
-
此 IRP 为驱动程序提供了机会,例如,报告设备不应显示在用户界面(如设备管理器和热插拔程序)中。 这对于系统中存在但在当前配置下无法使用的设备很有用,例如笔记本计算机上的游戏端口,在笔记本计算机未连接基座时,该端口无法使用。
IRP_MN_QUERY_DEVICE_RELATIONS 用于总线关系
PnP 管理器发送此 IRP 以确定设备是否具有任何子设备。 如果是这样,PnP 管理器将配置每个子设备。
使用 GUID_PNP_LOCATION_INTERFACE
GUID_PNP_LOCATION_INTERFACE 接口为设备提供 SPDRP_LOCATION_PATHS 即插即用 (PnP) 设备属性。
若要在你的驱动程序中实现此接口,请处理 IRP_MN_QUERY_INTERFACE IRP,并将 InterfaceType 设置为 GUID_PNP_LOCATION_INTERFACE。 驱动程序提供指向 PNP_LOCATION_INTERFACE 结构的指针,该结构包含指向接口的各个例程的指针。 PnpGetLocationString routine 提供设备 SPDRP_LOCATION_PATHS 属性的设备特定部分。