wince -- kitl篇

[复制链接]
6406|4
 楼主| 驱动开发版 发表于 2007-9-22 16:05 | 显示全部楼层 |阅读模式
 楼主| 驱动开发版 发表于 2007-9-22 16:05 | 显示全部楼层

基于Windows CE的KITL技术

KITL(Kernel&nbsp;Independent&nbsp;Transport&nbsp;Layer)是基于Windows&nbsp;CE平台的一种软件技术,开发商基于它可以很容易地支持各种调试功能。因为Windows&nbsp;CE的调试是一种远程调试,所以开发工作站(运行PB的机器)和设备端必须要有相应的通信通道,不同的硬件平台会有不同的通信硬件,这样会增加开发的难度。KITL的目的就是将硬件层和通信协议层分开,开发商只要根据相应的API实现控制通信硬件的代码就可以实现KITL。<br /> <br />&nbsp;<br />KITL要工作必须要具备两个条件,一是在开发工作站上要运行PB(Platform&nbsp;Builder),另外在Windows&nbsp;CE的OAL层要实现支持KITL的代码。如下图:<br /> <br />&nbsp;<br />&nbsp;&nbsp;<br />&nbsp;KITL有两种模式:主动和被动模式。主动模式是指系统在启动的过程就要求和开发工作站建立KITL连接,而且这个连接必须一直保持,否则系统会不正常,它主要适用于开发工程师作调试;被动模式不要求在启动阶段就建立KITL连接,只有当系统出现异常,而且没有相应的处理器处理这个异常的时候,才会请求建立一个KITL连接,这种模式主要适用于测试人员或者是系统已经比较稳定的情况,这样可以方便开发人员去跟踪问题。<br /> <br />&nbsp;<br />目前KITL支持的传输方式有以下几种:<br />&nbsp;<br />串口:&nbsp;优点是容易实现,但速度太慢&nbsp;<br /><br />网卡:&nbsp;优点是快,容易实现。但一般的嵌入式设备都不支持网卡&nbsp;<br /><br />基于USB仿真的串口:&nbsp;优点是快,而且现在的嵌入式设备都支持USB。但是PB不支持通过它下载&nbsp;<br /><br />基于USB仿真的网卡:&nbsp;因为在开发工作站端看到的是一个网卡,所以相比USB仿真的串口,PB支持通过它下载image&nbsp;<br />&nbsp;<br />基于KITL可以提供很多调试服务,除了微软已经提供的,开发商也可以自己开发:&nbsp;<br />代码调试:&nbsp;可以调试kernel和普通的application&nbsp;<br /><br />远程工具:&nbsp;PB自带的一些远程工具会通过KITL和设备通信,比如编辑注册表,测试系统性能,spy窗口消息&nbsp;<br /><br />Release文件系统:&nbsp;如果Windows&nbsp;CE要加载某个文件,但image里又没有。系统会通过KITL从开发工作站的release目录下加载该文件。这样就可以提高开发效率。比如开发人员在调试一个driver,他就可以不要将该driver放到os&nbsp;image里,也就不要每次修改driver后都下载整个os&nbsp;image,因为系统会自动从release目录下加载最新的该driver。&nbsp;<br />&nbsp;<br />KITL的实现,可以提高开发效率,有利于开发出更稳定的产品。所以开发商在产品的设计初期就要考虑怎样支持KITL,要有相应的通信硬件,要开发相应的代码。<br />&nbsp;<br />
 楼主| 驱动开发版 发表于 2007-9-22 16:12 | 显示全部楼层

解读WINCE 5.0 KITL代码流程

