static int buildFw(const char* outputPath, const char *devName) //构建完整的固件镜像(如 up_boot.bin 和 flash.bin),将多个组件(如 bootloader、kernel、rootfs、tpheader、模块规格、扩展固件等)按照特定布局打包进一个缓冲区,并最终写入输出文件。
{
int i = 0;
char *buf = NULL;
char *p = NULL;
char *fwBase = NULL;
int fwLen = 0;
int ret = EXIT_FAILURE;
int bufLen = layout->flashSize + sizeof(BLOCK_HEADER) + gExtModuleSpecBlockLen;
int ext_fw_file_len = 0;
char *p_tmp = NULL;
int default_flag = 0;
char tpheaderName[FIRMWARE_NAME_MAXLEN] = {0};
char factoryInfoName[FIRMWARE_NAME_MAXLEN] = {0};
char ispConfigName[FIRMWARE_NAME_MAXLEN]= {0};
char ispDefaultName[FIRMWARE_NAME_MAXLEN]= {0};
char cp_cmd[FIRMWARE_NAME_MAXLEN]= {0};
int facinfo_len = 0;
int is_larger_jffs2 = 0;
int user_record_alarm_offset = 0;
if (gExtFwUpgrade)
{
bufLen = layout->flashSize + sizeof(BLOCK_HEADER) + gExtModuleSpecBlockLen + gExtFwBlockLen;
}
buf = (char *)malloc(bufLen);
if (!buf)
{
ERR("no memory for buffer");
goto out_free_buf;
}
memset(buf, 0xff, bufLen);
/* deal with up_boot.bin first */
if (1 == gNoPackUboot)
{
/* up_boot.bin 布局(从低地址到高地址): up header + factory_boot + tphead + kernel + romfs + jffs2_len */
fwBase = buf + mi.tpHeaderOffset - mi.fdtLen - mi.bootloaderLen - uhTagLength;
fwLen = bufLen - mi.tpHeaderOffset + mi.bootloaderLen + mi.fdtLen + uhTagLength;
DBG("2680:mi.fdtLen is %d", mi.fdtLen);
}
else
{
fwBase = buf + mi.bootloaderOffset - mi.fdtLen - uhTagLength; /* have a up header before bootloader */
fwLen = bufLen - mi.bootloaderOffset + mi.fdtLen + uhTagLength;
DBG("2686:mi.fdtLen is %d", mi.fdtLen);
}
#ifdef FDT_UPGRADE_SUPPORT
//fdt
p = buf + mi.bootloaderOffset - mi.fdtLen;
DBG("read dts to 0x%x",p - buf);
ret = readToBuf(&fdtInfo, p);
if (ret)
{
goto out_free_buf;
}
#endif
if (use_nand_flash)
{
if (1 == gNoPackUboot)
{
p = buf + mi.tpHeaderOffset + mi.tpHeaderLen;
}
else
{
p = buf + mi.bootloaderOffset + mi.bootloaderLen + mi.tpHeaderLen;
}
}
else
{
p = buf + mi.kernelOffset;
}
DBG("read kernel to 0x%x",p - buf);
ret = readToBuf(&kernelInfo, p);
if (ret)
{
goto out_free_buf;
}
p += mi.kernelLen;
DBG("read rootfs to 0x%x",p - buf);
ret = readToBuf(&rootfsInfo, p);
if (ret)
{
goto out_free_buf;
}
if(encrypt_rootfs_header)
{
i = _AES_cfb1_encrypt_rijndael(AES_CFB1_key, AES_KEY_LEN, AES_CFB1_iv, p, ENCRYPT_ROOTFS_HEADER_SIZE, encrypt_rootfs_header_buffer);
if(i != ENCRYPT_ROOTFS_HEADER_SIZE){
ERR("ENCRYPT_ROOTFS_HEADER Fail! (ret:%d != %d)\n", i , ENCRYPT_ROOTFS_HEADER_SIZE);
goto out_free_buf;
}
memcpy(p,encrypt_rootfs_header_buffer,ENCRYPT_ROOTFS_HEADER_SIZE);
if (use_nand_flash)
{
DBG("nand flash, write back to rootfs : 0x%x", rootfsInfo.fileSize);
writeToBinFile(p, rootfsInfo.fileSize, rootfsInfo.fileName);
}
}
if (mi.jffs2fsOffset != 0)
{
if (SUPPORT_SPMINIOS)
{
if (mi.rootfsOffset + mi.rootfsLen > layout->flashSize)
{
ERR("space left is not enough for jffs2");
exit(1);
}
if (use_nand_flash) {
mi.rootfsLen = ALIGN(rootfsInfo.fileSize, rootfsAlign);
}
else {
mi.rootfsLen = mi.jffs2fsOffset - mi.rootfsOffset;
}
DBG("rootfsLen: 0x%x", mi.rootfsLen);
mi.jffs2fsLen = layout->flashSize - USER_RECORD_PARTITION_SIZE - VERIFY_SIZE - mi.jffs2fsOffset;
#ifndef SLP_CAMERA_SUPPORT
/* SLP升级机型只指定jffs2 offset,不按照larger jffs2的方式进行固件打包 */
is_larger_jffs2 = 1;
#endif
if (mi.rootfsLen < rootfsInfo.fileSize)
{
ERR("space left is not enough for rootfs");
exit(1);
}
}
else
{
if (mi.jffs2fsLen == 0)
{
if (mi.statisticsOffset)
{
mi.jffs2fsLen = mi.statisticsOffset - mi.jffs2fsOffset;
}
else if (mi.exfsOffset)
{
mi.jffs2fsLen = mi.exfsOffset - mi.jffs2fsOffset;
}
else if (mi.upbootOffset)
{
mi.jffs2fsLen = mi.upbootOffset - mi.jffs2fsOffset;
}
else
{
mi.jffs2fsLen = layout->flashSize - mi.jffs2fsOffset;
}
}
if (0 != (mi.jffs2fsLen % FLASH_SECTOR_SIZE) || 0 != (mi.jffs2fsOffset % FLASH_SECTOR_SIZE))
{
ERR("fixed jffs2 offset or length is not on flash sector boundary");
ret = EXIT_FAILURE;
goto out_free_buf;
}
if (use_nand_flash)
{
mi.rootfsLen = ALIGN(rootfsInfo.fileSize, rootfsAlign);
DBG("rootfsLen: 0x%x", mi.rootfsLen);
}
else
{
if (mi.rootfsOffset + rootfsInfo.fileSize > mi.jffs2fsOffset
|| mi.jffs2fsOffset + mi.jffs2fsLen > layout->flashSize)
{
ERR("space left is not enough for fixed jffs2 length");
ret = EXIT_FAILURE;
goto out_free_buf;
}
mi.rootfsLen = mi.jffs2fsOffset - mi.rootfsOffset;
//ALIGN to flash sector_size,for factory_boot uip recovery
mi.rootfsLen = ALIGN(mi.rootfsOffset + rootfsInfo.fileSize, FLASH_SECTOR_SIZE) - mi.rootfsOffset;
DBG("rootfsLen:0x%x",mi.rootfsLen);
}
}
DBG("Jffs2Len:0x%x",mi.jffs2fsLen);
}
else
{
#if 0
if (use_nand_flash)
{
ERR("not adapt");
exit(1);
}
#endif
if (mi.rootfsOffset + mi.rootfsLen > layout->flashSize)
{
ERR("space left is not enough for jffs2");
exit(1);
}
if (SUPPORT_SPMINIOS)
{
mi.jffs2fsOffset = (layout->flashSize - USER_RECORD_PARTITION_SIZE - VERIFY_SIZE + mi.tpHeaderOffset) / 2;
mi.rootfsLen = mi.jffs2fsOffset - mi.rootfsOffset;
mi.jffs2fsLen = layout->flashSize - USER_RECORD_PARTITION_SIZE - VERIFY_SIZE - mi.jffs2fsOffset;
}
else
{
if (use_nand_flash)
{
mi.rootfsLen = ALIGN(rootfsInfo.fileSize, rootfsAlign);
DBG("rootfsLen: 0x%x", mi.rootfsLen);
}
else
{
/* jffs2offset 按64K对齐,如果不使用jffs2(IMG_JFFS2_OFFSET==0), 把jffs2fsLen设置为0
未指定IMG_JFFS2_OFFSET则认为按默认打包jffs2分区,指定jffs2区间为从rootfs结束到固件最后*/
mi.jffs2fsOffset = ALIGN(mi.rootfsOffset + rootfsInfo.fileSize, FLASH_SECTOR_SIZE);
mi.rootfsLen = mi.jffs2fsOffset - mi.rootfsOffset;
mi.jffs2fsLen = gUseJffs2 ? layout->flashSize - mi.jffs2fsOffset : 0;
}
}
}
if (SUPPORT_SPMINIOS)
{
p += mi.rootfsLen;
DBG("read servicefs to 0x%x", p - buf);
ret = readToBuf(&serviceFileInfo, p);
if (ret)
{
goto out_free_buf;
}
if (is_larger_jffs2)
{
mi.jffs2RealFsLen = serviceFileInfo.fileSize;
}
if (mi.jffs2fsLen < serviceFileInfo.fileSize)
{
ERR("space left is not enough for service");
exit(1);
}
#if 0
if(encrypt_rootfs_header)
{
i = _AES_cfb1_encrypt_rijndael(AES_CFB1_key, AES_KEY_LEN, AES_CFB1_iv, p, ENCRYPT_ROOTFS_HEADER_SIZE, encrypt_rootfs_header_buffer);
if(i != ENCRYPT_ROOTFS_HEADER_SIZE){
ERR("ENCRYPT_ROOTFS_HEADER Fail! (ret:%d != %d)\n", i , ENCRYPT_ROOTFS_HEADER_SIZE);
goto out_free_buf;
}
memcpy(p,encrypt_rootfs_header_buffer,ENCRYPT_ROOTFS_HEADER_SIZE);
if (use_nand_flash)
{
DBG("nand flash, write back to service : 0x%x", serviceFileInfo.fileSize);
writeToBinFile(p, serviceFileInfo.fileSize, serviceFileInfo.fileName);
}
}
#endif
}
/* 如果只使用factory_boot,那么将factory_boot.bin打包进upboot.bin的位置 */
if (1 == gNoPackUboot)
{
p = buf + mi.tpHeaderOffset - mi.bootloaderLen;
DBG("read bootloader to 0x%x",p - buf);
ret = readToBuf(&factorybootInfo, p);
if (ret)
{
goto out_free_buf;
}
}
else
{
p = buf + mi.bootloaderOffset;
DBG("read bootloader to 0x%x",p - buf);
ret = readToBuf(&bootloaderInfo, p);
if (ret)
{
goto out_free_buf;
}
}
if (gExtFwUpgrade)
{
if (use_nand_flash)
{
if (gNoPackUboot == 1)
{
p = buf + mi.tpHeaderOffset + mi.tpHeaderLen + mi.kernelLen + mi.rootfsLen;
}
else
{
p = buf + mi.bootloaderOffset + mi.bootloaderLen + mi.tpHeaderLen + mi.kernelLen + mi.rootfsLen;
}
/* 再加上service分区的大小 */
if (SUPPORT_SPMINIOS) {
p += serviceFileInfo.fileSize;
}
}
else
{
/* 计算升级固件block偏移位置 */
if (SUPPORT_SPMINIOS)
{
if (is_larger_jffs2)
{
p = buf + mi.jffs2fsOffset + serviceFileInfo.fileSize;
}
else
{
#ifdef SLP_CAMERA_SUPPORT
/* SLP升级机型在固件的jffs2结束和block之间预留verify分区 */
p = buf + layout->flashSize - USER_RECORD_PARTITION_SIZE;
#else
p = buf + layout->flashSize - VERIFY_SIZE - USER_RECORD_PARTITION_SIZE;
#endif
}
}
else
{
p = buf + mi.rootfsOffset + mi.rootfsLen;
}
}
DBG("read BlockHeader to 0x%x sizeof(BLOCK_HEADER):0x%x",p - buf, sizeof(BLOCK_HEADER));
memcpy(p, &gBlockHeader, sizeof(BLOCK_HEADER));
p += sizeof(BLOCK_HEADER);
/* 往up_boot.bin固件的jffs2fsOffset中写module_spec */
if (0 != gExtModuleSpecBlockNum)
{
ret = packExtModuleSpec(&gExtModuleSpecBlock, p);
if (ret)
{
goto out_free_buf;
}
p += ALIGN(gExtModuleSpecBlock.fileInfo.fileSize + sizeof(EXT_MODULE_SPEC_HEADER), 4);
}
/* 往up_boot.bin固件的jffs2fsOffset中写isp_config */
for (i = 0; i < gExtFwBlockNum; i++)
{
if (gExtFwBlock[i].add)
{
/* 将所有add配置项为true的 isp_config往up_boot.bin的jffs2fs分区写 */
ret = packExtFw(&gExtFwBlock[i], p, outputPath);
if (ret)
{
goto out_free_buf;
}
/* upboot 的 extFw 头部和文件长度累加,需要使用文件对齐后的长度 */
ext_fw_file_len += ALIGN(gExtFwBlock[i].fileInfo.fileSize + sizeof(EXT_FW_HEADER), 4);
p += ALIGN(gExtFwBlock[i].fileInfo.fileSize + sizeof(EXT_FW_HEADER), 4);
}
}
if (use_nand_flash)
{
/* FwID md5 content: bootloader + tpHeader + kernel + rootfs + (servicefs ? )+ extBlock */
fillTpHeader(fwBase + uhTagLength + mi.fdtLen, mi.bootloaderLen + mi.tpHeaderLen + mi.kernelLen + mi.rootfsLen + (SUPPORT_SPMINIOS ? serviceFileInfo.fileSize : 0) + \
sizeof(BLOCK_HEADER) + gExtModuleSpecBlockLen + gExtFwBlockLen);
DBG("fill tpheader done %s()-%d", __FUNCTION__, __LINE__);
fwLen = uhTagLength + mi.fdtLen + mi.bootloaderLen;
fwLen += mi.tpHeaderLen + mi.kernelLen + mi.rootfsLen;
fwLen += (SUPPORT_SPMINIOS ? serviceFileInfo.fileSize : 0);
fwLen += sizeof(BLOCK_HEADER) + gExtModuleSpecBlockLen + gExtFwBlockLen;
DBG("3004:mi.fdtLen is %d", mi.fdtLen);
DBG("up_boot fwLen:0x%x", fwLen);
DBG("up_boot (p - fwBase):0x%x", (p - fwBase));
if (fwLen != (p - fwBase))
{
ERR("fwLen is wrong!!! pls fix!!!");
exit(1);
}
}
/* FwID md5 content: bootloader + tpHeader + kernel + romfs + extFwBlock */
else
{
if (SUPPORT_SPMINIOS)
{
fillTpHeader(fwBase + uhTagLength + mi.fdtLen, fwLen - uhTagLength - mi.fdtLen - VERIFY_SIZE - USER_RECORD_PARTITION_SIZE);
}
else
{
fillTpHeader(fwBase + uhTagLength + mi.fdtLen, fwLen - uhTagLength - mi.fdtLen - mi.jffs2fsLen - (mi.jffs2fsOffset - mi.rootfsOffset - mi.rootfsLen));
}
DBG("fill tpheader done %s()-%d", __FUNCTION__, __LINE__);
}
}
else
{
if(use_nand_flash)
{
ERR("not adapt,if adapt pls attention fwLen");
exit(1);
}
else
{
p = buf + mi.jffs2fsOffset;
DBG("read BlockHeader to 0x%x sizeof(BLOCK_HEADER):0x%x",p - buf, sizeof(BLOCK_HEADER));
memcpy(p, &gBlockHeader, sizeof(BLOCK_HEADER));
p += sizeof(BLOCK_HEADER);
/* 往up_boot.bin固件的jffs2fsOffset中写module_spec */
if (0 != gExtModuleSpecBlockNum)
{
ret = packExtModuleSpec(&gExtModuleSpecBlock, p);
if (ret)
{
goto out_free_buf;
}
p += ALIGN(gExtModuleSpecBlock.fileInfo.fileSize + sizeof(EXT_MODULE_SPEC_HEADER), 4);
}
/* FwID md5 content: bootloader + tpHeader + kernel + romfs */
if (SUPPORT_SPMINIOS)
{
fillTpHeader(fwBase + uhTagLength + mi.fdtLen, fwLen - uhTagLength - mi.fdtLen);
}
else
{
fillTpHeader(fwBase + uhTagLength + mi.fdtLen, fwLen - uhTagLength - mi.fdtLen - mi.jffs2fsLen);
}
DBG("fill tpheader done %s()-%d", __FUNCTION__, __LINE__);
}
}
if(use_nand_flash)
{
snprintf(tpheaderName, FIRMWARE_NAME_MAXLEN, "%s/%s", outputPath, "tpheader.bin");
DBG("write tpheader.bin");
writeToBinFile(fwBase + uhTagLength + mi.fdtLen + mi.bootloaderLen, mi.tpHeaderLen, tpheaderName);
}
if (gUpbootBuildTimes != 1)
{
if (!use_nand_flash)
{
fwLen = uhTagLength + mi.fdtLen + mi.bootloaderLen;
fwLen += mi.jffs2fsOffset - mi.tpHeaderOffset + sizeof(BLOCK_HEADER) + gExtModuleSpecBlockLen + ext_fw_file_len;
fwLen -= (mi.jffs2fsOffset - mi.rootfsOffset - mi.rootfsLen);
if (SUPPORT_SPMINIOS)
{
if (is_larger_jffs2)
{
fwLen += serviceFileInfo.fileSize;
DBG("block offset:0x%x\n", fwLen-(sizeof(BLOCK_HEADER) + gExtModuleSpecBlockLen + ext_fw_file_len));
}
else
{
fwLen += mi.jffs2fsLen;
#ifdef SLP_CAMERA_SUPPORT
/* SLP机型在jffs2结束和block之间多预留了verify分区的大小,计算固件长度时需要加上这部分 */
fwLen += VERIFY_SIZE;
#endif
}
}
}
ret = writeFw(fwBase, fwLen, outputPath, "up_boot.bin", TRUE);
if (ret != EXIT_SUCCESS)
{
goto out_free_buf;
}
if(gUpbootBuildTimes == 0)
{
gUpbootBuildTimes = 1;
}
}
/* then deal with flash.bin */
if (1 == gNoPackUboot)
{
/* clear up header and factory_boot filled in up_boot.bin */
memset(fwBase, 0xff, uhTagLength + mi.fdtLen + mi.bootloaderLen);
}
else
{
/* clear up header filled in up_boot.bin */
memset(fwBase, 0xff, uhTagLength + mi.fdtLen);
}
fwBase = buf;
fwLen =layout->flashSize;
DBG("reset to 0xff from:0x%x len:0x%x", mi.rootfsOffset + mi.rootfsLen, mi.jffs2fsOffset - mi.rootfsOffset - mi.rootfsLen);
if(mi.jffs2fsOffset - mi.rootfsOffset - mi.rootfsLen > 0)
{
ERR("rootfs is not aligned");
memset(buf + mi.rootfsOffset + mi.rootfsLen, 0xff , mi.jffs2fsOffset - mi.rootfsOffset - mi.rootfsLen);
}
/* 往flash.bin中写入default isp_config */
if (gExtFwUpgrade)
{
p = NULL;
for (i = 0; i < gExtFwBlockNum; i++)
{
/* 将 default extFw 写入 radio 分区,即往 flash.bin 写入 default isp_config */
if (0 == strcasecmp(gExtFwBlock[i].deviceName, DEFAULT_EXT_FW_NAME))
{
p = buf + mi.radioOffset;
if (useLittleFs)
{
if (gExtFwBlock[i].lfsFileInfo.fileSize > mi.radioLen)
{
DBG("radio sector[size 0x%08x] is not enough to write isp file [size 0x%08x]",
mi.radioLen, gExtFwBlock[i].fileInfo.fileSize);
ret = EXIT_FAILURE;
goto out_free_buf;
}
ret = packExtLfsFw(&gExtFwBlock[i], p, outputPath);
if (ret)
{
goto out_free_buf;
}
}
else
{
if (sizeof(EXT_FW_HEADER) + gExtFwBlock[i].fileInfo.fileSize > mi.radioLen)
{
DBG("radio sector[size 0x%08x] is not enough to write isp file [size 0x%08x]",
mi.radioLen,
sizeof(EXT_FW_HEADER) + gExtFwBlock[i].fileInfo.fileSize);
ret = EXIT_FAILURE;
goto out_free_buf;
}
ret = packExtFw(&gExtFwBlock[i], p, outputPath);
if (ret)
{
goto out_free_buf;
}
}
}
}
/* 如果 ext_fw.config中 没有找到 default,那么往flash.bin的radio写入ext_Fw.config 中的第一个 block */
if (NULL == p)
{
if (useLittleFs)
{
if (gExtFwBlock[0].lfsFileInfo.fileSize > mi.radioLen)
{
DBG("radio sector[size 0x%08x] is not enough to write isp file [size 0x%08x]",
mi.radioLen, gExtFwBlock[0].fileInfo.fileSize);
ret = EXIT_FAILURE;
goto out_free_buf;
}
ret = packExtLfsFw(&gExtFwBlock[0], buf + mi.radioOffset, outputPath);
if (ret)
{
goto out_free_buf;
}
}
else
{
if (sizeof(EXT_FW_HEADER) + gExtFwBlock[0].fileInfo.fileSize > mi.radioLen)
{
DBG("radio sector[size 0x%08x] is not enough to write isp file [size 0x%08x]",
mi.radioLen,
sizeof(EXT_FW_HEADER) + gExtFwBlock[0].fileInfo.fileSize);
ret = EXIT_FAILURE;
goto out_free_buf;
}
ret = packExtFw(&gExtFwBlock[0], buf + mi.radioOffset, outputPath);
if (ret)
{
goto out_free_buf;
}
}
/* nand flash生成一份默认isp参数,便于estar命令直接写入flash */
if (use_nand_flash)
{
snprintf(ispConfigName, FIRMWARE_NAME_MAXLEN, "%s/isp_config/%s.isp_config", outputPath, gExtFwBlock[0].deviceName);
snprintf(ispDefaultName, FIRMWARE_NAME_MAXLEN, "%s/isp_config/%s.isp_config", outputPath, DEFAULT_EXT_FW_NAME);
snprintf(cp_cmd, 1024, "cp \"%s\" \"%s\"", ispConfigName, ispDefaultName);
system(cp_cmd);
}
}
}
/* 由于打包 up_boot 时填入 ispconfig,需要将 jffs2 写入deadcode */
if (USER_RECORD_PARTITION_SIZE > 0)
{
if (0 == use_nand_flash)
{
if (gUseJffs2)
{
ERR("\n=============================Now not support to enable both CONFIG_USES_JFFS2 and CONFIG_TAPO_USR_DEF_AUDIO_ALARM!!! Please check.============\n");
abort();
}
user_record_alarm_offset = layout->flashSize - VERIFY_SIZE - USER_RECORD_PARTITION_SIZE;
if (NULL == jffs2FileInfo.fileName || 0 == jffs2FileInfo.fileSize)
{
memcpy(buf + user_record_alarm_offset, jffs2EofMark, sizeof(jffs2EofMark));
}
else
{
if (readToBuf(&jffs2FileInfo, buf + user_record_alarm_offset))
{
goto out_free_buf;
}
}
}
else
{
DBG("\n==========use nand flash?!!!!!!!============\n");
}
}
else
{
if (gUseJffs2 && use_nand_flash == 0)
{
if (jffs2FileInfo.fileName == NULL || jffs2FileInfo.fileSize == 0)
{
if (SUPPORT_SPMINIOS)
{
}
else
{
memcpy(buf + mi.jffs2fsOffset, jffs2EofMark, sizeof(jffs2EofMark));
}
}
else
{
if (jffs2FileInfo.fileSize > mi.jffs2fsLen)
{
ERR("not enough space for jffs2.");
goto out_free_buf;
}
if (readToBuf(&jffs2FileInfo, buf + mi.jffs2fsOffset))
{
goto out_free_buf;
}
}
}
}
/* fill factory boot */
p = buf;
ret = readToBuf(&factorybootInfo, p);
if (ret)
{
goto out_free_buf;
}
/* prefill factory info area */
p = buf + mi.factoryInfoOffset;
facinfo_len = fillFactoryInfo(p, devName);
if (facinfo_len == -1)
{
goto out_free_buf;
}
if(use_nand_flash)
{
snprintf(factoryInfoName, FIRMWARE_NAME_MAXLEN, "%s/%s", outputPath, "factory_info.bin");
DBG("write factory_info.bin");
writeToBinFile(p, facinfo_len, factoryInfoName);
}
/* fill uc partition with uc file */
p = buf + mi.ucOffset;
ret = readToBuf(&ucFileInfo, p);
if (ret)
{
goto out_free_buf;
}
if(use_nand_flash)
{
;//dont pack flash.bin image
}
else
{
ret = writeFw(fwBase, fwLen, outputPath, "flash.bin", FALSE);
if (ret != EXIT_SUCCESS)
{
goto out_free_buf;
}
}
DBG("make firmware done");
ret = EXIT_SUCCESS;
out_free_buf:
if (buf)
{
free(buf);
}
out:
return ret;
}详细解析一下
最新发布