[克隆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就完成了.