WDF驱动开发-WDF总线枚举(一)

2024-06-24 11:52
文章标签 wdf 枚举 总线 驱动 开发

本文主要是介绍WDF驱动开发-WDF总线枚举(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

支持在总线驱动程序中进行 PnP 和电源管理

某些设备永久插入系统,而其他设备可以在系统运行时插入和拔出电源。 总线驱动 必须识别并报告连接到其总线的设备,并且他们必须发现并报告系统中设备的到达和离开情况。

总线驱动程序标识和报告的设备称为总线的 子设备。 标识和报告子设备的过程称为 总线枚举。 在总线枚举期间,总线驱动程序会为其子 设备创建设备对象 。 

总线驱动程序本质上是同时处理总线枚举的功能驱动程序,或者很少是Filter驱动程序。 总线驱动程序通常是总线适配器的功能驱动程序,但它不是连接到总线的子设备的功能驱动程序。

总线驱动程序还具有与功能驱动程序相同的 PnP 和电源管理职责。

枚举总线上的设备

总线枚举 是确定哪些子设备连接到父设备的行为。 父设备通常是总线适配器,但它也可以是支持多种功能(例如声音卡)的设备,每个功能需要一组单独的驱动程序。

Kernel-Mode 驱动程序框架 (KMDF) 支持两种类型的总线枚举:

  • 静态枚举易于实现,如果子设备的数量和类型不是特定于系统的,并且硬件接通后不会更改,则静态枚举是理想的;
  • 当子设备的数量或类型从一台计算机更改为另一台计算机时,应使用动态枚举;

总线驱动程序可以使用或两种类型的总线枚举。

静态枚举

静态枚举 是驱动程序在系统初始化期间检测和报告设备是否存在的功能,并且报告系统配置的后续更改的能力有限。

如果设备或功能子单元的数量和类型是预先确定的和永久的,并且不依赖于运行驱动程序的系统的配置,则总线驱动程序可以使用静态枚举。

例如,声音卡的驱动程序可以充当总线驱动程序, PDO为卡的每个功能(例如 MIDI、音频和游戏杆)创建单独的物理设备对象。

静态子列表

框架通过提供静态子列表,使驱动程序能够支持静态枚举。 每个静态子列表表示连接到父设备的子设备列表。 父设备的总线驱动程序必须标识父设备的子设备,将它们添加到父设备的静态子设备列表中,并为每个子设备创建 PDO。

创建静态子列表

每次驱动程序创建一个框架设备对象,该框架对象表示 (设备的 FDO) 功能设备对象时,框架都会为设备创建一个空的静态子列表。

1. 当框架调用总线驱动程序的 EvtDriverDeviceAdd 回调函数时,回调函数必须调用 WdfDeviceCreate 为父设备创建 FDO。 

2. 然后,驱动程序必须枚举父设备的子级,为子级创建 PDO,并将子级添加到子级列表。

3. (可选)驱动程序可以调用 WdfDeviceSetBusInformationForChildren ,为框架提供有关总线的信息。 建议这样做,因为这样可以更轻松地让子设备和应用识别总线。

若要为检测到的子设备创建 PDO,总线驱动程序必须:

  • 调用 WdfPdoInitAllocate 以获取 WDFDEVICE_INIT 结构;
  • 初始化WDFDEVICE_INIT结构;
  • 调用 WdfDeviceCreate 以创建表示 PDO 的框架设备对象;

调用 WdfDeviceCreate 后,驱动程序必须调用 WdfFdoAddStaticChild 以将子设备添加到子列表。

修改静态子列表

由于驱动程序应仅对预先确定的永久性设备配置使用静态子列表,因此驱动程序在创建静态子列表后几乎不需要修改它。 如果驱动程序确定子设备变得不可访问,驱动程序可以调用 WdfPdoMarkMissing。 如果子设备仍可访问,但变得无响应且不可用,则驱动程序应将 WDF_DEVICE_STATE 结构的 Failed 成员设置为 WdfTrue,然后调用 WdfDeviceSetDeviceState。

遍历静态子列表

如果需要检索静态子列表的内容,驱动程序可以通过执行以下操作遍历该列表:

  • 调用 WdfFdoLockStaticChildListForIteration;
  • 根据需要多次调用 WdfFdoRetrieveNextStaticChild ;
  • 调用 WdfFdoUnlockStaticChildListFromIteration;

动态枚举

动态枚举 是驱动程序能够检测和报告在系统运行时连接到系统的设备的数量和类型的更改。

如果连接到父设备的设备数量或类型取决于系统的配置,则总线驱动程序必须使用动态枚举。 其中一些设备可能始终连接到系统,有些设备可能在系统运行时接通电源并拔下电源。

例如,插入系统 PCI 总线的设备的数量和类型取决于系统,但它们是永久性的,除非用户关闭电源、打开外壳,并使用螺丝刀添加或删除设备。 另一方面,用户可以通过在系统运行时插入或拔下电缆来添加或删除 USB 设备。

动态子列表

框架通过提供框架子列表对象,使驱动程序能够支持动态枚举。 每个子列表对象表示连接到父设备的子设备的列表。 父设备的总线驱动程序必须标识父设备的子设备,将它们添加到父设备的子列表中,并为每个子设备创建物理设备对象 (PDO) 。

每次驱动程序创建表示设备的 FDO 的框架设备对象时,框架都会为设备创建一个空的默认子列表。 驱动程序可以通过调用 WdfFdoGetDefaultChildList 来获取设备默认子列表的句柄。 通常,如果要编写枚举设备子级的总线驱动程序,驱动程序可以将子级添加到默认子列表。 如果需要创建其他子列表,驱动程序可以调用 WdfChildListCreate。

在驱动程序可以使用子列表之前,它必须通过初始化 WDF_CHILD_LIST_CONFIG 结构并将结构传递给 WdfFdoInitSetDefaultChildListConfig对于默认子列表或 WdfChildListCreate对于其他子列表来配置子列表对象。

动态子级说明

每次总线驱动程序标识子设备时,都必须将子设备的说明添加到子列表中。 子说明由必需的标识说明和可选的地址说明组成。

标识说明 是一种结构,它包含唯一标识驱动程序枚举的每个设备的信息。 驱动程序定义此结构,但其第一个成员必须是 WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER 结构。

通常,标识说明包含设备的 设备标识字符串(可能是序列号)以及有关设备在总线上的位置的信息,例如槽号。

驱动程序可以提供以下一组回调函数,这些函数允许框架操作标识说明中的信息:

  • EvtChildListIdentificationDescriptionCompare,用于比较两个标识描述结构的内容;
  • EvtChildListIdentificationDescriptionCopy,将一个标识说明结构的内容复制到另一个标识说明结构;
  • EvtChildListIdentificationDescriptionDuplicate,它通过复制现有标识说明结构并在必要时分配其他缓冲区来创建新的标识说明;
  • EvtChildListIdentificationDescriptionCleanup,它解除分配由 EvtChildListIdentificationDescriptionDuplicate 回调函数分配的缓冲区;

通常,如果驱动程序的标识说明结构包含指向动态分配的缓冲区的指针,则需要提供这些回调函数。 

地址说明 地址说明是一种结构,它包含驱动程序所需的信息,以便它可以访问其总线上的设备,如果信息可以在设备接通电源时发生更改。 驱动程序定义此结构,但其第一个成员必须是 WDF_CHILD_ADDRESS_DESCRIPTION_HEADER 结构。

地址说明是可选的。 如果设备地址信息在设备接通电源和拔出电源之间无法更改,则设备的所有地址信息都可以存储在标识说明中。 例如,USB 控制器在设备接通电源时将地址分配给设备,并且这些地址不会更改。

另一方面,一些总线使用可能会更改的寻址信息。 例如,IEEE 1394 总线使用“代数”,即已发生的总线重置次数。 向 IEEE 1394 设备发送的每个异步 I/O 请求都必须包含生成计数。 由于此地址信息可能会更改,因此驱动程序必须将其存储在地址说明中。

驱动程序可以提供以下回调函数集来操作地址说明中的信息:

  • EvtChildListAddressDescriptionCopy,将一个地址说明结构的内容复制到另一个地址说明结构;
  • EvtChildListAddressDescriptionDuplicate,它通过复制现有地址说明结构并在必要时分配其他缓冲区来创建新的地址说明;
  • EvtChildListAddressDescriptionCleanup,可解除分配由 EvtChildListAddressDescriptionDuplicate 回调函数分配的缓冲区;

通常,如果驱动程序的地址描述结构包含指向动态分配的缓冲区的指针,则需要提供这些回调函数。 

将设备添加到动态子列表

当框架调用总线驱动程序的 EvtDriverDeviceAdd 回调函数时,回调函数必须调用 WdfDeviceCreate 为父设备(通常是总线适配器)创建 FDO。 然后,驱动程序必须枚举父设备的子级,并将子级添加到子列表中。

(可选)驱动程序可以调用 WdfDeviceSetBusInformationForChildren ,为框架提供有关总线的信息。 建议这样做,因为它使子设备和应用更容易识别总线。

若要将子级添加到子列表,驱动程序必须为找到的每个子设备调用 WdfChildListAddOrUpdateChildDescriptionAsPresent 。 此调用通知框架驱动程序已发现连接到父设备的子设备。 当驱动程序调用 WdfChildListAddOrUpdateChildDescriptionAsPresent 时,它会提供标识说明和地址说明(可选)。

在驱动程序调用 WdfChildListAddOrUpdateChildDescriptionAsPresent 来报告新设备后,框架会通知 PnP 管理器新设备存在。 然后,PnP 管理器为新设备生成设备堆栈和驱动程序堆栈。 在此过程中,框架调用总线驱动程序的 EvtChildListCreateDevice 回调函数。 此回调函数必须调用 WdfDeviceCreate 才能为新设备创建 PDO。

通常,多个子设备连接到父设备,因此总线驱动程序需要多次调用 WdfChildListAddOrUpdateChildDescriptionAsPresent 。 执行此操作的最有效方法是:

  • 调用 WdfChildListBeginScan;
  • 为每个子设备调用 WdfChildListAddOrUpdateChildDescriptionAsPresent ;
  • 调用 WdfChildListEndScan;

如果使用对 WdfChildListBeginScan 和 WdfChildListEndScan 的调用包围驱动程序的动态枚举,框架会将所有更改存储到子列表,并在驱动程序调用 WdfChildListEndScan 时通知 PnP 管理器所做的更改。 稍后,框架会为子列表中的每台设备调用总线驱动程序的 EvtChildListCreateDevice 回调函数。 此回调函数调用 WdfDeviceCreate 为每个新设备创建 PDO。

当驱动程序调用 WdfChildListBeginScan 时,框架会将以前报告的所有设备标记为不再存在。 因此,驱动程序必须为驱动程序可以检测的所有子级,而不仅仅是新发现的子级调用 WdfChildListAddOrUpdateChildDescriptionAsPresent 。 若要将单个子级添加到子列表,驱动程序可以调用 WdfChildListUpdateAllChildDescriptionsAsPresent ,而无需先调用 WdfChildListBeginScan。

更新动态子列表

有两种常见方法来更新动态子列表中的信息:

  • 当父设备收到指示子项到达或删除的中断时,如果设备已接通电源,则驱动程序的 EvtInterruptDpc 回调函数将调用 WdfChildListAddOrUpdateChildDescriptionAsPresent ;如果设备已拔出电源,则 调用 WdfChildListUpdDescriptionAsMissing ;
  • 驱动程序可以提供 EvtChildListScanForChildren 回调函数,每次父设备进入其工作 (D0) 状态时,框架都会调用该函数。 此回调函数应通过调用 WdfChildListBeginScan、 WdfChildListAddOrUpdateChildDescriptionAsPresent (或 WdfChildListUpdateAllChildDescriptionsAsPresent) 和 WdfChildListEndScan 来枚举所有子设备;

可以在驱动程序中使用其中一种或两种技术。

遍历动态子列表

如果希望驱动程序检查子列表的内容,它可以使用以下方法之一遍历列表:

1. 若要获取每个子设备说明的内容(一次一个),驱动程序可以:

调用 WdfChildListBeginIteration。
根据需要多次调用 WdfChildListRetrieveNextDevice。
调用 WdfChildListEndIteration。
调用 WdfChildListBeginIteration 时,驱动程序指定 WDF_RETRIEVE_CHILD_FLAGS类型的标志,该标志指示框架是应检索所有设备说明还是仅检索子集。 当 WdfChildListRetrieveNextDevice 找到匹配项时,它将检索子设备的标识和地址说明,以及其设备对象的句柄。

2. 如果需要获取当前包含在子设备说明中的地址说明,驱动程序可以调用 WdfChildListRetrieveAddressDescription,并指定标识说明。 框架遍历子列表,直到找到具有匹配标识说明的子设备,然后检索地址说明。

3. 如果需要获取与特定子设备关联的框架设备对象的句柄,驱动程序可以调用 WdfChildListRetrievePdo。 框架遍历子列表,直到找到具有匹配标识说明的子设备,然后返回设备对象句柄。 请务必使用 WdfChildListBeginIteration 和 WdfChildListEndIteration 包装调用,以防止调用方在另一个线程上突然删除 PDO。

访问 PDO 的标识和地址说明

驱动程序可以调用以下方法来访问 PDO 的标识说明或地址说明:

  • WdfPdoRetrieveIdentificationDescription,用于检索与 PDO 关联的标识说明;
  • WdfPdoRetrieveAddressDescription,用于检索与 PDO 关联的地址说明;
  • WdfPdoUpdateAddressDescription,用于更新与 PDO 关联的地址说明;

处理重新枚举请求

支持动态枚举的基于框架的总线驱动程序可以接收通过 REENUMERATE_SELF_INTERFACE_STANDARD 接口恢复特定子设备的请求。

 

这篇关于WDF驱动开发-WDF总线枚举(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1090071

相关文章

Eclipse+ADT与Android Studio开发的区别

下文的EA指Eclipse+ADT,AS就是指Android Studio。 就编写界面布局来说AS可以边开发边预览(所见即所得,以及多个屏幕预览),这个优势比较大。AS运行时占的内存比EA的要小。AS创建项目时要创建gradle项目框架,so,创建项目时AS比较慢。android studio基于gradle构建项目,你无法同时集中管理和维护多个项目的源码,而eclipse ADT可以同时打开

Python应用开发——30天学习Streamlit Python包进行APP的构建(9)

st.area_chart 显示区域图。 这是围绕 st.altair_chart 的语法糖。主要区别在于该命令使用数据自身的列和指数来计算图表的 Altair 规格。因此,在许多 "只需绘制此图 "的情况下,该命令更易于使用,但可定制性较差。 如果 st.area_chart 无法正确猜测数据规格,请尝试使用 st.altair_chart 指定所需的图表。 Function signa

JavaWeb系列六: 动态WEB开发核心(Servlet) 上

韩老师学生 官网文档为什么会出现Servlet什么是ServletServlet在JavaWeb项目位置Servlet基本使用Servlet开发方式说明快速入门- 手动开发 servlet浏览器请求Servlet UML分析Servlet生命周期GET和POST请求分发处理通过继承HttpServlet开发ServletIDEA配置ServletServlet注意事项和细节 Servlet注

手把手教你入门vue+springboot开发(五)--docker部署

文章目录 前言一、前端打包二、后端打包三、docker运行总结 前言 前面我们重点介绍了vue+springboot前后端分离开发的过程,本篇我们结合docker容器来研究一下打包部署过程。 一、前端打包 在VSCode的命令行中输入npm run build可以打包前端代码,出现下图提示表示打包完成。 打包成功后会在前端工程目录生成dist目录,如下图所示: 把

Sapphire开发日志 (十) 关于页面

关于页面 任务介绍 关于页面用户对我组工作量的展示。 实现效果 代码解释 首先封装一个子组件用于展示用户头像和名称。 const UserGrid = ({src,name,size,link,}: {src: any;name: any;size?: any;link?: any;}) => (<Box sx={{ display: "flex", flexDirecti

ROS2从入门到精通4-4:局部控制插件开发案例(以PID算法为例)

目录 0 专栏介绍1 控制插件编写模板1.1 构造控制插件类1.2 注册并导出插件1.3 编译与使用插件 2 基于PID的路径跟踪原理3 控制插件开发案例(PID算法)常见问题 0 专栏介绍 本专栏旨在通过对ROS2的系统学习,掌握ROS2底层基本分布式原理,并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。 🚀详情:《ROS2从入门到精通》 1 控制插

JavaWeb 学习笔记 spring+jdbc整合开发初步

JdbcTemplate类是Spring的核心类之一,可以在org.springframework.jdbc.core中找到它。JdbcTemplate类在内部已经处理数据库的建立和释放,可以避免一些常见的错误。JdbcTemplate类可直接通过数据源的应用实例化,然后在服务中使用,也可在xml配置中作为JavaBean应用给服务使用直接上一个实例步骤1.xml配置 <?xml version

Android开发的一些不错的学习资料

一个有趣的学习网站:http://hukai.me/android-training-course-in-chinese/index.html 这个是一些基础的教程,看看很好的,赏心悦目。 工具下载网站:http://www.androiddevtools.cn/ 这里的东西很不错,AndroidStudio,UIStyle的压缩工具,开发必备。

同城跑腿APP开发,随叫随到超方便!

随着移动互联网的发展和人们生活节奏的加快,越来越多的人们没有闲暇的时间来做一些繁琐的事情,比如说买药、挂号、排队、送花、取文件等等。如果没有时间去处理这些事情怎么办?开发同城跑腿APP,提供跑腿服务,随时办事随时下单,只需在手机上轻轻一点,就可完成跑腿需求。 首先,跑腿小程序有几种开发方式。第一种是自己组建开发,这种方式比较适合有软件开发能力的企业,比较花费时间和金钱成本。第二种是找到第三方

一二三应用开发平台应用开发示例(4)——视图类型介绍以及新增、修改、查看视图配置

调整上级属性类型 前面为了快速展示平台的低代码配置功能,将实体文件夹的数据模型上级属性的数据类型暂时配置为文本类型,现在我们调整下,将其数据类型调整为实体,如下图所示: 数据类型需要选择实体,并在实体选择框中选择自身“文件夹” 这时候,再点击生成代码,平台会报错,提示“实体【文件夹】未设置主参照视图”。这是因为文件夹选择的功能页面,同样是基于配置产生的,因为视图我们还没有配置,所以会报错。