WDF驱动开发-I/O目标与专用USBI/O目标

Windows 驱动程序框架 (WDF) 驱动程序转发 I/O 请求或创建新请求并将其发送到另一个驱动程序就被称为 I/O 目标。

当 功能驱动程序、Filter驱动程序、微型端口驱动程序 收到 I/O 请求时,驱动程序可能能够单独处理请求,或者可能需要其他驱动程序的帮助。 如果驱动程序需要帮助,它可以将请求转发给另一个驱动程序,也可以创建一个或多个新请求并将其发送到另一个驱动程序。

在 Kernel-Mode Driver Framework 中, I/O 目标 表示作为 I/O 请求目标的设备对象。 函数、筛选器或微型端口驱动程序可以使用 I/O 目标将 I/O 请求发送到另一个驱动程序。 这些驱动程序通常会将其 I/O 请求发送到驱动程序堆栈中下一个较低的驱动程序。 因此,每个基于框架的函数、筛选器和微型端口驱动程序都有每个设备的 本地 I/O 目标 ,这是设备的下一个较低驱动程序。

有时,驱动程序必须将 I/O 请求发送到另一个目标,即不同驱动程序堆栈的顶部,或者发送驱动程序堆栈中的某些其他驱动程序。 因此,该框架还提供 远程 I/O 目标,这些目标由除本地 I/O 目标以外的所有 I/O 目标组成。

每个 I/O 目标由一个 I/O 目标对象表示。 每个 I/O 目标对象主要是一个队列,用于控制何时将请求传递到目标设备对象。 当驱动程序向 I/O 目标发送请求时,框架会将请求存储在队列中,直到它可以将请求传递到目标设备对象。

框架支持常规 I/O 目标和专用 I/O 目标:

  • 常规 I/O 目标 可由所有函数、筛选器和微型端口驱动程序使用,但它们不支持任何特定于设备的特殊数据格式;
  • 专用 I/O 目标使函数、筛选器和微型端口驱动程序能够轻松发送需要特定于目标的特殊数据格式的 I/O 请求。 目前,该框架只支持USB I/O 目标;

如果框架提供支持设备数据格式的专用 I/O 目标,则驱动程序应使用专用 I/O 目标。 否则,驱动程序应使用常规 I/O 目标。

常规 I/O 目标不支持特定于设备的特殊数据格式,例如 USB 请求块。 在驱动程序将数据发送到常规 I/O 目标之前,它们必须以 I/O 目标可以解释的格式将数据放入写入缓冲区。 同样,当驱动程序从常规 I/O 目标读取数据时,驱动程序必须能够解释从目标接收的数据缓冲区的内容。

常规 I/O 目标为本地或远程:

  • 本地 I/O 目标:每个基于框架的功能驱动程序、Filter驱动程序、微型端口驱动程序都有每个驱动程序设备的本地 I/O 目标。 设备的本地 I/O 目标始终是驱动程序堆栈中下一个较低的驱动程序;
  • 远程 I/O 目标: 远程 I/O 目标表示不同驱动程序堆栈的顶部, (很少) 当前驱动程序堆栈中的不同驱动程序;

USB I/O 目标

从版本 2 开始,Kernel-Mode Driver Framework (KMDF) 和 User-Mode Driver Framework (UMDF) 驱动程序可以和通用串行总线 (USB) 设备进行交互。

每个 USB 设备和 USB 设备接口支持的每个管道都有单独的 I/O 目标。 控制 USB 设备句柄发送到设备的 I/O 目标传输。 特定管道句柄的 I/O 传输将发送到该管道的 I/O 目标。

框架通过发送 USB 请求块 (URB) 来与 USB 设备的 I/O 目标通信。 框架提供对象方法,用于从驱动程序中隐藏 URL,以便驱动程序不必自行生成和发送它们。 如果希望驱动程序生成 URL,KMDF 驱动程序可以使用一组额外的对象方法来生成和发送 URL。

使用 USB 设备

下面介绍从版本 2 开始,Kernel-Mode驱动程序框架 (KMDF) 或User-Mode驱动程序框架 (UMDF) 驱动程序可以使用 Windows 驱动程序框架 (WDF) 提供的 USB 设备对象方法执行的操作。它包含以下部分:

