windows xp 驱动开发(五) USB驱动程序、应用软件概述

2023-11-03

转载请标明是引用于 http://blog.csdn.net/chenyujing1234

 

欢迎大家提出意见,一起讨论!

1、USB设备驱动程序(WDM模型)

1、1  分类

USB设备驱动程序的设计是基于微软件的WDM。

WDM采用分层驱动程序模型,对于USB设备来说,可作来两个部分:

USB总线驱动程序

    它由操作系统提供,它位于USB功能驱动程序的下面,负责与实现的硬件打交道,实现烦琐的低层通信。   

USB功能驱动程序

    由设备开发者编写,位于USB总线驱动程序的上面,不与实现的硬件打交道,而是通过USB总线驱动程序

    发送包含URB(USB Request Block, USB请求块)的IRP(I/O Request Packet, I/O请求包)来祥瑞对USB设备

   信息的发送或接收。

注意: USB功能驱动程序调用的API函数都不能与开发Window 应用程序的API(工作于CLR上面)一样,得用自己WDK SDK包里特有的内核API函数才可以。

         

 

在WDM模式中,完成一个设备的操作,至少有两个设备对象共同完成。

(1)物理设备对象(PDO)

(2)另一个是功能设备对象(FDO)

 其关系是“附加”与“被附加”关系。

1、2  对象栈数据结构

下图的左边是WMD模型的设备对象栈。(设备对象是系统为帮助软件管理而创建的数据结构,一个物理硬件可以有多个这样的数据结构)。

PDO(physical device object):  处于堆栈最底层的设备对象。

FDO(functional device object):功能设备对象。

FIDO(filter device object):  过滤器设备对象

 

当PC插入某个设备进,PDO会自动创建,确切说由总线驱动创建的。 

PDO不能单独操作设备,需要配合FDO一起使用。

系统会提示检测到新设备。这时要求安装驱动程序。

驱动程序指的就是WDM程序,此驱动程序负责创建FDO,并附加到PDO上。

当一个FDO附加在PDO上的时候,PDO设备对象的子域AttackedDevice会记录FDO的位置。

PDO被称作底层驱动或下层驱动。而FDO称为上层驱动。

 

1、3 描述符的概念

     USB设备硬件中的数据结构称为描述符,可以被主机软件识别。

    每个描述符开始于一个两字节的头,头中指出该描述符的字节长度(包括头)和描述符类型

   描述符的长度对于相同的描述符类型是固定的。

1、4 设备描述符

     每个设备都有一个唯一 的设备描述符,它向主机软件标识该设备。主机使用GET_DESCRIPTOR控制事务直接

     从设备的0号端点读取该描述符。

   

typedef struct _USB_DEVICE_DESCRIPTOR {
    UCHAR bLength;   // 设备描述符的bLength域等于18
    UCHAR bDescriptorType; // 域为1, 以指出该结构是一个设备描述符还是一个管道描述符
    USHORT bcdUSB;   // 包含描述符遵循的USB规范的版本号( 以BCD编码),
       // 现在,设备可以使用值0x0100 或 0x0110 来指出它是1.0版还是1.1版本.
    UCHAR bDeviceClass;  // 指出设备类型
    UCHAR bDeviceSubClass; // 指出设备的子类型
    UCHAR bDeviceProtocol; // 指出设备所使用的协议
    UCHAR bMaxPacketSize0; // 给出了默认控制端点(端点0)上的数据包容量的最大值
    USHORT idVendor;  // 厂商代码
    USHORT idProduct;  // 厂商专用的产品标识
    USHORT bcdDevice;  // 指出设备的发行版本号(0x0100对应版本1.0)
    UCHAR iManufacturer; // iManufacturer iProduct  iSerialNumber指向一个串描述符,
       // 该串用人可读语言描述设备生产厂商。
    UCHAR iProduct;
    UCHAR iSerialNumber;
    UCHAR bNumConfigurations;// 指出该设备能实现多少种配置。
} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;

 

我们已经知道在控制管道中,传输分为三个阶段.

第一阶段是令牌阶段,这里Host向设备发送"80 06 00 02 00 00 20 00" 8个字节。

