﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-milkyway的窝-文章分类-wince(别人的文章技巧总结)</title><link>http://www.cppblog.com/milkyway/category/3375.html</link><description>多看,多想,多实验</description><language>zh-cn</language><lastBuildDate>Tue, 20 May 2008 03:36:23 GMT</lastBuildDate><pubDate>Tue, 20 May 2008 03:36:23 GMT</pubDate><ttl>60</ttl><item><title>VirtualCopy in WinCE6.0 </title><link>http://www.cppblog.com/milkyway/articles/22195.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Wed, 18 Apr 2007 02:56:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/22195.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/22195.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/22195.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/22195.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/22195.html</trackback:ping><description><![CDATA[<a href="http://blog.csdn.net/fredzeng/archive/2007/04/04/1551898.aspx">http://blog.csdn.net/fredzeng/archive/2007/04/04/1551898.aspx</a><br><br>
<p><font size=3>在CE4.2/5.0里面滚打多年的兄弟应该经常用这个函数吧。这个函数方便驱动和应用程序范围任何的物理地址，包括物理内存啊，设备控制器的寄存器啊，甚至GPIO也可以在AP里面随便拉上拉下。</font></p>
<p><font size=3>这个函数虽然方便，但是并不安全，你想你好不容易把一个功能完善的image给build出来了，结果碰到了一个写AP的&#8220;高手&#8221;，把你的寄存器和共享内存中的数据修改得一塌糊涂，最后报出bug来说你驱动的你会不会晕倒！</font></p>
<p><font size=3>还好从CE6.0开始我们可以安枕无忧了，因为AP再也不能调用VirtualCopy函数来直接访问物理地址了，但因此带来了一些应用上的不便。</font></p>
<p><font size=3>VirtualCopy的限制来源于CE6.0之后kernel的巨大变革，在CE5.0之前的Windows CE操作系统中，kenrel就仅仅是kern.exe（nk.exe），这个exe其实是OAL、KITL和Kernel三个的合体，nk.exe是运行于内核模式（kernel mode），也就具有了访问特殊地址的权限，然后除此之外的代码默认都是运行于用户模式（user mode），所以它们的驱动和AP都是等级的，都在用户模式运行，要运行在kernel模式也可以，调用一个API SetKmode（）就行了。因为驱动是肯定要访问物理地址的，所以CE5.0以前的OS都是运行用户模式的程式访问物理地址的，然后又为了方便做从物理地址到虚拟地址的映射，就提供了一系列的帮助函数，virtualcopy就是最常用的函数之一。</font></p>
<p><font size=3>CE6.0开始，kernel模式变得比较正规，类似于台式机上的windows系统了，驱动和ap的权限是严格区分的，大部分的驱动程序运行在kernel模式，它们可以用virtualcopy读写物理地址对应的物理设备，但用户模式的AP将从此没有直接访问物理地址的权限，virtualcopy每次调用都会失败返回。</font></p>
<p><font size=3>在这里还要注意的是，其实并不是用户模式就不能使用virtualcopy，virtualcopy只是不能在用户模式的AP中使用，但是却还可以在用户模式的驱动使用，但是在用户模式的驱动中使用也有条件，那就是必须在对应的注册表中设置可以访问的内存地址的范围才行。</font></p>
<p><font size=3>在某些场合，一些特殊功能的AP确实需要访问物理地址的，比如设置保存物理内存指定位置的全局变量，开发读写GPIO的测试工具等等。在这种情况下一种简单的方法是实现一个最简单的跑在kernel模式的流驱动，提供一个deviceiocontrol的接口来帮助AP申请对应于物理内存地址的虚拟内存地址。</font></p>
<p><font size=3>除了virtualcopy之外，CE6下还有很多API是AP和user模式的驱动不能调用的，给大家参考一下，大家要把CE50下的AP移植到6.0下一定要注意找到替代</font></p>
<p><font size=3><strong>Virtual Memory APIs</strong><br>CeVirtualSharedAlloc <br>LockPages <br>LockPagesEx <br>UnlockPages<br>UnlockPagesEx<br>VirtualAllocCopyEx<br>VirtualCopyEx<br>VirtualSetAttributes<br>CreateStaticMapping<br>NKDeleteStaticMapping<br>VirtualCopy</font></p>
<p><font size=3><strong>File System APIs</strong><br>ReadRegistryFromOEM <br>SetStoreQueueBase <br>WriteRegistryToOEM </font></p>
<p><font size=3><strong>Power APIs</strong><br>PowerOffSystem （很多测试AP用到）</font></p>
<p><font size=3><strong>Miscellaneous APIs<br></strong>SetOOMEvent </font></p>
<img src ="http://www.cppblog.com/milkyway/aggbug/22195.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-04-18 10:56 <a href="http://www.cppblog.com/milkyway/articles/22195.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows CE6 新特性</title><link>http://www.cppblog.com/milkyway/articles/22191.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Wed, 18 Apr 2007 01:57:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/22191.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/22191.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/22191.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/22191.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/22191.html</trackback:ping><description><![CDATA[来自 <a href="http://www.winbile.net/BBS/1015270/ShowPost.aspx">http://www.winbile.net/BBS/1015270/ShowPost.aspx</a><br><br>重新设计了Kernel，支持的进程数从32个扩展到32000 <br>每个进程的地址空间从32MB扩展到2GB <br>很多系统模块（如文件系统和设备管理器）将运行在kernel模式，OAL也从kernel独立出来，driver可以运行在kernel模式和user模式 <br>Visual Studio 2005专业版将包括一个被称为Platform Builder的功能强大的插件，它是一个专门为嵌入式平台提供的&#8220;集成开发环境&#8221;。这个集成开发环境使得整个开发链融为一体，并提供了一个从设备到应用都易于使用的工具，极大地加速了设备开发的上市 <br>Windows Embedded CE 6.0加入了新的单元核心数据和语音组件 <br>Windows Embedded CE 6.0包含的组件更便于开发者创建通过Windows Vista&#8482;内置功能无线连接到远程桌面共享体验的投影仪<br><br>来自 <a href="http://develop.csai.cn/ebd/200702031135001231.htm">http://develop.csai.cn/ebd/200702031135001231.htm</a><br><br><a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0大幅改变了核心定址以及<a href="http://www.csai.cn/incsearch/search.asp?key=%D7%CA%D4%B4" target=_blank><u><font color=#356299>资源</font></u></a>分配的机制，旧版CE同时间只能有32个程序执行于各自分配的32MB虚拟存储器空间中，6.0版则是大幅放宽了限制，最大可同时执行32,000个程序，而且每个程序可拥有独立分配的2GB虚拟存储器，在此同时，核心服务、硬件装置的驱动程序、视窗绘图以及事件子系统、档案系统等服务都被转移到系统核心保留空间中。不过这样的改变也会带给使用者疑虑，毕竟过去NT 4.0就曾经上演过类似的戏码，将驱动程序从使用者模式转移到核心模式，虽然可以大幅改进应用的速度，但是一个体质不良的驱动程序，可能就会拖垮整个系统，因此在硬件驱动程序的开发上，就必需要有个有效<a href="http://www.csai.cn/incsearch/search.asp?key=%B1%EA%D7%BC" target=_blank><u><font color=#356299>标准</font></u></a>来规范，最好还要导入如WHQL之类的驱动程序验证服务，以避免影响整个系统的稳定性。
<p>　　<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0的新变革</p>
<p>　　<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0依旧把眼光投注在ARM架构中，新的BSP与编译器也都支持了ARM的最新体系，但是其它<a href="http://www.csai.cn/incsearch/search.asp?key=%C7%B6%C8%EB%CA%BD" target=_blank><u><font color=#356299>嵌入式</font></u></a>处理器的支持也没有被忽视，威盛公司最新的处理器也在不久前宣称支持了<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0<a href="http://www.csai.cn/incsearch/search.asp?key=%B2%D9%D7%F7%CF%B5%CD%B3" target=_blank><u><font color=#356299>操作系统</font></u></a>。而6.0版也是<a href="http://www.csai.cn/incsearch/search.asp?key=%CE%A2%C8%ED" target=_blank><u><font color=#356299>微软</font></u></a>首个导入次世代档案系统ExFAT的<a href="http://www.csai.cn/incsearch/search.asp?key=%B2%D9%D7%F7%CF%B5%CD%B3" target=_blank><u><font color=#356299>操作系统</font></u></a>，虽然到目前为止其细节还不明了，但是根据已有的信息指出，ExFAT在<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0中，担当了总管所有外接储存媒体的中界层的角色，广为产业界所称赞的是，这能够解除过去传统FAT档案系统的32GB单一容量限制。ExFAT同样也解除了单一档案只能在2GB以下的限制，这对于硬件厂商以<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE发展大容量储存管理伺服架构，有著相当大的帮助。加上一些安全机制，我们可以把ExFAT视为<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0上的NTFS加强版。</p>
<p>　　VoIP也是<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0另一个持续加强的重点，除了在应用程序层的整合更进一步以外，<a href="http://www.csai.cn/incsearch/search.asp?key=%B2%D9%D7%F7%CF%B5%CD%B3" target=_blank><u><font color=#356299>操作系统</font></u></a>核心也具备直接支持的能力，因此硬件开发上可以更容易的在<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE环境上进行各种网络的语音通讯服务。而因应这样的趋势，6.0版自然也把过去5.0版的缺失补正过来，在网络堆叠协定方面，直接支持了802.11i、WAP2、802.11e（无线QoS）、蓝牙A2DP/AVRCP的AES加密等等，为无线通讯建立了一个稳定、安全以及可靠的应用环境。</p>
<p>　　而从使用者观点来看，<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0扩展了超越以往版本总和的承诺，这些功能包括了最新的多媒体能力，诸如<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> Media 10/11的支持、，对于网络多媒体装置的原生整合能力，在Platform Builder开发工具中，甚至也加入了行动媒体中心的支持，可以藉由<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> Media Connect 2.0大幅强化多媒体应用的支持能力，并且可以与其它<a href="http://www.csai.cn/incsearch/search.asp?key=%CE%A2%C8%ED" target=_blank><u><font color=#356299>微软</font></u></a>的<a href="http://www.csai.cn/incsearch/search.asp?key=%B2%D9%D7%F7%CF%B5%CD%B3" target=_blank><u><font color=#356299>操作系统</font></u></a>或硬件装置做同步统合的动作。这些功能包含了以下项目：</p>
<p>　　■ TIFF编解码器的支持<br>　　■ HD-DVD的解码器支持<br>　　■ MPEG-2解码器<br>　　■ 更多的影音编码与格式支持<br>　　■ UDF 2.5格式的支持<br>　　■ 虚拟环绕声道的支持<br>　　■ 多轨音效的支持<br>　　■ 强化DirectDraw，可支持电视使用的交错显示模式<br>　　■ USB OTG功能加入，可作为USB的控制端</p>
<p>　　虽然在核心部分做出这么大的更新，但是<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0的储存上并没有如<a href="http://www.csai.cn/incsearch/search.asp?key=%CE%A2%C8%ED" target=_blank><u><font color=#356299>微软</font></u></a>其它<a href="http://www.csai.cn/incsearch/search.asp?key=%B2%D9%D7%F7%CF%B5%CD%B3" target=_blank><u><font color=#356299>操作系统</font></u></a>般的飞涨（Vista甚至需要超过10GB的初始储存安装空间！），相较起5.0版，6.0在体积上也不过增加了5％左右，虽然这对<a href="http://www.csai.cn/incsearch/search.asp?key=%C7%B6%C8%EB%CA%BD" target=_blank><u><font color=#356299>嵌入式</font></u></a>系统产业来说是理所当然，但对于<a href="http://www.csai.cn/incsearch/search.asp?key=%CE%A2%C8%ED" target=_blank><u><font color=#356299>微软</font></u></a>可以说是另一项奇迹。</p>
<p>　　<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0带给开发者的好处</p>
<p>　　在开放原始码的历史中，<a href="http://www.csai.cn/incsearch/search.asp?key=%CE%A2%C8%ED" target=_blank><u><font color=#356299>微软</font></u></a>要写下另一个新的里程碑，100％对产品开发者释放出原始码，且可允许厂商进行自订的变更或订做，而无须释放出经过修改的程序码，虽然在广义上并不能视为真正开放，但是为这些喜欢藏私留一手的厂商来说，无疑是增加竞争力的最佳手段之一。而作为开发工具的Visual Studio 2005 PRO将会作为<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0的整体套件之一，内建的许多开发工具与定义对于开发者来说相对便利许多。</p>
<p>　　<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0支持了<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> <a href="http://www.csai.cn/incsearch/search.asp?key=%2ENET" target=_blank><u><font color=#356299>.NET</font></u></a> Compact Framework 2.0作为应用程序管理开发以及Win32、 MFC、 ATL、 WTL和STL等<a href="http://www.csai.cn/incsearch/search.asp?key=%B3%CC%D0%F2%BF%AA%B7%A2" target=_blank><u><font color=#356299>程序开发</font></u></a>界面提供给开发原生应用程序的开发者使用。具备了如此势力庞大以及完整的开发环境作为支持，开发者与制造商也可确保后续的支持不虞匮乏。</p>
<p>　　而在硬件方面，根据<a href="http://www.csai.cn/incsearch/search.asp?key=%CE%A2%C8%ED" target=_blank><u><font color=#356299>微软</font></u></a>方面的说法，在不变更原有的硬件架构之下，导入<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0可以大幅改善原有程序的执行效率，并且也容许同时间有更多程序同步执行，由于每个程序都具备有独立的执行空间，特定程序当掉，也不会影响到其它应用程序或系统执行，提供给使用者比起以往旧版系统更强的稳固性与更大的弹性。而目前逐渐风行的多核心处理架构上，<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0也可以在不变更<a href="http://www.csai.cn/incsearch/search.asp?key=%B3%CC%D0%F2%BF%AA%B7%A2" target=_blank><u><font color=#356299>程序开发</font></u></a>者原有程序模型的状况之下，提供最佳化的核心工作自动分配与指定，当然，如果有需求的话，<a href="http://www.csai.cn/incsearch/search.asp?key=%B3%CC%D0%F2%BF%AA%B7%A2" target=_blank><u><font color=#356299>程序开发</font></u></a>者依然可以自行决定指定核心的方式。</p>
<p>　　市场上的实际应用与结论</p>
<p>　　截至目前为止，台湾已经有研华科技在针对物流、仓储管理、公共服务以及领域维护方面的应用，进行基于<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0<a href="http://www.csai.cn/incsearch/search.asp?key=%B2%D9%D7%F7%CF%B5%CD%B3" target=_blank><u><font color=#356299>操作系统</font></u></a>下的工业级掌上型终端机的开发，而精技计算机则是开发了可应用于搜集资料、RFID、BarCode管理的垂直市场PDA，采用<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0中的cell core元件，缩短GPRS端的资料传输设计时程。而各大手持式智能装置的开发者也都已经在着手导入<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0，实际产品预计将会于2007年第二季之后逐渐浮现台面。</p>
<p>　　100％开放原始码的创举对于<a href="http://www.csai.cn/incsearch/search.asp?key=%CE%A2%C8%ED" target=_blank><u><font color=#356299>微软</font></u></a>来说是个相当大的突破，虽然这有一大部分是因为<a href="http://www.csai.cn/incsearch/search.asp?key=%C7%B6%C8%EB%CA%BD" target=_blank><u><font color=#356299>嵌入式</font></u></a><a href="http://www.csai.cn/incsearch/search.asp?key=Linux" target=_blank><u><font color=#356299>Linux</font></u></a>所带来的竞争挑战所致，但是鹬蚌相争的结果，带给开发伙伴的好处也远大于过去采用封闭模式的流程，而延续过去<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 5.0的成果，<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE 6.0也将会继续在<a href="http://www.csai.cn/incsearch/search.asp?key=%C7%B6%C8%EB%CA%BD" target=_blank><u><font color=#356299>嵌入式</font></u></a>应用、行动装置、GPS、智能型<a href="http://www.csai.cn/incsearch/search.asp?key=%CA%D6%BB%FA" target=_blank><u><font color=#356299>手机</font></u></a>等市场继续攻城掠地，不过在面对市场上诸多对手的竞争，<a href="http://www.csai.cn/incsearch/search.asp?key=%CE%A2%C8%ED" target=_blank><u><font color=#356299>微软</font></u></a>仍须做出更多的改进。比如在修正漏洞方面可以更快速的反应、并且提供给开发者更大的支持力度等等，当开发者甜头吃的够多，自然也会对<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE<a href="http://www.csai.cn/incsearch/search.asp?key=Windows" target=_blank><u><font color=#356299>Windows</font></u></a> CE架构更为忠诚，出现在市面上的产品自然也会更为成熟。<br></p>
<br>
<img src ="http://www.cppblog.com/milkyway/aggbug/22191.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-04-18 09:57 <a href="http://www.cppblog.com/milkyway/articles/22191.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VirtualAlloc/Copy and MmMapIospace</title><link>http://www.cppblog.com/milkyway/articles/22123.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Tue, 17 Apr 2007 05:38:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/22123.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/22123.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/22123.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/22123.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/22123.html</trackback:ping><description><![CDATA[Tuesday, March 20, 2007 12:35 PM by <a id=ctl00___ctl00___ctl01___Comments___Comments_ctl04_NameLink title="Kurt Kennett" target=_blank>Kurt Kennett</a>
<div class=commentsbody>
<p>VirtualCopy can be a bit confusing to use.</p>
<p><a href="http://msdn2.microsoft.com/en-us/library/aa908789.aspx" target=_new rel=nofollow>http://msdn2.microsoft.com/en-us/library/aa908789.aspx</a></p>
<p>Let's look at the parameters. &nbsp;I've commented with -- after each of them.</p>
<p>lpvDest </p>
<p>[in] Pointer to the destination memory, which must be reserved.</p>
<p>-- This means that the destination range of VIRTUAL memory must already be reserved by a call to VirtualAlloc(). &nbsp;You must have allocated a range of virtual memory that you are going to map to some physical range.</p>
<p><span style="COLOR: red">lpvSrc</span> </p>
<p>[in] Pointer to committed memory.</p>
<p>-- This is the range of *either* VIRTUAL *or* PHYSICAL memory that you want to map the range specified by the 'lpvDest' parameter to. &nbsp;</p>
<p>If you specify a VIRTUAL address and omit the PAGE_PHYSICAL flag from the fdwProtect parameter, then you are simply saying "Copy the mapping at the lpvSrc address to the lpvDest address". &nbsp;This just means that there will be two ranges of virtual memory that point to the same physical range.</p>
<p>If you specify a PHYSICAL address (shifted right 8 bits) and include the PAGE_PHYSICAL flag in the fdwProtect parameter, then you are saying "Map the range at the lpvDest address to this specific physical address". &nbsp;This sets your new range of virtual memory to point to a piece of physical memory.</p>
<p>cbSize </p>
<p>[in] Size, in bytes, of the region. The allocated pages include all pages containing one or more bytes in the range from lpAddress to lpAddress+cbSize. This means that a 2-byte range straddling a page boundary causes both pages to be included in the allocated region.</p>
<p>-- pretty straight forward here.</p>
<p>fdwProtect </p>
<p>[in] Type of access protection. If the pages are being committed, any one of a number of flags can be specified, along with the PAGE_GUARD and PAGE_NOCACHE, protection modifier flags. The following table shows the flags that can be specified.</p>
<p>-- 'Reserving' a page means you're allocating a range of virtual memory but not pointing it at anything yet. &nbsp;'Commiting' a page means you are actually taking up physical storage somewhere - be it in RAM or in physical addres space. &nbsp;For the purposes of our discussion here, you would normally map registers and i/o space with PAGE_NOCACHE. &nbsp;If you used a physical address (shifted right 8 bits) in the lpvSrc parameter, then you would also specify the PAGE_PHYSICAL flag.<br><br><br>quetion: </p>
<p>If I want to access some physical memory in my driver, &nbsp;can I do like these way?</p>
<p>Method (1) define static map relationship in OEMAddressTable and reserve difined virtual address in config.bib first, then use VirtualAlloc() and VirtualCopy() without the page_physical parameter.</p>
<p>or (2) &nbsp;directly use MmMapIoSpace() or VirtualAlloc+Copy() with the page_physical parameter<br><br>answer:<br>Yes, either of those would work I believe. &nbsp;#2 is the preferred/recommended method<br><br><br>Monday, March 26, 2007 12:07 PM by <a id=ctl00___ctl00___ctl01___Comments___Comments_ctl12_NameLink title="Kurt Kennett" target=_blank>Kurt Kennett</a> </p>
<div class=commentsbody>
<p>Wow! &nbsp;Lots of interest in VirtualCopy! &nbsp;Ok, I'll try to be super-clear here.</p>
<p>VirtualCopy *copies* or *sets* a range of virtual addresses. &nbsp;</p>
<p>&nbsp; &nbsp;- You use it to *copy* an existing Virtual-&gt;Physical mapping (no matter where it is). &nbsp;</p>
<p>OR</p>
<p>&nbsp; &nbsp;- You use it to *set* a mapping to a range of physical addresses.</p>
<p>In *either* case, the virtual memory you want to create the new map in must already be allocated (via VirtualAlloc()).</p>
<p>OEMAddressTable is a static (unchanging, available at startup without doing any work or setup) table of virtual -&gt; physical mappings. &nbsp;The kernel is the only thing that has default access to the resources mapped by this table. &nbsp;If you are operating outside the OAL (i.e. in any kind of driver or application), you must use VirtualCopy() to copy or create memory page mappings. &nbsp;As mentioned above, you can copy any existing mapping as long as you have access to it. &nbsp;This includes a static mapping done by the OEMAddressTable. &nbsp;Some people will map all resources in the OEMAddressTable (so the kernel has access to everything), then just copy those mappings in drivers when they need to. &nbsp;This is not a best practice because it makes driver code less portable -- it is better to read the physical address of a component from the registry, then use the value found there to map to it. If you do this your driver code does not have to change if it is moved to a different platform or extended to use multiple components in different physical locations.</p>
<p>A mapping does not have to exist in OEMAddressTable in order for you to access the physical resources mapped. &nbsp;You can create a new mapping unknown to the OEMAddressTable by using VirtualCopy with the PAGE_PHYSICAL flag.</p>
<p>MmMapIoSpace is a CEDDK function -- it simply does the appropriate calls to VirtualAlloc/VirtualCopy. &nbsp;You could write your own MmMapIoSpace if you wanted to. &nbsp;Some people have in the past - calling the function "VirtualMemCopyPhysical" or something like that. &nbsp;Some platforms need to modify the MmMapIoSpace to set Virtual mapping attributes when pages are mapped (hence the original purpose for this blog entry).</p>
<p>Using flags like PAGE_EXECUTE_READWRITE with VirtualCopy are a 'request'. &nbsp;If the platform/CPU does not have a differentiation between executable pages and non-executable pages, the EXECUTE property will not be able to be set. &nbsp;For example, the X86 CPUs can explicitly state that memory is executable, but can't be read or written to. &nbsp;The ARM processors have no notion of this - you can read it or read and write it.</p>
<br><br>
<p>In Windows CE <span style="COLOR: red">5.0 and earlier</span>, virtual allocations below 2MB come out of the address space of the process calling it, while allocations above 2MB come out of the shared address space.</p>
<p>Sue</p>
<br></div>
</div>
<img src ="http://www.cppblog.com/milkyway/aggbug/22123.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-04-17 13:38 <a href="http://www.cppblog.com/milkyway/articles/22123.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ce内存映射的点点滴滴</title><link>http://www.cppblog.com/milkyway/articles/18270.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Fri, 02 Feb 2007 01:27:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/18270.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/18270.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/18270.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/18270.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/18270.html</trackback:ping><description><![CDATA[
		<table style="TABLE-LAYOUT: fixed; WORD-BREAK: break-all" cellspacing="1" cellpadding="3" width="98%" bgcolor="#cccccc" border="0">
				<tbody>
						<tr bgcolor="#f8f8f8">
								<td>wy12218 发表于 2006-11-9 18:17:00 </td>
						</tr>
						<tr bgcolor="#ffffff">
								<td height="0">
										<table cellspacing="0" cellpadding="0" width="100%" border="0">
												<tbody>
														<tr>
																<td>
																		<p>OEMAddressTable里定义的映射关系是给ARM MMU用的,是在KernelStart(source code参考wince420private目录)时建立的,只要WINCE还在跑,就不会解除. <br />OEMAddressTable里的Virtual Addr和Physical Addr是对ARM来说的. 其实对于WINCE,就只能访问到它的Virtual address. 也就是说,OEMAddressTable里的Virtual address对WINCE 系统来说才是Physical Address. <br /><br />经过OEMAddressTable映射后的系统的物理地址,在0x80000000~0x9fffffff之间.是caching and buffering的地址,这个地址加上0x20000000,就是它的cache &amp; buffering disabled地址.所有的硬件寄存器的地址都在这个地址段上,受MMU保护的. <br /><br />上面讲的系统的物理地址,从0x80000000~0xbfffffff,在Kernel Mode下都可以直接访问. ISR是在KERNEL里,也就可以直接访问这些系统的物理地址.无所谓"因为ISR只能访问静态映射的虚拟地址". <br /><br />上面说过,对于ARM来说,有虚拟地址和物理地址之分,对于WINCE来说,也有虚拟地址和物理地址之分. 可以这么说,ARM的虚拟地址就是WINCE系统的物理地址. <br />32位的OS总共有4G的虚拟地址空间,WINCE也不例外. 其中,0x00000000~0x80000000是Application Space; 0x80000000~0xffffffff是System Reserved. 系统的物理地址就在System Reserved的这段,只能在KERNEL MODE访问. 那么,当APPLICATION和DRIVER(都是运行在USER MODE)要访问这些在System Reserved地址段的硬件寄存器或MEMORY怎么办呢? 只好再建立一层映射关系,在Application Space里分配一段空间,把它映射到System Reserved里的地址上,这就是VirtualAlloc/Copy和MmMapIoSpace干的事情.</p>
																		<p>如果你的地址是这样声明的: <br />#define RTC_COUNTER *((volatile unsigned *)0x91000000) <br />那么直接读写就可以了,比如: <br />int nRtc = RTC_COUNTER; <br />RTC_COUNTER = nRtc; <br /><br />否则,可以用: <br />int nRtc = READ_REGISTER_ULONG(0x91000000); <br />WRITE_REGISTER_ULONG(0X91000000, nRtc); <br /><br />其实这两种方式的本质是一样的,都是把地址声明成某个数据类型,然后就可以直接读写了.下面是READ_REGISTER_ULONG()和WRITE_REGISTER_ULONG()的定义: <br />#define READ_REGISTER_ULONG(reg) (*(volatile unsigned long * const)(reg)) <br />#define WRITE_REGISTER_ULONG(reg, val) (*(volatile unsigned long * const)(reg)) = (val)</p>
																		<p> </p>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cppblog.com/milkyway/aggbug/18270.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-02-02 09:27 <a href="http://www.cppblog.com/milkyway/articles/18270.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>wince中断问题</title><link>http://www.cppblog.com/milkyway/articles/18268.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Fri, 02 Feb 2007 01:25:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/18268.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/18268.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/18268.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/18268.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/18268.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 我现在遇到的问题是不知道如何编写硬件触发中断处理程序 环境为wince.net x86,代码如下希望您给予关注 //ÖÐ¶Ï´¦Àí²¿·Ö HANDLE g_hevInterrupt;; HANDLE g_htIST; DWORD g_dwSysInt; DWORD SetupInterrupt ( void ); DWORD WINAPI ThreadIST ( LPVOID lpvParam...&nbsp;&nbsp;<a href='http://www.cppblog.com/milkyway/articles/18268.html'>阅读全文</a><img src ="http://www.cppblog.com/milkyway/aggbug/18268.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-02-02 09:25 <a href="http://www.cppblog.com/milkyway/articles/18268.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinCE中OEM适配层编程点滴之创建OAL</title><link>http://www.cppblog.com/milkyway/articles/18267.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Fri, 02 Feb 2007 01:23:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/18267.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/18267.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/18267.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/18267.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/18267.html</trackback:ping><description><![CDATA[付林林： 
