wince流驱动入门讲解.docx
《wince流驱动入门讲解.docx》由会员分享,可在线阅读,更多相关《wince流驱动入门讲解.docx(19页珍藏版)》请在冰豆网上搜索。
![wince流驱动入门讲解.docx](https://file1.bdocx.com/fileroot1/2023-4/23/fb1f8bb0-8ffd-4c37-b3e0-d02c8afe6d32/fb1f8bb0-8ffd-4c37-b3e0-d02c8afe6d321.gif)
wince流驱动入门讲解
第一章驱动的准备
1.驱动程序完成以下功能:
Ø对设备初始化和释放;
Ø把数据从内核传送到硬件和从硬件读取数据;
Ø读取应用程序传送给设备文件的数据和回送应用程序请求的数据;
Ø检测和处理设备出现的错误。
2.整个硬件系统资源在驱动程序面前是赤裸裸的,驱动可以使用所有系统资源,编写驱动程序时我们必须格外小心驱动代码的边界条件,确保它们不会损坏整个操作系统。
3.WinCE毕竟是一个嵌入式系统,有其自身的特殊性,为了提高运行效率,所有驱动皆为动态链接库,驱动实现中可以调用所有标准的API。
而在其他Windows系统中可能的驱动文件还有.vxd,.sys和动态链接库。
4.Windows支持的驱动:
1)虚拟设备驱动程序(VirtualDeviceDriver):
Windows3.1(Windows95/98/Me)
2)内核模式驱动程序(KernelModeDriver):
WindowsNT
3)Win32驱动程序模型(Win32DriverMode):
从Windows98开始使用。
其中WDM是目前主流,然而在WinCE系统中,由于硬件资源有限和嵌入式系统的特点,对其的支持非常有限。
第二章WINCE驱动
1.WINCE驱动模型
目前WindowsCE提供了4种设备模型,其中2种专门用于WindowsCE模型,另外2种模型来自于其它的操系统,如图1所示:
图1WindowsCE各种驱动模型的关系
2.我们的工作
为了帮助开发者快速地开发WindowsCE驱动程序,微软在PlatformBuilder中提供了大量的驱动程序例源代码,同时,芯片厂商或OEM厂商有时也提供一些设备的驱动程序源代码,这些驱动程序源代码在多数情况下可以直接拿来使用,但是在少数情况下需要开发者根据自己的设备硬件特性做一些移植的工作,修改例源代码,重新编译和调试驱动程序。
移植工作虽然没有像开发一个全新的驱动程序那样富有挑战性,但它仍具有相当大的难度,其原因如下:
移植工作仍然要求开发者具有良好的软、硬件基础,熟悉驱动程序的基本开发和调试方法,并要求具有一定的开发环境和测试手段。
移植工作仍然需要了解驱动程序的架构,需要确切知道驱动程序对外暴露哪些接口,微软提供了哪些接口,还必须实现哪些接口等。
对于同一设备的驱动程序,其源代码往往位于PlatformBuilder多个不同的安装目录,移植工作首先需要找出所移植驱动程序的所有源代码的位置。
移植工作需要在所移植驱动程序的所有源代码中区分出与硬件有关的代码和独立于硬件的代码,熟悉每个软件模块的大致功能,找出需要更改的与特定硬件有关的代码,并详细分析这些代码。
大部分驱动程序的代码放在目录%_WINCEROOT%\public\COMMON\oak\drivers\下,这些驱动程序都是与平台无关的。
此外,对于不同的平台,在BSP目录中也有一些驱动程序的代码,它们在%_WINCEROOT%platform\\src\drivers\中,这些驱动都是与平台相关的。
移植工作所修改的源代码有可能仅仅只有几十行甚至几行代码,但在修改之前却需要花费大量的时间了解驱动架构、熟悉驱动接口、分析源程序代码、找出需要修改的位置。
本质上讲,移植与从头开发一个驱动的差别仅仅在于少写了很多程序。
省去了编写这部分程序的时,但对驱动程序开发者的水平要求似乎并没有丝毫的降低。
第三章流接口函数
流接口函数也称作流接口驱动程序的入口点,每个流接口驱动程序必须实现一组标准的函数,用来实现标准的文件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,指向字符串的指针。
通常这个参数都为一个流接口驱动在注册表内的设置。
返回值:
如果调用成功返回一个驱动程序的句柄。
描述:
当用户开始使用一个设备的时候,例如,当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)
参数:
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创建时生成的设备句柄。
返回值:
无返回值。
(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传递给编译器、连接器或其他工具。
*2.source文件
source也是一个文本文件,它为子目录中的源代码设置了不少宏定义。
TARGETNAME=SimpleDriver
RELEASETYPE=PLATFORM
TARGETTYPE=DYNLINK
TARGETLIBS=$(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib
DEFFILE=$(TARGETNAME).def
DLLENTRY=DllEntry
SOURCES=SimpleDriver.c
以上是simpledriver里面source的内容,具体解释如下:
TARGETNAME=SimpleDriver;指定生成最终生成的.exe,.lib,.dll文件的名称,这里是SimpleDriver.dll
RELEASETYPE=PLATFORM;它设置两种旗标:
RELEASEDIR和RELEASELIBDIR,用于指定编译生成二进制和库文件存放的目录。
默认情况下,为目标生成的二进制和库文件存放在目录%_PROJECTROOT%\oak下,这里存放在D:
\WINCE600\PLATFORM\Mini2440\target\ARMV4I\retail目录下。
TARGETTYPE=DYNLINK;这个宏定义指定构建文件的最终类型,可以把TARGETTYPE类型设置为以下四种类型中的任意一种。
1)MANAGED_EXE;
2)MANAGED_DLL;
3)MANAGED_WINEXE;
4)MANAGED_MODULE;
这里设置的最终类型为dll。
TARGETLIBS=$(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib ;
_COMMONSDKROOT等价于\WINCE600\public\common\sdk
它指定了额外的库文件(.lib)和目标文件(.Obj)链接为目标可执行文件(.exe或.dll).这里将coredll.lib链接生成最终的目标文件SimpleDriver.dll
DEFFILE=$(TARGETNAME).def;它指定模块定义文件(.def)的名称,这里指定了模块定义文件的名称为SimpleDriver.def
DLLENTRY=DllEntry;它为一个DLL文件指定DLL的入口函数,此时TARGETTYPE被设置为DYNLINK。
如果 DLLENTRY对应的值没有被设置时,那么DllMainCRTStartUp是DLL的C程序运行入口点。
这里 DLLENTRY的入口函数被指定为DllEntry,因而,上面的 TARGETTYPE被设置成DYNLINK;
SOURCES=SimpleDriver.c;它包含编译过程的文件列表,这些列表中包含汇编文件和源文件,这些文件的类型有.cxx,.cpp,.c,.asm,.s,.src,.rc,.obj,.ire,.res,.odl,.tlb,.i,.cs,.resx等。
这些文件编译之后可能是静态库文件(.lib),也有可能是动态库文件(.dll)。
这里编译过程中需要用到的源文件有SimpleDriver.c,编译之后的生成SimpleDriver.dll的动态链接库文件。
*3.SimpleDriver.def文件
.def文件定义了DLL的导出函数列表。
这里包括的内容如下:
LIBRARYSimpleDriver
EXPORTS
SPL_Init
SPL_Deinit
SPL_Open
SPL_Close
SPL_Read
SPL_Write
SPL_Seek
SPL_IOControl
SPL_PowerDown
SPL_PowerUp
主要是针对当前流接口函数,将相应的函数导出。
*4.SimpleDriver.h
这个而就不用多介绍了吧,主要是一些头文件的声明,定义等等
*5.SimpleDriver.c
下面给出基本的代码,有些函数给出了空定义,方便以后实现,同时方便理解。
*6.platform.reg
配置文件的编写。
涉及到的文件有.platform.bib,platform.reg,dirs,source,SimpleDriver.def,其中后面两个文件在前文已有绍,这里就以前两个配置文件为主。
首先,从注册表说起,先简单的介绍一下注册表:
Makeimg.exe使用.reg文件来为CE镜像建立注册表并添加默认的键值。
也就是说,在.reg中写入的注册表的键值会被默认地放入CE镜像的初始化注册表中。
其中Platform.reg定义了目标设备硬件的注册表设置,Project.reg定义基于WindowsCE项目工程的注册表设置。
注册表键值的类型如下:
1. REG_SZ表示一个字符串类型,如reg_sz:
”mystring”。
2. REG_DWORD表示一个双字节类型,如dword:
12345678(十六进制数)。
3. REG_MULTI_SZ表示多字符串类型,如multi_sz:
“mystring”,“mystring”。
4. REG_BINARY二进制类型。
在实际应用中可以使用IF/ENDIF关键字来引入一个注册表设置块,通过设置一个环境变量或一个特殊的值来达到这个目的。
为了包含一个注册表设置块,当一个环境变量没有被设置或者没有等于一个特定的值的时候,引入的注册表设置块的行尾应使用一个空格和“!
”。
这里比较好找,一目了然,在目录D:
\WINCE600\PLATFORM\SMDKXXXX\Files\platform.reg下添加:
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\SPL]
"Prefix"="SPL"
"Dll"="SimplDriver.Dll"
"FriendlyName"="Simple Driver"
"Order"=dword:
0//驱动安装顺序
"Index"=dword:
0//驱动名的数字部分
注:
Order
给所有的驱动设置相对安装顺序。
Order为0的驱动先安装,依次安装Order为1,2,…的驱动。
Oder可以让开发者保证有相到依赖关系的多个驱动以适当的顺序进行安装。
Index
指定在文件系统中的驱动名的数字部分。
在默认情况下,带有前缀COM的驱动在文件系统中分配名字为COM1,下一个即为COM2。
为了保证驱动总是安装成COM2,必须提供Index=2。
这样在WINCE启动时,就自动加载该驱动了。
*7.platform.bib
接着我们介绍platform.bib文件,关于bib文件,这里做一个简要的介绍:
二进制镜像文件构建文件(.bib)定义了哪个模块或者文件被包含到运行时的CE镜像中。
在编译期间,makeimg.exe使若干个.bib文件合并成ce.bib文件,然后,romimage.exe使用ce.bib决定哪些文件应该被打包进运行时CE镜像中,它同时还使用ce.bib来决定如何加载模块和文件到CE镜像(下载到目标设备的CE镜像)所在的内存中。
按照功能划分,.bib文件可以分为如下几种类型。
(1) Platform.bib。
Platform.bib位于目录D:
\WINCE600\PLATFORM\SMDK6410\Files,它包含硬件平台相关的模块和文件,如目标设备的驱动文件。
这些模块和文件是CE镜像的入口,比如.exe文件,如波形音频文件(.wav)等。
(2) Project.bib。
Project.bib位于目录D:
\WINCE600\PUBLIC\CEBASE\OAK\FILES,如果我们新建一个工程项目(OSDesign1)则这个工程位于目录:
D:
\WINCE600\OSDesigns\OSDesign1\OSDesign1\Wince600\SMDK6410_ARMV4I\OAK\files下。
Project.bib文件定义与创建CE镜像的工程相关的模块,如果在OSDesign1中创建了一个自己的模块或者应用程序,那么就要把它们添加到Project.bib文件中的MODILES部分。
(3) Common.bib。
Common.bib位于目录D:
\WINCE600\PUBLIC\COMMON\OAK\FILES下,它定义了CE镜像文件包含的通用显示驱动和核心系统模块。
(4) Config.bib。
Config.bib位于目录D:
\WINCE600\PLATFORM\SMDK6410\Files下,它定义了ROM和RAM的配置信息。
它同样包含了CE镜像文件的MEMORY和CONFIG部分。
Config.bib的MEMORY部分定义了运行时CE镜像内存分配表,指定了名称、地址、大小和MEMORY区域的类型。
.bib文件可以被分为四个部分,分别是MEMORY、CONFIG、MODULES和FILES。
下面将分别说明这四个部分各自的含义。
(1) MEMORY。
定义可用的物理内存,包括起始地址、大小和内存类型。
(2) CONFIG。
定义romimage.exe输出的配置选项。
默认情况下,这个区域是在config.bib文件中。
不过,也并不是.bib文件中必须要包含CONFIG部分。
(3) FILES。
指定放在CE镜像中的文件列表。
(4) MODULES。
指定放在CE镜像总的模块列表,包括EXE和DLL文件,与FILES的区别是放在MODULES中的文件通常是代码文件,并且构建系统时不会压缩这些文件。
这里我们主要介绍Modules部分。
Modules部分指定了哪些基于WindowsCE的模块包含到CE镜像中,以及如何给加载到config.bib文件中的MEMORY部分建立内存表。
这个部分可以包含200个模块,这些模块有源代码和数据两个部分组成。
MODULES的语法格式如下:
; Name Path Memoryblock Sectionoverride MemoryType
; --- ----- ------------ --------- --------
各参数之间用空格分隔。
NAME:
这个参数指定了MODULES模块的名称。
通常情况下,它就像被路径引用的文件名称一样。
PATH:
指定要打包进CE镜像的MODULES模块的完整路径。
MEMORYBLOCK:
这个参数指定romimage.exe加载目标模块到内存区域的ramimage(镜像)部分。
这个内存位于config.bib文件中memory部分指定的某一段内存。
SECTIONOVERRIDE:
这个参数的设置时可选的,它可以为modules、files或者空。
如果设置了这个参数,那么构建系统就会根据它来决定这一项是modules还是files。
TYPE:
这个参数指定文件的类型,主要有以下几种类型,在实际的使用中可以选用其中的一种或者多种组合。
1. S:
定义一个系统文件
2. H:
定义一个隐藏文件
3. R:
压缩资源,只应用于MODULES部分
4. C:
如果应用于一个模块,则压缩全部内容
5. D:
运行时不允许调试。
6. N:
标记一个模块为不可信任的,只应用MODULES部分。
7. K:
指定romimage.exe必须修正模块到一个内核地址。
在这个过程中,romimage.exe分配一个固定的虚拟地址给DLL,设置了此标志的模块只可以被loadKernelLibrary()函数加载。
8. P:
指定romimage.exe禁止在头文件中检查指定的CPU类型。
这个旗标只用于资源dll,可以在一种CPU伤编译,在不同CPU上使用。
9. M:
表示对此页