[克隆BSP]
Clone一個(gè)BSP.
WinCE6.0安裝armv4i架構(gòu)后,里面提供了一個(gè)名字為DeviceEmulator的BSP. 這個(gè)BSP是s3c2410的BSP.我的是s3c2440a,就克隆這個(gè)吧.
[移植OAL]
WinCE5.0的OAL是編譯成為一個(gè)靜態(tài)庫oal.lib,然后與內(nèi)核nk.lib靜態(tài)編譯成kernel.exe,也就是nk.exe.
而WinCE6.0把OAL從kernel中剝離出來單獨(dú)編譯成為oal.exe,內(nèi)核則編譯成了Kernel.dll. 分離的代價(jià)是不能再直接使用相互的資源了,即相互間的全局變量和函數(shù)不能直接訪問了. OEMGLOBAL和 NKGLOBAL這2個(gè)結(jié)構(gòu)體充當(dāng)了OAL和Kernel的接口橋梁.
分離的好處是更方便內(nèi)核獨(dú)立升級(jí)(嗯哼~ 這在將來會(huì)發(fā)生么?設(shè)備的架構(gòu)可是千差萬別的.我想替內(nèi)核升級(jí)最有可能的還是OEMs,不是MS),不過另外一個(gè)好處是接口更清晰了,內(nèi)核會(huì)需要哪些OEM函數(shù)顯得更直觀明了.
內(nèi)核的啟動(dòng)和原來略有不同了,簡單回顧WinCE5.0的內(nèi)核啟動(dòng)過程:
[NK.exe=Kern.exe]
StartUp() [
OAL入口點(diǎn)]
KernelStart() [
kernel入口點(diǎn)]
ArmInit()
OEMInitDebugSerial()
OEMInit()
KernelInit()
HeapInit()
InitMemoryPool()
ProcInit()
SchedInit()
FirstSchedule()
|
|
下面是WinCE6.0的內(nèi)核啟動(dòng)過程:
OAL不能調(diào)用內(nèi)核的KernelStart()函數(shù)了, 所以自己要實(shí)現(xiàn)一個(gè)KernelStart() (nkldr.lib替我們完成了這個(gè), 把nkldr.lib鏈接到OAL),調(diào)用nkldr.lib中的KernelStart().然后執(zhí)行的ARMInit()函數(shù)有一個(gè)很重要的任務(wù),它將位于OAL的OEMinitGlobals()函數(shù)指針賦值到Kdata中, 后面內(nèi)核需要這個(gè)指針.接下來根據(jù)Kdata找到并跳轉(zhuǎn)到內(nèi)核kernel.dll的入口點(diǎn)NKStartup().
Kernel.dll開始執(zhí)行NKStartUp(),首先要把和OAL的橋梁打通.也就是把內(nèi)核的NKGlobal數(shù)據(jù)結(jié)構(gòu)指針交給OAL,并且獲得OAL的OEMGlobal的數(shù)據(jù)結(jié)構(gòu)指針. 怎么實(shí)現(xiàn)的? 內(nèi)核根據(jù)Kdata找到OAL中一個(gè)函數(shù)OEMInitGlobals() — 還記得嗎,前面說到OAL的ARMInit()曾經(jīng)鄭重的把OEMInitGlobals函數(shù)指針賦值給了Kdata,就是為了這一天... 然后,把NKGlobal指針作為參數(shù)執(zhí)行這個(gè)函數(shù), 這個(gè)函數(shù)返回OAL的OEMGlobal的指針.(嗯哼~
OAL要實(shí)現(xiàn)OEMInitGlobals(), oemmain.lib替我們完成了這個(gè),把oemmain.lib鏈接到OAL).
這個(gè)Kdata是個(gè)什么玩意?
它是內(nèi)核數(shù)據(jù)結(jié)構(gòu), 簡單理解成共享內(nèi)存好了, oal 和
kernel都可訪問到.再往后沒啥好說了,內(nèi)核可以訪問OAL了,痛快的調(diào)用OEM函數(shù),和以前WinCE5.0差不多.
最后調(diào)用KernelStart(),這回這個(gè)函數(shù)可是在內(nèi)核里面了,WinCE6.0起來了……
這個(gè)流程和WinCE5.0有點(diǎn)點(diǎn)差別.都是因?yàn)?/span>OAL和kernel分離了.
[編譯OAL]
空談了這么久,來做實(shí)質(zhì)性的工作吧,開始編譯WinCE6.0的OAL.
微軟希望它的設(shè)計(jì)使得移植OAL時(shí)候盡可能少工作量,所以oal.exe分2個(gè)步驟來實(shí)現(xiàn).第一步:編譯oal.lib.第二步:編譯oal.exe.
第一步的oal.lib可以就是原來版本的,你拷貝原來的oal目錄代碼到OALLIB目錄編譯一個(gè)oal.lib吧.關(guān)鍵是第二步,原來在WinCE5.0時(shí)候,把oal.lib+nk.lib編譯成了kern.exe,然后改名成nk.exe.現(xiàn)在,要把oal+nkstub.lib,編譯成oal.exe.nk.lib也不是說不要就可以直接不要的.在編譯oal.lib時(shí)候可是大量使用了nk.lib的東東. 你不會(huì)想全文重新改變函數(shù)和變量的調(diào)用形式吧? ok,把nkstub.lib鏈接上.這么一來,
在OALEXE目錄下的SOURCES文件里面,這4個(gè)庫被添加進(jìn)來.
TARGETLIBS=
oal.lib oemmain.lib nkldr.lib nkstub.lib ……(路徑省略)
Nkldr.lib和oemmain.lib是干嗎的?回溯前面的啟動(dòng)過程吧. nkldr.lib提供了KernelStart()的實(shí)現(xiàn),
oemmain.lib提供了OEMInitGlobals()的實(shí)現(xiàn).當(dāng)然還有更多的,不羅列了.
[定制OAL]
沒有oal.lib咋辦?做一個(gè)吧……前面提到OAL和Kernel分離,使得接口更加明顯了,kernel到底需要OEMs提供哪些函數(shù),可以參照著oemglobal.h文件里面OEMGLOBAL結(jié)構(gòu)體來完成.并且在oemglobal.c里面對這個(gè)結(jié)構(gòu)體初始化.這個(gè)文件位于oemmain.lib.發(fā)揚(yáng)愚公移山的精神,我來抽絲撥繭一下,下面根據(jù)我的2440來分析最重要的必須的幾個(gè)接口:
[Init相關(guān)接口]
要提供OEMInit(),
OEMInitDebugSerial(),
OEMInit()函數(shù),建立一個(gè)init.c,然后實(shí)現(xiàn)這個(gè)函數(shù).
OEMInitDebugSerial()放到下面debug.c中實(shí)現(xiàn).
OEMWriteDebugString
OEMInitDebugSerial
OEMWriteDebugByte
OEMReadDebugByte
OEMWriteDebugLED
PQOAL的oal_other.lib提供了OEMWriteDebugString(),
在OALLIB下創(chuàng)建debug.c
,然后實(shí)現(xiàn)OEMInitDebugSerial, OEMWriteDebugByte,
OEMReadDebugByte, OEMWriteDebugLED這4個(gè)函數(shù).
需要提供OEMCacheRangeFlush,
根據(jù)自己的架構(gòu)去已有的PQOAL找吧,我的是oal_cache_arm920t.lib
[Time相關(guān)接口]
需要提供
InitClock
OEMGetRealTime
OEMSetRealTime
OEMSetAlarmTime
OEMQueryPerfCounter
OEMQueryPerfFreq
OEMGetTickCount
InitClock,這個(gè)功能已經(jīng)廢除了, 相關(guān)功能被移到OEMPowerOff中.所以可以實(shí)現(xiàn)一個(gè)空函數(shù),在初始化OemGlobal時(shí)候,把這個(gè)指針賦值RetuanFalse()函數(shù)也是一樣的效果.OEMGetRealTime ,OEMSetRealTime是設(shè)置讀取rtc的日期功能.OEMSetAlarmTime是設(shè)置rtc的報(bào)警時(shí)刻,找到oal_rtc_s3c2440a.lib
OEMQueryPerfCounter,和OEMQueryPerfFreq是提供更高精度時(shí)間的查詢,
OEMGetTickCount返回當(dāng)前CurMSec值,系統(tǒng)運(yùn)行了多少毫秒.在oal_time.lib中已經(jīng)有實(shí)現(xiàn).特別強(qiáng)調(diào)的是,這個(gè)函數(shù)在WinCE5.0里面是SC_GetTickCount.需要把名字改了.這個(gè)是OAL的一個(gè)區(qū)別.
OEMIdle, OEMNotifyThreadExit,
OEMNotifyIntrOccurs, OEMUpdateReschedTime, 一個(gè)變量DefaultThreadQuantum.
OEMPowerOff, 這個(gè)還要說啥,
掛起時(shí)候會(huì)執(zhí)行這個(gè)函數(shù). PQOAL的oal_power_s3c2440a.lib已經(jīng)幫忙實(shí)現(xiàn)了最基礎(chǔ)的工作,會(huì)調(diào)用BSPPowerOff來完成平臺(tái)相關(guān)的動(dòng)作,建立一個(gè)文件power.c來實(shí)現(xiàn)這個(gè)BSPPowerOff.
[DRAM相關(guān)接口]
OEMGetExtensionDRAM,
OEMEnumExtensionDRAM, CalcFSPages, 變量MainMemoryEndAddress
這組函數(shù)詢問擴(kuò)展RAM的情況,如果OEMEnumExtensionDRAM函數(shù)提供了,
就執(zhí)行這個(gè)函數(shù),否則執(zhí)行OEMGetExtensionDRAM,
CalcFSPages計(jì)算pages, 這個(gè)功能內(nèi)核自己實(shí)現(xiàn)了,已經(jīng)不要了,指向一個(gè)空函數(shù)即可.
OEMInterruptEnable, OEMInterruptDisable,
OEMInterruptDone,
OEMIniterruptMask,OEMInterruptHandler
基本工作PQOAL已經(jīng)做好了,PQOAL下面幾個(gè)接口完成平臺(tái)相關(guān)工作,BSPIntrInit, BSPIntrRequestIrq, BSPINtrEnableIrq,
BSPIntrDisableIrq, BSPIntrDoneIrq, BSPIntrActiveIrq.最后一個(gè)OEMInterruptHandler就是系統(tǒng)ISR,
將物理irq轉(zhuǎn)換成邏輯中斷SYSINTR_XXX.
OEMIoControl .PQOAL里面已經(jīng)實(shí)現(xiàn)了這個(gè)功能.如果OEMs要添加新的控制命令, 建立一個(gè)ioctl.c,定義一個(gè)全局?jǐn)?shù)組g_oalIoCtlTable, 然后往數(shù)組里面填入命令字和對應(yīng)命令執(zhí)行的函數(shù)的名稱.這個(gè)對應(yīng)命令的執(zhí)行函數(shù)當(dāng)然要自己實(shí)現(xiàn)了. 驅(qū)動(dòng)或者應(yīng)用使用KernelIOControl這個(gè)api時(shí)候,相應(yīng)命令的函數(shù)就會(huì)執(zhí)行.
[總結(jié)]
1.WinCE6.0不只是將OAL和kernel分離.還將kitl也分離成為了kitl.dll.
所以,OAL也不能直接使用kitl的資源.
首先把kitl.c和kitl相關(guān)代碼從OAL里面給放到kitl目錄去.然后在OEMInit中不能調(diào)用OALKitlStart()了,用KITLIoctl(IOCTL_KITL_STARTUP, NULL,
0, NULL, 0, NULL);這一句來替代.
2.OAL需要使用的數(shù)據(jù)結(jié)構(gòu)定義,和外部函數(shù)聲明都在頭文件nkexport.h中.可以在oal.h中include這個(gè)nkexport.h頭文件.
3. PQOAL的oal_log庫有了變化,如果使用了這個(gè)庫的要注意,它不在使用g_oalLogMask這個(gè)全局變量了.
4. timer和timer_dvs目錄下的watchdog.c文件和nkexport.h中重復(fù)定義了pfnOEMRefreshWatchDog,dwOemWatchDogPeriod等.一個(gè)解決辦法是去掉watchdog.c.反正也可以不使用這個(gè)接口.默認(rèn)傳過去的是空接口ReturnFalse()
5. 編譯smflash.dll出錯(cuò),需要fal.lib一起編譯成為smflash.dll.
fal的源代碼位于private\winceos\driver\msflash\src.這個(gè)fal.lib.好像和以前的不一樣.從錯(cuò)誤信息中觀察,好像多出來2個(gè)接口.在fmd.cpp里面增加這2個(gè)接口:
LPVOID
FMDHOOK_HookInterface(PFMDInterface pInterface)
{ return (LPVOID) pInterface;}
Void FMDHOOK_UnhookInterface(LPVOID
pContext, FMDInterface *pInterface){}
至此, 在給oal加上一個(gè)啟動(dòng)代碼startup.s. 一個(gè)oal.lib就完成了.