分类: Windows平台
2013-06-27 10:15:42
WinCE系统在驱动设计上有一个很方便的功能,就是原始设备制造商(OEMs)和独立硬件开发商(IHVs)可以自主开发设备驱动程序来支持他们的硬件。因此,深入了解WinCE系统驱动方式是非常有必要的。
(1)从驱动加载方式上的分类
从驱动加载方式上WinCE可分为内建设备驱动(Built-In Driver)和可加载驱动(Loadable Driver)。
WinCE系统可直接使用内建设备,因为内建设备驱动程序是与WinCE的核心组件紧密相连的,也就是内建设备驱动程序是被静态地链接到GWES(Graphics Windowing and Events Subsystem)的。这些驱动对应的设备通常在系统启动时,在GWES的进程空间内被加载,主要是与显示和输入有关的驱动。内建设备包括显示、触摸屏、音频、串行埠、LED、电池和PC卡插座等。
可加载设备是指可与平台连接和分离的第三方接口设备,可由用户随时安装和卸载。这种外围设备的驱动也被称为流驱动,这些驱动可以在系统启动时、或者启动后的任何时候由设备管理器动态加载,通常这类驱动是以DLL动态链接库的形式存在。在WinCE中典型的可加载驱动有:PCMCIA driver(PCMCIA.dll)。
与内建驱动程序不同的是,所有可加载流驱动程序都共享一个公用接口,而且功能也与应用程序所用的文件API中的功能匹配。因此,控制可加载设备的流接口驱动程序一般由应用程序存取。也就是说,流接口驱动程序是由一个特殊文件来将设备功能展现给应用程序的,该文件可被打开、读取、写入和关闭。例如,用户将一个GPS设备与平台相连后,就可启动有GPS功能的应用程序来存取并使用该设备。通常只有OEMs才会对内建设备驱动程序进行修改,其它自由设备生产商由于只提供附加的硬件设备,对内建设备驱动程序不会有过多涉及。
(2)从驱动程序层次上分类
按照结构分,WinCE驱动程序又可分为分层的驱动程序和不分层的驱动程序。分层的驱动程序由两个设置好的层组成:上层是模型设备驱动程序(Model Device Driver, MDD),下层是依赖平台的驱动程序(Platform Dependent Driver, PDD)。
分层的驱动程序中的MDD通常是无需修改可直接使用,MDD的作用是链接PDD层并定义它希望调用的函数接口:设备驱动程序提供器接口(Device Driver Service Provider Interface, DDSI)。同时MDD又把不同的函数集提供给WinCE内核,这些函数叫做设备驱动程序接口(Device Driver Interface, DDI)。不分层的驱动程序是把PDD与MDD写在一起,没有做严格的区分,通常这种驱动比较简单,比如ATADISK。
简单的说,内建驱动和加载式流驱动是从驱动与系统其它模块(调用者)的接口形式上做的分类;而不分层和分层是从驱动实现方式上的分类。在开发过程中,MDD层驱动是不需要被修改的。但和MDD层驱动不同的是,PDD层驱动必须被修改成和特定硬件相匹配的代码。
(3)从加载位置上分类
在WIN CE中,驱动既可以在用户模式下运行也可以在内河模式下运行。内核模式驱动运行在内核上下文中,用户模式驱动运行在一个或多个进程上。
内核模式驱动:设备管理器默认是把所有驱动当作内核模式驱动加载到内核空间,除非在注册表中设置了DEVFLAGS_LOAD_AS_USERPROC标志。内核模式驱动可以调用内核API函数,因此执行效率较高。而且它可以直接使用用户存储空间,快速存取用户空间的缓冲区。因为内核模式驱动对读写内存没有任何限制,因此要求健壮性很高,否则极易造成系统崩溃。
用户模式驱动:在注册表中设置了DEVFLAGS_LOAD_AS_USERPROC标志,这种驱动跟其它驱动是隔离的,所以即使这个驱动出现问题,也不会造成系统影响。由于用户模式驱动程序不能存取内核存储空间,执行效率会受到影响。
(4)关于外部变量使用跟模块调用之间的关系
碰到一个很奇怪的问题,两个模块之间存在调用和被调用的关系,那么两者要使用的外部变量跟该变量的申明位置就有关系,如果不正确就会出现编译错误。比如A模块最终会调用B模块,那么两者都要用到的外部变量要申明在B中,在A中用extern申明调用;如果这个变量申明在A中,却在B中用extern申明,则会出现为定义变量的编译错误。这种现象只存在于嵌入式操作系统中,在单片机中不会存在。
(5)从系统角度,分析流驱动的加载过程和工作原理
1,加载驱动程序。WINCE加载驱动程序有两种方式:(1)系统启动时,设备管理器搜寻注册表的HKEY_LOCAL_MACHINE/Drivers/BulitIn键下面的子键,并逐一加载子键下的每个驱动,此过程称为BusEnum;(2)应用程序可调用ActivateDeviceEx函数动态加载驱动程序。
2,设备管理器从注册表的DLL键值中获取驱动程序所在的DLL文件名。
3,设备管理器调用LoadDriver函数把DLL加载到自己(Device.exe)的虚拟地址空间内。
4,设备管理器在注册表的HKEY_LOCAL_MACHINE/Drivers/Active下面,记录所有已经加载的驱动程序记录。
5,设备管理器调用驱动程序中的XXX_Init函数,在该函数中,对硬件进行一些最基本的初始化工作。
至此,流式驱动加载完毕。
6,应用程序开始使用该设备,通过CreateFile调用XXX_Open打开设备,并通过ReadFile和WriteFile对设备进行一些操作。操作完毕后,通过CloseHandle调用XXX_Close关闭设备。
需要注意的是:在使用CreateFile打开设备时,这个参数设备名跟XXX是有关系的,应用程序就是根据这个名字(要去掉Index)再追加对应的操作名,来查找正确的驱动DLL导出函数(之前认为可能是根据索引号来查找,后来觉得不大可能,因为这种方式太死板了)。我们还会碰到这种情况,同一个DLL驱动,对应两个要打开的设备,比如“COM1”“COM2”,这两者可能是调用同样的驱动函数,但是在执行各自的init函数时已经对应的是不同的硬件,在以后的操作中也各自分配有独立的内存空间。所以是不冲突的。
http://blog.csdn.net/zhandoushi1982/article/details/6016596