开发usb驱动程序的方法(连载一)
- 格式:doc
- 大小:43.50 KB
- 文档页数:6
USB驱动程序(较详细)一内核使用2.6.29.4拓扑结构上, 一个 USB 子系统并不是以总线的方式来分布; 它是一棵由几个点对点连接构成的树。
这些连接是连接设备和集线器的4线电缆(地, 电源, 和 2 个差分信号线), 如同以太网的双绞线。
USB主控制器负责询问每个USB设备是否有数据需要发送。
由于这个拓扑结构,一个 USB 设备在没有主控制器要求的情况下不能发送数据. 也就是说:USB是单主方式的实现,主机轮询各外设。
但是设备也可以要求一个固定的数据传输带宽,以保证可靠的音视频I/O。
USB只作为数据传输通道,对他所收发的数据格式没有特殊的内容和结构上的要求,也就是类似于透传。
Linux内核支持两种主要类型的USB驱动程序:Host系统上的驱动程序(USB device driver)和device上的驱动程序(USB gadget driver)(设备端驱动)。
USB 驱动程序存在于不同的内核子系统和USB硬件控制器之中。
USB核心为USB驱动程序提供了一个用于访问和控制USB硬件的接口,它隐藏了USB控制器的硬件细节。
从这里我们要知道:《LDD3》所谓的USB驱动是针对USB核心提供的接口而写的,并不是真正去操纵USB硬件控制器中的寄存器。
这样你必须保证你的板子上CPU的USB硬件控制器的驱动是可用的。
否则您就得先搞定CPU的USB硬件控制器的驱动才行。
以下是Linux内核中USB驱动的软件构架:如左下图所示,从主机侧的观念去看,在Linux驱动中,USB驱动处于最底层的是USB主机控制器硬件,在其之上运行的是USB主机控制器驱动,主机控制器之上为USB核心层,再上层为USB设备驱动层(插入主机上的U盘、鼠标、USB转串口等设备驱动)。
因此,在主机侧的层次结构中,要实现的USB驱动包括两类:USB主机控制器驱动和USB设备驱动,前者控制插入其中的USB设备,后者控制USB 设备如何与主机通信。
很多写Windows Device Driver的开发人员基本上都是使用Windows DDK进行开发的。
但是,现在也有不少人都开始借助一些辅助工具。
笔者去年开始接触到DriverStudio,发现它真的是一个不错的开发工具,不仅写代码的时候思路清晰,而且和DDK的结合很好。
当然,也有很多人觉得用DriverStudio不够正宗,或者说不能很好的理解Windows Device Driver的架构。
我感觉这就有点像MFC和SDK的关系,关于这个问题在很多地方都有争论,比如在万千新闻组上,就讨论了将近2个月。
每个人都有自己的最爱,都有自己的习惯,只要你能把事情做好,我想用什么方法应该都是一样的。
如果你已经习惯了用DDK开发,那完全还可以继续用下去;如果你觉得DriverStudio不错,那尝试用一个可以给你按照OOP 概念来编程的工具有什么不好呢?在驱动开发网上,经常看到有人询问一些关于DriverStudio的使用的问题。
我正好很有幸用它作了几个驱动程序,包括VXD, KMD和WDM,稍微有点心得,因此想写下来给大家作一个小小的参考。
如果其中有错误,欢迎大家给我指出,谢谢。
下面我就介绍一下用DriverStudio开发一个USB驱动程序的过程。
这个USB设备有3个双向端点,每个端点的配置如下:EP 类型地址buffer(Bytes)0 IN/OUT Control 0x80/0x00 16/161 IN/OUT Bulk 0x81/0x01 16/162 IN/OUT Bulk 0x82/0x02 64/64我们的驱动程序需要实现的功能就是控制设备上的LED灯的亮和灭,以及通过Endpoint 2对设备进行读写。
由于DriveStudio由几个部分组成,我们写这个驱动程序只要用到DriverWorks,因此下面我们就简称它为DW。
在这里,我们假定读者已经正确的安装了DW,并且已经编译好了各个库文件。
USB驱动程序编写linux下usb驱动编写(内核2.4)——2.6与此接口有区别2006-09-15 14:57我们知道了在Linux下如何去使用一些最常见的USB设备。
但对于做系统设计的程序员来说,这是远远不够的,我们还需要具有驱动程序的阅读、修改和开发能力。
在此下篇中,就是要通过简单的USB驱动的例子,随您一起进入USB驱动开发的世界。
USB骨架程序(usb-skel eton),是USB驱动程序的基础,通过对它源码的学习和理解,可以使我们迅速地了解USB驱动架构,迅速地开发我们自己的USB硬件的驱动。
USB驱动开发在掌握了USB设备的配置后,对于程序员,我们就可以尝试进行一些简单的USB驱动的修改和开发了。
这一段落,我们会讲解一个最基础U SB框架的基础上,做两个小的US B驱动的例子。
USB骨架在Linux kernel源码目录中driver/usb/u sb-skeleton.c为我们提供了一个最基础的USB驱动程序。
我们称为USB骨架。
通过它我们仅需要修改极少的部分,就可以完成一个USB设备的驱动。
我们的US B驱动开发也是从她开始的。
那些linux下不支持的USB设备几乎都是生产厂商特定的产品。
如果生产厂商在他们的产品中使用自己定义的协议,他们就需要为此设备创建特定的驱动程序。
当然我们知道,有些生产厂商公开他们的USB协议,并帮助Linux驱动程序的开发,然而有些生产厂商却根本不公开他们的USB协议。
因为每一个不同的协议都会产生一个新的驱动程序,所以就有了这个通用的USB驱动骨架程序,它是以pci 骨架为模板的。
如果你准备写一个lin ux驱动程序,首先要熟悉USB协议规范。
USB主页上有它的帮助。
一些比较典型的驱动可以在上面发现,同时还介绍了USB urbs的概念,而这个是usb驱动程序中最基本的。
USB 接口驱动程序开发1 引言随着微机技术水平的日益提高,传统的计算机接口已经不能满足当前计算机高速发展的需求,计算机业迫切需要一种新的通用型、高速总线接口,通用外设接口标准USB 就应运而生。
USB,全称是Universal Serial Bus(通用串行总线),是一种新型的、基于令牌的、高速的串行总线标准,由Compaq、Microsoft、Intel、IBM 等七家公司共同开发的, 旨在解决日益增加的PC 外设与有限的主板插槽和端口之间的矛盾而制定的一种串行通信标准[3],自1995年在Comdex 上亮相以来已广泛地为各PC 厂家支持。
现在市场上几乎所有的P C 机器都配备了US B 接口,USB 接口之所以能够得到广泛支持和快速普及,是因为它具备以下优点:正由于上述优点, 开发USB 接口的设备已成为一种发展趋势。
然而随着USB 技术的迅猛发展, 传统的USB1 . 1 接口已经不能适应用户的需求, 于是在1 9 9 9年在I nt e l 的开发者论坛大会上又提出了USB2 . 0 技术, 使得US B 不仅支持1 . 5Mb / s 的“低速”, 传输和12Mb/s 的“全速”传输,而且支持480Mb/s 的“高速”传输,比USB1.1 标准快40 倍左右,速度的提高对于用户的最大好处就是意味着用户可以使用到更高效的外部设备, 而且具有多种速度的周边设备都可以被连接到USB 2.0 的线路上,而且无需担心数据传输时发生瓶颈效应。
2 USB 驱动程序设计一个完整的USB 系统包括主机系统包括主机系统和USB 设备。
所有的传输事务都是由主机发起的。
一个主机系统又可以分为以下几个层次结构, 。
USB 总线接口包括USB 主控制器和根集线器,其中USB 主控制器负责处理主机与设备之间电气和协议层的互连,根集线器提供USB 设备连接点。
USB设备的驱动程序实现
USB驱动是用来控制使用USB接口的设备的软件程序,其实现是将实
际的硬件设备抽象为虚拟的设备,使其能够在计算机操作系统上应用。
一
般来讲,当你将USB设备插入你的计算机时,它将通过计算机的USB主控
芯片找到USB设备,然后测试它的功能,并决定它是否能够被用来通信,
最后安装相应的驱动程序。
实际的USB驱动程序的实现有若干方法,其中
有两种常用的技术:应用程序编程接口(API)和驱动程序模板。
1、应用程序编程接口(API)
API是一组用于访问操作系统提供的服务和功能的特殊指令序列。
应
用程序编程接口(API)可以用来创建USB驱动程序,其实现包括以下步骤:
(1)定义硬件设备的描述
在编写USB驱动程序时,首先需要定义硬件设备,即定义设备的功能,记录其编号、最大支持通信速率、硬件连接方式、发送和接收设备数据的
方式以及支持的驱动软件要求等信息。
(2)实现设备驱动的关键函数
关键函数是控制USB设备正常工作所必需的函数,包括初始化函数、
发送和接收数据的函数、获取设备状态的函数以及关闭设备的函数等。
LinuxUSB驱动开发实例(一)——USB摄像头驱动实现源码分析Spac5xx的实现是按照标准的USB VIDEO设备的驱动框架编写(其具体的驱动框架可参照/usr/src/linux/drivers/usb/usbvideo.c文件),整个源程序由四个主体部分组成:总结送免费学习资料(包含视频、技术学习路线图谱、文档等)设备模块的初始化模块和卸载模块,上层软件接口模块,数据传输模块。
具体的模块分析如下:一、初始化设备模块该驱动采用了显式的模块初始化和消除函数,即调用module_init 来初始化一个模块,并在卸载时调用moduel-exit函数其具体实现如下:1、模块初始化:总结送免费学习资料(包含视频、技术学习路线图谱、文档等)2、模块卸载:总结送免费学习资料(包含视频、技术学习路线图谱、文档等)关键数据结构 USB驱动结构,即插即用功能的实现总结送免费学习资料(包含视频、技术学习路线图谱、文档等)用两个函数调用spca5xx_probe 和spca5xx_disconnect来支持USB设备的即插即用功能:a -- spca5xx_probe具体实现如下:static void * spca5xx_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id) { struct usb_interface_descriptor *interface; //USB设备接口描述符struct usb_spca50x *spca50x; //物理设备数据结构 int err_probe; int i; if (dev->descriptor.bNumConfigurations != 1) //探测设备是不是可配置goto nodevice; if (ifnum > 0) goto nodevice; interface = &dev->actconfig->interface[ifnum].altsetting[0];MOD_INC_USE_COUNT; interface = &intf->altsetting[0].desc; if (interface->bInterfaceNumber > 0) goto nodevice; if ((spca50x = kmalloc (sizeof (struct usb_spca50x), GFP_KERNEL)) == NULL) //分配物理地址空间{ err ('couldn't kmalloc spca50x struct'); goto error; } memset (spca50x, 0, sizeof (struct usb_spca50x)); spca50x->dev = dev; spca50x->iface = interface->bInterfaceNumber; if ((err_probe = spcaDetectCamera (spca50x)) < 0) //具体物理设备查找,匹配厂商号,设备号(在子程序中){ err (' Devices not found !! '); goto error; } PDEBUG (0, 'Camera type %s ', Plist[spca50x->cameratype].name) for (i = 0; i < SPCA50X_NUMFRAMES; i++) init_waitqueue_head (&spca50x->frame[i].wq); //初始化帧等待队列init_waitqueue_head (&spca50x->wq); //初始化驱动等待队列if (!spca50x_configure (spca50x)) //物理设备配置(主要完成传感器侦测和图形参数配置),主要思想是给控制寄存器写值,读回其返回值,以此判断具体的传感器型号{ spca50x->user = 0; init_MUTEX (&spca50x->lock); //信号量初始化init_MUTEX (&spca50x->buf_lock); spca50x->v4l_lock = SPIN_LOCK_UNLOCKED; spca50x->buf_state = BUF_NOT_ALLOCATED; } else { err ('Failed to configure camera'); goto error; } /* Init video stuff */ spca50x->vdev = video_device_alloc (); //设备控制块内存分配if (!spca50x->vdev) goto error; memcpy (spca50x->vdev, &spca50x_template, sizeof (spca50x_template)); //系统调用的挂接,在此将驱动实现的系统调用,挂到内核中video_set_drvdata (spca50x->vdev, spca50x); if (video_register_device (spca50x->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { //video设备注册err ('video_register_device failed'); goto error; } spca50x->present = 1; if (spca50x->force_rgb) info ('data format set to RGB'); spca50x->task.sync = 0; spca50x->task.routine = auto_bh; spca50x->task.data = spca50x; spca50x->bh_requested = 0; MOD_DEC_USE_COUNT; //增加模块使用数 return spca50x; //返回数剧结构error://错误处理if (spca50x->vdev) { if (spca50x->vdev->minor == -1) video_device_release (spca50x->vdev); else video_unregister_device (spca50x->vdev); spca50x->vdev = NULL; } if (spca50x) { kfree (spca50x); spca50x = NULL; } MOD_DEC_USE_COUNT; return NULL; nodevice: return NULL; }b -- Spca5xx_disconnect 的具体实现如下:总结送免费学习资料(包含视频、技术学习路线图谱、文档等)static void spca5xx_disconnect (struct usb_device *dev, void *ptr) {struct usb_spca50x *spca50x = (struct usb_spca50x *) ptr;int n; MOD_INC_USE_COUNT; //增加模块使用数if (!spca50x) return;down (&spca50x->lock); //减少信号量spca50x->present = 0; //驱动卸载置0for (n = 0; n < SPCA50X_NUMFRAMES; n++) //标示所有帧ABORTING状态{spca50x->frame[n].grabstate = FRAME_ABORTING;spca50x->curframe = -1; }for (n = 0; n < SPCA50X_NUMFRAMES; n++) //唤醒所有等待进程{if (waitqueue_active (&spca50x->frame[n].wq))wake_up_interruptible (&spca50x->frame[n].wq);if (waitqueue_active (&spca50x->wq))wake_up_interruptible (&spca50x->wq); } spca5xx_kill_isoc(spca50x); //子函数终止URB包的传输PDEBUG (3,'Disconnect Kill isoc done');up (&spca50x->lock); //增加信号量while(spca50x->user) /如果还有进程在使用,进程切换schedule(); down (&spca50x->lock);if (spca50x->vdev) {video_unregister_device (spca50x->vdev); //注销video 设备usb_driver_release_interface (&spca5xx_driver,&spca50x->dev->actconfig->interface[spca50x->iface]); //端口释放spca50x->dev = NULL; }up (&spca50x->lock); #ifdef CONFIG_PROC_FSdestroy_proc_spca50x_cam (spca50x); //注销PROC文件 #endif /* CONFIG_PROC_FS */ if (spca50x && !spca50x->user) //释放内存空间{spca5xx_dealloc (spca50x);kfree (spca50x); spca50x = NULL;} MOD_DEC_USE_COUNT; //减少模块记数PDEBUG (3, 'Disconnect complete'); }二、上层软件接口模块:总结送免费学习资料(包含视频、技术学习路线图谱、文档等)该模块通过file_operations数据结构,依据V4L协议规范,实现设备的关键系统调用,实现设备文件化的UNIX系统设计特点。
开发WDM型USB设备驱动程序USB设备驱动程序开发⼯具开发USB设备驱动程序需要专门的开发⼯具,⽬前应⽤⼴泛的⼯具主要有两⼤类。
⼀类是Microsoft公司提供的Windows DDK(Device Driver Kit)。
它有Windows 98 DDK 和Windows 2000 DDK两个版本。
Windows 98 DDK能够开发Windows 95/98/Me/NT下的VxD、KMD和WDM驱动程序。
Windows 2000 DDK 能够开发Windows 98/Me/NT/2000下的KMD和WDM驱动程序。
由于DDK基于汇编语⾔的编程⽅式和内核模式的调⽤,对没有深厚的OS原理和编程⽔平的⼈员来说,任务相当艰巨。
另⼀类是NuMega公司提供的DriverStudio,它是⼀个⼤的开发⼯具包,包含VtoolsD、SoftICE和DriverWorks等开发⼯具。
VtoolsD开发包提供了对VxD编程的C/C++类库⽀持,利⽤VtoolsD中的QuickVxD⼯具可以快速⽣成VxD的C/C++代码框架,开发者可以在此基础上根据各⾃的需要添加⾃⼰的代码。
DriverWorks⽤于开发KMD和WDM驱动程序,并且对DDK函数进⾏了类的封装,从⽽为开发Windows NT、Windows 2000和Widnwos98 WDM设备驱动程序提供了⼀个⾃动化的⽅法。
DriverWorks,提供了VC++下的开发向导Driver Wizard,按照它的提⽰可以迅速地⽣成驱动程序的框架。
这个框架结构提供可以正确执⾏WDM动态环境中IRP的请求,⽽且,也包含⽤于简化系统提供的标准类驱动程序(如HID、流)和总线驱动程序(如PCI和USB)接⼝的类等。
总之,利⽤DriverWorks开发WDM驱动程序,可以⼤⼤简化开发⼈员的⼯作量、缩短开发周期以及降低开发驱动程序的难度。
[1]⽤DriverWorks开发WDM型USB设备驱动程序要想使⽤DriverWorks,必须先安装以下软件:DriverStudio2.5,VisualStudio6.0,以及Windows2000DDK。
最新开发USb驱动程序的方法连载一开发USb驱动程序的方法(连载二)NT还有更藝其他的对象,例如中断对紀Controller对盘、定时器对歓等等,但在我们开发的驱动程序中并没有用到•因此在这里不做介绍。
I/O缓冲策賂很明显的,驱动程序和客户应用程序经常需要进行数据交换,但我们知道驱动程序和客户应用程序可能不在同一个地址空间,因此操作系统必须解决两者之间的数据交换。
这就就设讣到设备的"0缓冲策賂。
读写请求的I/O缓冲策賂前面说到通过设置Device对象的Flag可以选择控制处理读写请求的I/O缓冲策略。
下啲对这些缓冲策略分别做一介绍。
1、缓冲I/O(DO_BUFFERED_IO)在读写请求的一开始,I/O管理器检査用户缓冲区的可访问性,然后分配与调用者的缓冲区一样大的非分贞池,并把它的地址放在IRP的Associatedlrp.SystemBuiYer域中。
驱动程序就利用这个域來进行实际数据的传输°对于IRPJV1J_READ读请求.I/O管理器还把IRP的UserBuffer域设宜成调用者缓冲区的用户空间地址。
X请求完成时• I/O管理器利用这个地址将数据从驱动程序的系统空间拷贝回调用者的缓冲区。
对于IRP」V1J_WRITE写请求.UserBuffer被设辻为NULL,并把川户缓冲区的数据拷贝到系统缓冲区中。
2、直接VO(DO_DIRECTJO)I/O管理器首先检査用户缓冲区的可访问性,并在物理内存中锁定它。
然后它为该缓冲区创建一个内存描述表(MDL).并把MDL 的地址存放在IRP 的MdlAddress 域中。
Associaledlrp.SystemBuffer 和UserBuffer 都被设置为NULJ驱动程序可以调用函数MmGetSystemAddressForMdl得到用户缓冲区的系统空间地址. 从而进行数据操作。
这个函数将调用者的缓冲区映射到非份页的地址空间。
驱动程序完成I/O请求后.系统自动从系统空间解除缓冲区的映射。
usb驱动教程Windows驱动编程基础教程我经常在网上遇到心如火燎的提问者。
他们碰到很多工作中的技术问题,是关于驱动开发的。
其实绝大部分他们碰到的“巨大困难”是被老牛们看成初级得不能再初级的问题。
比如经常有人定义一个空的UNICODE_STRING,然后往里面拷贝字符串。
结果无论如何都是蓝屏。
也有人在堆栈中定义一个局部SPIN_LOCK,作为下面的同步用——这样用显然没有任何意义。
我无法一一回答这些问题:因为往往要耐心的看他们的代码,才能很不容易的发现这些错误。
而且我又不是总是空闲的,可以无休止的去帮网友阅读代码和查找初级错误。
但是归根结底,这些问题的出现,是因为现在写驱动的同行越来越多,但是做驱动开发又没有比较基础的,容易读懂的资料。
为此我决定从今天开始连载一篇超级入门级的教程,来解决那些最基本的开发问题。
老牛们就请无视这篇教程,一笑而过了。
Windows驱动编程基础教程(1.1-1.3)1.1 使用字符串结构常常使用传统C语言的程序员比较喜欢用如下的方法定义和使用字符串:char *str = { “my first string” }; // ansi字符串wchar_t *wstr = { L”my first string” }; // unicode字符串size_t len = strlen(str); // ansi字符串求长度size_t wlen = wcslen(wstr); // unicode字符串求长度printf(“%s %ws %d %d”,str,wstr,len,wlen); // 打印两种字符串但是实际上这种字符串相当的不安全。
很容易导致缓冲溢出漏洞。
这是因为没有任何地方确切的表明一个字符串的长度。
仅仅用一个’\0’字符来标明这个字符串的结束。
一旦碰到根本就没有空结束的字符串(可能是攻击者恶意的输入、或者是编程错误导致的意外),程序就可能陷入崩溃。
使用高级C++特性的编码者则容易忽略这个问题。
开发usb驱动程序的方法(连载一)开始驱动程序设计下面的文字是从Microsoft的DDK帮助中节选出来的,它让我们明白在开始设计驱动程序应该注意些什么问题,这些都是具有普遍意义的开发准则。
应该支持哪些I/O请求在开始写任何代码之前,应该首先确定我们的驱动程序应该处理哪些IRP例程。
如果你在设计一个设备驱动程序,你应该支持和其他相同类型设备的NT驱动程序相同的IRP_MJ_XXX 和IOCTL请求代码。
如果你是在设计一个中间层NT驱动程序,应该首先确认你下层驱动程序所管理的设备,因为一个高层的驱动程序必须具有低层驱动程序绝大多数IRP_MJ_XXX例程入口。
高层驱动程序在接到I/O 请求时,在确定自身IRP当前堆栈单元参数有效的前提下,设置好IRP中下一个低层驱动程序的堆栈单元,然后再调用IoCallDriver 将请求传递给下层驱动程序处理。
一旦决定好了你的驱动程序应该处理哪些IRP_MJ_XXX,就可以开始确定驱动程序应该有多少个Dispatch例程。
当然也可以考虑把某些 RP_MJ_XXX处理的例程合并为同一例程处理。
例如在ChangerDisk 和 VDisk里,对IRP_MJ_CREATE和IRP_MJ_CLOSE处理的例程就是同一函数。
对IRP_MJ_READ和IRP_MJ_WRITE处理的例程也是同一个函数。
应该有多少个Device对象?一个驱动程序必须为它所管理的每个可能成为I/O请求的目标的物理和逻辑设备创建一个命名Device对象。
一些低层的驱动程序还可能要创建一些不确定数目的Device对象。
例如一个硬盘驱动程序必须为每一个物理硬盘创建一个Device对象,同时还必须为每个物理磁盘上的每个逻辑分区创建一个Device对象。
一个高层驱动驱动程序必须为它所代表的虚拟设备创建一个Device 对象,这样更高层的驱动程序才能连接它们的Device对象到这个驱动程序的Device对象。
另外,一个高层驱动程序通常为它低层驱动程序所创建的Device对象创建一系列的虚拟或逻辑Device对象。
尽管你可以分阶段来设计你的驱动程序,因此一个处在开发阶段的驱动程序不必一开始就创建出所有它将要处理的所有Device对象。
但从一开始就确定好你最终要创建的所有Device对象将有助于设计者所要解决的任何同步问题。
另外,确定所要创建的Device对象还有助于你定义Device对象的Device Extension 的内容和数据结构。
开始驱动程序开发驱动程序的开发是一个从粗到细逐步求精的过程。
NT DDK的src\ 目录下有一个庞大的样板代码,几乎覆盖了所有类型的设备驱动程序、高层驱动程序和过滤器驱动程序。
在开始开发你的驱动程序之前,你应该在这个样板库下面寻找是否有和你所要开发的类似类型的例程。
例如我们所开发的驱动程序,虽然DDK 对USB描述得不是很详细,我们还是可以在src\storage\class目录发现很多和USB设备有关的驱动程序。
下面我们来看开发驱动程序的基本步骤。
最简的驱动程序框架1、写一个DriverEntry例程,在里面调用IoCreateDevice创建一个Device对象。
2、写一个处理IRP_MJ_CREA TE请求的Dispatch例程的基本框架 (参见DDK Kernel-Mode Drivers 4.4.3描述的一个DispatchCreate 例程所要完成的最基本工作。
当然写了DispatchCreate例程后,要在DriverEntry 例程为IRP_MJ_CREA TE初始化例程入口)。
如果驱动程序创建了多于一个Device对象,则必须为IRP_MJ_CLOSE 请求写一个例程,该例程通常情况下可以和DispatchCreate共用一个例程,参见参见DDK Kernel-Mode Drivers 4.4.3。
3、编译连接你的驱动程序。
用下面的方法来测试你的驱动程序。
首先按上面介绍的方法安装好驱动程序。
其次我们还得为NT逻辑设备名称和目标Device对象名称之间建立起符号连接,我们在前面已经知道Device对象名称对WIN32用户模式是不可见的,是不能直接通过API来访问的,WIN 32 API只能访问NT 逻辑设备名称。
我们可以通过修改注册表来建立这两种名称之间的符号连接。
运行REGEDT32.EXE在\HKEY_LOCAL_MACHINE\ System\ CurrentControlSet\Control\ Session Manager\ DOS Devices下建立起符号连接(这种符号连接也可以在驱动程序里调用函数 IoCreateSymbolicLink来创建)。
重新启动系统。
编写一个简单的测试程序调用WIN32API CreateFile函数以刚才你命名的NT逻辑设备名打开这个设备。
如果打开成功,那么你也就成功地写出了一个最简单的驱动程序了。
支持更多的设备I/O请求例如你的驱动程序可能需要对IRP_MJ_READ请求做出响应(完成后可用WIN32 API ReadFile函数进行测试)。
如果你的驱动程序需要能够手工卸载,那么还必须对IRP_MJ_CLOSE做出响应。
为你所需要处理IRP_MJ_XXX写好处理例程,并在DriverEntry里面初始化好这些例程入口。
一个低层的驱动程序可能需要最起码一个StartIo,ISR和DpcForIsr 例程,可能需要一个SynchCritSection 例程,如果设备使用了DMA,那么可能还需要一个AdapterControl例程。
关于这些例程,请参考 DDK相应文档。
对于高层驱动程序可能需要一个或多个IoCompletion例程,最起码完成检查I/O状态块然后调用IoCompleteRequest的工作。
如果需要,还要对Device Extension数据结构和内容做些修改开发usb驱动程序的方法(连载二)NT还有更多其他的对象,例如中断对象、Controller对象、定时器对象等等,但在我们开发的驱动程序中并没有用到,因此在这里不做介绍。
I/O缓冲策略很明显的,驱动程序和客户应用程序经常需要进行数据交换,但我们知道驱动程序和客户应用程序可能不在同一个地址空间,因此操作系统必须解决两者之间的数据交换。
这就就设计到设备的I/O缓冲策略。
读写请求的I/O缓冲策略前面说到通过设置Device对象的Flag可以选择控制处理读写请求的I/O缓冲策略。
下面对这些缓冲策略分别做一介绍。
1、缓冲I/O(DO_BUFFERED_IO)在读写请求的一开始,I/O管理器检查用户缓冲区的可访问性,然后分配与调用者的缓冲区一样大的非分页池,并把它的地址放在IRP的AssociatedIrp.SystemBuffer域中。
驱动程序就利用这个域来进行实际数据的传输。
对于IRP_MJ_READ读请求,I/O管理器还把IRP的UserBuffer域设置成调用者缓冲区的用户空间地址。
当请求完成时,I/O管理器利用这个地址将数据从驱动程序的系统空间拷贝回调用者的缓冲区。
对于IRP_MJ_WRITE写请求,UserBuffer被设置为NULL,并把用户缓冲区的数据拷贝到系统缓冲区中。
2、直接I/O(DO_DIRECT_IO)I/O管理器首先检查用户缓冲区的可访问性,并在物理内存中锁定它。
然后它为该缓冲区创建一个内存描述表(MDL),并把MDL的地址存放在IRP的MdlAddress域中。
AssociatedIrp.SystemBuffer和 UserBuffer 都被设置为NULL。
驱动程序可以调用函数 MmGetSystemAddressForMdl得到用户缓冲区的系统空间地址,从而进行数据操作。
这个函数将调用者的缓冲区映射到非份页的地址空间。
驱动程序完成I/O请求后,系统自动从系统空间解除缓冲区的映射。
3、这两种方法都不是这种情况比较少用,因为这需要驱动程序自己来处理缓冲问题。
I/O管理器仅把调用者缓冲区的用户空间地址放到IRP的UserBuffer 域中。
我们并不推荐这种方式。
IOCTL缓冲区的缓冲策略IOCTL请求涉及来自调用者的输入缓冲区和返回到调用者的输出缓冲区。
为了理解IOCTL请求,我们先来看看WIN32 API DeviceIoControl函数的原型。
BOOL DeviceIoControl (HANDLE hDevice, // 设备句柄DWORD dwIoControlCode, // IOCTL请求操作代码LPVOID lpInBuffer, // 输入缓冲区地址DWORD nInBufferSize, // 输入缓冲区大小LPVOID lpOutBuffer, // 输出缓冲区地址DWORD nOutBufferSize, // 输出缓冲区大小LPDWORD lpBytesReturned, // 存放返回字节数的指针LPOVERLAPPED lpOverlapped // 用于同步操作的Overlapped结构体指针);IOCTL请求有四种缓冲策略,下面一一介绍。
1、输入输出缓冲I/O(METHOD_BUFFERED)I/O管理器首先分配一个非分页池,它足够大地存放调用者的输入或输出缓冲区(不管哪个更大)。
非分页缓冲区的地址放在IRP的AssociatedIrp.SystemBuffer域中,然后把IOCTL的输入数据拷贝到这个非份页缓冲区中,并把IRP的UserBuffer域设置成调用者输出缓冲区的用户空间地址。
当驱动程序完成IOCTL请求时,I/O管理器将这个非份页缓冲区中的数据拷贝到调用者的输出缓冲区。
注意这里同一个非份页池同时用于输入和输出缓冲区,因此驱动程序在向缓冲区写东西之前应该把输入的所有数据读出来。
2、直接输入缓冲输出I/O(METHOD_IN_DIRECT)I/O管理器首先检查调用者输入缓冲区的可访问性,并在物理内存中将其锁定。
然后为该输入缓冲区创建一个MDL,并把指定该MDL的指针存放到IRP的MdlAddress域中。
同时,I/O管理器还在非份页池中分配一输出缓冲区,并把这个缓冲区的地址存放在IRP的AssociatedIrp.SystemBuffer域中,并把IRP的UserBuffer 域设置成调用者输出缓冲区的用户空间地址。
当驱动程序完成IOCTL请求时,I/O管理器将非份页缓冲区中的数据拷贝到调用者的输出缓冲区。
3、缓冲输入直接输出I/O(METHOD_OUT_DIRECT)I/O管理器首先检查调用者输出缓冲区的可访问性,并在物理内存中将其锁定。
然后为该输出缓冲区创建一个MDL,并把指定该MDL的指针存放到IRP的MdlAddress域中。