首页 > 系统相关 >windows USB 设备驱动开发- 不同模型下的控制传输

windows USB 设备驱动开发- 不同模型下的控制传输

时间:2024-07-07 22:26:17浏览次数:18  
标签:请求 WDF windows 控制传输 发送 hr 驱动程序 USB

在不同的模型下,USB控制传输会有不同的特点,但是任何控制传输的目标都始终是默认端点。 接收者是设备的实体,其信息(描述符、状态等)是主机感兴趣的。请求可进一步分为:配置请求、功能请求和状态请求。

发送配置请求以从设备获取信息,以便主机可以对其进行配置,例如GET_DESCRIPTOR请求。 这些请求也可能是主机发送的写入请求,目的是在设备中设置特定的配置或备用设置。
客户端驱动程序发送功能请求以启用或禁用设备、接口或端点支持的某些布尔设备设置。
状态请求 使主机能够获取或设置设备、端点或接口的 USB 定义状态位。

下面我们借助代码来仔细分析一下:

驱动程序模型

下面是三种常见的驱动模式

  • 内核模式驱动程序框架
  • 用户模式驱动程序框架
  • WinUSB
前提条件

在客户端驱动程序能够枚举管道之前,请确保客户端驱动程序必须已创建框架 USB 目标设备对象。

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

KMDF 客户端驱动程序:KMDF 客户端驱动程序必须调用 WdfUsbTargetDeviceCreateWithParameters 方法来获取 WDFUSBDEVICE 句柄。

UMDF 客户端驱动程序:UMDF 客户端驱动程序必须通过查询框架目标设备对象获取 IWDFUsbTargetDevice 指针。 

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

  • 请求的方向:从主机到设备,或者从设备到主机;
  • 请求的接收者:设备、接口、端点或其他;
  • 请求的类别:标准、类或供应商;可以从官方的 USB 规范中获取所有此类信息;
  • 请求的类型,例如 GET_DESCRIPTPOR 请求。 有关详细信息,请参阅 USB 规范中的 9.5 节;
  • wValue 和 wIndex 值。 这些值取决于请求的类型;

所有 UMDF 驱动程序必须与内核模式驱动程序通信才能通过设备发送和接收数据。 对于 USB UMDF 驱动程序,内核模式驱动程序始终是 Microsoft 提供的驱动程序 WinUSB (Winusb.sys)。

每当 UMDF 启动程序针对 USB 驱动程序堆栈发出请求时,Windows I/O 管理器就会将该请求发送给 WinUSB。 收到请求后,WinUSB 会处理请求,或者将其转发给 USB 驱动程序堆栈。

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

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

  • 标准请求 在 USB 规范中定义。 发送标准请求的目的是获取有关设备、其配置、接口和端点的信息。 每个请求的接收者取决于请求的类型。 接收方可以是设备、接口或端点;
  • 类请求 由特定的设备类规范定义;
  • 供应商请求 由供应商提供,取决于设备支持的请求;

Microsoft 提供的 USB 堆栈处理与设备进行的所有协议通信,如前面的跟踪所示。 此驱动程序会公开设备驱动程序接口 (DDI),后者允许客户端驱动程序以多种方式发送控制传输。 如果客户端驱动程序是 Windows Driver Foundation (WDF) 驱动程序,则它可以直接调用例程来发送常见类型的控制请求。 WDF 本质上支持 KMDF 和 UMDF 的控制传输。

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

UMDF 驱动程序

请使用下表来确定向 USB 驱动程序堆栈发送控制请求的最佳方式。 

KMDF发送供应商控制传输 -

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

1.声明一个用于供应商命令的常量。 研究硬件规范,确定要使用的供应商命令。

2.通过调用 WDF_MEMORY_DESCRIPTOR_INIT_BUFFER 宏来声明 WDF_MEMORY_DESCRIPTOR 结构并将其初始化。 此结构会在 USB 驱动程序完成请求后从设备接收响应。

3.指定发送选项,具体取决于你是以同步方式还是异步方式发送请求:

  • 如果通过调用 WdfUsbTargetDeviceSendControlTransferSynchronously 以同步方式发送请求,请指定超时值。 该值很重要,因为如果没有超时值,则可能无限期阻止线程。

为此,请通过调用 WDF_REQUEST_SEND_OPTIONS_INIT 宏声明 WDF_REQUEST_SEND_OPTIONS 结构并将其初始化。 将该选项指定为 WDF_REQUEST_SEND_OPTION_TIMEOUT。

接下来,通过调用 WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT 宏设置超时值。

  • 如果以异步方式发送请求,请实施一个完成例程。 释放完成例程中所有分配的资源。

4.声明一个 WDF_USB_CONTROL_SETUP_PACKET 结构,使之包含设置令牌,然后将结构格式化。 为此,请调用 WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR 宏来格式化设置数据包。 在调用中指定请求的方向、接收者、发送-请求选项(已在步骤 3 中初始化)以及供应商命令的常量。