第二阶段是数据传输阶段,方向是由设备传给主机,在我们的例子中给主机传递了18个字节。

这18字节就是对应于USB_DEVICE_DESCRIPTOR数据结构的.


第三阶段是握手阶段.

 

1、5  配置描述符

        每个设备有一个或多个配置描述符,它描述了设备能实行的各种配置方式。

   

typedef struct _USB_CONFIGURATION_DESCRIPTOR {
    UCHAR bLength;          // 应该是9
    UCHAR bDescriptorType;  // 应该是2,即是一个9字节的配置描述符
    USHORT wTotalLength;    // 为该配置描述符长度加上该配置内所有接口和
                         // 端点描述符长度的总和。
       // 通常,主机在发出一个GET_DESCRIPTOR请求并正确接收到9字节长的
       // 配置描述符后,就会再发出一个GET_DESCRIPTOR请求并指定这个总长度。
       // 第二个请求把这个大联合描述符传输回来
    UCHAR bNumInterfaces;   // 指出该配置有多少个接口
    UCHAR bConfigurationValue;// 该配置的索引值
    UCHAR iConfiguration;   // 是一个可选的串描述符索引,指向描述该配置的Unicode字符串
    UCHAR bmAttributes;
    UCHAR MaxPower;         // 指出要从USB总线上获取的最大电流量
} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; 

 

1、6 接口描述符
typedef struct _USB_INTERFACE_DESCRIPTOR {
    UCHAR bLength;			// 应该是9
    UCHAR bDescriptorType;	// 
    UCHAR bInterfaceNumber; // 是索引值
    UCHAR bAlternateSetting;// 是索引值。用在SET_INTERFACE控制事务中以指定要激活的接口
    UCHAR bNumEndpoints;    // 指出该接口有多少端点,不包括端点0.
    UCHAR bInterfaceClass;  // 为接口类
    UCHAR bInterfaceSubClass;// 为子接口类
    UCHAR bInterfaceProtocol;// 为协议
    UCHAR iInterface;        // 一个串描述符。
} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR;


 

1、7  端点描述符
typedef struct _USB_ENDPOINT_DESCRIPTOR {
    UCHAR bLength;
    UCHAR bDescriptorType;
    UCHAR bEndpointAddress;
    UCHAR bmAttributes;
    USHORT wMaxPacketSize;
    UCHAR bInterval;
} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR;


 

1、8  USB请求使用总线驱动程序
1、8、0  USB设备请求

USB的请求是通过管道传输的,请求是8个字节

 

其中bRequest代表不同的USB请求.定义在USB100.h中

 

 1、8、1 初始化请求

         为了创建一个URB,首先应该为URB分配内存,然后调用初始化例和把URB结构中的各个域填入内容。

        eg: 当你为响应IRP_START_DEVICE请求而配置设备时,首要的任务就是读取该设备的设备描述符.

            

