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&parallel 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中該中斷對應以太網卡中斷。


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/gooogleman/archive/2008/11/23/3353894.aspx

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章