如何发送 USB 控制传输

本主题介绍了控制传输结构,以及客户端驱动程序应如何将控制请求发送到设备。

本主题内容:

关于默认终结点

所有 USB 设备必须至少支持一个终结点(称为默认终结‏‏‎点)。针对默认终结点的任何传输称为控制传输。控制传输的目的是使主机能够获取设备信息,配置设备或执行设备特有的控制操作。

让我们开始分析默认终结点的这些属性。

  • 默认终结点的地址为 0。
  • 默认终结点是双向的,即,主机可以在一个传输中将数据发送到终结点以及从中接收数据。
  • 默认终结点在设备级别可用,它不是在设备的任何接口中定义的。
  • 在主机和设备之间建立连接后,默认终结点将立即处于活动状态。甚至在选择配置之前,它就处于活动状态。
  • 默认终结点的最大包大小取决于设备的总线速度。低速为 8 个字节;全速和高速为 64 个字节;超高速为 512 个字节。

控制传输布局

由于控制传输是高优先级传输,因此,主机在总线上保留一定数量的带宽。低速和全速设备保留 10% 带宽;高速和超高速传输设备保留 20% 带宽。现在,让我们看一下控制传输布局。

USB 控制传输

控制传输分为三个事务:设置事务数据事务状态事务。每个事务包含三种类型的包:令牌包数据包握手包

某些字段是所有包通用的。这些字段包括:

  • 指示包开始的 Sync 字段。
  • 指示包类型和事务方向的包标识符 (PID);对于握手包,它还指示事务成功或失败。
  • 指示包结束的 EOP 字段。
其他字段取决于包类型。

  • Token packet

    每个设置事务以令牌包开始。下面是包的结构。主机始终发送令牌包。

    Ff539261.token(zh-cn,VS.85).png

    PID 值指示令牌包类型。下面是可能的值:

    • SETUP:指示控制传输中的设置事务的开始位置。
    • IN:指示主机从设备中请求数据(读取时)。
    • OUT:指示主机将数据发送到设备(写入时)。
    • SOF:指示帧的开始位置。这种类型的令牌包包含一个 11 位的帧编号。主机发送 SOF 包。发送包的频率取决于总线速度。对于全速总线,主机每 1 毫秒发送一次包;对于高速总线,主机每 125 微秒发送一次包。
  • Data packet

    紧跟令牌包后面的是包含负载的数据包。每个数据包可以包含的字节数取决于默认终结点的最大包大小。可以由主机或设备发送数据包,具体取决于传输方向。

    Ff539261.data(zh-cn,VS.85).png

  • Handshake packet

    紧跟数据包后面的是握手包。包的 PID 指示是由主机还是设备接收包。可以由主机或设备发送握手包,具体取决于传输方向。

    Ff539261.handshake(zh-cn,VS.85).png

