[WinCE]KITL 流程

先解釋一下KITL(Kernel Independent Transport Layer),KITL的目的在於將硬體層和通信協議層分開,開發商只要根據相應的API實現控制通信硬體的code就可以實現KITL。相關說明請參考 基於Windows CE的KITL技術

本文參考自 http://www.cppblog.com/milkyway/articles/17671.html

解讀WINCE 5.0 KITL代碼流程

作者:Walzer
日期:2005.3.19
摘要:KITL是PLATFORM BUILDER中的一個亮點,提供了和本地調試類似的斷點、變量跟蹤、內存查看等手段,如果沒有KITL,嵌入式調試應該只能用串口打印消息來看了,工作效率大大下降。本文以實現最簡單的SERIAL KITL為目的,就其實現代碼進行跟蹤調試,這些代碼跨越了WINCE的PLATFORM、PUBLIC、PRIVATE三大主要目錄,有些煩瑣,不過只要能調通,一切工作和彎路都是值得的。我把調試經驗和個人理解寫下來,希望能幫助別人少走彎路。如果文章中有理解失當的地方,請不吝賜教。
正文:
一.void OEMInit()  [platformprojectsrckerneloalinit.c]
首先從OEMInit()函數看起。。在依次初始化Branch-Target Buffer、KERNEL函數、初始化中斷、TIMER之後,就輪到KITL了。調用了這個函數OALKitlStart()。此時有個編譯的分支,如果是RELEASE版本,那麼在kernelkernstubs.c裡面的OALKitlStart()函數是個STUB,只是return TRUE; 如果是DEBUG版本,那就進到kerneloalkitl.c裡面的OALKitlStart().

二.BOOL OALKitlStart()  [platformmyprojectsrckerneloal]
在OALKitlStart()裡面,首先試圖從0xA00FF00處讀取bootloader裡面留下的kitl參量,如果讀不到東西則使用該函數裡的默認配置。由於原來用了ethernet同時作為download和kitl途徑,所以在InitSpecifiedEthDevice函數裡給 pKitlArgs結構體賦值了。現在想把兩者劃分清楚,首先就把讀取bootloader裡面的kitl參量一句幹掉,強迫使用我們在下面的默認配置。主要就是填充三個參量

1.首先是這個結構體
OAL_KITL_ARGS pKITLArgs
{
UINT32 flags;  //設好ENABLED標誌,按需要設POLL標誌,但注意一定不要設PASSIVE標誌
DEVICE_LOCATION  devLoc;
{
DWORD  IfcType;  //不論ether還是serial,都是internal type =0;
DWORD  BusNumber; // =0
DWORD  LogicalLoc; //物理地址
PVOID     PhysicalLoc; //留做後面=OALPAtoVA(LogicalLoc, FALSE). 真見鬼, 感覺應該和上面的LogicalLoc作用調過來看著比較順吧?
DWORD  Pin;  //Ethernet才用的東東
}
union
{
struct
{
UINT32  baudRate; //不用解釋了
UINT32  dataBits;
UINT32  stopBits;
UINT32  parity;
}
struct
{
UINT16  mac[3]; //這個也不用解釋了
UINT32  ipAddress;
UINT32  ipMask;
UINT32  ipRoute
}
}
}
2. pszDeviceID.  感覺這名字就是起了好玩, 賦成Walzer應該比較拽,不過還是保留原來賦的AMOISOFT好了, 免得被打.
3. 全局變量OAL_KITL_DEVICE g_kitlDevices. 這個東東在kitl.c開頭包含的kitl_cfg.h中被賦值,  最主要就是修改g_kitlDevices.pDriver.  這個pDrvier指向一個函數指針列表的結構體,該列表定義了用做kitl模塊的初始化、讀寫、中斷、流控制等函數。 g_kitlDevices本身是個二維數組, 可以定義許多設備用做kitl時提供的參數設置, 後面會用一個for來循環判斷pKITLArgs的參數和g_kilDevices裡面哪個一維數組成員相匹配.

這三個參量填充好以後,就可以進到OALKitlInit(pszDeviceID, pKITLArgs, g_kitlDevices)裡面了.

三.BOOL OALKitlInit( deviceId, pArgs, pDevice)    [platformcommonsrccommonkitlkitl.c]
這個函數先把輸入的參量全部用OALMSG打印出來,這個不管。
重要的是引入了g_kitlState全局變量,開頭一句
g_kitlState.pDevice = OALKitlFindDevice(&pArgs->devLoc, pDevice) 這個就是上面所說的從g_kitlDevices裡可用設備列表裡循環判斷,找到選用的設備的匹配函數指針。
接著把輸入參量devicdId和前面填充好的OAL_KITL_ARGS結構COPY到g_kitlState裡面
然後就可以調用KItlInit(TRUE)了,如果前面在FLAG裡面設了PASSSIVE標誌,現在就是KitlInit(FALSE)了,嘿嘿爽到了吧。

