?---------------by nasiry??
?????來自
http://www.cnblogs.com/nasiry/archive/2004/09/22/45473.aspx????????????????????????????????
---------------------------------
注:由于我們主要是分析kitl的工作原理我們就電源管理的代碼不做分析,以加電啟動的程序流進行分析。
part1.
kitl初始化
-------------
Kitl的加載于其他調試服務之前,以提供為這些調試服務發布調試信息和接收主機調試命令的的通道。通常kitl在系統HAL初始化工作完成后進行加載,MS建議在OEMInit中啟動kitl。這樣就可以使用NIC或者是serial/Pal作為kitl的物理傳輸介質。
kitl的初始化由KitlInit完成,這部分代碼主要負責:(to be fill later)
下面我們來看看kitl的具體代碼,這些代碼位于%CEROOT%\PRIVATE\WINCEOS\COREOS\NK\KITL下。
BOOL KitlInit (BOOL fStartKitl)
{
??? // just initialize function pointers
??? pKITLRegisterDfltClient? = KITLRegisterDfltClient;
??? pKITLIoCtl = KITLIoctl;
??? pfnIsDesktopDbgrExist = IsDesktopDbgrExist;
??? // Initialize default clients
??? NewClient (KITL_SVC_DBGMSG, KITL_SVCNAME_DBGMSG, FALSE);
??? NewClient (KITL_SVC_PPSH,?? KITL_SVCNAME_PPSH,?? FALSE);
??? NewClient (KITL_SVC_KDBG,?? KITL_SVCNAME_KDBG,?? FALSE);
??? return fStartKitl? StartKitl (TRUE) : TRUE;
}
這段代碼主要完成兩個動作:
1.裝載函數指針,為后續代碼的執行裝載入口點。
2.注冊kitl客戶端,這些客戶端實現傳輸層以后就是我們所需要的調試界面。
輸入參數決定是否立即啟動KITL服務,如果false的話就僅僅進行初始化等待后續動作使用startKitl來啟動kitl.
我們再來看看NewClient的原型
static PKITL_CLIENT NewClient (UCHAR uId, LPCSTR pszSvcName, BOOL fAlloc)
{
??? DEBUGCHK(IS_VALID_ID(uId));
??? DEBUGCHK (!KITLClients[uId]);
??? if (!fAlloc) {
??????? DEBUGCHK(IS_DFLT_SVC(uId));
??????? KITLClients[uId] = &DfltClnts[uId];
??? } else if (!(KITLClients[uId] = (PKITL_CLIENT) AllocMem (HEAP_KITLCLIENT))) {
??????? return NULL;
??? }
??? memset (KITLClients[uId], 0, sizeof(KITL_CLIENT));
??? KITLClients[uId]->ServiceId = uId;
??? strcpy (KITLClients[uId]->ServiceName, pszSvcName);
??? return KITLClients[uId];
}
這個被稱為NewClient的函數所完成的功能十分的簡單,先檢查所需要創建的結構是否是系統默認服務所需要的,如果是的話就直接將該結構的指針指向全局結構DfltClnts并初始化結構,如果不是就申請相應的空間完成該結構的初始化。默認的服務有 KITL_SVCNAME_DBGMSG, KITL_SVCNAME_PPSH, KITL_SVCNAME_KDBG分別對應Debug信息的發布通道(Debug message),文本控制臺界面(PPshell),和內核調試界面(kernel debug),在這里大家可能會問:為什么不統一使用固定的全局結構來存放這些服務的信息呢?原因很簡單,因為這些"client"在WindowSCE下是可以注冊擴充和注銷的,這樣用AllocMem所分配的內存空間在不再需要這些服務的時候可以釋放掉,就可以避免不必要的浪費。另外KITLClients是這樣定義的PKITL_CLIENT KITLClients[MAX_KITL_CLIENTS];所以kitl所能注冊的client連同3個默認的服務一共最多可以有MAX_KITL_CLIENTS--128個。
下面繼續沿著程序流往下看吧,kitlInit完成最基本的初始化動作即可啟動kitl服務了。再看一下這個函數的原型。
static BOOL StartKitl (BOOL fInit)
{
??? // KITL already started?
??? if (!fInit && (KITLGlobalState & KITL_ST_DESKTOP_CONNECTED)) {
??????? return TRUE;
??? }
??? /*
???? * When this function is called, the kernel hasn't yet been initialized,
???? * so can't make any system calls.? Once the system has come up far
???? * enough to handle system calls, KITLInitializeInterrupt() is called to complete
???? * initialization.? This is indicated by the KITL_ST_MULTITHREADED flag in KITLGlobalState.
???? */
??? // Detect/initialize ethernet hardware, and return address information
??? if (!OEMKitlInit (&Kitl))
??????? return FALSE;
??? // verify that the Kitl structure is initialized.
??? if (!Kitl.pfnDecode || !Kitl.pfnEncode || !Kitl.pfnEnableInt || !Kitl.pfnRecv || !Kitl.pfnSend
??????? || !Kitl.dwPhysBuffer || !Kitl.dwPhysBufLen || !Kitl.WindowSize || !Kitl.pfnGetDevCfg || !Kitl.pfnSetHostCfg) {
??????? return FALSE;
??? }
??? // Validate that address is not in free RAM area - the HAL should put it in a reserved
??? // section of memory conditional on some environment var.
??? if ((pTOC->ulRAMStart < Kitl.dwPhysBuffer + Kitl.dwPhysBufLen)
??????? && (pTOC->ulRAMEnd > Kitl.dwPhysBuffer)) {
??????? KITLOutputDebugString("\r\n!Debug Ethernet packet buffers in free RAM area - must set IMGEBOOT=1\r\n");
??????? return FALSE;
??? }
??? if (Kitl.dwPhysBufLen < (DWORD) 3 * KITL_BUFFER_POOL_SIZE) {
??????? KITLOutputDebugString("\r\n!Debug Ethernet buffer size too small, must be at least 0x%x bytes (3 * WindowSize * 2 * KITL_MTU)\r\n",
??????????? 3 * KITL_BUFFER_POOL_SIZE);
??????? return FALSE;
??? }
??? KITLGlobalState |= KITL_ST_KITLSTARTED; // indicate (to kdstub) that KITL has started
??? // If the initialized flag is already set, we are being called from the power on routine,
??? // so reinit the HW, but not any state.
??? if (!(KITLGlobalState & KITL_ST_ADAPTER_INITIALIZED)) {
??????? // perform the initial handshake with the desktop
??????? if (!KITLConnectToDesktop ()) {
??????????? KITLOutputDebugString ("\r\n!Unable to establish KITL connection with desktop!\r\n");
??????????? return FALSE;
??????? }
???????
??????? // Set up kernel function pointers
??????? pKITLInitializeInterrupt = KITLInitializeInterrupt;
??????? pKITLSend = KITLSend;
??????? pKITLRecv = KITLRecv;
??????? KITLGlobalState |= KITL_ST_ADAPTER_INITIALIZED;
??????? if (Kitl.dwBootFlags & KITL_FL_DBGMSG)
??????????? SetKernelCommDev (KERNEL_SVC_DBGMSG, KERNEL_COMM_ETHER);
??????? if (Kitl.dwBootFlags & KITL_FL_PPSH)
??????????? SetKernelCommDev (KERNEL_SVC_PPSH, KERNEL_COMM_ETHER);
??????? if (Kitl.dwBootFlags & KITL_FL_KDBG)
??????????? SetKernelCommDev (KERNEL_SVC_KDBG, KERNEL_COMM_ETHER);
??????? // only perform cleanboot if it's connected at boot. Cleanboot flag is
??????? // ignored if it's started dynamically.
??????? if (fInit && (Kitl.dwBootFlags & KITL_FL_CLEANBOOT)) {
??????????? extern ROMHDR *const volatile pTOC;???? // Gets replaced by RomLoader with real address
??????????? // just clear the magic nOEMKitlInitumber (see SC_SetCleanRebootFlag)
??????????? // NOTE: We can NOT call SC_SetCleanRebootFlag here since logPtr isn't
??????????? // initialized yet.
??????????? ((fslog_t *)((pTOC->ulRAMFree + MemForPT) | 0x20000000))->magic1 = 0;
??????? }
??????? // if OEM calls KitlInit (FALSE), KITLInitializeInterrupt will
??????? // not be called in SystemStartupFuc. We need to initialize
??????? // interrupt here (when RegisterClient is called)
??????? if (fKITLcsInitialized && !InSysCall ()) {
??????????? KITLInitializeInterrupt ();
??????? }
??? }
??? LOG (KITLGlobalState);
??? return TRUE;
}
啟動代碼首先判斷是否已經啟動kitl服務,之后調用OEMKitlInit,該函數并不在private目錄下實現,通常windowsCE需要用戶定制的代碼都是這種結構---MS提供的代碼接口,用戶自己完成相應的OEM部分,通常這些代碼都是與具體的硬件平臺相關的代碼。kitl的OEM代碼在HAL中實現,通常在platform\kernel\hal\.下,這部分的代碼我們先跳過,看完startkitl的全貌再回過頭逐個說明。OEMkitlInit為kitl初始化硬件傳輸介質,同時分配初始化一些kitl所需要的全局結構。隨后startkitl繼續檢查OEMkitlInit所分配和初始化的KITL結構和內存區域是否有效后設置kitl的全局標示KITL_ST_KITLSTARTED;之后設置終端服務程序以及接收和發送程序的入口點后設置全局標示KITL_ST_ADAPTER_INITIALIZED。現在傳輸介質已經全部就緒,通過SetKernelCommDev設置kernel通過ethernet傳送調試信息,調試輸入,以及CESH控制臺。再后調用KITLInitializeInterrupt完成中斷的初始化kitl啟動的過程就結束了。
?? 緊接著我們來看看,OEMkitlInit都須要我們干什么。下面用SMDK2440的kitl為實例來進行分析:
BOOL OEMKitlInit (PKITLTRANSPORT pKitl)
{
??? KITLOutputDebugString ("+OEMKitlInit\n");
??? RETAILMSG(1, (_T("+OEMKitlInit\r\n")));
??? // try to find a transport available
??? if (!InitEther (pKitl)
??????? && !InitParallelSerial (pKitl)) {
??????? KITLOutputDebugString ("Unable to initialize KITL Transports!\n");
??????? return FALSE;
??? }
??? gpKitl = pKitl;
??? KITLOutputDebugString ("-OEMKitlInit\n");
??? RETAILMSG(1, (_T("-OEMKitlInit\r\n")));
??? return TRUE;
}
事實上工作很簡單,調用InitEther (pKitl) 和 !InitParallelSerial (pKitl)初始化網卡直接把初始化的KITL全局結構返回就是所有的工作。這兒的InitParallelSerial是一個dummy永遠返回false,也就是說這里沒有對serial¶llel transport進行支持。真正的工作量集中在InitEther之后。事實上InitEther 和 InitParallelSerial只要任意的實現一個就可以達到建立傳輸界面的目的.下面,我們繼續看后面的代碼。
BOOL InitEther(PKITLTRANSPORT pKitl)
{
??? EDBG_ADAPTER adp;
??? DWORD dwDHCPLeaseTime;
??? DWORD dwSubnetMask;
??? KITLOutputDebugString ("+InitEther\n");
??? memset (&adp, 0, sizeof(adp));
??? memset (pKitl, 0, sizeof (KITLTRANSPORT));
??? // use existing code for ether initialization
??? if (!OEMEthInit (&adp))
??????? return FALSE;
??? // we are going to completely ignore the info in bootargs and the adaptor info
??? // returned from OEMEthInit, except MAC address. Just to prove that KITL will connect standalone
??? // get the MAC address
??? MyAddr.wMAC[0] = adp.Addr.wMAC[0];
??? MyAddr.wMAC[1] = adp.Addr.wMAC[1];
??? MyAddr.wMAC[2] = adp.Addr.wMAC[2];
??? //MyAddr = adp.Addr;
???
??? CreateDeviceName(&MyAddr, pKitl->szName);
??? KITLOutputDebugString ("Using device name: %s\n", pKitl->szName);
??? // If we haven't been given an IP address from our loader (or if we're not using static IP), get an IP address
??? // from a DHCP server.
??? if (adp.Addr.dwIP)
??? {
??????? // Static IP or we got the IP from our bootloader...
??????? MyAddr.dwIP???? = adp.Addr.dwIP;
??????? dwSubnetMask??? = 0;??? // Don't care about subnet mask...
??????? dwDHCPLeaseTime = adp.DHCPLeaseTime;
??? }
??? else
??? {
??????? // Get a DHCP address...
??????? if (!EbootGetDHCPAddr (&MyAddr, &dwSubnetMask, &dwDHCPLeaseTime))
??????????? return FALSE;
??? }
???
??? MyAddr.wPort = htons (EDBG_SVC_PORT);
??? KITLOutputDebugString ("Device %s, IP %s, Port %d\n", pKitl->szName, inet_ntoa (MyAddr.dwIP), htons (MyAddr.wPort));
??? // initialize KITL Ethernet transport layer
??? if (!KitlEtherInit (&MyAddr, dwDHCPLeaseTime)) {
??????? KITLOutputDebugString ("Unable to initialize KITL Ether transport\n");
??????? return FALSE;
??? }
???
??? // fill in the blanks in KITLTRANSPORT structure.
??? pKitl->FrmHdrSize = KitlEtherGetFrameHdrSize ();
??? pKitl->Interrupt = (UCHAR) adp.SysIntrVal;
??? pKitl->dwPhysBuffer = EDBG_PHYSICAL_MEMORY_START;
??? pKitl->dwPhysBufLen = 0x20000;????????????????????? // 128K of buffer available
??? pKitl->dwBootFlags = 0;
??? pKitl->WindowSize = EDBG_WINDOW_SIZE;
??? pKitl->pfnDecode = KitlEtherDecodeUDP;
??? pKitl->pfnEncode = KitlEtherEncodeUDP;
??? pKitl->pfnSend = EthSend;
??? pKitl->pfnRecv = OEMEthGetFrame;
??? pKitl->pfnEnableInt = KitlEthEnableInts;
??? pKitl->pfnSetHostCfg = SetHostCfg;
??? pKitl->pfnGetDevCfg = GetDevCfg;
??? KITLOutputDebugString ("-InitEther\n");
??? return TRUE;
}
這個函數完成的工作主要是調用OEMEthInit初始化網卡的服務程序及獲得相應的IP和MAC,如果IP無效則用DHCP動態獲得IP.通過MAC值產生一個標示,這個標示用來給PB的IDE使用。剛才的我們在kitlInit中看到除了檢查OEMkitlInit的返回值之外還檢查了KITL結構,該結構的這些特征值正是在這兒設置的。在這兒可以看到pKitl->pfnDecode pKitl->pfnEncode pKitl->pfnSetHostCfg pKitl->pfnGetDevCfg 以及kitl所用的中斷號這些都是OEM代碼,也就是用于傳輸的編碼和解碼形式以及配置函數都是可以自己定義的,這樣一來也就無所謂使用什么傳輸介質作為KITK
的transport了,這就為使用1394或者是USB這一類的傳輸鏈路也能充當傳輸界面作了準備。 OEMEthInit函數是用于初始化傳輸介質--以太網卡。這部分代碼直接是硬件控制代碼,我們來簡單的看一下。
BOOL
OEMEthInit(EDBG_ADAPTER *pAdapter)
{
?PBYTE pBaseIOAddress;
?// Driver globals from the bootloader.
?//
?if (pDriverGlobals->eth.EbootMagicNum == EBOOT_MAGIC_NUM)
?{
??memcpy(pAdapter, &pDriverGlobals->eth.TargetAddr, sizeof(EDBG_ADAPTER));
??switch(pDriverGlobals->misc.EbootDevice)
??{
??case(DOWNLOAD_DEVICE_PCMCIA):?// NE2000 CF card.
???pBaseIOAddress? = (PBYTE)PCMCIA_Init();
???if (pBaseIOAddress)
???{
????// Initialize the built-in Ethenet controller.
????//
????if (!NE2000Init((PBYTE)pBaseIOAddress, 1, pAdapter->Addr.wMAC))
????{
?????EdbgOutputDebugString("ERROR: OEMEthInit: Failed to initialize Ethernet controller.\r\n");
?????return(FALSE);
????}
???}
???pfnEDbgInit?????????? ?= NE2000Init;
???pfnEDbgEnableInts???? ?= NE2000EnableInts;
???pfnEDbgDisableInts??? ?= NE2000DisableInts;
???pfnEDbgGetPendingInts ?= NE2000GetPendingInts;
???pfnEDbgGetFrame?????? ?= NE2000GetFrame;
???pfnEDbgSendFrame????? ?= NE2000SendFrame;
???pfnEDbgReadEEPROM???? ?= NE2000ReadEEPROM;
???pfnEDbgWriteEEPROM??? ?= NE2000WriteEEPROM;
???pfnEDbgSetOptions???? ?= NE2000SetOptions;
??#ifdef IMGSHAREETH
???pfnCurrentPacketFilter?= Ne2000CurrentPacketFilter;
???pfnMulticastList??= NE2000MulticastList;
??#endif?// IMGSHAREETH.
???break;
??case(DOWNLOAD_DEVICE_CS8900):?// CS8900A.
???// Initialize the CS8900.
???//
???if (!CS8900DBG_Init((PBYTE)CS8900DBG_IOBASE, CS8900DBG_MEMBASE, pAdapter->Addr.wMAC))
???{
????EdbgOutputDebugString("ERROR: OEMEthInit: CS8900 initialization failed.\r\n");
????return(FALSE);
???}
???pfnEDbgInit??= CS8900DBG_Init;
???pfnEDbgEnableInts???? ?= CS8900DBG_EnableInts;
???pfnEDbgDisableInts??? ?= CS8900DBG_DisableInts;
???pfnEDbgGetFrame??= CS8900DBG_GetFrame;
???pfnEDbgSendFrame?= CS8900DBG_SendFrame;
???pfnEDbgGetPendingInts ?= CS8900DBG_GetPendingInts;
??#ifdef IMGSHAREETH
???pfnCurrentPacketFilter?= CS8900DBG_CurrentPacketFilter;
???pfnMulticastList?= CS8900DBG_MulticastList;
??#endif?// IMGSHAREETH.
???break;
??default:
???EdbgOutputDebugString("ERROR: OEMInit: Unknown download NIC (0x%x).\r\n", pDriverGlobals->misc.EbootDevice);
???return(FALSE);
?
?}}
?else
?{
??// TODO - retrieve CS8900 MAC address from flash...
??// TODO - intialize the CS8900 from scratch...
?}
?EdbgOutputDebugString("::: OEMEthInit() IP Address : %s\r\n", inet_ntoa(pAdapter->Addr.dwIP));
?EdbgOutputDebugString("::: OEMEthInit() Netmask??? : %s\r\n", inet_ntoa(pDriverGlobals->eth.SubnetMask));
?if (pDriverGlobals->misc.EbootDevice == DOWNLOAD_DEVICE_PCMCIA)
??pAdapter->SysIntrVal??? = SYSINTR_PCMCIA_LEVEL;
?else
??pAdapter->SysIntrVal??? = SYSINTR_ETHER;
?pAdapter->DHCPLeaseTime = DEFAULT_DHCP_LEASE;
?pAdapter->EdbgFlags???? = pDriverGlobals->eth.EdbgFlags;
??
#ifdef IMGSHAREETH
??? VBridgeInit();
??? VBridgeKSetLocalMacAddress((char *)pAdapter->Addr.wMAC);
#endif?// IMGSHAREETH.
?return(TRUE);
}
? ?
這個函數看起來很復雜其實真正的工作并不多,首先判斷是不是由eboot啟動的,如果已經eboot中已經完成了對以太網卡的初始化動作就直接使用網卡并裝載/掛接網卡所需的函數和網卡信息,否則就需要自己設置網卡的MAC地址和初始化網卡(事實上以上函數并沒有對這部分代碼進行實現,這就是很多使用2410/2440的用戶在不使用eboot啟動的情況下總是不能使用kitl的原因--找不到eboot在DriverGlobal中留下的magic NUMBER)。這兒之所以有NE2000和Cs8900的區分是因為SMDK2440可以使用PCMICA掛接Ne2000兼容的NIC或板載CS8900,后面設置中斷標示有兩個分支也是這個原因。
IMGSHAREETH的部分是Vbridge的部分,為什么要使用這個叫vbridge的東西呢?我們看看下面的假設。
為了建立kitl占用了一個網卡資源,而該資源如果在windowsCE下復用(該設備同時被兩個驅動使用1.kitl 2.windowsCE NIC driver)的話會不會導致問題呢?看看下面的兩個函數。
??? VBridgeInit();
??? VBridgeKSetLocalMacAddress((char *)pAdapter->Addr.wMAC);
??? 該函數在內核調試傳輸通道和tcp/ip&windsock之間建立一個虛擬的網橋v-bridge,在外部看來vbridge就像在mac層一樣,vbridge一方面和硬件通訊建立傳輸的物理界面,另一方面和調試所需的EDBG和vmini.dll提供相同的邏輯界面,在更高的層面vmini.dll就像一個專門的網卡一樣支持NDIS以至于tcp/ip協議棧。這樣我們就可以一方面使用網卡做調試另外一方面仍然能讓windowsCE使用網卡通訊,對于windowCE而言所使用的網卡不在是與底層對應的網絡設備,而是通過vbridge虛擬出來的網絡設備,所以在直接使用SMDK24XX的bsp編譯出來的系統網卡顯示為vmini就是這個原因。這個時候網卡驅動怎么配置呢?答案很簡單,就是不要網卡驅動,因為我們已經從vbridge中抽象(虛擬---用一個網卡)出一個網卡了,原來的網卡驅動也就不在需要了。
???
從上面的OemKitlInit到InitEther都是OEM代碼,目的在于使Kitl與相應的transport的物理介質聯系起來,也就是構建kitl的硬件抽象層,在一個kitl初始化代碼中中只有這部分工作(OEM代碼)是必要的,其余的代碼直接使用MS編譯好的lib就可以了。盡管如此我們還是繼續看下面的代碼,雖然這對我們來說不是必須的不過對一個程序要有全面的認識,框架上的重要模塊都是需要了解和認識的。
看完了這一系列的OEM代碼,繼續看看StartKitl里面我們沒有看完的部分在設置啟動標示位之前做了2個檢查分別是檢查buffer的位置是否在編譯系統的時候預留下來以及是否有足夠的長度可用。這個內存區域不是動態分配的,而是在bsp的內存配置文件中指定并保留下來的(見bsp file目錄下的config.bib)。再下來進行一個KITLConnectToDesktop的動作,這個看名字就知道作用了。就是和PC連接。同樣看看代碼:
static BOOL KITLConnectToDesktop (void)
{
??? // we'll use the PpfsFmtBuf for send buffer since ppfs can't be started yet at this stage
??? //
??? PKITL_HDR pHdr = (PKITL_HDR) (PpfsFmtBuf + Kitl.FrmHdrSize);
??? PKITL_DEV_TRANSCFG pCfg = (PKITL_DEV_TRANSCFG) KITLDATA(pHdr);
??? USHORT cbData = sizeof (PpfsFmtBuf) - Kitl.FrmHdrSize - sizeof (KITL_HDR) - sizeof (KITL_DEV_TRANSCFG);
??? if (!Kitl.pfnGetDevCfg ((LPBYTE) (pCfg+1), &cbData))
??????? return FALSE;
??? memset (pHdr, 0, sizeof (KITL_HDR));
??? pHdr->Id = KITL_ID;
??? pHdr->Service = KITL_SVC_ADMIN;
??? pHdr->Cmd = KITL_CMD_TRAN_CONFIG;
??? cbData += sizeof (KITL_DEV_TRANSCFG);
??? pCfg->wLen = cbData;
??? pCfg->wCpuId = KITL_CPUID;
??? memcpy (pCfg->szDevName, Kitl.szName, KITL_MAX_DEV_NAMELEN);
??? cbData += sizeof (KITL_HDR);
??? return KitlSendFrame (PpfsFmtBuf, cbData)
??????? && KITLPollResponse (FALSE, ChkCnxDsktp, TranCnxDsktp, (LPVOID) cbData);
}
結構PKITL_HDR就是kilt的傳輸頭格式,而PKITL_DEV_TRANSCFG信息則是傳輸設備的設置。首先通過調用Kitl.pfnGetDevCfg得到傳輸設備的信息,Kitl.pfnGetDevCfg是函數指針,在對以太網卡初始化的時候指向OEM代碼中的GetDevCfg函數。通過這個函數得到設備信息(smdk2410的bsp中這兒返回的就是IP地址)。然后再繼續設置傳輸頭的標示,類型,命令等等信息,然后就是發送數據了,具體的動作就是調用KitlSendFrame。(To be continue...)
BOOL KitlSendFrame (LPBYTE pbFrame, WORD cbData)
{
??? if (!Kitl.pfnEncode (pbFrame, cbData)) {
??????? KITLOutputDebugString ("!KitlSendFrame: transport failed to encode the data frame\r\n");
??????? return FALSE;
??? }
??? return KitlSendRawData (pbFrame, (USHORT) (cbData + Kitl.FrmHdrSize + Kitl.FrmTlrSize));
}
這個函數首先調用KitlEtherEncodeUDP對數據幀進行編碼為UDP協議需要的格式。然后調用 KitlSendRawData將數據送出至PC.
BOOL KitlSendRawData (LPBYTE pbData, WORD wLength)
{
??? BOOL fRet;
??? if (!(KITLGlobalState & KITL_ST_MULTITHREADED) || InSysCall())
??????? fRet = Kitl.pfnSend (pbData, wLength);
??? else if (IsDesktopDbgrExist ())
??????? fRet = KCall((PKFN) Kitl.pfnSend, pbData, wLength);
??? else {
??????? EnterCriticalSection (&KITLKCallcs);
??????? fRet = Kitl.pfnSend (pbData, wLength);
??????? LeaveCriticalSection (&KITLKCallcs);
??? }
??? return fRet;
}
首先判定系統沒有在調度中且當前代碼在不可剝奪狀態狀態運行,通過EthSend調用OEMEthSendFrame將數據送出就完成了工作。另外兩個分支與我們分析的程序流沒有關系先放一下。
BOOL
OEMEthSendFrame(
??? BYTE *pData,???? // IN - Data buffer
??? DWORD dwLength)? // IN - Length of buffer
{
??? int retries = 0;
??? while (retries++ < 4) {
??????? if (!pfnEDbgSendFrame(pData, dwLength))
??{
#ifdef IMGSHAREETH
???ProcessVMiniSend();
#endif //IMGSHAREETH
??????????? return TRUE;
??}
??????? else
??????????? EdbgOutputDebugString("!OEMEthSendFrame failure, retry %u\n",retries);
??? }
??? return FALSE;
}
在發送數據幀的過程中專門有處理vMini發送的過程。由于kitl本身就不是很簡單我們以后后面再用專門的文章說明vbridge的工作過程。完成了發送,我們繼續下面的過程。
BOOL KITLPollResponse (BOOL fUseSysCalls, PFN_CHECK pfnCheck, PFN_TRANSMIT pfnTransmit, LPVOID pData)
{
??? DWORD dwLoopCnt = 0, dwLoopMax = MIN_POLL_ITER;
??? DWORD dwStartTime = CurMSec;
??? int?? nTimeMax = MIN_POLL_TIME;? // start with 1 sec
??? BOOL? fUseIter = FALSE, fUseTick = FALSE;
??? while (!pfnCheck (pData)) {
??????? //
??????? // if we've already connected with desktop, use the desktop
??????? // "Retransmit" package to determine if we need to retransmit
??????? //
??????? if (!(KITLGlobalState & KITL_ST_DESKTOP_CONNECTED)) {
??????????? if (fUseTick) {
??????????????? if ((int) (CurMSec - dwStartTime) > nTimeMax) {
??????????????????? // retransmit
??????????????????? if (!pfnTransmit (pData, fUseSysCalls))
??????????????????????? return FALSE;
??????????????????? dwStartTime = CurMSec;
??????????????????? if (nTimeMax < MAX_POLL_TIME)
??????????????????????? nTimeMax <<= 1;
??????????????? }
??????????? } else if (fUseIter || (dwStartTime == CurMSec)) {
??????????????? // if time isn't moving for a while, we'll
??????????????? // use iteration.
??????????????? if (dwLoopCnt ++ > dwLoopMax) {
??????????????????? if (!pfnTransmit (pData, fUseSysCalls))
??????????????????????? return FALSE;
??????????????????? if (dwLoopMax < MAX_POLL_ITER)
??????????????????????? dwLoopMax <<= 1;
??????????????????? dwLoopCnt = 0;
??????????????????? fUseIter = TRUE;
??????????????? }
??????????? } else {
??????????????? // time is moving, just use tick from here
??????????????? fUseTick = TRUE;
??????????? }
??????? }
??????? if (!KITLPollData(fUseSysCalls, pfnTransmit, pData)) {
??????????? return FALSE;
??????? }
??? }
??? return TRUE;
}
static BOOL TranCnxDsktp (LPVOID pParam, BOOL fUseSysCalls)
{
??? return KitlSendFrame (PpfsFmtBuf, (WORD) (DWORD) pParam);
}
這個函數的主體是一個循環,終止的條件是!pfnCheck (pData),這個函數是由前面傳遞進來的,返回值為KITLGlobalState & KITL_ST_DESKTOP_CONNECTED,也就是說在得到桌面連接之前是不會返回的也就是說啟動kitl以后不與桌面計算機連接windowsCE的是無法啟動的。由于從dwStartTime定義到dwStartTime == CurMSec的判定僅僅需要很少的時間就可以完成這段時間內CurMSec是不會被改寫的就可以通過循環進行超時檢查
并通過TranCnxDsktp重新發送數據,直到KITLPollData設置KITLGlobalState。
static BOOL KITLPollData(BOOL fUseSysCalls, PFN_TRANSMIT pfnTransmit, LPVOID pData)
{
??? LPBYTE pRecvBuf = PollRecvBuf;
???
??? if (fUseSysCalls && (KITLGlobalState & KITL_ST_MULTITHREADED)
??????? && !(pRecvBuf = _alloca(KITL_MTU))) {
??????? KITLOutputDebugString("!KITLPollData: STACK OVERFLOW!\r\n");
??????? return FALSE;
??? }
??? HandleRecvInterrupt(pRecvBuf, fUseSysCalls, pfnTransmit, pData);
??? return TRUE;
}
由于我們上面傳來的fUseSysCalls參數為false所以前一段檢查操作并不進行。直接調用 HandleRecvInterrupt。
void HandleRecvInterrupt(UCHAR *pRecvBuf, BOOL fUseSysCalls, PFN_TRANSMIT pfnTransmit, LPVOID pData)
{
??? WORD wLen = KITL_MTU;
??? BOOL fFrameRecvd;
??? // Receive data into buffer
??? do {
??????? if (!fUseSysCalls)
??????????? fFrameRecvd = Kitl.pfnRecv (pRecvBuf, &wLen);
??????? else if (IsDesktopDbgrExist ())
??????????? fFrameRecvd = KCall((PKFN) Kitl.pfnRecv, pRecvBuf, &wLen);
??????? else {
??????????? EnterCriticalSection (&KITLKCallcs);
??????????? fFrameRecvd = Kitl.pfnRecv (pRecvBuf, &wLen);
??????????? LeaveCriticalSection (&KITLKCallcs);
??????? }
??????? if (fFrameRecvd) {
??????????? ProcessRecvFrame (pRecvBuf,wLen,fUseSysCalls, pfnTransmit, pData);
??????????? wLen = KITL_MTU;
??????? }
??? } while (fFrameRecvd);
}
通過Kitl.pfnRecv調用pfnEDbgGetFrame指向的CS8900DBG_GetFrame讀取當前的數據幀送交ProcessRecvFrame處理。注意,這兒的pfnEDbgGetFrame并不是通過DMA或者是網卡傳來的中斷啟動的而是使用查詢的方法進行的,這就是為什么這兒并沒有啟動中斷仍然能夠使用以太網卡進行數據傳輸數據的原因。隨后,我們將計算機端送來的數據送交ProcessRecvFrame處理。
static BOOL ProcessRecvFrame(UCHAR *pFrame, WORD wMsgLen, BOOL fUseSysCalls, PFN_TRANSMIT pfnTransmit, LPVOID pData)
{
??? KITL_HDR *pMsg;
??? KITL_CLIENT *pClient = NULL;
??? BOOL fRet = TRUE;
??? UCHAR RxBufOffset;
??? WORD? wDataLen;
??? UCHAR ClientIdx;
??? // let the transport layer decode the frame
??? if (!(pMsg = (KITL_HDR *) Kitl.pfnDecode (pFrame, &wMsgLen))) {
??????? KITL_DEBUGMSG(ZONE_RECV, ("ProcessRecvFrame: Received Unhandled frame\n"));
??????? return FALSE;
??? }
??? // is it a valid KITL message?
??? if (pMsg->Id != KITL_ID) {
??????? KITL_DEBUGMSG(ZONE_WARNING,("KITL: Got unrecognized Id: %X\r\n",pMsg->Id));
??????? return FALSE;
??? }
???
??? // Validate length
??? if (wMsgLen < KITL_DATA_OFFSET) {
??????? KITL_DEBUGMSG(ZONE_WARNING,("KITL: Invalid length %u\n",wMsgLen));
??????? return FALSE;
??? }
??? if (KITLDebugZone & ZONE_FRAMEDUMP)
??????? KITLDecodeFrame("<<KITLRecv",pMsg, wMsgLen);
???
??? // Check for administrative messages
??? if (pMsg->Service == KITL_SVC_ADMIN)
??????? return ProcessAdminMsg(pMsg, wMsgLen, fUseSysCalls, pfnTransmit, pData);
??? // Service Id is index into KITLClients array
??? ClientIdx = pMsg->Service;
??? if (ClientIdx >= MAX_KITL_CLIENTS) {
??????? KITL_DEBUGMSG(ZONE_WARNING,("!ProcessKITLMsg: Invalid ServiceId: %u\n",pMsg->Service));
??????? return FALSE;
??? }
??? pClient = KITLClients[ClientIdx];
???
??? // Until we complete registering, only handle administrative messages
??? if (!pClient || !(pClient->State & KITL_CLIENT_REGISTERED)) {
??????? KITL_DEBUGMSG(ZONE_WARNING,("!ProcessKITLMsg: Client %u not registered\n",ClientIdx));
??????? return FALSE;
??? }
??? if (pMsg->Service != pClient->ServiceId) {
??????? KITL_DEBUGMSG(ZONE_WARNING,("!ProcessKITLMsg: Mismatch in service Id for Client %u (Got %u, expect %u)\n",
??????????????????????????????????? ClientIdx,pMsg->Service,pClient->ServiceId));
??????? return FALSE;
??? }
??? if (pClient->State & KITL_USE_SYSCALLS) {
??????? if (fUseSysCalls)?
??????????? EnterCriticalSection(&pClient->ClientCS);
??????? else if (pClient->ClientCS.OwnerThread) {
??????????? // We can't get the client CS, and it is owned - just toss frame
??????????? KITL_DEBUGMSG(ZONE_WARNING,("!KITL(%u) tossing msg %u (Can't get CS)\n",ClientIdx, pMsg->SeqNum));
??????????? return FALSE;
??????? }
??? }
??? // we've being in sync with the desktop
??? pClient->State |= KITL_SYNCED;
???
??? // Put flags and seq # to LEDs
??? KITL_DEBUGLED(LED_PEM_SEQ, ((DWORD) pMsg->Flags << 24) | pMsg->SeqNum);
???
??? // OK, valid message, see if it's an ACK
??? if (pMsg->Flags & KITL_FL_ACK) {
??????? KITL_DEBUGMSG(ZONE_RECV,("KITL(%u): Received ack for msg %u, Tx window: %u,%u\n",
???????????????????????????????? ClientIdx,pMsg->SeqNum, pClient->AckExpected,pClient->TxSeqNum));
??????? // ACKs acknowledge all data up to the ACK sequence #
??????? while (SEQ_BETWEEN(pClient->AckExpected, pMsg->SeqNum, pClient->TxSeqNum)) {???????
??????????? if ((pClient->State & KITL_USE_SYSCALLS) &&
??????????????? ((pClient->CfgFlags & KITL_CFGFL_STOP_AND_WAIT) ||
???????????????? (SEQ_DELTA(pClient->AckExpected, pClient->TxSeqNum) >= pClient->WindowSize-1) ||
???????????????? !(KITLGlobalState & KITL_ST_INT_ENABLED))) {
??????????????? if (fUseSysCalls)
??????????????????? SetClientEvent(pClient,pClient->evTxFull);
??????????????? else {
??????????????????? // Can't process message at this time...
??????????????????? KITL_DEBUGMSG(ZONE_WARNING,("!KITL(%u): Tossing ACK %u (Can't set event)\n",
??????????????????????????????????????????????? ClientIdx, pMsg->SeqNum));
??????????????????? return FALSE;
??????????????? }
??????????? }
??????????? // Stop retransmission timer.
??????????? TimerStop(pClient, (UCHAR)(pClient->AckExpected % pClient->WindowSize),fUseSysCalls);
??????????? SEQ_INC(pClient->AckExpected);
??????? }
??????? goto ProcessKITLMsg_exit;
??? }
??? // Handle NACKs - retransmit requested frame if it is in our Tx window
??? if (pMsg->Flags & KITL_FL_NACK) {
??????? KITL_DEBUGMSG(ZONE_WARNING,("KITL(%u): Received NACK for msg %u, Tx window: %u,%u\n",
??????????????????????????????????? ClientIdx,pMsg->SeqNum, pClient->AckExpected,pClient->TxSeqNum));
??????? if (SEQ_BETWEEN(pClient->AckExpected, pMsg->SeqNum, pClient->TxSeqNum)) {
??????????? UCHAR Index = pMsg->SeqNum % pClient->WindowSize;
??????????? if (pClient->TxFrameLen[Index]) {
??????????????? // Restart retransmission timer (note we can't start timers if syscalls
??????????????? // are disabled, but this shouldn't screw us up, we'll just potentially
??????????????? // retransmit an extra frame if the timer fires before we get the ACK)
??????????????? if (fUseSysCalls)
??????????????????? TimerStop(pClient,Index,fUseSysCalls);
??????????????? RetransmitFrame(pClient, Index, fUseSysCalls);
??????????????? if (fUseSysCalls)
??????????????????? TimerStart(pClient,Index,KITL_RETRANSMIT_INTERVAL_MS,fUseSysCalls);
??????????? }
??????????? else
??????????????? KITL_DEBUGMSG(ZONE_WARNING,("!KITL(%u): NACK in window, but TxFrameLen empty!\n",ClientIdx));
??????? }
??????? else
??????????? KITL_DEBUGMSG(ZONE_WARNING,("!KITL(%u): Received NACK outside of TX window: Seq: %u, Window: %u,%u\n",
??????????????????????????????????????? ClientIdx,pMsg->SeqNum,pClient->AckExpected,pClient->TxSeqNum));
??????? goto ProcessKITLMsg_exit;
??? }
???
??? // Data frame.? Place in appropriate slot in Rx buffer pool. Note that we defer acking
??? // received frames until they are read from the buffer, in KITLRecv.
??? RxBufOffset = pMsg->SeqNum % pClient->WindowSize;
??? if (! SEQ_BETWEEN(pClient->RxSeqNum, pMsg->SeqNum, pClient->RxWinEnd)) {
??????? UCHAR uLastACK = (UCHAR) (pClient->RxWinEnd - pClient->WindowSize - 1);
??????? KITL_DEBUGMSG (ZONE_WARNING, ("KITL(%u): Received msg outside window: Seq:%u, Win:%u,%u\n",
????????????????????????????? ClientIdx,pMsg->SeqNum,pClient->RxSeqNum,pClient->RxWinEnd));
??????? // Special case to handle lost ACKs - if an ack is dropped, our Rx window will have
??????? // advanced beyond the seq # of the retransmitted frame.? Since ACKs ack all messages
??????? // up to the ack #, we only need to check the last frame.
??????? if (pMsg->SeqNum == uLastACK) {
??????????? KITL_DEBUGMSG(ZONE_WARNING,("KITL(%u): Lost ACK (seq: %u, win: %u,%u)\n",ClientIdx,
??????????????????????????????????????? pMsg->SeqNum,uLastACK,pClient->RxWinEnd));
??????????? SendAckNack (TRUE, pClient, uLastACK);
??????? }
??? } else if (pClient->RxFrameLen[RxBufOffset] != 0) {
??????? // If all our buffers are full, toss frame (will be acked when data is read in KITLRecv)
??????? KITL_DEBUGMSG(ZONE_WARNING,("KITL(%u): Received duplicate (Seq:%u), slot %u already full. Win: %u,%u\n",
??????????????????????????????????? ClientIdx,pMsg->SeqNum,RxBufOffset,pClient->RxSeqNum,pClient->RxWinEnd));
??? } else {
??????? DWORD OldProcPerms;
??????? // If we're in non-preemptible mode, can't set the receive event, so just toss message
??????? // and wait for retry.
??????? if (!fUseSysCalls && (pClient->State & KITL_USE_SYSCALLS)) {
??????????? KITL_DEBUGMSG(ZONE_WARNING,("KITL(%u): Tossing frame %u (Can't signal Rx event)\n",
??????????????????????????????????????? ClientIdx,pMsg->SeqNum));
??????????? return FALSE;
??????? }
??????? KITL_DEBUGMSG(ZONE_RECV,("KITL(%u): Received frame Seq: %u, len: %u, putting in slot %u\n",
???????????????????????????????? ClientIdx, pMsg->SeqNum, wMsgLen, RxBufOffset));
??????? // If frames were dropped, send NACK (only allow one outstanding NACK)
??????? if (pMsg->SeqNum != pClient->RxSeqNum) {
??????????? KITL_DEBUGMSG(ZONE_WARNING,("!KITL(%u): Dropped frame (seq: %u, win: %u,%u)\n",
??????????????????????????????????????? ClientIdx,pMsg->SeqNum,pClient->RxSeqNum, pClient->RxWinEnd));
??????????? if (!(pClient->State & KITL_NACK_SENT)) {
??????????????? SendAckNack(FALSE, pClient, pClient->RxSeqNum);
??????????????? pClient->State |= KITL_NACK_SENT;??????????
??????????? }
??????? }
??????? else
??????????? pClient->State &= ~KITL_NACK_SENT;
???????
??????? // Copy data to receive buffer, unblock anyone waiting, and close receive window
??????? wDataLen = wMsgLen - (WORD)KITL_DATA_OFFSET;
??????? if (wDataLen == 0)
??????????? KITL_DEBUGMSG(ZONE_WARNING,("!KITL: Received data message with 0 length!\n"));
??????? if (pClient->ProcPerms) {
??????????? // acquire permission of pClient and add it to current thread
??????????? ACCESSKEY aKey = GETCURKEY() | pClient->ProcPerms;
??????????? SWITCHKEY (OldProcPerms, aKey);
??????? }
??????? memcpy(pClient->pRxBufferPool + RxBufOffset*KITL_MTU,KITLDATA(pMsg), wDataLen);
??????? if (pClient->ProcPerms) {
??????????? SETCURKEY (OldProcPerms);???????????
??????? }
??????? pClient->RxFrameLen[RxBufOffset] = wDataLen;
??????? if (pClient->State & KITL_USE_SYSCALLS)
??????????? // If we get here, we know that fUseSysCalls is TRUE
??????????? SetClientEvent(pClient,pClient->evRecv);
???????
??????? // Close receive window
??????? while (pClient->RxFrameLen[pClient->RxSeqNum % pClient->WindowSize] &&
?????????????? (SEQ_DELTA(pClient->RxSeqNum, pClient->RxWinEnd) >= 1)) {
??????????? KITL_DEBUGMSG(ZONE_RECV,("Rx win: %u,%u, usesyscalls: %u\n",pClient->RxSeqNum, pClient->RxWinEnd, fUseSysCalls));
??????????? SEQ_INC(pClient->RxSeqNum);
??????? }
??? }
???
ProcessKITLMsg_exit:
??? if (fUseSysCalls && (pClient->State & KITL_USE_SYSCALLS))
??????? LeaveCriticalSection(&pClient->ClientCS);
???
??? return fRet;
}
ProcessRecvFramed是一個近200行的函數,樣子很嚇人。它是數據幀的解析和處理模塊的主體。我們從上到下看看都干了些什么。先調用KitlEtherDecodeUDP將來自主機的數據幀解碼為KITL_HDR結構,然后效驗魔法數KITL_ID,確認該幀的信息的有效性以及數據長度是否有效,如果ZONE_FRAMEDUMP標簽是打開的則需要則解析Frame的內容并記錄下來(輸出到調試界面,初始化流程并不包含該信息),然后判定該數據幀描述的信息是否屬于管理命令(連接桌面,新建client等等),如果是則調用ProcessAdminMsg進行處理。
static BOOL ProcessAdminMsg(KITL_HDR *pHdr, WORD wMsgLen, BOOL fUseSysCalls, PFN_TRANSMIT pfnTransmit, LPVOID pData)
{
??? KITL_CLIENT *pClient = NULL;???
???
??? switch (pHdr->Cmd)
??? {
??????? case KITL_CMD_SVC_CONFIG:
??????? {
??????????? KITL_SVC_CONFIG_DATA *pCfg = (KITL_SVC_CONFIG_DATA *) KITLDATA (pHdr);
??????????? int i, iStart;
???????????
??????????? if (wMsgLen != (KITL_DATA_OFFSET + sizeof(KITL_SVC_CONFIG_DATA))) {
??????????????? KITL_DEBUGMSG(ZONE_WARNING,("!ProcessAdminMsg: Invalid legnth for CONFIG msg: %u\n",wMsgLen));
??????????????? return FALSE;
??????????? }
??????????? // Find client struct
??????????? if ((i = ChkDfltSvc (pCfg->ServiceName)) < 0)
??????????????? i = HASH(pCfg->ServiceName[0]);
??????????? iStart = i;
??????????? while (KITLClients[i]) {
??????????????? // For multi instanced services, skip clients that are already registered
??????????????? if (!strcmp(KITLClients[i]->ServiceName,pCfg->ServiceName) &&
??????????????????? (!(KITLClients[i]->State & KITL_CLIENT_REGISTERED) || !(KITLClients[i]->CfgFlags & KITL_CFGFL_MULTIINST))) {
??????????????????? pClient = KITLClients[i];
??????????????????? break;
??????????????? }
??????????????? if (i < NUM_DFLT_KITL_SERVICES)
??????????????????? // no dups for default services
??????????????????? break;
??????????????? if (MAX_KITL_CLIENTS == ++ i)
??????????????????? i = NUM_DFLT_KITL_SERVICES;
??????????????? if (iStart == i)
??????????????????? break;? // couldn't find a client
??????????? }
??????????? if (!pClient || !(pClient->State & (KITL_CLIENT_REGISTERING|KITL_CLIENT_REGISTERED))) {
??????????????? KITL_DEBUGMSG(ZONE_WARNING,("!Received config for unrecognized service %s\n",
??????????????????????????????????????????? pCfg->ServiceName));
??????????????? return TRUE;
??????????? }
??????????? if (fUseSysCalls)
??????????????? EnterCriticalSection(&pClient->ClientCS);
??????????? // Send config to peer, unless this was a response to our cmd
??????????? if (!(pHdr->Flags & KITL_FL_ADMIN_RESP)) {
??????????????? // ack this config message
??????????????? SendConfig(pClient,TRUE);
??????????????? // Stop any pending transfers, reset sequence #s, etc
??????????????? // WARNING - can cause lost transmit data if the other side doesn't get
??????????????? // our config, and retries the config command.
??????????????? if (pClient->State & KITL_SYNCED) {
??????????????????? ResetClientState(pClient);
??????????????? }
??????????? }
??????????? //
??????????? // we got the response from desktop, connecting the client
??????????? //
??????????? KITL_DEBUGMSG(ZONE_INIT, ("ProcessAdminMsg: Receive Config message for service %s\n", pClient->ServiceName));
??????????? pClient->State &= ~(KITL_WAIT_CFG|KITL_CLIENT_REGISTERING);
??????????? pClient->State |= KITL_CLIENT_REGISTERED;
??????????? // Set our event in case anyone is waiting for config info
??????????? if (fUseSysCalls)
??????????????? SetClientEvent(pClient,pClient->evCfg);
???????????
??????????? if (fUseSysCalls)???????????
??????????????? LeaveCriticalSection(&pClient->ClientCS);
??????????? break;
??????? }
??????? case KITL_CMD_RESET:
??????????? {
??????????????? KITL_RESET_DATA *pResetData =? (KITL_RESET_DATA *) KITLDATA (pHdr);
??????????????? KITLOutputDebugString("KITL: Got RESET command\n");
??????????????? // Set for clean boot if requested
??????????????? if (pResetData->Flags & KITL_RESET_CLEAN)
??????????????????? SC_SetCleanRebootFlag();
???????????????
??????????????? // This function never returns
??????????????? KernelIoctl(IOCTL_HAL_REBOOT, NULL,0,NULL,0,NULL);
??????????????? KITLOutputDebugString("KITL: IOCTL_HAL_REBOOT not supported on this platform\n");
??????????????? break;
??????????? }
??????? case KITL_CMD_DEBUGBREAK:
??????????? if (fUseSysCalls && IsDesktopDbgrExist ())
??????????????? DebugBreak ();
??????????? break;
??????? case KITL_CMD_TRAN_CONFIG:
??????????? {
??????????????? int i;
???????????????
??????????????? PKITL_HOST_TRANSCFG pCfg = (PKITL_HOST_TRANSCFG) KITLDATA(pHdr);
??????????????? wMsgLen -= KITL_DATA_OFFSET;
??????????????? if (pCfg->dwLen != wMsgLen) {
??????????????????? KITLOutputDebugString ("!Host config message size mismatch %d, %d\r\n", pCfg->dwLen, wMsgLen);
??????????????????? return FALSE;
??????????????? }
??????????????? wMsgLen -= sizeof (KITL_HOST_TRANSCFG);
??????????????? if (!Kitl.pfnSetHostCfg ((LPBYTE) (pCfg+1), wMsgLen))
??????????????????? return FALSE;
??????????????? Kitl.dwBootFlags = pCfg->dwFlags;
??????????????? if (pCfg->dwKeySig == HOST_TRANSCFG_KEYSIG) {
??????????????????? for (i = 0; i < HOST_TRANSCFG_NUM_REGKEYS; i++) {
??????????????????????? g_dwKeys[i] = pCfg->dwKeys[i];
??????????????????????? KITL_DEBUGMSG (ZONE_INIT, (" KeyIndex %d = %d \n", i, g_dwKeys[i]));
??????????????????? }
??????????????? }???
??????????????? KITLGlobalState |= KITL_ST_DESKTOP_CONNECTED;
??????????? }
??????????? break;
??????? // in case we're polling (pfnTransmit && pData only set to non-null if we're polling)
??????? // we'll use desktop as our timer (desktop sends a retransmit packet to us every 2 seconds).
??????? case KITL_CMD_RETRASMIT:
??????????? if (pfnTransmit && pData) {
??????????????? // KITLOutputDebugString ("Retrasmitting packets....\n");
??????????????? pfnTransmit (pData, fUseSysCalls);
??????????? }
??????????? break;
??????? default:
??????????? KITL_DEBUGMSG(ZONE_WARNING,("!ProcessAdminMsg: Unhandled command 0x%X\n",pHdr->Cmd));
??????????? return FALSE;
??? }
??? return TRUE;
}
我們直接看KITL_CMD_TRAN_CONFIG分支,目前我們的主要工作仍然是配置連接。首先得到PKITL_HOST_TRANSCFG結構指針,并送SetHostCfg設置主機信息之后,讀入從主機送出的8個key值后設置以及kitl啟動選項和KITL_ST_DESKTOP_CONNECTED標示位。繞了這么大一圈也就干了個初始化連接的工作,現在我們繼續回到startKitl。首先先是設置三個函數指針供后面程序調用,并設置標示位KITL_ST_ADAPTER_INITIALIZED;然后通過SetKernelCommDev從定位KERNEL_SVC_DBGMSG,KERNEL_SVC_PPSH,KERNEL_SVC_KDBG的transport,注意:這個函數并不是kitl的函數,而是windowsCE kernel的系統函數在WINCE420\PRIVATE\WINCEOS\COREOS\NK\KERNELkwin32.c下,作用是從定位系統調試信息的發布通道,有興趣可以自己看看。之后StartKitl的過程就結束了,這個時候你肯定想問那kitl的中斷初始化函數是什么時候才運行呢?沒有中斷的支持kitl是如何通訊和工作的呢,事實上KITLInitializeInterrupt就負責這部分的工作,但是由于系統內核還沒有完成初始化的動作,所以這個時候起動kitl的中斷是會影響kernel的工作。因此在后面由SystemStartupFunc()調用KITLInitializeInterrupt來完成初始化的動作。
BOOL
KITLInitializeInterrupt()
{
??? int i;
??? if (!(KITLGlobalState & KITL_ST_ADAPTER_INITIALIZED))
??????? return FALSE;
???
??? // Check if we are coming up for the first time, or resuming interrupts (e.g. when coming
??? // back from OEMPowerOff)
??? if (KITLGlobalState & KITL_ST_MULTITHREADED) {
??????? // Just enable interrupt and return
??????? EnableInts();
??????? return TRUE;
??? }
???
??? KITLOutputDebugString("KITL: Leaving polling mode...\n");
??? InitializeCriticalSection(&KITLODScs);
??? InitializeCriticalSection(&KITLKCallcs);
??? KITLGlobalState |= KITL_ST_MULTITHREADED;
???
??? KITL_DEBUGMSG(ZONE_INIT,("KITL Checking client registrations\n"));
??? // Some clients may have registered already, finish initialization now that
??? // the system is up. KDBG continues to run in polling mode.
??? for (i=0; i< MAX_KITL_CLIENTS; i++) {
??????? if (KITLClients[i] && (i != KITL_SVC_KDBG)
??????????? && (KITLClients[i]->State & (KITL_CLIENT_REGISTERED|KITL_CLIENT_REGISTERING))) {
??????????? if (!RegisterClientPart2((UCHAR)i))
??????????????? return FALSE;
??????? }
??? }
??? // Start interrupt thread. If we have clients registered, also turn on receive interrupts
??? // from the ethernet controller, otherwise leave them disabled.
??? if ((UCHAR) KITL_SYSINTR_NOINTR != Kitl.Interrupt) {
??????? KITL_DEBUGMSG(ZONE_INIT,("KITL Creating IST\n"));
??????? if ((hIntrThread = CreateKernelThread((LPTHREAD_START_ROUTINE)KITLInterruptThread,
????????????????????????????????????????????? NULL, (WORD)g_dwKITLThreadPriority, 0)) == NULL) {
??????????? KITLOutputDebugString("Error creating interrupt thread\n");
??????????? return FALSE;
??????? }
??? }
??? return TRUE;
}
由于將會有IST為kitl專門服務,所以也就需要臨界區來完成線程的操作,這兒創建了KITLODScs\KITLKCallcs兩個臨界區。之后標記 KITL_ST_MULTITHREADED。檢查注冊了的服務,完成后就創建IST.
kitl的初始化,啟動的大致過程就是如此,start-->注冊服務-〉初始化transport->創建IST
最后我們來看看IST里面我們都干些什么
static DWORD KITLInterruptThread (DWORD Dummy)
{
??? HANDLE hIntEvent;
??? DWORD dwRet;
??? KITL_DEBUGMSG(ZONE_INIT,("KITL Interrupt thread started (hTh: 0x%X, pTh: 0x%X), using SYSINTR %u\n",
???????????????????????????? hCurThread,pCurThread, Kitl.Interrupt));
??? pCurThread->bDbgCnt = 1;?? // no entry messages
???
??? if ((hIntEvent = CreateEvent(0,FALSE,FALSE,EDBG_INTERRUPT_EVENT)) == NULL) {
??????? KITLOutputDebugString("KITL CreateEvent failed!\n");
??????? return 0;
??? }
??? if (!SC_InterruptInitialize(Kitl.Interrupt, hIntEvent, NULL,0)) {
??????? CloseHandle(hIntEvent);
??????? KITLOutputDebugString("KITL InterruptInitialize failed\n");
??????? return 0;
??? }
??? // always enable interrupt as long as OEM told us so
??? EnableInts();
???
??? KITLGlobalState |= KITL_ST_IST_STARTED;
???
??? while ((dwRet = SC_WaitForMultiple (1,&hIntEvent,0,INFINITE)) == WAIT_OBJECT_0) {
??????? KITL_DEBUGLED(LED_IST_ENTRY,0);
??????? KITL_DEBUGMSG(ZONE_INTR,("KITL Interrupt event\n"));
??????? // no need to check pending, just call HandleRecvInterrupts because it'll
??????? // just return if there is no interrupt pending
??????? HandleRecvInterrupt(ISTRecvBuf,TRUE, NULL, NULL);
??????? SC_InterruptDone(Kitl.Interrupt);
???????
??????? KITL_DEBUGMSG(ZONE_INTR,("Processed Interrupt event\n"));
??? }
??? KITLOutputDebugString("!KITL Interrupt thread got error in WaitForMultipleObjects: dwRet:%u, GLE:%u\n",
????????????????????????? dwRet,GetLastError());
??? return 0;
}
首先是創建該IST所屬的事件,并將該事件與所屬的中斷聯系起來,再后自然是啟動中斷了。設定KITL_ST_IST_STARTED標記后的代碼就是IST的實際內容,當中斷發生,交付HandleRecvInterrupt處理,返回中斷。和多數IST一樣該調用永遠不會返回,除非結束該線程,所以后面的調試信息輸出的是錯誤。
void HandleRecvInterrupt(UCHAR *pRecvBuf, BOOL fUseSysCalls, PFN_TRANSMIT pfnTransmit, LPVOID pData)
{
??? WORD wLen = KITL_MTU;
??? BOOL fFrameRecvd;
??? // Receive data into buffer
??? do {
??????? if (!fUseSysCalls)
??????????? fFrameRecvd = Kitl.pfnRecv (pRecvBuf, &wLen);
??????? else if (IsDesktopDbgrExist ())
??????????? fFrameRecvd = KCall((PKFN) Kitl.pfnRecv, pRecvBuf, &wLen);
??????? else {
??????????? EnterCriticalSection (&KITLKCallcs);
??????????? fFrameRecvd = Kitl.pfnRecv (pRecvBuf, &wLen);
??????????? LeaveCriticalSection (&KITLKCallcs);
??????? }
??????? if (fFrameRecvd) {
??????????? ProcessRecvFrame (pRecvBuf,wLen,fUseSysCalls, pfnTransmit, pData);
??????????? wLen = KITL_MTU;
??????? }
??? } while (fFrameRecvd);
}
HandleRecvInterrupt是中斷處理程序的實體,將transport送來的數據填入緩沖區待處理。上面所提到的中斷在OEM代碼中指定,在SMDK2440bsp中該中斷對應以太網卡中斷。