(3)Windows CE 6.0 的KITL初始化过程分析
在WinCE6.0中,内核调用OEMInit()函数对目标平台进行初始化过程时,需要启动KITL的支持,但由于OEMInit()函数属于oal.exe文件,不能像WinCE5.0那样直接调用初始化KITL的函数,根据Windows CE 6.0中共享变量或函数的互访原则(见Windows CE 6.0启动分析部分),它们只能通过系统定义的OEMGLOBAL和NKGLOBAL两个结构体来实现,当在编译选项(Build Options)中选中Enable KITL时,编译器就会将kitl.dll动态链接库链接到到内核中,在OEMGLOBAL结构体定义了利用KITLOEM宏将kitl.dll的入口函数KitlDllMain编译到系统中条件,这是系统链接的库为oemmain_statickitl.lib,下面为OEMGLOBAL结构体中KITL的定义部分:
// Platform specific information passed from OAL to KITL.
NULL, // LPVOID pKitlInfo
#ifdef KITLOEM
KitlDllMain, // KITL entry point (KITL is part of OEM)
#else
NULL, // KITL entry point (KITL is in a separate DLL)
#endif
一旦kitl.dll启动时,在其入口函数KitlDllMain中将KITLIoctl函数地址赋给NKGLOBAL的pfnKITLIoctl指针,同时也从内核启动参数结构体(KDataStruct )g_pKData中获取OEMGLOBAL结构体的指针,这样便可以实现共享变量或函数的互访。即OAL中的OEMInit()函数通过NKGLOBAL结构体间接调用KITLIoctl 函数启动KITL的初始化。
$(_PRIVATEROOT)\WINCEOS\COREOS\NK\KITL\ethdbg.c
BOOL WINAPI KitlDllMain (HINSTANCE DllInstance, DWORD dwReason, LPVOID Reserved)
{
if (DLL_PROCESS_ATTACH == dwReason) {
PFN_KLIBIOCTL pfnKLibIoctl = (PFN_KLIBIOCTL) Reserved;
g_kpriv.pfnExtKITLIoctl = ExtKITLIoctl;
(* pfnKLibIoctl) ((HANDLE)KMOD_KITL, IOCTL_KLIB_KITL_INIT, NULL, 0, &g_kpriv, sizeof (g_kpriv), NULL);
g_pKData = g_kpriv.pKData;
g_pprcNK = g_kpriv.pprcNK;
g_pNKGlobal = g_pKData->pNk;
g_pOemGlobal = g_pKData->pOem;
g_pNKGlobal->pfnKITLIoctl = KITLIoctl;
#ifdef DEBUG
g_pNKGlobal->pKITLDbgZone = &dpCurSettings;
#endif
}
return TRUE;
}
$(_PRIVATEROOT)\WINCEOS\COREOS\NK\OEMSTUB\oemstub.c
void OEMInit (void)
{
g_pOemGlobal->pfnInitPlatform ();
}
$(_PRIVATEROOT)\WINCEOS\COREOS\NK\NKSTUB\kitlstub.c
BOOL KITLIoctl (DWORD dwCode, LPVOID pInBuf, DWORD nInSize, LPVOID pOutBuf, DWORD nOutSize, LPDWORD pcbRet)
{
return g_pNKGlobal->pfnKITLIoctl (dwCode, pInBuf, nInSize, pOutBuf, nOutSize, pcbRet);
}
$(_PLATFORMROOT)\xsbase270\src\oal\OalLib\Init.c
void OEMInit()
{
volatile XSBASE270_CPLD_REGS *pCPLDRegs = (volatile XSBASE270_CPLD_REGS *) OALPAtoVA(XSBASE270_BASE_REG_PA_CPLD, FALSE);
volatile XSBASE270_MEMBCR_REGS *pMEMBCRRegs = (volatile XSBASE270_MEMBCR_REGS *) OALPAtoVA(XSBASE270_BASE_PA_BCR, FALSE);
……
// Initialize the KITL connection if required.
KITLIoctl(IOCTL_KITL_STARTUP, NULL, 0, NULL, 0, NULL);
…….
OALMSG(OAL_FUNC, (L"-OEMInit\r\n"));
}
$(_PRIVATEROOT)\WINCEOS\COREOS\NK\KITL\ethdbg.c
BOOL KITLIoctl (DWORD dwIoControlCode, LPVOID lpInBuf, DWORD nInBufSize, LPVOID lpOutBuf, DWORD nOutBufSize, LPDWORD lpBytesReturned)
{
BOOL retval = FALSE;
switch (dwIoControlCode) {
case IOCTL_KITL_STARTUP:
retval = OEMKitlStartup ();
break;
……
return retval;
}
根据上述分析和上面五段代码,我们再理一下函数的调用顺序。
内核调用$(_PRIVATEROOT)\WINCEOS\COREOS\NK\OEMMAIN\oemstub.c中的OEMInit函数,该函数通过OEMGLOBAL结构体中的函数指针pfnInitPlatform(在oemglobal.c中初始化)调用$(_PLATFORMROOT)\xsbase270\src\oal\OalLib\Init.c中的OEMInit函数,该函数在调用KITLIoctl函数时,实际上调用$(_PRIVATEROOT)\WINCEOS\COREOS\NK\NKSTUB\kitlstub.c中的KITLIoctl,KITLIoctl函数通过NKGLOBAL结构体中的pfnKITLIoctl函数指针再调用$(_PRIVATEROOT)\WINCEOS\COREOS\NK\KITL\ethdbg.c中的KITLIoctl函数。Windows CE 6.0中函数调用好像是在绕圈子,这也是它的奥妙之处。在理顺上述函数调用顺序之后,后面函数的调用便相当简单啦。
OEMInit 函数中调用KITLIoctl(IOCTL_KITL_STARTUP, NULL, 0, NULL, 0, NULL)函数的第一个参数为IOCTL_KITL_STARTUP,根据上述函数调用顺序关系,该KITLIoctl函数最终被调用的位置为$(_PRIVATEROOT)\WINCEOS\COREOS\NK\KITL\ethdbg.c中的KITLIoctl函数,从源代码可以发现,IOCTL_KITL_STARTUP对于的选择语句为retval = OEMKitlStartup (),即函数调用回到类似Windows CE 5.0中OALKitlStart()函数,Windows CE 6.0 KITL的移植步骤中提到需要将Windows CE 5.0中OALKitlStart()函数重命名为OEMKitlStartup()函数。如果你对Windows CE 5.0的KITL函数调用顺序比较熟悉的话,后面的内容就可以不用再看啦