如何发送 USB 控制传输(UWP 应用)

本文演示了:

  • 如何设置 USB 设置数据包的格式
  • 如何从应用启动 USB 控制传输

重要的 API

与 USB 设备通信的应用通常发送多个控制传输请求。 这些请求获取有关设备的信息,并发送硬件供应商定义的控制命令。 在本主题中,你将了解控制传输以及如何在 UWP 应用中格式化和发送它们。

控制传输可以读取或写入配置信息,或者执行硬件供应商定义的特定于设备的函数。 如果传输执行写入操作,则是 OUT 传输;如果执行读取操作,则是 IN 传输。 无论方向如何,主机系统上的软件(如 UWP 应用)始终生成并发起控制传输请求。 有时,应用可以发起读取或写入数据的控制传输。 在这种情况下,可能需要发送额外的缓冲区。在这种情况下,

为了适应所有类型的控制传输,Windows.Devices.Usb 提供了以下方法:

适用于 USB 的 Windows 运行时 API 的 USB 控制传输。

USB 控制传输还用于获取描述符数据或发送标准命令。 但是,建议你通过调用 Windows.Devices.Usb 提供的特定方法而不是手动生成控制传输来发送这些类型的请求。 例如,若要选择备用设置,请调用 SelectSettingAsync,而不是调用 SendControlOutTransferAsync (UsbSetupPacket)

不支持某些类型的标准请求的控制传输。 但是,如果你的设备属于 Windows.Devices.Usb 支持的设备类,则可以根据设备类规范的定义发送一些请求。

准备工作

  • 必须打开设备,并获取 UsbDevice 对象。 请阅读如何连接 USB 设备(UWP 应用)
  • 获取有关供应商定义的控制命令的信息。 这些命令通常在硬件规范中定义。
  • 可以在 CustomUsbDeviceAccess 示例、Scenario2_ControlTransfer.cpp 和 Scenario2_ControlTransfer.h 中看到本主题中显示的完整代码。

步骤 1:填充设置数据包

在本主题中,我们将向以各种模式闪烁灯光的设备发送控制传输。 为了填充设置数据包,必须知道控制命令是由硬件供应商定义的:

  • bmRequestType (D7):OUT
  • bmRequestType (D4):设备
  • bmRequestType (D6…D5):供应商
  • bRequest:0x03
  • wValue:0-7(该范围内的任何数字,包括在内)
  • wIndex:0
  • wLength:0

对于控制传输,必须填充一个设置数据包,其中包含有关传输的所有信息;无论请求读取还是写入数据、请求类型等。 设置数据包的格式在官方 USB 规范中定义。 设置数据包字段的值由设备的硬件规范提供。

  1. 创建 UsbSetupPacket 对象。

  2. 通过设置各种属性填充 UsbSetupPacket 对象。 下表显示了 USB 定义的设置数据包字段,以及与这些字段对应的属性:

    第 9.3 节中的字段 properties 说明
    bmRequestType (D7) UsbControlRequestType.Direction 请求的方向。 请求是从主机到设备(输出传输)还是设备到主机(输入传输)
    bmRequestType (D4) UsbControlRequestType.Recipient 请求的接收方。 所有控制传输都以默认终结点为目标。 但是,接收方可能是设备、接口、终结点或其他。 有关 USB 设备、接口、终结点层次结构的详细信息,请参阅“设备布局”。
    bmRequestType (D6…D5) UsbControlRequestType.ControlTransferType 请求类别。 标准、类或供应商。
    bRequest UsbSetupPacket.Request 请求类型。 如果请求是标准请求(例如,GET_DESCRIPTOR 请求),则请求由 USB 规范定义。 否则,可能由供应商定义。
    wValue UsbSetupPacket.Value 取决于请求的类型。
    wIndex UsbSetupPacket.Index 取决于请求的类型。
    wLength UsbSetupPacket.Length 此请求中发送或接收的数据包的长度。

![注意] 对于某些控制传输,可能需要提供 bmRequestType 作为原始字节。 在这种情况下,可以在 UsbControlRequestType.AsByte 属性中设置字节。

步骤 2:启动异步操作以发送控制传输

若要发送控制传输,必须具有 UsbDevice 对象。 控制传输可能需要也可能不需要设置数据包后面的数据包。

若要启动控制传输,请调用 SendControlInTransferAsyncSendControlOutTransferAsync 的替代。 如果传输使用数据包,则调用 SendControlOutTransferAsync(UsbSetupPacket,IBuffer)SendControlInTransferAsync(UsbSetupPacket,IBuffer)。 这些方法采用一个附加参数,其中包含要从设备写入或从设备接收数据的数据。 使用流程图确定要调用的替代。

调用将启动并进行异步操作。 操作完成后,调用将返回包含操作结果的 IAsyncOperation 对象。 对于 OUT 传输,该对象返回传输中发送的字节数。 对于 IN 传输,对象包含缓冲区,其中包含从设备读取的数据。

USB 控制传输代码示例

此示例代码显示了如何发送控制传输,以更改 SuperMUTT 设备上的闪烁模式。 传输的设置数据包包含供应商定义的命令。 该示例位于 Scenario2_ControlTransfer.cpp 中。

async Task SetSuperMuttLedBlinkPatternAsync(Byte pattern)
        {
            UsbSetupPacket initSetupPacket = new UsbSetupPacket
            {
                RequestType = new UsbControlRequestType
                {
                    Direction = UsbTransferDirection.Out,
                    Recipient = UsbControlRecipient.Device,
                    ControlTransferType = UsbControlTransferType.Vendor
                },
                Request = SuperMutt.VendorCommand.SetLedBlinkPattern,
                Value = pattern,
                Length = 0
            };

            UInt32 bytesTransferred = await EventHandlerForDevice.Current.Device.SendControlOutTransferAsync(initSetupPacket);

            MainPage.Current.NotifyUser("The Led blink pattern is set to " + pattern.ToString(), NotifyType.StatusMessage);
        }

此示例代码显示了如何发送控制传输,以更改 SuperMUTT 设备上的闪烁模式。 传输的设置数据包包含供应商定义的命令。 该示例位于 Scenario2_ControlTransfer.cpp 中。

async Task<IBuffer> SendVendorControlTransferInToDeviceRecipientAsync(Byte vendorCommand, UInt32 dataPacketLength)
 {
    // Data will be written to this buffer when we receive it
    var buffer = new Windows.Storage.Streams.Buffer(dataPacketLength);

    UsbSetupPacket initSetupPacket = new UsbSetupPacket
    {
        RequestType = new UsbControlRequestType
        {
            Direction = UsbTransferDirection.In,
            Recipient = UsbControlRecipient.Device,
            ControlTransferType = UsbControlTransferType.Vendor,
        },
        Request = vendorCommand,
        Length = dataPacketLength
    };

    return await EventHandlerForDevice.Current.Device.SendControlInTransferAsync(initSetupPacket, buffer);
}