5.通过调用 WdfUsbTargetDeviceSendControlTransferSynchronously 或 WdfUsbTargetDeviceFormatRequestForControlTransfer 来发送请求。

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;
}

UMDF发送 GET_STATUS 的控制传输 -

以下过程演示了客户端驱动程序如何发送 GET_STATUS 命令的控制传输。 请求的接收者为设备,请求获取 D1-D0 位中的信息。 

  • 包括随附在 OSR USB Fx2 学习工具包的 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 函数。

标签:请求,WDF,windows,控制传输,发送,hr,驱动程序,USB
From: https://blog.csdn.net/m0_72813396/article/details/140165878

相关文章

  • windows USB 设备驱动开发- USB管道的通讯(一)
    WindowsWDF框架提供一个名为连续读取器的专用对象。此对象使USB客户端驱动程序能够连续读取批量和中断终结点中的数据,只要有数据可用。若要使用读取器,客户端驱动程序必须具有与驱动程序从中读取数据的终结点关联的USB目标管道对象的句柄。终结点必须位于活动配置中。......
  • Windows系统组合键
    前言到目前为止,使用Windows系统的用户占全球量,微软为了使用户更便捷,更有效的工作和提高工作效率,因此许多功能组合键就出现了。那么,今天,我们就跟随我的介绍,一步步来解释常用快捷键组合,以此为大家节省时间,提高效率目录本文分为几个部分来介绍1.win键2.ctrl键3alt键4.特......
  • Windows 电源管理中的 "快速启动(推荐)" 是一种功能选项,它允许电脑在关机后以一种较快的
    Windows电源管理中的"快速启动(推荐)"是一种功能选项,它允许电脑在关机后以一种较快的方式启动。这个功能通过将系统的部分内容保存到硬盘上的一个文件中,而不是完全关闭电脑,从而实现更快的启动速度。具体来说,当你选择启用快速启动时,Windows会将当前的系统状态保存到一个名为hibe......
  • 2024-07-07 如何把ipad当作windows副屏使用 ==》 通过软件dute display和数据线连接
    windows:进入dutedisplay官网https://www.duetdisplay.com/zh#download,下载并安装ipad:在苹果应用商店搜索dutedisplay,选中并下载 注意:你需要注册一个dutedisplay账号,才能登录该软件,它是付费的,so,我看到付费我就放弃了。如果,你给钱了,那么,接下来我也不知道对不对,你用ipad充电线......
  • QML仿Windows开机动画
    importQtQuick2.5importQtQuick.Window2.2Window{visible:truewidth:640height:320id:roottitle:qsTr("win10loading")color:"#1086a2"Repeater{model:5id:repeaterRe......
  • Windows 11 中使用 Win10的文件资源管理器!
    1.在Windows11中恢复旧文件资源管理器,首先打开记事本并粘贴以下文本代码:WindowsRegistryEditorVersion5.00[HKEY_CURRENT_USER\Software\Classes\CLSID\{2aa9162e-c906-4dd9-ad0b-3d24a8eef5a0}]@="CLSID_ItemsViewAdapter"[HKEY_CURRENT_USER\Software\Classes\CLS......
  • 这些 PowerShell 命令适用于清理 Windows 中更多不同组件和服务的日志。通过定期执行
    清理临时文件:powershellCopyCodeRemove-Item-Path"$env:TEMP\*"-Force-RecurseRemove-Item-Path"$env:LOCALAPPDATA\Temp\*"-Force-Recurse清理回收站:powershellCopyCodeClear-RecycleBin-Force清理浏览器缓存(例如清理Chrome缓存):powershellCopy......
  • Windows如何查看端口是否占用,并结束端口进程
    需求与问题:前后端配置了跨域操作,但是仍然报错,可以考虑端口被两个程序占用,找不到正确端口或者后端接口书写是否规范,特别是利用PythonFlask书写时要保证缩进是否正确!Windows操作系统中,查看端口是否占用并结束占用端口的程序是一个常见的操作,特别是在进行网络配置或软件安装时。......
  • 安装MySQL(Windows10和Linux CentOS7) 很详细的
    Windows10下安装MySQL1.下载MySQL官网下载MySQL:https://www.mysql.com/进入官网点击DOWNLOADS下滑点击MySQLCommunity(GPL)Downloads点击MySQLInstallerforWindows选择版本下载这里就不需要登录注册了,直接下载2.安装MySQL找到下载的文件双击之后选择Se......
  • windows下C++配置googletest过程记录
    文章目录下载googletest将googletest解压到项目目录并创建build文件夹编译googletest在项目的CMakeLists.txt文件中添加相关依赖编写测试并执行参考下载googletestReleasev1.14.0·google/googletest·GitHub将googletest解压到项目目录并创建build文件夹在g......