windows USB 设备驱动开发-USB复合设备的注册

USB 多功能设备的驱动程序(称为复合驱动程序)可以向基础 USB 驱动程序堆栈注册和注销复合设备。 Microsoft 提供的驱动程序(Usbccgp.sys)是由 Windows 加载的默认复合驱动程序。 本文中的过程适用于替换Usbccgp.sys的基于 WDM的自定义 Windows 驱动程序模型 。

通用串行总线 (USB) 设备可以提供多个同时处于活动状态的功能。 此类多功能设备也称为 复合设备。 例如,复合设备可能为键盘功能定义一个函数,为鼠标定义另一个函数。 复合驱动程序枚举设备的功能。 复合驱动程序可以在整体模型中管理这些函数本身,或为每个函数创建物理设备对象 (PDO) 。 这些单独的 PDO 由各自的 USB 功能驱动程序、键盘驱动程序和鼠标驱动程序管理。

USB 3.0 规范定义了 函数暂停和远程唤醒功能 ,使各个函数能够进入和退出低功耗状态,而不会影响其他功能或整个设备的电源状态。 

若要使用该功能,复合驱动程序需要将设备注册到基础 USB 驱动程序堆栈。 由于该功能适用于 USB 3.0 设备,因此复合驱动程序必须确保基础堆栈支持版本USBD_INTERFACE_VERSION_602。 通过注册请求,复合驱动程序:

  • 通知基础 USB 驱动程序堆栈,驱动程序负责发送请求,以支持功能驱动进行远程唤醒。 远程唤醒请求由 USB 驱动程序堆栈处理,该堆栈将必要的协议请求发送到设备;
  • 获取 (USB 驱动程序堆栈分配的每个函数) 一个函数句柄的列表。 然后,复合驱动程序可以在驱动程序的请求中使用函数句柄,以便远程唤醒与句柄关联的函数;

通常,复合驱动程序在驱动程序的 AddDevice 或启动设备例程中发送注册请求来处理 IRP_MN_START_DEVICE。 因此,复合驱动程序会释放在驱动程序的卸载例程,例如 stop-device (IRP_MN_STOP_DEVICE ) 或 remove-device 例程 (IRP_MN_REMOVE_DEVICE) 中为释放在注册时分配的资源。

先决条件

发送注册请求之前,请确保:

  • 你拥有设备中的函数数。 该数字可以派生 get-configuration 请求检索到的描述符;
  • 在对 USBD_CreateHandle 的上一次调用中,你已获得 USBD 句柄;
  • 基础 USB 驱动程序堆栈支持 USB 3.0 设备。 为此,请调用 USBD_IsInterfaceVersionSupported 并将 USBD_INTERFACE_VERSION_602 作为版本传递给 检查;
注册复合设备

以下过程介绍如何生成和发送注册请求,以将复合驱动程序与 USB 驱动程序堆栈相关联。

1.分配 COMPOSITE_DEVICE_CAPABILITIES 结构并通过调用 COMPOSITE_DEVICE_CAPABILITIES_INIT 宏对其进行初始化;

2.将 COMPOSITE_DEVICE_CAPABILITIES 的 CapabilityFunctionSuspend 成员设置为 1;

3.分配 REGISTER_COMPOSITE_DEVICE 结构,并通过调用 USBD_BuildRegisterCompositeDevice 例程初始化 结构 。 在调用中,指定 USBD 句柄、初始化 COMPOSITE_DEVICE_CAPABILITIES 结构和函数数;

4.通过调用 IoAllocateIrp (IRP) 分配 I/O 请求数据包,并通过调用 IoGetNextIrpStackLocation 获取指向 IRP 的第一个堆栈位置的指针 (IO_STACK_LOCATION) ;

5.为足以容纳函数句柄数组的缓冲区分配内存 (USBD_FUNCTION_HANDLE) 。 数组中的元素数必须是 PDO 的数量;

