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.hconst __declspec(selectany) LONGLONGDEFAULT_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 requestBmRequestToDevice,           // RecipientUSBFX2_GET_FIRMWARE_VERSION, // Vendor command0,                           // Value0);                          // Index status = WdfUsbTargetDeviceSendControlTransferSynchronously(DeviceContext->UsbDevice,WDF_NO_HANDLE,               // Optional WDFREQUEST&sendOptions,&controlSetupPacket,&memoryDescriptor,           // MemoryDescriptorNULL);                       // 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, //pCallbackInterfaceNULL, //pParentObject&pWdfRequest);if (SUCCEEDED(hr)){m_FxDevice->GetDriver(&FxDriver);hr = FxDriver->CreatePreallocatedWdfMemory( Buffer,BufferLength,NULL,        //pCallbackInterfacepWdfRequest, //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.mzph.cn/diannao/41621.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode力扣_双指针问题

141. 环形链表 思路:判断链表中是否有环是经典的算法问题之一。常见的解决方案有多种,其中最经典、有效的一种方法是使用 快慢指针(Floyd’s Cycle-Finding Algorithm)。 初始化两个指针:一个快指针(fast&…

uni-app 使用Pinia进行全局状态管理并持久化数据

1.引言 最近在学习移动端的开发,使用uni-app前端应用框架,通过学习B站的视频以及找了一个开发模板,终于是有了一些心得体会。 B站视频1:Day1-01-uni-app小兔鲜儿导学视频_哔哩哔哩_bilibili B站视频2:01-课程和uni的…

[FFmpeg] windows下安装带gpu加速的ffmpeg

1.显卡能力排查 目前只有 NIVIDIA 支持 ffmpeg 的 gpu加速(AMD貌似也陆续开始支持)。 在下述网站中查找自己的显卡能够支持的编解码格式。https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-newhttps://developer.nvidia.com/video-encode-and-decod…

Vue88-Vuex中的mapActions、mapMutations

一、mapMutations的调用 此时结果不对,因为:若是点击事件不传值,默认传的是event!,所以,修改如下: 解决方式1: 解决方式2: 不推荐,写法麻烦! 1-…

【Unity数据交互】二进制私

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 专栏交流🧧&…

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

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

【C++】 解决 C++ 语言报错:Invalid Conversion from ‘const char*’ to ‘char*’

文章目录 引言 在 C 编程中,类型转换错误(Invalid Conversion)是常见的编译错误之一。特别是当程序试图将一个常量字符指针(const char*)转换为非常量字符指针(char*)时,会导致编译…

Vmware环境下ESXi主机 配置上行链路、虚拟交换机、端口组、VMkernel网卡

一、适用场景 1、使用专业服务器跑多种不同的业务,每种业务可能所需运行的server环境不同,有的需要Linux server CentOS7/8、kali、unbuntu……有的需要windows server2008、2003、2016、2019、2022…… 2、本例采用的是VMware ESXi6.7 update 3版本&am…

力扣习题--找不同

目录 前言 题目和解析 1、找不同 2、 思路和解析 总结 前言 本系列的所有习题均来自于力扣网站LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台 题目和解析 1、找不同 给定两个字符串 s 和 t ,它们只包含小写字母。 字符串 t…

Java Maven中自动代码检查插件详细介绍

文章目录 Checkstyle主要特点使用场景配置与使用checkstyle.xmlsuppressions.xml 验证打包时验证执行命令验证 Spotless配置文件内容Java配置部分POM 配置部分Markdown 配置部分Up to Date Checking执行部分 验证打包时验证在插件中执行命令验证 Checkstyle Spotless 结合chec…

redis学习(005 java客户端 RedisTemplate学习)

黑马程序员Redis入门到实战教程,深度透析redis底层原理redis分布式锁企业解决方案黑马点评实战项目 总时长 42:48:00 共175P 此文章包含第16p-第p23的内容 文章目录 java客户端jedisSpringDataRedis项目实现hash哈希操作 java客户端 jedis 测试 ps:如果连接不上&…

vs2019 无法打开项目文件

vs2019 无法打开项目文件,无法找到 .NET SDK。请检查确保已安装此项且 global.json 中指定的版本(如有)与所安装的版本相匹配 原因:缺少组件 解决方案:选择需要的组件进行安装完成

步进电机改伺服电机

步进电机: 42:轴径5mm 57:轴径8mm 86:轴径14mm 【86CME120闭环】// 12牛米 伺服电机: 40: 60: 80: 86: ECMA——C 1 0910 R S 4.25A 轴径…

评价ChatGPT与强人工智能的未来

在人工智能领域,ChatGPT的出现无疑是一个里程碑事件。它不仅展示了自然语言处理技术的巨大进步,也引发了人们对于强人工智能(AGI)的无限遐想。本文将从多个角度评价ChatGPT,并探讨强人工智能距离我们还有多远。 ChatGP…

无人机运营合格证及无人机驾驶员合格证(AOPA)技术详解

无人机运营合格证及无人机驾驶员合格证(AOPA)技术详解如下: 一、无人机运营合格证 无人机运营合格证是无人机运营企业或个人必须获得的证书,以确保无人机在运营过程中符合相关法规和标准。对于无人机运营合格证的具体要求和申请…

无人机人员搜救

人员搜救-水域救援 水域搜救:快速水面搜查 物资抛投:救生物资抛投 绳索牵引:牵引救援绳索 领航船艇:水面侦察领航 人员搜救 昼夜搜救,精准定位 水域搜救 经纬 M300 RTK 搭载禅思 H20T 能够满足全天候作业需求&a…

新手教学系列——Git Stash踩坑

在之前的文章《如何彻底避免Git代码相互覆盖问题》中,我曾介绍过通过规范分支合并和使用git stash来避免代码覆盖问题。今天,我要深入探讨一下git stash的使用,并分享一些使用过程中遇到的坑,希望能帮你避免类似问题。 脚本mg.sh简介 为了更好地管理代码合并,我编写了一…

linux之管道重定向

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

windows USB 设备驱动开发-控制传输的数据包

每次在主机控制器和 USB 设备之间移动数据时,都会发生传输。 通常,USB 传输可大致分为控制传输和数据传输。 所有 USB 设备都必须支持控制传输,并且可以支持用于数据传输的端点。 每种类型的传输都与设备缓冲区USB 端点 的类型相关联。 控制传…

泛微开发修炼之旅--32ecology对接海康威视综合安防管理系统,人脸识别机器数据同步代码方案及源码

文章链接:32ecology对接海康威视综合安防管理系统,人脸识别机器数据同步代码方案及源码