![文件wince杂谈1 WindowsCE下流驱动的动态加载_第1页](http://file4.renrendoc.com/view/afce4c3b3dc67b83f62fa146a57a40db/afce4c3b3dc67b83f62fa146a57a40db1.gif)
![文件wince杂谈1 WindowsCE下流驱动的动态加载_第2页](http://file4.renrendoc.com/view/afce4c3b3dc67b83f62fa146a57a40db/afce4c3b3dc67b83f62fa146a57a40db2.gif)
![文件wince杂谈1 WindowsCE下流驱动的动态加载_第3页](http://file4.renrendoc.com/view/afce4c3b3dc67b83f62fa146a57a40db/afce4c3b3dc67b83f62fa146a57a40db3.gif)
![文件wince杂谈1 WindowsCE下流驱动的动态加载_第4页](http://file4.renrendoc.com/view/afce4c3b3dc67b83f62fa146a57a40db/afce4c3b3dc67b83f62fa146a57a40db4.gif)
![文件wince杂谈1 WindowsCE下流驱动的动态加载_第5页](http://file4.renrendoc.com/view/afce4c3b3dc67b83f62fa146a57a40db/afce4c3b3dc67b83f62fa146a57a40db5.gif)
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、WindowsCE驱动的动态加很多WinCE的开发人员,尤其是刚并且做驱动开发的工程师,都曾碰DLL打包到先前做好的操作系统映像当中,最简单也得MakeImg一下,还要修改BIB文件、表文作上。这种调试驱动的方法实在效率太低了。想到Linux下面的驱动调试,多方便!直接INSMOD一下,应用程序就可以调用,出现问题就RMMOD,根本无须来回倒腾操作系统的映像文件。那么,WinCE下难道就没有这么简便的方法闲话少说,进入正题。查找ECtivtevic(可用tivteDvicEx(。HANDLEActivateDevice(LPCWSTRlpszDevKey,DWORDdwInfo[in]Pointertotheregistrypathstringofthedevice'sdriverkeyunderHKEY_LOCAL_MACHINE.AdriverkeycontainstheDLLname,thedeviceprefix,friendlyname,andotherdeviceinformation.dw[in]Datathatwillbestoredinthedevice'sActivekeyintheInfovalue.Theregistrypathtothedriver'sActivekeyispassedinasthecontextparametertothedevice'sXXX_Initfunction.ThevalueindwInfoisstoredintheregistrybeforeXXX_Initiscalled.ReturnOnsuccess,ActivateDevicereturnsahandletothedevicethatcanbeusedinsubsequentcallstoDeactivateDevice.ThisfunctionissupersededbyActivateDeviceloadsadevicedriver.ActivateDevicereadstheregistrykeyspecifiedinlpszDevKeytogettheDLLname,deviceprefix,index,andothervalues.NextitaddsthedevicetotheactivedevicelistintheregistrybranchHKEY_LOCAL_MACHINE\Drivers\Active,andstorestherelevantvaluesthere.IfnodeviceindexwasspecifiedinthekeynamedinlpszDevKey,thenitassignsaindex.ThenitloadsthedevicedriverDLLintheprocessspaceoftheDeviceManager.ThenitbroadcastsaWM_DEVICECHANGEmessageforthenewdeviceandtriggersNOTIFICATION_EVENT_DEVICE_CHANGEeventintheapplicationnotificationsystemtonotifyapplicationsofthepresenceofthenewdevice.,而这个参数是表中的一个路径字符串。所以,要想很方便的动态加载任意一个驱动,我们还要了解一下有关表的内容。但其中最的就是一条驱动的相关表信息放到HKEY_LOCAL_MACHINE下主要内容包括Prefix、DLL、Index、Order等信息。这里就不展开说明了。,我们假设已经做好一个简单的流驱动DrvDemo.dll,其对应的表文件为DrvDemo.regDrvDemo.exe。我们把这三个文件和驱动调试助手(DM.exe)WinCE系统上,如下图所示。首先运行DrvDemo.exe,点击打开设备,或者其他按钮,我们发现打开设备失DrvDemo.dll还没有工作呢。.x表,浏览到我们准备好的表文件rvDmo.rg,选中并点击K按钮,rvDmo.rgDrvDemo.exeDLLDLL览到我们准备好的DrvDemo.dll文件在弹出的框中设置正确的PrefixOrder、Index怎么样,这种方法就很类似于Linux下的InsmodRmmod其实,如果在表中已经有了相应的键,你甚至可以直接选中它,然后在效果,但这样就连导入文件都省了。有的可以自己试一下。从此摆脱不断MakeImgDownload的梦魇,节省很多宝贵的开发时间。WinCE4.2中,有关中断的驱动是无法直接用此方法来动态加载的,而5.06.0WinCE4.2中的中断处理机制所致。驱动调试助手可以同时加载几个驱动,这时候建议将所有表信息写到驱动调试助手中的表编辑功能经几番修改已基本完善编辑IE-E的表修改。于用户态的驱动也可以通过该方法加载,但是需要设定相应的表信息。手可以 /we-hjb/WINCEDM.rar到一起学习WindowsEmbedded。最后,希望不要被认为是“性质的文章。2、单机搭建WinCE没有时间调,所以只能回家做,可家里又没实验板。好在WindowsCE是有模拟题,不过都一一解决了,写出来跟大家一下。最早主机上已经安装了EVC4+SP4,VS2005+SP1+tformBuilder6.0+R2等WindowsCE的主要开发工具。既然没有实验板,那就定制一个中文的WinCE6.0的操作系统,再导出SDK,这不就解决了板子的问题吗?思路是对了,可一动手就发现有问题。PB6.0MFC的支持。而目前很多程序都是基于MFC的这要调试MFC的程序可怎么办?想了,系统要支持MFC,其实就是要DLLMFC相关的几个DLL加到NK.bin中不就行了?在ProgramFiles\VisualStudio8\VC\ce\Dll\armv4的MFCDLLMFC,只是想让大伙少用把这几个DLL拷贝到BSP对应的下并修改tform.bib文件。第一个问题顺利解决。MFC加进去了,.NETCompactFramework那更是必须选问题是系统启动后得自己去创建接,还要修改”PC连接”的选项才能用。册表信息导出来,放到tform.reg里去,这样重新编译完的系统就能直接使用ActiveSyncActiveSync,让DMA的连接方式。该导出SDK了。在导SDKMFC也没有相关选项的支持。这不又SDKMFCVS2005MFC编写设备的程序。得想办法把MFC加进来。还是在ProgramFiles\VisualStudio8\VC\ce\的下面有一个atlmfc的。这里面包括了MFC对应的头文件和库。在定制SDKAdditionalFolders中添加相关的项,最后编译安装,MFC时只选择armv4I的,要全部选可有好几百至此,我们就在单机上搭建好了完整的WindowsCE的开发环境,不光是装了很多开发软件,连硬件都有了!很多想学WindowsCE开发的学生就不用费心费力费钱去选板子,暂时用模拟器做的学习也是个不错的选择。 /we-hjb/WINCE6.0CHSEMU.rar有WinCE6.0中文模拟器SDK的使用方法和实验的效果。Devicetextconsolwindow来模拟。我们VS2005EVC来编写程序,通过文件共享放到WinCE6.0的模拟器中去执行,添加打印信息的调试方法在这里也可以使用。另外,文中定制的SDK可以在这里到VS2005+PB6.0VS2008的。机器也支持同步。在使用模拟器的过程中发现一个小技巧。如果从DeviceEmulatorManager中连接到该模拟器,则每次都必须重新设置网络和共享等内容。这点击“属性”后弹出的框如下图所示,点击“仿真器选项,进行配置,就不一一介绍了,根据自己的需求进行设定。配置完成以后,下次再从Device在调试程序时,难免要用到工具。前段时间在调试2410开发板的WinCE6.0接。但测试发现根本不行,截图如下,提示说”UnabletoloaddevicesideEVC4.0RemoteToolsWinCE5.0的设备时也出现这样的情况。难道是RemoteTools6.0的,这太C:\ProgramFiles的下看到过一个CERemoteTools的,这里面放的应该也是工具吧。进去看看,果然有几个。但比C:\ProgramFiles\CommonFiles\Shared\WindowsCETools\tman\bin\wce500下的少多了文件名也不大一样不是以CE打头而是用CC打头打开看看再说,连接,等待,DownloadingBitmap,果然可以!再比较一下这两个版本到底哪里ActiveSync或者网络进最后补充说明一下,你可以从开始\程序\ VisualStudio2005\VisualStudioRemoteTools中找到这个能用的6.0版的RemoteTools。所以是否说明(3)(3)通过前两篇的介绍,我们已经搭建了WinCE开发环境,并了解了如何使用工具进行相关调试。这一篇,我们进一步完善单机搭建的WinCE开发环境——在WinCE6.0的模拟器中使用串口。WinCE设备中串口的使用频率是相当高的,一般有一个调试串口,而很多实际应用中与GPS、GSM(GPRS)模块的通信都是通过串口来进行的。在第一篇中,绍过为调试串口创建一个TextConsolWindowVS2005发布的6.0的RemoteTools是真的可用的6.0版本而随tform6.0不管怎样,我们在单机上搭建的WinCE来调试GPS、GSM模块的程序了了。今天便在VS2008中安装了一下WinCE6.0中文模拟器的SDK,又遇到了一些小问题,贴出来跟大家一下,也算是对单机搭建WinCEOKRollBack了。重新试了两遍,都是如此。难道VS2008不支持WinCE6.0 歉”。再Go一下,果然有6项相关的网页。其中有一篇提到,在VS2008中安装WinCE6.0的SDK时,不能完全安装,必须选择定制,并把.NETCompactFramework取消掉。 ation也取消掉,其他两项NativeDevelopmentSupport和Common保留,竟然可以了!新建了一个MFC的工程,编译 完全没有问题,这证明VS2008确实是支持WinCE6.0应用程序的开发的!ToolsMsmCA(Error):IHxFiltersfilterregistrationfailure:Err= ,Context=pFilters->SetNameSapce(Namespace)ToolsMsmCA(Error):IHxRegisterSessiontransactionfailure:Err=0x 3、WinCE下的控WindowsCERemoteZoominRemoteZoominRefreshCE和PCPCCEWinCERDPCEWindowsXP,CE本身并不能做Server,只能做 初步分析了一下,为了实现这个功能,CE端需要做两个事情,第一,实时PCPC端发过来令(主要是鼠标和键盘的消息命令)并模拟鼠标和键盘。而PCCE端发来的屏幕内容的BUF并将其显示,第二是捕获鼠标和键盘的消息并给CE发CEDIBPCStretchDIBits()显示接收ColorData。这里唯一需要注意的就是在截屏时并没有截取鼠标指针的信息,所以在截屏之后得手动画上鼠标的指针。否则,PCCE 在CE端是很容易的,用mouse_event()和keybd_event()这两个函数就足够了。而PC端捕获鼠标和键盘的消息则有两种方法,方法一是在PreTranslateMessage中来做,方法二则是在各个消息处理函数中来做。这里需要注意一下,键盘的消息必须在PreTranslateMessage中Windows下的控制类软件的思路是一样的。接下来就考虑一些细节的问题。譬如通信的方式,EVC中的工具是可以通过ActiveSync来通信的,也可以用Ethernet。所以为了方便最好也能用这两种方式。另外,如果在使用ActiveSync时,是否可以利用RAPI,直接CE仔细琢磨了一阵,发现这些需求都可以实现。把各部分都实现完了以后联调,基本达到了预期的效果。但在CE分辨率较大时感觉有点迟钝。所以又想优化的方法。基本上有以下三点,比较、分块、压缩。所谓比较,就是在CE屏之后做一个备份,再次截屏时将其与备份进行比较,如果不一致再发送,否则就不用发给PC做更新。分块是可以和比较一起使用的,如果屏幕只是局部新,那可以只发送更新的块,而其他块则不发送。压缩理论上是可以提高传输效率的。粗略的比较了一下,一张640*480的16Bit的位图是600K,而压缩之后的JPG 才几十K,差别还是相当可观的。这三种方法都能节省网络带CPU后来有一次,有人问起CE下的触摸屏校准在哪里,CE同步的连接怎么建 专家的麻烦。于是,找了一个很方便用的从BMP保存为AVI的类,添加了录屏功能。当然由于没有压缩,所以录制的AVI文件WinCERMCUSBActiveSyncLCDEbootpowertoys,RMCEthernetUSBSOTI5.0RMC/we- 4、FoxitReaderV1.1forPXA270WinCE6.0WinCE6.0的强大,定制了一个增强型的操作系统但WinCE6.0中已经不支持PDF等阅读器了于是从上找PPCFoxitReaderWinCE6.0OS不支持,PPCFoxitReaderWindowsCE应用程序”是不一样的。既然这样,那说明程序还是运行起来了,只是检测OS时出错,所以才会出现如下图所示的提示。想,如果让程序跳过检测OS,是不是就可以正常运行呢。满怀着希望,用IDA反BEQB。UltraEdit0AEABEQB,再拷贝到设备上面去,果然可以运可以看到,上面的AboutFoxitReader框竟然没有关闭的按钮。这可怎么退出呢?重启ECS退出的,只是觉得很不爽,得加个关闭按钮。开始想IDAexescope就可以很轻松的达到这个效果,用exescope打开编辑了一下,再运行,关闭按钮的出现了。嘿嘿,到这里就算成功了PPC版的FoxitReader,使其能在CE6.0上面运行。虽然这对点击这 工欲善其事,必先利其器。做工欲善其事,必先利其器。做WinCE驱动的开发已有一段时间了,WinCE驱动调试助手也跟着更新了很多功能。现在只要做驱动,DM是必须用的。最近在调试WinCE6.0下的驱动,所以又针对WinCE6.0增加了新的功能,也修正了原来的一些先介绍一下WinCE驱动调试助手V2.5 PC上的方法一样。这也是一个方便使用的功能。V2.5中新建键、值以及删除、修改键值的功能都做了提示,尽量减少因误操作而ID,方便开发者找出原因。动时,相当于按下了Backspace,向右滑动时,相当于按下空格。有点意思。V2.5的导入导出功能在WinCE5.0和6.0上测试,未发现问题,在4.2中测试4.2下有保留的使用吧。附带的中演示了6.0下测试导出导 下的是,在导 总之, V2.5中新增了对系统启动时加载的设备驱动的管理。这个功能类似与PC6.05.0中使用,4.2是不支持的。附带的中演示了停用StorageCard设备的方法。该功能与先前的功能一起在驱动列表中操V2.5ERRORID,一般ID5.06.04.2上测试时,同时加载两个以上设备的驱动,经常会出现ErrorID0的情况,再试几次又能ErrorID还是有一定的参考意义,而不象以前,只知道出错 综上所述,除了增加了设备管理器的功能外,V2.5的主要更新都是细节的完善, CE的开发者使用。WinCE驱动调试助手V2.5的 演 地 /we-博客园的件 空间用完了,所这中间版本已被清。请到这里 e-hjbWMrr6、驱动调试助手的今天发 的DM程序,在PPC上面不能正常运行,截图如下每个主键下只打开了一个子键。而TREAdditionally,thesystemprotectsasetofregistrykeyssothattheycannotbemodifiedbynormalapplications.Allapplicationscanreadallregistrykeysandvalues,butonlyprivilegedapplicationscanmodifyvaluesorsubkeysbelowprotectedkeys.Thesystemprotectsabasesetofkeys.ThissetofkeysisextensiblebytheoriginalequipmentGetCursorPos()PPC上似乎是不支持这个API的,用()替换掉该API不光是PPC上会有这个问题,在某些特定的WinCE系统上(没有包涵GWES_CURSORORGWES_MCURSOR组件)也会存在这个问题,只是自己平时定BUG。这里又涉及到一个问题,如何确定一个程序能否在特定的WinCE系统上正常运行。因为WinCE系统是一个非标的操作系统,它对API的支持在定制系统时就决定了。关于这个问题,有时间Report,方便开发者判断需要在定刚刚去书店转了一下,发现已经有介绍WinCE6.0还有一本是《EVC高级编程及应用开发》的升级版,基于VS2005WinCE系统,通过修改表,了开机运行explorer.exe,直接运行自己的程序。所谓可没有任何问题,只是S没运行而已。外接键盘根本无法输入以致进入不了系统的问题他想到用RMC来输入,可没想系统也不支持。这主要是因为他的车载GPS系统根本没有键盘的驱动。这个问无法输入。当时的解决办法就是重新烧系统,很冤。其实,这个问题的关键就在软键盘OK了?为了解决客户和linsk碰到的问题,修改了一下RMC的程序,增加了控制设备端软键盘显示和启动设备S 的功能。只要能同步上,PC就能控制CE了。后,点击S 按钮,设备进入WinCE系统,接下来可以怎么调试都行。当然,如果是忘记了,那还真是件麻烦事。简单试了一下NTLM的几个函数,虽然文中提到的RMC的地址 8、S3C2410处理器支持将启动代码 在NANDFlash中。为了实现这 2410配备了一个名为“Step stone”的内部SRAM。在启动时,NANDFlash中第一个4K字节的内容将被加载到Step stone中并执行。这个工作由MCU主动完成,而我们只需将NANDFlash配置为AutoBoot模式即可。 在NANDFlash中的内容至SDRAM中。在使用ECC校验时,NANDFlash中数据的有效性将被确认。在完成拷贝的工作后,启动代码将跳转到已加载到SDRAM中的主程序中,这时启动代码的使命完成,MCU由WinCE操作系统从文件的组成来看一般分为两部分:BOOTLOADER和NK.bin。在WinCE中,BOOTLOADER一般为EBOOT。它的主要功能是初始化硬件设备,主要包括CPU内部的相关控制器、内存、网络、串口甚至USB口和LCD。在初始化完成后,它将通过网络或USB从外部 NK.bin,或从本地Flash中加载NK.bin并执行,从而启动WinCE操作系统。可以看到Eboot虽然是启动代码,但它得完成相当多的工作,最终的映像文件也将超过4K。所以,我们不能直接将Eboot存放在NANDFlash的第一个4K字节中。我们需要一个更小的启动代码,这就是通常所说的NBOOT(NANDBOOT)。通过上面的介绍,我们知道了NBOOTEBOOT加载到内存并运行。在EBOOT开始执行后,NBOOT在NBOOT的代码实现中 CPU内部相关控制器,如设置GPIO,关闭WatchDog,关闭中 初始化NANDFlash,因为在MCU启动时默认是AutoBootNANDFlashEBOOTNANDFlashMode 址是跟EBOOT有关,介绍EBOOT时再详细说明。完 一般来说,前面两个功能在startup.s中实现,WinCE6.0 tartup.s的相关代码。后面四个功能可在main.c中实现。总之,在实现必备功能的情况下,尽可能减少代码量,以将最后的NBOOT4K以内。NBOOT的编译环境一般有两种,ADS1.2(或RVDS)、 tformBuilder。用ADS1.2编译NBOOT是比较方便的,需要注意的就是ROBase和RWBase的设置,ROBase必须设置为0x0。否则,系统启动时NBOOT将不被运行。在Post-Linker中选择ARMfromELF,在Linker——ARMfromELF——OutputFormat inBinary,这样,编译完成后最终生成NBoot.bin。该文件就可以 NANDFlash的第0个BLOCK中。 tformBuilder6.0是作为VS2005的插件来用的,所以,我们现在也可以用VS2005来编译NBOOT,这样就免得再安装ADS或RVDS等软件。用VS2005来编译NBOOT也有两种方法,第一种跟编译EBOOT映像文件类似,第二种是自己写makefile文件,然后用命令行的方式调用ARM编译 法在编译OS时,会自动编译生成映像,而第二种方法可由ADS下的程序直接移植过来,不过,要写好makefile文件是相当困下面就介绍如何用VS2005来编译NBOOT首先修改BSP 2410"SRC"Bootloader"的dir文件,添加 在bootloader下创建NBOOT ,并创建makefile、makefile.inc、sources、nboot.bib文件,也可从EBOOT的 !IF"$(NOLINK)"==romimage$(ROMIMAGE_FLAGS)!IFcopy$(_ EBUG)"nboot.*$(_FLATRELEASEDIR) ;
Memory; LDEFINES=-subsystem:native/DEBUG/DEBUGTYPE:CV startup.s\ 再将对应的代码文件拷贝到NBOOTmain.c中需要添加一个全局变量的定义,这在ADS1.2的工程代码中是没有的,也//GlobalsROMHDR*volatileconstpTOC=(ROMHDR*)-否则,最终生成的Nboot.bin就只有一个文件头。做完相关代码的修改之后,就可以编译NBOOT了。可以看到,最终编译生成了Nboot.bin、NBOOT.nb0、NBOOT.nb1、NBOOT.nb2。上面我们提到用ADS1.2编译最后可用的NBOOT映像为NBOOT.binNBOOT.bin,但这里我们却得用NBOOT.nb1。对比一下ADS下生成的和Nboot.nb1最后,再看一下反汇编nboot.exe的样子,将其跟startup.s至此,我们就完成了用至此,我们就完成了用VS2005编译得到NBOOT当然,S3C2410&&WinCE6.0也可以用Nor,直接用为一个新的硬件设备定制WinCE6.0操作系统,一般需要完成以下几个主要步骤 针对特定的硬件设备创建板级支持包(BoardSupportPackage缩写为BSP),BSP必须包括BOOTLOADER、OEM适配层(OEMAdaptationLayer缩写为OAL)和一利用创建的BSP,定制一个系统设计(OSDesign)通过VS2005创建一 tformBuilder的工程。该针对板上的设备创建相关驱动,并添加到BSP中 CatalogItems的方式,修改OS 编译OSDesign,编译得到的运行时映像文件到目SDK(SoftwareDevelopmentKit),应用程序的开发人SDK编写该设备的应用程序。可以看出,在整个WinCE操作系统的移植过程中,BSP关键的一步。而创建BSP 创建BOOTLOADER。BOOTLOADER在开发的过程中 创建OAL。OAL最终被到内核映像文件,它主要完 修改运行时映像的配置文件。配置文件主要包括BIB、REG等文件。BOOTLOADEROS的启动程序处。它的这一作用跟前一篇介绍的NBOOT的作用完全一致。BOOTLOADER获取运行时映像(NK)一般有两种方法。它可以通过有线连接的方式象网络(Ethernet)、USB或串口从外部NK。它也可以从本地的器(Flash、HardDisk)中加载NK。通常,BOOTLOADER通过Ethernet操作系统映像故将其称为EBOOT。在开发的过程中使用EBOOT,可以提高开发效率。通过使用EBOOT,你可以很快速的NK到目标设备中。而利用Flash编程工具或者是通过JTAG则很慢。在一些产品最终发布时,EBOOT是以去掉的,但也有一些则必须包括BOOTLOADERX86 初始化MCU。包括初始化MCU的相关寄存器、中断、看门狗、系统时钟、内存和MMU。前面几项跟NBOOT基本一致,但这里增加了对MMU的初始化。BootloaderMain()。这个函数的定义在WinCE6.0中对 必须由EBOOT的代码来实现。最终跳转到OAL.exe的StartUp处,进而启动。这里针对S3C2410EBOOT做几点说明。前一篇介绍NBOOT加载EBOOT的方法时提到,NBOOTEBOOT放在内存中指定的位置,这个位置是EBOOT的来决定的。具体的,在EBOOT中的体现是boot.bibNBOOT加载EBOOT到内存的地址必须与此地址对应。由于在NBOOT中没有使用MMU,所以NBOOT使用的实际地址应该为0x 常启动。第二点,如果没有采用NBOOT加载EBOOT的方法,而是将EBOOT直在NORFlashEBOOT的代码中实现自加载的过程,即将NORFlashEBOOTRAM中,并执行,实现代码如下:; Copybootloaderto r9,pc,#0xFF000000 ;seeifweareinflashorinram ;goaheadifwearealreadyinram;Thisistheloopthatperform r0,=0x21000 ;offsetintotheRAM r0,r0,#PHYBASE ;addphysicalbase r1, ;(r1)copy r2,=0x0 ;(r2)flashstartedatphysicaladdress0 r3, ;counter r4,[r2],#4 r4,[r1], r3,r3, ;RestartfromtheRAMpositionaftercopying.movpc,r0;Shouldn'tgethere.本文粗略的介绍了WinCE6.0EBOOT的内容,但没有涉及具体的代码实现, 10、S3C2410下WinCE6.0通过前两篇文章的介绍,我们已经知道通过前两篇文章的介绍,我们已经知道NBOOTEBOOT,继而EBOOTWinCENK)。那么,WinCE6.0呢?本文基于S3C2410WinCE6.0MCUS3C2410PXA270ARMMCU,所以他们的启动过程是类似的,可以说唯一的不同就在OALWinCEOALOAL(OEMAdaptationLayer)即OEM适配层,它的主要作用是在移植WinCE到新的硬 减少操作系统的修改,通俗的说就是为WinCE操作系统抹MCUMCUOALOALOAL内核和设备硬件之间的一个代码层,是一个抽象的概念。物理上,OAL和其他一些库一 成可执行文件,在WinCE6.0中对应的文件是OAL.exe,这是的客观存在。WinCE6.0OALOALOAL.exe,而内核则变成了Kernel.dll单独升级OAL。但整体的OALOEMOALKernelNKGLOBAL下图所示为WinCE6.0OAL在移植WinCE到新的硬 ,创建OAL是最复杂的任务之一。一般来说,最简单的方法是拷贝一个跟新的硬件平台类似的且 OAL,然后从EBOOTOAL.exeOEMLaunch()开始的,函数OEMLaunch()中调用Launch(dwPhysLaunchAddr),LEAF_ENTRY r2,= r3,=(VIR_RAM_START- r2,r2,r1,;Disablep15,0,r1,c1,c0,pc,;Jumpto;MMU&cachesnow处对应的是NK.exe,而这里的NK.exe至此,我们已经知道EBOOT是如何跳转到OAL.exe中的了。接下来继续看OAL.exe的执行过程。OALLEAF_ENTRY;ComputetheOEMAddressTable'sphysicaladdress r2, p15,0,r2,c8,c7, ;Flushthe pc, ;Jumptoprogramweare函数Launch()的参数为物理地址,因为在跳转之前已将MMU关闭。该地址可通过VIEWBIN来查看,如下图所示:如何确定这个地址对应的是NK.bin中的哪一个文件呢,先前说是 何在。在PB6.0中增加了浏览NK.bin的功能,我们可以利用此功能查看NK.bin的详细情况,如下图所示:;loaditintor0.KernelStartexpectsr0to;thephysicaladdressofthistable.TheMMU;turnedonuntilwellinto r0,pc,#g_oalAddressTable-(.+ OAL的启动代码和EBOOT的启动代码经常复用,但为了代码的简洁,最好还是分开实现,而且在EBOOTOAL的启动代码就可可以看出,OALKernelStart(),而这个函数是在文件C:\WINCE600\PRIVATE\WINCEOS\COREOS\NK\LDR\ARM\armstart.sLEAF_ENTRY r11, ;(r11)=&OEMAddressTable(save;figureoutthevirtualaddressof r1, ;(r1)=&OEMAddressTable(2ndargumentto r6, ;(r6)=VAof;convertbaseofPTstoPhysical r4, ;(r4)=virtualaddressof r0, ;(r0)=virtualaddressof r1, ;(r1)=&OEMAddressTable(2ndargumentto r10, ;(r10)=ptrtoFirstPT Zerooutpagetables&kerneldatar0,;(r0-r3)=0'stor1,r2,r3,r4,;(r4)=firstaddresstor5,r10,#KDEnd-;(r5)=lastaddress+ r4!,{r0-r4!,{r0-r4,;;readthearchitecture r5,r0LSR ;r5>>= r5,r5, ;r5&= f==architecture Setup2ndlevelpagetabletomapthehighmemoryareawhichcontains;firstlevelpagetable,2ndlevelpagetables,kerneldatapage, (r5)=architecture r4,r10,#HighPT- ;(r4)=ptrtohighpage r5, ;v6or; r0,r10,#PTL2_KRW+PTL2_SMALL_PAGE+;(r0)=PTEfor4K,kr/wu-/,uncachedunbuffered,;PRE r0,r10,#PTL2_KRW+(PTL2_KRW<<2)+(PTL2_KRW<<4)+(PTL2_KRW<<;NeedtoreplicateAPbitsintoall4 r0,r0,#PTL2_SMALL_PAGE+;(r0)=PTEfor4K,kr/wu-/,uncachedunbuffered,r0,[r4,;storetheentryinto4slotstomap16Kofprimarypager0,r0,;steponthephysicalr0,[r4,r0,r0,;steponthephysicalr0,[r4,r0,r0,;steponthephysical r0,[r4, r8,r10,#ExceptionVectors-PTs;(r8)=ptrtovector r0,r8, ;constructthePTE;;Theexceptionstacksandthevectorsaremappedasasinglekr/w;;Anyalternativewillusemorephysical;;Multiple sdon'tprovideanyrealprotection:ifthevectorswereinar/o;;theycouldstillbecorruptedviathekr/wsettingrequiredforthe r5, ;v6or; r0,r0,;PRE r0,r0,#PTL2_KRW+(PTL2_KRW<<2)+(PTL2_KRW<<4)+(PTL2_KRW<<;NeedtoreplicateAPbitsintoall4fieldsforpre-V6 r0,[r4, ;storeentryforexceptionstacksand;other3entriesnow r9,r10,#K ;(r9)=ptrtokdata r0,r9, ;(r0)=PTEfor4K;ARMV6_MMU(conditioncodesstill r0,r0, ;Nosubpageaccesscontrol,sowemustsetthisallto;PRE r0,r0,#(PTL2_KRW<<0)+(PTL2_KRW<<2)+(PTL2_KRW_URO<<;(r0)=setpermskr/wkr/wkr/w+ur/o r0,[r4, ;storeentryforkerneldata r0,r4, ;(r0)=1stlevelPTEforhighmemory r1,r10, r0,[r1,#- ;storePTEinlastslotof1stlevel Fillinfirstlevelpagetableentriestocreate"staticallymapped";fromthecontentsoftheOEMAddressTable; (r5)=architecture (r9)=ptrtoKData (r10)=ptrto1stlevelpage (r11)=ptrtoOEMAddressTable r10,r10, ;(r10)=ptrto1stPTEfor"unmapped r0, r0,r0, ;(r0)=PTEfor0:1MB(C=B=0,kernel r1, ;(r1)=ptrtoOEMAddressTablearrayr2,[r1],;(r2)=virtualaddresstomapBankr3,[r1],;(r3)=physicaladdresstomapr4,[r1],;(r4)=numMBto r4, ;Endof r12, r2,r2, ;VAneeds512MB,1MB r12, r3,r3, ;PAneeds4GB,1MBr2,r10,r2,LSRr0,r0,;(r0)=PTEfornextphysicalr0,[r2],r0,r0,;(r0)=PTEfornextphysical r4,r4, ;DecrementnumberofMB r4, ;Mapnext r0,r0, ;ClearSectionBaseAddress r0,r0, ;ClearSectionBaseAddress ;Getnext r10,r10, ;(r10)=restoreaddressof1stlevelpage;Theminimalpage saresetup.InitializetheMMUandturnit;therearesomeCPUswithpipelineissuesthatrequiresidentity beforeturningon;We'llcreateanidentity fortheaddresswe'lljumptowhenturningonMMUonand;the afterweturnonMMUandrunningonVirtual r12, ;(r12)=maskforsection r1,pc, ;physicaladdressofwherewe;NOTE:weassumethattheKernelStartfunctionneverspamacrossM r0,r1, r0,r0, ;(r0)=PTEfor1Mforcurrentphysicaladdress,C=B=0,kernel r7,r10,r1,LSR ;(r7)=1stlevelPTentryfortheidentity r8, ;(r8)=savedcontentofthe1st-level r0, ;createtheidentity r1, r1, ;Setupaccess 0andclear r10, ;setuptranslationbase(physicalof1stlevel r0, p15,0,r0,c8,c7, ;FlushtheI&D r1, r1,r1, ;changedtoread-mod-writeforARM920Enable:MMU,Align,DCache, r5, ;r5still; r1,r1, ;vectoradjust, r1,r1, ;V6-formatpage r1,r1, ;V6-setUbit,letAbitcontrolunalignment;PRE r1,r1, ;vectoradjust,ICache,ROMr0,r0,;makesurenostallon"movpc,r0"r1,;enabletheMMU&pc,;&jumptonewvirtual;MMU&cachesnow; (r10)=physcialaddressof1stlevelpage (r7)=entryin1stlevelPTforidentity (r8)=saved1stlevelPTsaveatVStart r2, ;(r2)=VAof1stlevel r7,r7, ;(r7)=offsetinto1st-level r8,[r2, ;restorethetemporaryidentity p15,0,r0,c8,c7, ;FlushtheI&D;;setupstackforeachmodes:currentmode=supervisor; sp, r4,sp,#KData- ;(r4)=ptrto;setupABORT r1, cpsr_c, ;switchtoAbortModew/IRQs sp,r4,#AbortStack-;setupIRQ r2, cpsr_c, ;switchtoIRQModew/IRQs sp,r4,#IntStack-;setupFIQ r3, cpsr_c, ;switchtoFIQModew/IRQs sp,r4,#FIQStack-;setupUNDEF r3, cpsr_c, ;switchtoUndefinedModew/IRQs sp, ;(sp_undef)=;switchbacktoSupervisor r0, cpsr_c, ;switchtoSupervisorModew/IRQs sp,;continueinitializationin r0,sp,#KData- ;(r0)=ptrto r6,[r0, ;storeVAofOEMAddressTablein ;callCfunctiontoperformtherestof;uponreturn,(r0)=entrypointof r12, r0, pc, ;jumptoentryof从上面的代码可以看出,KernelStart()通过OEMAddressTable初始化了MMU,然后通过调用函数ARMInit()获得kernel.dll 点,最后跳转 点处为了找到Kernel.dll 点,用IDA反汇编kernel.dll文件,可以看到,Kernel.dll 点为NKStartupNKStartup()的实现在文件C:\WINCE600\PRIVATE\WINCEOS\COREOS\NK\KERNEL\ARMmdarm.c//NKStartup-entrypointof//NKLoadersetuponlytheminimal s,whichincludesARMHigharea,andthecachedstatic //with*EVERYTHINGUNCACHED*.Interruptvectorsarenotsetupeither.So,theinitsequence// datapassedfromnk//(2)Findentrypointofoal,exchangeglobals,findoutthecache//(3)fillintherestofstaticmappedarea -0xbfffffff),PSLfaultingaddress,interrupt modstacks,etc.Then,changethe'cached'static areatousecache,andflushI&D//(4)continuenormalloadingofkernel(findKITLdll,callOEMInitDebugSerial,voidNKStartup(structKDataStruct*{PFN_OEMInitGlobalsPFN_DllMainDWORDdwCpuId=GetCpuId// argumentsfromthenk = =(constROMHDR*)pKData-g_pOEMAddressTable=(PADDRMAP)pKData-/*getarchitectureidandupdatepageprotectionattributespKData->dwArchitectureId=(dwCpuId>>16)&if(pKData->dwArchitectureId>=ARMArchitectureV6)//v6orpKData->dwProtMask= = = = =}else//pre-pKData->dwProtMask= = = = =}//initializenk =(ROMHDR*) = =KInfoTable[KINX_PTOC]=KInfoTable[KINX_PAGESIZE]=g_ppdirNK=(PPAGEDIRECTORY)&ArmHigh-pKData->pNk=//(2)findentryofpfnInitGlob=(PFN_OEMInitGlobals)pKData-//nocheckinghere,ifOALentrypointdoesn'texist,wecan'tg_pOemGlobal=pfnInitGlobpKData->pOem=//setup //(3)setupvectors,UC s,modestacks,ARMSetup//cacheisenabledfromhere//(4)commonstartup//trytoloadKITLifif((pfnKitlEntry=(PFN_DllMain)g_pOemGlobal->pfnKITLGlobalInit)(pfnKitlEntry=(PFN_DllMain)FindROMDllEntry(pTOC,KITLDLL)))(*pfnKitlEntry)(NULL,DLL_PROCESS_ATTACH,(DWORD)}#ifdefCurMSec=dwPrevReschedTime=(DWORD)- //~3minutesbeforeOEMInitDebugSerial//debugchkonlyworksafterwehavesomethingtoprintDEBUGCHK(pKData==(structKDataStruct*)DEBUGCHK(pKData==&ArmHigh-OEMWriteDebugString/*CopyinterlockedapicodeintothekpageDEBUGCHK(sizeof(structKDataStruct)<=/*setupprocessorversioninformation =(dwCpuId>>4)& =CEProcessorRevision=(WORD)dwCpuId& = G(1,(L"ProcessorType=%4.4xRevision=%d\r\n",CEProcessorType, G(1,(L"OEMAddressTable=%8.8lx\r\n", //initialize//flushI&DOEMCacheRangeFlush(NULL,0,可以看到,这里调用了可以看到,这里调用了KernelInit()和FirstSchedule()这两个函数。先说FirstSchedule(),它开始了WinCE6.0KernelStart()在同一文件中,而实现代码跟WinCE5.0中,代码如下://KernelInit-Kernelinitializationbeforeschedulingthe1stvoidKernelInit{#ifdefg_pNKGlobal->pfnWriteDebugString(TEXT("WindowsCEKernelInit\r\n"));APICallInit//setupAPIHeapInit//setupkernelInitMemoryPool//setupphysicalPROCInit//initializeVMInit//setupVMforTHRDInit//initializeMapfileInitDEBUGMSGDEBUGMSG(1,(TEXT("NKStartupdone,startingupKernelStart//neverreturnedDEBUGCHK(0);}()的实现在文件#ifdefg_pNKGlobal->pfnWriteDebugString(TEXT("Schedulingthefirst}这段代码跟WinCE5.0WinCE6.0启动最紧密的函数是THRDInit(),这之前都是做相应的初始化。THRDInit()的实现在文件C:\WINCE600\PRIVATE\WINCEOS\COREOS\NK\KERNEL\thread.c中,代码如下://THRDInit-initializethreadhandling(calledatsystemvoidTHRDInit{ DEBUGLOG //don'tallowthreadcreateonememorydropbelow1%if(g_cMinPageThrdCreate< Count/100)g_cMinPageThrdCreate= Count/}//mapW32threadpriorityifOEMchooseif(g_pOemGlobal->pfnMapW32Priority)BYTEintmemcpy(prioMap,W32PrioMap,sizeof//validatethethepriorityismono-for(i=0;i<MAX_WIN32_PRIORITY_LEVELS-1;i++)if(prioMap[i]>=}DEBUGMSG((MAX_WIN32_PRIORITY_LEVELS-1)!=i,(L"ProcInit:InvalidprioritymapprovidedbyOEM,if((MAX_WIN32_PRIORITY_LEVELS-1)==i)memcpy(W32PrioMap,prioMap,sizeof}}//allocatememoryforthe1stpCurThread=AllocMemDEBUGCHKdwCurThId=(DWORD)HNDLCreateHandle hread, K)&DEBUGCHKInitThreadStruct(pCurThread,(HANDLE) K,if(g_pOemGlobal->cbCoProcRegSize)//checkthedebugregisterrelatedif(g_pOemGlobal->cbCoProcRegSize>MAX_COPROCREGSIZE)g_pOemGlobal->cbCoProcRegSize=g_pOemGlobal->fSaveCoProcReg=}elsePNAMEpTmp ame(g_pOemGlobal-DEBUGCHKg_dwCoProcPool=pTmp-Name}}elseg_pOemGlobal->fSaveCoProcReg=}DEBUGMSG(ZONE_SCHEDULE,(TEXT("cbCoProcRegSize=%d\r\n"),g_pOemGlobal-AddToDListHead t#ifdefOEMCacheRangeFlush(0,0,if(!OpenExecutable(NULL, K->oe,TOKEN_SYSTEM,NULL,0))LoadE32 K->e32,0,0,K->BasePtr= }//create/setuppStack=VMCreateStack K,pCurThread->dwOrigBase=(DWORD)pCurThread->dwOrigStkSize=pCurThread->tlsSecure=pCurThread->tlsNonSecure=pCurThread->tlsPtr=TLSPTR(pStack,pCurThread->hTok=//Saveoffthethread'sprogramcounterforgettingitsnamepCurThread->dwStartAddr=(DWORD)MDSetupThread(pCurThread,(LPVOID)SystemStartupFunc,0,TH_KMODE, K,DEBUGMSG(ZONE_SCHEDULE,(TEXT("Scheduler:Createdmasterthread}可以看到,这里开始了一个线程,线程处理函数为SystemStartupFunc(),其实现在文件C:\WINCE600\PRIVATE\WINCEOS\COREOS\NK\KERNEL\schedule.c,实现代码如ulong){HANDLE//recordPendEventaddressforKInfoTable[KINX_PENDEVENTS]=(DWORD)//adjustalarmresolutionifitit'snotinif(g_pOemGlobal->dwAlarmResolution<g_pOemGlobal->dwAlarmResolution=elseif(g_pOemGlobal->dwAlarmResolution>g_pOemGlobal->dwAlarmResolution=VERIFY(LoaderInit//initializethecompiler -thismusthappenbeforeother//start PagePoolInit//Thiscanonlybedoneaftertheloader //InitializationforCeLog,profiler,code-coverage,SysDebugInit //initializeSystemDebugger(HWDebugstub,Kerneldumpcapture,SWKernelDebug//dothisnow,sothatwecontinuerunningafterwe'vecreatedthenew#ifdefhTh=HNDLCloseHandle K,pCleanupThread=hAlarmThreadWakeup=IntrEvents[SYSINTR_RTC_ALARM-SYSINTR_DEVICES]=LockIntrEvt//GivetheOEMafinalchancetodoamorefull-featuredinitbefore//appsareKernelIoctl(IOCTL_HAL_POSTINIT,NULL,0,NULL,0,InitMsgQueueInitWatchDog//createthepowerhandlereventandguardhEvtPwrHndlr=NKCreateEvent(NULL,FALSE,FALSE,DEBUGCHKhTh=CreateKernelThread(PowerHandlerGuardThrd,NULL,THREAD_PWR_GUARD_PRIORITY,HNDLCloseHandle K,//dirtypageevent,initiallyhEvtDirtyPage=NKCreateEvent(NULL,FALSE,TRUE,DEBUGCHK//wedon'twanttowasteathreadhere(createaseparateforcleaningdirty//Instead,RunAppsthread e"CleanDirtyPage"threadoncefilesyshTh=CreateKernelThreadHNDLCloseHandle K,#definewhile(1)}}这里创建了一个内核线程,处理函数为RunApps,继续 件C:\WINCE600\PRIVATE\WINCEOS\COREOS\NK\KERNEL\kmisc.c中,代码如下DWORDLPVOID){HMODULEDEBUGMSG(ZONE_ENTRY,(L"RunAppshFilesys=(HMODULE)NKLoadLibraryEx(L"filesys.dll",MAKELONG(LOAD_LIBRARY_IN_KERNEL,LLIB_NO_PAGING),if(hFilesys)FARPROCpfnMain=GetProcAddressA(hFilesys,(LPCSTR) //WinMainofHANDLEhFSReady,DEBUGCHKhFSReady=NKCreateEvent(NULL,TRUE,FALSE,hTh=CreateKernelThread((LPTHREAD_START_ROUTINE)pfnMain,hFilesys,THREAD_RT_PRIORITY_NORMAL,DEBUGCHK(hTh&&HNDLCloseHandle K,//IfpSignalStartedisNULL,wedon'thavefilesys(tinykern).Don'tbotherwaitingforif(pSignalStarted)NKWaitForSingleObject(hFSReady,DEBUGCHK//InitializeMUI-Resourceloader(requires//ReadsystemsettingsfromInitSystemSettingsInitSystemSettings//signalfilesysthatwe're(*pSignalStarted)}HNDLCloseHandleK,}else G(1,(L"Filesysdoesn'texist,noapp}//insteadofexiting,we'remakethisthreadcleaningdirtypagesinthe//should'veneverreturnedDEBUGCHK(0);NKExitThreadreturn}终于启动filesys.dll了。这个过程简单说明一下,启动filesys.dll后等待其执行的情况,在完成了文件系统的相应的初始化之后,这里继续初始化MUI和系统设置,完成后再通知filesys这边的工作已经完成,filesys继续启动。这一部分的具体内容请参考MSDN,FileSystemBootProcess:http://m us/library/aa912276.aspx。总之,filesys会完成WinCE的最后启动过程,包括gwes.dll和explorer.exe等。至此,WinCE6.0启动完成,如果有LCD且驱动能正常工作,现在就应该能看见可爱的WinCE6.0的界面了。本文通本文通 代码的方式,介绍了WinCE6.0的启动流程。流于表面了一点,很多细节应该进一步研究,以后再慢慢看吧。文中有不确切的地方,也请您11、WinCE6.0OALWinCE6.0WinCE6.0的OAL跟WinCE5.0中的有较大差别。了解这些差别,对我们移植部分很有帮助。本文将简要介绍WinCE6.0OALWinCE5.0将内核、OAL和成一个内核可执行文件NK.exeWinCE6.0将这三者分开,分别对应Kernel.dll、OAL.exe和KITL.dll。显而易见,动全身的。WinCE5.0的OAL1WinCE6.0的OAL212CE6.0的OALCE5.0要包括初始化、中断管理和要包括初始化、中断管理和IOCTL2中增加了NKStub.lib、和OEMGLOBAL等内容。这对于WinCE6.0CE5.0核、OAL和KITL执行。Kernel、OAL和KITL相互独立,则消除了这个安全隐患。现在,它们之间的模块导出列表NKGLOBAL表内核API以及共享的全局变量。OAL只能调用该列表中包括的内核API。为了方便OAL代码的移植,微软将函数指针列表封装到NKStub.lib中。所以,我们在移植OAL代码时没有必要通过结构中的函数指针去调用内核API,OAL代码保持原来的函数调用方式。简单看一下NKStub.c的代码实现,下面是部分代码:BOOLINTERRUPTS_ENABLE(BOOL{return{returng_pNKGlobal->pfnINT_ENABLE}可以看到,OAL中调用相应的内核API时,NKStub将执行对应的结构中的函数指针。所以在编译OAL时,添加NKStub.lib后就可以跟以前一样使用这些内核函数。当然NKStub也包括了KITL中的相应函数列表,所以OAL也能使用KITL中的OAL导出了列表OEMGLOBALOAL函数指针和全局变量。其中是可选的。具体内容可查看PB帮助的相应部分。OEMGLOBAL的定义在文件:总结一下,CE6.0的OAL从内核中独立出来,这样方便OAL的单独升级,也提高了安全性。由于内核、OAL和KITL相互独立,所以引入了NKGLOBAL和提供了NKStub.lib和OEMStub.lib,他们做了从函数指针到函数的转换工作。所以,在移植BSP时,原来的代码可保持不变。到这里,我们已经知道了WinCE6.0的OAL 组织。这里将5.0和6.0OAL部分 CE5.0 组对应文CE6.0 组b其中,其中,6.0的OALLIB5.0的OAL,OALEXE对应Kern,KITLKernkitl5.0的kernkitlprof6.0中一般放在oallib下。 组织上介绍了WinCE6.0OAL的特点在移植BSP时可作参考。有关OAL移植的具体实现,后文将详细介绍。另外,文中有不贴切的地方指正。12、WinCE6.0KITLKITLKITL(KernelIndependentTransportLayer)提供了一种调试WinCE的简便方法。KITL来。所以我们在建硬件传输时就省去很麻,否则我们自己须实现与设进行数据交互的协。KL工作在硬件输层之,因此,它无须心用于通信具体硬件,我们可以用UB、ral或者hrnt作为KL的调试通道。具体选择哪一个,由硬件平和软件资源定。有些设没有hrnt和Srial接口,以只能采用UB,如 设备。如果系统采用了B,则建议使用hrnt作调试通道。这时配置KL的代价相对来说小。无论如何,KL相当大,在BP的移植过程中花一些时间实现KL的功能是完全值得的完成KL之后,你会发现所有的时间都没有白花。由于KITL的实现,后续的调试节省了很多时间。磨刀不误砍柴工!工欲善其事,必先利其器!我深有体会!:-D以前没有认识到KITL的强大,一直没有碰它。最近在实现KITL的功能之后,随即顺利调通了几个顽固的驱动。虽然问题本身不值一提,但没有KITL时,驱动出了状况,内核就挂了,不知道挂在哪里,无从下手,也不好分析。而KITL可以帮助我们定位出现问题的位 要的。BTW:按启动顺序来说,KITL启动应该在OAL之后,内核之前。所以,必须先完成OAL的移植,才能进一步移植KITL。闲话少说,接下来介绍WinCE6.0KITL的基本情况。WinCE6.0中,KITLOAL中独立出来,单独编译成kitl.dll。在BSP中 一为 kitl.c的文件,这是BSP中有关KITL的主要代码所在 //tformentrypointforKITL.CalledwhenKITLIoctl(IOCTL_K )iscalled.BOOL{BOOLOAL_KITL_ARGS*pArgs,CHAR*szDeviceId,buffer[OAL_KITL_ID_SIZE];KITL_RETAIG(ZONE_KITL_OAL,("+OEMKitlStartup\r\n"));//Printbanner.WillremovewhenKITL-over-ethernetsupportisdropped//(inKITLOutputDebugString("*ThisimageusesKITL-over- KITLOutputDebugString("*PBConnectivityOptionsmustbesetto:*\n");KITLOutputDebugString("*Download:\"DeviceKITLOutputDebugString("*Transport://Lookforbootargsleftbythebootloaderorleftoverfromanearlierboot. =szDeviceId=//Ifwedon'thavebootargsinRAM,lookfirstinNORflashfortheinformation//otherwiselookontheSmartMediaNANDcard(incasewe'reperformingaNAND-only)boot.if(pArgs=={SectorInfosi;UINT8maccount=0;//GetMACaddressfromNANDif(FMD_Init(NULL,NULL,NULL)=={KITL_RETAIG(ZONE_ERROR,("ERR
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 语文教学中小学生批判性思维的培养途径
- 小学德育活动的信息化教学策略研究
- 编程教育助力孩子成长科技时代的必由之路
- 紧急情况下用电设备的快速反应与急救措施
- 质量管理体系在教育行业的推广与应用
- 2024年不见面审批投资申请报告代可行性研究报告
- 环保意识培养与孩子的未来成长
- 新医改背景下少数民族医药的机遇与挑战
- 小学英语绘本教学中的情感教育与价值观培养
- 现代家庭亲子关系的挑战与对策
- 人教版《道德与法治》四年级下册教材简要分析课件
- 2023年MRI技术操作规范
- 办公用品、易耗品供货服务方案
- 自行联系单位实习申请表
- 医疗废物集中处置技术规范
- 媒介社会学备课
- 2023年检验检测机构质量手册(依据2023年版评审准则编制)
- 三相分离器原理及操作
- 新教科版五年级下册科学全册每节课后练习+答案(共28份)
- 葫芦岛尚楚环保科技有限公司医疗废物集中处置项目环评报告
- 全国物业管理项目经理考试试题
评论
0/150
提交评论