四.BOOL KitlInit(BOOL fStartKitl)    [privatewinceoscoreosnkkitlethdbg.c]
太猥瑣了,我要用串口,它居然叫ethdbg.c,不給面子。不過是private裡面的東東,可遠觀而不可褻玩焉~~
這個函數幹了三件事:
1. 裝載了三個全局的函數指針
2. 用NewClient註冊了三個KITL客戶端:
KITL_SVCNAME_DBGMSG   //debug message, Debug信息發佈通道
KITL_SVCNAME_PPSH  //PPshell, 文本控制台界面
KITL_SVCNAME_KDBG //kernel debug, 內核調試界面

3.由fStartKitl來決定是否啟動StartKitl()函數. (這裡順便提一下,按照匈牙利命名法, BOOL變量前面加個b, 但MS的做法我覺得很合理, BOOL變量就是個FLAG嘛, 前面加個f, 把小b留著給BYTE類型用.)

五.static BOOL StartKitl(BOOL fInit)    [privatewinceoscoreosnkkitlethdbg.c]
這又是prviate裡面的東東。最痛苦的地方開始了。這函數及其子函數將第一次調用OEM自己寫的KITL模塊初始化、讀寫程序。是騾子是馬,拉出來溜溜就知道啦~
按順序看下來,首先判斷輸入參量是否要啟動KITL,並且如果KITLGlobalState裡面被打上KITL_ST_DESKTOP_CONNECTED標記的話,那下面的步驟就全免了。當然我們是第一次運行到這裡,若這麼就跳出的話,俺就馬加爵了。

第一步:
干的第一件正事就是調用OEMKitlInit(&Kitl). 這個後面詳述. 繼續把這個函數看完.
OEMKitlInit初始化KITL的硬件抽象層後並把相關指針數據填充給全局變量KITLTRANSPORT Kitl (有沒有搞錯,為什麼不叫g_kitl), 這些工作做完就返回了
StartKitl收貨後把Kitl結構整個檢查一遍,保證沒錯後, 馬上買單, 把全局變量KITLGlobalState打上個KITL_ST_KITLSTARTED標記. 這才算KITL啟動的第一步OK了.

第二步:
接下來就是KITLConnectToDesktop(), 進這個函數後就是第一次使用了前面KITL傳輸介質硬件抽象層裡的讀寫函數了, 這時候就需要調試了. 這個ConnectToDesktop大致就是先Send了一個kITL…..的frame過去,然後polling等待PC端的response, 那邊再發個kITL…..的frame過來, 搞得跟地下黨打暗號似的, 其實也沒什麼玄乎的,就是普通的數據包前面加個KTIL專用的HEADER而已. 這個CONNECT成功後,KITLGlobalState裡面就加個KITL_ST_DESKTOP_CONNECTED標記了.

第三步:
set up kernel function pointers, 也沒什麼,就三個函數指針, 賦完後就KITL_ST_ADAPTER_INITIALIZED了. 其實這個KITLGolbalSate的總共有7個標誌,分別是
KITL_STARTED,  (OK)
DESKTOP_CONNECTED,   (OK)
TIMER_INIT, (?)
INT_ENABLED,  (POLLING)
IST_STARTED,  (POLLING)
MULTITHREADED,   (?)
ADAPTER_INITIALIZED. (OK)
後面括號裡打上OK是到這裡已經完成的, 打問號的我還不太清楚什麼作用, INT和IST兩項,我們用的POLLING所以肯定是不需要了.

