CE6 驱动: 你不得不知道的事情

原地址:http://blog.csdn.net/armeasy/article/details/4965710

许多人担心CE6驱动的向后兼容性。在CE6上,应用程序和OAL可以比较良好的兼容,但驱动就比较难。驱动在移植到CE6上必须做一定的修改,原封不动的放到CE6上运行是不太可能的。

驱动需要修改的主要原因:

1、  API的差异

2、  内存传递

3、  Buffer异步访问

4、  用户层接口处理

CE6驱动的最大差异在于内嵌指针和数据传递,这个在《Memory marshalling in Windows CE》有详细描述。有2个主要修改点:

1、 找出所有代码有映射函数,如MapCallerPtr和MapPtrToProcess,改为CeOpenCallerBuffer / CeCloseCallerBuffer。

2、 找出SetKMode、SetProcPermissions的地方,有异步访问的,修改为CeAllocAsynchronousBuffer / CeFreeAsynchronousBuffer。

其次查找UI相关的函数,内核中不允许驱动运行UI相关功能(显示UI)。CE6中,几乎所有驱动都运行在内核中。即使是用户态驱动,最好也使用kernel UI handling方式来处理UI。CE6中,驱动一旦使用UI相关的功能,就会切入到一个用户态的DLL。所有的资源、shell call等,都会带入到此DLL中。使用CeCallUserProc会较为方便。

BOOL CeCallUserProc(
LPCWSTR pszDllName,
LPCWSTR pszFuncName,
LPVOID lpInBuffer, DWORD nInBufferSize,
LPVOID lpOutBuffer, DWORD nOutBufferSize,
LPDWORD lpBytesReturned);

这个函数类似调用IOCTL来得到LoadLibrary 和GetProcAddress的整合功能。当驱动在内核态调用此函数,udevice.exe实例中将会加载一个DLL。如果驱动在用户态调用此函数,也会在同个udevice.exe实例中加载此DLL,那么用户态和内核态驱动,在使用这个函数时候没有什么区别了。

CeCallUserProc和IOCTL之间最大区别在于,CeCallUserProc不允许内嵌指针。传入数据必须放在inbuffer参数中,而 传出数据只能放在outbuffer中,而不能通过内嵌指针再获取数据。问题是当内核调用用户层代码时,用户层无法同个CeOpenCallerBuffer或其他方法获取内核内存的大小。所以用户态不允许访问内核态的内存。

还有,当使用新的内存重建函数和CeCallUserProc,修改驱动时。最好注意是否需要做安全备份和异常处理,如上文所提。现在驱动都在内核态下,必须要保证系统的安全和稳定。

用户态驱动:

CE6具有用户态驱动进程,udevice.exe。用户态驱动和内核驱动一样,应用层可以使用ActivateDevice(Ex)和DeactivateDevice函数。设备管理器会读取注册表来判断驱动,是否需要运行在用户态。可以使用注册表指定udevice.exe的ID值, 让同一个进程加载多个用户态驱动。

例如,一个用户态驱动的驱动组ID为3,那么多个驱动都可以加载到这个组中。在CE6 %_WINCEROOT%/public/common/oak/files/common.reg中注册表,可以看到驱动组是怎样配置,驱动是怎样归纳到驱动组中的。如下:

[HKEY_LOCAL_MACHINE/Drivers/ProcGroup_0003]

    "ProcName"="udevice.exe"

    "ProcVolPrefix"="$udevice"

; Flags==0x10 is DEVFLAGS_LOAD_AS_USERPROC

[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/Ethman]

   "Flags"=dword:12

   "UserProcGroup"=dword:3

[HKEY_LOCAL_MACHINE/Drivers/Console]

    "Flags"=dword:10

    "UserProcGroup"=dword:3

[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/SIP]

    "Flags"=dword:10

    "UserProcGroup"=dword:3

如果你不配置这个,那么会单独由一个udevice来加载此驱动。系统将会有多个udevice进程。

设备管理器会创建一个转接服务来协助用户态驱动。转接服务负责加载udevice,加载特殊卷标,注册文件系统。应用程序和用户态驱动,通过转接服务进行buffer marshalling,来相互通讯。同时转接服务也会帮助用户态驱动做用户态上无法实现的工作,如映射物理内存等。

我们希望用户态和内核态驱动能达到完全一致,但内核态总是比用户态有更多的权限。不断改进的内核功能,将会使内核态驱动更难移植为用户态驱动。

另外,如前文所提。用户态驱动不能异步的回写指针参数,甚至可以认为,用户态不能异步访问caller的内存。最好把需要异步访问的驱动放到内核态,或者修改框架,让驱动不要异步访问caller。

还有,用户态驱动不能从内核中获取内嵌指针,CeCallUserProc也不支持内嵌指针。如果驱动需要从内核态获取内嵌指针,那么只能把此驱动放到内核中运行。或者修改驱动,不使用内嵌指针,而是调用CeCallUserProc让内存传递通过简单的in/out buffer来传递。

有些函数在用户态是必须注意使用的,如VirtualCopy和类似的MmMapIoSpace。用户态程序不允许使用VirtualCopy,但是用户 态能通过转接服务来实现这功能。转接服务能代替用户态驱动来调用VirtualCopy,但是前提是转接服务要知道这些地址是可以访问的。在驱动的注册表 入口,键值IOBase和IOLen来指出地址的位置和大小。当驱动使用VirtualCopy来,转接服务会检查这些键值,确保驱动能正常访问这些物理 地址。下面是串口驱动的例子:

[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/Serial]

    "IoBase"=dword:02F8

    "IoLen"=dword:8

如果只有一块地址需要访问,使用dword类型。如果是多块地址,那么使用multi_sz类型。

[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/Serial]

    "IoBase"=multi_sz:"2f8","3f6"

    "IoLen"=multi_sz:"8","2"

由于这些地址只能被有特权的应用程序访问,那么需要确保这些地址不能被非特权程序访问。

用户态驱动不能调用以下函数:

1、  VM虚拟内存函数:VirtualCopy[Ex], LockPages[Ex], CreateStaticMapping

2、  中断函数:InterruptInitialize, InterruptDone, LoadIntChainHandler

3、  不能直接使用IISR,需要通过转接服务来做GIISR。

4、  OAL层的IOCTL不能直接使用。

禁止从用户态驱动回调任何进程。你不能在总线驱动中回调一个内核态的client驱动。如果一个总线驱动放到用户态运行,只能把总线上的client驱动也放到用户态中。那么这个总线上所有的client驱动,都必须和总线驱动放到同一个udevice进程中。

有些OEM厂商可能会把一些OAL IOCTL和函数,通过编写特有的转接服务,让内核态驱动提供给用户态使用。值得注意的是,通过内核态驱动来公开这些功能,实际上是公开了微软特意封装的内容,最好不要这样使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值