4. 关键点二:嵌入式设备中差分算法库的移植(还原差分包)
4.1. 移植开关算法库代码
代码已开源,地址:
https://gitee.com/qq791314247/mcu_bsdiff_upgrade
整体代码如下图所示:
如上图所示,99%的代码用户都不用去关心,用户只需要提供一个flash写入接口即可,也就是该库给定用户flash地址、数据内容指针、数据内容长度,用户将该段数据写入到flash即可,移植起来特别简单,花不了几分钟的功夫,这也是我花大力气抽象接口的原因。
4.2. 使用该库的流程
4.2.1. 使用库的接口
对于整个库的代码,我们只需要关心一个接口iap_patch,iap_patch在文件”user_interface.h”中。
该接口介绍也比较清晰,差分包的还原,只需要调用这一个接口即可。
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 用户使用差分升级时唯一需要关心的接口
*
* @param old 设备中执行区代码所在的地址,用户可指定flash执行区的地址,方便算法读出来当前
* 运行中的代码
* @param oldsize 设备中执行区代码的长度,用户可在差分包bin头获取
* @param patch 设备中已经下载的差分包所在的flash地址,或者ram地址,只要能让算法读出来即可
* 注意,下载的差分包自带image_header_t格式的文件头,真正的差分包需要偏
* 移sizeof(image_header_t)的长度
* @param patchsize 设备中已经下载的差分包的长度,用户可在差分包bin头获取
* @param newfile 新文件的大小,用户需填入新版本bin的长度,用户亦可以差分包bin头获取
* [url=home.php?mod=space&uid=266161]@return[/url] int 然后错误码,0成功,1失败
*/
extern int iap_patch(const uint8_t* old, uint32_t oldsize, const uint8_t* patch,
uint32_t patchsize, uint32_t newfile);
另外,使用该接口还原时所需要的一些信息可以在差分包文件头中获取,上位机在制作差分包时,会自动在差分包的bin头加上64字节的文件头,以便于告诉嵌入式设备旧/新版本bin文件的CRC校验、长度等信息。所以用户在收到差分包头时,偏移掉这64个字节的文件头的地址才是需要给到iap_patch接口的真正的bin文件。
文件头格式如下代码,用户只需要关心中文注释的部分,其余的都是预留的信息。
/* 差分包制作时自带的文件头信息,用户只需要关心中文注释的部分 */
typedef struct image_header
{
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum 差分包包头校验 */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size 差分包的大小 */
uint32_t ih_load; /* Data Load Address 上一版本旧文件的大小 */
uint32_t ih_ep; /* Entry Point Address 要升级的新文件的大小 */
uint32_t ih_dcrc; /* Image Data CRC Checksum 新文件的CRC */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
uint32_t ih_ocrc; /* Old Image Data CRC Checksum 上一版本旧文件的CRC */
} image_header_t;
/* 差分包制作时自带的文件头信息,用户只需要关心中文注释的部分 */
4.2.2. 接口使用例子
我截取一段我工程中的代码来讲解如何使用该接口还原出新版本bin文件:
#ifdef BSDIFF_UPGRADE
image_header_t recv_head;
uint32_t recv_hcrc; /* 接收到的文件头CRC */
uint32_t calculation_crc; /* 计算出来的文件头CRC */
uint32_t spi_flash_addr = UPGRADE_PROGRAM_ADDR;
memcpy(&recv_head, (uint8_t *)APPLICATION_A, sizeof(image_header_t));
recv_hcrc = BigtoLittle32(recv_head.ih_hcrc);
recv_head.ih_hcrc = 0;
calculation_crc = crc32((uint8_t *)&recv_head, sizeof(image_header_t));
if (recv_hcrc == calculation_crc)
{
recv_head.ih_hcrc = recv_hcrc;
recv_head.ih_time = BigtoLittle32(recv_head.ih_time);
recv_head.ih_size = BigtoLittle32(recv_head.ih_size);
recv_head.ih_dcrc = BigtoLittle32(recv_head.ih_dcrc);
recv_head.ih_ocrc = BigtoLittle32(recv_head.ih_ocrc);
/* 差分升级包 */
recv_head.ih_hcrc = calculation_crc;
if (crc32((uint8_t *)APPLICATION_RUN, recv_head.ih_load) != recv_head.ih_ocrc)
{
APP_ERR_PRINT("file oldcrc err,calcrc:0X%08X, ih_oldbin_crc:0X%08X,",
crc32((uint8_t *)APPLICATION_RUN,
recv_head.ih_load), recv_head.ih_ocrc);
goto bsdiff_out;
}
RTOS_LOCK();
disable_task_monitoring(ALL_TASK_RUNFLAG_BIT, true);
// flash_erase_sector(UPGRADE_PROGRAM_ADDR, UPGRADE_PROGRAM_PAGE);
recv_hcrc = iap_patch((uint8_t *)APPLICATION_RUN, recv_head.ih_load,
(uint8_t *)(APPLICATION_A + sizeof(image_header_t)),
recv_head.ih_size, UPGRADE_PROGRAM_ADDR);
if (recv_hcrc != recv_head.ih_ep)
{
APP_ERR_PRINT("iap_patch len err.");
APP_ERR_PRINT("iap_patch len: %lu, new_len: %lu", recv_hcrc, recv_head.ih_ep);
goto bsdiff_out;
}
if (erase_program(APPLICATION_A))
{
APP_ERR_PRINT("I erase program failed.");
goto bsdiff_out;
}
current_flash_write_addr = APPLICATION_A;
for (uint32_t i = 0; i < (recv_head.ih_ep / 1024); i++)
{
xmq25qxx_read(spi_flash_addr, spi_read_cache, 1024);
if (xflash_write(current_flash_write_addr, spi_read_cache, 1024))
{
APP_ERR_PRINT("I write program failed.");
goto bsdiff_out;
}
spi_flash_addr += 1024;
current_flash_write_addr += 1024;
APP_PRINT("current_flash_write_addr: 0X%08X", current_flash_write_addr);
}
if (recv_head.ih_ep % 1024 != 0)
{
memset(spi_read_cache, 0XFF, 1024);
xmq25qxx_read(spi_flash_addr, spi_read_cache, recv_head.ih_ep % 1024);
if (xflash_write(current_flash_write_addr, spi_read_cache, 1024))
{
APP_ERR_PRINT("I write program failed.");
goto bsdiff_out;
}
}
if (crc32((uint8_t *)APPLICATION_A, recv_head.ih_ep) != recv_head.ih_dcrc)
{
APP_ERR_PRINT("file newcrc err,calcrc:0X%08X, newcrc:0X%08X, len: %lu",
crc32((uint8_t *)APPLICATION_A, recv_head.ih_ep),
recv_head.ih_load, recv_head.ih_dcrc);
goto bsdiff_out;
}
/* 下载成功,开始升级 */
if (check_bin_file((bin_info_t *)(APPLICATION_A + BIN_INFO_OFFSET))) /* bin文件非法 */
{
APP_ERR_PRINT("check_bin_file err.");
goto bsdiff_out;
}
recv_head.ih_dcrc = CRT_CRC16_check(0, (uint8_t *)APPLICATION_A,
recv_head.ih_ep);
readwrite_app_run_bin_info(0, &recv_head.ih_ep, (uint16_t *)&recv_head.ih_dcrc);
/* 整体校验成功,确认升级 */
if (switch_program_stage(STAGE_1))
{
APP_ERR_PRINT("I write switch_program_stage STAGE_0 failed."); /* 置位升级标志写失败 */
goto bsdiff_out;
}
APP_PRINT("upgrade success.");
bsdiff_out:
SYSTEM_RESET();
}
|