本主题介绍了控制传输结构,以及客户端驱动程序应如何将控制请求发送到设备。
本主题内容:
- 关于默认终结点
- 控制传输布局
- 支持的驱动程序模型
- 先决条件
- Microsoft 定义的控制传输请求发送方法
- 如何为供应商命令发送控制传输 - KMDF
- 如何为 GET_STATUS 发送控制传输 - UMDF
关于默认终结点
所有 USB 设备必须至少支持一个终结点(称为默认终结点)。针对默认终结点的任何传输称为控制传输。控制传输的目的是使主机能够获取设备信息,配置设备或执行设备特有的控制操作。
让我们开始分析默认终结点的这些特性。
- 默认终结点的地址为 0。
- 默认终结点是双向的,即,主机可以在一个传输中将数据发送到终结点以及从中接收数据。
- 默认终结点在设备级别可用,它不是在设备的任何接口中定义的。
- 在主机和设备之间建立连接后,默认终结点将立即处于活动状态。甚至在选择配置之前,它就处于活动状态。
- 默认终结点的最大包大小取决于设备的总线速度。低速为 8 个字节;全速和高速为 64 个字节;超高速为 512 个字节。
控制传输布局
由于控制传输是高优先级传输,因此,主机在总线上保留一定数量的带宽。低速和全速设备保留 10% 带宽;高速和超高速传输设备保留 20% 带宽。现在,让我们看一下控制传输布局。
控制传输分为三个事务:设置事务、数据事务和状态事务。每个事务包含三种类型的包:令牌包、数据包和握手包。
某些字段是所有包通用的。这些字段包括:
- 指示包开始的 Sync 字段。
- 指示包类型和事务方向的包标识符 (PID);对于握手包,它还指示事务成功或失败。
- 指示包结束的 EOP 字段。
- Token packet
每个设置事务以令牌包开始。下面是包的结构。主机始终发送令牌包。
PID 值指示令牌包类型。下面是可能的值:
- SETUP:指示控制传输中的设置事务的开始位置。
- IN:指示主机从设备中请求数据(读取时)。
- OUT:指示主机将数据发送到设备(写入时)。
- SOF:指示帧的开始位置。这种类型的令牌包包含一个 11 位的帧编号。主机发送 SOF 包。发送包的频率取决于总线速度。对于全速总线,主机每 1 毫秒发送一次包;对于高速总线,主机每 125 微秒发送一次包。
- Data packet
紧跟令牌包后面的是包含负载的数据包。每个数据包可以包含的字节数取决于默认终结点的最大包大小。可以由主机或设备发送数据包,具体取决于传输方向。
- Handshake packet
紧跟数据包后面的是握手包。包的 PID 指示是由主机还是设备接收包。可以由主机或设备发送握手包,具体取决于传输方向。
你可以使用任何 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) 2 0x0100 请求值指示描述符类型为 DEVICE。 wIndex(请参阅第 9.3.4 节) 2 0x0000 方向是从主机到设备(D7 为 1)
终结点编号为 0。
wLength(请参阅第 9.3.5 节) 2 0x0012 该请求将检索 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 个字节,如下所示:
因此,我们看到要将 18 个字节从设备传输到主机,主机启动三个数据事务并跟踪传输的字节数 (8+8+2)。
注意 请注意数据事务 19、23 和 26 中的数据包的 PID。PID 交替使用 DATA0 和 DATA1。此顺序称为数据切换。如果有多个数据事务,则使用数据切换检查包顺序。这种方法可确保数据包不会出现重复或丢失。
通过将合并的数据包映射到设备描述符结构(请参阅表 9-8),我们可以看到以下字段和值:字段 大小 值 描述 bLength 1 0x12 设备描述符长度(18 个字节)。 bDescriptorType 1 0x01 描述符类型为设备。 bcdUSB 2 0x0100 规范版本号为 1.00。 bDeviceClass 1 0x00 设备类为 0。配置中的每个接口具有类信息。 bDeviceSubClass 1 0x00 子类为 0,因为设备类为 0。 bProtocol 1 0x00 协议为 0。此设备不使用任何类特定的协议。 bMaxPacketSize0 1 0x08 终结点的最大包大小为 8 个字节。 idVendor 2 0x0562 电报通信。 idProduct 2 0x0002 USB 麦克风。 bcdDevice 2 0x0100 指示设备版本号。 iManufacturer 1 0x01 制造商字符串。 iProduct 1 0x02 产品字符串。 iSerialNumber 1 0x03 序列号。 bNumConfigurations 1 0x01 配置数。 -
Status transaction
最后,主机启动最后一个事务以完成控制传输:状态事务。
主机使用 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 节。
- wValue 和 wIndex 值。这些值取决于请求类型。
- 如果要编写 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 定义的状态位。
- 类请求—是由特定设备类规范定义的。
- 供应商请求—是由供应商提供的,这些请求取决于设备支持的请求。
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 节。 |
|
| 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 选择第一个配置。要检索设备定义的配置编号,请执行以下操作:
| 默认情况下,UMDF 选择第一个配置。要检索设备定义的配置编号,请执行以下操作:
| _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 节。 |
|
| _URB_CONTROL_GET_INTERFACE_REQUEST URB_FUNCTION_GET_INTERFACE |
GET_STATUS:从设备、终结点或接口中获取状态位。请参阅 USB 规范中的第 9.4.5 节。 |
|
| _URB_CONTROL_GET_STATUS_REQUEST 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 选择默认配置和每个接口中的第一个备用设置。客户端驱动程序无法更改配置。 | ( USBD_SelectConfigUrbAllocateAndBuild) URB_FUNCTION_SELECT_CONFIGURATION |
SET_DESCRIPTOR:更新现有的设备、配置或字符串描述符。请参阅 USB 规范中的第 9.4.8 节。 此请求并不常用。不过,USB 驱动程序堆栈从客户端驱动程序中接受此类请求。 |
|
| _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 节。 |
|
| 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
|
| ( USBD_SelectInterfaceUrbAllocateAndBuild) URB_FUNCTION_SELECT_INTERFACE |
SYNC_FRAME:设置和获取终结点的同步帧编号。请参阅 USB 规范中的第 9.4.10 节。 | 此请求是由 USB 驱动程序堆栈处理的;客户端驱动程序无法执行此操作。 | 此请求是由 USB 驱动程序堆栈处理的;客户端驱动程序无法执行此操作。 | 此请求是由 USB 驱动程序堆栈处理的;客户端驱动程序无法执行此操作。 |
对于设备类特定的请求和供应商命令。 |
|
| _URB_CONTROL_VENDOR_OR_CLASS_REQUEST 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
此步骤说明客户端驱动程序如何发送控制传输。在此示例中,客户端驱动程序发送一个供应商命令以从设备中检索固件版本。
- 为该供应商命令声明一个常量。查看硬件规范并确定要使用的供应商命令。
- 声明一个 WDF_MEMORY_DESCRIPTOR 结构,然后调用 WDF_MEMORY_DESCRIPTOR_INIT_BUFFER 宏以初始化该结构。在 USB 驱动程序完成请求后,该结构将从设备中收到响应。
- 根据是同步还是异步发送请求,指定发送选项:
- 如果调用 WdfUsbTargetDeviceSendControlTransferSynchronously 以同步发送请求,请指定超时值。该值是非常重要的,因为如果没有超时,则可能会无限期阻止线程。
为此,声明一个 WDF_REQUEST_SEND_OPTIONS 结构,然后调用 WDF_REQUEST_SEND_OPTIONS_INIT 宏以初始化该结构。将该选项指定为WDF_REQUEST_SEND_OPTION_TIMEOUT。
接下来,调用 WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT 宏以设置超时值。
- 如果要异步发送请求,请实现一个完成例程。释放在完成例程中分配的所有资源。
- 如果调用 WdfUsbTargetDeviceSendControlTransferSynchronously 以同步发送请求,请指定超时值。该值是非常重要的,因为如果没有超时,则可能会无限期阻止线程。
- 声明一个 WDF_USB_CONTROL_SETUP_PACKET 结构以包含设置令牌,并设置该结构的格式。为此,请调用WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR 宏以设定设置包的格式。在调用中,指定请求方向、接收方、发送请求选项(在步骤 3 中初始化)以及供应商命令常量。
- 调用 WdfUsbTargetDeviceSendControlTransferSynchronously 或 WdfUsbTargetDeviceFormatRequestForControlTransfer 以发送请求。
- 检查框架返回的 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。
- 包含 OSR USB Fx2 Learning Kit 的 UMDF 示例驱动程序中的头文件 Usb_hw.h。
- 声明一个 WINUSB_CONTROL_SETUP_PACKET 结构。
- 调用帮助程序宏 WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS 以初始化设置包。
- 将 BmRequestToDevice 指定为接收方。
- 在 Index 值中指定 0。
- 调用帮助程序方法 SendControlTransferSynchronously 以同步发送请求。
通过调用 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,帮助程序方法将初始化的设置包与框架请求对象和传输缓冲区相关联以生成请求。然后,帮助程序方法调用 IWDFIoRequest::Send 方法以发送请求。在该方法返回后,检查返回的值。
- 要确定状态是否指示自已供电和远程唤醒,请使用 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 函数。