作者:Walzer
日期:2005.3.19
摘要:KITL是PLATFORM BUILDER中的一個(gè)亮點(diǎn),提供了和本地調(diào)試類似的斷點(diǎn)、變量跟蹤、內(nèi)存查看等手段,如果沒(méi)有KITL,嵌入式調(diào)試應(yīng)該只能用串口打印消息來(lái)看了,工作效率大大下降。本文以實(shí)現(xiàn)最簡(jiǎn)單的SERIAL KITL為目的,就其實(shí)現(xiàn)代碼進(jìn)行跟蹤調(diào)試,這些代碼跨越了WINCE的PLATFORM、PUBLIC、PRIVATE三大主要目錄,有些煩瑣,不過(guò)只要能調(diào)通,一切工作和彎路都是值得的。我把調(diào)試經(jīng)驗(yàn)和個(gè)人理解寫(xiě)下來(lái),希望能幫助別人少走彎路。如果文章中有理解失當(dāng)?shù)牡胤剑?qǐng)不吝賜教。
正文:
一.void OEMInit()? [platform\project\src\kernel\oal\init.c]
??? 首先從OEMInit()函數(shù)看起。。在依次初始化Branch-Target Buffer、KERNEL函數(shù)、初始化中斷、TIMER之后,就輪到KITL了。調(diào)用了這個(gè)函數(shù)OALKitlStart()。此時(shí)有個(gè)編譯的分支,如果是RELEASE版本,那么在kernel\kern\stubs.c里面的OALKitlStart()函數(shù)是個(gè)STUB,只是return TRUE; 如果是DEBUG版本,那就進(jìn)到kernel\oal\kitl.c里面的OALKitlStart().
二.BOOL OALKitlStart()? [platform\myproject\src\kernel\oal]
??? 在OALKitlStart()里面,首先試圖從0xA00FF00處讀取bootloader里面留下的kitl參量,如果讀不到東西則使用該函數(shù)里的默認(rèn)配置。由于原來(lái)用了ethernet同時(shí)作為download和kitl途徑,所以在InitSpecifiedEthDevice函數(shù)里給pKitlArgs結(jié)構(gòu)體賦值了。現(xiàn)在想把兩者劃分清楚,首先就把讀取bootloader里面的kitl參量一句干掉,強(qiáng)迫使用我們?cè)谙旅娴哪J(rèn)配置。主要就是填充三個(gè)參量
1.首先是這個(gè)結(jié)構(gòu)體
OAL_KITL_ARGS pKITLArgs
{
?UINT32 flags;? //設(shè)好ENABLED標(biāo)志,按需要設(shè)POLL標(biāo)志,但注意一定不要設(shè)PASSIVE標(biāo)志
?DEVICE_LOCATION? devLoc;
?{
??DWORD? IfcType;??//不論ether還是serial,都是internal type =0;
??DWORD? BusNumber;?// =0
??DWORD? LogicalLoc;?//物理地址
??PVOID???? PhysicalLoc;?//留做后面=OALPAtoVA(LogicalLoc, FALSE). 真見(jiàn)鬼, 感覺(jué)應(yīng)該和上面的LogicalLoc作用調(diào)過(guò)來(lái)看著比較順吧?
??DWORD? Pin;??//Ethernet才用的東東
?}
?union
?{
??struct
??{
???UINT32? baudRate;?//不用解釋了
???UINT32? dataBits;
???UINT32? stopBits;
???UINT32? parity;
??}
??struct
??{
???UINT16? mac[3];?//這個(gè)也不用解釋了
???UINT32? ipAddress;
???UINT32? ipMask;
???UINT32? ipRoute
??}
?}
}
2. pszDeviceID.? 感覺(jué)這名字就是起了好玩, 賦成Walzer應(yīng)該比較拽,不過(guò)還是保留原來(lái)賦的AMOISOFT好了, 免得被打.
3. 全局變量OAL_KITL_DEVICE g_kitlDevices. 這個(gè)東東在kitl.c開(kāi)頭包含的kitl_cfg.h中被賦值,? 最主要就是修改g_kitlDevices.pDriver.? 這個(gè)pDrvier指向一個(gè)函數(shù)指針列表的結(jié)構(gòu)體,該列表定義了用做kitl模塊的初始化、讀寫(xiě)、中斷、流控制等函數(shù)。 g_kitlDevices本身是個(gè)二維數(shù)組, 可以定義許多設(shè)備用做kitl時(shí)提供的參數(shù)設(shè)置, 后面會(huì)用一個(gè)for來(lái)循環(huán)判斷pKITLArgs的參數(shù)和g_kilDevices里面哪個(gè)一維數(shù)組成員相匹配.
這三個(gè)參量填充好以后,就可以進(jìn)到OALKitlInit(pszDeviceID, pKITLArgs, g_kitlDevices)里面了.
三.BOOL OALKitlInit( deviceId, pArgs, pDevice)??? [platform\common\src\common\kitl\kitl.c]
??? 這個(gè)函數(shù)先把輸入的參量全部用OALMSG打印出來(lái),這個(gè)不管。
??? 重要的是引入了g_kitlState全局變量,開(kāi)頭一句
??? g_kitlState.pDevice = OALKitlFindDevice(&pArgs->devLoc, pDevice) 這個(gè)就是上面所說(shuō)的從g_kitlDevices里可用設(shè)備列表里循環(huán)判斷,找到選用的設(shè)備的匹配函數(shù)指針。
??? 接著把輸入?yún)⒘縟evicdId和前面填充好的OAL_KITL_ARGS結(jié)構(gòu)COPY到g_kitlState里面
??? 然后就可以調(diào)用KItlInit(TRUE)了,如果前面在FLAG里面設(shè)了PASSSIVE標(biāo)志,現(xiàn)在就是KitlInit(FALSE)了,嘿嘿爽到了吧。
四.BOOL KitlInit(BOOL fStartKitl)??? [private\winceos\coreos\nk\kitl\ethdbg.c]
??? 太猥瑣了,我要用串口,它居然叫ethdbg.c,不給面子。不過(guò)是private里面的東東,可遠(yuǎn)觀而不可褻玩焉~~
??? 這個(gè)函數(shù)干了三件事:
1. 裝載了三個(gè)全局的函數(shù)指針
2. 用NewClient注冊(cè)了三個(gè)KITL客戶端:
??? KITL_SVCNAME_DBGMSG?? //debug message, Debug信息發(fā)布通道
??? KITL_SVCNAME_PPSH? //PPshell, 文本控制臺(tái)界面
??? KITL_SVCNAME_KDBG //kernel debug, 內(nèi)核調(diào)試界面
3.由fStartKitl來(lái)決定是否啟動(dòng)StartKitl()函數(shù). (這里順便提一下,按照匈牙利命名法, BOOL變量前面加個(gè)b, 但MS的做法我覺(jué)得很合理, BOOL變量就是個(gè)FLAG嘛, 前面加個(gè)f, 把小b留著給BYTE類型用.)
五.static BOOL StartKitl(BOOL fInit)??? [private\winceos\coreos\nk\kitl\ethdbg.c]
??? 這又是prviate里面的東東。最痛苦的地方開(kāi)始了。這函數(shù)及其子函數(shù)將第一次調(diào)用OEM自己寫(xiě)的KITL模塊初始化、讀寫(xiě)程序。是騾子是馬,拉出來(lái)溜溜就知道啦~
??? 按順序看下來(lái),首先判斷輸入?yún)⒘渴欠褚獑?dòng)KITL,并且如果KITLGlobalState里面被打上KITL_ST_DESKTOP_CONNECTED標(biāo)記的話,那下面的步驟就全免了。當(dāng)然我們是第一次運(yùn)行到這里,若這么就跳出的話,俺就馬加爵了。
??? 第一步:
??? 干的第一件正事就是調(diào)用OEMKitlInit(&Kitl). 這個(gè)后面詳述. 繼續(xù)把這個(gè)函數(shù)看完.
??? OEMKitlInit初始化KITL的硬件抽象層后并把相關(guān)指針數(shù)據(jù)填充給全局變量KITLTRANSPORT Kitl (有沒(méi)有搞錯(cuò),為什么不叫g(shù)_kitl), 這些工作做完就返回了
???? StartKitl收貨后把Kitl結(jié)構(gòu)整個(gè)檢查一遍,保證沒(méi)錯(cuò)后, 馬上買(mǎi)單, 把全局變量KITLGlobalState打上個(gè)KITL_ST_KITLSTARTED標(biāo)記. 這才算KITL啟動(dòng)的第一步OK了.
??? 第二步:
??? 接下來(lái)就是KITLConnectToDesktop(), 進(jìn)這個(gè)函數(shù)后就是第一次使用了前面KITL傳輸介質(zhì)硬件抽象層里的讀寫(xiě)函數(shù)了, 這時(shí)候就需要調(diào)試了. 這個(gè)ConnectToDesktop大致就是先Send了一個(gè)kITL.....的frame過(guò)去,然后polling等待PC端的response, 那邊再發(fā)個(gè)kITL.....的frame過(guò)來(lái), 搞得跟地下黨打暗號(hào)似的, 其實(shí)也沒(méi)什么玄乎的,就是普通的數(shù)據(jù)包前面加個(gè)KTIL專用的HEADER而已. 這個(gè)CONNECT成功后,KITLGlobalState里面就加個(gè)KITL_ST_DESKTOP_CONNECTED標(biāo)記了.
??? 第三步:
??? set up kernel function pointers, 也沒(méi)什么,就三個(gè)函數(shù)指針, 賦完后就KITL_ST_ADAPTER_INITIALIZED了. 其實(shí)這個(gè)KITLGolbalSate的總共有7個(gè)標(biāo)志,分別是
KITL_STARTED,? (OK)
DESKTOP_CONNECTED,?? (OK)
TIMER_INIT, (?)
INT_ENABLED,? (POLLING)
IST_STARTED,? (POLLING)
MULTITHREADED,?? (?)
ADAPTER_INITIALIZED. (OK)
后面括號(hào)里打上OK是到這里已經(jīng)完成的, 打問(wèn)號(hào)的我還不太清楚什么作用, INT和IST兩項(xiàng),我們用的POLLING所以肯定是不需要了.
??? 第四步:
??? 調(diào)用SetKernelCommDev設(shè)置kernel通過(guò)何種介質(zhì)傳送DBGMSG, PPSH和KDBG.
??? OHYEAH, 我的SERIAL KITL就夭折在這里. 進(jìn)到SerKernelCommDev(service, CommDevice)函數(shù)里看, 它只認(rèn)CommDevice=KERNEL_COMM_ETHER的情況,而屏蔽了與ETHER并列的SERIAL和PARALLER,直接return FALSE, 下面的事情都不用干了. 而在MS提供的WinCE Documantation里面,這個(gè)SetKernelCommDev函數(shù)的說(shuō)明上寫(xiě)著"This function is obsolete and should not be used". 若想改嘛,這個(gè)是在private里面的還動(dòng)它不得. NND, 感覺(jué)被MS raped了.
???? 如果使用ETHER在這里成功的話, 下面還有兩個(gè)函數(shù)NKForceCleanBoot()和KITLInitializeInterrupt()走過(guò)去, 這KITL初始化就全部結(jié)束了. 我估計(jì)KITLGolbalSate里面的INIT_ENABLED和IST_STARTED就是在這個(gè)函數(shù)過(guò)程中被標(biāo)記上的.
????
???
六.BOOL OEMKitlInit(PKITLTRANSPORT pKitl)??? [platform\common\src\common\kitl\kitl.c]
??? 前面提到StartKItl起來(lái)后,首要的就是調(diào)用OEMKitlInit. 這個(gè)函數(shù)在WinCE4.2和5.0里差別很大, 4.2里的做法是??? if (!InitEther (pKitl) && !InitParallelSerial (pKitl)), 把ETHER, SERIAL, PARALLEL都初始化了一遍,碰運(yùn)氣看哪個(gè)用得上,而5.0里是進(jìn)來(lái)后就一個(gè)很明顯的分支劇情,由g_kitlState.pDevice->type來(lái)決定是調(diào)用OALKitlEthInit還是OALKitlSerialInit. 典型的種族歧視, 居然沒(méi)有OALKitlParallelinit. 還好我們用的是SERIAL.
??? 這里有個(gè)選擇編譯的地方,就是#ifdef KITL_ETHER和#ifdef KITL_SERIAL, 具體定義的地方是該目錄下的sources文件里面一行 CDEFINES=$(CDEFINES) -DKITL_SERIAL -DKITL_ETHER, 猥瑣啊找了半天. 其實(shí)我覺(jué)得既然有if結(jié)構(gòu)來(lái)選了,那么選擇編譯也是可有可無(wú)的了.
??? 好,下面就進(jìn)到OALKItlSerialInit()里面.
七.BOOL OALKitlSerialInit(LPSTR deviceId, OAL_KITL_DEVICE *pDevice, OAL_KITL_ARGS *pArgs, KITLTRANSPORT *pKitl)
??? [platform\common\src\common\kitl\kitlserial.c]
??? 我自己往這個(gè)kitlserial.c文件里寫(xiě)了六個(gè)函數(shù).
??? BOOL KitlSerialInit(KITL_SERIAL_INTFO *pSerInfo)
??? UINT16 KitlSerialWriteData(UINT8 *pch, UINT16 length)
??? UINT16 KitlSerialReadData(UINT8 *pch, UINT16 length)
??? void KitlSerialFlowControl? //stub, 我所用的FFUART只有TXD和RXD兩根線, RTS等都沒(méi)有, 所以FlowControl自然也應(yīng)該是STUB了
??? void KitlSerialEnableInt(void)?? //stub, use polling
??? void KitlSerialDisableInt(void)? //stub, use polling
??? 否則前面的g_kitlDevices里面沒(méi)有相應(yīng)的硬件抽象層來(lái)填充.
??? 上面的SerialRecv, Encode, Decode等就意思都很明顯了,不用多說(shuō). OK現(xiàn)在已經(jīng)走到最底層了, 文章也可以結(jié)束了.
八、記錄一下調(diào)試經(jīng)驗(yàn)
??? 雖然這是我第三次調(diào)串口了,由于沒(méi)總結(jié)前面的經(jīng)驗(yàn),還是耗了兩天才到private里面夭折的地方。實(shí)際上應(yīng)該一天就能走到了。問(wèn)題出在
1. 第一天調(diào)試器根本用不上手。調(diào)試器軟件、PB不斷死翹,經(jīng)常重啟軟件甚至重啟電腦,第一天有3/4以上的時(shí)間耗在這些問(wèn)題上, 不斷重啟。
2. 在UART初始化函數(shù)的最后,居然忘記了在interrupt controller register里面enable uart unit, 這么烏龍的事。
3. KitlSerialFlowControl的問(wèn)題. 寫(xiě)的時(shí)候照搬了X86下的函數(shù), 沒(méi)想明白到底要Control什么. 調(diào)試時(shí)死在這里后,? 一開(kāi)始把指向這個(gè)函數(shù)的指針設(shè)置成NULL, 但這樣PRIVATE里面有些要IF判斷的函數(shù)就進(jìn)不去了. 后來(lái)?yè)Q成STUB就OK了.
4. receive函數(shù)里面, 在收每個(gè)BYTE之前先去判斷了Line Status Register里面的Data Ready bit, 如果該為零, 則返回失敗. 但這里是有問(wèn)題的,具體也沒(méi)太想明白, 反正在調(diào)試debug serial的時(shí)候就把這個(gè)判斷從MS提供的源碼里頭刪去了,現(xiàn)在做KITL serial時(shí)又手癢加進(jìn)去, 果然還是不行. 這可能是MS或INTEL的一處BUG, 但按照INTEL CPU MANUL UPDATE里面給的讀流程, 一開(kāi)始只判斷LSR里面的ERROR, 沒(méi)有判斷DR位就開(kāi)始讀第一個(gè)BYTE了. 讀過(guò)后再判斷如果DR位為1,則繼續(xù)讀下一BYTE.
5. 在receive函數(shù)里加通過(guò)debugger加break point, 結(jié)果receive buffer register里面的數(shù)據(jù)被debugger掃描去以后,就變零了,CPU上卻什么都收不到, 這事情耗了大半個(gè)下午,最后還是Jeffery發(fā)現(xiàn)的這個(gè)問(wèn)題.
參考文章:
KITL解析 by Nasiry? (http://nasiry.cnblogs.com/archive/2004/09/22/45473.html)