windows 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 函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/776312.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

二刷力扣——单调栈

739. 每日温度 单调栈应该从栈底到栈顶 是递减的。 找下一个更大的 ,用递减单调栈,就可以确定在栈里面的每个比当前元素i小的元素,下一个更大的就是这个i,然后弹出并记录;然后当前元素i入栈,仍然满足递减…

基于.NET开源游戏框架MonoGame实现的开源项目合集

前言 今天分享一些基于.NET开源游戏框架MonoGame实现的开源项目合集。 MonoGame项目介绍 MonoGame是一个简单而强大的.NET框架,使用C#编程语言可以创建桌面PC、视频游戏机和移动设备游戏。它已成功用于创建《怒之铁拳4》、《食肉者》、《超凡蜘蛛侠》、《星露谷物…

linux之管道重定向

管道与重定向 一、重定向 将原输出结果存储到其他位置的过程 标准输入、标准正确输出、标准错误输出 ​ 进程在运行的过程中根据需要会打开多个文件,每打开一个文件会有一个数字标识。这个标识叫文件描述符。 进程使用文件描述符来管理打开的文件(FD--…

【Dell R730 折腾记录】风扇调速--在 Ubuntu 系统上开机自启动并每隔30分钟执行一次风扇定速脚本

前段时间升级了一下机柜里的服务器,替换掉了一台旧的 Dell 服务器,换上了这台 R730。但是无奈于噪音的袭扰,搁置了一段时间。我在这台机器上目前安装了一块 Intel Xeon E5-2630v3 芯片以及一张改过散热的 NVIDIA Tesla P4 计算卡。结果就是散…

电脑硬盘分区的基本步骤(2个实用的硬盘分区方法)

在现代计算机中,硬盘分区是非常重要的一步。无论是新硬盘的初始化,还是重新组织现有硬盘,分区都是必不可少的操作。本文将详细介绍电脑硬盘分区的基本步骤,帮助您更好地管理和利用硬盘空间。 文章开始,我们先简单说一…

【C++】类和对象3.0

浅浅介绍最近所学,希望有兴趣的读者老爷垂阅! 目录 1.再谈构造函数 1.1.构造函数体赋值 1.2.初始化列表 1.3.构造函数的小知识 2. explicit关键字 3.static成员 3.1.static成员概念 3.2.static成员特性 4.友元 4.1.友元函数 4.2.友元类 5…

七、Linux二进制安装Redis集群

目录 七、Linux二进制安装Redis集群1 安装Redis所需依赖2 单机安装Redis(7.2.4)2.1 下载Redis2.2 安装Redis 3 分布式部署模式(Redis Cluster)3.1 分布式部署模式的配置文件3.2创建集群 4 主从复制模式(Redis Sentinel…

jenkins搭建部署前端工程 ,从0到1

一.java环境配置 1 安装tomcatjdk17 这个也行 3 安装maven3.3.9 安装教程参考 4 安装Jenkins 下载地址 参考教程 二、相关配置 1 访问http://localhost:8080/jenkins,进入Jenkins初始化页面,第一次启动时间可能有点长,耐心等待。进入成功后会…

AndroidKille不能用?更新apktool插件-cnblog

AndroidKiller不更新插件容易报错 找到apktool管理器 填入apktool位置,并输入apktool名字 选择默认的apktool版本 x掉,退出重启 可以看到反编译完成了

《Windows API每日一练》8.3 scrollbar控件

在第三章SYSMETS2.C实例中,我们是通过CreateWindow函数创建窗口的参数窗口样式中添加垂直或水平滚动条。本节我们将讲述作为子窗口控件的滚动条。 本节必须掌握的知识点: 滚动条类 滚动条控件和着色 8.3.1 滚动条类 ■窗口滚动条与滚动条控件的异同 …

带你了解“Java新特性——模块化”

Java平台从Java 8向Java 9及更高版本的进化,其中引入了一个重要的新特性——模块系统(Project Jigsaw)。模块系统的目的是解决大型应用的依赖管理问题,提升性能,简化JRE,增强兼容性和安全性,并提…

一文了解常见DNS问题

当企业的DNS出现故障时,为不影响企业的正常运行,团队需要能够快速确定问题的性质和范围。那么有哪些常见的DNS问题呢? 域名解析失败: 当您输入一个域名,但无法获取到与之对应的IP地址,导致无法访问相应的网…

Blazor 逐键搜索并动态反馈到url

Blazor 逐键搜索并动态反馈到url 源码 前言: 今天打开了 spotify 网页版找歌, 突然发现这个功能很抓眼球,于是试试blazor能不能模仿一下. 1. 节省时间,直接用模板开搞 新建项目,使用 Bootstrap Blazor App 模板 , 命名为 b22dynamicURL BootstrapBlazor简介: BootstrapBlaz…

数据库概念题总结

1、 2、简述数据库设计过程中,每个设计阶段的任务 需求分析阶段:从现实业务中获取数据表单,报表等分析系统的数据特征,数据类型,数据约束描述系统的数据关系,数据处理要求建立系统的数据字典数据库设计…

SQL注入方法

文章目录 前言如何测试与利用注入点手工注入思路工具sqlmap-r-u-m--level--risk-v-p--threads-batch-smart--os-shell--mobiletamper插件获取数据的相关参数 前言 记录一些注入思路和经常使用的工具,后续有用到新的工具和总结新的方法再继续补充。 如何测试与利用注…

使用 OpenCV 和 Python 进行车道检测和物体检测(YOLO)

本项目旨在开发一个集车道检测与物体检测功能于一体的智能视觉分析系统,利用先进的计算机视觉技术和深度学习模型,实现实时的道路场景理解和目标识别。系统主要依托OpenCV这一强大的计算机视觉库,以及Python作为编程语言,融合了车…

CentOS7.9下yum升级Apache HTTP Server2.4.6到2.4.60

1、CentOS7.9系统默认的Apache版本 在CentOS7.9上,如果使用yum安装Apache HTTP Server是最多到2.4.6版本的,这是因为el7下官方仓库的最高版本就是2.4.6,证据如下: $ yum info httpd ...... Installed Packages Name : ht…

Jetpack Compose实战教程(五)

Jetpack Compose实战教程(五) 第五章 如何在Compose UI中使用基于命令式UI的自定义View 文章目录 Jetpack Compose实战教程(五)一、前言二、本章目标三、开始编码3.1 先让自定义控件能跑起来3.2给自定义控件使用compose的方式赋值…

【设计模式】工厂模式(定义 | 特点 | Demo入门讲解)

文章目录 定义简单工厂模式案例 | 代码Phone顶层接口设计Meizu品牌类Xiaomi品牌类PhoneFactory工厂类Customer 消费者类 工厂方法模式案例 | 代码PhoneFactory工厂类 Java高级特性---工厂模式与反射的高阶玩法方案:反射工厂模式 总结 其实工厂模式就是用一个代理类帮…

Java项目:基于SSM框架实现的学生公寓管理中心系统【ssm+B/S架构+源码+数据库+毕业论文】

一、项目简介 本项目是一套基于SSM框架实现的学生公寓管理中心系统 包含:项目源码、数据库脚本等,该项目附带全部源码可作为毕设使用。 项目都经过严格调试,eclipse或者idea 确保可以运行! 该系统功能完善、界面美观、操作简单、…