创建 USB 设备对象
配置 USB 设备
获取设备信息
获取 USB 描述符
发送控制传输
重置和Power-Cycling设备的端口
将 URB 发送到设备。

创建 USB 设备对象

若要使用框架的 USB I/O 目标对象 (WDFUSBDEVICE、WDFUSBINTERFACE 和 WDFUSBPIPE) ,客户端驱动程序必须首先调用 WdfUsbTargetDeviceCreateWithParameters 来创建 USB 设备对象。 通常,驱动程序从其 EvtDevicePrepareHardware 回调函数调用 WdfUsbTargetDeviceCreateWithParameters。

当驱动程序调用 WdfUsbTargetDeviceCreateWithParameters 时,框架会创建一个 WDFUSBDEVICE 对象,并将其与表示 USB 设备的 FDO 相关联。 方法返回新的框架 USB 设备对象的句柄,USB 客户端驱动程序随后可以使用该句柄与物理设备通信。

调用 WdfUsbTargetDeviceCreateWithParameters 后,驱动程序可以调用 WdfUsbTargetDeviceGetDeviceDescriptor 和 WdfUsbTargetDeviceRetrieveConfigDescriptor 从设备获取 USB 描述符。 这些描述符包含有关设备的第一个配置、其接口设置及其定义的终结点的信息。 USB 描述符在官方 USB 规范中定义。

配置 USB 设备

WdfUsbTargetDeviceCreateWithParameters 方法还会为设备的第一个配置包含的每个 USB 接口创建框架 USB 接口对象。

调用 WdfUsbTargetDeviceCreateWithParameters 后,客户端驱动程序必须调用 WdfUsbTargetDeviceSelectConfig 来选择配置。 此方法为所选配置中接口的每个备用设置创建框架接口对象。

方法还创建管道对象,这些对象表示在所选配置的每个接口的每个备用设置中定义的终结点。

选择配置后,可以根据需要更改配置接口的 备用设置 。

还可以调用 WdfUsbTargetDeviceSelectConfig 来取消配置设备。

获取设备信息

配置设备后,客户端驱动程序可以调用以下方法来获取有关 USB 设备的信息:

  • WdfUsbTargetDeviceQueryUsbCapability:确定主控制器和 USB 驱动程序堆栈是否支持特定功能。 在调用 WdfUsbTargetDeviceQueryUsbCapability 之前,驱动程序必须调用 WdfUsbTargetDeviceCreateWithParameters;
  • WdfUsbTargetDeviceGetIoTarget:返回与 USB 设备关联的 I/O 目标对象的句柄。 驱动程序可以将此句柄传递给 WdfRequestSend 或 WdfIoTargetStop;
  • WdfUsbTargetDeviceRetrieveInformation:检索与 USB 设备关联的版本和功能信息;
  • WdfUsbTargetDeviceIsConnectedSynchronous (仅KMDF ):确定设备是否已连接;
  • WdfUsbTargetDeviceRetrieveCurrentFrameNumber (仅 KMDF):检索当前 USB 帧编号;

获取 USB 描述符

若要获取 USB 设备描述符中包含的 Unicode 字符串,驱动程序可以调用以下任一方法:

  • WdfUsbTargetDeviceGetDeviceDescriptor:获取设备的 USB 设备描述符;
  • WdfUsbTargetDeviceRetrieveConfigDescriptor:获取设备的 USB 配置描述符、接口描述符和终结点描述符;
  • WdfUsbTargetDeviceQueryString:将 Unicode 字符串复制到驱动程序提供的缓冲区;
  • WdfUsbTargetDeviceAllocAndQueryString:将 Unicode 字符串复制到框架提供的缓冲区;
  • WdfUsbTargetDeviceFormatRequestForString:设置 Unicode 字符串请求的格式。 驱动程序可以调用 WdfRequestSend 以同步或异步方式发送请求;

发送控制传输

驱动程序可以调用以下方法来发送 I/O 请求,该请求描述标准、特定于设备类或特定于供应商的 USB 控制传输。

  • WdfUsbTargetDeviceSendControlTransferSynchronously:同步发送 USB 控制传输请求;
  • WdfUsbTargetDeviceFormatRequestForControlTransfer:设置 USB 控制传输请求的格式。 驱动程序可以调用 WdfRequestSend 以同步或异步方式发送请求;

