Kitl是怎样工作的?
---------------by nasiry
转载请说明出处,并通知我
---------------------------------
注:由于我们主要是分析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("<
// 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中该中断对应以太网卡中断。