1第一章驱动的准备1.驱动程序完成以下功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。2.整个硬件系统资源在驱动程序面前是赤裸裸的,驱动可以使用所有系统资源,编写驱动程序时我们必须格外小心驱动代码的边界条件,确保它们不会损坏整个操作系统。3.WinCE毕竟是一个嵌入式系统,有其自身的特殊性,为了提高运行效率,所有驱动皆为动态链接库,驱动实现中可以调用所有标准的API。而在其他Windows系统中可能的驱动文件还有.vxd,.sys和动态链接库。4.Windows支持的驱动:1)虚拟设备驱动程序(VirtualDeviceDriver):Windows3.1(Windows95/98/Me)2)内核模式驱动程序(KernelModeDriver):WindowsNT3)Win32驱动程序模型(Win32DriverMode):从Windows98开始使用。其中WDM是目前主流,然而在WinCE系统中,由于硬件资源有限和嵌入式系统的特点,对其的支持非常有限。第二章WINCE驱动1.WINCE驱动模型目前WindowsCE提供了4种设备模型,其中2种专门用于WindowsCE模型,另外2种模型来自于其它的操系统,如图1所示:2图1WindowsCE各种驱动模型的关系2.我们的工作为了帮助开发者快速地开发WindowsCE驱动程序,微软在PlatformBuilder中提供了大量的驱动程序例源代码,同时,芯片厂商或OEM厂商有时也提供一些设备的驱动程序源代码,这些驱动程序源代码在多数情况下可以直接拿来使用,但是在少数情况下需要开发者根据自己的设备硬件特性做一些移植的工作,修改例源代码,重新编译和调试驱动程序。移植工作虽然没有像开发一个全新的驱动程序那样富有挑战性,但它仍具有相当大的难度,其原因如下:移植工作仍然要求开发者具有良好的软、硬件基础,熟悉驱动程序的基本开发和调试方法,并要求具有一定的开发环境和测试手段。移植工作仍然需要了解驱动程序的架构,需要确切知道驱动程序对外暴露哪些接口,微软提供了哪些接口,还必须实现哪些接口等。对于同一设备的驱动程序,其源代码往往位于PlatformBuilder多个不同的安装目录,移植工作首先需要找出所移植驱动程序的所有源代码的位置。移植工作需要在所移植驱动程序的所有源代码中区分出与硬件有关的代码和独立于硬件的代码,熟悉每个软件模块的大致功能,找出需要更改的与特定硬件有关的代码,并详细分析这些代码。大部分驱动程序的代码放在目录%_WINCEROOT%\public\COMMON\oak\drivers\下,这些驱动程序都是与平台无关的。此外,对于不同的平台,在BSP目录中也有一些驱动程序的代码,它们在%_WINCEROOT%platform\BSPName\src\drivers\中,这些驱动都是与平台相关的。移植工作所修改的源代码有可能仅仅只有几十行甚至几行代码,但在修改之前却需要花费大量的时间了解驱动架构、熟悉驱动接口、分析源程序代码、找出需要修改的位置。本质上讲,移植与从头开发一个驱动的差别仅仅在于少写了很多程序。省去了编写这部分程序外部驱动模型流接口驱动程序本机驱动程序NDIS驱动程序USB驱动程序设备管理器GWES系统引导时间设备加载时间应用程序加载时间基于WindowsCE的驱动模型3的时,但对驱动程序开发者的水平要求似乎并没有丝毫的降低。第三章流接口函数流接口函数也称作流接口驱动程序的入口点,每个流接口驱动程序必须实现一组标准的函数,用来实现标准的文件I/O函数和电源管理函数,这些函数提供给WindowsCE操作系统的内核使用。这些函数通常叫做流接口驱动程序的DLL接口。以下介绍几个主要的流接口驱动接口函数。(1)DWORDXXX_Open(DWORDhDeviceContext,DWORDAccessCode,DWORDShareMode)参数:DWORDhDeviceContext,设备驱动的句柄,由XXX_Init函数创建的时候返回。DWORDAccessCode,传给驱动程序使用的地址,这个地址跟读和写有关。DWORDShareMode,共享模式,这个参数用于一些特殊的设备。例如一些PC卡的设备读或写的时候是否可以共享。返回值:返回驱动程序引用事例句柄。描述:这个函数用于打开一个设备驱动程序,当应用程序准备对某一个设备进行读或写操作时,系统必须先执行CreateFile()这个函数用于打开这个设备。这个函数执行以后系统才能够执行读和写操作。(2)BOOLXXX_Close(DWORDhOpenContext)参数:DWORDhOpenCnntext,设备驱动的引用事例句柄,由XXX_Open创建。返回值:调用成功返回TRUE,失败返回FALSE口描述:这个函数用于关闭一个驱动程序的引用实例。应用程序通过CloseHandle()来调用这个函数,当执行完这个函数的时候驱动程序引用的事例,hOpenContext将不再有效。(3)DWORDXXX_Init(DWORDdwContext)参数:DWORDdwContext,指向字符串的指针。通常这个参数都为一个流接口驱动在注册表内的设置。4返回值:如果调用成功返回一个驱动程序的句柄。描述:当用户开始使用一个设备的时候,例如,当PC卡初始化的时候,设备管理器调用这个函数来初始化PC卡设备。这个函数并不是由应用程序直接调用的,而是通过设备管理器提供的ActivateDeviceEx()函数来调用的。函数执行后如果成功则返回一个设备的句柄。(4)BDOLXXX_Deinit(DWORDhDeviceContext)参数:DWORDhDeviceContext,由xxx_Init创建时生成的设备句柄。返回值:调用成功返回TRUE,失败返回FALSE。描述:当一个用户需要卸载一个驱动程序的时候,设备管理器调用这个函数来卸载这个驱动程序,应用程序不能够直接调用这个函数,设备管理器通过DeactivateDeviec()函数调用这个函数。(5)DWORDXXX_Read(DWORDhOpenContext,LPVOIDpBuffer,DWORDCount)参数:DWORDhOpenContext,CreateFile()函数返回的句柄。LPVOIDpBuffer,一个缓冲区地址用于从驱动读数据。DWORDCount,需要读缓冲区的长度。返回值:实际读取字节的长度。描述:这个函数与ReadFile很相似,当一个流接口驱动程序已经被打开后,应用程序可以使用ReadFile()函数对这个设备进行读操作,ReadFile()里面的hFile参数就是这个设备的引用实例句柄hOpenContext,而参数lpBuffer将传给pBuffer,用于表示要读/写缓冲区的地址。参数nNumberofBytesToRead将传送给Count,用于表示要读写缓冲区的长度。同样,返回的参数,如果操作成功则返回实际读/写的地址,如果操作失败则返回值为-1。(6)DWORDXXX_Write(DWDRDhOpenContext,LPCVOIDpuffer,DWDRDCount)5参数:DWDRDhOpenContext,由CreateFile()函数返回的句柄。LPVOIDpBuffer,一个缓冲区地址,用于从驱动写数据。DWORDCount,需要写缓冲区的长度。返回值:实际写入字节的长度。描述:当一个流接口驱动程序打开以后,应用程序可以使用WriteFile()函数进行写操作。(7)BOOLXXX_IOControl(DWORDhQpenContext.WORDdwCode,PBYTEpBufIn,DWORDdwLenIn,PBYTEpBufOut,DWORDdwLenDut,PDWORDpdwActualOut)参数:DWORDhOpenContext,由CreateFile()函数返回的句柄。WORDdwCode,特殊的WORD型用于描述这次IOControl操作的语义,一般这个都由用户自己定义。PBYTEpBufIn,缓冲区指针指向需要传送给驱动程序使用的数据。DWORDdwLenIn,要传送给驱动程序使用数据的长度。PI3YTEpBufOut,缓冲区指针指向驱动程序传给应用程序使用的数据:DWDRDdwLEnOut,要传送给应用程序使用数据的长度。PDWORDpdwActualOut,DWORD型指针用于返回实际处理数据的长度。返回值:调用成功返回TRUE,调用失败返回FALSE。描述:这个函数通常用于向设备发送命令。应用程序使用DeviceIOControl函数来通知操作系统调用这个函数。通过参数dwCode来通知驱动程序要执行的操作。这个函数扩展了流接口驱动程序的功能(8)VOIDXXX_PowerDown(DWORDhDeviceContext)参数:DWORDhDeviceContext,由XXX_Init创建时生成的设备句柄。返回值:无返回值。6(9)VOIDXXX_PowerUp(DWORDhDeviceContext)参数:WORDhDeviceContext,由XXX_init创建时生成的设备句柄。返回值:无返回值。描述:PowerDown和PowerUp这两个函数通常都必须要硬件的支持才能够有效,也是说相关的硬件必须支持PowerDown和PowerUp这两个模式。(10)VOIDXXX_Seek(hDeviceContext,LongAmount,WORDType)参数:hDeviceContext,由XXX_init创建时生成的设备句柄。LongAmount定义要移动的设备数据的指针的字节数WORDType定义数据指针的起始点返回值:无返回值描述:当一个应用程序调用SetFilePointer函数移动设备数据指针时,操作系统会调用XXX_Seek函数。如果一个设备可以被多次打开,这个函数只修改由hDeviceContext定义的设备实例的数据指针。第四章最简单的流接口函数对于一个驱动我们要认清楚里面到底有哪些文件,他们的作用又是干什么的呢?下面以SimpleDriver为例,进行第一个简单流接口驱动的讲解。*1.Makefile文件这里的Makefile文件请不要和其他环境下(GCC,VS2005)的Makefile文件弄混,它是BSP里面的Makefile。WindowsCE中的Makefile比较特别,它包含对所有项目都通用的配置信息。其内容很简单,只有一句话:!INCLUDE$(_MAKEENVROOT)\makefile.def当build.exe查找dirs和source文件之后,它就会设置一个内部环境变量。这个环境变量可以被Nmake.exe传递给编译器、连接器或其他工具。7*2.source文件source也是一个文本文件,它为子目录中的源代码设置了不少宏定义。TARGETNAME=SimpleDriverRELEASETYPE=PLATFORMTARGETTYPE=DYNLINKTARGETLIBS=$(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.libDEFFILE=$(TARGETNAME).defDLLENTRY=DllEntrySOURCES=SimpleDriver.c以上是simpledriver里面source的内容,具体解释如下:TARGETNAME=SimpleDriver;指定生成最终生成的.exe,.lib,.dll文件的名称,这里是SimpleDriver.dllRELEASETYPE=PLATFORM;它设置两种旗标:RELEASEDIR和RELEASELIBDIR,用于指定编译生成二进制和库文件存放的目录。默认情况下,为目标生成的二进制和库文件存放在目录%_PROJECTROOT%\oak下,这里存放在D:\WINCE600\PL