重置和Power-Cycling设备的端口

驱动程序可以调用以下方法来重置设备连接到的 USB 端口或重启电源:

  • WdfUsbTargetDeviceResetPortSynchronously:同步发送重置设备的 USB 端口的请求;
  • WdfUsbTargetDeviceCyclePortSynchronously (仅KMDF):同步发送请求以对设备的 USB 端口进行电源循环;
  • WdfUsbTargetDeviceFormatRequestForCyclePort (仅 KMDF):设置对设备 USB 端口进行电源循环的请求的格式。 驱动程序必须调用 WdfRequestSend 以同步或异步方式发送请求;

将 URB 发送到设备

如果 KMDF 驱动程序通过发送包含 URB 的 I/O 请求与其 USB 设备通信,则驱动程序可以调用以下方法:

  • WdfUsbTargetDeviceCreateUrb (仅KMDF):分配 USB 请求块 (URB) 。 在调用 WdfUsbTargetDeviceCreateUrb 之前,驱动程序必须调用 WdfUsbTargetDeviceCreateWithParameters;
  • WdfUsbTargetDeviceCreateIsochUrb (仅KMDF):分配常时等量 USB 请求块 (URB) 。 在调用 WdfUsbTargetDeviceCreateIsochUrb 之前,驱动程序必须调用 WdfUsbTargetDeviceCreateWithParameters;
  • WdfUsbTargetDeviceSendUrbSynchronously (仅KMDF):同步发送包含 URB 的 I/O 请求;
  • WdfUsbTargetDeviceFormatRequestForUrb (仅KMDF):设置包含 URB 的 I/O 请求的格式。 驱动程序必须调用 WdfRequestSend 以同步或异步方式发送请求;
  • WdfUsbTargetDeviceWdmGetConfigurationHandle (仅KMDF):返回设备的 USBD 配置句柄。 某些 URL 需要此句柄;

使用 USB 接口

框架将每个 USB 接口表示为 框架 USB 接口对象。 当驱动程序 创建框架 USB 设备对象时,框架会为设备的第一个 USB 配置包含的每个 USB 接口创建框架 USB 接口对象。

大多数 USB 设备只有一个接口,该接口只有一个备用设置。 此类设备的驱动程序通常不需要使用框架的 USB 接口对象定义的对象方法。

如果驱动程序支持提供多个接口或备用设置的 USB 设备,则接口对象方法使驱动程序能够执行以下操作:

  • 获取接口信息;
  • 选择 USB 接口的备用设置;

获取接口信息

驱动程序调用 WdfUsbTargetDeviceCreateWithParameters 后,它可以调用 WdfUsbTargetDeviceGetInterface 以获取表示设备 USB 接口之一的框架 USB 接口对象的句柄。 然后,驱动程序可以调用 USB 接口对象定义的几种方法来获取有关 USB 接口的信息。

驱动程序可以在调用 WdfUsbTargetDeviceCreateWithParameters 后随时调用以下方法:

  • WdfUsbInterfaceGetInterfaceNumber返回与 USB 接口对象关联的 USB 接口编号;
  • WdfUsbInterfaceGetDescriptor检索与 USB 接口的备用设置之一关联的 USB 接口描述符;
  • WdfUsbInterfaceGetNumEndpoints返回与 USB 接口的备用设置之一关联的终结点数;
  • WdfUsbInterfaceGetEndpointInformation检索有关终结点及其关联管道的信息;

驱动程序在调用 WdfUsbTargetDeviceSelectConfig 后,可以调用以下方法:

  • WdfUsbInterfaceGetConfiguredSettingIndex返回一个索引值,该值标识当前为 USB 接口选择的备用设置;
  • WdfUsbInterfaceGetNumConfiguredPipes返回为指定的 USB 设备接口配置的管道数;
  • WdfUsbInterfaceGetConfiguredPipe返回与指定的 USB 设备接口和管道索引关联的框架管道对象的句柄;

为 USB 接口选择备用设置

驱动程序调用 WdfUsbTargetDeviceCreateWithParameters 后,驱动程序可以调用 WdfUsbInterfaceGetNumSettings 以获取 USB 接口支持的备用设置数。

驱动程序调用 WdfUsbTargetDeviceSelectConfig 为 USB 设备选择配置后,驱动程序可以调用 WdfUsbInterfaceSelectSetting ,为配置的 USB 接口之一选择备用设置。

设备的备用设置必须连续编号,从零开始。

使用 USB 管道

框架将 USB 接口中的每个管道表示为框架 USB 管道对象。 当驱动程序 配置 USB 设备时,框架会为每个所选接口中的每个管道创建一个框架 USB 管道对象。 管道对象方法使驱动程序能够执行以下操作:

  • 获取管道信息;
  • 从管道读取;
  • 写入管道;
  • 停止或重置管道;
  • 将 URB 发送到管道;

获取管道信息

调用 WdfUsbInterfaceGetConfiguredPipe 以获取框架 USB 管道对象的句柄后,驱动程序可以调用 USB 管道对象为获取有关 USB 管道的信息而定义的以下方法:

  • WdfUsbTargetPipeGetIoTarget:返回与 USB 管道关联的 I/O 目标对象的句柄。 驱动程序可以将此句柄传递给 WdfRequestSend;
  • WdfUsbTargetPipeGetInformation:检索有关 USB 管道及其终结点的信息;
  • WdfUsbTargetPipeGetType:返回 USB 管道的类型;
  • WdfUsbTargetPipeIsInEndpoint:确定 USB 管道是否连接到输入终结点;
  • WdfUsbTargetPipeIsOutEndpoint:确定 USB 管道是否连接到输出终结点;
  • WDF_USB_PIPE_DIRECTION_IN:确定 USB 终结点是否为输入终结点;
  • WDF_USB_PIPE_DIRECTION_OUT:确定 USB 终结点是否为输出终结点;

从管道读取

若要从 USB 输入管道读取数据,驱动程序可以使用以下三种技术中的任何 (或所有) :

1. 同步读取数据:若要从 USB 输入管道同步读取数据,驱动程序可以调用 WdfUsbTargetPipeReadSynchronously 方法。 此方法生成并发送读取请求,并在 I/O 操作完成后返回。

2. 异步读取数据:若要从 USB 输入管道异步读取数据,驱动程序可以调用 WdfUsbTargetPipeFormatRequestForRead 方法来生成读取请求。 然后,驱动程序可以调用 WdfRequestSend 以异步 (或同步) 发送请求。

3. 以异步方式连续读取数据:连续读取器是一种框架提供的机制,用于确保读取请求始终可用于 USB 管道。 此机制保证驱动程序始终准备好从提供异步、未经请求的输入流的设备接收数据。 例如,网络接口卡 (NIC) 的驱动程序可能会使用连续读取器来接收输入数据。

若要为输入管道配置连续读取器,驱动程序的 EvtDevicePrepareHardware 回调函数必须调用 WdfUsbTargetPipeConfigContinuousReader 方法。 此方法将一组读取请求排队到设备的 I/O 目标。

此外,驱动程序的 EvtDeviceD0Entry 回调函数必须调用 WdfIoTargetStart 才能启动连续读取器,驱动程序的 EvtDeviceD0Exit 回调函数必须调用 WdfIoTargetStop 来停止连续读取器。

每次从设备获取数据时,I/O 目标都将完成读取请求,框架将调用以下两个回调函数之一: EvtUsbTargetPipeReadComplete(如果 I/O 目标成功读取数据),如果 I/O 目标报告错误,则调用 EvtUsbTargetPipeReadersFailed。

如果不提供可选的 EvtUsbTargetPipeReadersFailed 回调,框架将通过发送另一个读取请求来响应失败的读取尝试。 因此,如果总线处于不接受读取的状态,框架会不断发送新的请求,以从失败的读取中恢复。

驱动程序调用 WdfUsbTargetPipeConfigContinuousReader 后,驱动程序无法使用 WdfUsbTargetPipeReadSynchronously 或 WdfRequestSend 将 I/O 请求发送到管道,除非调用了驱动程序的 EvtUsbTargetPipeReadersFailed 回调函数并返回 FALSE。

默认情况下,如果驱动程序指定的读取缓冲区不是管道最大数据包大小的倍数,框架将报告错误。 驱动程序可以调用 WdfUsbTargetPipeSetNoMaximumPacketSizeCheck 来禁用读取缓冲区大小的此测试。

写入管道

若要将数据写入 USB 输出管道,驱动程序可以使用一 (或两者) 以下技术:

  • 同步写入数据:若要将数据同步写入 USB 输出管道,驱动程序可以调用 WdfUsbTargetPipeWriteSynchronously 方法。 此方法生成并发送写入请求,并在 I/O 操作完成后返回;
  • 异步写入数据:若要将数据异步写入 USB 输入管道,驱动程序可以调用 WdfUsbTargetPipeFormatRequestForWrite 方法来生成写入请求。 然后,驱动程序可以调用 WdfRequestSend 以异步发送请求;

停止和重置管道

驱动程序可以调用以下方法来停止或重置 USB 管道:

  • WdfUsbTargetPipeAbortSynchronously:同步发送停止 USB 管道的请求;
  • WdfUsbTargetPipeFormatRequestForAbort:设置停止 USB 管道的请求的格式。 驱动程序可以调用 WdfRequestSend 以同步或异步方式发送请求;
  • WdfUsbTargetPipeResetSynchronously:同步发送重置 USB 管道的请求;
  • WdfUsbTargetPipeFormatRequestForReset:设置重置 USB 管道的请求的格式。 驱动程序必须调用 WdfRequestSend 以同步或异步方式发送请求;

如果驱动程序的 USB 目标以错误状态值 完成 I/O 请求,则驱动程序应执行以下操作:

  • 1. 如果目标尚未完成请求,请停止管道并取消驱动程序已发送到 USB 目标的任何其他 I/O 请求,调用 设置了 WdfIoTargetCancelSentIo 标志的 WdfIoTargetStop ;
  • 2. 向管道同步发送中止请求:调用 WdfUsbTargetPipeAbortSynchronously,或调用 WdfUsbTargetPipeFormatRequestForAbort ,后跟 WdfRequestSend 并设置 WDF_REQUEST_SEND_OPTION_SYNCHRONOUS 标志;
  • 3. 同步向管道发送重置请求:调用 WdfUsbTargetPipeResetSynchronously,或调用 WdfUsbTargetPipeFormatRequestForReset ,然后调用 WdfRequestSend 并设置 WDF_REQUEST_SEND_OPTION_SYNCHRONOUS 标志;
  • 4. 重启管道:调用 WdfIoTargetStart;
  • 5. 重新发送失败的 I/O 请求,以及失败请求后的所有 I/O 请求;

多次失败后,驱动程序应尝试通过执行以下操作来重置 USB 端口:

  • 1. 如果目标尚未完成,请停止所有活动管道,并取消驱动程序发送到每个管道的 USB 目标的任何其他 I/O 请求。对于每个活动管道,调用 设置了 WdfIoTargetCancelSentIo 标志的 WdfIoTargetStop ;
  • 2. 同步发送重置 USB 端口的请求。调用 WdfUsbTargetDeviceResetPortSynchronously;
  • 3. 重启管道。为驱动程序停止的每个管道调用 WdfIoTargetStart ;
  • 4. 重新发送失败的最后一个 I/O 请求,以及失败请求后的所有 I/O 请求;

将 URB 发送到管道

如果 KMDF 驱动程序通过发送包含 URB 的 I/O 请求与 USB 管道通信,则驱动程序可以调用以下方法:

  • WdfUsbTargetPipeSendUrbSynchronously (仅KMDF):同步发送包含 URB 的 I/O 请求;
  • WdfUsbTargetPipeFormatRequestForUrb (仅 KMDF):设置包含 URB 的 I/O 请求的格式。 驱动程序可以调用 WdfRequestSend 以同步或异步方式发送请求;
  • WdfUsbTargetPipeWdmGetPipeHandle (仅KMDF ):返回设备的 USBD 管道句柄。 某些 URB 需要此句柄;

 

  • 32
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值