[克隆BSP]
Clone一个BSP.
WinCE6.0安装armv4i架构后,里面提供了一个名字为DeviceEmulator的BSP. 这个BSP是s3c2410的BSP.我的是s3c2440a,就克隆这个吧.
[移植OAL]
WinCE5.0的OAL是编译成为一个静态库oal.lib,然后与内核nk.lib静态编译成kernel.exe,也就是nk.exe.
而WinCE6.0把OAL从kernel中剥离出来单独编译成为oal.exe,内核则编译成了Kernel.dll. 分离的代价是不能再直接使用相互的资源了,即相互间的全局变量和函数不能直接访问了. OEMGLOBAL和 NKGLOBAL这2个结构体充当了OAL和Kernel的接口桥梁.
分离的好处是更方便内核独立升级(嗯哼~ 这在将来会发生么?设备的架构可是千差万别的.我想替内核升级最有可能的还是OEMs,不是MS),不过另外一个好处是接口更清晰了,内核会需要哪些OEM函数显得更直观明了.
内核的启动和原来略有不同了,简单回顾WinCE5.0的内核启动过程:
[NK.exe=Kern.exe]
StartUp() [
OAL入口点]
KernelStart() [
kernel入口点]
ArmInit()
OEMInitDebugSerial()
OEMInit()
KernelInit()
HeapInit()
InitMemoryPool()
ProcInit()
SchedInit()
FirstSchedule()
|
|
下面是WinCE6.0的内核启动过程:
OAL不能调用内核的KernelStart()函数了, 所以自己要实现一个KernelStart() (nkldr.lib替我们完成了这个, 把nkldr.lib链接到OAL),调用nkldr.lib中的KernelStart().然后执行的ARMInit()函数有一个很重要的任务,它将位于OAL的OEMinitGlobals()函数指针赋值到Kdata中, 后面内核需要这个指针.接下来根据Kdata找到并跳转到内核kernel.dll的入口点NKStartup().
Kernel.dll开始执行NKStartUp(),首先要把和OAL的桥梁打通.也就是把内核的NKGlobal数据结构指针交给OAL,并且获得OAL的OEMGlobal的数据结构指针. 怎么实现的? 内核根据Kdata找到OAL中一个函数OEMInitGlobals() — 还记得吗,前面说到OAL的ARMInit()曾经郑重的把OEMInitGlobals函数指针赋值给了Kdata,就是为了这一天... 然后,把NKGlobal指针作为参数执行这个函数, 这个函数返回OAL的OEMGlobal的指针.(嗯哼~
OAL要实现OEMInitGlobals(), oemmain.lib替我们完成了这个,把oemmain.lib链接到OAL).
这个Kdata是个什么玩意?
它是内核数据结构, 简单理解成共享内存好了, oal 和
kernel都可访问到.再往后没啥好说了,内核可以访问OAL了,痛快的调用OEM函数,和以前WinCE5.0差不多.
最后调用KernelStart(),这回这个函数可是在内核里面了,WinCE6.0起来了……
这个流程和WinCE5.0有点点差别.都是因为OAL和kernel分离了.
[编译OAL]
空谈了这么久,来做实质性的工作吧,开始编译WinCE6.0的OAL.
微软希望它的设计使得移植OAL时候尽可能少工作量,所以oal.exe分2个步骤来实现.第一步:编译oal.lib.第二步:编译oal.exe.
第一步的oal.lib可以就是原来版本的,你拷贝原来的oal目录代码到OALLIB目录编译一个oal.lib吧.关键是第二步,原来在WinCE5.0时候,把oal.lib+nk.lib编译成了kern.exe,然后改名成nk.exe.现在,要把oal+nkstub.lib,编译成oal.exe.nk.lib也不是说不要就可以直接不要的.在编译oal.lib时候可是大量使用了nk.lib的东东. 你不会想全文重新改变函数和变量的调用形式吧? ok,把nkstub.lib链接上.这么一来,
在OALEXE目录下的SOURCES文件里面,这4个库被添加进来.
TARGETLIBS=
oal.lib oemmain.lib nkldr.lib nkstub.lib ……(路径省略)
Nkldr.lib和oemmain.lib是干吗的?回溯前面的启动过程吧. nkldr.lib提供了KernelStart()的实现,
oemmain.lib提供了OEMInitGlobals()的实现.当然还有更多的,不罗列了.
[定制OAL]
没有oal.lib咋办?做一个吧……前面提到OAL和Kernel分离,使得接口更加明显了,kernel到底需要OEMs提供哪些函数,可以参照着oemglobal.h文件里面OEMGLOBAL结构体来完成.并且在oemglobal.c里面对这个结构体初始化.这个文件位于oemmain.lib.发扬愚公移山的精神,我来抽丝拨茧一下,下面根据我的2440来分析最重要的必须的几个接口:
要提供OEMInit(),
OEMInitDebugSerial(),
OEMInit()函数,建立一个init.c,然后实现这个函数.
OEMInitDebugSerial()放到下面debug.c中实现.
OEMWriteDebugString
OEMInitDebugSerial
OEMWriteDebugByte
OEMReadDebugByte
OEMWriteDebugLED
PQOAL的oal_other.lib提供了OEMWriteDebugString(),
在OALLIB下创建debug.c
,然后实现OEMInitDebugSerial, OEMWriteDebugByte,
OEMReadDebugByte, OEMWriteDebugLED这4个函数.
需要提供OEMCacheRangeFlush,
根据自己的架构去已有的PQOAL找吧,我的是oal_cache_arm920t.lib
需要提供
InitClock
OEMGetRealTime
OEMSetRealTime
OEMSetAlarmTime
OEMQueryPerfCounter
OEMQueryPerfFreq
OEMGetTickCount
InitClock,这个功能已经废除了, 相关功能被移到OEMPowerOff中.所以可以实现一个空函数,在初始化OemGlobal时候,把这个指针赋值RetuanFalse()函数也是一样的效果.OEMGetRealTime ,OEMSetRealTime是设置读取rtc的日期功能.OEMSetAlarmTime是设置rtc的报警时刻,找到oal_rtc_s3c2440a.lib
OEMQueryPerfCounter,和OEMQueryPerfFreq是提供更高精度时间的查询,
OEMGetTickCount返回当前CurMSec值,系统运行了多少毫秒.在oal_time.lib中已经有实现.特别强调的是,这个函数在WinCE5.0里面是SC_GetTickCount.需要把名字改了.这个是OAL的一个区别.
OEMIdle, OEMNotifyThreadExit,
OEMNotifyIntrOccurs, OEMUpdateReschedTime, 一个变量DefaultThreadQuantum.
OEMPowerOff, 这个还要说啥,
挂起时候会执行这个函数. PQOAL的oal_power_s3c2440a.lib已经帮忙实现了最基础的工作,会调用BSPPowerOff来完成平台相关的动作,建立一个文件power.c来实现这个BSPPowerOff.
OEMGetExtensionDRAM,
OEMEnumExtensionDRAM, CalcFSPages, 变量MainMemoryEndAddress
这组函数询问扩展RAM的情况,如果OEMEnumExtensionDRAM函数提供了,
就执行这个函数,否则执行OEMGetExtensionDRAM,
CalcFSPages计算pages, 这个功能内核自己实现了,已经不要了,指向一个空函数即可.
OEMInterruptEnable, OEMInterruptDisable,
OEMInterruptDone,
OEMIniterruptMask,OEMInterruptHandler
基本工作PQOAL已经做好了,PQOAL下面几个接口完成平台相关工作,BSPIntrInit, BSPIntrRequestIrq, BSPINtrEnableIrq,
BSPIntrDisableIrq, BSPIntrDoneIrq, BSPIntrActiveIrq.最后一个OEMInterruptHandler就是系统ISR,
将物理irq转换成逻辑中断SYSINTR_XXX.
OEMIoControl .PQOAL里面已经实现了这个功能.如果OEMs要添加新的控制命令, 建立一个ioctl.c,定义一个全局数组g_oalIoCtlTable, 然后往数组里面填入命令字和对应命令执行的函数的名称.这个对应命令的执行函数当然要自己实现了. 驱动或者应用使用KernelIOControl这个api时候,相应命令的函数就会执行.
[总结]
1.WinCE6.0不只是将OAL和kernel分离.还将kitl也分离成为了kitl.dll.
所以,OAL也不能直接使用kitl的资源.
首先把kitl.c和kitl相关代码从OAL里面给放到kitl目录去.然后在OEMInit中不能调用OALKitlStart()了,用KITLIoctl(IOCTL_KITL_STARTUP, NULL,
0, NULL, 0, NULL);这一句来替代.
2.OAL需要使用的数据结构定义,和外部函数声明都在头文件nkexport.h中.可以在oal.h中include这个nkexport.h头文件.
3. PQOAL的oal_log库有了变化,如果使用了这个库的要注意,它不在使用g_oalLogMask这个全局变量了.
4. timer和timer_dvs目录下的watchdog.c文件和nkexport.h中重复定义了pfnOEMRefreshWatchDog,dwOemWatchDogPeriod等.一个解决办法是去掉watchdog.c.反正也可以不使用这个接口.默认传过去的是空接口ReturnFalse()
5. 编译smflash.dll出错,需要fal.lib一起编译成为smflash.dll.
fal的源代码位于private\winceos\driver\msflash\src.这个fal.lib.好像和以前的不一样.从错误信息中观察,好像多出来2个接口.在fmd.cpp里面增加这2个接口:
LPVOID
FMDHOOK_HookInterface(PFMDInterface pInterface)
{ return (LPVOID) pInterface;}
Void FMDHOOK_UnhookInterface(LPVOID
pContext, FMDInterface *pInterface){}
至此, 在给oal加上一个启动代码startup.s. 一个oal.lib就完成了.