urb = ExAllocatePool(NonPagedPool, 
                         sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));

    if(urb)
	{

        siz = sizeof(USB_DEVICE_DESCRIPTOR);
        deviceDescriptor = ExAllocatePool(NonPagedPool, siz);

        if(deviceDescriptor) 
		{

			// 构造请求
            UsbBuildGetDescriptorRequest(
                    urb, 
                    (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                    USB_DEVICE_DESCRIPTOR_TYPE, 
                    0, 
                    0, 
                    deviceDescriptor, 
                    NULL, 
                    siz, 
                    NULL);


#define UsbBuildGetDescriptorRequest(urb, \
                                     length, \
                                     descriptorType, \
                                     descriptorIndex, \
                                     languageId, \
                                     transferBuffer, \
                                     transferBufferMDL, \
                                     transferBufferLength, \
                                     link) { \
            (urb)->UrbHeader.Function =  URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE; \
            (urb)->UrbHeader.Length = (length); \
            (urb)->UrbControlDescriptorRequest.TransferBufferLength = (transferBufferLength); \
            (urb)->UrbControlDescriptorRequest.TransferBufferMDL = (transferBufferMDL); \
            (urb)->UrbControlDescriptorRequest.TransferBuffer = (transferBuffer); \
            (urb)->UrbControlDescriptorRequest.DescriptorType = (descriptorType); \
            (urb)->UrbControlDescriptorRequest.Index = (descriptorIndex); \
            (urb)->UrbControlDescriptorRequest.LanguageId = (languageId); \
            (urb)->UrbControlDescriptorRequest.UrbLink = (link); }

Urb: 用来输出的URB结构的指针

Length: 用来描述该UR结构的大小

DecscriptorType: 描述该URB的类型。它可以是USB_DEVICE_DESCRIPTOR_TYPE\

USB_CONFIGURATION_DESCRIPTOR_TYPE和USB_STRING_DESCRIPTOR_TYPE

Index : 用来描述设备描述符的索引

LanguageId: 用来描述语言的ID

TransferBuffer: 如果用缓冲区读取设备,TransferBuffer是缓冲区内存指针

TransferBufferMDL:  如果用直接读取内存时,TransferBufferMDL是直接读取内存指针时MDL的指针

transferBufferLength : 对于该URB所操作内存的大小

 

 

 

1、8、2  发送URB

创建完URB后,需要创建并发送一个内部I/O控制(IOCTL)请求到USBD驱动程序

USBD驱动程序位于驱动程序层次结构的低端。需要等待设备回应。

NTSTATUS SendAwaitUrb(PDEVICE_OBJECT fdo, PURB urb)
{
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
	KEVENT event;
	KeInitializeEvent(&event, NotificationEvent, FALSE):
	IO_STATUS_BLCOK iostatus;
	PIRRIrp = IoBuildDeviceIoControlRequest(IOCTRL_INTERNAL_USB_SUBMIT_URB,
		pdx->LowerDeviceObject, NULL, 0, TRUE, &event, &iostatus);
	PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp);
	stack->Parameters.Others.Argumentl = (PVOID)urb;
	NTSTATUS status = IoCallDriver(pdx->LowerDeviceObject, Irp);
	if(status == STATUS_PENDING)
	{
		KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
		status = iostatus.Status;
	}
	return status;
}

1、8、3  URB返回的状态

当提交一个URB到USB总线驱动程序时,我们将收到一个描述该操作结果的NTSTATUS代码。状态码的定义你可以在DDK的头文件NTSTATUS.H中找到)

当USBD完成一个URB时,它就把URB的UrbHeader.Status域设置为某个USBD_STATUS值,

DDK中的URB_STATUS宏可以简化这个值的提取:

 

NTSTATUS status = SendAwaitUrb(fdo, &urb);

USBD_STATUS ustatus = URB_STATUS(&urb);

 

1、9 关于USB设备的配置

1、9、1 

USB总线驱动程序自动检测新插入的USB设备。然后读取设备内的设备描述符以查明插入的是何种设备,

描述符中的厂商和产品标识及其它描述符一同决定具体安装哪一个驱动程序。

1、9、2

配置管理器调用驱动程序的AddDevice函数AddDevice做所有你已知的任务:

创建设备对象、

把设备对象连接到驱动程序堆栈上

。。。。。

1、9、3

配置管理器向驱动程序发送一个即插即用请求IRP_MN_START_DEVICE.

1、9、4

通过调用StartDevice函数并传递一些参数,这些参数描述了赋予设备的经过

转换的未经转换的I/O资源

StartDevice执行过程:

首先为设备选择一个配置。

接着选择配置中的一个或多个接口。

然后向总线驱动程序发送配置选择URB。

最后,总线驱动程序向设备发出命令使能选定的配置和接口。

补充:总线驱动程序负责驱动程序与选定接口端点之间的通信,

            它同时还创建配置句柄和接口句柄,我们可以从完成的URB中提取这些句柄并保存为以后使用。

           也可以通过URB成员UrbSelectConfiguration.ConfigurationHandle返回该配置句柄;

           USBD_INTERFACE_INFORMATION结构中的InterfaceHandle返回接口句柄;

           每个USBD_PIPE_INFORMATION结构中都含有与每个端点对应的管道句柄PipeHandle.

 

 


 