6.通过设置 IO_STACK_LOCATION的以下成员来生成请求:

  • 通过将 Parameters.DeviceIoControl.IoControlCode 设置为 IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE,指定请求的类型。
  • 通过将 Parameters.Others.Argument1 设置为初始化的 REGISTER_COMPOSITE_DEVICE 结构的地址来指定输入参数。
  • 通过将 AssociatedIrp.SystemBuffer 设置为在步骤 5 中分配的缓冲区来指定输出参数。

7.调用 IoCallDriver ,通过将 IRP 传递到下一个堆栈位置来发送请求;

完成后,检查 USB 驱动程序堆栈返回的函数句柄数组。 可以将数组存储在驱动程序的设备上下文中,以供将来使用;

下面的代码示例演示如何生成和发送注册请求。 该示例假定复合驱动程序将以前获取的函数数和 USBD 句柄存储在驱动程序的设备上下文中。

VOID  RegisterCompositeDriver(PPARENT_FDO_EXT parentFdoExt)  
{  PIRP                            irp;  REGISTER_COMPOSITE_DRIVER       registerInfo;  COMPOSITE_DRIVER_CAPABILITIES   capabilities;  NTSTATUS                        status;  PVOID                           buffer;  ULONG                           bufSize;  PIO_STACK_LOCATION              nextSp;  buffer = NULL;  COMPOSITE_DRIVER_CAPABILITIES_INIT(&capabilities);  capabilities.CapabilityFunctionSuspend = 1;  USBD_BuildRegisterCompositeDriver(parentFdoExt->usbdHandle,  capabilities,  parentFdoExt->numFunctions,  &registerInfo);  irp = IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE);  if (irp == NULL) {  //IoAllocateIrp failed.status = STATUS_INSUFFICIENT_RESOURCES;  goto ExitRegisterCompositeDriver;    }  nextSp = IoGetNextIrpStackLocation(irp);  bufSize = parentFdoExt->numFunctions * sizeof(USBD_FUNCTION_HANDLE);  buffer = ExAllocatePoolWithTag (NonPagedPool, bufSize, POOL_TAG);  if (buffer == NULL) {  // Memory alloc for function-handles failed.status = STATUS_INSUFFICIENT_RESOURCES;  goto ExitRegisterCompositeDriver;    }  nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;     nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DRIVER;  //Set the input buffer in Argument1      nextSp->Parameters.Others.Argument1 = &registerInfo;  //Set the output buffer in SystemBuffer field for USBD_FUNCTION_HANDLE.      irp->AssociatedIrp.SystemBuffer = buffer;  // Pass the IRP down to the next device object in the stack. Not shown.status = CallNextDriverSync(parentFdoExt, irp, FALSE);  if (!NT_SUCCESS(status)){  //Failed to register the composite driver.goto ExitRegisterCompositeDriver;    }  parentFdoExt->compositeDriverRegistered = TRUE;  parentFdoExt->functionHandleArray = (PUSBD_FUNCTION_HANDLE) buffer;  End:  if (!NT_SUCCESS(status)) {  if (buffer != NULL) {  ExFreePoolWithTag (buffer, POOL_TAG);  buffer = NULL;  }  }  if (irp != NULL) {  IoFreeIrp(irp);  irp = NULL;  }  return;  
}
取消注册复合设备
  1. 通过调用 IoAllocateIrp 分配 IRP,并通过调用 IoGetNextIrpStackLocation 获取指向 IRP 的第一个堆栈位置 (IO_STACK_LOCATION) 的指针;
  2. 通过将 IO_STACK_LOCATION 的 Parameters.DeviceIoControl.IoControlCode 成员设置为IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE来生成请求;
  3. 调用 IoCallDriver ,通过将 IRP 传递到下一个堆栈位置来发送请求;

复合驱动程序在 remove-device 例程的上下文中发送一次 IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE 请求。 请求的目的是删除 USB 驱动程序堆栈与复合驱动程序及其枚举函数之间的关联。 该请求还会清理为维护该关联而创建的所有资源,以及上一个注册请求中返回的所有函数句柄。