<p>　　2001年计算机专业毕业。从毕业起一直从事软件开发工作。目前从事 Windows CE 下操作系统内核定制和应用程序开发。在实际工作中积累了CE下开发的一些经验。希望和 CE 下开发者交流、探讨，更希望你们能不吝赐教。我的EMail：windowsce@tom.com <br /><br />　　如果您有技术问题向我咨询，请登录<a href="http://club.yesky.com/bbs/jsp/list.jsp?forumID=378" target="_blank"><font color="#0000ff" size="2">天极网嵌入式开发论坛</font></a>，本人将在此论坛回复您的问题。在论坛上交流会更方便些，其它网友也可以回答参与，弥补了我的不足。</p><p>　　<a href="http://dev.yesky.com/SoftChannel/72340168526266368/20040908/1851586.shtml" target="_blank"><font color="#ff0000" size="2">进入作者专栏</font></a><br /></p>　　正文<br /><br />　　正如CE的帮助文档所言，创建OAL是一个非常复杂的任务，而通常的办法是复制原有的相同平台的OAL代码，然后修改来适应平台的特殊要求。也就是说对于没有特殊要求的平台，复制原有相同平台的OAL代码就足够了。由于OAL的复杂性在这篇文章中我只讲解常用的部分。 <br /><br />　　<b>一、实现ISR</b><br /><br />　　1. ISR的概念<br /><br />　　ISR（interrupt service routine）是处理IRQs（interrupt request line）的程序。Windows CE用一个ISR来处理所有的IRQ请求。当一个中断发生时，内核的异常处理程序先调用内核ISR，内核ISR禁用所有具有相同优先级和较低优先级的中断，然后调用已经注册的OAL ISR程序，一般ISR有下列特征：<br /><br />　　1) 执行最小的中断处理，最小的中断处理指能够检验、答复产生中断的硬件，而把更多的处理工作留给IST（interrupt service thread）。<br /><br />　　2) 当ISR完成时返回中断ID（中断ID大部分是预定义的）。<br /><br />　　2. X86平台的ISR结构<br /><br />　　X86平台的ISR保存在%_WINCEROOT%\PUBLIC\COMMON\OAK\CSP\I486\OAL\fwpc.c中，函数名为PeRPISR。下面分析一下此函数的主要代码：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>ULONG PeRPISR(void)<br />{<br />　ULONG ulRet = SYSINTR_NOP; ///返回值，既中断ID（以SYSINTR_为前缀）<br />　UCHAR ucCurrentInterrupt; ///当前中断号<br />　if (fIntrTime) ////// fIntrTime 用于测试SR和IST的延时时间，测试工具为ILTiming.exe。<br />　　......<br />　　ucCurrentInterrupt = PICGetCurrentInterrupt(); ////返回当前中断IRQ<br />　if (ucCurrentInterrupt == INTR_TIMER0) ///IRQ0，IRQ0为系统时钟（system tick）中断，具体见“二、实现系统时钟”<br />　......<br />　if (dwRebootAddress) ////是否需要重启动<br />　　RebootHandler(); <br />　　......<br />　if(ucCurrentInterrupt == INTR_RTC) ////IRQ8，real-time clock的中断<br />　　......<br />　else if (ucCurrentInterrupt &lt;= INTR_MAXIMUM) ///如果中断小于 INTR_MAXIMUM<br />　{<br />　　ulRet = NKCallIntChain(ucCurrentInterrupt); ////调用中断链<br />　　if (ulRet == SYSINTR_CHAIN) ///如果中断链未包含中断<br />　　　ulRet = OEMTranslateIrq(ucCurrentInterrupt); ////在IRQ 和SYSINTR之间转换，此函数返回IRQ对应的SYSINTR<br />　　　......<br />　　　PICEnableInterrupt(ucCurrentInterrupt, FALSE); ///启用除当前中断以外的所有中断<br />　} ///else if<br />　OEMIndicateIntSource(ulRet); ///通知内核已经发生SYSINTR中断<br />}</td></tr></tbody></table><br />　　从以上代码不难看出ISR的任务就是返回以“SYSINTR_”为前缀的中断ID，如果不需要进一步执行IST，那么就返回SYSINTR_NOP。<br /><br />　　3. 中断注册步骤<br /><br />　　参考X86平台的代码，中断注册步骤如下：<br /><br />　　1) 用SETUP_INTERRUPT_MAP宏关联SYSINTR和IRQ。以“SYSINTR_”为前缀的常量由内核使用，用于唯一标识发生中断的硬件。在Nkintr.h文件中预定义了一些SYSINTR，OEM可以在Oalintr.h文件中自定义SYSINTR。<br /><br />　　2) 用HookInterrupt函数关联硬件中断号和ISR。这里的硬件中断号为物理中断号，而非逻辑中断号IRQ。在InitPICs函数（和上述ISR位于同一文件）的最后调用了HookInterrupt函数，如下：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>for (i = 64; i &lt; 80; i++)<br />　HookInterrupt(i, (void *)PeRPISR); ///用ISR关联16个中断号 </td></tr></tbody></table><br />　　4. 中断处理步骤<br /><br />　　1) 调用InterruptInitialize函数关联SYSINTR和IST，具体是关联IST等待的事件。一般在驱动程序中按如下编写：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>hEvent = CreateEvent(...) ///创建一个事件对象<br />InterruptInitialize(SYSINTR_SERIAL, hEvent, ...) ///关联一个串口中断ID和这个事件<br />hThd = CreateThread(..., MyISTRoutine, hEvent, ...) ///创建一个线程（IST）<br />CeSetThreadPriority(hThd, 152); ///提高此线程的优先级</td></tr></tbody></table><br />　　2) IST执行I/O操作，一般IST按如下编写：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>for(;;) ///驱动程序一直处于服务状态<br />{<br />　WaitForSingleObject(hEvent, INFINITE); ////无限等待事件<br />　...... //// I/O操作<br />　InterruptDone(InterruptId); ///结束当前中断处理<br />}</td></tr></tbody></table><br />　　3) ISR和IST之间数据传输<br /><br />　　假如我们要从一个设备频繁的读取数据而每次读取量非常少，那么每次读取都要调用IST会降低性能。作为解决方案，ISR可以做读取工作（存放到缓冲区），并在缓冲区存放满后由IST到缓冲区读取。因为ISR运行在内核模式而IST运行在用户模式，IST不能轻易地访问ISR的缓冲区，为此CE提供了一个办法（参见标题为“Passing Data between an ISR and an IST”的帮助文档），您也可以到<a href="http://club.yesky.com/bbs/jsp/list.jsp?forumID=378" target="_blank"><font color="#0000ff" size="2">天极网嵌入式开发论坛</font></a>询问。<br /><br />　　<b>二、实现系统时钟</b><br /><br />　　1. 系统时钟（system tick）概念<br /><br />　　系统时钟是内核需要的唯一中断（IRQ0），系统时钟每毫秒产生一个中断，当发生中断时内核在ISR中累计，到1000的倍数就是过了一秒钟。在处理系统时钟的ISR中不仅要累计计数，还要决定是否通知内核开始重新调度当前所有的线程。要实现一个OAL，系统时钟是第一个必须做的事。<br /><br />　　2. X86平台系统时钟中断的处理工作 系统时钟由InitClock函数负责初始化工作，一般是在OEMInit函数中调用。当发生中断时，ISR首先用下列语句累计计数：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>CurMSec += SYSTEM_TICK_MS; /////SYSTEM_TICK_MS = 1 </td></tr></tbody></table><br />　　然后根据下列语句判断应该返回什么值：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>if ((int) (dwReschedTime – CurMSec) &gt;= 0) <br />　return SYSINTR_RESCHED; ///重新调度<br />else<br />　return SYSINTR_NOP; ///不再执行任何操作</td></tr></tbody></table><br />　　上述代码中全局变量dwReschedTime在schedule.c中定义，也就是由内核的调度模块决定在何时开始重新调度线程。CurMSec累计了从WindowsCE启动到当前总共产生了多少个system tick。实现系统时钟后还要实现OEMIdle函数，当没有线程准备运行时OEMIdle被调用，OEMIdle函数将CPU置于空闲模式，但在空闲模式下仍然要累计系统时钟。<br /><br />　　<b>三、I/O控制代码</b><br /><br />　　1. I/O控制代码作用<br /><br />　　应用软件或者驱动程序可以调用KernelIoControl函数与OAL层通信，而KernelIoControl在内部调用OEMIoControl函数。OEMIoControl是一个OAL API，OEM可以在OEMIoControl中编写自己的I/O控制代码实现一些功能，或者说与应用软件通信。I/O控制代码常用的例子如重启计算机、得到系统信息、设置RTC、得到设备ID等。还有一些系统程序使用的特殊的I/O控制代码。在这里说明一下，我经过实验证实CE提供的得到设备ID方法并非有效。<br /><br />　　2. 编写自己的I/O控制代码步骤<br /><br />　　1) 在pkfuncs.h或者新编写一个.h文件中按如下格式定义：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>#define IOCTL_MY_CONTROL CTL_CODE(FILE_DEVICE_HAL, 3000, METHOD_NEITHER, FILE_ANY_ACCESS)</td></tr></tbody></table><br />　　2) 在oemioctl.c中修改OEMIoControl函数，添加如下代码：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>case IOCTL_MY_CONTROL:<br /><br />......</td></tr></tbody></table><br />　　3) 在应用程序中调用KernelIoControl函数，具体参数参见帮助文档<img src ="http://www.cppblog.com/milkyway/aggbug/18267.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-02-02 09:23 <a href="http://www.cppblog.com/milkyway/articles/18267.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>NAND and NOR and XIP</title><link>http://www.cppblog.com/milkyway/articles/18266.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Fri, 02 Feb 2007 01:20:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/18266.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/18266.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/18266.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/18266.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/18266.html</trackback:ping><description><![CDATA[来自 <a href="http://www.student.buct.edu.cn/snc/blog/blog.asp?name=wy12218&amp;month=2003-9">http://www.student.buct.edu.cn/snc/blog/blog.asp?name=wy12218&amp;month=2003-9</a><br /><br />NOR和NAND是现在市场上两种主要的非易失闪存技术。Intel于1988年首先开发出NOR flash技术，彻底改变了原先由EPROM和EEPROM一统天下的局面。紧接着，1989年，东芝公司发表了NAND flash结构，强调降低每比特的成本，更高的性能，并且象磁盘一样可以通过接口轻松升级。但是经过了十多年之后，仍然有相当多的硬件工程师分不清NOR和NAND闪存。 <br />相“flash存储器”经常可以与相“NOR存储器”互换使用。许多业内人士也搞不清楚NAND闪存技术相对于NOR技术的优越之处，因为大多数情况下闪存只是用来存储少量的代码，这时NOR闪存更适合一些。而NAND则是高数据存储密度的理想解决方案。 <br />NOR的特点是芯片内执行(XIP, eXecute In Place)，这样应用程序可以直接在flash闪存内运行，不必再把代码读到系统RAM中。NOR的传输效率很高，在1～4MB的小容量时具有很高的成本效益，但是很低的写入和擦除速度大大影响了它的性能。 <br />NAND结构能提供极高的单元密度，可以达到高存储密度，并且写入和擦除的速度也很快。应用NAND的困难在于flash的管理和需要特殊的系统接口。 <br /><br />性能比较 <br />flash闪存是非易失存储器，可以对称为块的存储器单元块进行擦写和再编程。任何flash器件的写入操作只能在空或已擦除的单元内进行，所以大多数情况下，在进行写入操作之前必须先执行擦除。NAND器件执行擦除操作是十分简单的，而NOR则要求在进行擦除前先要将目标块内所有的位都写为0。 <br />由于擦除NOR器件时是以64～128KB的块进行的，执行一个写入/擦除操作的时间为5s，与此相反，擦除NAND器件是以8～32KB的块进行的，执行相同的操作最多只需要4ms。 <br />执行擦除时块尺寸的不同进一步拉大了NOR和NADN之间的性能差距，统计表明，对于给定的一套写入操作(尤其是更新小文件时)，更多的擦除操作必须在基于NOR的单元中进行。这样，当选择存储解决方案时，设计师必须权衡以下的各项因素。 <br />● NOR的读速度比NAND稍快一些。 <br />● NAND的写入速度比NOR快很多。 <br />● NAND的4ms擦除速度远比NOR的5s快。 <br />● 大多数写入操作需要先进行擦除操作。 <br />● NAND的擦除单元更小，相应的擦除电路更少。 <br /><br />接口差别 <br />NOR flash带有SRAM接口，有足够的地址引脚来寻址，可以很容易地存取其内部的每一个字节。 <br />NAND器件使用复杂的I/O口来串行地存取数据，各个产品或厂商的方法可能各不相同。8个引脚用来传送控制、地址和数据信息。 <br />NAND读和写操作采用512字节的块，这一点有点像硬盘管理此类操作，很自然地，基于NAND的存储器就可以取代硬盘或其他块设备。 <br /><br />容量和成本 <br />NAND flash的单元尺寸几乎是NOR器件的一半，由于生产过程更为简单，NAND结构可以在给定的模具尺寸内提供更高的容量，也就相应地降低了价格。 <br />NOR flash占据了容量为1～16MB闪存市场的大部分，而NAND flash只是用在8～128MB的产品当中，这也说明NOR主要应用在代码存储介质中，NAND适合于数据存储，NAND在CompactFlash、Secure Digital、PC Cards和MMC存储卡市场上所占份额最大。 <br /><br />可*性和耐用性 <br />采用flahs介质时一个需要重点考虑的问题是可*性。对于需要扩展MTBF的系统来说，Flash是非常合适的存储方案。可以从寿命(耐用性)、位交换和坏块处理三个方面来比较NOR和NAND的可*性。 <br />寿命(耐用性) <br />在NAND闪存中每个块的最大擦写次数是一百万次，而NOR的擦写次数是十万次。NAND存储器除了具有10比1的块擦除周期优势，典型的NAND块尺寸要比NOR器件小8倍，每个NAND存储器块在给定的时间内的删除次数要少一些。 <br />位交换 <br />所有flash器件都受位交换现象的困扰。在某些情况下(很少见，NAND发生的次数要比NOR多)，一个比特位会发生反转或被报告反转了。 <br />一位的变化可能不很明显，但是如果发生在一个关键文件上，这个小小的故障可能导致系统停机。如果只是报告有问题，多读几次就可能解决了。 <br />当然，如果这个位真的改变了，就必须采用错误探测/错误更正(EDC/ECC)算法。位反转的问题更多见于NAND闪存，NAND的供应商建议使用NAND闪存的时候，同时使用EDC/ECC算法。 <br />这个问题对于用NAND存储多媒体信息时倒不是致命的。当然，如果用本地存储设备来存储操作系统、配置文件或其他敏感信息时，必须使用EDC/ECC系统以确保可*性。 <br />坏块处理 <br />NAND器件中的坏块是随机分布的。以前也曾有过消除坏块的努力，但发现成品率太低，代价太高，根本不划算。 <br />NAND器件需要对介质进行初始化扫描以发现坏块，并将坏块标记为不可用。在已制成的器件中，如果通过可*的方法不能进行这项处理，将导致高故障率。 <br /><br />易于使用 <br />可以非常直接地使用基于NOR的闪存，可以像其他存储器那样连接，并可以在上面直接运行代码。 <br />由于需要I/O接口，NAND要复杂得多。各种NAND器件的存取方法因厂家而异。 <br />在使用NAND器件时，必须先写入驱动程序，才能继续执行其他操作。向NAND器件写入信息需要相当的技巧，因为设计师绝不能向坏块写入，这就意味着在NAND器件上自始至终都必须进行虚拟映射。 <br /><br />软件支持 <br />当讨论软件支持的时候，应该区别基本的读/写/擦操作和高一级的用于磁盘仿真和闪存管理算法的软件，包括性能优化。 <br />在NOR器件上运行代码不需要任何的软件支持，在NAND器件上进行同样操作时，通常需要驱动程序，也就是内存技术驱动程序(MTD)，NAND和NOR器件在进行写入和擦除操作时都需要MTD。 <br />使用NOR器件时所需要的MTD要相对少一些，许多厂商都提供用于NOR器件的更高级软件，这其中包括M-System的TrueFFS驱动，该驱动被Wind River System、Microsoft、QNX Software System、Symbian和Intel等厂商所采用。 <br />驱动还用于对DiskOnChip产品进行仿真和NAND闪存的管理，包括纠错、坏块处理和损耗平衡<img src ="http://www.cppblog.com/milkyway/aggbug/18266.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-02-02 09:20 <a href="http://www.cppblog.com/milkyway/articles/18266.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WINCE的内存配置</title><link>http://www.cppblog.com/milkyway/articles/17845.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Sat, 20 Jan 2007 13:25:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17845.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17845.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17845.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17845.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17845.html</trackback:ping><description><![CDATA[
		<p>by  microsun<br /><br />WINCE的内存（包括SDRAM及FLASH）的配置包含两个方面：源代码(包括C和汇编)中的定义,及系统配置文件CONFIG.BIB中的定义。源代码中需要定义内存的物理及虚拟地址，大小，并初始化名为OEMAddressTable的结构数组，以告知系统物理地址与虚拟地址的对应关系，系统根据其设置生成MMU页表。而CONFIG.BIB中一般会将内存定义成不同的段，各段用作不同的用途。<br /><br />CONFIG.BIB文件<br />CONFIG.BIB文件分两个部分，我们且称之为段，MEMORY段和CONFIG段。MEMORY段定义内存的分片方法，CONFIG段定义系统其它的一些属性。以下是一个CONFIG。BIB文件MEMORY段的例子：<br />MEMORY<br />； 名称 起始地址 大小 属性<br />RESERVED 80000000 00008000 RESERVED<br />DRV_GLB 80008000 00001000 RESERVED<br />CS8900 80010000 00030000 RESERVED<br />EDBG 80040000 00080000 RESERVED<br />NK 800C0000 00740000 RAMIMAGE<br />RAM 81000000 00800000 RAM<br /><br />名称原则上可以取任意字符串，ROMIMAGE通过一个内存片的属性来判断它的用途。RESERVE属性表明该片内存是BSP自己使用的，系统不必关心其用途；RAMIMAGE说明它是一片存放OS IMAGE的内存；而RAM则表示些片内存为RAM，系统可以在其中分配空间，运行程序。<br />但存放ROM的这片内存的名称，即NK一般不要改动。因为BIB文件中定义将一个文件加入到哪个ROM片（WINCE支持将ROM　IMAGE存放在不连续的几个内存片中）中时会用到这个名称，如下现这行BIB文件项就定义将touch.dll放在名称为NK这片ROM中，<br />touch.dll $(_FLATRELEASEDIR)\touch.dll NK SH<br />因而，如果将NK改为其它名称，则系统中所有的BIB文件中的这个NK串都需要改动。<br />注意：保证各片内存不要重叠；而且中间不要留空洞，以节约内存；两种设备如果不能同时被加载，就应该只为其保留一片从而节约内存，例如，本例中的CS8950是为网卡驱动程序保留的，EDBG是为网卡作调试（KITL）用时保留的，而系统设计成这两个程序不会同时加载（CS8950在启动时判断如果EDBG在运行就会自动退出），这样为这两个驱动程序各保留一片内存实在浪费而且也没有必要。<br />RAM片必须在物理上是连续的，如果系统的物理内存被分成了几片，则在RAM片只能声明一片，其它的内存在启动阶段由OEMGetExtensionDRAM报告给系统，如果有多于一个的内存片，应该用OEMEnumExtensionDRAM报告。NK片则没有此限制，只是NK跨越两个以上物理内存片时，系统启动时会显示这个OS包跨越了多个物理内存片，认为是个错误，但并不影响系统的执行与稳定性，因为系统启动之时便会打开MMU而使用虚拟地址，从而看到连续的内存空间。当然，如果内核自己都被放在了两个内存片上，那系统应该就无法启动了。而其它保留起来的内存片是一般是给驱动程序DMA用，应该保证它们在物理上的连续性，因为DMA是直接用物理地址的。<br />CONFIG段中以下几个需要格外注意：<br />ROMSTART，它定义ROM的起始位置，应该和NK片的起始位置相同。<br />ROMSIZE，定义ROM的大小，应该和NK片的大小相同。<br />如果不需要NK。BIN文件，则可以不设这两个值。<br />ROMWIDTH，它只是定义ROMIMAG生成ROM包时如何组织文件，而非其字面含义：ROM的宽度，所以一般都应该为32<br />COMPRESSION，一般定义为ON，以打开压缩功能，从而减小BIN文件的尺寸。<br />AUTOSIZE，一般应该设为ON，以使系统将定义给ROM但没有用掉的内存当做RAM使用，而提高RAM的使用率。注意，如果ROM是FLASH，则不能设为ON，因为FLASH不能当作RAM使用。<br />ROMOFFSET，它定义OS起始位置（即ROMSTART）的物理地址和虚拟地址的差值，有些BSP中并没有使用这个定义。<br />OEMAddressTable及其它<br />OEMAddressTable用来初始化系统中各种设备的虚拟地址与物理地址的对映关系。在我使用的BSP中，它是这样定义并初始化的：<br />typedef struct<br />{<br />ULONG ulVirtualAddress;<br />ULONG ulPhysicalAddress;<br />ULONG ulSizeInMegs;<br />} AddressTableStruct;<br /><br />#define MEG(A) (((A - 1)&gt;&gt;20) + 1)<br /><br />const AddressTableStruct OEMAddressTable[] =<br />{<br />{ SDRAM_VIRTUAL_MEMORY, //虚拟地址<br />PHYSICAL_ADDR_SDRAM_MAIN, //物理地址<br />MEG(SDRAM_MAIN_BLOCK_SIZE) //这段空间的大小，以M计<br />},<br />………………………<br />{<br />0,<br />0,<br />0<br />}<br />}；<br />如例子所示，OEMAddressTable为一个结构数组，每项的第一个成员为虚拟地址，第二个成员为对应的物理地址，最后一个成员为该段空间的大小。这个数组的最后一项必须全部为0，以示整个数组的结束。内核启动时会读取这个数组的内容以初始化MMU页表，启用MMU，从尔使程序可以用虚拟地址来访问设备。当然，OEMAddressTable中所用到的每个物理地址及虚拟地址都需要在头文件中定义，每个BSP中定义这些值的文件不尽相同，所以，在此不能说明具体在哪个文件，读者朋友可以参考具体BSP的文档及代码。<br /><br />不连续内存的处理<br />如果内存在物理上是连续的，则OEMAddressTable中只需要一项就可以完成对内存的地址映射。但如果BSP运行在SDRAM物理上不连续的系统上时，OEMAddressTable中需要更多的项来将SDRAM映射到连续的虚拟地址上，当然也可以将它们映射到不连续的虚拟地址上，但似乎没有理由那么做。而且，当其物理地址不连续时系统需要做更多的工作。例如，我有这样一个系统：32M SDRAM，16M FLASH，SDRAM在物理上不连续，被分成了4个8M的内存块，我的SDRAM的使用情况如下图所示：<br /><br /><br /><br />CONFIG。BIB文件的MEMORY段如下所示：<br />MEMORY<br />RESERVED 80000000 00008000 RESERVED<br />DRV_GLB 80008000 00001000 RESERVED<br />CS8900 80010000 00030000 RESERVED<br />EDBG 80040000 00080000 RESERVED<br />NK 800C0000 00940000 RAMIMAGE<br />RAM 81800000 00800000 RAM<br /><br />在这32M的空间中，BSP保留了前0x80000字节，接下来是NK，它占用了0x940000字节，而且它跨越了两个内存片,这些和其它BSP的设置都没有多大差别,接下来看RAM片,它只占用了最后的8M空间,前面说过，在这种物理内存不连续的系统中，RAM片不能跨越两个物理内存块，所以它被设计成只占用该系统中的最后一个物理内存片，而其它两片则由OEMEnumExtensionDRAM在运行时刻报告给系统，该函数的内容如下:<br /><br />pMemSections[0].dwFlags=0;<br />pMemSections[0].dwStart=(SDRAM_VIRTUAL_MEMORY + 0x1000000);<br />pMemSections[0].dwLen=0x800000;<br /><br />pMemSections[1].dwFlags=0;<br />pMemSections[1].dwStart=(SDRAM_VIRTUAL_MEMORY + 0x0A00000);<br />pMemSections[1].dwLen=0x600000;<br />return 2;<br />这样,系统所有的内存都被激活,系统可用内存就变成了8+8+6=24M，可以将RAM定义为这三片中的任意一片，而在OEMEnumExtensionDRAM中报告其它两片。但把RAM放在最后一片物理内存上有一个很大的好处，即如果NK变大，例如编译一个DEBUG版的系统时，这时，只需要将OEMEnumExtensionDRAM中的内容注释掉，CONFIG.BIB文件不用做任何改动，系统就可运行，只是在MAKEIMG时会有一个警告说系统包太大，可能无法运行，但实际不会影响系统的执行与稳定性，因为NK之后的那段内存并没有被使用，正好被涨大的系统占用，这在调试时极其方便。<br />而如果系统物理内存是连续的，那将变得简单的多，还以上面的设置为例，如果这32M的SDRAM是物理上连续的，内存的使用情况就可以表示如下图：<br /><br /><br />所有者系统可用内存都可以定义在RAM片中。<br />对硬件知识了解不多的朋友请注意：SDRAM是否在物理上连续，与我们的板上有几片SDRAM没有关系，应该向硬件工程师了解SDRAM的地址分布情况。</p>
<img src ="http://www.cppblog.com/milkyway/aggbug/17845.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-20 21:25 <a href="http://www.cppblog.com/milkyway/articles/17845.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ARM开发过程中最最需要注意的问题</title><link>http://www.cppblog.com/milkyway/articles/17844.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Sat, 20 Jan 2007 13:24:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17844.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17844.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17844.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17844.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17844.html</trackback:ping><description><![CDATA[作者：microsun <br /><br />平时大家接触最多的可能是X86平台，在这种系统上写程序几乎不需要考虑太多问题，但ARM上就不一样了，最常见也最容易被忽略的问题可能就是字节的对齐，即使像我这样有六七年程序开发经验的才手也时常难于提防，最近就有一个BUG，花了一天时间最终发现是对齐引发的，在此与大家分享，但愿大家能够注意到。<br /><br />　　我在EBOOT中读取存在HARD　DISK上的nk.bin文件，从而从HARD　DISK上LOAD　WINCE系统，在这个过程中总是有check sum错误，但从ethernet下载时不会有错，所以问题应该还是在我加的这部分代码上，而且同样的代码在PC上能正常运行。经过检查代码的逻辑关系是正确的。接着我在出错时将那些数据全部用调试信息打出来，发现从文件开始算起第4096个字节被丢掉了，而其它的字节都是对的。初步判断是对齐引发的问题，所以去查每一个BUFFER，最终发现是在读取硬盘数据时BUFFERR并没有按双字节对齐，而硬盘以16BIT读取数据，而引发了错误。<br /><br />实际上，这类问题在ARM系统上很常见，让人防不胜防，以下是我的一些例子。<br /><br />1，解析数据流时应该时刻注意。如果需要把一个数据流（BUFFER）转化成结构进行取值，就应该把这个结构定义为按字节存取.考虑如下结构：<br /><br />struct a{<br /><br />char a;<br />short b;<br />long c;<br />};<br />如果某个数据流中包含这样的结构，而且我们要直接将数据流的指针转化成该结构的指针，然后直接取结构成员的值，我们就应该将这个结构定义成按字节访问，即将其夹在语句<br />#pragma pack(push,1)<br />...<br /><br />#pragma pack(pop)<br />之中。如果我们不这样做，编译器会将成员b的地址对齐到short指针的地址，即在a之后加上一个char即8位的成员，将C对齐到LONG，即在B之后再加一个char成员。如此一来，成员B和成员C就得不到正确的值了。<br /><br />如果我们定义一个普通的结构用来存放一些数据，则不用定义成按字节存取，编译器会加上一些占位成员，但并不会影响程序的运行。从这个意义上讲，在ARM中，将结构成员定义成CHAR和SHORT来节约内存是没有意义的。<br /><br />一个典型的例子就文件系统的驱动程序，文件是以一些已经定义好的结构存放在存储介质上的，它们被读取到一个BUFFER中，而具体取某个文件、目录结构时，我们会将地址转化成结构而读取其中的值。<br /><br /><br />2，访问外设时。<br />例如，磁盘驱动通常以16BIT的方式存取数据，即每次存取两个字节，这样就要求传给它的BUFFER是双字节对齐的，驱动程序应该至上层传来的指针做出正确的处理以保证数据的正确性。<br /><br /><br />3.有时，我们没有将数据流指针转化为结构指针取值，但如果我们读取的是双字节或者是四字节的数据，同样需要注意对齐的问题，例如，如果从一个BUFFER的偏移10处读取一个四字节值，则实际得到的值是偏移8处的<br />地址上的DWORD值。<img src ="http://www.cppblog.com/milkyway/aggbug/17844.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-20 21:24 <a href="http://www.cppblog.com/milkyway/articles/17844.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>加速CE的编译过程</title><link>http://www.cppblog.com/milkyway/articles/17842.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Sat, 20 Jan 2007 13:22:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17842.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17842.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17842.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17842.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17842.html</trackback:ping><description><![CDATA[
		<p>来自<a href="http://winceblog.blogspot.com/2006_11_01_archive.html">http://winceblog.blogspot.com/2006_11_01_archive.html</a><br /><br />一个常见的办法就是执行菜单命令：Build-&gt;Open Release Directory去打开一个命令行窗口，然后在这个窗口中ＣＤ到想到编译的目录，执行Build.如果要重新编译就运行Build -c,<br />然后找到生成的ＤＬＬ文件，将其拷到Release 目录下，再执行makeimg就行了．</p>
<img src ="http://www.cppblog.com/milkyway/aggbug/17842.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-20 21:22 <a href="http://www.cppblog.com/milkyway/articles/17842.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windowsCE常用编译参数 及编译器列表 </title><link>http://www.cppblog.com/milkyway/articles/17674.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Tue, 16 Jan 2007 02:13:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17674.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17674.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17674.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17674.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17674.html</trackback:ping><description><![CDATA[来自 <a href="http://www.cnblogs.com/nasiry/archive/2004/11/09/61958.aspx">http://www.cnblogs.com/nasiry/archive/2004/11/09/61958.aspx</a><br /><br />---------------by nasiry  
<p>RELEASETYPE <br />指定发布的类型,实际上就是编译后的文件的存放位置可选项为 <br />SDK|DDK|PLATFORM|LOCAL|CUSTOM 其余选项等效OAK </p><p>DLLENTRY|EXEENTRY|NODLLENTRY <br />指定入口点 </p><p>_TGTCPUFAMILY </p><p>指定处理器所属家族可选x86|MIPS|SH|ARM <br /></p><p><br />LINT_TYPE <br />可选all | lob | ind <br /><br />TARGETEXEFILES <br />控制输出文件 可选NOLINK---不链接 <br /></p><p><br />  </p><p>TARGETTYPE <br />输出文件的类型 可选DYNLINK|PROGRAM|LIBRARY <br /></p><p>WINCEDEBUG <br />文件发布类型 可选retail|debug 空=debug <br /></p><p>CDEBUG_DEFINES <br />调试编译选项 WINCE_LMEM_DEBUG|DISABLE_OPTIMIZER|WINCESHIP|WINCE_OVERRIDE_CFLAGS <br /><br /></p><p>TARGET_PDB_NAME <br />PDB文件名 <br /></p><p>WINCEPROFILE <br />可选0|1 <br /></p><p>TGTCPUISANAME <br />处理器名称 </p><p><br /></p><p>编译器 <br />x86 <br /></p><p>cxx cl386 -nologo <br />asm ml -nologo <br />-machine:iX86 </p><p>SH3 <br /></p><p>cxx clsh -nologo <br />asm shasm <br />-machine:sh3 </p><p>SH4 <br />cxx clsh -nologo <br />asm shasm <br />-machine:sh4 <br /></p><p>MIPSII <br />cxx clmips -nologo <br />asm mipsasm <br />-machine:mips /-machine:-machine:mipsfpu </p><p>ARMV4                : <br /></p><p>cxx clarm -nologo <br />asm armasm -coff <br />-machine:arm <br /></p><p>ARMV4T <br />ARMV4I <br />clthumb -nologo <br />asm armasm -coff <br />-machine:thumb <br /></p><img src ="http://www.cppblog.com/milkyway/aggbug/17674.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-16 10:13 <a href="http://www.cppblog.com/milkyway/articles/17674.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Kitl是怎样工作的？</title><link>http://www.cppblog.com/milkyway/articles/17673.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Tue, 16 Jan 2007 02:08:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17673.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17673.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17673.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17673.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17673.html</trackback:ping><description><![CDATA[ ---------------by nasiry  <br />     来自 <a href="http://www.cnblogs.com/nasiry/archive/2004/09/22/45473.aspx">http://www.cnblogs.com/nasiry/archive/2004/09/22/45473.aspx</a>                                 <br />--------------------------------- <br />注：由于我们主要是分析kitl的工作原理我们就电源管理的代码不做分析，以加电启动的程序流进行分析。 <br />part1. <br />kitl初始化 <br />------------- <br />Kitl的加载于其他调试服务之前，以提供为这些调试服务发布调试信息和接收主机调试命令的的通道。通常kitl在系统HAL初始化工作完成后进行加载，MS建议在OEMInit中启动kitl。这样就可以使用NIC或者是serial/Pal作为kitl的物理传输介质。 <br />kitl的初始化由KitlInit完成，这部分代码主要负责：(to be fill later) <br />下面我们来看看kitl的具体代码，这些代码位于%CEROOT%\PRIVATE\WINCEOS\COREOS\NK\KITL下。 <br />BOOL KitlInit (BOOL fStartKitl) <br />{ <br />    // just initialize function pointers <br />    pKITLRegisterDfltClient  = KITLRegisterDfltClient; <br />    pKITLIoCtl = KITLIoctl; <br />    pfnIsDesktopDbgrExist = IsDesktopDbgrExist; 
<p>    // Initialize default clients <br />    NewClient (KITL_SVC_DBGMSG, KITL_SVCNAME_DBGMSG, FALSE); <br />    NewClient (KITL_SVC_PPSH,   KITL_SVCNAME_PPSH,   FALSE); <br />    NewClient (KITL_SVC_KDBG,   KITL_SVCNAME_KDBG,   FALSE); </p><p><br />    return fStartKitl? StartKitl (TRUE) : TRUE; <br />} <br />这段代码主要完成两个动作： <br />1.装载函数指针，为后续代码的执行装载入口点。 <br />2.注册kitl客户端，这些客户端实现传输层以后就是我们所需要的调试界面。 <br />输入参数决定是否立即启动KITL服务，如果false的话就仅仅进行初始化等待后续动作使用startKitl来启动kitl. </p><p>我们再来看看NewClient的原型 <br />static PKITL_CLIENT NewClient (UCHAR uId, LPCSTR pszSvcName, BOOL fAlloc) <br />{ <br />    DEBUGCHK(IS_VALID_ID(uId)); <br />    DEBUGCHK (!KITLClients[uId]); <br />    if (!fAlloc) { <br />        DEBUGCHK(IS_DFLT_SVC(uId)); <br />        KITLClients[uId] = &amp;DfltClnts[uId]; <br />    } else if (!(KITLClients[uId] = (PKITL_CLIENT) AllocMem (HEAP_KITLCLIENT))) { <br />        return NULL; <br />    } <br />    memset (KITLClients[uId], 0, sizeof(KITL_CLIENT)); <br />    KITLClients[uId]-&gt;ServiceId = uId; <br />    strcpy (KITLClients[uId]-&gt;ServiceName, pszSvcName); </p><p>    return KITLClients[uId]; <br />} <br />这个被称为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个。 </p><p>下面继续沿着程序流往下看吧，kitlInit完成最基本的初始化动作即可启动kitl服务了。再看一下这个函数的原型。 <br />static BOOL StartKitl (BOOL fInit) <br />{ <br />    // KITL already started? <br />    if (!fInit &amp;&amp; (KITLGlobalState &amp; KITL_ST_DESKTOP_CONNECTED)) { <br />        return TRUE; <br />    } </p><p>    /* <br />     * When this function is called, the kernel hasn't yet been initialized, <br />     * so can't make any system calls.  Once the system has come up far <br />     * enough to handle system calls, KITLInitializeInterrupt() is called to complete <br />     * initialization.  This is indicated by the KITL_ST_MULTITHREADED flag in KITLGlobalState. <br />     */ <br />    // Detect/initialize ethernet hardware, and return address information <br />    if (!OEMKitlInit (&amp;Kitl)) <br />        return FALSE; </p><p>    // verify that the Kitl structure is initialized. <br />    if (!Kitl.pfnDecode || !Kitl.pfnEncode || !Kitl.pfnEnableInt || !Kitl.pfnRecv || !Kitl.pfnSend <br />        || !Kitl.dwPhysBuffer || !Kitl.dwPhysBufLen || !Kitl.WindowSize || !Kitl.pfnGetDevCfg || !Kitl.pfnSetHostCfg) { <br />        return FALSE; <br />    } </p><p>    // Validate that address is not in free RAM area - the HAL should put it in a reserved <br />    // section of memory conditional on some environment var. <br />    if ((pTOC-&gt;ulRAMStart &lt; Kitl.dwPhysBuffer + Kitl.dwPhysBufLen) <br />        &amp;&amp; (pTOC-&gt;ulRAMEnd &gt; Kitl.dwPhysBuffer)) { <br />        KITLOutputDebugString("\r\n!Debug Ethernet packet buffers in free RAM area - must set IMGEBOOT=1\r\n"); <br />        return FALSE; <br />    } </p><p>    if (Kitl.dwPhysBufLen &lt; (DWORD) 3 * KITL_BUFFER_POOL_SIZE) { <br />        KITLOutputDebugString("\r\n!Debug Ethernet buffer size too small, must be at least 0x%x bytes (3 * WindowSize * 2 * KITL_MTU)\r\n", <br />            3 * KITL_BUFFER_POOL_SIZE); <br />        return FALSE; <br />    } </p><p>    KITLGlobalState |= KITL_ST_KITLSTARTED; // indicate (to kdstub) that KITL has started</p><p>    // If the initialized flag is already set, we are being called from the power on routine, <br />    // so reinit the HW, but not any state. <br />    if (!(KITLGlobalState &amp; KITL_ST_ADAPTER_INITIALIZED)) { <br />        // perform the initial handshake with the desktop <br />        if (!KITLConnectToDesktop ()) { <br />            KITLOutputDebugString ("\r\n!Unable to establish KITL connection with desktop!\r\n"); <br />            return FALSE; <br />        } <br />        <br />        // Set up kernel function pointers <br />        pKITLInitializeInterrupt = KITLInitializeInterrupt; <br />        pKITLSend = KITLSend; <br />        pKITLRecv = KITLRecv; </p><p>        KITLGlobalState |= KITL_ST_ADAPTER_INITIALIZED;</p><p>        if (Kitl.dwBootFlags &amp; KITL_FL_DBGMSG) <br />            SetKernelCommDev (KERNEL_SVC_DBGMSG, KERNEL_COMM_ETHER); <br />        if (Kitl.dwBootFlags &amp; KITL_FL_PPSH) <br />            SetKernelCommDev (KERNEL_SVC_PPSH, KERNEL_COMM_ETHER); <br />        if (Kitl.dwBootFlags &amp; KITL_FL_KDBG) <br />            SetKernelCommDev (KERNEL_SVC_KDBG, KERNEL_COMM_ETHER); </p><p>        // only perform cleanboot if it's connected at boot. Cleanboot flag is <br />        // ignored if it's started dynamically. <br />        if (fInit &amp;&amp; (Kitl.dwBootFlags &amp; KITL_FL_CLEANBOOT)) { <br />            extern ROMHDR *const volatile pTOC;     // Gets replaced by RomLoader with real address <br />            // just clear the magic nOEMKitlInitumber (see SC_SetCleanRebootFlag) <br />            // NOTE: We can NOT call SC_SetCleanRebootFlag here since logPtr isn't <br />            // initialized yet. <br />            ((fslog_t *)((pTOC-&gt;ulRAMFree + MemForPT) | 0x20000000))-&gt;magic1 = 0; <br />        } <br />        // if OEM calls KitlInit (FALSE), KITLInitializeInterrupt will <br />        // not be called in SystemStartupFuc. We need to initialize <br />        // interrupt here (when RegisterClient is called) <br />        if (fKITLcsInitialized &amp;&amp; !InSysCall ()) { <br />            KITLInitializeInterrupt (); <br />        } <br />    } </p><p>    LOG (KITLGlobalState); <br />    return TRUE; <br />} <br />启动代码首先判断是否已经启动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启动的过程就结束了。 <br />   紧接着我们来看看，OEMkitlInit都须要我们干什么。下面用SMDK2440的kitl为实例来进行分析： <br />BOOL OEMKitlInit (PKITLTRANSPORT pKitl) <br />{ <br />    KITLOutputDebugString ("+OEMKitlInit\n"); <br />    RETAILMSG(1, (_T("+OEMKitlInit\r\n"))); <br />    // try to find a transport available <br />    if (!InitEther (pKitl) <br />        &amp;&amp; !InitParallelSerial (pKitl)) { <br />        KITLOutputDebugString ("Unable to initialize KITL Transports!\n"); <br />        return FALSE; <br />    } </p><p>    gpKitl = pKitl; <br />    KITLOutputDebugString ("-OEMKitlInit\n"); <br />    RETAILMSG(1, (_T("-OEMKitlInit\r\n"))); <br />    return TRUE; <br />} </p><p>事实上工作很简单，调用InitEther (pKitl) 和 !InitParallelSerial (pKitl)初始化网卡直接把初始化的KITL全局结构返回就是所有的工作。这儿的InitParallelSerial是一个dummy永远返回false,也就是说这里没有对serial&amp;parallel transport进行支持。真正的工作量集中在InitEther之后。事实上InitEther 和 InitParallelSerial只要任意的实现一个就可以达到建立传输界面的目的.下面，我们继续看后面的代码。 <br />BOOL InitEther(PKITLTRANSPORT pKitl) <br />{ <br />    EDBG_ADAPTER adp; <br />    DWORD dwDHCPLeaseTime; <br />    DWORD dwSubnetMask; </p><p>    KITLOutputDebugString ("+InitEther\n");</p><p>    memset (&amp;adp, 0, sizeof(adp)); <br />    memset (pKitl, 0, sizeof (KITLTRANSPORT)); </p><p>    // use existing code for ether initialization <br />    if (!OEMEthInit (&amp;adp)) <br />        return FALSE; </p><p>    // we are going to completely ignore the info in bootargs and the adaptor info <br />    // returned from OEMEthInit, except MAC address. Just to prove that KITL will connect standalone </p><p>    // get the MAC address <br />    MyAddr.wMAC[0] = adp.Addr.wMAC[0]; <br />    MyAddr.wMAC[1] = adp.Addr.wMAC[1]; <br />    MyAddr.wMAC[2] = adp.Addr.wMAC[2]; <br />    //MyAddr = adp.Addr; <br />    <br />    CreateDeviceName(&amp;MyAddr, pKitl-&gt;szName); <br />    KITLOutputDebugString ("Using device name: %s\n", pKitl-&gt;szName); </p><p>    // If we haven't been given an IP address from our loader (or if we're not using static IP), get an IP address <br />    // from a DHCP server. <br />    if (adp.Addr.dwIP) <br />    { <br />        // Static IP or we got the IP from our bootloader... <br />        MyAddr.dwIP     = adp.Addr.dwIP; <br />        dwSubnetMask    = 0;    // Don't care about subnet mask... <br />        dwDHCPLeaseTime = adp.DHCPLeaseTime; <br />    } <br />    else <br />    { <br />        // Get a DHCP address... <br />        if (!EbootGetDHCPAddr (&amp;MyAddr, &amp;dwSubnetMask, &amp;dwDHCPLeaseTime)) <br />            return FALSE; <br />    } <br />    <br />    MyAddr.wPort = htons (EDBG_SVC_PORT); <br />    KITLOutputDebugString ("Device %s, IP %s, Port %d\n", pKitl-&gt;szName, inet_ntoa (MyAddr.dwIP), htons (MyAddr.wPort)); </p><p>    // initialize KITL Ethernet transport layer <br />    if (!KitlEtherInit (&amp;MyAddr, dwDHCPLeaseTime)) { <br />        KITLOutputDebugString ("Unable to initialize KITL Ether transport\n"); <br />        return FALSE; <br />    } <br />    <br />    // fill in the blanks in KITLTRANSPORT structure. <br />    pKitl-&gt;FrmHdrSize = KitlEtherGetFrameHdrSize (); <br />    pKitl-&gt;Interrupt = (UCHAR) adp.SysIntrVal; <br />    pKitl-&gt;dwPhysBuffer = EDBG_PHYSICAL_MEMORY_START; <br />    pKitl-&gt;dwPhysBufLen = 0x20000;                      // 128K of buffer available <br />    pKitl-&gt;dwBootFlags = 0; <br />    pKitl-&gt;WindowSize = EDBG_WINDOW_SIZE; <br />    pKitl-&gt;pfnDecode = KitlEtherDecodeUDP; <br />    pKitl-&gt;pfnEncode = KitlEtherEncodeUDP; <br />    pKitl-&gt;pfnSend = EthSend; <br />    pKitl-&gt;pfnRecv = OEMEthGetFrame; <br />    pKitl-&gt;pfnEnableInt = KitlEthEnableInts; <br />    pKitl-&gt;pfnSetHostCfg = SetHostCfg; <br />    pKitl-&gt;pfnGetDevCfg = GetDevCfg; </p><p>    KITLOutputDebugString ("-InitEther\n");</p><p><br />    return TRUE; <br />} <br />这个函数完成的工作主要是调用OEMEthInit初始化网卡的服务程序及获得相应的IP和MAC，如果IP无效则用DHCP动态获得IP.通过MAC值产生一个标示，这个标示用来给PB的IDE使用。刚才的我们在kitlInit中看到除了检查OEMkitlInit的返回值之外还检查了KITL结构，该结构的这些特征值正是在这儿设置的。在这儿可以看到pKitl-&gt;pfnDecode pKitl-&gt;pfnEncode pKitl-&gt;pfnSetHostCfg pKitl-&gt;pfnGetDevCfg 以及kitl所用的中断号这些都是OEM代码，也就是用于传输的编码和解码形式以及配置函数都是可以自己定义的，这样一来也就无所谓使用什么传输介质作为KITK <br />的transport了，这就为使用1394或者是USB这一类的传输链路也能充当传输界面作了准备。 OEMEthInit函数是用于初始化传输介质--以太网卡。这部分代码直接是硬件控制代码，我们来简单的看一下。 <br />BOOL <br />OEMEthInit(EDBG_ADAPTER *pAdapter) <br />{ <br /> PBYTE pBaseIOAddress; </p><p> // Driver globals from the bootloader. <br /> // <br /> if (pDriverGlobals-&gt;eth.EbootMagicNum == EBOOT_MAGIC_NUM) <br /> { <br />  memcpy(pAdapter, &amp;pDriverGlobals-&gt;eth.TargetAddr, sizeof(EDBG_ADAPTER)); </p><p>  switch(pDriverGlobals-&gt;misc.EbootDevice) <br />  { <br />  case(DOWNLOAD_DEVICE_PCMCIA): // NE2000 CF card. <br />   pBaseIOAddress  = (PBYTE)PCMCIA_Init(); <br />   if (pBaseIOAddress) <br />   { <br />    // Initialize the built-in Ethenet controller. <br />    // <br />    if (!NE2000Init((PBYTE)pBaseIOAddress, 1, pAdapter-&gt;Addr.wMAC)) <br />    { <br />     EdbgOutputDebugString("ERROR: OEMEthInit: Failed to initialize Ethernet controller.\r\n"); <br />     return(FALSE); <br />    } <br />   } <br />   pfnEDbgInit            = NE2000Init; <br />   pfnEDbgEnableInts      = NE2000EnableInts; <br />   pfnEDbgDisableInts     = NE2000DisableInts; <br />   pfnEDbgGetPendingInts  = NE2000GetPendingInts; <br />   pfnEDbgGetFrame        = NE2000GetFrame; <br />   pfnEDbgSendFrame       = NE2000SendFrame; <br />   pfnEDbgReadEEPROM      = NE2000ReadEEPROM; <br />   pfnEDbgWriteEEPROM     = NE2000WriteEEPROM; <br />   pfnEDbgSetOptions      = NE2000SetOptions; <br />  #ifdef IMGSHAREETH <br />   pfnCurrentPacketFilter = Ne2000CurrentPacketFilter; <br />   pfnMulticastList  = NE2000MulticastList; <br />  #endif // IMGSHAREETH. <br />   break; <br />  case(DOWNLOAD_DEVICE_CS8900): // CS8900A. <br />   // Initialize the CS8900. <br />   // <br />   if (!CS8900DBG_Init((PBYTE)CS8900DBG_IOBASE, CS8900DBG_MEMBASE, pAdapter-&gt;Addr.wMAC)) <br />   { <br />    EdbgOutputDebugString("ERROR: OEMEthInit: CS8900 initialization failed.\r\n"); <br />    return(FALSE); <br />   } </p><p>   pfnEDbgInit  = CS8900DBG_Init; <br />   pfnEDbgEnableInts      = CS8900DBG_EnableInts; <br />   pfnEDbgDisableInts     = CS8900DBG_DisableInts; <br />   pfnEDbgGetFrame  = CS8900DBG_GetFrame; <br />   pfnEDbgSendFrame = CS8900DBG_SendFrame; <br />   pfnEDbgGetPendingInts  = CS8900DBG_GetPendingInts; <br />  #ifdef IMGSHAREETH <br />   pfnCurrentPacketFilter = CS8900DBG_CurrentPacketFilter; <br />   pfnMulticastList = CS8900DBG_MulticastList; <br />  #endif // IMGSHAREETH. <br />   break; <br />  default: <br />   EdbgOutputDebugString("ERROR: OEMInit: Unknown download NIC (0x%x).\r\n", pDriverGlobals-&gt;misc.EbootDevice); <br />   return(FALSE); <br />  <br /> }} <br /> else <br /> { <br />  // TODO - retrieve CS8900 MAC address from flash... <br />  // TODO - intialize the CS8900 from scratch... <br /> } </p><p> EdbgOutputDebugString("::: OEMEthInit() IP Address : %s\r\n", inet_ntoa(pAdapter-&gt;Addr.dwIP)); <br /> EdbgOutputDebugString("::: OEMEthInit() Netmask    : %s\r\n", inet_ntoa(pDriverGlobals-&gt;eth.SubnetMask)); </p><p> if (pDriverGlobals-&gt;misc.EbootDevice == DOWNLOAD_DEVICE_PCMCIA) <br />  pAdapter-&gt;SysIntrVal    = SYSINTR_PCMCIA_LEVEL; <br /> else <br />  pAdapter-&gt;SysIntrVal    = SYSINTR_ETHER; </p><p> pAdapter-&gt;DHCPLeaseTime = DEFAULT_DHCP_LEASE; <br /> pAdapter-&gt;EdbgFlags     = pDriverGlobals-&gt;eth.EdbgFlags; <br />   <br />#ifdef IMGSHAREETH <br />    VBridgeInit(); <br />    VBridgeKSetLocalMacAddress((char *)pAdapter-&gt;Addr.wMAC); <br />#endif // IMGSHAREETH. </p><p> return(TRUE); <br />} <br />    <br />这个函数看起来很复杂其实真正的工作并不多，首先判断是不是由eboot启动的，如果已经eboot中已经完成了对以太网卡的初始化动作就直接使用网卡并装载/挂接网卡所需的函数和网卡信息，否则就需要自己设置网卡的MAC地址和初始化网卡(事实上以上函数并没有对这部分代码进行实现，这就是很多使用2410/2440的用户在不使用eboot启动的情况下总是不能使用kitl的原因--找不到eboot在DriverGlobal中留下的magic NUMBER)。这儿之所以有NE2000和Cs8900的区分是因为SMDK2440可以使用PCMICA挂接Ne2000兼容的NIC或板载CS8900，后面设置中断标示有两个分支也是这个原因。 <br />IMGSHAREETH的部分是Vbridge的部分,为什么要使用这个叫vbridge的东西呢？我们看看下面的假设。 <br />为了建立kitl占用了一个网卡资源，而该资源如果在windowsCE下复用(该设备同时被两个驱动使用1.kitl 2.windowsCE NIC driver)的话会不会导致问题呢？看看下面的两个函数。 <br />    VBridgeInit(); <br />    VBridgeKSetLocalMacAddress((char *)pAdapter-&gt;Addr.wMAC); <br />    该函数在内核调试传输通道和tcp/ip&amp;windsock之间建立一个虚拟的网桥v-bridge，在外部看来vbridge就像在mac层一样，vbridge一方面和硬件通讯建立传输的物理界面，另一方面和调试所需的EDBG和vmini.dll提供相同的逻辑界面，在更高的层面vmini.dll就像一个专门的网卡一样支持NDIS以至于tcp/ip协议栈。这样我们就可以一方面使用网卡做调试另外一方面仍然能让windowsCE使用网卡通讯，对于windowCE而言所使用的网卡不在是与底层对应的网络设备，而是通过vbridge虚拟出来的网络设备，所以在直接使用SMDK24XX的bsp编译出来的系统网卡显示为vmini就是这个原因。这个时候网卡驱动怎么配置呢？答案很简单，就是不要网卡驱动，因为我们已经从vbridge中抽象(虚拟---用一个网卡)出一个网卡了，原来的网卡驱动也就不在需要了。 <br />    <br />从上面的OemKitlInit到InitEther都是OEM代码，目的在于使Kitl与相应的transport的物理介质联系起来，也就是构建kitl的硬件抽象层，在一个kitl初始化代码中中只有这部分工作(OEM代码)是必要的，其余的代码直接使用MS编译好的lib就可以了。尽管如此我们还是继续看下面的代码，虽然这对我们来说不是必须的不过对一个程序要有全面的认识，框架上的重要模块都是需要了解和认识的。 </p><p><br />看完了这一系列的OEM代码，继续看看StartKitl里面我们没有看完的部分在设置启动标示位之前做了2个检查分别是检查buffer的位置是否在编译系统的时候预留下来以及是否有足够的长度可用。这个内存区域不是动态分配的，而是在bsp的内存配置文件中指定并保留下来的(见bsp file目录下的config.bib)。再下来进行一个KITLConnectToDesktop的动作，这个看名字就知道作用了。就是和PC连接。同样看看代码： <br />static BOOL KITLConnectToDesktop (void) <br />{ <br />    // we'll use the PpfsFmtBuf for send buffer since ppfs can't be started yet at this stage <br />    // <br />    PKITL_HDR pHdr = (PKITL_HDR) (PpfsFmtBuf + Kitl.FrmHdrSize); <br />    PKITL_DEV_TRANSCFG pCfg = (PKITL_DEV_TRANSCFG) KITLDATA(pHdr); <br />    USHORT cbData = sizeof (PpfsFmtBuf) - Kitl.FrmHdrSize - sizeof (KITL_HDR) - sizeof (KITL_DEV_TRANSCFG); </p><p>    if (!Kitl.pfnGetDevCfg ((LPBYTE) (pCfg+1), &amp;cbData)) <br />        return FALSE; </p><p>    memset (pHdr, 0, sizeof (KITL_HDR)); <br />    pHdr-&gt;Id = KITL_ID; <br />    pHdr-&gt;Service = KITL_SVC_ADMIN; <br />    pHdr-&gt;Cmd = KITL_CMD_TRAN_CONFIG; <br />    cbData += sizeof (KITL_DEV_TRANSCFG); <br />    pCfg-&gt;wLen = cbData; <br />    pCfg-&gt;wCpuId = KITL_CPUID; <br />    memcpy (pCfg-&gt;szDevName, Kitl.szName, KITL_MAX_DEV_NAMELEN); <br />    cbData += sizeof (KITL_HDR); </p><p>    return KitlSendFrame (PpfsFmtBuf, cbData) <br />        &amp;&amp; KITLPollResponse (FALSE, ChkCnxDsktp, TranCnxDsktp, (LPVOID) cbData); <br />} <br />结构PKITL_HDR就是kilt的传输头格式，而PKITL_DEV_TRANSCFG信息则是传输设备的设置。首先通过调用Kitl.pfnGetDevCfg得到传输设备的信息，Kitl.pfnGetDevCfg是函数指针，在对以太网卡初始化的时候指向OEM代码中的GetDevCfg函数。通过这个函数得到设备信息(smdk2410的bsp中这儿返回的就是IP地址)。然后再继续设置传输头的标示，类型，命令等等信息，然后就是发送数据了，具体的动作就是调用KitlSendFrame。(To be continue...) </p><p>BOOL KitlSendFrame (LPBYTE pbFrame, WORD cbData) <br />{ <br />    if (!Kitl.pfnEncode (pbFrame, cbData)) { <br />        KITLOutputDebugString ("!KitlSendFrame: transport failed to encode the data frame\r\n"); <br />        return FALSE; <br />    } </p><p>    return KitlSendRawData (pbFrame, (USHORT) (cbData + Kitl.FrmHdrSize + Kitl.FrmTlrSize)); <br />} <br />这个函数首先调用KitlEtherEncodeUDP对数据帧进行编码为UDP协议需要的格式。然后调用 KitlSendRawData将数据送出至PC. <br />BOOL KitlSendRawData (LPBYTE pbData, WORD wLength) <br />{ <br />    BOOL fRet; <br />    if (!(KITLGlobalState &amp; KITL_ST_MULTITHREADED) || InSysCall()) <br />        fRet = Kitl.pfnSend (pbData, wLength); <br />    else if (IsDesktopDbgrExist ()) <br />        fRet = KCall((PKFN) Kitl.pfnSend, pbData, wLength); <br />    else { <br />        EnterCriticalSection (&amp;KITLKCallcs); <br />        fRet = Kitl.pfnSend (pbData, wLength); <br />        LeaveCriticalSection (&amp;KITLKCallcs); <br />    } <br />    return fRet; <br />} <br />首先判定系统没有在调度中且当前代码在不可剥夺状态状态运行，通过EthSend调用OEMEthSendFrame将数据送出就完成了工作。另外两个分支与我们分析的程序流没有关系先放一下。 <br />BOOL <br />OEMEthSendFrame( <br />    BYTE *pData,     // IN - Data buffer <br />    DWORD dwLength)  // IN - Length of buffer <br />{ <br />    int retries = 0; </p><p>    while (retries++ &lt; 4) { <br />        if (!pfnEDbgSendFrame(pData, dwLength)) <br />  { <br />#ifdef IMGSHAREETH <br />   ProcessVMiniSend(); <br />#endif //IMGSHAREETH <br />            return TRUE; <br />  } <br />        else <br />            EdbgOutputDebugString("!OEMEthSendFrame failure, retry %u\n",retries); <br />    } <br />    return FALSE; <br />} <br />在发送数据帧的过程中专门有处理vMini发送的过程。由于kitl本身就不是很简单我们以后后面再用专门的文章说明vbridge的工作过程。完成了发送，我们继续下面的过程。 <br />BOOL KITLPollResponse (BOOL fUseSysCalls, PFN_CHECK pfnCheck, PFN_TRANSMIT pfnTransmit, LPVOID pData) <br />{ <br />    DWORD dwLoopCnt = 0, dwLoopMax = MIN_POLL_ITER; <br />    DWORD dwStartTime = CurMSec; <br />    int   nTimeMax = MIN_POLL_TIME;  // start with 1 sec <br />    BOOL  fUseIter = FALSE, fUseTick = FALSE; </p><p>    while (!pfnCheck (pData)) { <br />        // <br />        // if we've already connected with desktop, use the desktop <br />        // "Retransmit" package to determine if we need to retransmit <br />        // <br />        if (!(KITLGlobalState &amp; KITL_ST_DESKTOP_CONNECTED)) { <br />            if (fUseTick) { <br />                if ((int) (CurMSec - dwStartTime) &gt; nTimeMax) { <br />                    // retransmit <br />                    if (!pfnTransmit (pData, fUseSysCalls)) <br />                        return FALSE; <br />                    dwStartTime = CurMSec; <br />                    if (nTimeMax &lt; MAX_POLL_TIME) <br />                        nTimeMax &lt;&lt;= 1; <br />                } <br />            } else if (fUseIter || (dwStartTime == CurMSec)) { <br />                // if time isn't moving for a while, we'll <br />                // use iteration. <br />                if (dwLoopCnt ++ &gt; dwLoopMax) { <br />                    if (!pfnTransmit (pData, fUseSysCalls)) <br />                        return FALSE; <br />                    if (dwLoopMax &lt; MAX_POLL_ITER) <br />                        dwLoopMax &lt;&lt;= 1; <br />                    dwLoopCnt = 0; <br />                    fUseIter = TRUE; <br />                } <br />            } else { <br />                // time is moving, just use tick from here <br />                fUseTick = TRUE; <br />            } <br />        } <br />        if (!KITLPollData(fUseSysCalls, pfnTransmit, pData)) { <br />            return FALSE; <br />        } <br />    } <br />    return TRUE; <br />} <br />static BOOL TranCnxDsktp (LPVOID pParam, BOOL fUseSysCalls) <br />{ <br />    return KitlSendFrame (PpfsFmtBuf, (WORD) (DWORD) pParam); <br />} <br />这个函数的主体是一个循环，终止的条件是!pfnCheck (pData)，这个函数是由前面传递进来的，返回值为KITLGlobalState &amp; KITL_ST_DESKTOP_CONNECTED，也就是说在得到桌面连接之前是不会返回的也就是说启动kitl以后不与桌面计算机连接windowsCE的是无法启动的。由于从dwStartTime定义到dwStartTime == CurMSec的判定仅仅需要很少的时间就可以完成这段时间内CurMSec是不会被改写的就可以通过循环进行超时检查 <br />并通过TranCnxDsktp重新发送数据，直到KITLPollData设置KITLGlobalState。 <br />static BOOL KITLPollData(BOOL fUseSysCalls, PFN_TRANSMIT pfnTransmit, LPVOID pData) <br />{ <br />    LPBYTE pRecvBuf = PollRecvBuf; <br />    <br />    if (fUseSysCalls &amp;&amp; (KITLGlobalState &amp; KITL_ST_MULTITHREADED) <br />        &amp;&amp; !(pRecvBuf = _alloca(KITL_MTU))) { <br />        KITLOutputDebugString("!KITLPollData: STACK OVERFLOW!\r\n"); <br />        return FALSE; <br />    } <br />    HandleRecvInterrupt(pRecvBuf, fUseSysCalls, pfnTransmit, pData); <br />    return TRUE; <br />} <br />由于我们上面传来的fUseSysCalls参数为false所以前一段检查操作并不进行。直接调用 HandleRecvInterrupt。 </p><p>void HandleRecvInterrupt(UCHAR *pRecvBuf, BOOL fUseSysCalls, PFN_TRANSMIT pfnTransmit, LPVOID pData) <br />{ <br />    WORD wLen = KITL_MTU; <br />    BOOL fFrameRecvd; </p><p>    // Receive data into buffer <br />    do { <br />        if (!fUseSysCalls) <br />            fFrameRecvd = Kitl.pfnRecv (pRecvBuf, &amp;wLen); <br />        else if (IsDesktopDbgrExist ()) <br />            fFrameRecvd = KCall((PKFN) Kitl.pfnRecv, pRecvBuf, &amp;wLen); <br />        else { <br />            EnterCriticalSection (&amp;KITLKCallcs); <br />            fFrameRecvd = Kitl.pfnRecv (pRecvBuf, &amp;wLen); <br />            LeaveCriticalSection (&amp;KITLKCallcs); <br />        } <br />        if (fFrameRecvd) { <br />            ProcessRecvFrame (pRecvBuf,wLen,fUseSysCalls, pfnTransmit, pData); <br />            wLen = KITL_MTU; <br />        } <br />    } while (fFrameRecvd); <br />} <br />通过Kitl.pfnRecv调用pfnEDbgGetFrame指向的CS8900DBG_GetFrame读取当前的数据帧送交ProcessRecvFrame处理。注意，这儿的pfnEDbgGetFrame并不是通过DMA或者是网卡传来的中断启动的而是使用查询的方法进行的，这就是为什么这儿并没有启动中断仍然能够使用以太网卡进行数据传输数据的原因。随后，我们将计算机端送来的数据送交ProcessRecvFrame处理。 </p><p>static BOOL ProcessRecvFrame(UCHAR *pFrame, WORD wMsgLen, BOOL fUseSysCalls, PFN_TRANSMIT pfnTransmit, LPVOID pData) <br />{ <br />    KITL_HDR *pMsg; <br />    KITL_CLIENT *pClient = NULL; <br />    BOOL fRet = TRUE; <br />    UCHAR RxBufOffset; <br />    WORD  wDataLen; <br />    UCHAR ClientIdx; <br />    // let the transport layer decode the frame <br />    if (!(pMsg = (KITL_HDR *) Kitl.pfnDecode (pFrame, &amp;wMsgLen))) { <br />        KITL_DEBUGMSG(ZONE_RECV, ("ProcessRecvFrame: Received Unhandled frame\n")); <br />        return FALSE; <br />    } </p><p>    // is it a valid KITL message? <br />    if (pMsg-&gt;Id != KITL_ID) { <br />        KITL_DEBUGMSG(ZONE_WARNING,("KITL: Got unrecognized Id: %X\r\n",pMsg-&gt;Id)); <br />        return FALSE; <br />    } <br />    <br />    // Validate length <br />    if (wMsgLen &lt; KITL_DATA_OFFSET) { <br />        KITL_DEBUGMSG(ZONE_WARNING,("KITL: Invalid length %u\n",wMsgLen)); <br />        return FALSE; <br />    } <br />    if (KITLDebugZone &amp; ZONE_FRAMEDUMP) <br />        KITLDecodeFrame("&lt;&lt;KITLRecv",pMsg, wMsgLen); <br />    <br />    // Check for administrative messages <br />    if (pMsg-&gt;Service == KITL_SVC_ADMIN) <br />        return ProcessAdminMsg(pMsg, wMsgLen, fUseSysCalls, pfnTransmit, pData); </p><p>    // Service Id is index into KITLClients array <br />    ClientIdx = pMsg-&gt;Service; <br />    if (ClientIdx &gt;= MAX_KITL_CLIENTS) { <br />        KITL_DEBUGMSG(ZONE_WARNING,("!ProcessKITLMsg: Invalid ServiceId: %u\n",pMsg-&gt;Service)); <br />        return FALSE; <br />    } </p><p>    pClient = KITLClients[ClientIdx]; <br />    <br />    // Until we complete registering, only handle administrative messages <br />    if (!pClient || !(pClient-&gt;State &amp; KITL_CLIENT_REGISTERED)) { <br />        KITL_DEBUGMSG(ZONE_WARNING,("!ProcessKITLMsg: Client %u not registered\n",ClientIdx)); <br />        return FALSE; <br />    } <br />    if (pMsg-&gt;Service != pClient-&gt;ServiceId) { <br />        KITL_DEBUGMSG(ZONE_WARNING,("!ProcessKITLMsg: Mismatch in service Id for Client %u (Got %u, expect %u)\n", <br />                                    ClientIdx,pMsg-&gt;Service,pClient-&gt;ServiceId)); <br />        return FALSE; <br />    } </p><p>    if (pClient-&gt;State &amp; KITL_USE_SYSCALLS) { <br />        if (fUseSysCalls)  <br />            EnterCriticalSection(&amp;pClient-&gt;ClientCS); <br />        else if (pClient-&gt;ClientCS.OwnerThread) { <br />            // We can't get the client CS, and it is owned - just toss frame <br />            KITL_DEBUGMSG(ZONE_WARNING,("!KITL(%u) tossing msg %u (Can't get CS)\n",ClientIdx, pMsg-&gt;SeqNum)); <br />            return FALSE; <br />        } <br />    } </p><p>    // we've being in sync with the desktop <br />    pClient-&gt;State |= KITL_SYNCED; <br />    <br />    // Put flags and seq # to LEDs <br />    KITL_DEBUGLED(LED_PEM_SEQ, ((DWORD) pMsg-&gt;Flags &lt;&lt; 24) | pMsg-&gt;SeqNum); <br />    <br />    // OK, valid message, see if it's an ACK <br />    if (pMsg-&gt;Flags &amp; KITL_FL_ACK) { <br />        KITL_DEBUGMSG(ZONE_RECV,("KITL(%u): Received ack for msg %u, Tx window: %u,%u\n", <br />                                 ClientIdx,pMsg-&gt;SeqNum, pClient-&gt;AckExpected,pClient-&gt;TxSeqNum)); <br />        // ACKs acknowledge all data up to the ACK sequence # <br />        while (SEQ_BETWEEN(pClient-&gt;AckExpected, pMsg-&gt;SeqNum, pClient-&gt;TxSeqNum)) {        <br />            if ((pClient-&gt;State &amp; KITL_USE_SYSCALLS) &amp;&amp; <br />                ((pClient-&gt;CfgFlags &amp; KITL_CFGFL_STOP_AND_WAIT) || <br />                 (SEQ_DELTA(pClient-&gt;AckExpected, pClient-&gt;TxSeqNum) &gt;= pClient-&gt;WindowSize-1) || <br />                 !(KITLGlobalState &amp; KITL_ST_INT_ENABLED))) { <br />                if (fUseSysCalls) <br />                    SetClientEvent(pClient,pClient-&gt;evTxFull); <br />                else { <br />                    // Can't process message at this time... <br />                    KITL_DEBUGMSG(ZONE_WARNING,("!KITL(%u): Tossing ACK %u (Can't set event)\n", <br />                                                ClientIdx, pMsg-&gt;SeqNum)); <br />                    return FALSE; <br />                } <br />            } <br />            // Stop retransmission timer. <br />            TimerStop(pClient, (UCHAR)(pClient-&gt;AckExpected % pClient-&gt;WindowSize),fUseSysCalls); <br />            SEQ_INC(pClient-&gt;AckExpected); <br />        } <br />        goto ProcessKITLMsg_exit; <br />    } </p><p>    // Handle NACKs - retransmit requested frame if it is in our Tx window <br />    if (pMsg-&gt;Flags &amp; KITL_FL_NACK) { <br />        KITL_DEBUGMSG(ZONE_WARNING,("KITL(%u): Received NACK for msg %u, Tx window: %u,%u\n", <br />                                    ClientIdx,pMsg-&gt;SeqNum, pClient-&gt;AckExpected,pClient-&gt;TxSeqNum)); <br />        if (SEQ_BETWEEN(pClient-&gt;AckExpected, pMsg-&gt;SeqNum, pClient-&gt;TxSeqNum)) { <br />            UCHAR Index = pMsg-&gt;SeqNum % pClient-&gt;WindowSize; <br />            if (pClient-&gt;TxFrameLen[Index]) { <br />                // Restart retransmission timer (note we can't start timers if syscalls <br />                // are disabled, but this shouldn't screw us up, we'll just potentially <br />                // retransmit an extra frame if the timer fires before we get the ACK) <br />                if (fUseSysCalls) <br />                    TimerStop(pClient,Index,fUseSysCalls); <br />                RetransmitFrame(pClient, Index, fUseSysCalls); <br />                if (fUseSysCalls) <br />                    TimerStart(pClient,Index,KITL_RETRANSMIT_INTERVAL_MS,fUseSysCalls); <br />            } <br />            else <br />                KITL_DEBUGMSG(ZONE_WARNING,("!KITL(%u): NACK in window, but TxFrameLen empty!\n",ClientIdx)); <br />        } <br />        else <br />            KITL_DEBUGMSG(ZONE_WARNING,("!KITL(%u): Received NACK outside of TX window: Seq: %u, Window: %u,%u\n", <br />                                        ClientIdx,pMsg-&gt;SeqNum,pClient-&gt;AckExpected,pClient-&gt;TxSeqNum)); <br />        goto ProcessKITLMsg_exit; <br />    } <br />    <br />    // Data frame.  Place in appropriate slot in Rx buffer pool. Note that we defer acking <br />    // received frames until they are read from the buffer, in KITLRecv. <br />    RxBufOffset = pMsg-&gt;SeqNum % pClient-&gt;WindowSize; </p><p>    if (! SEQ_BETWEEN(pClient-&gt;RxSeqNum, pMsg-&gt;SeqNum, pClient-&gt;RxWinEnd)) { <br />        UCHAR uLastACK = (UCHAR) (pClient-&gt;RxWinEnd - pClient-&gt;WindowSize - 1); </p><p>        KITL_DEBUGMSG (ZONE_WARNING, ("KITL(%u): Received msg outside window: Seq:%u, Win:%u,%u\n", <br />                              ClientIdx,pMsg-&gt;SeqNum,pClient-&gt;RxSeqNum,pClient-&gt;RxWinEnd)); </p><p>        // Special case to handle lost ACKs - if an ack is dropped, our Rx window will have <br />        // advanced beyond the seq # of the retransmitted frame.  Since ACKs ack all messages <br />        // up to the ack #, we only need to check the last frame. <br />        if (pMsg-&gt;SeqNum == uLastACK) { <br />            KITL_DEBUGMSG(ZONE_WARNING,("KITL(%u): Lost ACK (seq: %u, win: %u,%u)\n",ClientIdx, <br />                                        pMsg-&gt;SeqNum,uLastACK,pClient-&gt;RxWinEnd)); <br />            SendAckNack (TRUE, pClient, uLastACK); <br />        } <br />    } else if (pClient-&gt;RxFrameLen[RxBufOffset] != 0) { <br />        // If all our buffers are full, toss frame (will be acked when data is read in KITLRecv) <br />        KITL_DEBUGMSG(ZONE_WARNING,("KITL(%u): Received duplicate (Seq:%u), slot %u already full. Win: %u,%u\n", <br />                                    ClientIdx,pMsg-&gt;SeqNum,RxBufOffset,pClient-&gt;RxSeqNum,pClient-&gt;RxWinEnd)); <br />    } else { <br />        DWORD OldProcPerms; </p><p>        // If we're in non-preemptible mode, can't set the receive event, so just toss message <br />        // and wait for retry. <br />        if (!fUseSysCalls &amp;&amp; (pClient-&gt;State &amp; KITL_USE_SYSCALLS)) { <br />            KITL_DEBUGMSG(ZONE_WARNING,("KITL(%u): Tossing frame %u (Can't signal Rx event)\n", <br />                                        ClientIdx,pMsg-&gt;SeqNum)); <br />            return FALSE; <br />        } </p><p>        KITL_DEBUGMSG(ZONE_RECV,("KITL(%u): Received frame Seq: %u, len: %u, putting in slot %u\n", <br />                                 ClientIdx, pMsg-&gt;SeqNum, wMsgLen, RxBufOffset)); <br />        // If frames were dropped, send NACK (only allow one outstanding NACK) <br />        if (pMsg-&gt;SeqNum != pClient-&gt;RxSeqNum) { <br />            KITL_DEBUGMSG(ZONE_WARNING,("!KITL(%u): Dropped frame (seq: %u, win: %u,%u)\n", <br />                                        ClientIdx,pMsg-&gt;SeqNum,pClient-&gt;RxSeqNum, pClient-&gt;RxWinEnd)); </p><p>            if (!(pClient-&gt;State &amp; KITL_NACK_SENT)) { <br />                SendAckNack(FALSE, pClient, pClient-&gt;RxSeqNum); <br />                pClient-&gt;State |= KITL_NACK_SENT;           <br />            } <br />        } <br />        else <br />            pClient-&gt;State &amp;= ~KITL_NACK_SENT; <br />        <br />        // Copy data to receive buffer, unblock anyone waiting, and close receive window <br />        wDataLen = wMsgLen - (WORD)KITL_DATA_OFFSET; <br />        if (wDataLen == 0) <br />            KITL_DEBUGMSG(ZONE_WARNING,("!KITL: Received data message with 0 length!\n")); <br />        if (pClient-&gt;ProcPerms) { <br />            // acquire permission of pClient and add it to current thread <br />            ACCESSKEY aKey = GETCURKEY() | pClient-&gt;ProcPerms; <br />            SWITCHKEY (OldProcPerms, aKey); <br />        } <br />        memcpy(pClient-&gt;pRxBufferPool + RxBufOffset*KITL_MTU,KITLDATA(pMsg), wDataLen); <br />        if (pClient-&gt;ProcPerms) { <br />            SETCURKEY (OldProcPerms);            <br />        } <br />        pClient-&gt;RxFrameLen[RxBufOffset] = wDataLen; </p><p>        if (pClient-&gt;State &amp; KITL_USE_SYSCALLS) <br />            // If we get here, we know that fUseSysCalls is TRUE <br />            SetClientEvent(pClient,pClient-&gt;evRecv); <br />        <br />        // Close receive window <br />        while (pClient-&gt;RxFrameLen[pClient-&gt;RxSeqNum % pClient-&gt;WindowSize] &amp;&amp; <br />               (SEQ_DELTA(pClient-&gt;RxSeqNum, pClient-&gt;RxWinEnd) &gt;= 1)) { <br />            KITL_DEBUGMSG(ZONE_RECV,("Rx win: %u,%u, usesyscalls: %u\n",pClient-&gt;RxSeqNum, pClient-&gt;RxWinEnd, fUseSysCalls)); <br />            SEQ_INC(pClient-&gt;RxSeqNum); <br />        } <br />    } <br />    <br />ProcessKITLMsg_exit: </p><p>    if (fUseSysCalls &amp;&amp; (pClient-&gt;State &amp; KITL_USE_SYSCALLS)) <br />        LeaveCriticalSection(&amp;pClient-&gt;ClientCS); <br />    <br />    return fRet; <br />} <br />ProcessRecvFramed是一个近200行的函数，样子很吓人。它是数据帧的解析和处理模块的主体。我们从上到下看看都干了些什么。先调用KitlEtherDecodeUDP将来自主机的数据帧解码为KITL_HDR结构，然后效验魔法数KITL_ID，确认该帧的信息的有效性以及数据长度是否有效，如果ZONE_FRAMEDUMP标签是打开的则需要则解析Frame的内容并记录下来(输出到调试界面，初始化流程并不包含该信息)，然后判定该数据帧描述的信息是否属于管理命令(连接桌面，新建client等等),如果是则调用ProcessAdminMsg进行处理。 <br />static BOOL ProcessAdminMsg(KITL_HDR *pHdr, WORD wMsgLen, BOOL fUseSysCalls, PFN_TRANSMIT pfnTransmit, LPVOID pData) <br />{ <br />    KITL_CLIENT *pClient = NULL;    <br />    <br />    switch (pHdr-&gt;Cmd) <br />    { <br />        case KITL_CMD_SVC_CONFIG: <br />        { <br />            KITL_SVC_CONFIG_DATA *pCfg = (KITL_SVC_CONFIG_DATA *) KITLDATA (pHdr); <br />            int i, iStart; <br />            <br />            if (wMsgLen != (KITL_DATA_OFFSET + sizeof(KITL_SVC_CONFIG_DATA))) { <br />                KITL_DEBUGMSG(ZONE_WARNING,("!ProcessAdminMsg: Invalid legnth for CONFIG msg: %u\n",wMsgLen)); <br />                return FALSE; <br />            } </p><p>            // Find client struct <br />            if ((i = ChkDfltSvc (pCfg-&gt;ServiceName)) &lt; 0) <br />                i = HASH(pCfg-&gt;ServiceName[0]); </p><p>            iStart = i;</p><p>            while (KITLClients[i]) { <br />                // For multi instanced services, skip clients that are already registered <br />                if (!strcmp(KITLClients[i]-&gt;ServiceName,pCfg-&gt;ServiceName) &amp;&amp; <br />                    (!(KITLClients[i]-&gt;State &amp; KITL_CLIENT_REGISTERED) || !(KITLClients[i]-&gt;CfgFlags &amp; KITL_CFGFL_MULTIINST))) { <br />                    pClient = KITLClients[i]; <br />                    break; <br />                } <br />                if (i &lt; NUM_DFLT_KITL_SERVICES) <br />                    // no dups for default services <br />                    break; </p><p>                if (MAX_KITL_CLIENTS == ++ i) <br />                    i = NUM_DFLT_KITL_SERVICES; </p><p>                if (iStart == i) <br />                    break;  // couldn't find a client <br />            } </p><p>            if (!pClient || !(pClient-&gt;State &amp; (KITL_CLIENT_REGISTERING|KITL_CLIENT_REGISTERED))) { <br />                KITL_DEBUGMSG(ZONE_WARNING,("!Received config for unrecognized service %s\n", <br />                                            pCfg-&gt;ServiceName)); <br />                return TRUE; <br />            } </p><p>            if (fUseSysCalls) <br />                EnterCriticalSection(&amp;pClient-&gt;ClientCS); </p><p>            // Send config to peer, unless this was a response to our cmd <br />            if (!(pHdr-&gt;Flags &amp; KITL_FL_ADMIN_RESP)) { <br />                // ack this config message <br />                SendConfig(pClient,TRUE); </p><p>                // Stop any pending transfers, reset sequence #s, etc</p><p>                // WARNING - can cause lost transmit data if the other side doesn't get <br />                // our config, and retries the config command. <br />                if (pClient-&gt;State &amp; KITL_SYNCED) { <br />                    ResetClientState(pClient); <br />                } <br />            } </p><p>            // <br />            // we got the response from desktop, connecting the client <br />            // <br />            KITL_DEBUGMSG(ZONE_INIT, ("ProcessAdminMsg: Receive Config message for service %s\n", pClient-&gt;ServiceName)); <br />            pClient-&gt;State &amp;= ~(KITL_WAIT_CFG|KITL_CLIENT_REGISTERING); <br />            pClient-&gt;State |= KITL_CLIENT_REGISTERED; <br />            // Set our event in case anyone is waiting for config info <br />            if (fUseSysCalls) <br />                SetClientEvent(pClient,pClient-&gt;evCfg); </p><p>            <br />            if (fUseSysCalls)            <br />                LeaveCriticalSection(&amp;pClient-&gt;ClientCS); <br />            break; <br />        } <br />        case KITL_CMD_RESET: <br />            { <br />                KITL_RESET_DATA *pResetData =  (KITL_RESET_DATA *) KITLDATA (pHdr); </p><p>                KITLOutputDebugString("KITL: Got RESET command\n");</p><p>                // Set for clean boot if requested <br />                if (pResetData-&gt;Flags &amp; KITL_RESET_CLEAN) <br />                    SC_SetCleanRebootFlag(); <br />                <br />                // This function never returns <br />                KernelIoctl(IOCTL_HAL_REBOOT, NULL,0,NULL,0,NULL); <br />                KITLOutputDebugString("KITL: IOCTL_HAL_REBOOT not supported on this platform\n"); <br />                break; <br />            } </p><p>        case KITL_CMD_DEBUGBREAK: <br />            if (fUseSysCalls &amp;&amp; IsDesktopDbgrExist ()) <br />                DebugBreak (); <br />            break; </p><p>        case KITL_CMD_TRAN_CONFIG: <br />            { <br />                int i; <br />                <br />                PKITL_HOST_TRANSCFG pCfg = (PKITL_HOST_TRANSCFG) KITLDATA(pHdr); <br />                wMsgLen -= KITL_DATA_OFFSET; <br />                if (pCfg-&gt;dwLen != wMsgLen) { <br />                    KITLOutputDebugString ("!Host config message size mismatch %d, %d\r\n", pCfg-&gt;dwLen, wMsgLen); <br />                    return FALSE; <br />                } <br />                wMsgLen -= sizeof (KITL_HOST_TRANSCFG); <br />                if (!Kitl.pfnSetHostCfg ((LPBYTE) (pCfg+1), wMsgLen)) <br />                    return FALSE; <br />                Kitl.dwBootFlags = pCfg-&gt;dwFlags; </p><p>                if (pCfg-&gt;dwKeySig == HOST_TRANSCFG_KEYSIG) { <br />                    for (i = 0; i &lt; HOST_TRANSCFG_NUM_REGKEYS; i++) { <br />                        g_dwKeys[i] = pCfg-&gt;dwKeys[i]; <br />                        KITL_DEBUGMSG (ZONE_INIT, (" KeyIndex %d = %d \n", i, g_dwKeys[i])); <br />                    } <br />                }    <br />                KITLGlobalState |= KITL_ST_DESKTOP_CONNECTED; <br />            } <br />            break; </p><p>        // in case we're polling (pfnTransmit &amp;&amp; pData only set to non-null if we're polling) <br />        // we'll use desktop as our timer (desktop sends a retransmit packet to us every 2 seconds). <br />        case KITL_CMD_RETRASMIT: <br />            if (pfnTransmit &amp;&amp; pData) { <br />                // KITLOutputDebugString ("Retrasmitting packets....\n"); <br />                pfnTransmit (pData, fUseSysCalls); <br />            } <br />            break; <br />        default: <br />            KITL_DEBUGMSG(ZONE_WARNING,("!ProcessAdminMsg: Unhandled command 0x%X\n",pHdr-&gt;Cmd)); <br />            return FALSE; <br />    } <br />    return TRUE; <br />} <br />我们直接看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来完成初始化的动作。 <br />BOOL <br />KITLInitializeInterrupt() <br />{ </p><p>    int i; <br />    if (!(KITLGlobalState &amp; KITL_ST_ADAPTER_INITIALIZED)) <br />        return FALSE; <br />    <br />    // Check if we are coming up for the first time, or resuming interrupts (e.g. when coming <br />    // back from OEMPowerOff) <br />    if (KITLGlobalState &amp; KITL_ST_MULTITHREADED) { <br />        // Just enable interrupt and return <br />        EnableInts(); <br />        return TRUE; <br />    } <br />    <br />    KITLOutputDebugString("KITL: Leaving polling mode...\n"); </p><p>    InitializeCriticalSection(&amp;KITLODScs); <br />    InitializeCriticalSection(&amp;KITLKCallcs); </p><p>    KITLGlobalState |= KITL_ST_MULTITHREADED; <br />    <br />    KITL_DEBUGMSG(ZONE_INIT,("KITL Checking client registrations\n")); <br />    // Some clients may have registered already, finish initialization now that <br />    // the system is up. KDBG continues to run in polling mode. <br />    for (i=0; i&lt; MAX_KITL_CLIENTS; i++) { <br />        if (KITLClients[i] &amp;&amp; (i != KITL_SVC_KDBG) <br />            &amp;&amp; (KITLClients[i]-&gt;State &amp; (KITL_CLIENT_REGISTERED|KITL_CLIENT_REGISTERING))) { <br />            if (!RegisterClientPart2((UCHAR)i)) <br />                return FALSE; <br />        } <br />    } </p><p>    // Start interrupt thread. If we have clients registered, also turn on receive interrupts <br />    // from the ethernet controller, otherwise leave them disabled. <br />    if ((UCHAR) KITL_SYSINTR_NOINTR != Kitl.Interrupt) { <br />        KITL_DEBUGMSG(ZONE_INIT,("KITL Creating IST\n")); <br />        if ((hIntrThread = CreateKernelThread((LPTHREAD_START_ROUTINE)KITLInterruptThread, <br />                                              NULL, (WORD)g_dwKITLThreadPriority, 0)) == NULL) { <br />            KITLOutputDebugString("Error creating interrupt thread\n"); <br />            return FALSE; <br />        } <br />    } </p><p>    return TRUE; <br />} <br />由于将会有IST为kitl专门服务，所以也就需要临界区来完成线程的操作，这儿创建了KITLODScs\KITLKCallcs两个临界区。之后标记 KITL_ST_MULTITHREADED。检查注册了的服务，完成后就创建IST. </p><p>kitl的初始化，启动的大致过程就是如此，start--&gt;注册服务-〉初始化transport-&gt;创建IST</p><p>最后我们来看看IST里面我们都干些什么 <br />static DWORD KITLInterruptThread (DWORD Dummy) <br />{ <br />    HANDLE hIntEvent; <br />    DWORD dwRet; </p><p>    KITL_DEBUGMSG(ZONE_INIT,("KITL Interrupt thread started (hTh: 0x%X, pTh: 0x%X), using SYSINTR %u\n", <br />                             hCurThread,pCurThread, Kitl.Interrupt)); </p><p>    pCurThread-&gt;bDbgCnt = 1;   // no entry messages <br />    <br />    if ((hIntEvent = CreateEvent(0,FALSE,FALSE,EDBG_INTERRUPT_EVENT)) == NULL) { <br />        KITLOutputDebugString("KITL CreateEvent failed!\n"); <br />        return 0; <br />    } <br />    if (!SC_InterruptInitialize(Kitl.Interrupt, hIntEvent, NULL,0)) { <br />        CloseHandle(hIntEvent); <br />        KITLOutputDebugString("KITL InterruptInitialize failed\n"); <br />        return 0; <br />    } </p><p>    // always enable interrupt as long as OEM told us so <br />    EnableInts(); <br />    <br />    KITLGlobalState |= KITL_ST_IST_STARTED; <br />    <br />    while ((dwRet = SC_WaitForMultiple (1,&amp;hIntEvent,0,INFINITE)) == WAIT_OBJECT_0) { </p><p>        KITL_DEBUGLED(LED_IST_ENTRY,0); <br />        KITL_DEBUGMSG(ZONE_INTR,("KITL Interrupt event\n")); </p><p>        // no need to check pending, just call HandleRecvInterrupts because it'll <br />        // just return if there is no interrupt pending <br />        HandleRecvInterrupt(ISTRecvBuf,TRUE, NULL, NULL); </p><p>        SC_InterruptDone(Kitl.Interrupt); <br />        <br />        KITL_DEBUGMSG(ZONE_INTR,("Processed Interrupt event\n")); <br />    } <br />    KITLOutputDebugString("!KITL Interrupt thread got error in WaitForMultipleObjects: dwRet:%u, GLE:%u\n", <br />                          dwRet,GetLastError()); <br />    return 0; <br />} <br />首先是创建该IST所属的事件，并将该事件与所属的中断联系起来，再后自然是启动中断了。设定KITL_ST_IST_STARTED标记后的代码就是IST的实际内容，当中断发生，交付HandleRecvInterrupt处理，返回中断。和多数IST一样该调用永远不会返回，除非结束该线程，所以后面的调试信息输出的是错误。 </p><p>void HandleRecvInterrupt(UCHAR *pRecvBuf, BOOL fUseSysCalls, PFN_TRANSMIT pfnTransmit, LPVOID pData) <br />{ <br />    WORD wLen = KITL_MTU; <br />    BOOL fFrameRecvd; </p><p>    // Receive data into buffer <br />    do { <br />        if (!fUseSysCalls) <br />            fFrameRecvd = Kitl.pfnRecv (pRecvBuf, &amp;wLen); <br />        else if (IsDesktopDbgrExist ()) <br />            fFrameRecvd = KCall((PKFN) Kitl.pfnRecv, pRecvBuf, &amp;wLen); <br />        else { <br />            EnterCriticalSection (&amp;KITLKCallcs); <br />            fFrameRecvd = Kitl.pfnRecv (pRecvBuf, &amp;wLen); <br />            LeaveCriticalSection (&amp;KITLKCallcs); <br />        } <br />        if (fFrameRecvd) { <br />            ProcessRecvFrame (pRecvBuf,wLen,fUseSysCalls, pfnTransmit, pData); <br />            wLen = KITL_MTU; <br />        } <br />    } while (fFrameRecvd); <br />} <br />HandleRecvInterrupt是中断处理程序的实体，将transport送来的数据填入缓冲区待处理。上面所提到的中断在OEM代码中指定，在SMDK2440bsp中该中断对应以太网卡中断。 </p><img src ="http://www.cppblog.com/milkyway/aggbug/17673.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-16 10:08 <a href="http://www.cppblog.com/milkyway/articles/17673.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>解读WINCE 5.0 KITL代码流程 </title><link>http://www.cppblog.com/milkyway/articles/17671.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Tue, 16 Jan 2007 02:00:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17671.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17671.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17671.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17671.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17671.html</trackback:ping><description><![CDATA[
		<div class="postbody">
				<p>作者：Walzer<br />日期：2005.3.19<br /><br />摘要：KITL是PLATFORM BUILDER中的一个亮点，提供了和本地调试类似的断点、变量跟踪、内存查看等手段，如果没有KITL，嵌入式调试应该只能用串口打印消息来看了，工作效率大大下降。本文以实现最简单的SERIAL KITL为目的，就其实现代码进行跟踪调试，这些代码跨越了WINCE的PLATFORM、PUBLIC、PRIVATE三大主要目录，有些烦琐，不过只要能调通，一切工作和弯路都是值得的。我把调试经验和个人理解写下来，希望能帮助别人少走弯路。如果文章中有理解失当的地方，请不吝赐教。<br /><br />正文：<br /><br />一.void OEMInit()  [platform\project\src\kernel\oal\init.c]<br />    首先从OEMInit()函数看起。。在依次初始化Branch-Target Buffer、KERNEL函数、初始化中断、TIMER之后，就轮到KITL了。调用了这个函数OALKitlStart()。此时有个编译的分支，如果是RELEASE版本，那么在kernel\kern\stubs.c里面的OALKitlStart()函数是个STUB，只是return TRUE； 如果是DEBUG版本，那就进到kernel\oal\kitl.c里面的OALKitlStart().</p>
				<p>二.BOOL OALKitlStart()  [platform\myproject\src\kernel\oal]<br />    在OALKitlStart()里面，首先试图从0xA00FF00处读取bootloader里面留下的kitl参量，如果读不到东西则使用该函数里的默认配置。由于原来用了ethernet同时作为download和kitl途径，所以在InitSpecifiedEthDevice函数里给pKitlArgs结构体赋值了。现在想把两者划分清楚，首先就把读取bootloader里面的kitl参量一句干掉，强迫使用我们在下面的默认配置。主要就是填充三个参量</p>
				<p>1.首先是这个结构体<br />OAL_KITL_ARGS pKITLArgs<br />{<br /> UINT32 flags;  //设好ENABLED标志，按需要设POLL标志，但注意一定不要设PASSIVE标志<br /> DEVICE_LOCATION  devLoc;<br /> {<br />  DWORD  IfcType;  //不论ether还是serial，都是internal type =0;<br />  DWORD  BusNumber; // =0<br />  DWORD  LogicalLoc; //物理地址<br />  PVOID     PhysicalLoc; //留做后面=OALPAtoVA(LogicalLoc, FALSE). 真见鬼, 感觉应该和上面的LogicalLoc作用调过来看着比较顺吧?<br />  DWORD  Pin;  //Ethernet才用的东东<br /> }<br /> union<br /> {<br />  struct<br />  {<br />   UINT32  baudRate; //不用解释了<br />   UINT32  dataBits;<br />   UINT32  stopBits;<br />   UINT32  parity;<br />  }<br />  struct<br />  {<br />   UINT16  mac[3]; //这个也不用解释了<br />   UINT32  ipAddress;<br />   UINT32  ipMask;<br />   UINT32  ipRoute<br />  }<br /> }<br />}<br />2. pszDeviceID.  感觉这名字就是起了好玩, 赋成Walzer应该比较拽,不过还是保留原来赋的AMOISOFT好了, 免得被打.<br />3. 全局变量OAL_KITL_DEVICE g_kitlDevices. 这个东东在kitl.c开头包含的kitl_cfg.h中被赋值,  最主要就是修改g_kitlDevices.pDriver.  这个pDrvier指向一个函数指针列表的结构体，该列表定义了用做kitl模块的初始化、读写、中断、流控制等函数。 g_kitlDevices本身是个二维数组, 可以定义许多设备用做kitl时提供的参数设置, 后面会用一个for来循环判断pKITLArgs的参数和g_kilDevices里面哪个一维数组成员相匹配.</p>
				<p>这三个参量填充好以后，就可以进到OALKitlInit(pszDeviceID, pKITLArgs, g_kitlDevices)里面了.</p>
				<p>三.BOOL OALKitlInit( deviceId, pArgs, pDevice)    [platform\common\src\common\kitl\kitl.c]<br />    这个函数先把输入的参量全部用OALMSG打印出来，这个不管。<br />    重要的是引入了g_kitlState全局变量，开头一句<br />    g_kitlState.pDevice = OALKitlFindDevice(&amp;pArgs-&gt;devLoc, pDevice) 这个就是上面所说的从g_kitlDevices里可用设备列表里循环判断，找到选用的设备的匹配函数指针。<br />    接着把输入参量devicdId和前面填充好的OAL_KITL_ARGS结构COPY到g_kitlState里面<br />    然后就可以调用KItlInit(TRUE)了，如果前面在FLAG里面设了PASSSIVE标志，现在就是KitlInit(FALSE)了，嘿嘿爽到了吧。</p>
				<p>四.BOOL KitlInit(BOOL fStartKitl)    [private\winceos\coreos\nk\kitl\ethdbg.c]<br />    太猥琐了，我要用串口，它居然叫ethdbg.c，不给面子。不过是private里面的东东，可远观而不可亵玩焉~~<br />    这个函数干了三件事：<br />1. 装载了三个全局的函数指针<br />2. 用NewClient注册了三个KITL客户端：<br />    KITL_SVCNAME_DBGMSG   //debug message, Debug信息发布通道<br />    KITL_SVCNAME_PPSH  //PPshell, 文本控制台界面 <br />    KITL_SVCNAME_KDBG //kernel debug, 内核调试界面 </p>
				<p>3.由fStartKitl来决定是否启动StartKitl()函数. (这里顺便提一下，按照匈牙利命名法, BOOL变量前面加个b, 但MS的做法我觉得很合理, BOOL变量就是个FLAG嘛, 前面加个f, 把小b留着给BYTE类型用.)</p>
				<p>五.static BOOL StartKitl(BOOL fInit)    [private\winceos\coreos\nk\kitl\ethdbg.c]<br />    这又是prviate里面的东东。最痛苦的地方开始了。这函数及其子函数将第一次调用OEM自己写的KITL模块初始化、读写程序。是骡子是马，拉出来溜溜就知道啦~<br />    按顺序看下来，首先判断输入参量是否要启动KITL，并且如果KITLGlobalState里面被打上KITL_ST_DESKTOP_CONNECTED标记的话，那下面的步骤就全免了。当然我们是第一次运行到这里，若这么就跳出的话，俺就马加爵了。</p>
				<p>    第一步:<br />    干的第一件正事就是调用OEMKitlInit(&amp;Kitl). 这个后面详述. 继续把这个函数看完.<br />    OEMKitlInit初始化KITL的硬件抽象层后并把相关指针数据填充给全局变量KITLTRANSPORT Kitl (有没有搞错，为什么不叫g_kitl), 这些工作做完就返回了<br />     StartKitl收货后把Kitl结构整个检查一遍,保证没错后, 马上买单, 把全局变量KITLGlobalState打上个KITL_ST_KITLSTARTED标记. 这才算KITL启动的第一步OK了.</p>
				<p>    第二步:<br />    接下来就是KITLConnectToDesktop(), 进这个函数后就是第一次使用了前面KITL传输介质硬件抽象层里的读写函数了, 这时候就需要调试了. 这个ConnectToDesktop大致就是先Send了一个kITL.....的frame过去,然后polling等待PC端的response, 那边再发个kITL.....的frame过来, 搞得跟地下党打暗号似的, 其实也没什么玄乎的，就是普通的数据包前面加个KTIL专用的HEADER而已. 这个CONNECT成功后,KITLGlobalState里面就加个KITL_ST_DESKTOP_CONNECTED标记了.</p>
				<p>    第三步:<br />    set up kernel function pointers, 也没什么,就三个函数指针, 赋完后就KITL_ST_ADAPTER_INITIALIZED了. 其实这个KITLGolbalSate的总共有7个标志,分别是<br />KITL_STARTED,  (OK)<br />DESKTOP_CONNECTED,   (OK) <br />TIMER_INIT, (?)<br />INT_ENABLED,  (POLLING)<br />IST_STARTED,  (POLLING)<br />MULTITHREADED,   (?)<br />ADAPTER_INITIALIZED. (OK)<br />后面括号里打上OK是到这里已经完成的， 打问号的我还不太清楚什么作用, INT和IST两项,我们用的POLLING所以肯定是不需要了.</p>
				<p>    第四步:<br />    调用SetKernelCommDev设置kernel通过何种介质传送DBGMSG, PPSH和KDBG.<br />    OHYEAH, 我的SERIAL KITL就夭折在这里. 进到SerKernelCommDev(service, CommDevice)函数里看, 它只认CommDevice=KERNEL_COMM_ETHER的情况，而屏蔽了与ETHER并列的SERIAL和PARALLER,直接return FALSE, 下面的事情都不用干了. 而在MS提供的WinCE Documantation里面，这个SetKernelCommDev函数的说明上写着"This function is obsolete and should not be used". 若想改嘛,这个是在private里面的还动它不得. NND, 感觉被MS raped了.<br />     如果使用ETHER在这里成功的话, 下面还有两个函数NKForceCleanBoot()和KITLInitializeInterrupt()走过去, 这KITL初始化就全部结束了. 我估计KITLGolbalSate里面的INIT_ENABLED和IST_STARTED就是在这个函数过程中被标记上的.<br />     <br />    <br />六.BOOL OEMKitlInit(PKITLTRANSPORT pKitl)    [platform\common\src\common\kitl\kitl.c]<br />    前面提到StartKItl起来后，首要的就是调用OEMKitlInit. 这个函数在WinCE4.2和5.0里差别很大, 4.2里的做法是    if (!InitEther (pKitl) &amp;&amp; !InitParallelSerial (pKitl)), 把ETHER, SERIAL, PARALLEL都初始化了一遍,碰运气看哪个用得上，而5.0里是进来后就一个很明显的分支剧情,由g_kitlState.pDevice-&gt;type来决定是调用OALKitlEthInit还是OALKitlSerialInit. 典型的种族歧视, 居然没有OALKitlParallelinit. 还好我们用的是SERIAL.<br />    这里有个选择编译的地方,就是#ifdef KITL_ETHER和#ifdef KITL_SERIAL, 具体定义的地方是该目录下的sources文件里面一行 CDEFINES=$(CDEFINES) -DKITL_SERIAL -DKITL_ETHER, 猥琐啊找了半天. 其实我觉得既然有if结构来选了，那么选择编译也是可有可无的了.<br />    好，下面就进到OALKItlSerialInit()里面.</p>
				<p>七.BOOL OALKitlSerialInit(LPSTR deviceId, OAL_KITL_DEVICE *pDevice, OAL_KITL_ARGS *pArgs, KITLTRANSPORT *pKitl) <br />    [platform\common\src\common\kitl\kitlserial.c]<br />    我自己往这个kitlserial.c文件里写了六个函数.<br />    BOOL KitlSerialInit(KITL_SERIAL_INTFO *pSerInfo)<br />    UINT16 KitlSerialWriteData(UINT8 *pch, UINT16 length)<br />    UINT16 KitlSerialReadData(UINT8 *pch, UINT16 length)<br />    void KitlSerialFlowControl  //stub, 我所用的FFUART只有TXD和RXD两根线, RTS等都没有, 所以FlowControl自然也应该是STUB了<br />    void KitlSerialEnableInt(void)   //stub, use polling<br />    void KitlSerialDisableInt(void)  //stub, use polling<br />    否则前面的g_kitlDevices里面没有相应的硬件抽象层来填充.<br />    上面的SerialRecv, Encode, Decode等就意思都很明显了,不用多说. OK现在已经走到最底层了, 文章也可以结束了.</p>
				<p>八、记录一下调试经验<br />    虽然这是我第三次调串口了，由于没总结前面的经验，还是耗了两天才到private里面夭折的地方。实际上应该一天就能走到了。问题出在<br />1. 第一天调试器根本用不上手。调试器软件、PB不断死翘，经常重启软件甚至重启电脑，第一天有3/4以上的时间耗在这些问题上, 不断重启。<br />2. 在UART初始化函数的最后，居然忘记了在interrupt controller register里面enable uart unit, 这么乌龙的事。<br />3. KitlSerialFlowControl的问题. 写的时候照搬了X86下的函数, 没想明白到底要Control什么. 调试时死在这里后,  一开始把指向这个函数的指针设置成NULL, 但这样PRIVATE里面有些要IF判断的函数就进不去了. 后来换成STUB就OK了.<br />4. receive函数里面, 在收每个BYTE之前先去判断了Line Status Register里面的Data Ready bit, 如果该为零, 则返回失败. 但这里是有问题的，具体也没太想明白, 反正在调试debug serial的时候就把这个判断从MS提供的源码里头删去了，现在做KITL serial时又手痒加进去, 果然还是不行. 这可能是MS或INTEL的一处BUG, 但按照INTEL CPU MANUL UPDATE里面给的读流程, 一开始只判断LSR里面的ERROR, 没有判断DR位就开始读第一个BYTE了. 读过后再判断如果DR位为1,则继续读下一BYTE.<br />5. 在receive函数里加通过debugger加break point, 结果receive buffer register里面的数据被debugger扫描去以后，就变零了，CPU上却什么都收不到, 这事情耗了大半个下午，最后还是Jeffery发现的这个问题.<br /><br />参考文章：<br />KITL解析 by Nasiry  (<a href="http://nasiry.cnblogs.com/archive/2004/09/22/45473.html"><font color="#1d58d1">http://nasiry.cnblogs.com/archive/2004/09/22/45473.html</font></a>)</p>
		</div>
<img src ="http://www.cppblog.com/milkyway/aggbug/17671.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-16 10:00 <a href="http://www.cppblog.com/milkyway/articles/17671.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>"Out of Memory" and Required components for ActiveSync </title><link>http://www.cppblog.com/milkyway/articles/17670.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Tue, 16 Jan 2007 01:51:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17670.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17670.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17670.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17670.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17670.html</trackback:ping><description><![CDATA[
		<div class="postbody">
				<font face="Arial" size="2">来自 <a href="http://www.cnblogs.com/walzer/archive/2006/02/05/325574.html">http://www.cnblogs.com/walzer/archive/2006/02/05/325574.html</a><br />Author: Walzer<br />Date:    2005.5.20<br /><br />摘要: WINCE 5.0中关于ActiveSync的Dependence没有做好，这肯定是MS的一处BUG。我们必须手动地添加这些相关的组件（有4个），否则就会在启动ActiveSync连接时出现Out of Memory的让人匪夷所思的错误。文章的最后给出了如何在注册表中注册一个USB连接作为ActiveSync默认连接的做法。<br /><br />    At first I was so puzzled by this problem for several days. I connected my target to PC by usb and wana enable ActiveSync, the usb serial function seems work well and  repllog.exe is autoloaded. But on the target, there jumps out a  warning window with the title "<strong>Out of Memory</strong>", and the descriptions are "Cannot connect to PC. No program memory available. Stop some programs, and try again. If the problem persist, reset your device according to the owner's manual".</font>
				<div>
						<font face="Arial" size="2">    This platform have 32MB flash  and 32MB SDRAM. I burn nk.nb0 (20M) into the flash and use XIP, so the memory must be enough for this application. But what's the problem?</font>
				</div>
				<div>
						<font face="Arial" size="2">    The debug messages are shown below. </font>
				</div>
				<div>
						<font face="Arial" size="2">
						</font> </div>
				<div>
						<font size="2">
								<font face="Arial">0x83f21000: [NOTIFY] HandleSystemEvent 9 none<br />0x83f21000: [NOTIFY] HandleSystemEvent found repllog.exe for event 9<br />0x83f21000: [NOTIFY] HandleSystemEvent schedules immediate notify for repllog.exe AppRunAtRs232Detect<br />0x83f21000: [NOTIFY] SetUserNotification (or replacing 00000000)<br />0x83f21000: [NOTIFY] SetUserNotification::Setting event semaphore<br />0x83f21000: [NOTIFY] ProcessDatabase::started at local time 01/01/2003 12:00:42<br />0x83f24434: NOTIFICATION::NewPacket attempt to exec 0000073e:repllog.exe AppRunAtRs232Detect<br />0x83d14000: &gt;&gt;&gt; Loading module coredll.dll at address 0x03F40000-0x03FF4000 (RW data at 0x01FFE000-0x01FFF145)<br />0x83d14000: &gt;&gt;&gt; Loading module ws2.dll at address 0x03BB0000-0x03BC4000 (RW data at 0x01FCD000-0x01FCDB24)<br />0x83d14000: &gt;&gt;&gt; Loading module winsock.dll at address 0x03BD0000-0x03BD6000 (RW data at 0x01FCF000-0x01FCF08C)<br />Loaded symbols for 'D:\SOURCE_CODE\WINCE500\PBWORKSPACES\WINDOWTV\RELDIR\WINDOWTV_ARMV4I_DEBUG\WINSOCK.DLL'<br />0x83d14000: &gt;&gt;&gt; Loading module repllog.exe at address 0x0E010000-0x0E02E000<br />Loaded symbols for 'D:\SOURCE_CODE\WINCE500\PBWORKSPACES\WINDOWTV\RELDIR\WINDOWTV_ARMV4I_DEBUG\REPLLOG.EXE'<br />0x83d14000: RLOG: [0x43F24D0E] Started with cmdline: AppRunAtRs232Detect<br />0x83d14000: AddToProcessInputLocaleTable: Added process to ProcessInputLocale table, hProcess = 0x43D3A242<br />0x83d14000: RLOG: [0x43F24D0E] Using '`USB' connection<br />0x83d14000: RLOG: [0x43F24D0E] Welcome to repllog. Port in use: Dccman: 5679; RRA: 5678<br />0x83d14000: RLOG: [0x43F24D0E] WM_WINDOWREADY<br />0x83d14000: RLOG: [0x43F24D0E] About to run rapisrv.exe<br />0x83f24434: [NOTIFY] DeleteUserNotification 0000073e<br />0x83f24434: [NOTIFY] DeleteUserNotification: 0000073e deleted<br />0x83e84bb4: &gt;&gt;&gt; Loading module coredll.dll at address 0x03F40000-0x03FF4000 (RW data at 0x01FFE000-0x01FFF145)<br />0x83e84bb4: &gt;&gt;&gt; Loading module ws2.dll at address 0x03BB0000-0x03BC4000 (RW data at 0x01FCD000-0x01FCDB24)<br />0x83e84bb4: &gt;&gt;&gt; Loading module winsock.dll at address 0x03BD0000-0x03BD6000 (RW data at 0x01FCF000-0x01FCF08C)<br />0x83e84bb4: &gt;&gt;&gt; Loading module rapisrv.exe at address 0x10010000-0x10031000<br />Loaded symbols for 'D:\SOURCE_CODE\WINCE500\PBWORKSPACES\WINDOWTV\RELDIR\WINDOWTV_ARMV4I_DEBUG\RAPISRV.EXE'<br />0x83e84bb4: AddToProcessInputLocaleTable: Added process to ProcessInputLocale table, hProcess = 0xE3D4A1BE<br />RpcSrv: Rapi Server running on DeviceType: 2<br />0x83d14000: RLOG: [0x43F24D0E] Using '`USB' connection<br />0x83d14000: RLOG: [0x43F24D0E] About to run rnaapp.exe -n -m -e"`USB"<br />RpcSrv: Winsock Started version 1.1<br />0x83d01000: &gt;&gt;&gt; Loading module wspm.dll at address 0x03B90000-0x03B97000 (RW data at 0x01FC9000-0x01FC9154)<br />Loaded symbols for 'D:\SOURCE_CODE\WINCE500\PBWORKSPACES\WINDOWTV\RELDIR\WINDOWTV_ARMV4I_DEBUG\WSPM.DLL'<br />RpcSrv: SocketBufSize=16384 bytes<br />0x83d01000: &lt;RPC:RESLIST&gt;  <br />0x83d01000: CResList::CResList()<br /></font>
								<font face="Arial">
										<strong>0x83d01928: CreateNewProc failure on rnaapp.exe!<br />0x83d14000: RLOG: [0x43F24D0E] CreateProcess rnaapp.exe failed. GetLastError: 2</strong>
										<br />0x83d14000: Grow Gdi handle table from 448 to 512<br />0x83d14000: DlgMgr: FindDlgItem id 1 returning NULL.<br /></font>
						</font>
				</div>
				<div>
						<font size="2">
								<font face="Arial">
								</font>
						</font> </div>
				<div>
						<font size="2">
								<font face="Arial">BTW, the source code of rapllog.exe is at <strong>private\datasync\apps\conn31\rep\repllog\</strong> . But Microsoft haven't publish these codes yet.</font>
						</font>
				</div>
				<div>
						<font size="2">
								<font face="Arial">
								</font>
						</font> </div>
				<div>
						<font face="Arial" size="2">----------------------------------------------------------------------------------------------------------------</font>
				</div>
				<div>
						<font face="Arial" size="2">
						</font> </div>
				<div>
						<font face="Arial" size="2">
								<strong>In fact, "out of memory" is a fake message. I asked it on the newsgroup and someone tell me they have seen a similar message when missing a required OS component.</strong> I am so sorry to say that, the dependency checks on ActiveSync in WinCE5.0 are not very good, so we have to add them into our OSDesign manually. </font>
				</div>
				<div>
						<font face="Arial" size="2">
						</font> </div>
				<div>
						<div>
								<font face="Arial">
										<strong>The required and relative components are:</strong>
								</font>
						</div>
						<div>
								<div>
										<font face="Arial">
												<strong>  (1) Core OS-&gt;Applications - End User-&gt;ActiveSync-&gt;File Sync</strong>
										</font>
								</div>
								<div>
										<font face="Arial">
												<strong>  (2) Core OS-&gt;Communication Services and Networking-&gt;Networking - Wide Area Network (WAN)-&gt;Dial Up Networking (RAS/PPP) <br />  (3) Core OS-&gt;Communication Services and Networking-&gt;Networking - Wide Area Network (WAN)-&gt;Telephony API (TAPI 2.0)-&gt;Unimodem Support</strong>
										</font>
								</div>
								<div>
										<font face="Arial">
												<strong>  (4) Core OS-&gt;Shell and User Interface-&gt;User Interface-&gt;Network User Interface</strong>
										</font>
								</div>
								<div>
										<font face="Arial">
										</font> </div>
								<div>
										<font face="Arial">----------------------------------------------------------------------------------------------------------------</font>
								</div>
								<div>
										<font face="Arial">
										</font> </div>
								<div>
										<font face="Arial">After the system completely booted, we need to creat a new connection in control pannel -&gt; Dial Up Networking. Select USB direct connection then OK, for example use the default name "My Connection".  In the other hand, set PC connection to the newly created "My Connection". Now connect the USB cable, and the ActiveSync on desktop will sound a dulcet tune~~~</font>
								</div>
								<div>
										<font face="Arial">
										</font> </div>
								<div>
										<font face="Arial">But these two steps we can wirte them into registry before. To take an easy way, I compare the registry on traget device before these settings and after it, and these are the changes:</font>
								</div>
								<div>
										<font face="Arial">
										</font> </div>
						</div>
				</div>
				<div>
						<font size="2">[HKEY_CURRENT_USER\Comm\RasBook\USB Connection]<br />"Entry"=hex:\<br />      08,02,40,00,00,00,00,00,00,00,00,00,00,00,00,00,f4,db,04,12,d0,77,01,7c,01,\<br />      00,00,00,01,00,00,00,01,00,00,00,1c,e3,04,12,0a,00,00,00,74,dc,04,12,01,00,\<br />      00,00,18,dc,04,12,68,53,03,00,f0,4b,01,7c,18,dc,04,12,d8,55,03,00,20,dc,04,\<br />      12,ca,1b,05,12,00,00,00,00,00,00,00,00,00,00,00,00,f0,4b,01,7c,00,00,00,00,\<br />      0a,00,00,00,00,00,00,00,ec,3b,37,03,54,dc,04,12,f0,4b,01,7c,00,1c,1a,00,00,\<br />      00,00,00,07,18,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\<br />      00,00,b8,dd,04,12,00,00,00,00,f0,aa,00,00,08,00,00,00,00,00,00,00,0a,00,00,\<br />      00,00,00,00,00,4c,f7,f6,03,00,00,00,00,6c,02,00,00,00,00,00,00,4a,c0,d7,83,\<br />      00,00,00,00,a4,dc,04,12,00,00,00,00,f0,4b,01,7c,01,00,00,00,04,6e,f6,03,b0,\<br />      dc,04,12,b8,c5,14,80,38,22,05,00,c0,71,01,7c,38,22,05,00,38,22,05,00,b0,dc,\<br />      04,12,c0,71,01,7c,f0,4b,01,7c,01,00,00,00,4a,c0,d7,83,74,dc,04,12,74,dc,04,\<br />      12,0a,00,00,00,01,00,00,00,eb,ff,ff,ff,4a,cf,d7,83,00,00,00,00,00,00,00,00,\<br />      00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,01,00,00,00,64,00,69,00,72,\<br />      00,65,00,63,00,74,00,00,00,04,12,00,00,00,00,00,00,00,00,0a,00,00,00,f0,4b,\<br />      01,7c,f0,4b,55,00,53,00,42,00,20,00,43,00,61,00,62,00,6c,00,65,00,3a,00,00,\<br />      00,00,00,00,00,a0,6f,fe,80,00,00,00,00,01,00,00,00,0a,00,00,00,02,00,00,00,\<br />      00,00,00,00,78,dd,04,12,b4,df,0f,00,d0,77,01,7c,89,01,00,00,00,00,00,00,ca,\<br />      1b,05,12,0a,00,00,00,7c,09,f6,03,00,00,00,00,0a,00,00,00,00,00,00,00,0a,00,\<br />      00,00,0a,00,00,00,ca,1b,05,12,b0,dd,04,12,b8,af,0f,00,00,ca,2a,0a,81,00,00,\<br />      00,ca,1b,05,12,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,ca,2a,0a,\<br />      ca,1b,05,12,50,18,00,00,03,00,00,00,05,00,00,00,74,66,2a,0a,df,fd,ff,ff,00,\<br />      00,00,00,4a,c0,d7,83,ca,1b,05,12,88,01,00,00,7c,fd,ff,ff,84,02,00,00,94,66,\<br />      2a,0a,00,de,04,12,00,00,00,00,0c,de,04,12,0c,de,04,12,04,6e,f6,03,0c,de,04,\<br />      12,9c,66,2a,0a,80,12,05,00,a0,5f,01,7c,80,12,05,00,80,12,05,00,0c,de,04,12,\<br />      a0,5f,01,7c,80,22,05,00,80,22,05,00,00,00,00,00,44,e0,04,12,18,08,e3,03,00,\<br />      00,00,00,d4,e1,e2,03,08,00,00,00,0a,00,00,00,01,00,00,00,01,00,00,00,38,22,\<br />      05,00,00,ca,2a,0a,4a,c0,d7,83,ca,1b,05,12,ca,1b,05,00,ca,1b,05,00,0c,00,00,\<br />      00,7c,09,f6,03,00,00,00,00,0a,00,00,00,88,de,04,12,cc,f3,f6,03,88,de,04,12,\<br />      4c,f7,f6,03,4a,c0,d7,83,4a,c0,d7,83,e4,b9,14,80,4a,c0,d7,83,a8,de,04,12,fc,\<br />      5c,08,00,ca,1b,05,12,ac,de,04,12,4a,c0,d7,83,4a,c0,d7,83,00,00,00,00,4a,c0,\<br />      d7,83,d6,9a,f9,e3,01,00,00,00,4a,c0,d7,83,00,ff,04,12,f0,4b,01,7c,f0,4b,01,\<br />      7c,64,aa,0f,00,4a,cf,d7,83,01,00,00,00,01,00,00,00,00,00,00,00,f4,de,04,12,\<br />      58,bf,02,00,00,00,00,00,f0,4b,01,7c,04,df,04,12,b4,5b,08,00,18,df,04,12,28,\<br />      df,04,12,10,df,04,12,01,00,00,00,00,00,00,00,ca,1b,05,12,00,00,00,00,00,87,\<br />      03,00,d6,9a,f9,e3,0a,00,00,00,00,00,00,00,d0,76,01,7c,00,4b,01,7c,64,aa,0f,\<br />      00,ca,1b,05,12,01,00,00,00,0a,00,00,00,00,00,00,00,d0,76,01,7c,01,00,00,00,\<br />      01,4b,01,7c,38,df,04,12,01,df,04,12,60,c1,02,00,d0,76,01,7c,01,00,00,00,01,\<br />      00,00,00,01,00,00,00,1c,e3,04,12,0a,00,00,00,f4,df,04,12,01,00,00,00,98,df,\<br />      04,12,68,53,03,00,f0,4b,01,7c,98,df,04,12,d8,55,03,00,a0,df,04,12,ca,1b,05,\<br />      12,00,00,00,00,00,00,00,00,00,00,00,12,00,00,00,00,00,00,00,00,01,00,00,00,\<br />      fc,82,cb,03,20,8f,d4,83,a0,6f,fe,80,a0,6f,fe,80,02,00,00,00,00,00,00,12,dc,\<br />      df,04,12,bc,0a,12,80,e4,fc,d3,83,01,00,00,00,20,8f,d4,83,a0,6f,fe,80,a0,6f,\<br />      fe,80,02,00,00,00,e4,fc,d3,83,a0,e0,04,12,00,00,00,00,20,8f,d4,83,d0,1f,f7,\<br />      83,00,00,00,00,00,00,00,00,fc,82,cb,03,00,00,00,00,0f,00,00,00,f0,6a,fe,80,\<br />      fc,82,cb,03,20,8f,d4,83,00,00,00,00,00,00,00,00,ec,df,04,12,01,d2,06,00,01,\<br />      00,00,00,23,00,00,00,00,00,00,00</font>
				</div>
				<div>
						<font size="2">
						</font>
				</div>
				<div>
						<font size="2">
						</font> </div>
				<div>
						<font size="2">[HKEY_CURRENT_USER\ControlPanel\Comm] <br />    "Cnct"="USB Connection" </font>
				</div>
				<div>
						<font size="2"> </font>
				</div>
				<div>
						<font size="2">I can't understand what does the table of hexes mean, but it did work well in the registry. So, after the system booted, we can conntect usb cable and enable ActiveSync automatically without any manual settings.</font>
				</div>
		</div>
<img src ="http://www.cppblog.com/milkyway/aggbug/17670.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-16 09:51 <a href="http://www.cppblog.com/milkyway/articles/17670.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinCE中划分Storage Memory和Program Memory </title><link>http://www.cppblog.com/milkyway/articles/17669.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Tue, 16 Jan 2007 01:49:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17669.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17669.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17669.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17669.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17669.html</trackback:ping><description><![CDATA[来自 <a href="http://www.cnblogs.com/walzer/archive/2006/04/21/380851.html">http://www.cnblogs.com/walzer/archive/2006/04/21/380851.html</a><br /><br /><p>首先解释下这两个东东</p><p>The RAM on a Windows CE–based device is divided into two areas: the object store and the program memory. </p><p>The object store resembles a permanent, virtual RAM disk. Data in the object store is retained when you suspend or perform a soft reset operation on the system. Devices typically have a backup power supply for the RAM to preserve data if the main power supply is interrupted temporarily. When operation resumes, the system looks for a previously created object store in RAM and uses it, if one is found. Devices that do not have battery-backed RAM can use the hive-based registry to preserve data during multiple boot processes. </p><p>The program memory consists of the remaining RAM. Program memory works like the RAM in personal computers — it stores the heaps and stacks for the applications that are running. </p><p>我承认我很懒，上面一段话的URL是ms-help://MS.WindowsCE.500/wcecoreos5/html/wce50conMemoryArchitecture.htm</p><p>具体的设置可以在系统启动后，Control Panel -&gt; System -&gt; Memory 里面看到。默认的是把内存五五开，一半给Storage Memory， 一半给Program Memory用。这样显然是不合算的。以64M的RAM为例, 启动后Storage Memory 32M, 而因为没有留出界面让用户往里面拷东西, 任何时候in use都不会超过10M; Program Memory也是32M, 但启动后就用掉27M, 实际上应用程序可用的内存只有5M, 一旦达到了上限, 那么每前进一步都要很艰难地去释放几十K内存，然后用掉，再去释放几十K内存，如此循环，此时应用程序的运行速度狂慢无比.</p><p>划分的方法也很简单, 只不过可能没人注意到而已.</p><p>说明在ms-help://MS.WindowsCE.500/wceosdev5/html/wce50lrfFSRAMPERCENT.htm  懒得看英文的人就继续往下看</p><p>其实说白了就一句话, 在BSP的config.bib里 CONFIG 区添加这个变量 FSRAMPERCENT = 0xXXXXXX, 但注意两点，</p><p>(1) 必须写在config.bib的CONFIG区里, 不是plagform.bib不是config.reg等其他文件而是config.bib，也不是config.bib文件的任意地方而一定要在CONFIG REGION里. <br />(2) FSRAMPERCENT这个变量一定得写为FSRAMPERCENT, 不能写成FSROMPERCENT不能写成ILOVEU, 或者阿猫阿狗什么的. </p><p>写下这两句的时候本人已经打开无敌光环, 免疫一切鸡蛋和西红柿.</p><p>FSRAMPERCENT是一个4byte长度的十六进制数, 我们用代数假设 FSRAMPERCENT = 0xQXYZ, 其中Q,X,Y,Z都是十六进制数</p><p>那么最终划分给Storage Memory的大小 =  ( Q + X + Y +  Z ) / 0x400 * TOTAL_RAM_SIZE</p><p>以文中的例子来算, FSRAMPERCENT=0x10203040, 假设TOTAL_RAM_SIZE=64M, 那么StorageMemory= (0x10 + 0x20 + 0x30 + 0x40) / 0x400 * 64M = 10M.</p><img src ="http://www.cppblog.com/milkyway/aggbug/17669.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-16 09:49 <a href="http://www.cppblog.com/milkyway/articles/17669.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>RAPI初始化算法和SAMPLE CODE</title><link>http://www.cppblog.com/milkyway/articles/17667.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Tue, 16 Jan 2007 01:42:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17667.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17667.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17667.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17667.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17667.html</trackback:ping><description><![CDATA[来自 <a href="http://www.cnblogs.com/walzer/archive/2006/02/05/325605.html">http://www.cnblogs.com/walzer/archive/2006/02/05/325605.html</a><br /><p>作者：Walzer<br />日期：2005.12.12<br /><br />RAPI全写为Remote Application Interface, 就是PC端调用这组API, 通过ActiveSync来操作TARGET端WindowsCE作业. 这个功能估计以后在WINCE或WIN MOBILE的应用上会用到许多</p><p>我今天修改了同事留下的Updateboot.exe的代码, 改进蓝牙读写的模块. 这个地方我们用到了RAPI, 看一下他们在程序中初始化RAPI的做法</p><p> HRESULT hRapiResult;<br /> hRapiResult = CeRapiInit();<br /> if(hRapiResult != S_OK)<br /> {<br />     m_ValueEdit.SetWindowText((LPCTSTR)"初始化RAPI失败");<br />     return;<br /> }</p><p>看起来是平淡无奇, 实际上单步一下就可以发现运行到CeRapiInit()时, 程序就BLOCK在这里了，死活走不下去, 并没有达到 if(hRapiResult != S_OK)的预期目的. 我查了一下CeRapiInit()的说明:</p><p>A call to <b><font style="BACKGROUND-COLOR: #b2b4bf">CeRapiInit</font></b> does not return until the connection is made, an error occurs, or another thread calls <b>CeRapiUninit</b>. </p><p>也就是说像我现在并没有把板子和PC相连并启动ACTIVE SYNC时, 这个CeRapiInit()是肯定赖着不走了, 程序会死在这里. (鄙视一下谁写的代码，这个坑好大啊)  因此想到了重新创立个等待进程调用CeRapiUninit来干掉它. 不过这样做显然不厚道, 创立进程需要占用更多的内存. 所以用了上句说明的下半段:</p><p>The <b>CeRapiInitEx</b> function does not block the calling thread. Instead, it uses an event to signal when initialization is complete.</p><p>建立个事件, 用WaitForSingleObject来等他, 超时就BYEBYE了. 贡献自己写的如下代码, 以后RAPI INIT可以参考<br /><br /></p><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">BOOL RapiInitialzation()<br />{<br />    RAPIINIT struRapiInit;   </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">这个是CeRapiInitEx函数要求的入口参数</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">    DWORD dwWaitResult </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;  </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">等待初始化完成事件的变量</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">    HRESULT hRapiResult </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> NULL; </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">CeRapiInitEx的返回HANDLE</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000"><br />    </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> ( m_bRapiInitFlag </span><span style="COLOR: #000000">==</span><span style="COLOR: #000000"> FALSE ) </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">全局的一个FLAG,如果初始化过就不再重复了</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">    {<br />        struRapiInit.cbSize </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(RAPIINIT);  </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">填满该结构体仅有的三个成员</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">        struRapiInit.hrRapiInit </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> NULL;  </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">明知是输出参数也顺手填一下, 我以前吃过亏, 惊弓之鸟</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">        struRapiInit.heRapiInit </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> NULL;<br /><br />        hRapiResult </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> CeRapiInitEx(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">struRapiInit);  </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">关键点</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000"><br />        m_ValueEdit.SetWindowText((LPCTSTR)</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Wait 2 second for RapiInit</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">); </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">后面2秒程序要顿一下了, 得告诉用户.  m_ValutEdit和对话框里一个IDC_STATIC关联了.</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">        dwWaitResult </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> WaitForSingleObject(struRapiInit.heRapiInit, </span><span style="COLOR: #000000">2000</span><span style="COLOR: #000000">);  </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">关键点</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000"><br />        </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">( hRapiResult </span><span style="COLOR: #000000">==</span><span style="COLOR: #000000"> S_OK </span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000"> <br />            struRapiInit.hrRapiInit </span><span style="COLOR: #000000">==</span><span style="COLOR: #000000"> S_OK </span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000"><br />            dwWaitResult </span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000"> WAIT_TIMEOUT)    </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">保守起见, 三个返回值都判断</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">        {<br />           m_bRapiInitFlag </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> TRUE;<br />           </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> TRUE;<br />        }<br />        </span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br />        {<br />            m_ValueEdit.SetWindowText((LPCTSTR)</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">The initialization of RAPI falied, you need to install an ActiveSync or connect the IPTV to PC</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);   </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">一般是没连接导致, 当然也可能用户没装ActiveSync</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">           </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> FALSE;<br />        }<br />    }<br />    </span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br />    {<br />         m_bRapiInitFlag </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> TRUE;<br />         </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> TRUE;<br />    }<br />}<br /></span></div><img src ="http://www.cppblog.com/milkyway/aggbug/17667.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-16 09:42 <a href="http://www.cppblog.com/milkyway/articles/17667.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>multi-xip的实现 </title><link>http://www.cppblog.com/milkyway/articles/17664.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Tue, 16 Jan 2007 01:29:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17664.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17664.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17664.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17664.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17664.html</trackback:ping><description><![CDATA[来自 <a href="http://www.cnblogs.com/yakin/archive/2005/07/20/196572.aspx">http://www.cnblogs.com/yakin/archive/2005/07/20/196572.aspx</a><br />作者: yakin<br /><br /><p>multi-xip实际上很有用，但是现在有一个为难的事情：就是OS起来之后无法写flash，这个很让人苦恼。所以这也导致升级程序无法设置标志位。只能用GPIO口。</p><p>Multi-xip的实现：<br />1) Bib文件的修改：<br />   MEMORY</p><p>    RSVD     80000000  000FF000  RESERVED<br />    ARGS     800FF000  00001000  RESERVED<br />    NK       9C600000  01000000   RAMIMAGE<br />    APP      9D600000  00500000   RAMIMAGE<br />    CHAIN    9DB00000  00002000   RESERVED<br />    RAM      80100000  01F00000  RAM<br />    pdwXIPLoc 00000000 9DB00000  FIXUPVAR</p><p>CONFIG</p><p>    AUTOSIZE=ON<br />    ROM_AUTOSIZE=OFF<br />    RAM_AUTOSIZE=OFF<br />    DLLADDR_AUTOSIZE=ON <br />    XIPSCHAIN=9DB00000<br />    ROMSTART=9C600000<br />    ROMWIDTH=32<br />    ROMSIZE=01600000</p><p>    KERNELFIXUPS=ON</p><p>2) 在OEMInit加一个连接各个bin的函数：<br />void InitRomChain()<br />{<br /> // Added for MultiXIP stuff<br /> static  ROMChain_t s_pNextRom[MAX_ROM] = {0};<br /> DWORD  dwRomCount = 0;<br />    DWORD       dwChainCount = 0;<br />    DWORD *     pdwCurXIP;<br />    DWORD       dwNumXIPs;<br />    PXIPCHAIN_ENTRY pChainEntry = NULL;<br />    if(pdwXIPLoc == NOT_FIXEDUP){<br />        return;  // no chain or not fixed up properly<br />    }<br />    // set the top bit to mark it as a virtual address<br />    pdwCurXIP = (DWORD*)(((DWORD)pdwXIPLoc) | 0x80000000);<br />    // first DWORD is number of XIPs<br />    dwNumXIPs = (*pdwCurXIP);<br />   if(dwNumXIPs &gt; MAX_ROM){<br />      OALMSG(TRUE, (L"ERROR: Number of XIPs exceeds MAX\r\n"));<br />      //lpWriteDebugStringFunc(TEXT("ERROR: Number of XIPs exceeds MAX\n"));<br />      return;<br />    }<br />    pChainEntry = (PXIPCHAIN_ENTRY)(pdwCurXIP + 1);<br />    while(dwChainCount &lt; dwNumXIPs)<br />    {<br />        if ((pChainEntry-&gt;usFlags &amp; ROMXIP_OK_TO_LOAD) &amp;&amp;  // flags indicates valid XIP<br />            *(LPDWORD)(((DWORD)(pChainEntry-&gt;pvAddr)) + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE)<br />        {<br />            s_pNextRom[dwRomCount].pTOC = *(ROMHDR **)(((DWORD)(pChainEntry-&gt;pvAddr)) + ROM_SIGNATURE_OFFSET + 4);<br />            s_pNextRom[dwRomCount].pNext = NULL;<br />            if (dwRomCount != 0)<br />            {<br />                s_pNextRom[dwRomCount-1].pNext = &amp;s_pNextRom[dwRomCount];<br />            }<br />            else<br />            {<br />                OEMRomChain = s_pNextRom;<br />            }<br />            dwRomCount++;<br />        }<br />        else<br />        {<br />            OALMSG(TRUE, (L"Invalid XIP found\r\n"));<br />            //lpWriteDebugStringFunc( _T("Invalid XIP found\n") );<br />        }<br />        ++pChainEntry;<br />  dwChainCount++;<br /> }<br />}<br />  这是从CEPC中拷贝过来的。</p><p><br />通过上面的设置，romimage会生成3个bin，nk.bin，app.bin，chain.bin，还有一个xip.bin，是上面三个bin的集合体。我们download是要download xip.bin，这样就可以实现multibin。通过调试发现，InitRomChain就是利用chain.bin来连接各个bin的。<br />这样我们也理解了bib文件中这个语句的含义：<br />pdwXIPLoc 00000000 9DB00000  FIXUPVAR<br />也就是FIXUPVAR的含义。我们看到在代码中我们同样定义了pdwXIPLoc，这样romimage时，就将9DB00000赋给pdwXIPLoc。这就是FIXUPVAR的作用。正如pTOC也是由romimage赋值一样。</p><img src ="http://www.cppblog.com/milkyway/aggbug/17664.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-16 09:29 <a href="http://www.cppblog.com/milkyway/articles/17664.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CE中将PC上指定文件加入NK, 并放到目标机上指定目录</title><link>http://www.cppblog.com/milkyway/articles/17662.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Tue, 16 Jan 2007 01:14:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17662.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17662.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17662.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17662.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17662.html</trackback:ping><description><![CDATA[来自 <a href="http://www.cnblogs.com/walzer/archive/2006/02/05/325604.html">http://www.cnblogs.com/walzer/archive/2006/02/05/325604.html</a><br /><div class="postbody"><p>作者：Walzer<br />日期：2005.9.29<br /><br />以这次要加入PC上事先做好的bookmark.htm, 放到板上DiskOnChip/Documents and Settings/Walzer/ 目录下为例.</p><p>首先把bookmark.htm拷贝到ie.bib的同级目录public\ie\oak\files\下，然后在bib里包含<br />bookmark.htm    $(_FLATRELEASEDIR)\bookmark.htm             NK SH<br />即把该文件放到NK里，DOWNLOAD下去后是系统隐藏文件。由于我们用了Multi-Bin的做法，所以还得在PLATFORM下的xip.cfg里指定新加入文件放到哪个BIN里面.</p><p>这个地球人都知道了。重点是下面的如何把文件在DOWNLOAD后放到指定目录里。</p><p>找到ie.dat, 里面的语法格式参考ms-help://MS.WindowsCE.500/wceosdev5/html/wce50conFileSystemFile.htm<br />我在里面加了一段<br />Directory("\DiskOnChip"):-Directory("Documents and Settings")<br />Directory("\DiskOnChip\Documents and Settings"):-Directory("Walzer")<br />Directory("\DiskOnChip\Documents and Settings\Amoi"):-File("bookmark.htm","\Windows\bookmark.htm")<br />先建立两层子目录，然后把ROM里面的bookmark.htm拷贝到目录里。值得一提的是第三行的最后一个参量，\Windows\bookmark.htm 下载后的文件都在ROM里面，\windows就是指ROM里的文件。</p><p>类似的dat文件还很多，SYSGEN的时候就在PBWorkspace的相应RELEASE目录里胜利会师, 合并为initobj.tmp, 这个文件和合并前零散的都为ASCII码，然后要转成UNICODE生成initobj.dat，最后我们可以看到ce.bib里包含了initobj.dat这个文件，并入最终生成的BIN文件里。</p></div><img src ="http://www.cppblog.com/milkyway/aggbug/17662.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-16 09:14 <a href="http://www.cppblog.com/milkyway/articles/17662.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何为Win CE系统开发应用程序</title><link>http://www.cppblog.com/milkyway/articles/17589.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Sat, 13 Jan 2007 08:34:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17589.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17589.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17589.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17589.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17589.html</trackback:ping><description><![CDATA[
		<p class="MsoNormal">
				<span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: #666666; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">To write applications for Windows CE, Microsoft offers a rich set of languages for creating managed (.NET) or native applications. You can use Visual Studio .NET to write managed code or use eMbedded Visual C ++ to write native code.<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /?><o:p></o:p></span>
		</p>
		<p class="MsoNormal">
				<span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: #666666; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">
						<o:p> </o:p>
				</span>
		</p>
		<p class="MsoNormal">
				<span lang="EN-US" style="FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">
						<span style="mso-tab-count: 1">    </span>
				</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">在什么情况下应该用</span>
				<span lang="EN-US" style="FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">Visual Studio .NET</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">而不是</span>
				<span lang="EN-US" style="FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">eMbedded Visual C++</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">来为</span>
				<span lang="EN-US" style="FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">Win CE</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">系统开发应用程序？</span>
				<span lang="EN-US" style="FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">
						<o:p>
						</o:p>
				</span>
		</p>
		<p class="MsoNormal">
				<span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: #666666; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">The type of application that you want to create will dictate the choice between native and managed code (.NET). When performance and control are the highest priorities, developers should turn to eMbedded Visual C++ or native code. When a consistent programming model and time-to-market are the primary considerations, the advantages offered by Visual Studio .NET are unparalleled.<o:p></o:p></span>
		</p>
		<p class="MsoNormal">
				<span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: #666666; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">
						<o:p> </o:p>
				</span>
		</p>
		<p class="MsoNormal">
				<span lang="EN-US" style="FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">.NET Compact Framework</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">应用程序的运行性能是否和</span>
				<span lang="EN-US" style="FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">eMbedded Visual C++</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">应用程序一样？</span>
				<span lang="EN-US" style="FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 10.5pt">
						<o:p>
						</o:p>
				</span>
		</p>
		<p class="MsoNormal">
				<span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: #666666; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">In most cases, applications that you write with eMbedded Visual C++ will run faster than those that you write by using Microsoft Visual Basic .NET or Microsoft Visual C# .NET. However, for computationally-intensive portions of an application, you will see a substantial improvement of your Visual Basic .NET applications over their eMbedded Visual Basic .NET equivalents.<o:p></o:p></span>
		</p>
		<p class="MsoNormal">
				<span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: #666666; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">
						<o:p> </o:p>
				</span>
		</p>
<img src ="http://www.cppblog.com/milkyway/aggbug/17589.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-13 16:34 <a href="http://www.cppblog.com/milkyway/articles/17589.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Pocket PC的应用程序是否可以直接在Windows CE设备上运行</title><link>http://www.cppblog.com/milkyway/articles/17588.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Sat, 13 Jan 2007 08:32:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17588.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17588.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17588.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17588.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17588.html</trackback:ping><description><![CDATA[来自<a href="http://www.advantech.com.cn/microsoft/shownews.asp?id=79">http://www.advantech.com.cn/microsoft/shownews.asp?id=79</a><br /><br /><p class="MsoNormal"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: #666666; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">Running an application natively refers to running an application without recompiling it for a different environment. Windows CE .NET 4.2 and Windows CE 5.0 devices can run Pocket PC applications if both of the following are true: <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /?><o:p></o:p></span></p><p class="MsoNormal"><span style="FONT-SIZE: 9pt; COLOR: #666666; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">•</span><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: #666666; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><span style="mso-tab-count: 1">      </span>The application is designed for the same CPU architecture <o:p></o:p></span></p><p class="MsoNormal"><span style="FONT-SIZE: 9pt; COLOR: #666666; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">•</span><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: #666666; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><span style="mso-tab-count: 1">      </span>The application calls the same API set as the Pocket PC 2003 SDK. Windows CE .NET 4.2 and Windows CE 5.0 support the same SDK APIs from CESHELL and AYGSHELL. <o:p></o:p></span></p><p class="MsoNormal"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: #666666; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">For more information, visit the Windows CE Application Development page.<o:p></o:p></span></p><img src ="http://www.cppblog.com/milkyway/aggbug/17588.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-13 16:32 <a href="http://www.cppblog.com/milkyway/articles/17588.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 怎样添加驱动到CE的CATALOG中去</title><link>http://www.cppblog.com/milkyway/articles/17247.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Thu, 04 Jan 2007 08:18:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17247.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17247.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17247.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17247.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17247.html</trackback:ping><description><![CDATA[
		<font size="4">try: <br />1,create a new catalog,and save it to WINCE PLATFORM BUILDER\4.00\cepc\cec\ directory. <br />2,create a new feature group for the catalog,define the "name" and "group". <br />3,add a new feature for the feature group.define the "name" and "locale:0409". <br />4,add some new build method for the feature,select from "step". <br />5,add a new action for the build method. <br />6,if u have *.bib file,and a new bib file and location the bib file to the NK.BIN whatever bib section defined to modules or files <br />7,save it and add it to catalog,can use pb or cec edit. <br />the important is: <br />select the correct build method to using and the correct action to using. <br />build method include:PreSYSGEN,SYSGEN,PostSYSGEN,PreBSP,BSP,PostBSP,PreBuildRel,BuildRel,PostBuildRel,PreMakeImg,MakeImg,PostMakeImg. <br />action include:Build,Copy,Custom,Environment,Source Browse. <br />if the drvier have source file(*.c,*.h),must select BSP and Source Browse. <br />if the driver have no source file and have *.dll file please select any build method and "Copy" action.</font>
<img src ="http://www.cppblog.com/milkyway/aggbug/17247.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-04 16:18 <a href="http://www.cppblog.com/milkyway/articles/17247.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何防止Windows CE .NET盗用COM1端口</title><link>http://www.cppblog.com/milkyway/articles/17246.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Thu, 04 Jan 2007 08:01:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17246.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17246.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17246.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17246.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17246.html</trackback:ping><description><![CDATA[
		<span class="postbody">
				<font size="2">在缺省状态下，Windows CE .NET将COM1端口用于调试目的。如果您在自己的设备上还拥有其它COM端口，那么，Windows CE .NET则会将它们划归应用程序使用（物理意义上的COM2端口将成为逻辑意义上的COM1端口，依此类推）。然而，某些特定项目可能需要将全部COM端口划归应用程序使用。 <br /><br />为此，请遵循下列处理步骤： <br /><br />通过修改OEMInitDebugSerial()（可在OAL的debug.c文件中找到）函数的方式告知操作系统不要将COM端口用于调试目的 <br />if ( ! pBootArgs-&gt;ucBaudDivisor ) { <br />pBootArgs-&gt;ucBaudDevisor = 6; // Default to 19.2 if nothing specified. <br />} <br /><br />pBootArgs-&gt;ucComPort = 0; // ADD THIS LINE <br /><br />switch ( pBootArgs-&gt;ucComPort ) { <br /><br />通过对注册表文件platform.reg加以修改来调整COM端口映射关系。 <br />举例来说，将现有注册表项： <br />[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial] <br />"SysIntr"=dword:13 ; NOTE: This is physical COM2 (subtract 10 for IRQ) <br />"IoBase"=dword:02F8 ; NOTE: This is physical COM2 <br />... <br />[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial2] <br />"SysIntr"=dword:14 ; NOTE: This is physical COM3 (subtract 10 for IRQ) <br />"IoBase"=dword:03E8 ; NOTE: This is physical COM3 <br />... <br /><br />修改为： <br /><br />[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial] <br />"SysIntr"=dword:14 ; NOTE: This is physical COM1 (subtract 10 for IRQ) <br />"IoBase"=dword:03F8 ; NOTE: This is physical COM1 <br />... <br />[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial2] <br />"SysIntr"=dword:13 ; NOTE: This is physical COM2 (subtract 10 for IRQ) <br />"IoBase"=dword:02F8 ; NOTE: This is physical COM2 <br />... <br /><br />说明：您所看到的结果可能有所不同。如果您的系统针对COM端口使用了不同的IoBase/IRQ设置，则请对注册表文件platform.reg进行适当配置。 <br /><br />另一种替代方法就是在您的BIOS中对COM端口的IoBase/IRQ设置进行修改。Windows CE .NET已就将3F8/4（典型的COM1）留作自用进行了硬性规定（针对<span style="COLOR: #ffa34f"><b>CEPC</b></span>）。举例来说，如果您将此类设置指派给COM4，那么，Windows CE .NET调试信息便会路由至物理端口。</font>
				<br />
		</span>
<img src ="http://www.cppblog.com/milkyway/aggbug/17246.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-04 16:01 <a href="http://www.cppblog.com/milkyway/articles/17246.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何在Windows CE.NET下使用大于256MB内存</title><link>http://www.cppblog.com/milkyway/articles/17240.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Thu, 04 Jan 2007 07:11:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17240.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17240.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17240.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17240.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17240.html</trackback:ping><description><![CDATA[
		<font size="2">首先，你必须安装Windows CE .NET Platform Builder 4.0（以下简称PB4）,假设你安装的Windows CE.NET位于D:\WINCE目录下，那么，首先找到文件oeminit.asm,位于D:\Wince\Public\Common\Oak\Csp\I486\Oal,这个文件是提供给OEM厂商做一些特定的配置的。 <br />打开oeminit.asm文件,然后找到“_OEMAddressTable:”，_OEMAddressTable是一个非常关键的内存映射描述表，主要描述物理内存和虚拟内存之间的映射，表的每一条目有3个DWORD,依次是（VA，PA，cbSize），其中VA是虚拟内存的开始地址，一般为80000000h,PA是物理内存的开始地址，一般为0，cbSize就是CE内核支持物理内存的大小，这个DWORD是我们最最关心的,其单位是BYTE。需要注意的是，cbSize,一定要是4M Byte的倍数，因为Windows CE.NET内核中检测内存大小，是以4M Byte为单位来检测的。cbSize系统默认为04000000h,其大小为64M,我们可以将其修改为10000000h,就可以支持256M内存了，当然，你如果想支持更大的内存，也可以增大cbSize,只要是4M的倍数即可。 <br />另外需要修改一个文件是：，打开pc.h,查找到： <br />#define CEPC_EXTRA_RAM_START 0x81C00000 // 28 MB is default top of RAM for auto-detect，这里规定Windows CE.NET内核检测内存的开始地址，默认从28M Byte开始。 <br />#define CEPC_EXTRA_RAM_SIZE 0x02400000 // Potentially add another 36 MB 这里规定了内核中探测内存容量的最大尺寸，0x02400000 ,表示最大探测36M，这样的话，28MB+36MB正好是64MB。 <br />我们需要支持256MB内存，只需要修改CEPC_EXTRA_RAM_SIZE即可，将其改修改： <br />#define CEPC_EXTRA_RAM_SIZE 0x02400000+0x0C000000 // Potentially add another 36 + 128 + 64MB <br />修改为上述两个文件后，需要重新编译系统内核，打开PB4.0,打开Build-&gt;Open Build Release Directory,进入一个DOS操作界面,然后进入D:\Wince\Public\Common\Oak\Csp\I486\目录下，执行“Build –cfs”,重新编译内核,然后重新建立一个新的Platform，编译后即可。</font>
		<br />
<img src ="http://www.cppblog.com/milkyway/aggbug/17240.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-04 15:11 <a href="http://www.cppblog.com/milkyway/articles/17240.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>串行通讯 </title><link>http://www.cppblog.com/milkyway/articles/17239.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Thu, 04 Jan 2007 07:09:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17239.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17239.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17239.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17239.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17239.html</trackback:ping><description><![CDATA[
		<font size="2">
				<br />
				<br />串行通讯实际上将被所有的Windows CE设备所支持，在硬件水平上，通过电缆和红外传送器进行串行通讯是很普通的。调制解调器也支持串行通讯。 <br /><br />1.PC和Windows CE的通信系统 <br /><br />Win32的通信系统 Win32提供给用户一个模块化的32位的保护模式的通信系统。在Win32中，各种通信资源的函数做了很大的改进和标准化，使得它们的操作就如同文件的操作一样。 <br /><br />在串口读写操作中，Win32引入了超时概念。超时直接影响读和写的操作行为。当超过预先设定的超时间隔时，ReadFile、WriteFile操作仍未结束，那么将无条件结束读写操作，而不论是否已读出或写入指定数量的字符。 <br /><br />Windows CE的通信系统 Windows CE将驱动程序分为两种：本地设备驱动程序和流设备驱动程序。本地设备驱动程序，也称为“内置驱动程序”，这些设备驱动程序是一些硬件所必需的，是由原始设备制造商创建的，用以驱动如键盘、触摸屏、音频设备等，往往在设备售出后就不会再更换。另一方面，流接口设备驱动程序（指可安装的启动程序）可以由第三方生产商提供，以支持添加到系统中的设备。Windows CE下的设备驱动程序在与应用程序相同的保护级上工作。当系统启动时，大多数驱动程序是由设备管理进程（DEVICE.EXE）加载的，所有的这些驱动程序将共享同一个进程地址空间。 <br /><br />本地设备驱动程序一般都被紧紧地限制在Windows CE的操作系统中，往往在设备售出后就不会再更换。因为Windows CE没有像ISA或PCI那样的用于附加插卡的总线，附加的硬件通常是通过PCMCIA或“小型快闪槽”安装的，例如串口2是通过PCMCIA卡实现的。观察注册表中的HKEY_LOCAL_MACHINE下的\Drivers\Active键中的内容，可以了解在Windows CE中加载了什么驱动程序。　 <br /><br />通信过程 包括（1）打开通信资源。在进程使用串口之前，首先应使用CreateFile函数打开通信资源，返回一个标识该资源的句柄。在CreateFile函数打开串口通信资源时，系统将根据上次打开资源时的数值初始化和配置资源；（2）读写串口资源。通过ReadFile和WriteFile函数来读写串口。读和写的超时时间由SetCommTimeouts函数设置；（3）关闭通信资源。在使用通信资源结束后，应调用CloseHandle函数来关闭通信句柄，释放资源。 <br /><br />基本的串行通信编程 串行设备被视为用于打开、关闭、读和写串口的常规、可安装的流设备。Win32 API提供了一组通信函数，Windows CE支持了其中的大多数通信函数。 <br /><br />打开和关闭串行端口：在所有的流设备都可以使用CreateFile来打开串行端口设备。一般的调用方法如下： <br /><br />hSer=CreateFile(TEXT(“COM1:”), <br /><br />GENERIC_READ|GENERIC_WRITE, <br /><br />0, <br /><br />NULL, <br /><br />OPEN_EXISTING, <br /><br />0, <br /><br />NULL); <br /><br />　 <br /><br />由于Windows CE不支持设备的重叠I/O，因此不能传递FILE_FLAG_OVERLAPPED标志。当不成功时，返回句柄INVALILD_HANDLE_VALUE，反之返回打开的串行端口句柄。 <br /><br />调用CloseHandle可以关闭一个串行端口: <br /><br />CloseHandle(hSer); <br /><br />读写串行端口：可以使用ReadFile和WriteFile来读写串行端口。从串口读出数据只需如下调用： <br /><br />int rc; <br /><br />DWORD cBytes; <br /><br />BYTE ch; <br /><br />rc=ReadFile(hSer,&amp;ch,1,&amp;cBytes,NULL); <br /><br />调用成功，则变量ch将读入一个字节，cBytes将被设置为读取的字节的数量。 <br /><br />从串口写入数据只需如下调用： <br /><br />int rc; <br /><br />DWORD cBytes; <br /><br />BYTE ch; <br /><br />ch=TEXT(‘A’); <br /><br />rc=WriteFile(hSer , &amp;ch , 1 , &amp;cBytes , NULL ); <br /><br />上面的代码将字母A写入已经打开的端口，成功的话，ReadFile和WriteFile都将返回TRUE。 <br /><br /><br /></font>
		<a href="http://palmheart.net/modules.php?op=modload&amp;name=Sections&amp;file=index&amp;req=viewarticle&amp;artid=154" target="_blank">
				<font color="#006699" size="2">http://palmheart.net/modules.php?op=modload&amp;name=Sections&amp;file=index&amp;req=viewarticle&amp;artid=154</font>
		</a>
		<font size="2">
				<br />
				<br />每一个串行设备都匹配有一个COM口，例如“COM1”。Windows CE为打开串口和管理接收设备上的连接提供了一个API。一旦连接成功，将用相同的函数进行数据传送，这些函数用以读一个文件或者写一个文件。数据只是简单的从一个设备传送到另一个设备。不支持同步和异步I/O。</font>
		<br />
<img src ="http://www.cppblog.com/milkyway/aggbug/17239.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-04 15:09 <a href="http://www.cppblog.com/milkyway/articles/17239.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinCE下直接启动自己应用程序的方法</title><link>http://www.cppblog.com/milkyway/articles/17170.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Wed, 03 Jan 2007 02:20:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17170.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17170.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17170.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17170.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17170.html</trackback:ping><description><![CDATA[来自<a href="http://jkflyfox.spaces.live.com/?_c11_blogpart_blogpart=blogview&amp;_c=blogpart&amp;partqs=cat%3d%25e7%25a8%258b%25e5%25ba%258f%25e6%258a%2580%25e5%25b7%25a7">http://jkflyfox.spaces.live.com/?_c11_blogpart_blogpart=blogview&amp;_c=blogpart&amp;partqs=cat%3d%25e7%25a8%258b%25e5%25ba%258f%25e6%258a%2580%25e5%25b7%25a7</a><br /><br />其实让一个程序在wince里启动和windows里差不多，直接设置其为启动项，这个有几个方法。一个就是制作一个快捷方式，指向我们的应用程序如app.exe，然后将快捷方式放到\windows\startup下面。 <br />    步骤如下：（假设app.exe已经拷贝到windows下面） <br />    1 在pb中创建一个文件，文件类型选txt，然后命名为.lnk后缀，假设名字为test.lnk <br />    2 编辑其内容为: 16#\windows\app.exe。备注：前面的16是# 后面所有字符的总和，包括空格。Wince的帮助文档上说这么定义就行，但是我尝试后，最后down到目标机上面时提示找不到文件，在wince里查看这么创建的test.lnk的属性，发现其指向\windows\app.exe后面还有两个方框，因此不对，我的解决方法是修改test.lnk的内容为16#"\Windows\app.exe" <br />    3 编辑好lnk文件内容后在pb中修改project.bib。在files段后面添加下面一行：(和添加别的文件到image中类似，见我的《WinCE中如何向image中添加文件》，也要在pb的flatform菜单的setting下添加build语句，不再赘述) <br />        test.lnk $(_FLATRELEASEDIR)\test.lnk NK S <br />    在project.dat中增加下面一行： <br />         Directory("\Windows\startup"):-File("test.lnk","\Windows\test.lnk") <br />    这样后系统启动后就会自动启动我们的程序了。 <br />    
<div>    另外一种方法是编辑注册表：在project.reg中添加如下内容 <br />        [HKEY_LOCAL_MACHINE\init] <br />        "Launch80"="app.exe" <br />        "Depend80"=hex:14,00,1e,00 <br />    这个是设定启动顺序，launch后面的数字越大的越是后启动，Depend80后面的指定依赖项，为16进制，上面的语句表明依赖项为launch20定义的device.exe和launch30中定义的gwes.exe， 注意Launch后面的数字范围为0到99 ，此范围之外的将不会有效果。 <br />    这样两种方法的效果都是系统都是系统先启动资源管理器explorer.exe（就是看到的默认桌面），然后启动我们的程序，（如果利用taskman shell然后去掉任务栏那么效果更好）但是这样还不够，我们如何不显示桌面，直接显示我们的程序呢？ <br />    网上有人介绍的方法是去掉standard shell，但是我编译总是报错。我采用的方法是替换注册表中lauch50中的explorer.exe为我的app.exe，即搞定。 <br />    修改注册表的方法：先把带KITL的系统跑起来，在PB的TOOLS-&gt;Remote registry editor里修改,验证有效后,再去修改platfrom.reg, 或者自己写个REG文件，然后在platform.reg里INCLUDE进来 SYSGEN后确认PBWORKSPACE里相关项目的REL目录里reginit.ini文件里包含了自己做的修改后make image然后DOWNLOAD下去就OK了。 <br />    值得补充的是，我们前面介绍的步骤中那个修改平台setting，添加语句的，是因为我每次都是重新sysgen和build，如果只是简单的make image的话（都是pb中的build OS菜单下的命令），那么将直接用release中的内容，因此也可以直接将文件放到release文件夹，然后改project.bib等实现往image中添加文件。 同样，也可以直接修改release中的shell.reg中的launch50值为我们自己的程序（或者类似修改reginit.ini文件，reginit.ini文件存放有所有wince的静态注册表，来达到去掉桌面，直接启动我们程序的效果）。 <br />注意，这么启动的程序，如果点击关闭，就会死机的，因为没有窗口运行了。实际运用中，当然不会让用户关闭我们的程序，除非他一起关闭系统。 <br />如果也需要build的话，可以通过往image中添加文件的方法将我们改好的shell.reg添加到release目录。 </div><img src ="http://www.cppblog.com/milkyway/aggbug/17170.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-03 10:20 <a href="http://www.cppblog.com/milkyway/articles/17170.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinCE中如何向image中添加文件</title><link>http://www.cppblog.com/milkyway/articles/17169.html</link><dc:creator>相思酸中有甜</dc:creator><author>相思酸中有甜</author><pubDate>Wed, 03 Jan 2007 02:19:00 GMT</pubDate><guid>http://www.cppblog.com/milkyway/articles/17169.html</guid><wfw:comment>http://www.cppblog.com/milkyway/comments/17169.html</wfw:comment><comments>http://www.cppblog.com/milkyway/articles/17169.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/milkyway/comments/commentRss/17169.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/milkyway/services/trackbacks/17169.html</trackback:ping><description><![CDATA[来自<a href="http://jkflyfox.spaces.live.com/?_c11_blogpart_blogpart=blogview&amp;_c=blogpart&amp;partqs=cat%3d%25e7%25a8%258b%25e5%25ba%258f%25e6%258a%2580%25e5%25b7%25a7">http://jkflyfox.spaces.live.com/?_c11_blogpart_blogpart=blogview&amp;_c=blogpart&amp;partqs=cat%3d%25e7%25a8%258b%25e5%25ba%258f%25e6%258a%2580%25e5%25b7%25a7</a><br /><br />分3种情况来说明如何添加 
<p>第一种，就是image已经在CEPC或者终端上跑起来了，那么这个时候要想添加文件可以通过PB或者EVC提供的remote file viewer，这个比较简单，看着界面操作就行了。 
</p><p>第二种，就是对定制的image已经build过了，现在想往里面添加文件的话，可以在你对应平台的release文件夹里面直接添加文件，然后修改平台配置文件project.bib，然后再make image，也可以将文件添加到image中去，将image启动后，会出现在windows文件夹下面 
</p><p>比如你想将test.txt添加到image中，则首先需要将此文件拷贝到平台release目录下面 
</p><p>（平台release目录也就是环境变量_FLATRELEASEDIR的值，_FLATRELEASEDIR的 = %_WINCEROOT%\PBWorkspaces\%_TGTPROJ%\RelDir\%_TGTPLAT%\%_TGTCPUFAMILY%_Release，也就是realease目录，（_TGTPLAT为平台名，_TGTCPUFAMILY为CPU名）。我新建的平台为shellTest，其值为E:\WINCE500\PBWorkspaces\ShellTest\RelDir\MyCEPC_x86_Release ） 
</p><p>然后按照以下修改project.bib： 
</p><p>在其中添加类似这样一行 
</p><p>test.txt$(_FLATRELEASEDIR)\test.txt NK S 
</p><p>这句话的意思是说将平台release文件夹下面的test.txt文件添加到image中，文件属性为系统文件，关于bib文件的格式，请查阅WinCE的帮助</p><p>修改好project.bib后，保存，然后在pb的bulid菜单下选择make image，成功后下载到终端或CEPC，就可以看到添加的文件了。</p><p>第三种，就是平台定制都没做好，或者做好了需要修改，那么如果你直接按照第二种来做，然后选择build菜单的sysgen and build的话，你会发现根本不会讲test.txt拷贝到image中，这个也是我的惨痛教训，花了好些时间才知道原因。</p><p>为什么呢？从build image时的output窗口，我每次都可以看到clean up项目release目录的输出，看来我直接把文件复制到release目录是不行的，因为在sysgen and build的过程中此文件夹会被清空，自然我的test.txt也被清掉了。那该怎么办呢？ <br />这里缺少一个步骤，那就是要在平台设置中，作一些配置，从而让Release目录在被清空以后能将目标文件从本地硬盘动态复制到release目录</p><p>修改配置以便拷贝文件到Release目录的主要步骤如下： <br />1 pb中从platform菜单选setting <br />2 在弹出对话框中Configuration一项确保正确，一般默认就是正确的。 <br />3 Custom Build Actions选项卡中的Build step下拉框，选择Pre-Make Image (有四个选项，分别为Pre-Sysgen，Post-Sysgen，Pre-Make Image，Post-Make Image，意思如其名) ，然后New，在弹出的Custom Build Action对话框中输入类似以下的语句： <br />copy &lt;Path&gt;\&lt;File name&gt; %_FLATRELEASEDIR%\&lt;File name&gt; </p><p>比如test.txt放在我电脑上的e盘根目录下，那么语句是这样的：</p><p>copy E:\test.txt %_FLATRELEASEDIR%\test.txt <br /></p><p>加上这个步骤后，再按第二种方法就可以达到目的了。</p><img src ="http://www.cppblog.com/milkyway/aggbug/17169.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/milkyway/" target="_blank">相思酸中有甜</a> 2007-01-03 10:19 <a href="http://www.cppblog.com/milkyway/articles/17169.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>