你可以使用任何 USB 分析器(例如 Beagle、Ellisys、LeCroy USB 协议分析器)查看事务和包的结构。分析器设备显示 USB 设备如何通过线路收发数据。在此示例中,我们来看一下 LeCroy USB 分析器所捕获的一些跟踪。此示例仅供参考。Microsoft 不对其担负背书责任。

  • Setup transaction

    主机始终启动控制传输。它通过发送设置事务来执行此操作。此事务包含一个称为设置令牌的令牌包,后跟 8 个字节的数据包。此屏幕截图显示了一个示例设置事务。

    设置事务的跟踪数据。

    在前面的跟踪数据中,主机发送设置令牌包 #434 以启动控制传输(由 H↓ 指示)。请注意,PID 指定用以指示设置令牌的 SETUP。PID 后跟设备地址和终结点地址。对于控制传输,该终结点地址始终为 0。

    接下来,主机发送数据包 #435。PID 是 DATA0,将使用该值进行包排序(将在后面介绍)。PID 后跟 8 个字节,其中包含有关此请求的主要信息。这 8 个字节指示请求类型以及设备将其响应写入到的缓冲区大小。

    将按相反的顺序接收所有字节。正如第 9.3 节中所述,我们看到以下字段和值:

    字段大小说明
    bmRequestType(请参阅 9.3.1 bmRequestType)1 0x80

    数据传输方向是从设备到主机(D7 为 1)

    请求是一个标准请求(D6…D5 为 0)

    请求的接收方是 DEVICE(D4 为 0)

    bRequest(请参阅第 9.3.2 节和表 9-4) 1 0x06请求类型为 GET_DESCRIPTOR。
    wValue(请参阅表 9-5) 20x0100请求值指示描述符类型为 DEVICE。
    wIndex(请参阅第 9.3.4 节) 2 0x0000

    方向是从主机到设备(D7 为 1)

    终结点编号为 0。

    wLength(请参阅第 9.3.5 节)20x0012该请求将检索 18 个字节。

     

    因此,我们可以得出结论,在此控制(读取)传输中,主机发送请求以检索设备描述符,并将 18 个字节指定为传输长度以保存该描述符。 设备发送这 18 个字节的方式取决于默认终结点可以在一个事务中发送多少数据。该信息包含在数据事务中的设备所返回的设备描述符中。

    设备发送一个握手包(由 D↓ 指示的 #436)以作出响应。请注意,PID 值为 ACK(ACK 包)。这表明设备确认了事务。

  • Data transaction

    现在,让我们看一下设备在响应请求时返回的内容。实际数据是在数据事务中传输的。

    下面是数据事务的跟踪数据。

    示例数据事务的跟踪数据。

    在收到 ACK 包后,主机将启动数据事务。要启动事务,它发送一个令牌包 (#450) 并将方向指定为 IN(称为 IN 令牌)。

    设备在 IN 令牌后面发送一个数据包 (#451) 以作出响应。此数据包包含实际设备描述符。第一个字节指示设备描述符长度为 18 个字节 (0x12)。此数据包中的最后一个字节指示默认终结点支持的最大包大小。 在这种情况下,我们看到设备每次可以通过其默认终结点发送 8 个字节。

    注意  默认终结点的最大包大小取决于设备的总线速度。 高速设备的默认终结点为 64 个字节;低速设备为 8 个字节。
    主机向设备发送一个 ACK 包 (#452) 以确认数据事务。

    让我们计算一下返回的数据量。在设置事务的数据包 (#435) 的 wLength 字段中,主机请求了 18 个字节。在数据事务中,我们看到仅从设备中收到设备描述符的前 8 个字节。那么,主机如何接收其余 10 个字节中存储的信息呢?设备在两个事务中执行此操作:一个事务为 8 个字节,另一个事务为是最后 2 个字节。

    由于主机知道默认终结点的最大包大小,因此,主机启动一个新的数据事务,并根据包大小请求下一个部分。

    下面是下一个数据事务:

    示例数据事务的跟踪数据。

    主机发送 IN 令牌 (#463) 并从设备中请求下一个 8 字节以启动前面的数据事务。设备发送包含设备描述符的下一个 8 字节的数据包 (#464) 以作出响应。

    一收到这 8 个字节,主机就向设备发送一个 ACK 包 (#465)。

    接下来,主机在另一个数据事务中请求最后 2 个字节,如下所示:

    Ff539261.datra_trans3(zh-cn,VS.85).png

    因此,我们看到要将 18 个字节从设备传输到主机,主机启动三个数据事务并跟踪传输的字节数 (8+8+2)。

    注意   请注意数据事务 19、23 和 26 中的数据包的 PID。PID 交替使用 DATA0 和 DATA1。此顺序称为数据切换。如果有多个数据事务,则使用数据切换检查包顺序。这种方法可确保数据包不会出现重复或丢失。
    通过将合并的数据包映射到设备描述符结构(请参阅表 9-8),我们可以看到以下字段和值:
    字段大小说明
    bLength10x12设备描述符长度(18 个字节)。
    bDescriptorType10x01描述符类型为设备。
    bcdUSB20x0100规范版本号为 1.00。
    bDeviceClass10x00设备类为 0。配置中的每个接口具有类信息。
    bDeviceSubClass10x00子类为 0,因为设备类为 0。
    bProtocol10x00协议为 0。此设备不使用任何类特定的协议。
    bMaxPacketSize010x08终结点的最大包大小为 8 个字节。
    idVendor20x0562电报通信。
    idProduct20x0002USB 麦克风。
    bcdDevice20x0100指示设备版本号。
    iManufacturer10x01制造商字符串。
    iProduct10x02产品字符串。
    iSerialNumber10x03序列号。
    bNumConfigurations10x01配置数。

     

    通过检查这些值,我们可以了解一些有关设备的初步信息。设备为低速 USB 麦克风。默认终结点的最大包大小为 8 个字节。设备支持一个配置。
  • Status transaction

    最后,主机启动最后一个事务以完成控制传输:状态事务。

    Ff539261.status_trans(zh-cn,VS.85).png

    主机使用 OUT 令牌包 (#481) 启动事务。此包的用途是检查设备是否发送了所有请求的数据。不会在此状态事务中发送任何数据包。设备发送 ACK 包以作出响应。如果发生错误,则 PID 可能为 NAK 或 STALL。

支持的驱动程序模型

相关技术

先决条件

在客户端驱动程序可以枚举管道之前,请确保满足以下要求:

  • 客户端驱动程序必须已创建框架 USB 目标设备对象。

    如果你 使用的是 Microsoft Visual Studio Professional 2012 提供的 USB 模板,则模板代码 会执行这些任务。模板代码会获取指向目标设备对象的句柄并将其存储在 设备上下文中。

    KMDF 客户端驱动程序:  

    KMDF 客户端驱动程序必须通过调用 WdfUsbTargetDeviceCreateWithParameters 方法来获取 WDFUSBDEVICE 句柄。有关详细信息,请参阅了解 USB 客户端驱动程序 代码结构 (KMDF) 中的“设备源代码”。

    UMDF 客户端 驱动程序:  

    UMDF 客户端驱动程序必须通过查询框架 目标设备对象获得一个 IWDFUsbTargetDevice 指针。有关详细信息,请参阅了解 USB 客户端驱动程序 代码结构 (UMDF) 中的“IPnpCallbackHardware 实现和特定于 USB 的任务”。

  • 控制传输的最重要属性是正确设定设置令牌的格式。在发送请求之前,请收集下面的这组信息:

    • 请求方向:主机到设备或设备到主机。
    • 请求接收方:设备、接口、终结点或其他。
    • 请求类别:标准、类或供应商。
    • 请求类型,如 GET_DESCRIPTPOR 请求。有关详细信息,请参阅 USB 规范中的第 9.5 节。
    • wValuewIndex 值。这些值取决于请求类型。
    可以从正式 USB 规范中获取所有这些信息。
  • 如果要编写 UMDF 驱动程序,请从 OSR USB Fx2 Learning Kit 的 UMDF 示例驱动程序中获取头文件 Usb_hw.h。该头文件包含有用的宏和结构以设置控制传输的设置包格式。

    所有 UMDF 驱动程序必须与内核模式驱动程序进行通信,才能将数据发送到设备以及从中接收数据。对于 USB UMDF 驱动程序,内核模式驱动程序始终为 Microsoft 提供的驱动程序 WinUSB (Winusb.sys)。

    只要 UMDF 驱动程序向 USB 驱动程序堆栈发出请求,Windows I/O 管理器就会向 WinUSB 发送该请求。在收到请求后,WinUSB 将处理该请求,或将其转发到 USB 驱动程序堆栈。

Microsoft 定义的控制传输请求发送方法

主机上的 USB 客户端驱动程序启动大多数控制请求以获取有关设备的信息,配置设备或发送供应商控制命令。可以将所有这些请求划分为:

  • 标准请求—标准请求是在 USB 规范中定义的。发送这些请求的目的是获取有关设备、其配置、接口和终结点的信息。每个请求的接收方取决于请求类型。接收方可以是设备、接口或终结点。

    注意  任何控制传输的目标始终是默认终结点。接收方是主机对其信息(描述符、状态等)感兴趣的设备实体。

    可以将这些请求进一步划分为:配置请求、功能请求和状态请求。

    • 发送配置请求以从设备中获取信息,使主机可以对其进行配置,如 GET_DESCRIPTOR 请求。这些请求也可以是主机发送的写入请求,以便在设备中设置特定的配置或备用设置。
    • 客户端驱动程序发送功能请求以启用或禁用设备、接口或终结点支持的某些布尔设备设置。
    • USB 设备支持状态请求以使主机能够获取或设置设备、终结点或接口的 USB 定义的状态位。
    有关详细信息,请参阅 USB 规范 2.0 版中的第 9.4 节。标准请求类型是在头文件 Usbspec.h 中定义的。
  • 类请求—是由特定设备类规范定义的。
  • 供应商请求—是由供应商提供的,这些请求取决于设备支持的请求。

Microsoft 提供的 USB 堆栈处理与设备之间的所有协议通信,如前面的跟踪数据中所示。驱动程序公开设备驱动程序接口 (DDI),以使客户端驱动程序能够通过多种方法发送控制传输。如果客户端驱动程序是 Windows 驱动程序基础 (WDF) 驱动程序,则它可以直接调用例程以发送常见类型的控制请求。对于 KMDF 和 UMDF,WDF 内在支持控制传输。

不会通过 WDF 公开某些类型的控制请求。对于这些请求,客户端驱动程序可以使用 WDF 混合模型。该模型允许客户端驱动程序生成 WDM URB 样式的请求并设置其格式,然后使用 WDF 框架对象发送这些请求。这种混合模型仅适用于内核模式驱动程序。

对于 UMDF 驱动程序:  

使用 usb_hw.h 中定义的帮助程序宏和结构。该头是随 OSR USB Fx2 Learning Kit 的 UMDF 示例驱动程序提供的。

可以使用下表确定将控制请求发送到 USB 驱动程序堆栈的最佳方法。如果无法查看该表,请参阅本主题中的表。

如果要将控制请求发送到...如果使用 KMDF 驱动程序,请使用以下 KMDF DDI... 如果使用 UMDF 驱动程序,请使用以下 UMDF 方法...如果使用 WDM 驱动程序,请生成 URB 结构(帮助程序例程)
CLEAR_FEATURE:在设备、其配置、接口和终结点中禁用某些功能设置。请参阅 USB 规范中的第 9.4.1 节。
  1. 声明一个设置包。请参阅 WDF_USB_CONTROL_SETUP_PACKET 结构。
  2. 调用 WDF_USB_CONTROL_SETUP_PACKET_INIT_FEATURE 以初始化该设置包。
  3. 指定 WDF_USB_BMREQUEST_RECIPIENT 中定义的接收方值。
  4. 指定功能选择器 (wValue)。请参阅 Usbspec.h 中的 USB_FEATURE_XXX 常量。另请参阅 USB 规范中的表 9-6。
  5. SetFeature 设置为 FALSE
  6. 调用 WdfUsbTargetDeviceSendControlTransferSynchronouslyWdfUsbTargetDeviceFormatRequestForControlTransfer 以发送请求。
  1. 声明一个设置包。请参阅 usb_hw.h 中声明的 WINUSB_CONTROL_SETUP_PACKET 结构。
  2. 调用 usb_hw.h 中定义的帮助程序宏 WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE 以初始化该设置包。
  3. 指定 WINUSB_BMREQUEST_RECIPIENT 中定义的接收方值。
  4. 指定功能选择器 (wValue)。请参阅 Usbspec.h 中的 USB_FEATURE_XXX 常量。另请参阅 USB 规范中的表 9-6。
  5. SetFeature 设置为 FALSE
  6. 通过调用 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,将初始化的设置包与框架请求对象和传输缓冲区相关联以生成请求。
  7. 调用 IWDFIoRequest::Send 方法以发送请求。

_URB_CONTROL_FEATURE_REQUEST

(UsbBuildFeatureRequest)

URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE

URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE

URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT

URB_FUNCTION_CLEAR_FEATURE_TO_OTHER

GET_CONFIGURATION:获取当前 USB 配置。请参阅 USB 规范中的第 9.4.2 节。

默认情况下,KMDF 选择第一个配置。要检索设备定义的配置编号,请执行以下操作:

  1. 设置 WDF_USB_CONTROL_SETUP_PACKET 格式并将其 bRequest 成员设置为 USB_REQUEST_GET_CONFIGURATION
  2. 调用 WdfUsbTargetDeviceSendControlTransferSynchronouslyWdfUsbTargetDeviceFormatRequestForControlTransfer 以发送请求。

默认情况下,UMDF 选择第一个配置。要检索设备定义的配置编号,请执行以下操作:

  1. 声明一个设置包。请参阅 usb_hw.h 中声明的 WINUSB_CONTROL_SETUP_PACKET 结构。
  2. 调用 usb_hw.h 中定义的帮助程序宏 WINUSB_CONTROL_SETUP_PACKET_INIT 以初始化该设置包。
  3. BmRequestToDevice 指定为方向,将 BmRequestToDevice 指定为接收方,并将 USB_REQUEST_GET_CONFIGURATION 指定为请求。
  4. 通过调用 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,将初始化的设置包与框架请求对象和传输缓冲区相关联以生成请求。
  5. 调用 IWDFIoRequest::Send 方法以发送请求。
  6. 接收传输缓冲区中的配置编号。调用 IWDFMemory 方法以访问该缓冲区。

_URB_CONTROL_GET_CONFIGURATION_REQUEST

URB_FUNCTION_GET_CONFIGURATION

GET_DESCRIPTOR:获取设备、配置、接口和终结点描述符。请参阅 USB 规范中的第 9.4.3 节。

有关详细信息,请参阅 USB 描述符

调用以下方法:

调用以下方法:

_URB_CONTROL_DESCRIPTOR_REQUEST

(UsbBuildGetDescriptorRequest)

URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE

URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT

URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE

GET_INTERFACE:获取接口的当前备用设置。请参阅 USB 规范中的第 9.4.4 节。

  1. 调用 WdfUsbTargetDeviceGetInterface 方法以获取指向目标接口对象的 WDFUSBINTERFACE 句柄。
  2. 调用 WdfUsbInterfaceGetConfiguredSettingIndex 方法。
  1. 获取指向目标接口对象的 IWDFUsbInterface 指针。
  2. 调用 IWDFUsbInterface::GetConfiguredSettingIndex 方法。

_URB_CONTROL_GET_INTERFACE_REQUEST

URB_FUNCTION_GET_INTERFACE

GET_STATUS:从设备、终结点或接口中获取状态位。请参阅 USB 规范中的第 9.4.5 节。
  1. 声明一个设置包。请参阅 WDF_USB_CONTROL_SETUP_PACKET 结构。
  2. 调用 WDF_USB_CONTROL_SETUP_PACKET_INIT_GET_STATUS 以初始化该设置包。
  3. 指定 WDF_USB_BMREQUEST_RECIPIENT 中定义的接收方值。
  4. 指定要获取的状态:设备、接口或终结点 (wIndex)。
  5. 调用 WdfUsbTargetDeviceSendControlTransferSynchronouslyWdfUsbTargetDeviceFormatRequestForControlTransfer 以发送请求。
  1. 声明一个设置包。请参阅 usb_hw.h 中声明的 WINUSB_CONTROL_SETUP_PACKET 结构。
  2. 调用 usb_hw.h 中定义的帮助程序宏 WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS 以初始化该设置包。
  3. 指定 WINUSB_BMREQUEST_RECIPIENT 中定义的接收方值。
  4. 指定要获取的状态:设备、接口或终结点 (wIndex)。
  5. 通过调用 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,将初始化的设置包与框架请求对象和传输缓冲区相关联以生成请求。
  6. 调用 IWDFIoRequest::Send 方法以发送请求。
  7. 接收传输缓冲区中的状态值。调用 IWDFMemory 方法以访问该缓冲区。
  8. 要确定状态是否指示自已供电和远程唤醒,请使用 WINUSB_DEVICE_TRAITS 枚举中定义的三个值:

_URB_CONTROL_GET_STATUS_REQUEST

(UsbBuildGetStatusRequest)

URB_FUNCTION_GET_STATUS_FROM_DEVICE

URB_FUNCTION_GET_STATUS_FROM_INTERFACE

URB_FUNCTION_GET_STATUS_FROM_ENDPOINT

URB_FUNCTION_GET_STATUS_FROM_OTHER.

SET_ADDRESS:请参阅 USB 规范中的第 9.4.6 节。此请求是由 USB 驱动程序堆栈处理的;客户端驱动程序无法执行此操作。此请求是由 USB 驱动程序堆栈处理的;客户端驱动程序无法执行此操作。此请求是由 USB 驱动程序堆栈处理的;客户端驱动程序无法执行此操作。
SET_CONFIGURATION:设置一个配置。请参阅 USB 规范中的第 9.4.7 节。

有关详细信息,请参阅如何为 USB 设备选择配置

默认情况下,KMDF 选择默认配置和每个接口中的第一个备用设置。客户端驱动程序可以调用 WdfUsbTargetDeviceSelectConfigType 方法并将 WdfUsbTargetDeviceSelectConfigTypeUrb 指定为请求选项以更改默认配置。然后,必须为该请求设置 URB 格式,并将其提交到 USB 驱动程序堆栈。默认情况下,UMDF 选择默认配置和每个接口中的第一个备用设置。客户端驱动程序无法更改配置。

_URB_SELECT_CONFIGURATION

(USBD_SelectConfigUrbAllocateAndBuild)

URB_FUNCTION_SELECT_CONFIGURATION

SET_DESCRIPTOR:更新现有的设备、配置或字符串描述符。请参阅 USB 规范中的第 9.4.8 节。

此请求并不常用。不过,USB 驱动程序堆栈从客户端驱动程序中接受此类请求。

  1. 为该请求分配和生成一个 URB
  2. _URB_CONTROL_DESCRIPTOR_REQUEST 结构中指定传输信息。
  3. 调用 WdfUsbTargetDeviceFormatRequestForUrbWdfUsbTargetDeviceSendUrbSynchronously 以发送请求。
  1. 声明一个设置包。请参阅 usb_hw.h 中声明的 WINUSB_CONTROL_SETUP_PACKET 结构。
  2. 按照 USB 规范指定传输信息。
  3. 通过调用 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,将初始化的设置包与框架请求对象和传输缓冲区相关联以生成请求。
  4. 调用 IWDFIoRequest::Send 方法以发送请求。

_URB_CONTROL_DESCRIPTOR_REQUEST

URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE

URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT

URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE

SET_FEATURE:在设备、其配置、接口和终结点中启用某些功能设置。请参阅 USB 规范中的第 9.4.9 节。
  1. 声明一个设置包。请参阅 WDF_USB_CONTROL_SETUP_PACKET 结构。
  2. 调用 WDF_USB_CONTROL_SETUP_PACKET_INIT_FEATURE 以初始化该设置包。
  3. 指定 WDF_USB_BMREQUEST_RECIPIENT 中定义的接收方值(设备、接口和终结点)。
  4. 指定功能选择器 (wValue)。请参阅 Usbspec.h 中的 USB_FEATURE_XXX 常量。另请参阅 USB 规范中的表 9-6。
  5. SetFeature 设置为 TRUE
  6. 调用 WdfUsbTargetDeviceSendControlTransferSynchronouslyWdfUsbTargetDeviceFormatRequestForControlTransfer 以发送请求。
  1. 声明一个设置包。请参阅 usb_hw.h 中声明的 WINUSB_CONTROL_SETUP_PACKET 结构。
  2. 调用 usb_hw.h 中定义的帮助程序宏 WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE 以初始化该设置包。
  3. 指定 WINUSB_BMREQUEST_RECIPIENT 中定义的接收方值。
  4. 指定功能选择器 (wValue)。请参阅 Usbspec.h 中的 USB_FEATURE_XXX 常量。另请参阅 USB 规范中的表 9-6。
  5. SetFeature 设置为 TRUE
  6. 通过调用 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,将初始化的设置包与框架请求对象和传输缓冲区相关联以生成请求。
  7. 调用 IWDFIoRequest::Send 方法以发送请求。

_URB_CONTROL_FEATURE_REQUEST

(UsbBuildFeatureRequest)

URB_FUNCTION_SET_FEATURE_TO_DEVICE

URB_FUNCTION_SET_FEATURE_TO_INTERFACE

URB_FUNCTION_SET_FEATURE_TO_ENDPOINT

URB_FUNCTION_SET_FEATURE_TO_OTHER

SET_INTERFACE:更改接口中的备用设置。请参阅 USB 规范中的第 9.4.9 节。

有关详细信息,请参阅如何在 USB 接口中选择备用设置

WdfUsbTargetDeviceSelectConfig

  1. 获取目标接口对象的 WDFUSBINTERFACE 句柄。
  2. 调用 WdfUsbInterfaceSelectSetting 方法。
  1. 获取指向目标接口对象的 IWDFUsbInterface 指针。
  2. 调用 IWDFUsbInterface::SelectSetting 方法。

_URB_SELECT_INTERFACE

(USBD_SelectInterfaceUrbAllocateAndBuild)

URB_FUNCTION_SELECT_INTERFACE

SYNC_FRAME:设置和获取终结点的同步帧编号。请参阅 USB 规范中的第 9.4.10 节。此请求是由 USB 驱动程序堆栈处理的;客户端驱动程序无法执行此操作。此请求是由 USB 驱动程序堆栈处理的;客户端驱动程序无法执行此操作。此请求是由 USB 驱动程序堆栈处理的;客户端驱动程序无法执行此操作。
对于设备类特定的请求和供应商命令。
  1. 声明一个设置包。请参阅 WDF_USB_CONTROL_SETUP_PACKET 结构。
  2. 调用 WDF_USB_CONTROL_SETUP_PACKET_INIT_CLASS 特定的请求或为供应商命令调用 WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR 以初始化设置包。
  3. 指定 WDF_USB_BMREQUEST_RECIPIENT 中定义的接收方值(设备、接口和终结点)。
  4. 调用 WdfUsbTargetDeviceSendControlTransferSynchronouslyWdfUsbTargetDeviceFormatRequestForControlTransfer 以发送请求。
  1. 声明一个设置包。请参阅 usb_hw.h 中声明的 WINUSB_CONTROL_SETUP_PACKET 结构。
  2. 调用 usb_hw.h 中定义的帮助程序宏 WINUSB_CONTROL_SETUP_PACKET_INIT_CLASSWINUSB_CONTROL_SETUP_PACKET_INIT_VENDOR 以初始化该设置包。
  3. 指定方向(请参阅 WINUSB_BMREQUEST_DIRECTION 枚举)、接收方(请参阅 WINUSB_BMREQUEST_RECIPIENT 枚举)和请求,如类或硬件规范中所述。
  4. 通过调用 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,将初始化的设置包与框架请求对象和传输缓冲区相关联以生成请求。
  5. 调用 IWDFIoRequest::Send 方法以发送请求。
  6. 在传输缓冲区中接收来自设备的信息。调用 IWDFMemory 方法以访问该缓冲区。

_URB_CONTROL_VENDOR_OR_CLASS_REQUEST

(UsbBuildVendorRequest)

URB_FUNCTION_VENDOR_DEVICE

URB_FUNCTION_VENDOR_INTERFACE

URB_FUNCTION_VENDOR_ENDPOINT

URB_FUNCTION_VENDOR_OTHER

URB_FUNCTION_CLASS_DEVICE

URB_FUNCTION_CLASS_INTERFACE

URB_FUNCTION_CLASS_ENDPOINT

URB_FUNCTION_CLASS_OTHER

 

如何为供应商命令发送控制传输 - KMDF

此步骤说明客户端驱动程序如何发送控制传输。在此示例中,客户端驱动程序发送一个供应商命令以从设备中检索固件版本。

  1. 为该供应商命令声明一个常量。查看硬件规范并确定要使用的供应商命令。
  2. 声明一个 WDF_MEMORY_DESCRIPTOR 结构,然后调用 WDF_MEMORY_DESCRIPTOR_INIT_BUFFER 宏以初始化该结构。在 USB 驱动程序完成请求后,该结构将从设备中收到响应。
  3. 根据是同步还是异步发送请求,指定发送选项:
  4. 声明一个 WDF_USB_CONTROL_SETUP_PACKET 结构以包含设置令牌,并设置该结构的格式。为此,请调用 WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR 宏以设定设置包的格式。在调用中,指定请求方向、接收方、发送请求选项(在步骤 3 中初始化)以及供应商命令常量。
  5. 调用 WdfUsbTargetDeviceSendControlTransferSynchronouslyWdfUsbTargetDeviceFormatRequestForControlTransfer 以发送请求。
  6. 检查框架返回的 NTSTATUS 值,并检查收到的值。

此代码示例将控制传输请求发送到 USB 设备以检索其固件版本。将同步发送请求,并且客户端驱动程序指定相对超时值 5 秒(以 100 纳秒为单位)。驱动程序将收到的响应存储在驱动程序定义的设备上下文中。



enum {   
    USBFX2_GET_FIRMWARE_VERSION = 0x1,  
....

} USBFX2_VENDOR_COMMANDS; 

#define WDF_TIMEOUT_TO_SEC              ((LONGLONG) 1 * 10 * 1000 * 1000)  // defined in wdfcore.h

const __declspec(selectany) LONGLONG   
            DEFAULT_CONTROL_TRANSFER_TIMEOUT = 5 * -1 * WDF_TIMEOUT_TO_SEC; 


typedef struct _DEVICE_CONTEXT
{
 
    ...
	   union {  
        USHORT      VersionAsUshort;  
        struct {  
            BYTE Minor;  
            BYTE Major;  
        } Version;  
    } Firmware; // Firmware version.

} DEVICE_CONTEXT, *PDEVICE_CONTEXT;


__drv_requiresIRQL(PASSIVE_LEVEL)  
VOID  GetFirmwareVersion(  
    __in PDEVICE_CONTEXT DeviceContext  
)  
{  
    NTSTATUS                        status;  
    WDF_USB_CONTROL_SETUP_PACKET    controlSetupPacket;  
    WDF_REQUEST_SEND_OPTIONS        sendOptions;  
    USHORT                          firmwareVersion;  
    WDF_MEMORY_DESCRIPTOR           memoryDescriptor;  
      
    PAGED_CODE();  
  
    firmwareVersion = 0;  
  
    WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, (PVOID) &firmwareVersion, sizeof(firmwareVersion));  
  
    WDF_REQUEST_SEND_OPTIONS_INIT(  
                                  &sendOptions,  
                                  WDF_REQUEST_SEND_OPTION_TIMEOUT  
                                  );  
  
    WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(  
                                         &sendOptions,  
                                         DEFAULT_CONTROL_TRANSFER_TIMEOUT  
                                         );  
                
    WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket,  
                                        BmRequestDeviceToHost,       // Direction of the request
                                        BmRequestToDevice,           // Recipient
                                        USBFX2_GET_FIRMWARE_VERSION, // Vendor command
                                        0,                           // Value
                                        0);                          // Index    
  
    status = WdfUsbTargetDeviceSendControlTransferSynchronously(  
                                        DeviceContext->UsbDevice,  
                                        WDF_NO_HANDLE,               // Optional WDFREQUEST
                                        &sendOptions,  
                                        &controlSetupPacket,  
                                        &memoryDescriptor,           // MemoryDescriptor                                          
                                        NULL);                       // BytesTransferred    
     
    if (!NT_SUCCESS(status)) 
    {  
        KdPrint(("Device %d: Failed to get device firmware version 0x%x\n", DeviceContext->DeviceNumber, status));  
        TraceEvents(DeviceContext->DebugLog,  
                    TRACE_LEVEL_ERROR,  
                    DBG_RUN,  
                    "Device %d: Failed to get device firmware version 0x%x\n",  
                    DeviceContext->DeviceNumber,  
                    status);  
    }
    else 
    {  
        DeviceContext->Firmware.VersionAsUshort = firmwareVersion;  
        TraceEvents(DeviceContext->DebugLog,  
                    TRACE_LEVEL_INFORMATION,  
                    DBG_RUN,  
                    "Device %d: Get device firmware version : 0x%x\n",  
                    DeviceContext->DeviceNumber,  
                    firmwareVersion);  
    }  
  
    return;  
}  


如何为 GET_STATUS 发送控制传输 - UMDF

此过程显示了客户端驱动程序如何发送 GET_STATUS 命令的控制传输。请求的接收方是设备,并且请求将获取 D1-D0 位中的信息。有关详细信息,请参阅 USB 规范中的图 9-4。

  1. 包括随 OSR USB Fx2 Learning Kit 的 UMDF 示例驱动程序提供的头文件 Usb_hw.h。
  2. 声明 WINUSB_CONTROL_SETUP_PACKET 结构。
  3. 通过调用帮助程序宏 WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS 初始化该设置包。
  4. BmRequestToDevice 指定为接收方。
  5. Index 值中指定 0。
  6. 调用帮助程序方法 SendControlTransferSynchronously 以同步发送请求。

    通过调用 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,帮助程序方法将初始化的设置包与框架请求对象和传输缓冲区相关联以生成请求。然后,帮助程序方法通过调用 IWDFIoRequest::Send 方法发送请求。该方法返回后,将检查返回的值。

  7. 若要确定状态是否指示自已供电和远程唤醒,请使用 WINUSB_DEVICE_TRAITS 枚举中定义的以下值:

此代码示例发送控制传输请求以获取设备状态。此示例调用名为 SendControlTransferSynchronously 的帮助程序方法以同步发送请求。



HRESULT  
CDevice::GetDeviceStatus ()  
{  

    HRESULT hr = S_OK;

    USHORT deviceStatus;  
    ULONG bytesTransferred;  
  
  
    TraceEvents(TRACE_LEVEL_INFORMATION,  
                DRIVER_ALL_INFO,  
                "%!FUNC!: entry");  
  
 
    // Setup the control packet.      

    WINUSB_CONTROL_SETUP_PACKET setupPacket;  
  
    WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS(  
                                      &setupPacket,  
                                      BmRequestToDevice,  
                                      0);  

    hr = SendControlTransferSynchronously(  
                 &(setupPacket.WinUsb),  
                 & deviceStatus,  
                 sizeof(USHORT),  
                 &bytesReturned  
                ); 

     if (SUCCEEDED(hr))  
    {  
        if (deviceStatus & USB_GETSTATUS_SELF_POWERED)
        {
             m_Self_Powered = true;
        } 
        if (deviceStatus & USB_GETSTATUS_REMOTE_WAKEUP_ENABLED)
        {
             m_remote_wake-enabled = true;
        }  

    }  

  
    return hr;  

 }


以下代码示例说明了名为 SendControlTransferSynchronously 的帮助程序方法的实现。该方法将同步发送请求。



HRESULT  
CDevice::SendControlTransferSynchronously(  
    _In_ PWINUSB_SETUP_PACKET SetupPacket,  
    _Inout_ PBYTE Buffer,  
    _In_ ULONG BufferLength,  
    _Out_ PULONG LengthTransferred  
    )  
{  
    HRESULT hr = S_OK;  
    IWDFIoRequest *pWdfRequest = NULL;  
    IWDFDriver * FxDriver = NULL;  
    IWDFMemory * FxMemory = NULL;   
    IWDFRequestCompletionParams * FxComplParams = NULL;  
    IWDFUsbRequestCompletionParams * FxUsbComplParams = NULL;  
  
    *LengthTransferred = 0;  
      
    hr = m_FxDevice->CreateRequest( NULL, //pCallbackInterface
                                    NULL, //pParentObject
                                    &pWdfRequest);  
  
    if (SUCCEEDED(hr))  
    {  
        m_FxDevice->GetDriver(&FxDriver);  
  
        hr = FxDriver->CreatePreallocatedWdfMemory( Buffer,  
                                                    BufferLength,  
                                                    NULL,        //pCallbackInterface
                                                    pWdfRequest, //pParetObject
                                                    &FxMemory );  
    }  
  
    if (SUCCEEDED(hr))  
    {  
        hr = m_pIUsbTargetDevice->FormatRequestForControlTransfer( pWdfRequest,  
                                                                   SetupPacket,  
                                                                   FxMemory,  
                                                                   NULL); //TransferOffset
    }                                                            
                          
    if (SUCCEEDED(hr))  
    {  
        hr = pWdfRequest->Send( m_pIUsbTargetDevice,  
                                WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,  
                                0); //Timeout      }  
  
    if (SUCCEEDED(hr))  
    {  
        pWdfRequest->GetCompletionParams(&FxComplParams);  
  
        hr = FxComplParams->GetCompletionStatus();  
    }  
  
    if (SUCCEEDED(hr))  
    {  
        HRESULT hrQI = FxComplParams->QueryInterface(IID_PPV_ARGS(&FxUsbComplParams));  
        WUDF_TEST_DRIVER_ASSERT(SUCCEEDED(hrQI));  
  
        WUDF_TEST_DRIVER_ASSERT( WdfUsbRequestTypeDeviceControlTransfer ==   
                            FxUsbComplParams->GetCompletedUsbRequestType() );  
  
        FxUsbComplParams->GetDeviceControlTransferParameters( NULL,  
                                                             LengthTransferred,  
                                                             NULL,  
                                                             NULL );  
    }  
  
    SAFE_RELEASE(FxUsbComplParams);  
    SAFE_RELEASE(FxComplParams);  
    SAFE_RELEASE(FxMemory);  
  
    pWdfRequest->DeleteWdfObject();          
    SAFE_RELEASE(pWdfRequest);  
  
    SAFE_RELEASE(FxDriver);  
  
    return hr;  
}  



备注

如果将 Winusb.sys 作为设备的功能驱动程序,则可以从应用程序中发送控制传输。要在 WinUSB 中设定设置包的格式,请使用 UMDF 帮助程序宏和结构,如本主题的表中所述。要发送请求,请调用 WinUsb_ControlTransfer 函数。

 

 

显示:
© 2015 Microsoft