下面的代码示例演示如何生成并发送取消注册复合设备的请求。 该示例假定复合驱动程序以前是通过注册请求注册的,如本主题前面所述。

VOID  UnregisterCompositeDriver(  PPARENT_FDO_EXT parentFdoExt )  
{  PIRP                irp;  PIO_STACK_LOCATION  nextSp;  NTSTATUS            status;  PAGED_CODE();  irp = IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE);  if (irp == NULL) {  //IoAllocateIrp failed.status = STATUS_INSUFFICIENT_RESOURCES;  return;  }  nextSp = IoGetNextIrpStackLocation(irp);  nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;     nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DRIVER;  // Pass the IRP down to the next device object in the stack. Not shown.status = CallNextDriverSync(parentFdoExt, irp, FALSE);  if (NT_SUCCESS(status)) {  parentFdoExt->compositeDriverRegistered = FALSE;      }  IoFreeIrp(irp);  return;  
}

在后续会讲述复合设备的电源管理问题。 

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

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

相关文章

c语言的简易教法—— 函数递归

文章目录 一、什么是递归?1.1递归的思想1.2递归的限制条件 二、递归案例2.1 案例1:求n的阶层2.1.1分析2.1.2 递归函数(Fact)的代码实现2.1.3 测试:main函数实现2.1.4 运行结果和画图推演2.1.5 扩展:迭代方法…

【66个开源+44个闭源Agent项目】

开源AI Agent 1.AgentGPT 基于浏览器的 AutoGPT 实现,可通过无代码平台访问。https://agentgpt.reworkd.ai/zh 2.AI Legion 一个让智能体协同工作的平台,其类似于 AutoGPT 和 Baby AGI,但用 TypeScript 编写。https://github.com/eumemi…

如何使用BERT进行下游任务 - Transformer教程

BERT,即Bidirectional Encoder Representations from Transformers,是谷歌于2018年发布的预训练语言模型。BERT的出现标志着自然语言处理领域的一个重要里程碑,因为它大幅提高了多种语言任务的性能。本文将详细介绍如何使用BERT进行下游任务&…

华为如何做成数字化转型?

目录 企业数字化转型是什么? 华为如何定义数字化转型? 为什么做数字化转型? 怎么做数字化转型? 华为IPD的最佳实践之“金蝶云” 企业数字化转型是什么? 先看一下案例,华为经历了多次战略转型&#xf…

前端工程化:Webpack配置全攻略

前端工程化:Webpack配置全攻略 前端小伙伴们,今天我们来聊聊那个让人又爱又恨的 Webpack。没错,就是那个配置起来让你想砸键盘,但又离不开它的构建工具。别担心,跟着我来,保证让你从 Webpack 小白变成配置…

人脸识别与检测(保姆级教程--附带源码)

人脸识别与检测(保姆级教程–附带源码) 项目背景 因项目需要招聘了一些日结工人,因此需要对工地现场的工人进行考勤管理,但工地只有海康摄像头没有专业考勤设备,因此需要基于视频流开发人脸识别与检测功能&#xff1…

Windows 虚拟机服务器项目部署

目录 一、部署JDK下载JDK安装JDK1.双击 jdk.exe 安装程序2.点击【下一步】3.默认安装位置,点击【下一步】4.等待提取安装程序5.默认安装位置,点击【下一步】6.等待安装7.安装成功,点击【关闭】 二、部署TomcatTomcat主要特点包括:…

奇怪的错误记录

https://github.com/meta-llama/llama3/issues/80 读模型没问题,推理时出现: RuntimeError: “triu_tril_cuda_template” not implemented for ‘BFloat16’ ———————————————— 事发原因 我尝试了解transformers的AutoProcessor时&a…

感应触摸芯片集成为MCU,深度应用触控按键技术的VR眼镜

VR(Virtual Reality)即虚拟现实,简称VR,其具体内涵是综合利用计算机图形系统和各种现实及控制等接口设备,在计算机上生成的、可交互的三维环境中提供沉浸感觉的技术。它的工作原理是将左右眼图像交互显示在屏幕上的方式…