:KITL是PLATFORM&nbsp;BUILDER中的一个亮点,提供了和本地调试类似的断点、变量跟踪、内存查看等手段,如果没有KITL,嵌入式调试应该只能用串口打印消息来看了,工作效率大大下降。本文以实现最简单的SERIAL&nbsp;KITL为目的,就其实现代码进行跟踪调试,这些代码跨越了WINCE的PLATFORM、PUBLIC、PRIVATE三大主要目录,有些烦琐,不过只要能调通,一切工作和弯路都是值得的。我把调试经验和个人理解写下来,希望能帮助别人少走弯路。如果**中有理解失当的地方,请不吝赐教。<br /><br />正文:<br /><br />一.void&nbsp;OEMInit()&nbsp;&nbsp;[platformprojectsrckerneloalinit.c]<br />&nbsp;&nbsp;&nbsp;&nbsp;首先从OEMInit()函数看起。。在依次初始化Branch-Target&nbsp;Buffer、KERNEL函数、初始化中断、TIMER之后,就轮到KITL了。调用了这个函数OALKitlStart()。此时有个编译的分支,如果是RELEASE版本,那么在kernelkernstubs.c里面的OALKitlStart()函数是个STUB,只是return&nbsp;TRUE;&nbsp;如果是DEBUG版本,那就进到kerneloalkitl.c里面的OALKitlStart().<br /><br />二.BOOL&nbsp;OALKitlStart()&nbsp;&nbsp;[platformmyprojectsrckerneloal]<br />&nbsp;&nbsp;&nbsp;&nbsp;在OALKitlStart()里面,首先试图从0xA00FF00处读取bootloader里面留下的kitl参量,如果读不到东西则使用该函数里的默认配置。由于原来用了ethernet同时作为download和kitl途径,所以在InitSpecifiedEthDevice函数里给pKitlArgs结构体赋值了。现在想把两者划分清楚,首先就把读取bootloader里面的kitl参量一句干掉,强迫使用我们在下面的默认配置。主要就是填充三个参量<br /><br />1.首先是这个结构体<br />OAL_KITL_ARGS&nbsp;pKITLArgs<br />{<br />&nbsp;UINT32&nbsp;flags;&nbsp;&nbsp;//设好ENABLED标志,按需要设POLL标志,但注意一定不要设PASSIVE标志<br />&nbsp;DEVICE_LOCATION&nbsp;&nbsp;devLoc;<br />&nbsp;{<br />&nbsp;&nbsp;DWORD&nbsp;&nbsp;IfcType;&nbsp;&nbsp;//不论ether还是serial,都是internal&nbsp;type&nbsp;=0;<br />&nbsp;&nbsp;DWORD&nbsp;&nbsp;BusNumber;&nbsp;//&nbsp;=0<br />&nbsp;&nbsp;DWORD&nbsp;&nbsp;LogicalLoc;&nbsp;//物理地址<br />&nbsp;&nbsp;PVOID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PhysicalLoc;&nbsp;//留做后面=OALPAtoVA(LogicalLoc,&nbsp;FALSE).&nbsp;真见鬼,&nbsp;感觉应该和上面的LogicalLoc作用调过来看着比较顺吧?<br />&nbsp;&nbsp;DWORD&nbsp;&nbsp;Pin;&nbsp;&nbsp;//Ethernet才用的东东<br />&nbsp;}<br />&nbsp;union<br />&nbsp;{<br />&nbsp;&nbsp;struct<br />&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;UINT32&nbsp;&nbsp;baudRate;&nbsp;//不用解释了<br />&nbsp;&nbsp;&nbsp;UINT32&nbsp;&nbsp;dataBits;<br />&nbsp;&nbsp;&nbsp;UINT32&nbsp;&nbsp;stopBits;<br />&nbsp;&nbsp;&nbsp;UINT32&nbsp;&nbsp;parity;<br />&nbsp;&nbsp;}<br />&nbsp;&nbsp;struct<br />&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;UINT16&nbsp;&nbsp;mac[3];&nbsp;//这个也不用解释了<br />&nbsp;&nbsp;&nbsp;UINT32&nbsp;&nbsp;ipAddress;<br />&nbsp;&nbsp;&nbsp;UINT32&nbsp;&nbsp;ipMask;<br />&nbsp;&nbsp;&nbsp;UINT32&nbsp;&nbsp;ipRoute<br />&nbsp;&nbsp;}<br />&nbsp;}<br />}<br />2.&nbsp;pszDeviceID.&nbsp;&nbsp;感觉这名字就是起了好玩,&nbsp;赋成Walzer应该比较拽,不过还是保留原来赋的AMOISOFT好了,&nbsp;免得被打.<br />3.&nbsp;全局变量OAL_KITL_DEVICE&nbsp;g_kitlDevices.&nbsp;这个东东在kitl.c开头包含的kitl_cfg.h中被赋值,&nbsp;&nbsp;最主要就是修改g_kitlDevices.pDriver.&nbsp;&nbsp;这个pDrvier指向一个函数指针列表的结构体,该列表定义了用做kitl模块的初始化、读写、中断、流控制等函数。&nbsp;g_kitlDevices本身是个二维数组,&nbsp;可以定义许多设备用做kitl时提供的参数设置,&nbsp;后面会用一个for来循环判断pKITLArgs的参数和g_kilDevices里面哪个一维数组成员相匹配.<br /><br />这三个参量填充好以后,就可以进到OALKitlInit(pszDeviceID,&nbsp;pKITLArgs,&nbsp;g_kitlDevices)里面了.<br /><br />三.BOOL&nbsp;OALKitlInit(&nbsp;deviceId,&nbsp;pArgs,&nbsp;pDevice)&nbsp;&nbsp;&nbsp;&nbsp;[platformcommonsrccommonkitlkitl.c]<br />&nbsp;&nbsp;&nbsp;&nbsp;这个函数先把输入的参量全部用OALMSG打印出来,这个不管。<br />&nbsp;&nbsp;&nbsp;&nbsp;重要的是引入了g_kitlState全局变量,开头一句<br />&nbsp;&nbsp;&nbsp;&nbsp;g_kitlState.pDevice&nbsp;=&nbsp;OALKitlFindDevice(&pArgs-&gtdevLoc,&nbsp;pDevice)&nbsp;这个就是上面所说的从g_kitlDevices里可用设备列表里循环判断,找到选用的设备的匹配函数指针。<br />&nbsp;&nbsp;&nbsp;&nbsp;接着把输入参量devicdId和前面填充好的OAL_KITL_ARGS结构COPY到g_kitlState里面<br />&nbsp;&nbsp;&nbsp;&nbsp;然后就可以调用KItlInit(TRUE)了,如果前面在FLAG里面设了PASSSIVE标志,现在就是KitlInit(FALSE)了,嘿嘿爽到了吧。<br /><br />四.BOOL&nbsp;KitlInit(BOOL&nbsp;fStartKitl)&nbsp;&nbsp;&nbsp;&nbsp;[privatewinceoscoreos
kkitlethdbg.c]<br />&nbsp;&nbsp;&nbsp;&nbsp;太猥琐了,我要用串口,它居然叫ethdbg.c,不给面子。不过是private里面的东东,可远观而不可亵玩焉~~<br />&nbsp;&nbsp;&nbsp;&nbsp;这个函数干了三件事:<br />1.&nbsp;装载了三个全局的函数指针<br />2.&nbsp;用NewClient注册了三个KITL客户端:<br />&nbsp;&nbsp;&nbsp;&nbsp;KITL_SVCNAME_DBGMSG&nbsp;&nbsp;&nbsp;//debug&nbsp;message,&nbsp;Debug信息发布通道<br />&nbsp;&nbsp;&nbsp;&nbsp;KITL_SVCNAME_PPSH&nbsp;&nbsp;//PPshell,&nbsp;文本控制台界面&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;KITL_SVCNAME_KDBG&nbsp;//kernel&nbsp;debug,&nbsp;内核调试界面&nbsp;<br /><br />3.由fStartKitl来决定是否启动StartKitl()函数.&nbsp;(这里顺便提一下,按照匈牙利命名法,&nbsp;BOOL变量前面加个b,&nbsp;但MS的做法我觉得很合理,&nbsp;BOOL变量就是个FLAG嘛,&nbsp;前面加个f,&nbsp;把小b留着给BYTE类型用.)<br /><br />五.static&nbsp;BOOL&nbsp;StartKitl(BOOL&nbsp;fInit)&nbsp;&nbsp;&nbsp;&nbsp;[privatewinceoscoreos
kkitlethdbg.c]<br />&nbsp;&nbsp;&nbsp;&nbsp;这又是prviate里面的东东。最痛苦的地方开始了。这函数及其子函数将第一次调用OEM自己写的KITL模块初始化、读写程序。是骡子是马,拉出来溜溜就知道啦~<br />&nbsp;&nbsp;&nbsp;&nbsp;按顺序看下来,首先判断输入参量是否要启动KITL,并且如果KITLGlobalState里面被打上KITL_ST_DESKTOP_CONNECTED标记的话,那下面的步骤就全免了。当然我们是第一次运行到这里,若这么就跳出的话,俺就马加爵了。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;第一步:<br />&nbsp;&nbsp;&nbsp;&nbsp;干的第一件正事就是调用OEMKitlInit(&Kitl).&nbsp;这个后面详述.&nbsp;继续把这个函数看完.<br />&nbsp;&nbsp;&nbsp;&nbsp;OEMKitlInit初始化KITL的硬件抽象层后并把相关指针数据填充给全局变量KITLTRANSPORT&nbsp;Kitl&nbsp;(有没有搞错,为什么不叫g_kitl),&nbsp;这些工作做完就返回了<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StartKitl收货后把Kitl结构整个检查一遍,保证没错后,&nbsp;马上买单,&nbsp;把全局变量KITLGlobalState打上个KITL_ST_KITLSTARTED标记.&nbsp;这才算KITL启动的第一步OK了.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;第二步:<br />&nbsp;&nbsp;&nbsp;&nbsp;接下来就是KITLConnectToDesktop(),&nbsp;进这个函数后就是第一次使用了前面KITL传输介质硬件抽象层里的读写函数了,&nbsp;这时候就需要调试了.&nbsp;这个ConnectToDesktop大致就是先Send了一个kITL.....的frame过去,然后polling等待PC端的response,&nbsp;那边再发个kITL.....的frame过来,&nbsp;搞得跟地下d打暗号似的,&nbsp;其实也没什么玄乎的,就是普通的数据包前面加个KTIL专用的HEADER而已.&nbsp;这个CONNECT成功后,KITLGlobalState里面就加个KITL_ST_DESKTOP_CONNECTED标记了.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;第三步:<br />&nbsp;&nbsp;&nbsp;&nbsp;set&nbsp;up&nbsp;kernel&nbsp;function&nbsp;pointers,&nbsp;也没什么,就三个函数指针,&nbsp;赋完后就KITL_ST_ADAPTER_INITIALIZED了.&nbsp;其实这个KITLGolbalSate的总共有7个标志,分别是<br />KITL_STARTED,&nbsp;&nbsp;(OK)<br />DESKTOP_CONNECTED,&nbsp;&nbsp;&nbsp;(OK)&nbsp;<br />TIMER_INIT,&nbsp;(?)<br />INT_ENABLED,&nbsp;&nbsp;(POLLING)<br />IST_STARTED,&nbsp;&nbsp;(POLLING)<br />MULTITHREADED,&nbsp;&nbsp;&nbsp;(?)<br />ADAPTER_INITIALIZED.&nbsp;(OK)<br />后面括号里打上OK是到这里已经完成的,&nbsp;打问号的我还不太清楚什么作用,&nbsp;INT和IST两项,我们用的POLLING所以肯定是不需要了.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;第四步:<br />&nbsp;&nbsp;&nbsp;&nbsp;调用SetKernelCommDev设置kernel通过何种介质传送DBGMSG,&nbsp;PPSH和KDBG.<br />&nbsp;&nbsp;&nbsp;&nbsp;OHYEAH,&nbsp;我的SERIAL&nbsp;KITL就夭折在这里.&nbsp;进到SerKernelCommDev(service,&nbsp;CommDevice)函数里看,&nbsp;它只认CommDevice=KERNEL_COMM_ETHER的情况,而屏蔽了与ETHER并列的SERIAL和PARALLER,直接return&nbsp;FALSE,&nbsp;下面的事情都不用干了.&nbsp;而在MS提供的WinCE&nbsp;Documantation里面,这个SetKernelCommDev函数的说明上写着&quot;This&nbsp;function&nbsp;is&nbsp;obsolete&nbsp;and&nbsp;should&nbsp;not&nbsp;be&nbsp;used&quot;.&nbsp;若想改嘛,这个是在private里面的还动它不得.&nbsp;NND,&nbsp;感觉被MS&nbsp;raped了.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果使用ETHER在这里成功的话,&nbsp;下面还有两个函数NKForceCleanBoot()和KITLInitializeInterrupt()走过去,&nbsp;这KITL初始化就全部结束了.&nbsp;我估计KITLGolbalSate里面的INIT_ENABLED和IST_STARTED就是在这个函数过程中被标记上的.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />六.BOOL&nbsp;OEMKitlInit(PKITLTRANSPORT&nbsp;pKitl)&nbsp;&nbsp;&nbsp;&nbsp;[platformcommonsrccommonkitlkitl.c]<br />&nbsp;&nbsp;&nbsp;&nbsp;前面提到StartKItl起来后,首要的就是调用OEMKitlInit.&nbsp;这个函数在WinCE4.2和5.0里差别很大,&nbsp;4.2里的做法是&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!InitEther&nbsp;(pKitl)&nbsp;&&&nbsp;!InitParallelSerial&nbsp;(pKitl)),&nbsp;把ETHER,&nbsp;SERIAL,&nbsp;PARALLEL都初始化了一遍,碰运气看哪个用得上,而5.0里是进来后就一个很明显的分支剧情,由g_kitlState.pDevice-&gttype来决定是调用OALKitlEthInit还是OALKitlSerialInit.&nbsp;典型的种族歧视,&nbsp;居然没有OALKitlParallelinit.&nbsp;还好我们用的是SERIAL.<br />&nbsp;&nbsp;&nbsp;&nbsp;这里有个选择编译的地方,就是#ifdef&nbsp;KITL_ETHER和#ifdef&nbsp;KITL_SERIAL,&nbsp;具体定义的地方是该目录下的sources文件里面一行&nbsp;CDEFINES=$(CDEFINES)&nbsp;-DKITL_SERIAL&nbsp;-DKITL_ETHER,&nbsp;猥琐啊找了半天.&nbsp;其实我觉得既然有if结构来选了,那么选择编译也是可有可无的了.<br />&nbsp;&nbsp;&nbsp;&nbsp;好,下面就进到OALKItlSerialInit()里面.<br /><br />七.BOOL&nbsp;OALKitlSerialInit(LPSTR&nbsp;deviceId,&nbsp;OAL_KITL_DEVICE&nbsp;*pDevice,&nbsp;OAL_KITL_ARGS&nbsp;*pArgs,&nbsp;KITLTRANSPORT&nbsp;*pKitl)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;[platformcommonsrccommonkitlkitlserial.c]<br />&nbsp;&nbsp;&nbsp;&nbsp;我自己往这个kitlserial.c文件里写了六个函数.<br />&nbsp;&nbsp;&nbsp;&nbsp;BOOL&nbsp;KitlSerialInit(KITL_SERIAL_INTFO&nbsp;*pSerInfo)<br />&nbsp;&nbsp;&nbsp;&nbsp;UINT16&nbsp;KitlSerialWriteData(UINT8&nbsp;*pch,&nbsp;UINT16&nbsp;length)<br />&nbsp;&nbsp;&nbsp;&nbsp;UINT16&nbsp;KitlSerialReadData(UINT8&nbsp;*pch,&nbsp;UINT16&nbsp;length)<br />&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;KitlSerialFlowControl&nbsp;&nbsp;//stub,&nbsp;我所用的FFUART只有TXD和RXD两根线,&nbsp;RTS等都没有,&nbsp;所以FlowControl自然也应该是STUB了<br />&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;KitlSerialEnableInt(void)&nbsp;&nbsp;&nbsp;//stub,&nbsp;use&nbsp;polling<br />&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;KitlSerialDisableInt(void)&nbsp;&nbsp;//stub,&nbsp;use&nbsp;polling<br />&nbsp;&nbsp;&nbsp;&nbsp;否则前面的g_kitlDevices里面没有相应的硬件抽象层来填充.<br />&nbsp;&nbsp;&nbsp;&nbsp;上面的SerialRecv,&nbsp;Encode,&nbsp;Decode等就意思都很明显了,不用多说.&nbsp;OK现在已经走到最底层了,&nbsp;**也可以结束了.<br /><br />八、记录一下调试经验<br />&nbsp;&nbsp;&nbsp;&nbsp;虽然这是我第三次调串口了,由于没总结前面的经验,还是耗了两天才到private里面夭折的地方。实际上应该一天就能走到了。问题出在<br />1.&nbsp;第一天调试器根本用不上手。调试器软件、PB不断死翘,经常重启软件甚至重启电脑,第一天有3/4以上的时间耗在这些问题上,&nbsp;不断重启。<br />2.&nbsp;在UART初始化函数的最后,居然忘记了在interrupt&nbsp;controller&nbsp;register里面enable&nbsp;uart&nbsp;unit,&nbsp;这么乌龙的事。<br />3.&nbsp;KitlSerialFlowControl的问题.&nbsp;写的时候照搬了X86下的函数,&nbsp;没想明白到底要Control什么.&nbsp;调试时死在这里后,&nbsp;&nbsp;一开始把指向这个函数的指针设置成NULL,&nbsp;但这样PRIVATE里面有些要IF判断的函数就进不去了.&nbsp;后来换成STUB就OK了.<br />4.&nbsp;receive函数里面,&nbsp;在收每个BYTE之前先去判断了Line&nbsp;Status&nbsp;Register里面的Data&nbsp;Ready&nbsp;bit,&nbsp;如果该为零,&nbsp;则返回失败.&nbsp;但这里是有问题的,具体也没太想明白,&nbsp;反正在调试debug&nbsp;serial的时候就把这个判断从MS提供的源码里头删去了,现在做KITL&nbsp;serial时又手痒加进去,&nbsp;果然还是不行.&nbsp;这可能是MS或INTEL的一处BUG,&nbsp;但按照INTEL&nbsp;CPU&nbsp;MANUL&nbsp;UPDATE里面给的读流程,&nbsp;一开始只判断LSR里面的ERROR,&nbsp;没有判断DR位就开始读第一个BYTE了.&nbsp;读过后再判断如果DR位为1,则继续读下一BYTE.<br />5.&nbsp;在receive函数里加通过debugger加break&nbsp;point,&nbsp;结果receive&nbsp;buffer&nbsp;register里面的数据被debugger扫描去以后,就变零了,CPU上却什么都收不到,&nbsp;这事情耗了大半个下午,最后还是Jeffery发现的这个问题.<br /><br />参考**:<br />KITL解析&nbsp;by&nbsp;Nasiry&nbsp;&nbsp;(http://nasiry.cnblogs.com/archive/2004/09/22/45473.html)<br /><br />
w00m 发表于 2007-9-22 16:15 | 显示全部楼层

谢谢

收藏了
cecwxf 发表于 2011-6-5 16:20 | 显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则

4

主题

22

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部