阅读:1477回复:6
我该怎么办?
我是个新手,刚刚毕业,现在在一家私企搞linux潜入式开发,硬件还好一点,可现在老总却让我做驱动开发,而我只会用vim,gcc搞一点简单的,前段时间看了不少这方面的知识,但让我自己动手却无从下手,请高手指点一二,具体的从哪里下手啊;能不能给个简单的例子,如一个模拟开关量的驱动啊,看那些理论性的太抽象了,复杂的又看不太懂。
谢谢了!! :( |
|
|
沙发#
发布于:2003-09-13 17:25
看看ABC过去的帖子吧,已经有太多人问这个问题了.....
|
|
|
板凳#
发布于:2003-09-13 17:26
同情你。和我一样。偶也是什么也不懂就被弄到这来了。
先给你一个前辈发的帖子,写的不错。 看看吧。 能找到这里算你的福气,这高手多着呢。 ---------------转贴--------------------- 关键字 设备驱动程序 一、背景介绍 1.1 Windows NT操作系统的组成 1.1.1 用户模式(User Mode)与内核模式(Kernel Mode) 从Intel 80386开始,出于安全性和稳定性的考虑,该系列的CPU可以运行于ring0 ~ring3从高到低四个不同的权限级,对数据也提供相应的四个保护级别。运行于较 低级别的代码不能随意调用高级别的代码和访问较高级别的数据,而且也只有ring0 层的代码可以直接进行对物理硬件的访问。由于Windows NT是一个支持多平台的操作 系统,为了与其他平台兼容,它只利用了CPU的两个运行级别。一个被称为内核模式, 对应80x86的ring0层,操作系统的核心部分,包括设备驱动程序都运行在该模式;另 一个被称为用户模式,对应80x86的ring3层,操作系统的用户接口部分以及所有的用 户应用程序都运行在该级别。 1.1.2 Windows NT操作系统的结构 图1简要地描述了Windows NT的系统组成。 图一 从图中可以看到,在物理硬件(Hardware)与系统核心(Kernel)之间有一个硬件抽象 层(Hardware Abstraction Layer),它屏蔽了不同平台硬件的差异,向操作系统的 上层提供了一套统一的接口。从图中我们还可以看到,设备驱动程序(Device Driver) 是被I/O管理器(I/O Manager)包围起来的,即驱动程序与操作系统上层的通信全部都 要通过I/O管理器。这给驱动程序的编写带来了很大的便利,因为很多诸如接收用户的请 求 、与用户程序交换数据、内存映射、挂接中断、同步等等麻烦的工作都由I/O管理器代 劳了。 1.1.3 Windows NT设备驱动程序的分类 根据是否直接操作硬件,可以把驱动程序分成两大类:内核模式的驱动程序和专用驱 动程序。 内核模式的驱动程序根据硬件的通信协议,直接对硬件进行端口访问、中断响应、DM A传输。它包括:串、并行口,键盘,文件系统,SCSI,网络等驱动程序;专用驱动程序 包括视频,打印,多媒体,虚拟DOS等驱动程序,他们在实现上与前者有很大区别。我在 实习期间所做的工作以及本文以下的讨论都局限于内核模式的驱动程序。 1.2 Windows NT下内核模式设备驱动程序的结构和运行 一般来说,设备驱动程序的任务主要有二:第一,接受来自用户程序的读写请求,把 用户的数据传送给设备,或把从设备接收到的数据传送给用户;第二,轮询设备或处理 来自设备的中断请求,完成数据传输。 1.2.1 驱动程序与用户程序的通信 I/O管理器把每一个设备对上层都抽象成了文件,所以在Win32用户程序中只要通过以 下几条简单的文件操作API函数就可以实现与驱动程序中的某个设备通信(请注意,一个 驱动程序可以驱动多个设备): 函数名 功能 CreateFile 打开一个设备,准备进行数据传输。返回一个与设备相关的句柄。 CloseHandle 关闭一个由CreateFile打开的设备。 ReadFile 从设备读取数据。 WriteFile 向设备写数据。 DeviceIoControl 对设备进行一些自定义的操作,比如更改设置等。 表一 1.2.2 DriverEntry过程 这是每一个设备驱动程序的入口,每次该程序启动时被系统自动调用。大部分的设备 初始化的工作都在这个过程中完成。包括设置响应各种用户请求的过程的入口,使I/O管 理器能知道当用户的打开、关闭、读写等请求到来时各应调用那些过程来处理。驱动程 序中只有本过程的名字\"DriverEntry\"是固定的,以下列出的所有过程都要由本过程向系 统注册。 如果该驱动程序不响应任何请求的话,只要一个DriverEntry过程就可以构成一个能运 行的驱动程序。 1.2.3 Unload和ShutDown过程 Unload过程负责在驱动程序被停止前做一些必要的处理。比如释放资源,记录最终状 态等。ShutDown过程在系统即将关闭时被调用,与前者的区别在于不用释放任何资源。 1.2.4 DispatchOpen和DispatchClose过程 这两个过程在用户调用CreateFile和CloseHandle时被调用,为即将到来的读写操作做 备,或做一些读写完成后的必要处理。 1.2.5 DispatchRead, DispatchWrite与StartIo过程 这前两个过程在用户调用ReadFile和WriteFile时被调用。它们先做一些检验用户请求 合法性的工作,然后启动一个被称为StartIo的过程开始实际的与硬件间的数据传输。I /O管理器还通过IRP为它们提供了一个指向用户缓冲区的指针,用于与用户程序交换数据 。详情请见1.3.2 1.2.6 接受自定义的其他请求 这两个过程在用户调用DeviceIoControl时被调用。它通过IRP获得用户的请求号,以 及一个指向用户缓冲区的指针,可以与用户程序进行通信。 1.2.7 中断处理过程(ISR) 这些过程在中断发生时被系统调用。 1.2.8 推迟过程(Deferred Procedure) 这些过程用来在较低的运行级别完成较高运行级别过程(如中断处理过程)的一 些任务。详情请见1.3.3 1.3 实现细节 1.3.1 内核代码运行级别 Windows NT为它的内核模式的代码分配了不同的级别。在同一个CPU上,级别低的过程 可以被任何级别更大的过程中断。级别由低到高排列如下: 级别名称 运行于该级别的过程 PASSIVE_LEVEL DriverEntry, Unload, ShutDown, DispatchXxx。 APC_LEVEL 在某些特殊情况下,大存储量设备的驱动程序运行于该级别。 DISPATCH_LEVEL StartIo, AdapterControl, ControllerControl, IoTimer,Dpc。 DIRQLs 各种中断处理程序。 表二 1.3.2 几个对象 i) I/O请求包(IRP) I/O管理器每收到一个来自用户的请求就创建一个该结构,并将其作为参数传给驱 动程序的DispatchXxx、StartIo过程。该结构中存放有请求的类型,用户缓冲区的首地 址,用户请求数据的长度等信息。驱动程序处理完这个请求后,也在该结构中添入处理 结果的有关信息,调用IoCompleteRequest将其返回给I/O管理器,用户程序的请求随即 返回。 ii) DPC 当驱动程序中要用到Dpc过程时,需要创建该对象。具体作用请见1.3.3。 iii) 驱动程序对象(DriverObject) 该对象在驱动程序被启动时由I/O管理器创建,保存有该程序处理各种请求的过程 入口、该程序所驱动的全部设备对象的链表等。 iv) 设备对象(DeviceObject) 每发现一个可以驱动的设备,驱动程序调用IoCreateDevice创建一个该对象。该 对象有一个指针DeviceExtension指向一块由驱动程序定义的结构,其中保存有关此设备 的如端口号,中断向量等全部信息。 v) 中断对象(Interrupt) 该对象在驱动程序调用IoConnectInterrupt时创建,存有中断及处理的过程的信息。 当一个中断发生时,I/O管理器用它寻找对应的处理过程。 1.3.3 推迟过程调用(Deferred Procedure Call) 由于中断处理过程运行于较高的DIRQL级,它们能屏蔽许多级别小于或等于它们的过程 的执行,如果它们占用CPU时间过长,很容易使系统性能下降。因此中断处理过程应将一 些不是很紧急的任务放在被称为Dpc的过程中,在完成数据传输等紧急任务后将一个DPC 对象放在系统DPC队列的末尾,然后退出,尽量早地让出CPU。系统将在完成所有DIRQL级 的任务后处理DPC队列,在DISPATCH_LEVEL执行每一个DPC 对象指定的Dpc过程,完成中 处理断过程未尽的任务。 1.3.4 查找硬件信息 i) 系统自动搜索到的设备 在系统启动时,组件NTDETECT会自动地搜索计算机上已有的硬件,包括串、并行 口,键盘,鼠标,以及大多数PCI和EISA设备。并将它们的信息,包括总线类型,总线号 ,用到的端口号及数量、中断向量号、DMA通道号、占用内存等按一定格式添入注册表的 \\HKEY_LOCAL_MACHINE\\Hardware\\description\\System\\ 键之下。在驱动程序中可以用I oQueryDeviceDescription以及一个回调函数ConfigCallback来查找符合要求的设备,并 获取它的配置信息。 ii) 系统不能自动搜索到的设备 一些ISA的设备无法被系统自动检测到,只有在安装驱动程序时在注册表中人工添入它 们的配置信息。驱动程序启动时可以用RtlQueryRegistryvaluess等函数查询注册表获得 这些信息。 1.3.5 有关内存 80386以上的32位CPU可以管理多达4GB的物理内存。它将这些内存分为许多大小为64K B的段和4KB的页来管理,并通过段描述符和页表将物理地址映射成系统地址供程序访问 。由于Windows NT使用虚拟内存技术,可能某些系统地址对应的物理地址处于硬盘上, 每当程序读写这些地址时会产生一个缺页异常,使CPU将这些内存调入物理存储器中。这 部分内存被称为分页内存(Paged)。与之对应的是非分页内存(Nonpaged),这部分内 存保证是物理驻留的。驱动程序中运行级别大于等于DISPATCH_LEVEL的过程不能访问分 页内存,否则引起系统崩溃。 1.3.6 缓冲的I/O与直接I/O 在驱动程序创建了一个设备后,可以通过设置DeviceObject的Flags域的值来将设备设 置成缓冲的I/O或直接的I/O。 如果该值被设为DO_BUFFERED_IO,每当I/O管理器收到一个读写请求,就在内存的非分 页区分配一块与用户区大小相同的区域,并将首指针存放于Irp对象的AssociatedIrp.S ystemBuffer中,驱动程序就通过这个缓冲区与用户交换数据。每当一个读请求被完成时 I/O管理器自动将该缓冲区中的内容复制到用户区,并释放该区域。 如果用户区大于一页(在80x86上为4096字节),一般将该值设为DO_DIRECT_IO。 这时每当I/O管理器收到一个读写请求,先锁定用户区的物理内存,然后为其创建一个内 存描述表(MDL),并将该表的首指针存放于Irp对象的MdlAddress中,驱动程序可以通过 调用MmGetSystemAddressformdl获得用户区在系统空间中的地址。每当一个读请求被完 成时I/O管理器自动将该区域解锁。 1.3.7 定时 为了防止当设备出现某种故障时导致读写请求超时,或需要定时轮询某些设备的状态 ,驱动程序需要设置一些定时器。驱动程序中有两种方法可以设置定时器。一种是调用 IoInitializeTimer将一个定时器过程IoTimer与一个设备对象联系起来。在调用IoStar tTimer后,系统将每一秒钟调用一次IoTimer,直至驱动程序调用IoStopTimer。如果需 要设置更小间隔的定时器,需要用到被称为CustomTimerDpc的一种推迟过程调用机制。 它可以设置系统每隔一定时间将一个设置好的DPC对象放到DPC队列的末尾,执行一个指 定的定时器Dpc过程。这个时间间隔可以精确到100ns。 1.3.8 同步 如果驱动程序有可能在某时刻有多个部分在同时运行,比如有中断处理过程,或 存在多个设备等,对公共数据或代码的访问就需要同步。方法有 i) 自旋锁(SpinLock) 驱动程序可以在初始化时调用KeInitializeSpinLock创建该对象。在任何代码段 访问被保护的数据之前,先调用KeAcquireSpinLock试图获得该对象的所有权,如果成功 ,该段代码被系统提升至DISPATCH_LEVEL,进行数据访问。访问完毕后须调用KeRelease SpinLock释放所有权,运行级别也被恢复。此方法只适用于同步运行级别小于等于DISP ATCH_LEVEL的代码,主要用于多CPU的情形。此外,还有一种中断自旋锁用于与中断处理 过程同步,可以将较低级别的代码提升到需要与之同步的中断DIRQL。 ii) 控制器(Controller) 该对象主要用于同步一个驱动程序中的多个设备,保证它们能顺序地访问特定的 代码或数据。该对象在驱动程序初始化调用IoCreateController被创建。设备在StartI o过程中调用IoAllocateController请求获得Controller对象的独占权。使用完后调用I oFreeController释放。驱动程序停止时调用IoDeleteController从内存删除该对象。该 对象有一个指针ControllerExtension指向一块由驱动程序定义的结构,其中保存有此驱 动程序的公共数据。 iii) 适配器(Adapter) 该对象用于同步多个设备(不一定在一个驱动程序中)对DMA通道的使用。该对象 在系统启动侦测硬件时自动被创建。驱动程序在初始化时调用HalGetAdapter获得该对象 的指针。设备在StartIo过程中调用IoAllocateAdapterChannel请求获得DMA通道的独占 权,然后开始传输数据。使用完后调用IoFreeControllerChannel释放DMA通道。 iv) DPC 由于DPC队列中的对象总是被系统顺序地处理,所以也可以将需要同步的代码做成 Dpc过程,需要调用时将相应的DPC对象放到队列的末尾即可。 v) 其他 同用户模式的应用程序类似,驱动程序也可以使用多线程,也提供了一套用来同步的 对象,如Event, Mutex, Semaphore, Timer, Thread。其中Event对象可以被命名,不同 的驱动程序可以利用同名的Event对象同步对公共数据的访问。 1.3.9 分层 I/O管理器一个有用的功能是允许把一个驱动程序堆在另一个驱动程序之上。这样在分 编写如网络驱动等有协议栈程序时,可以为各层编写相对独立的代码。当驱动程序需要 在不同的平台上移植时,只需重新编写最下层的硬件驱动程序即可。高层驱动程序的另 一个功能是可以对用户请求进行予处理,比如把较大的请求分割成较小的请求分多次传 给给下层的程序。 1.3.10 设备名及其符号连接 Windows NT系统维护着一个对象名字空间,把所有在系统内注册过的对象的名字分类 存在一个树状空间里,用Win32 SDK提供的WinObj工具可以浏览这个空间。如果希望设备 能被用户的CreateFile函数打开,就需要在调用IoCreateDevice创建该设备对象时赋予 它一个名字,位于\\Device\\下,并调用IoCreateSymbolicLink在\\DosDevices\\下创建一 个符号连接。这样,用户程序就能用CreateFile(\"\\\\\\\\.\\\\符号连接名\",……)打开该设 备,并获得其句柄。 1.4 驱动程序的编译链接,调试、安装和启动。 Windows NT下编写驱动程序的环境被称为为DDK(Device Driver Kit) For Micro soft Windows NT,这是一个命令行下的工作环境。但是在安装DDK之前需要安装Win32 SDK(Software Development Kit)以及 Microsoft Visual C++。 编译链接器为Build.exe,他从配置文件Sources中读出待编译的程序的配置,包 括源文件、目标文件等,从环境变量Include中得到引用文件的地址,然后调用Visual C++的编译链接器Nmake.exe进行实际的编译链接工作。日志文件build.log,build.wrn ,build.err 中分别记录了编译链接中执行的命令行,遇到的错误,遇到的警告。编译 完成后的文件后缀为.sys 安装过程分两步:第一,将编译成的.sys文件拷贝到Windows NT的System32\\Dri vers\\下;第二,在注册表的HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Service s\\下创建与.sys文件同名的键,然后在之下创建名为Start,Type, ErrorControl的三 个REG_DWORD类型的数值键。其中Start的键值控制该驱动程序在系统启动的哪个阶段被 启动。小于3的数设定该驱动程序在系统启动的某个阶段被自动启动;3表示需要管理员 手动启动;4表示该程序被禁用。设置完毕后需要重新启动系统。 手动启动和停止一个驱动程序需要使用控制面板(ControlPanel)中的设备(Device )图标。 由于驱动程序的结构比较复杂,而且调试内核模式的代码需要两台安装有Windows NT 的计算机,比较麻烦,所以在编写一个较复杂的驱动程序的过程中应分步来进行测试。 在完成任何一部分工作后都应进行测试,以便及早地发现错误。根据本人的经验,驱动 程序中的大多数错误都是由于不正确地访问内存造成的。比如使用未被初始化的指针, 释放已经被释放的内存,在DISPATCH_LEVEL或以上的运行级别引用分页的内存。 ---------------------end----------------------------- :D |
|
地板#
发布于:2003-09-13 17:29
这个到是一步步的写了, 可是偶还是没有成功?
:( 这个HelloWDM.h要怎么写呢??? ----------------转贴(出处忘了,好象是21ic---------------- WDM驱动程序入门 (zz) WDM驱动程序入门(1)――HelloWDM WDM驱动程序是一种很新的东西,相信很多人都跟我一样,对它很感兴趣,但是又找不到学习的切入点。究其原因,还是因为WDM是一种非常“死板板”的程序,它一运行就是工作在系统的底层RING 0处,提供各种接口给应用程序调用。也正因为如此,它不像普通的应用程序一样,可以很快地上手――更多的时候,你是在阅读它的技术资料和各种接口信息,你还要非常地熟悉系统底层的工作原理,否则一个不小心,就“蓝屏”了,呵呵――话说回来,写驱动程序的时候,死机是家常便饭。 因此很多人都对WDM望而生畏了。回想一下,我刚开始学WDM的情形还历历在目――看书看了整整3天,但是看完之后好像跟没看也差不了多少,还是不知道怎么入门,甚至连怎么写一个“Hello World”都不知道――后来才知道其实WDM是没有所谓的“Hello World”程序的,唉,真是痛苦啊,这主要还是因为网络上的WDM资料太少造成的。为了不让大家重蹈我的覆辙并对WDM有个感性的认识,在此我给出一个最简单的完整的WDM框架,并附有注释,姑且可以算是一个入门的“Hello World”吧。 废话少说,让我们马上开始研究,要求读者已安装DDK 2000。(在Win98中我还没有测试过,不清楚是否能正常运行) /*************************************************************** 程序名称:Hello World for WDM 文件名称:HelloWDM.cpp 作者:罗聪 日期:2002-8-16 ***************************************************************/ //一定要的头文件,声明了函数模块和变量: #include \"HelloWDM.h\" /*************************************************************** 函数名称:DriverEntry() 功能描述:WDM程序入口 ***************************************************************/ //extern \"C\"是必须的,表示“用C链接”。如果你的文件名是HelloWDM.c的话,这句可以省略。 extern \"C\" NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { //指定“添加设备”消息由函数“HelloWDMAddDevice()”来处理: DriverObject->DriverExtension->AddDevice = HelloWDMAddDevice; //指定“即插即用”消息由函数“HelloWDMPnp()”来处理: DriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp; //返回一个NTSTATUS值STATUS_SUCCESS。几乎所有的驱动程序例程都必须返回一个NTSTATUS值,这些值在NTSTATUS.H DDK头文件中有详细的定义。 return STATUS_SUCCESS; } /*************************************************************** 函数名称:HelloWDMAddDevice() 功能描述:处理“添加设备”消息 ***************************************************************/ NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject) { //定义一个NTSTATUS类型的返回值: NTSTATUS status; //定义一个功能设备对象(Functional Device Object): PDEVICE_OBJECT fdo; //创建我们的功能设备对象,并储存到fdo中: status = IoCreateDevice( DriverObject, //驱动程序对象 sizeof(DEVICE_EXTENSION), //要求的设备扩展的大小 NULL, //设备名称,这里为NULL FILE_DEVICE_UNKNOWN, //设备的类型,在标准头文件WDM.H或NTDDK.H中列出的FILE_DEVICE_xxx值之一 0, //各种常量用OR组合在一起,指示可删除介质、只读等。 FALSE, //如果一次只有一个线程可以访问该设备,为TRUE,否则为FALSE &fdo); //返回的设备对象 //NT_SUCCESS宏用于测试IoCreateDevice内核是否成功完成。不要忘记检查对内核的所有调用是否成功。NT_ERROR宏不等同于!NT_SUCCESS,最好使用!NT_SUCCESS,因为除了错误外,它还截获警告信息。 if( !NT_SUCCESS(status)) return status; //创建一个设备扩展对象dx,用于存储指向fdo的指针: PDEVICE_EXTENSION dx = (PDEVICE_EXTENSION)fdo->DeviceExtension; dx->fdo = fdo; //用IoAttachDeviceToDeviceStack函数把HelloWDM设备挂接到设备栈: dx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject); //设置fdo的flags。有两个“位”是必须改变的,一个是必须清除DO_DEVICE_INITIALIZING标志,如果在DriverEntry例程中调用IoCreateDevice(),就不需要清除这个标志位。还有一个是必须设置DO_BUFFER_IO标志位: fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE; fdo->Flags &= ~DO_DEVICE_INITIALIZING; //返回值: return STATUS_SUCCESS; } WDM驱动程序入门(2)――驱动程序的小秘密 好啦,辛辛苦苦终于写完了程序,让我们编译运行吧!按下Ctrl+F5(嘿嘿,让我们先假设你习惯用VC来写程序),我等啊等……疑?怎么毫无动静的?再看看Output窗口,哇!有几百个错误啊!!不禁头大――这是怎么回事呢? 原来,WDM程序编译出来的并不是我们常见的.exe,而是.sys文件,在未经设置编译环境之前,是不能直接用VC来编译的(这就是为什么会有几百个错误了)。这种类型的文件你可以在WINNT\\System32\\Drivers里面找到很多。其实驱动程序也是一种PE文件,它同样由DOS MZ header开头,也有完整的DOS stub和PE header,同样拥有Import table和Export table――hoho……那跟普通的PE文件有什么不一样呢?伟大的领袖毛主席教育我们,实践是检验真理的唯一标准。那么就让我们先来做个小剖析,加深对.sys文件的认识吧!(如果你对.sys的内部细节没有兴趣的话,可以略过不看。^_^) 首先祭出Delphi里附带的tdump.exe程序(别问我为什么用这个,这只是纯粹的习惯问题)。让我们键入: C:\\WINNT\\System32\\Drivers>tdump ccport.sys -em -ee 参数-em是列出Import table,-ee是列出Export table。回车之后,屏幕列出一大堆东西: C:\\WINNT\\SYSTEM32\\DRIVERS>tdump ccport.sys -em -ee Turbo Dump Version 5.0.16.12 Copyright ? 1988, 2000 Inprise Corporation Display of File CCPORT.SYS IMPORT: NTOSKRNL.EXE={hint:011Fh}.’memcpy’ IMPORT: NTOSKRNL.EXE={hint:003Dh}.’IoDeleteDevice’ IMPORT: NTOSKRNL.EXE={hint:0030h}.’IoAttachDeviceToDeviceStack’ IMPORT: NTOSKRNL.EXE={hint:008Eh}.’KeSetEvent’ IMPORT: NTOSKRNL.EXE={hint:0068h}.’IofCallDriver’ IMPORT: NTOSKRNL.EXE={hint:0095h}.’KeWaitForSingleObject’ IMPORT: NTOSKRNL.EXE={hint:0074h}.’KeInitializeEvent’ IMPORT: NTOSKRNL.EXE={hint:003Fh}.’IoDetachDevice’ IMPORT: NTOSKRNL.EXE={hint:00D3h}.’RtlFreeUnicodeString’ IMPORT: NTOSKRNL.EXE={hint:0077h}.’KeInitializeSpinLock’ IMPORT: NTOSKRNL.EXE={hint:0129h}.’strcpy’ IMPORT: NTOSKRNL.EXE={hint:0121h}.’memset’ IMPORT: NTOSKRNL.EXE={hint:003Ch}.’IoCreateUnprotectedSymbolicLink’ IMPORT: NTOSKRNL.EXE={hint:0038h}.’IoCreateDevice’ IMPORT: NTOSKRNL.EXE={hint:00C2h}.’RtlAnsiStringToUnicodeString’ IMPORT: NTOSKRNL.EXE={hint:0069h}.’IofCompleteRequest’ IMPORT: NTOSKRNL.EXE={hint:0124h}.’sprintf’ IMPORT: NTOSKRNL.EXE={hint:003Eh}.’IoDeleteSymbolicLink’ IMPORT: NTOSKRNL.EXE={hint:0042h}.’IoFreeIrp’ IMPORT: NTOSKRNL.EXE={hint:004Dh}.’IoInitializeIrp’ IMPORT: NTOSKRNL.EXE={hint:002Dh}.’IoAllocateIrp’ IMPORT: NTOSKRNL.EXE={hint:0027h}.’InterlockedExchange’ IMPORT: NTOSKRNL.EXE={hint:0025h}.’InterlockedCompareExchange’ IMPORT: NTOSKRNL.EXE={hint:0035h}.’IoCancelIrp’ IMPORT: NTOSKRNL.EXE={hint:012Ah}.’strlen’ IMPORT: NTOSKRNL.EXE={hint:0126h}.’strcat’ IMPORT: NTOSKRNL.EXE={hint:0114h}.’atoi’ IMPORT: NTOSKRNL.EXE={hint:0128h}.’strcmp’ IMPORT: NTOSKRNL.EXE={hint:0034h}.’IoBuildSynchronousFsdRequest’ IMPORT: NTOSKRNL.EXE={hint:00D5h}.’RtlInitAnsiString’ IMPORT: HAL.DLL={hint:0006h}.’KfAcquireSpinLock’ IMPORT: HAL.DLL={hint:0009h}.’KfReleaseSpinLock’ EXPORT ord:0001=’Vcomm_DriverControl’ 我们可以很清楚地看到,它主要调用了NTOSKRNL.EXE和HAL.DLL文件(实际上你会发现,几乎所有的WDM驱动程序都会调用NTOSKRNL.EXE文件,从它的名字你可以看出为什么了吧?),并且输出了一个函数“Vcomm_DriverControl”。这表明,其实.sys跟.exe文件一样,都是一种PE文件来的。不同的是,.sys文件Import的通常是NTOSKRNL.EXE,而.exe文件Import的通常是KERNEL32.DLL和USER32.DLL。 知道了这些有什么用呢?实际上,由于.sys通常不调用KERNEL32.DLL和USER32.DLL,所以你是不能在设备驱动程序里面调用任何C、C++和Win32函数的,而且也不能用C++关键字new和delete等(可以用malloc和free来代替),而必须使用大量的内核函数。为了读者的方便,下面我列出一些常见的驱动程序可用的内核函数: Ex… 执行支持 Hal… 硬件抽象层(仅NT/Windows 2000) Io… I/O管理器(包括即插即用函数) Ke… 内核 Ks… 内核流IRP管理函数 Mm… 内存管理器 Ob… 对象管理器 Po… 电源管理 Ps… 进程结构 Rtl… 运行时库 Se… 安全引用监视 Zw… 其他函数 最后让我们再来看看,写设备驱动程序时必须注意的一些问题: 1、内核宏 如果查看DDK头文件,会发现有几个内核函数是以宏的方式实现的。这种宏中有几个宏的定义是相当糟糕的。例如,我们看到RemoveHeadList的定义如下: #define RemoveHeadList(ListHead) (ListHead)->Flink; {RemoveEntryList((ListHead)->Flink)} 如果以以下方式调用RemoveHeadList,则将编译错误的代码: if(SomethingInList) Entry = RemoveHeadList(list); 使这个调用安全的唯一方法是使用花括号: if(SomethingInList) { Entry = RemoveHeadList(list); } 所以我们切勿为了贪图一时的方便,而使用不太规范的写法,最好是在所有的if、for和while等语句中使用花括号。 2、驱动程序函数名称 跟C/C++的main()函数一样,设备驱动程序也有一个必须存在,而且只能以DriverEntry()为名称的入口函数。然而,除此之外,我们可以使用任何名字来给其他函数命名――只要你自己记得就行了,当然,最好符合某些特定的规范啦,例如匈牙利命名法…… 3、安装时的问题 ?在Windows98中驱动程序可执行文件必须是8.3文件名。(别问我为什么,我也不知道,我只能建议你去问比尔该死) ?如果INF文件中含有非法节的详细资料,Windows将不使用这个INF文件。 呼~~讲了那么多,先去喝口水吧。其实本节罗罗嗦嗦讲了一大堆,跟实际的编程却并没有太大的关系,不过为了将来养成良好的编程习惯,还是应该遵守一下的,对吗?好了,下一节我将详细讲解如何编译、安装驱动程序,敬请留意。 WDM驱动程序入门(3)――安装步骤 DDK分为98 DDK和2000 DDK两种,它们工作起来是大同小异的,不过有些驱动程序只能在2000 DDK中使用。由于Win98注定是一种即将被淘汰的操作系统了,所以我也不打算介绍如何在98 DDK中进行编译,以下的所有内容都是针对2000 DDK的。 ?准备工作 1、确定你已经安装了Visual C++ 2、安装2000 DDK 3、安装2000 DDK成功后,在“开始”->“程序”里应该有“Development Kits”->“Windows 2000 DDK”的项目。 (注意一定要先安装好VC,然后才安装DDK,这个顺序决不能颠倒!!) 4、保证DDKROOT环境变量设置为Windows 2000 DDK的基目录,如果不是的话,请在控制面板“系统”属性的“高级”标签环境变量编辑器中设置好这个环境变量。 ?编写必需的文件 编译WDM程序的时候,有两个文件是必须要有的,它们是: 1、makefile (这个是什么啊?你可能会问。)对于比较年轻的程序员来说,有可能没有见过这个文件吧。其实在VC这些IDE出现之前,我们都必须使用makefile来确定项目中哪些文件需要重新编译,现在的IDE都把这个工作自动做好了。(Well……其实这样也好。) 我们要做的工作很简单,就是提供这样一个文件,它的内容是: # # DO NOT EDIT THIS FILE!!! Edit .\\sources. If you want to add a new source # file to this component. This file merely indirects to the real make file # that is shared by all the driver components of the Windows NT DDK # !INCLUDE $(NTMAKEENV)\\makefile.def 正如它所述,不要编辑这个文件。事实上每个WDM程序所需要的makefile的内容都是一样的,也就是说,我们只需要简单地copy一个makefile到新的项目中就可以了。(呵呵,是不是很方便呢?) 2、Sources TARGETNAME=HelloWDM TARGETTYPE=DRIVER DRIVERTYPE=WDM TARGETPATH=OBJ INCLUDES=$(BASEDIR)\\inc;\\ $(BASEDIR)\\inc\\ddk;\\ TARGETLIBS=$(BASEDIR)\\lib\\*\\free\\usbd.lib\\ SOURCES=HelloWDM.cpp\\ 这个文件指定了驱动程序目标名是HelloWDM.sys,是一个WDM驱动程序,生成的文件存放在OBJ目录中。值得注意的是,“=”前后不能有空格,否则编译的时候会出错。 ?开始编译 娃哈哈,前面罗罗嗦嗦讲了一大堆,现在终于到重点了。WDM程序的编译过程比较特殊,它不是在VC里面按F7来编译的(尽管你可以通过设置来达到这一目的),而是通过一个DDK实用工具build来完成。下面我们来讲讲具体步骤: 1、“Debug”版的生成 首先,我们假设你的源代码放在D:\\HelloWDM里面。请跟着以下步骤: “开始”->“程序”->“Development Kits”->“Windows 2000 DDK”->“Checked Build Environment” 屏幕将显示:(有“回车”的那行是需要读者你亲自打进去的) New or updated MSVC detected. Updating DDK environment…. Setting environment for using Microsoft Visual C++ tools. Starting dirs creation…Completed. D:\\NTDDK>cd\\HelloWDM (回车) D:\\HelloWDM>build (回车) 如果源代码没有错误的话,生成的HelloWDM.sys将存放在objchk\\i386目录中。 2、“Release”版的生成 请跟着以下步骤: “开始”->“程序”->“Development Kits”->“Windows 2000 DDK”->“Free Build Environment” 随后的步骤跟“Debug”版相同,不同的是生成的HelloWDM.sys将存放在objfre\\i386目录中。 ?安装 如果前面的编译过程没有错误的话,现在我们应该已经得到了一个HelloWDM.sys文件,假设它是放在D:\\HelloWDM\\objfre\\i386中。 我们还要干什么呢?…………对啦,就是安装它!不然辛辛苦苦编译出来有什么用? 安装WDM驱动程序可以用两种方法,一种是利用注册表,还有一种是利用INF文件。我们一般是采用INF文件(这是微软推荐的)。INF文件可以在 WINNT\\INF 目录中找到很多。为了顺利安装,我在这里先给出 HelloWDM 所需要的 HelloWDM.INF 文件: ;; The Win2K DDK documentation contains an excellent INF reference. ;--------- Version Section --------------------------------------------------- [Version] Signature=\"$CHICAGO$\" Provider=LC_Device DriverVer=8/21/2002,3.0.0.3 ; If device fits one of the standard classes, use the name and GUID here, ; otherwise create your own device class and GUID as this example shows. Class=Unknown ClassGUID={ff646f80-8def-11d2-9449-00105a075f6b} ;--------- SourceDiskNames and SourceDiskFiles Section ----------------------- ; These sections identify source disks and files for installation. They are ; shown here as an example, but commented out. [SourceDisksNames] 1 = \"HelloWDM\",Disk1,, [SourceDisksFiles] HelloWDM.sys = 1,objfre\\i386, ;--------- ClassInstall/ClassInstall32 Section ------------------------------- ; Not necessary if using a standard class ; 9X Style [ClassInstall] Addreg=Class_AddReg ; NT Style [ClassInstall32] Addreg=Class_AddReg [Class_AddReg] HKR,,,,%DeviceClassName% HKR,,Icon,,\"-5\" ;--------- DestinationDirs Section ------------------------------------------- [DestinationDirs] YouMark_Files_Driver = 10,System32\\Drivers ;--------- Manufacturer and Models Sections ---------------------------------- [Manufacturer] %MfgName%=Mfg0 [Mfg0] ; PCI hardware Ids use the form ; PCI\\VEN_aaaa&DEV_bbbb&SUBSYS_cccccccc&REV_dd ;改成你自己的ID %DeviceDesc%=YouMark_DDI, PCI\\VEN_9999&DEV_9999 ;---------- DDInstall Sections ----------------------------------------------- ; --------- Windows 9X ----------------- ; Experimentation has shown that DDInstall root names greater than 19 characters ; cause problems in Windows 98 [YouMark_DDI] CopyFiles=YouMark_Files_Driver AddReg=YouMark_9X_AddReg [YouMark_9X_AddReg] HKR,,DevLoader,,*ntkern HKR,,NTMPDriver,,HelloWDM.sys HKR, \"Parameters\", \"BreakOnEntry\", 0x00010001, 0 ; --------- Windows NT ----------------- [YouMark_DDI.NT] CopyFiles=YouMark_Files_Driver AddReg=YouMark_NT_AddReg [YouMark_DDI.NT.Services] Addservice = HelloWDM, 0x00000002, YouMark_AddService [YouMark_AddService] DisplayName = %SvcDesc% ServiceType = 1 ; SERVICE_KERNEL_DRIVER StartType = 3 ; SERVICE_DEMAND_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL ServiceBinary = %10%\\System32\\Drivers\\HelloWDM.sys [YouMark_NT_AddReg] HKLM, \"System\\CurrentControlSet\\Services\\HelloWDM\\Parameters\",\\ \"BreakOnEntry\", 0x00010001, 0 ; --------- Files (common) ------------- [YouMark_Files_Driver] HelloWDM.sys ;--------- Strings Section --------------------------------------------------- [Strings] ProviderName=\"Flying L Co.,Ltd.\" MfgName=\"LC Soft\" DeviceDesc=\"Hello World WDM!\" DeviceClassName=\"LC_Device\" SvcDesc=\"???\" 注意它可以同时在Win98或者Win2000中使用(系统会通过这个INF文件里面的字段名称,自动选择适合当前系统的安装方法的)。由于INF文件的各个字段含义比较复杂,限于篇幅,我在这里就不详细讲解了,请读者自行参阅有关的文章或者书籍。 准备好这个 HelloWDM.INF 文件后,让我们打开控制面板,双击“添加/删除硬件”,选择“添加/排除设备故障”->“添加新设备”->“否,我想从列表选择硬件”->“其它设备”->“从磁盘安装”,选择 HelloWDM.INF 所在的路径,然后安装。 当安装完成后,系统就会添加上你写好的驱动程序了。(可以在“设备管理器”中查看到)。然后重启电脑,这个驱动程序就投入使用啦。 ------------------------------------------------------- |
|
地下室#
发布于:2003-09-14 16:14
谢谢各位的相助!!
我今天把论坛以前的帖子都过了一遍,脖子差点没累段! 但收获甚微啊,我现在搞的是linux嵌入开发,基于ppc823e的,所要的驱动也是for linux的,但论坛上讲的大都是win的ddk和vxd方面的 因为我没什么基础,那些例子的代码都十几、几十页的,就想找个最简单的代码看看,就象:Hellow World!一样,最起码,我看了之后知道该赶什么、怎么做了,拜托哪位大侠贴下模拟开关量的驱动好吗?或发到我的邮箱:ahai_1017@sina.com 万分感谢!! 另:我这有北京sinovee的关于linux嵌入式开发的一整套资料(PDF),很全面的,里面也有关于驱动开发的资料,要的跟。 好dd大家分享!! |
|
|
5楼#
发布于:2003-09-15 13:23
拜托楼上的把LINUX资料也发我一份,shushishu@sohu.com
我也想搞,但最近公司的事情比较忙,一直没有时间开头。等我有点头绪了再跟你讨论怎么样? |
|
|
6楼#
发布于:2003-09-15 17:24
有好东西也可以发我一份嘛,minewdy@163.com
这几天我也在学习linux 的驱动,天天抱着《linux device drivers》 再看,到没什么效果,有时间我们讨论一下吗 |
|