技术速递|宣布为 .NET 升级助手提供第三方 API 和包映射支持

作者:Marco Goertz 排版:Alan Wang .NET 升级助手是一个 Visual Studio 扩展和命令行工具,可帮助您将应用从之前的 .NET 和 .NET Framework 升级到最新版本的 .NET。正如我们在之前的文章中所描述的那样,它为升级 Microsoft 库和框…

技术总结(1)——方向与成长思考

不知不觉已经发了30篇技术博客,本来最开始想的是回顾自己的技术生涯,怎样做到失败的生涯,但是后面发现,开始逐步写技术博客,慢慢的开始沉浸里面这种回顾技术的感觉。做技术的人通常不喜欢研究市场,而做市场…

模型剪枝知识点整理

模型剪枝知识点整理 剪枝是深度学习模型优化的两种常见技术,用于减少模型复杂度和提升推理速度,适用于资源受限的环境。 剪枝(Pruning) 剪枝是一种通过移除模型中不重要或冗余的参数来减少模型大小和计算量的方法。剪枝通常分为…

编程是学什么:探索编程世界的四大核心领域

编程是学什么:探索编程世界的四大核心领域 在数字化时代的浪潮中,编程已成为一项重要的技能。但很多人对于编程的学习内容仍然感到困惑,那么,编程究竟是学什么呢?本文将从四个方面、五个方面、六个方面和七个方面&…

探索TASKCTL和 DataStage 的ETL任务调度协同

在复杂多变的企业环境中,高效、准确的数据处理是支撑业务决策与运营的核心。本文将深入探讨任务调度平台TASKCTL与ETL工具DataStage的深度融合,通过详尽的代码示例、结合细节以及实际案例的具体描述,展示这两个工具如何携手打造企业数据处理生…

Xcode构建设置自定义:打造个性化的编译环境

标题:Xcode构建设置自定义:打造个性化的编译环境 在软件开发过程中,根据不同的开发阶段和需求,经常需要调整编译设置以优化构建过程。Xcode作为苹果官方的集成开发环境(IDE),提供了丰富的自定义…

简述 Java 内存模型(JMM),特别是堆与栈的区别?

Java内存模型(JMM)是Java平台定义的一种多线程之间的通信规范,它确保了在不同的线程之间能够正确地共享和协调对内存的访问。 JMM的关键目标是解决并发编程中的可见性、原子性和有序性问题。 简单来说,它规定了如何在硬件内存、…

【C语言】 —— 预处理详解(下)

【C语言】 —— 预处理详解(下) 前言七、# 和 \##7.1 # 运算符7.2 ## 运算符 八、命名约定九、# u n d e f undef undef十、命令行定义十一、条件编译11.1、单分支的条件编译11.2、多分支的条件编译11.3、判断是否被定义11.4、嵌套指令 十二、头文件的包…

浅层神经网络示例

输出层采用sigmoid激活,隐藏层采用tanh激活 import h5py import numpy as npfrom project_02.code.planar_utils import load_planar_dataset, plot_decision_boundarydef sigmoid(z):s 1 / (1 np.exp(-z))return sdef init_parameters(n_x, n_h, n_y):"&qu…

如何在 Objective-C 中实现多态性,并且它与其他面向对象编程语言的多态性实现有何差异?

在Objective-C中,多态性可以通过使用父类的指针来调用子类的方法来实现。具体来说,可以定义一个父类的指针,然后将子类的实例赋值给这个指针。这样,即使使用父类的指针来调用方法,实际上会调用子类的方法。 需要注意的…

Day1每日编程题日记:数字统计、两个数组的交集、点击消除

前言:该篇用于记录自看。曾回看昨天的做题代码,竟然会觉得陌生,这竟然是我写的,细细读了一下,原来我当时是这么想的。因此我觉得记代码没有实际用处,重点是领悟了思想,这样子代码就在心中&#…