第四步:
調用SetKernelCommDev設置kernel通過何種介質傳送DBGMSG, PPSH和KDBG.
OHYEAH, 我的SERIAL KITL就夭折在這裡. 進到SerKernelCommDev(service, CommDevice)函數裡看, 它只認CommDevice=KERNEL_COMM_ETHER的情況,而屏蔽了與ETHER並列的SERIAL和PARALLER,直接return FALSE, 下面的事情都不用幹了. 而在MS提供的WinCE Documantation裡面,這個SetKernelCommDev函數的說明上寫著"This function is obsolete and should not be used". 若想改嘛,這個是在private裡面的還動它不得. NND, 感覺被MS raped了.
如果使用ETHER在這裡成功的話, 下面還有兩個函數NKForceCleanBoot()和KITLInitializeInterrupt()走過去, 這KITL初始化就全部結束了. 我估計KITLGolbalSate裡面的INIT_ENABLED和IST_STARTED就是在這個函數過程中被標記上的.
六.BOOL OEMKitlInit(PKITLTRANSPORT pKitl)    [platformcommonsrccommonkitlkitl.c]
前面提到StartKItl起來後,首要的就是調用OEMKitlInit. 這個函數在WinCE4.2和5.0里差別很大, 4.2里的做法是    if (!InitEther (pKitl) && !InitParallelSerial (pKitl)), 把ETHER, SERIAL, PARALLEL都初始化了一遍,碰運氣看哪個用得上,而5.0里是進來後就一個很明顯的分支劇情,由 g_kitlState.pDevice->type來決定是調用OALKitlEthInit還是OALKitlSerialInit. 典型的種族歧視, 居然沒有OALKitlParallelinit. 還好我們用的是SERIAL.
這裡有個選擇編譯的地方,就是#ifdef KITL_ETHER和#ifdef KITL_SERIAL, 具體定義的地方是該目錄下的sources文件裡面一行 CDEFINES=$(CDEFINES) -DKITL_SERIAL -DKITL_ETHER, 猥瑣啊找了半天. 其實我覺得既然有if結構來選了,那麼選擇編譯也是可有可無的了.
好,下面就進到OALKItlSerialInit()裡面.

七.BOOL OALKitlSerialInit(LPSTR deviceId, OAL_KITL_DEVICE *pDevice, OAL_KITL_ARGS *pArgs, KITLTRANSPORT *pKitl)
[platformcommonsrccommonkitlkitlserial.c]
我自己往這個kitlserial.c文件裡寫了六個函數.
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等都沒有, 所以FlowControl自然也應該是STUB了
void KitlSerialEnableInt(void)   //stub, use polling
void KitlSerialDisableInt(void)  //stub, use polling
否則前面的g_kitlDevices裡面沒有相應的硬件抽象層來填充.
上面的SerialRecv, Encode, Decode等就意思都很明顯了,不用多說. OK現在已經走到最底層了, 文章也可以結束了.

八、記錄一下調試經驗
雖然這是我第三次調串口了,由於沒總結前面的經驗,還是耗了兩天才到private裡面夭折的地方。實際上應該一天就能走到了。問題出在
1. 第一天調試器根本用不上手。調試器軟件、PB不斷死翹,經常重啟軟件甚至重啟電腦,第一天有3/4以上的時間耗在這些問題上, 不斷重啟。
2. 在UART初始化函數的最後,居然忘記了在interrupt controller register裡面enable uart unit, 這麼烏龍的事。
3. KitlSerialFlowControl的問題. 寫的時候照搬了X86下的函數, 沒想明白到底要Control什麼. 調試時死在這裡後,  一開始把指向這個函數的指針設置成NULL, 但這樣PRIVATE裡面有些要IF判斷的函數就進不去了. 後來換成STUB就OK了.
4. receive函數裡面, 在收每個BYTE之前先去判斷了Line Status Register裡面的Data Ready bit, 如果該為零, 則返回失敗. 但這裡是有問題的,具體也沒太想明白, 反正在調試debug serial的時候就把這個判斷從MS提供的源碼裡頭刪去了,現在做KITL serial時又手癢加進去, 果然還是不行. 這可能是MS或INTEL的一處BUG, 但按照INTEL CPU MANUL UPDATE裡面給的讀流程, 一開始只判斷LSR裡面的ERROR, 沒有判斷DR位就開始讀第一個BYTE了. 讀過後再判斷如果DR位為1,則繼續讀下一BYTE.
5. 在receive函數裡加通過debugger加break point, 結果receive buffer register裡面的數據被debugger掃瞄去以後,就變零了,CPU上卻什麼都收不到, 這事情耗了大半個下午,最後還是Jeffery發現的這個問題.
參考文章:
KITL解析 by Nasiry  (http://nasiry.cnblogs.com/archive/2004/09/22/45473.html)

關於KITL的Packet layout : http://blogs.gotdotnet.com/ce_base/archive/2006/06/27/648747.aspx

About these ads

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 變更 )

Twitter picture

You are commenting using your Twitter account. Log Out / 變更 )

Facebook照片

You are commenting using your Facebook account. Log Out / 變更 )

Google+ photo

You are commenting using your Google+ account. Log Out / 變更 )

連結到 %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: