编写函数控制器客户端驱动程序

本文介绍功能控制器客户端驱动程序在与 UFX) 的 USB 功能控制器扩展 (交互时执行的各种任务。

重要的 API

介绍功能控制器客户端驱动程序在与 UFX) 的 USB 功能控制器扩展 (交互时执行的各种任务。 UFX 和客户端驱动程序使用导出方法和事件回调函数相互通信。 (名为 UfxDeviceXxxUfxEndpointXxx) 的导出方法由 UFX 导出并由客户端驱动程序调用。 (名为 EVT_UFX_Xxx) 的回调函数在客户端驱动程序中实现,并由 UFX 调用。

UFX 以异步方式调用所有客户端驱动程序的回调函数,每个对象一次调用一个回调。 例如,有一个 USB 设备对象和三个终结点对象。 最多四个回调函数 (一个用于设备,每个终结点一个回调函数,) 一次可以调用。 对于每个回调方法,UFX 都会等待,直到客户端驱动程序调用 UfxDeviceEventComplete 以指示驱动程序已完成请求。 UFX 在等待这些导出时侦听的唯一其他导出方法是 UfxDeviceNotifyHardwareFailure。 许多客户端回调函数都是可选的。 所需函数如下所示:

初始化

  1. 当 Windows Driver Foundation (WDF) 调用客户端驱动程序的 EVT_WDF_DRIVER_DEVICE_ADD 回调实现时,函数控制器客户端驱动程序将启动初始化过程。 在该实现中,客户端驱动程序应调用 UfxFdoInit ,然后通过调用 WdfDeviceCreate 创建设备对象。
  2. 客户端驱动程序调用 UfxDeviceCreate 来创建 USB 设备对象并检索 UFXDEVICE 句柄。
  3. 客户端驱动程序调用 UfxDeviceNotifyHardwareReady 向 UFX 指示它现在可以调用客户端驱动程序的回调函数。
  4. UFX 执行初始化任务,例如:

类驱动程序通知

为了获得有关设置数据包和总线状态的通知,类驱动程序应发送 IOCTL_INTERNAL_USBFN_ACTIVATE_USB_BUS 请求。 UFX 将这些请求排队到特定于类驱动程序的通知队列中。 从客户端驱动程序收到有关总线事件的通知时,UFX 会从每个适当的队列弹出并完成请求。 为了防止类驱动程序丢失通知,UFX 为类驱动程序保留固定大小的通知队列。

设备附加和分离事件

UFX 假定设备分离,直到函数控制器客户端驱动程序调用 UfxDeviceNotifyAttach

调用后,UFX 将设备状态设置为 USB 规范中定义的 “已开机 ”。 为了通知客户端驱动程序状态更改,UFX 调用客户端驱动程序的 EVT_UFX_DEVICE_USB_STATE_CHANGE 实现。

UFX 通知充电器驱动程序 (Cad.sys) 以帮助为设备充电。 UFX 还通过完成类驱动程序先前发送 IOCTL_INTERNAL_USBFN_BUS_EVENT_NOTIFICATION 请求来通知类驱动程序。

分离总线时,客户端驱动程序必须调用 UfxDeviceNotifyDetach 。 每次调用 UfxDeviceNotifyAttach 后,客户端只能调用 detach 一次。 在 UfxDeviceNotifyDetach 调用后,如果这不是接口 更改) , 则 UFX 会调用EVT_UFX_DEVICE_HOST_DISCONNECT (。 UFX 然后继续执行所有清理任务,例如清除所有终结点队列和启动默认终结点队列。 UFX 调用EVT_UFX_DEVICE_USB_STATE_CHANGE 并通过完成 IOCTL_INTERNAL_USBFN_BUS_EVENT_NOTIFICATION 请求来通知类驱动程序。

硬件故障

如果发生硬件错误,客户端驱动程序应调用 UfxDeviceNotifyHardwareFailure。 作为响应,UFX 将拆毁设备堆栈,并可能尝试通过调用客户端驱动程序的 EVT_UFX_DEVICE_CONTROLLER_RESET从这种情况中恢复。 客户端应将控制器重置为其初始状态。 如果发生其他硬件故障,客户端应再次调用 UfxDeviceNotifyHardwareFailure。 在第二次调用时,UFX 将记录其状态和 bug 检查。

端口检测

端口检测由 UFX 执行。 它调用函数控制器客户端驱动程序的 EVT_UFX_DEVICE_PORT_DETECT 回调函数来确定设备所附加到的端口的类型。 客户端使用 USBFN_PORT_TYPE 中定义的端口类型之一调用 UfxDevicePortDetectCompleteUfxDevicePortDetectCompleteEx 进行响应。

如果客户端无法确定端口的类型,则客户端应报告 UsbfnUnknownPort。 如果端口未知或下游端口,则 UFX 会调用客户端驱动程序 的 EVT_UFX_DEVICE_HOST_CONNECT 函数。 UFX 侦听总线一段时间。 如果端口未知,但存在流量(如设置数据包),则 UFX 将采用 UsbfnStandardDownstreamPort。 否则,UFX 将端口分配为 UsbfnInvalidDedicatedChargingPort。 确定端口类型后,UFX 会通知 Cad.sys 并调用客户端驱动程序的 EVT_UFX_DEVICE_PORT_CHANGE 函数。 在 函数中,客户端驱动程序应更改硬件状态以匹配 UFX 端口类型。

终结点创建

UFX 通过调用客户端驱动程序的 EVT_UFX_DEVICE_DEFAULT_ENDPOINT_ADD 创建默认终结点 (终结点 0) ,以便它可以 处理主机发送的设置数据包。 UFX 通过调用 EVT_UFX_DEVICE_ENDPOINT_ADD 创建其他终结点。 UFX 仅在客户端驱动程序调用 UfxDeviceNotifyHardwareReady 后创建终结点。 在这些回调函数中,客户端驱动程序应调用 UfxEndpointCreate 到终结点对象并获取其 UFXENDPOINT 句柄。 UFX 将父级设置为与终结点所属的接口关联的类驱动程序 PDO。 默认终结点的父终结点是 USB 设备对象。 终结点包含两个框架队列对象:一个传输队列和一个命令队列,这两个对象仅当设备处于“已配置”状态 ((终结点 0 除外)时才能访问,后者可在 UFX 调用 EVT_UFX_DEVICE_HOST_CONNECT) 后访问。

设备枚举

在 UFX 调用驱动程序的 EVT_UFX_DEVICE_HOST_CONNECT之前,客户端驱动程序不应允许连接到主机。 当客户端驱动程序调用 UfxDeviceNotifyReset 时,设备枚举开始。 在 默认 状态下,UFX 处理标准设置数据包。

重置

UFX 清除所有终结点队列,并向客户端驱动程序发送 IOCTL_INTERNAL_USBFN_DESCRIPTOR_UPDATE 请求,以更新终结点 0 的 wMaxPacketSize 。 UFX 启动默认终结点的队列,并将状态设置为 Default

Default

UFX 调用客户端驱动程序的 EVT_UFX_DEVICE_USB_STATE_CHANGE 函数。 它还通知类驱动程序状态。 在 UFX 收到SET_ADDRESS标准安装数据包后,UFX 会将状态设置为 “已寻址”。

解决

UFX 调用客户端驱动程序的 EVT_UFX_DEVICE_ADDRESSED 函数,以向客户端指示它应使用哪个地址。 - 如果地址为 0,UFX 会将状态重新设置为 Default ,并调用 EVT_UFX_DEVICE_USB_STATE_CHANGE 并通知类驱动程序。 收到SET_CONFIGURATION标准安装数据包时,UFX 将状态设置为 “已配置”。

已配置

如果所选配置为 0,则 UFX 将清除接口终结点并将状态设置为“ 已寻址”。 UFX 向客户端驱动程序发送 IOCTL_INTERNAL_USBFN_DESCRIPTOR_UPDATE 请求,以更新接口终结点的 wMaxPacketSize 。 UFX 确保所有接口终结点队列已完成清除并启动接口终结点队列。 如果端口类型不是 UsbfnStandardDownstreamPortUsbfnChargingDownstreamPort,则 UFX 将端口类型更改为 UsbfnStandardDownstreamPort 并通知 Cad.sys;通过调用 EVT_UFX_DEVICE_PORT_CHANGEEVT_UFX_DEVICE_USB_STATE_CHANGE 来更新状态的客户端驱动程序;已配置状态的类驱动程序。

标准控制传输

UFX 在调用 EVT_UFX_DEVICE_DEFAULT_ENDPOINT_ADD 后,可以随时处理默认终结点上的控制传输,客户端驱动程序在其中使用 创建默认终结点。 所有控制传输都以 8 字节设置数据包开头。 若要将设置数据包发送到主机,客户端驱动程序应调用 UfxEndpointNotifySetup。 标准控制传输由 UFX 完成。 如果存在与控件传输关联的数据,UFX 会根据需要从默认控制终结点读取和写入。

非标准控制转移

如果 UFX 无法处理控制传输,则通过完成 IOCTL_INTERNAL_USBFN_BUS_EVENT_NOTIFICATION 请求将传输转发到相应的类驱动程序。 控制传输可以在终结点描述符中定义为控制终结点的任何终结点上发生。 默认控制终结点以外的终结点上的控制传输始终是非标准控制传输。 如果控制终结点是默认控制终结点,UFX 将通知类驱动程序安装数据包,这些数据包标记为类驱动程序的类请求。 如果控制终结点属于接口,则 UFX 会通知与该接口关联的类驱动程序。 如有必要,类驱动程序应从控件终结点进行读取和写入。

数据传输

数据传输由类驱动程序通过发送 IOCTL_INTERNAL_USBFN_TRANSFER_INIOCTL_INTERNAL_USBFN_TRANSFER_IN_APPEND_ZERO_PKTIOCTL_INTERNAL_USBFN_TRANSFER_OUT 请求来启动。 验证每个请求后,UFX 将其转发到相应的终结点队列,由客户端驱动程序处理。 客户端驱动程序应执行其他验证。 客户端驱动程序接收终结点队列上的传输请求。 客户端驱动程序可以从此队列中检索尽可能多的请求,以最大程度地提高总线利用率。 客户端驱动程序应使用 STATUS_SUCCESS 完成成功的请求。 驱动程序应尽最大努力尝试取消请求,并在取消后使用STATUS_CANCELLED完成已取消的请求。 如果传递的参数无效,则客户端驱动程序使用 STATUS_INVALID_PARAMETER 完成请求。

控制传输

控制传输以 8 字节设置数据包开头。 若要将设置数据包发送到主机,客户端驱动程序应调用 UfxEndpointNotifySetup。 UFX 通过完成通知请求来通知类驱动程序非标准控制传输。 客户端和 UFX 都使用 IOCTL_INTERNAL_USBFN_TRANSFER_INIOCTL_INTERNAL_USBFN_TRANSFER_IN_APPEND_ZERO_PKTIOCTL_INTERNAL_USBFN_TRANSFER_OUT 从默认控制终结点读取和写入。 但是,接口可以定义其他控制终结点,只有相应的类驱动程序才能使用。 为了响应设置数据包,可以停止控制终结点。 类驱动程序发送 IOCTL_INTERNAL_USBFN_SET_PIPE_STATE 请求来停止终结点。 发送停止后,硬件或客户端驱动程序应立即恢复终结点上的流量。 控制终结点还可以 (ZLP) 发送和接收零长度数据包,而无需任何先前数据。 客户端驱动程序和 UFX 可以使用 IOCTL_INTERNAL_USBFN_CONTROL_STATUS_HANDSHAKE_INIOCTL_INTERNAL_USBFN_CONTROL_STATUS_HANDSHAKE_OUT 执行此操作。

批量传输和中断传输

批量传输保证数据传输,并用于发送大量数据。 可以使用 IOCTL_INTERNAL_USBFN_TRANSFER_IN、IOCTL_INTERNAL_USBFN_TRANSFER_IN_APPEND_ZERO_PKTIOCTL_INTERNAL_USBFN_TRANSFER_OUT 在批量终结点上发送传输。 批量终结点可以像使用 IOCTL_INTERNAL_USBFN_SET_PIPE_STATE控制终结点一样停止。 客户端驱动程序应发送 STALL 数据包以响应所有主机请求并保留 IOCTL 请求。 与控制终结点不同,停止的批量终结点将保持停止状态,直到显式清除停止状态。

中断传输 中断传输类似于批量传输,但有保证的延迟。 中断传输的接口与批量传输相同,但没有流式处理功能。

常时等量传输

客户端驱动程序不应支持此版本中的常时等量传输。

电源管理

客户端驱动程序拥有电源管理的所有方面。 由于回调函数是异步的,因此客户端驱动程序应在调用相应的事件完成导出函数(如 UfxDeviceEventComplete)之前恢复到适当的电源状态并完成请求。

如果 USBFN_DEVICE_STATE) 中定义的设备状态 (为 UsbfnDeviceStateSuspendedUsbfnDeviceStateAttached,并且未报告端口类型,则 UFX 将处于工作状态。 或者,UFX 报告了 USBFN_PORT_TYPE () UsbfnStandardDownstreamPortUsbfnChargingDownstreamPort 中定义的端口类型。

UFX 通过调用 EVT_UFX_DEVICE_USB_STATE_CHANGEEVT_UFX_DEVICE_PORT_CHANGE 实现进入和退出工作状态。 当客户端驱动程序调用 UfxDeviceEventComplete 时,到工作状态或从工作状态转换完成。

在“工作”状态下,UFX 可以调用任何回调。 虽然 UFX 不处于“工作”状态,但仅调用 EVT_UFX_DEVICE_USB_STATE_CHANGE 进入工作状态; 如果支持) ,EVT_UFX_DEVICE_REMOTE_WAKEUP_SIGNAL 在暂停 (期间发出远程唤醒。

设备挂起

当总线上没有流量 3 毫秒时,会发生设备挂起。 在这种情况下,客户端驱动程序必须通过调用 UfxDeviceNotifySuspendUfxDeviceNotifyResume 来通知 UFX 检测到挂起和恢复。 接收这些调用后,UFX 会调用EVT_UFX_DEVICE_USB_STATE_CHANGE ,并通过完成 IOCTL_INTERNAL_USBFN_BUS_EVENT_NOTIFICATION 请求来通知类驱动程序。 如果设备支持远程唤醒并由主机启用,UFX 可能会在暂停时调用 调用EVT_UFX_DEVICE_USB_STATE_CHANGE 以发出远程唤醒信号。