S3C2450自动升级
2010年06月14日
  S3C2450自动升级
  在BSP包中,有两个bootloader文件夹,一个命名为bootloader,另一个命名为bootloader_update。Bootloader文件夹用于USB下载,调试用,bootloader_update用于生产,自动升级用。下面重点介绍bootloader_update文件夹。
  Bootloader_update文件夹下有四个文件夹,分别是BLCOMMON,Eboot_boot,FAT_LIB以及IROM_SD_TOOL。BLCOMMON文件夹编译后生成oal_blcommon.lib文件,供Eboot_boot调用。FAT_LIB文件夹里面包含三个静态库文件FAT_LIB.lib,HSMMCEBOOT_LIB.lib以及SDREAD_UPDATE.lib。这些静态库封装了一些用于升级的函数。Eboot_boot文件夹编译后将会产生生产用的Eboot.bin文件,该文件即能够启动系统,也能够通过硬件平台的按键控制,实现自动升级。IROM_SD_TOOL文件夹编译后,会将前面的Eboot.bin文件封装成SD卡识别的程序,最终生成IROM_SD_EBOOT.nb0。通过三星提供的IROM_Fusing_Tool工具,将该文件烧到SD卡后,2450平台就能够实现从SD卡启动了。当然前提是硬件本身要设置跳线成SD卡启动。
  主程序中(Eboot_boot文件夹)定义了三个重要的变量:AUTO_UPDATE_FROM_SD;
  ERASE_ALL_FLASH_BLOCKS;
  SHOW_UPDATE_LOGO。
  当AUTO_UPDATE_FROM_SD为真时,Eboot将会进入升级程序,为假时将引导flash中的NK,启动系统。
  当ERASE_ALL_FLASH_BLOCKS为真时,Eboot将会格式化flash,然后依次升级block0.nb0,eboot.bin,nk.bin,随后启动系统。
  当SHOW_UPDATE_LOGO为真时,LCD上将会出现升级文件的提示,否则显示开机LOGO。
  其中AUTO_UPDATE_FROM_SD标志位通过硬件平台的按键判断,当拨动电源键后在两秒之内回拨到LOCK位置,Eboot将识别到该LOCK标志,这时将AUTO_UPDATE_FROM_SD置高,程序进入升级状态。
  ERASE_ALL_FLASH_BLOCKS标志位通过从SD卡中的update.txt文件中读取。
  AUTO_UPDATE_FROM_SD的相关代码如下: if(1 == UPDATA_ALL_IMAGE) { AUTO_UPDATE_FROM_SD =TRUE; ERASE_ALL_FLASH_BLOCKS =TRUE; SHOW_UPDATE_LOGO = TRUE; } else if( _FAT_Boot_ReadFile(_BOOT_TYPE_SD_BIN, _UPDATE_FILE_DETECT) ||auto_updatekey_isPressed() ) { //自动升级健按下进行自动升级 AUTO_UPDATE_FROM_SD=TRUE; } else { AUTO_UPDATE_FROM_SD = FALSE; } 其中_FAT_Boot_ReadFile()函数用于从SD卡中读取_UPDATE_FILE_DETECT文件,该字符串在最前面有如下定义:
  #define _UPDATE_FILE_DETECT "DETECT BIN"
  auto_updatekey_isPressed()函数用于检测拍照键是否按下,从这里可以看出,如果SD卡中存在detect.bin文件,则升级时不用再按住拍照键了。如果没有则必须按住拍照键。
  Flash格式化的程序如下: if(1)// format flash. { //ADDED BY ZHOUPENG FOR TEST ReadDeviceSerialNumber(Serial_Block_Number,pBuf8); ReadDeviceSerialNumber(Smit_Device_Serial_Block_Nu mber,pBuf9); OALMSG(TRUE, (TEXT(" ++Format FIL (Erase All Blocks)\r\n"))); if (VFL_Close() != VFL_SUCCESS) { OALMSG(TRUE, (TEXT("[ERR] VFL_Close() Failure\r\n"))); } if (WMR_Format_FIL() == FALSE32) { OALMSG(TRUE, (TEXT("[ERR] WMR_Format_FIL() Failure\r\n"))); } if (WMR_Format_VFL() == FALSE32) { OALMSG(TRUE, (TEXT("[ERR] WMR_Format_VFL() Failure\r\n"))); } if (WMR_Format_FTL() == FALSE32) { OALMSG(TRUE, (TEXT("[ERR] WMR_Format_FTL() Failure\r\n"))); } OALMSG(TRUE, (TEXT("[INF] You can not use VFL before Format VFL\r\n"))); OALMSG(TRUE, (TEXT(" --Format FIL (Erase All Blocks)\r\n"))); WriteDeviceSerialNumber(Serial_Block_Number,pBuf8) ; WriteDeviceSerialNumber(Smit_Device_Serial_Block_N umber,pBuf9); } 下载映像的函数如下: static BOOL DownloadImage (LPDWORD pdwImageStart, LPDWORD pdwImageLength, LPDWORD pdwLaunchAddr) { BYTE hdr[BL_HDRSIG_SIZE]; DWORD dwRecLen, dwRecChk, dwRecAddr; BOOL fIsFlash = FALSE; LPBYTE lpDest = NULL; int nPkgNum = 0; BYTE nNumDownloadFiles = 1; DWORD dwImageStart = 0; DWORD dwImageLength = 0; RegionInfo *pCurDownloadFile; int i; *pdwImageStart = *pdwImageLength = *pdwLaunchAddr = 0; g_DownloadManifest.dwNumRegions = 0 ; for( i = 0 ; i read image signature.\r\n"); HALT (BLERR_MAGIC); return (FALSE); } // An N000FF packet is manufactured by Platform Builder when we're // downloading multiple files or when we're downloading a .nb0 file. if (!memcmp (hdr, "N000FF\x0A", BL_HDRSIG_SIZE))//比较数据相等时memcmp返回0 { // read the packet checksum. // if (!OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecChk)) { EdbgOutputDebugString("\r\nUnable to read download manifest checksum.\r\n"); HALT (BLERR_MAGIC); return (FALSE); } // read BIN region descriptions (start address and length). // if (!OEMReadData (sizeof (DWORD), (LPBYTE) &g_DownloadManifest.dwNumRegions) || !OEMReadData ((g_DownloadManifest.dwNumRegions * sizeof(RegionInfo)), (LPBYTE) &g_DownloadManifest.Region[0])) { EdbgOutputDebugString("\r\nUnable to read download manifest information.\r\n"); HALT (BLERR_MAGIC); return (FALSE); } // verify the packet checksum. if (!VerifyChecksum((g_DownloadManifest.dwNumRegions * sizeof(RegionInfo)), (LPBYTE) &g_DownloadManifest.Region[0], dwRecChk)) { EdbgOutputDebugString ("\r\nDownload manifest packet failed checksum verification.\r\n"); HALT (BLERR_CHECKSUM); return (FALSE); } if (g_pOEMMultiBINNotify) { g_pOEMMultiBINNotify((PDownloadManifest)&g_Downloa dManifest); } // look for next download... nNumDownloadFiles = (BYTE)(g_DownloadManifest.dwNumRegions + 1); // +1 to account for this packet. continue; } // Is this an old X000FF multi-bin packet header? It's no longer supported. else if (!memcmp (hdr, "X000FF\x0A", BL_HDRSIG_SIZE)) { EdbgOutputDebugString ("ERROR: The X000FF packet is an old-style multi-bin download manifest and it's no longer supported. \r\nPlease update your Platform Builder installation in you want to download multiple files.\r\n"); HALT (BLERR_MAGIC); return (FALSE); } // Is this a standard bin image? Check for the usual bin file signature. else if (!memcmp (hdr, "B000FF\x0A", BL_HDRSIG_SIZE))//lqm:下载xip.bin,nk.bin,eboot.bin时在这里执行 { g_bBINDownload = TRUE; if (!OEMReadData (sizeof (DWORD), (LPBYTE) &dwImageStart)//读取*.bin前7个头字节后的一个DWORD到dwImageStart。*.bin记录了其映像起始地址 || !OEMReadData (sizeof (DWORD), (LPBYTE) &dwImageLength))//读取上面接着的一个DWORD到dwImageLength。*.bin记录了其映像长度 { EdbgOutputDebugString ("Unable to read image start/length\r\n"); HALT (BLERR_MAGIC); return (FALSE); } } // If the header signature isn't recognized, we'll assume the // download file is a raw .nb0 file. // else { g_bBINDownload = FALSE; } if (!g_DownloadManifest.dwNumRegions) { g_DownloadManifest.dwNumRegions = 1; //声明*.bin有一条记录 g_DownloadManifest.Region[0].dwRegionStart = dwImageStart; //声明这条记录的起始地址 g_DownloadManifest.Region[0].dwRegionLength = dwImageLength;//声明这条记录的映像长度 // Provide the download manifest to the OEM. if (g_pOEMMultiBINNotify) { g_pOEMMultiBINNotify((PDownloadManifest)&g_Downloa dManifest); } } // Locate the current download manifest entry (current download file). pCurDownloadFile = &g_DownloadManifest.Region[g_DownloadManifest.dwNu mRegions - nNumDownloadFiles];//当前下载文件指向*.bin开始 // give the OEM a chance to verify memory.内存校验 if (g_pOEMVerifyMemory && !g_pOEMVerifyMemory (pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionLength)) { EdbgOutputDebugString ("!OEMVERIFYMEMORY: Invalid image\r\n");//lqm:很多情况下都会执行到这里!映像不能获得? HALT (BLERR_OEMVERIFY); return (FALSE); } // check for flash image. Start erasing if it is. if ( (fIsFlash = OEMIsFlashAddr(pCurDownloadFile->dwRegionStart))//l qm:校验起始地址是否在Flash相应地址区域,如果是则擦除相应下载区域 && !OEMStartEraseFlash (pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionLength)) { EdbgOutputDebugString ("Invalid Flash Address/Length\r\n"); HALT (BLERR_FLASHADDR); return (FALSE); } // if we're downloading a binary file, we've already downloaded part of the image when searching // for a file header. copy what we've read so far to the destination buffer, then finish downloading. if (!g_bBINDownload) { lpDest = OEMMapMemAddr (pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionStart); memcpy(lpDest, hdr, BL_HDRSIG_SIZE); // complete the file download... // read data block if (!OEMReadData ((pCurDownloadFile->dwRegionLength - BL_HDRSIG_SIZE), (lpDest + BL_HDRSIG_SIZE))) { EdbgOutputDebugString ("ERROR: failed when reading raw binary file.\r\n"); HALT (BLERR_CORRUPTED_DATA); return (FALSE); } } // we're downloading a .bin file - download each .bin record in turn... else//开始下载*.bin文件到DRAM中。 { while (OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecAddr) && //读取*.bin的第n条记录的地址. OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecLen) && //读取*.bin的第n条记录的长度. OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecChk)) //读取*.bin的第n条记录的校验和. { // last record of .bin file uses sentinel values for address and checksum. // 最后一条记录表示结束.其地址和校验和均为0. if (!dwRecAddr && !dwRecChk) { break;//读到最后一条记录时跳出循环 } // map the record address (FLASH data is cached, for example) // 将存入的记录地址映射到指针地址lpDest lpDest = OEMMapMemAddr (pCurDownloadFile->dwRegionStart, dwRecAddr); // read data block if (!OEMReadData (dwRecLen, lpDest))//读取*.bin的第n条记录的内容到lpDest.这里先把*.bin的数据读到DRAM中。 { EdbgOutputDebugString ("****** Data record %d corrupted, ABORT!!! ******\r\n", nPkgNum); HALT (BLERR_CORRUPTED_DATA); return (FALSE); } if (!VerifyChecksum (dwRecLen, lpDest, dwRecChk))//每读取一条记录则校验一次数据 { EdbgOutputDebugString ("****** Checksum failure on record %d, ABORT!!! ******\r\n", nPkgNum); HALT (BLERR_CHECKSUM); return (FALSE); } // Look for ROMHDR to compute ROM offset. NOTE: romimage guarantees that the record containing // the TOC signature and pointer will always come before the record that contains the ROMHDR contents. // if (dwRecLen == sizeof(ROMHDR) && (*(LPDWORD) OEMMapMemAddr(pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE)) { DWORD dwTempOffset = (dwRecAddr - *(LPDWORD)OEMMapMemAddr(pCurDownloadFile->dwRegionS tart, pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET + sizeof(ULONG))); ROMHDR *pROMHdr = (ROMHDR *)lpDest; // Check to make sure this record really contains the ROMHDR. // if ((pROMHdr->physfirst == (pCurDownloadFile->dwRegionStart - dwTempOffset)) && (pROMHdr->physlast == (pCurDownloadFile->dwRegionStart - dwTempOffset + pCurDownloadFile->dwRegionLength)) && (DWORD)(HIWORD(pROMHdr->dllfirst dlllast) && (DWORD)(LOWORD(pROMHdr->dllfirst dlllast)) { g_dwROMOffset = dwTempOffset; EdbgOutputDebugString("rom_offset=0x%x.\r\n", g_dwROMOffset); } } // verify partial checksum OEMShowProgress (nPkgNum ++); // lqm added Progress in LCD.10-01-22 while( nPkgNum>=8 ) { nPkgNum = 0; LcdDrawString(">"); } // end added. } } if (g_bBINDownload) { // Does this .bin file contain a TOC? if (*(LPDWORD) OEMMapMemAddr(pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE) //+ ksk 20051219 { // Contain the kernel? if (IsKernelRegion(pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionLength)) { *pdwImageStart = pCurDownloadFile->dwRegionStart; *pdwImageLength = pCurDownloadFile->dwRegionLength; *pdwLaunchAddr = dwRecLen; } } // No TOC - not made by romimage. However, if we're downloading more than one // .bin file, it's probably chain.bin which doesn't have a TOC (and which isn't // going to be downloaded on its own) and we should ignore it. // else if (g_DownloadManifest.dwNumRegions == 1) { *pdwImageStart = pCurDownloadFile->dwRegionStart; *pdwImageLength = pCurDownloadFile->dwRegionLength; *pdwLaunchAddr = dwRecLen; } } else // Raw binary file. { *pdwImageStart = pCurDownloadFile->dwRegionStart; *pdwLaunchAddr = pCurDownloadFile->dwRegionStart; *pdwImageLength = pCurDownloadFile->dwRegionLength; } // write to flash if it's flash image if (fIsFlash) { // finish the flash erase if (!OEMFinishEraseFlash ()) { HALT (BLERR_FLASH_ERASE); return (FALSE); } // Before writing the image to flash, optionally check the image signature. if (g_bBINDownload && g_pOEMCheckSignature) { if (!g_pOEMCheckSignature(pCurDownloadFile->dwRegionSt art, g_dwROMOffset, *pdwLaunchAddr, TRUE)) HALT(BLERR_CAT_SIGNATURE); } } } while (--nNumDownloadFiles); if (fIsFlash) { nNumDownloadFiles = (BYTE)g_DownloadManifest.dwNumRegions; while (nNumDownloadFiles--) { if (!OEMWriteFlash (g_DownloadManifest.Region[nNumDownloadFiles].dwRe gionStart, g_DownloadManifest.Region[nNumDownloadFiles].dwReg ionLength)) { HALT (BLERR_FLASH_WRITE); return (FALSE); } } } return (TRUE); } 上面程序中有如下代码:
  OEMShowProgress (nPkgNum ++);
  while( nPkgNum>=8 )
  {
  nPkgNum = 0;
  LcdDrawString(">");
  }
  该程序放在反复从SD卡中读取xip.bin的一条条记录的while循环中,这里是为了在升级界面上显示升级的进度。LcdDrawString(">")函数是调用了LcdDraw.c中的函数,用于在LCD上绘图。
  这里一定要了解xip.bin的文件格式,否则很难理解整个数据流的流程。
  Xip.bin以及NK.bin的组成:
  组成:标记(7)+Image开始地址(1)+Image长度(1)
  记录0地址+记录0长+记录0校验和+记录0内容(文件内容)
  记录1地址+记录1长+记录1校验和+记录1内容(文件内容)
  ......
  结束符号
  从上面的程序可以看出,
  if (!OEMReadData (BL_HDRSIG_SIZE, hdr))
  {
  EdbgOutputDebugString ("\r\nUnable to read image signature.\r\n");
  HALT (BLERR_MAGIC);
  return (FALSE);
  }
  即是将xip.bin的前7个字节的数据读到数组hdr,供后面分析所读的文件的类型。
  if (!OEMReadData (sizeof (DWORD), (LPBYTE) &dwImageStart)//读取*.bin前7个头字节后的一个DWORD到dwImageStart。*.bin记录了其映像起始地址
  || !OEMReadData (sizeof (DWORD), (LPBYTE) &dwImageLength))//读取上面接着的一个DWORD到dwImageLength。*.bin记录了其映像长度
  {
  EdbgOutputDebugString ("Unable to read image start/length\r\n");
  HALT (BLERR_MAGIC);
  return (FALSE);
  }
  则是将紧接着前7个字节的两个WORD数据读到dwImageStart和dwImageLength。随后的
  while (OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecAddr) &&
  //读取*.bin的第n条记录的地址.
  OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecLen) &&
  //读取*.bin的第n条记录的长度.
  OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecChk))
  //读取*.bin的第n条记录的校验和.
  则开始一条条的解析xip.bin的记录。
  if (!OEMReadData (dwRecLen, lpDest))
  {
  EdbgOutputDebugString ("****** Data record %d corrupted, ABORT!!! ******\r\n", nPkgNum);
  HALT (BLERR_CORRUPTED_DATA);
  return (FALSE);
  }
  用于将读取到的第N条记录存放到lpDest.
  分析要下载的文件并写入flash的代码如下: void OEMLaunch( DWORD dwImageStart, DWORD dwImageLength, DWORD dwLaunchAddr, const ROMHDR *pRomHdr ) { DWORD dwPhysLaunchAddr; OALMSG(1, (TEXT("+OEMLaunch.\r\n"))); // If the user requested that a disk image (stored in RAM now) be written to the SmartMedia card, so it now. if (g_bDownloadImage) // && (g_pBootCfg->ConfigFlags & TARGET_TYPE_NAND)) { // Since this platform only supports RAM images, the image cache address is the same as the image RAM address. switch (g_ImageType) { case IMAGE_TYPE_STEPLDR://stepldr.bin if (!WriteRawImageToBootMedia(dwImageStart, dwImageLength, dwLaunchAddr)) { OALMSG(OAL_ERROR, (TEXT("ERROR: OEMLaunch: Failed to store image to Smart Media.\r\n"))); goto CleanUp; } OALMSG(TRUE, (TEXT("INFO: Step loader image stored to Smart Media. Please Reboot. Halting...\r\n"))); if (g_bAutoDownload == TRUE) { return ; } while(1); break; case IMAGE_TYPE_LOADER://eboot.bin g_pTOC->id[0].dwLoadAddress = dwImageStart; g_pTOC->id[0].dwTtlSectors = FILE_TO_SECTOR_SIZE(dwImageLength); if (!WriteRawImageToBootMedia(dwImageStart, dwImageLength, dwLaunchAddr)) { OALMSG(OAL_ERROR, (TEXT("ERROR: OEMLaunch: Failed to store image to Smart Media.\r\n"))); goto CleanUp; } if (dwLaunchAddr && (g_pTOC->id[0].dwJumpAddress != dwLaunchAddr)) { g_pTOC->id[0].dwJumpAddress = dwLaunchAddr; #if 0 // don't write TOC after download Eboot if ( !TOC_Write() ) { EdbgOutputDebugString("*** OEMLaunch ERROR: TOC_Write failed! Next boot may not load from disk *** \r\n"); } TOC_Print(); #endif // by hmseo - 061123 } OALMSG(TRUE, (TEXT("INFO: Eboot image stored to Smart Media. Please Reboot. Halting...\r\n"))); if (g_bAutoDownload == TRUE) { return ; } while(1); break; case IMAGE_TYPE_RAMIMAGE://NK.bin OALMSG(1, (TEXT("+IMAGE_TYPE_RAMIMAGE dwImageStart:%08x.,dwLaunchAddr : %08x\r\n"),dwImageStart,dwLaunchAddr)); g_pTOC->id[g_dwTocEntry].dwLoadAddress = dwImageStart; g_pTOC->id[g_dwTocEntry].dwTtlSectors = FILE_TO_SECTOR_SIZE(dwImageLength); // 将NK.bin写到flash. if (!WriteOSImageToBootMedia(dwImageStart, dwImageLength, dwLaunchAddr)) { OALMSG(OAL_ERROR, (TEXT("ERROR: OEMLaunch: Failed to store image to Smart Media.\r\n"))); goto CleanUp; } EdbgOutputDebugString("INFO1:dwLaunchAdd : %08x g_pTOC->id[0].dwJumpAddress :%08x, g_pTOC->id[1].dwJumpAddress :%08x\r \r\n", dwLaunchAddr, g_pTOC->id[0].dwJumpAddress,g_pTOC->id[1].dwJumpAddr ess); if (dwLaunchAddr && (g_pTOC->id[g_dwTocEntry].dwJumpAddress != dwLaunchAddr)) { g_pTOC->id[g_dwTocEntry].dwJumpAddress = dwLaunchAddr; if ( !TOC_Write() ) //写TOC文件 { EdbgOutputDebugString("*** OEMLaunch ERROR: TOC_Write failed! Next boot may not load from disk *** \r\n"); } //masked by denis_wei for test 2009-02-05 TOC_Print(); } else { dwLaunchAddr= g_pTOC->id[g_dwTocEntry].dwJumpAddress; EdbgOutputDebugString("INFO2: using TOC[%d] dwJumpAddress: 0x%x\r\n", g_dwTocEntry, dwLaunchAddr); } break; } if(ERASE_ALL_FLASH_BLOCKS)//lqm added.10-01-22 { LcdSetStringPosition(1,15);// lqm added.10-01-22 LcdDrawString("Starting WINCE,Please Wait......");// lqm added.10-01-22 //LcdSetStringPosition(1,16);// lqm added.10-01-22 } if ( !ReadOSImageFromBootMedia( ) )//从FLASH读取映像到DRAM. { OALMSG(OAL_ERROR, (TEXT("OEMPlatformInit ERROR: Failed to load kernel region into RAM.\r\n"))); SpinForever(); } } OALMSG(1, (TEXT("waitforconnect\r\n"))); if (dwLaunchAddr && (g_pTOC->id[g_dwTocEntry].dwJumpAddress != dwLaunchAddr)) { g_pTOC->id[g_dwTocEntry].dwJumpAddress = dwLaunchAddr; } else { dwLaunchAddr= g_pTOC->id[g_dwTocEntry].dwJumpAddress; OALMSG(OAL_INFO, (TEXT("INFO: using TOC[%d] dwJumpAddress: 0x%x\r\n"), g_dwTocEntry, dwLaunchAddr)); } // Jump to downloaded image (use the physical address since we'll be turning the MMU off)... // dwPhysLaunchAddr = (DWORD)OALVAtoPA((void *)dwLaunchAddr); OALMSG(TRUE, (TEXT("INFO: OEMLaunch: Jumping to Physical Address 0x%Xh (Virtual Address 0x%Xh)...\r\n\r\n\r\n"), dwPhysLaunchAddr, dwLaunchAddr)); // Jump... if( g_bAutoDownload == TRUE ) { OALMSG(TRUE, (TEXT("INFO: auto update (BLOCK0IMAGE EBOOT NK) SUCCESS!\r\n")));} Launch(dwPhysLaunchAddr);//跳到指定物理地址运行程序. CleanUp: OALMSG(TRUE, (TEXT("ERROR: OEMLaunch: Halting...\r\n"))); SpinForever(); } 在上面代码中,如下代码用于将已经读入DRAM的数据烧写到flash. if (!WriteOSImageToBootMedia(dwImageStart, dwImageLength, dwLaunchAddr)) { OALMSG(OAL_ERROR, (TEXT("ERROR: OEMLaunch: Failed to store image to Smart Media.\r\n"))); goto CleanUp; } 整个bootloader代码量大,但是细下心来慢慢分析,其实不难理解。这里就不一一分析了。

S3C2450自动升级相关推荐

  1. S3C2450自动升级[原创作品,转载请注明出处]

    S3C2450自动升级 在BSP包中,有两个bootloader文件夹,一个命名为bootloader,另一个命名为bootloader_update.Bootloader文件夹用于USB下载,调试用 ...

  2. 【转载】插件自动升级

    ESFramework介绍之(20)―― 插件自动升级 当我们的服务平台搭建成功后,所需要做的主要事情就是开发服务端功能插件(IFunAddin)和客户端插件(IPassiveAddin),每个插件对 ...

  3. 在WinForm中使用Web Services 来实现软件自动升级(转)

    一.升级的好处. 长期以来,广大程序员为到底是使用Client/Server,还是使用Browser/Server结构争论不休,在这些争论当中,C/S结构的程序的可维护性差,布置困难,升级不方便,维护 ...

  4. tomcat升级_「shell脚本」懒人运维之自动升级tomcat应用(war包)

    准备: 提前修改war包里的相关配置,并上传到服务器: 根据要自动升级的tomcat应用修改或添加脚本相关内容: tomcat启动脚本如是自己写的,要统一格式命名,如:xxx.xxxTomcat 等: ...

  5. 软件自动升级ftp服务器,国人自己的专业FTP服务器软件(上)

    目前FTP服务器软件都为国外作品,国内成熟的FTP服务器软件很少,有一些,但从功能上看来远不能和那些流行的服务器软件媲美.不过,笔者近日发现了一款国人自行开发的FTP服务器软件Crob FTP Ser ...

  6. Ionic实战 自动升级APP(Android版)

    Ionic 框架介绍 Ionic是一个基于Angularjs.可以使用HTML5构建混合移动应用的用户界面框架,它自称为是"本地与HTML5的结合".该框架提供了很多基本的移动用户 ...

  7. VS 打包升成可自动升级的安装包

    VS 打包升成可自动升级的安装包 http://www.sufeinet.com/forum.php?mod=viewthread&tid=2187 posted on 2013-07-23 ...

  8. 强!chrome彻底关闭自动升级新方法实例演示,终于解决了chrome自动升级的烦恼

    我们做法是:清空Update文件夹,并设置权限,让chrome没有权限改这个文件夹. 原理:以往各种方法禁用chrome自动升级不成功,它有各种方法来升级谷歌,但一定是用update里的升级程序来升级 ...

  9. android自动更新列表,Android数据库表结构自动升级

    Android App开发如果涉及过数据库的朋友们肯定会碰到数据库升级的工作,Android官方的建议办法是override public void onUpgrade(SQLiteDatabase ...

最新文章

  1. Java进阶:synchronized使用详解
  2. MUI - 解决bug: iphone页面保存时popPicker自动弹出来
  3. 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  4. 我的敏捷、需求分析、UML、软件设计电子书 - 下载(持续更新中)
  5. PL/SQL DEVELOPER中查询结果复制出来中文乱码的解决方案
  6. c#窗口操作-句柄操控全解
  7. 不要变得迟钝,努努力,什么都迎刃而解
  8. 牛逼!它比传统数据库快 100-1000,真不相信?
  9. 脚本——LoadRunner文件操作函数详解
  10. 2019最新版Eclipse下载与安装
  11. 技术可行性分析和经济可行性分析
  12. ios审核提示:您的 Apple Developer Program 帐户已被标记为删除
  13. C语言怎么提出大写字母,c语言函数toupper()如何将小写字母转换为大写字母
  14. Kalman详尽原理介绍合集
  15. OpenCV学习——基本操作之绘制几何图形
  16. 十进制与二进制的转换
  17. Linux启动引导参数grub
  18. 联想小新 100 智能投影仪 评测
  19. 全媒体运营师胡耀文教你:教你从0到1,搭建直播电商的直播间模块
  20. 线结构光光条直线方程提取问题分析

热门文章

  1. 解决前端直接跳转后台,未登录问题
  2. 前端简历如何描述项目经历
  3. matlab 邦加球,吴先良(软件学院)老师 - 安徽大学
  4. 51单片机定时器和波特率
  5. 【Day12-Stream流Map集合】
  6. otter异常——zookeeper重新初始化
  7. Python之Email邮箱账号抓取
  8. 2017第三届美亚杯全国电子数据取证大赛个人赛wp
  9. 【jsPlumb】使用记录
  10. 4.17每日一题之杂物(洛谷c++)