NTSTATUS StartDevice(PDEVICE_OBJECT fdo)
{
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
	<configure device)
    return STATUS_SUCCESS;
}


 

 2、应用软件设计

2、1  分类

      应用软件分两部分组成:

   链接库程序

       它负责与USB功能驱动程序通信并接受应用程序的各种操作请求

   应用程序

       负责对所采集的数据进行分析处理

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

windows xp 驱动开发(五) USB驱动程序、应用软件概述 的相关文章

  • 为 Windows 7 构建自定义凭据提供程序

    在查看了为 Windows 7 构建自定义凭据提供程序的代码后 我设法让自己的磁贴显示在登录屏幕上 并且可以作为所需用户登录 但是 我正在尝试实现一个系统 其中事件 范围内的蓝牙设备 触发登录 解锁 而无需单击磁贴 我可以设置它自动填写密码
  • cygwin + Windows套接字编程

    我正在尝试学习 Windows 中的 Socket 编程 并使用 cygwin 来实现同样的目的 我发现所需的文件位于 usr include w32api 我从网上获取了一个示例程序并尝试编译但无法这样做 相同的代码是 include
  • 在 DLL 中,函数表的结构如何?

    我一直在研究不明确支持我的操作系统的设备库的实现 特别是 我有一个反汇编的 DLL 以及大量的支持源代码 现在 功能表 导出表是如何构造的 我的理解是 第一个结构 data部分是 RVA 表 接下来是通过索引链接到第一个地址表的字符串表 这
  • 从 Visual Studio 运行时,STARTUPINFO.wShowWindow 为 0

    我在调试名为 Notepad2 的开源记事本替代品中的功能时偶然发现了一个问题 更具体地说 是一个名为 Notepad2 的更新版本 Notepad2 mod https github com XhmikosR notepad2 mod 它
  • (Windows Phone 10) 是否可以在 Windows Phone 10 中以编程方式编辑、添加新联系人?

    我想在 Windows Phone 10 中以编程方式实现功能编辑和添加联系人 是否可以 有相关样本吗 以下是用于创建联系人的代码片段 public async Task AddContact String FirstName String
  • DirectX 11 ClearRenderTargetView 恢复透明缓冲区?

    我正在尝试创建一个使用 directx 进行绘制的窗口opaque上面的内容透明的视图 即桌面显示出来 使用 DirectX11 我尝试执行以下操作 但它并没有使背景透明 事实上 我输入的任何不透明度值都会给出完全相同的结果 我在做什么 f
  • IOCP AcceptEx 在连接时未创建完成

    我目前正在尝试一些用于套接字编程的新库 IOCP 我偶然发现了AcceptEx http msdn microsoft com en us library windows desktop ms737524 28v vs 85 29 aspx
  • Powershell添加系统变量

    我正在尝试使用 PowerShell 在此处添加系统变量 我已经尝试过两种方式使用 env MyTestVariable My test variable and Environment SetEnvironmentVariable Tes
  • 如何在 C++ 中更改活动桌面壁纸

    你好 我想写一个小程序来更改 Windows 7 中的壁纸 我想使用以下代码 include windows h include wininet h include shlobj h include wchar h include
  • .NET 中的 CoCreateInstance 完全匹配吗?

    我有进程内 DLL COM 服务器 但我选择以 DllSurrogate 身份运行 因此从非托管代码 Delphi 中我有 function TComWrapper GetServer IUnknown begin OleCheck CoC
  • 哪里可以下载 Microsoft.Phone.WinJS.2.1?

    我正在尝试使用 WinJS 制作通用 Windows 应用程序 我已经下载了VS2013的最新更新 RC 版本 我已经安装了我能找到的所有 SDK 我还安装了 WinLibJS VSE exe 扩展 有趣的是 VS 更新一直说我没有 当我打
  • 自定义 Windows 7 登录 [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我如何为 Windows 7 登录屏幕进行编程 不仅仅是替换背景图像 就像我希望它通过 RFID 阅读器进行身份验证一样 我能做到吗 我知道 XP 可
  • 如何在 Windows 中拦截 DNS 查询

    我正在研究如何在 Windows 中拦截 DNS 查询 以一种不需要将 DLL 注入到每个进程中的方式 并且理想情况下能够根据发出查询的进程做出决策 因此简单的 DNS 代理服务器是不够的 从表面上看 DNS 查询所采用的路径如下所示 某些
  • Windows 相当于 Mac OS X“打开”命令

    刘畅在这里问了一个与此非常相似的问题 Linux 相当于 Mac OS X 打开 命令 https stackoverflow com questions 264395 linux equivalent of the mac os x op
  • Windows 7 跳转列表(Windows 窗体、C#)

    有谁知道如何在 C 中自定义与我自己的应用程序相关的 Windows 跳转列表功能 我知道可以做到 但我在 MSDN 上找不到任何与 C Windows7 相关的内容 到目前为止 我能找到的与 W7 和 C 相关的唯一信息只是我已经阅读过的
  • 无法安装 Visual Studio C++ 包含文件“excpt.h”

    我正在尝试编译一个 Visual Studio C 项目 但由于编译器报告 无法打开包含文件 excpt h 没有这样的文件或目录 我无法到达任何地方 该问题已在互联网上多次报告 但我找不到有关我的具体情况的任何帮助 问题不在于项目的包含路
  • “sed” 在文件中插入反斜杠

    Tool Windows 版 Git Bash Problem 尝试在目录中每个文件的顶部插入文本 connectcentral Code for f in DIR sql do sed i 1i connect central f don
  • 开始作业时无法识别功能

    我在模块 sysinfo psm1 中创建了一个函数 Get Uptime 并导入了该模块 C pstools gt get command Module sysinfo CommandType Name Definition Functi
  • “PWC6345:调用 javac 时出错。”使用 Jetty WTP 插件在 Jetty 上部署 JSP 页面时出错

    我正在尝试在 Jetty 上部署 JSP 页面 使用Jetty WTP 插件 http wiki eclipse org Jetty WTP Plugin对于 Eclipse 但我收到以下错误 Jetty 好像找不到javac 我需要在 E
  • 如何远程获取系统的网络共享和连接?

    我正在寻找一种远程获取类似于以下控制台应用程序的信息的方法 net use 净份额 网络统计 ano 但是 我需要能够在系统上运行第三方应用程序的情况下执行此操作 这有效地排除了使用 psexec 远程执行命令 因为 psexec 将作为服

随机推荐

  • Out-Of-Vocabulary(OOV)的理解

    OOV 问题是NLP中常见的一个问题 其全称是Out Of Vocabulary 下面简要的说了一下OOV 怎么解决 下面说一下Bert中是怎么解决OOV问题 如果一个单词不在词表中 则按照subword的方式逐个拆分token 如果连逐个
  • 汉字简/繁体转换

  • C语言:删除字符

    本题要求实现一个删除字符串中的指定字符的简单函数 函数接口定义 void delchar char str char c 其中char str是传入的字符串 c是待删除的字符 函数delchar的功能是将字符串str中出现的所有c字符删除
  • 【kernel envirment】config tiny X86 kernel with vfs

    Automatically generated file DO NOT EDIT Linux x86 4 19 0 Kernel Configuration Compiler gcc Ubuntu 7 3 0 27ubuntu1 18 04
  • cocos creator 血条跟随3d convertToUINode导致的问题,使用worldToScreen解决跟随偏离问题

    cocos creator3 3 2 实现血条跟随 一开始使用的camera的converToUINode 也是按照麒麟子大师的博客操作 结果最终的效果 在屏幕中间 血条显示正常 在屏幕边缘就开始出现偏差 x和y都有的偏差 最终也没有解决方
  • 择后自动上传html代码,GitLab + Jenkins + Webhook 实现Push代码后自动更新

    一 介绍 通常是开发后的代码先推到Gitlab上管理 然后在Jenkins里通过脚本构建代码发布 这种方式每次在发版的时候 需要人工去执行jenkins上的构建动作 有时显得过于繁琐 Gitlab的Webhook功能 通过Webhook的相
  • 九月份参加OPPO和腾讯Android面试:技术一面+二面+三面+HR四面,我的面经总结!

    之前很多时候我是拒绝说我的面试经验的 因为我们简历经历不一样问的问题也会不一样 且大厂面试光靠背几个面试题就想过还是比较难的 因此在这里提醒一下大家不要临时抱佛脚 你花几天能背下的东西 别人花几天一定能超过你的 但我们花几年沉淀的东西 人家
  • 公司企业微信小程序创建步骤

    随着新一代互联网的发展 小程序已经成为当今社会不可或缺的重要部分 它的简单易用 公司企业小程序是一种基于微信平台构建的应用程序 旨在为企业提供灵活便捷的营销服务 关于公司企业微信小程序创建步骤 可分为以下几个部分 一 申请微信公众号并创建小
  • 2022年,软件测试还能学吗?别学了,软件测试岗位饱和了...

    8年前 我懵懂的选择了软件测试这个行业 穷困潦倒的时候 爸妈给我付了2万块钱进入了一家培训机构 我怀着感激和破釜沉舟的情绪开始学习软件测试 3个月的学习时间 住群租宿舍 吃盒饭 平时上课认真听讲 周末就跑自习室 在学了基础课程之后 找工作的
  • vue中纯JS调用自定义组件

    案例以vant为例 1 首先创建index vue index js文件 2 index vue跟我们平常写的组件是一样的
  • 51单片机学习笔记(十二) - 红外遥控

    文章目录 前言 一 红外遥控的背景知识 1 人机交互 2 红外遥控的相关知识 二 原理图电路分析 三 NEC协议讲解 1 逻辑1与逻辑0的表示 2 NEC协议格式 3 NEC协议的关键点 四 代码实现 总结 前言 随着科技的发展 红外遥控器
  • 【Spring Boot整合MyBatis教程】

    Spring Boot是由Pivotal团队提供的全新框架 其设计目的是用来简化新Spring应用的初始搭建以及开发过程 该框架使用了特定的方式来进行配置 从而使开发人员不再需要定义样板化的配置 通过这种方式 Spring Boot致力于在
  • 用4种语言编写端口扫描器(Java、C、Python、Go)

    Java import java net InetSocketAddress import java net Socket import java util concurrent ExecutorService import java ut
  • Ghost-Docker(五)Nginx+SSL+Https

    使用 Ngins SSL 证书 为 Ghost 实现 Https 访问 HTTPS 协议是由 HTTP 加上 TLS SSL 协议构建的可进行加密传输 身份认证的网络协议 主要通过数字证书 加密算法 非对称密钥等技术完成互联网数据传输加密
  • ElementUI过渡动画篇

    ElementUI过渡动画篇 element官方提供的过渡动画并不能很好的满足使用 我尝试过几种过渡动画的设置方式 最终选择了Animate css 一 使用方法 引入 引入 npm install animate css save 然后在
  • 保姆级连载讲义学Python:第四篇多文件项目的演练

    多文件项目的演练 开发 项目 就是开发一个 专门解决一个复杂业务功能的软件 通常每 一个项目 就具有一个 独立专属的目录 用于保存 所有和项目相关的文件 一个项目通常会包含 很多源文件 目标 在项目中添加多个文件 并且设置文件的执行 多文件
  • FastAPI从入门到实战(10)——响应模型与状态码

    前面一直记录的是请求相关的内容 这篇文章开始记录一下响应相关的内容 包括请求模型和模型继承以及状态码等相关的内容 一个任意dict构成的基本响应 任意dict构成的响应 app06 get stu06 dict response model
  • STL容器使用中的拷贝成本

    include stdafx h include
  • ESP32基础应用之HTTP 服务器

    文章目录 1 HTTP服务器简介 2 ApiPost测试工具 3 HTTP服务器实验 3 1 ApiPost之GET测试 3 2 ApiPost之POST测试 3 3 ApiPost值PUT测试 参考资料 esp32 http服务器编程指南
  • windows xp 驱动开发(五) USB驱动程序、应用软件概述

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家提出意见 一起讨论 1 USB设备驱动程序 WDM模型 1 1 分类 USB设备驱动程序的设计是基于微软件的WDM WDM采用分层驱动程序模型