#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "cmdopt.h" #include "image.h" #include "output.h" #include "modbmst.h" #include "error.h" ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// #define _ABBR_BOOTLOADER "Boot" #define _ABBR_APPLICATION "Appl" #define _ABBR_MININET "MiNe" #define _ABBR_MODBUS "ModB" ///////////////////////////////////////////////////////////////////////////// static bool g_bSig = false; ///////////////////////////////////////////////////////////////////////////// static void _SigHandler(int sig) { g_bSig = !!sig; TRACE3("\n"); } ///////////////////////////////////////////////////////////////////////////// static int _ShowDevImgInfo(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx) { int nRet; GFA_BL_APP_IMG_INFO aii; UNUSED(hIf); TRACE3("Getting image information.\n"); TTRACE3("Getting image information"); if((ctx == GfaBlmCtx_App) && ((nRet = GfaBlmGetInfoBI(hBlm, pcla->nNodeAddr, &aii)) == 0)) { GfaTfuDumpImageInfo("Bootloader", &aii.bl); GfaTfuDumpImageInfo("Application", &aii.app); if( (aii.bl.nImgLength != 0xFFFFFFFF) && (aii.bl.nImgCRC32 != 0xFFFFFFFF) && (*aii.bl.szImgMaterialNum != 0xFF) && (*aii.bl.szImgNameBuild != 0xFF)) { TTRACE(PLUGIN_TAG_IMG_LENGTH_BOOT, "%u", aii.bl.nImgLength); TTRACE(PLUGIN_TAG_IMG_CRC32_BOOT, "%u", aii.bl.nImgCRC32); TTRACE(PLUGIN_TAG_IMG_MATERIAL_BOOT, "%s", aii.bl.szImgMaterialNum); TTRACE(PLUGIN_TAG_IMG_BUILD_BOOT, "%s", aii.bl.szImgNameBuild); } else { TTRACE(PLUGIN_TAG_IMG_LENGTH_BOOT, "%u", 0); TTRACE(PLUGIN_TAG_IMG_CRC32_BOOT, "%u", 0); TTRACE(PLUGIN_TAG_IMG_MATERIAL_BOOT, "%s", "n/a"); TTRACE(PLUGIN_TAG_IMG_BUILD_BOOT, "%s", "n/a"); } if( (aii.app.nImgLength != 0xFFFFFFFF) && (aii.app.nImgCRC32 != 0xFFFFFFFF) && (*aii.app.szImgMaterialNum != 0xFF) && (*aii.app.szImgNameBuild != 0xFF)) { TTRACE(PLUGIN_TAG_IMG_LENGTH_APP, "%u", aii.app.nImgLength); TTRACE(PLUGIN_TAG_IMG_CRC32_APP, "%u", aii.app.nImgCRC32); TTRACE(PLUGIN_TAG_IMG_MATERIAL_APP, "%s", aii.app.szImgMaterialNum); TTRACE(PLUGIN_TAG_IMG_BUILD_APP, "%s", aii.app.szImgNameBuild); } else { TTRACE(PLUGIN_TAG_IMG_LENGTH_APP, "%u", 0); TTRACE(PLUGIN_TAG_IMG_CRC32_APP, "%u", 0); TTRACE(PLUGIN_TAG_IMG_MATERIAL_APP, "%s", "n/a"); TTRACE(PLUGIN_TAG_IMG_BUILD_APP, "%s", "n/a"); } } else { bool bStartApp = false; if(ctx == GfaBlmCtx_App) { TRACE3("Application doesn't recognize BI command!\n"); TRACE3("Starting bootloader.\n"); TTRACE3("Application doesn't recognize BI command"); TTRACE3("Starting bootloader"); if((nRet = GfaBlmBootloaderExecute(hBlm, pcla->nNodeAddr, NULL, 1000)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } bStartApp = true; } if(pcla->nExBaudrate != pcla->nInitBaudrate) { TRACE3("Setting baud-rate to %u.\n", pcla->nExBaudrate); TTRACE3("Setting baud-rate to %u", pcla->nExBaudrate); if((nRet = GfaBlmBootloaderSetBaudrate(hBlm, pcla->nNodeAddr, pcla->nExBaudrate)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } } if((nRet = GfaBlmGetInfoBD(hBlm, pcla->nNodeAddr, pcla->nStartAddr, &aii)) > -2) { GfaTfuDumpImageInfo("Bootloader", &aii.bl); GfaTfuDumpImageInfo("Application", &aii.app); if( (aii.bl.nImgLength != 0xFFFFFFFF) && (aii.bl.nImgCRC32 != 0xFFFFFFFF) && (*aii.bl.szImgMaterialNum != 0xFF) && (*aii.bl.szImgNameBuild != 0xFF)) { TTRACE(PLUGIN_TAG_IMG_LENGTH_BOOT, "%u", aii.bl.nImgLength); TTRACE(PLUGIN_TAG_IMG_CRC32_BOOT, "%u", aii.bl.nImgCRC32); TTRACE(PLUGIN_TAG_IMG_MATERIAL_BOOT, "%s", aii.bl.szImgMaterialNum); TTRACE(PLUGIN_TAG_IMG_BUILD_BOOT, "%s", aii.bl.szImgNameBuild); } else { TTRACE(PLUGIN_TAG_IMG_LENGTH_BOOT, "%u", 0); TTRACE(PLUGIN_TAG_IMG_CRC32_BOOT, "%u", 0); TTRACE(PLUGIN_TAG_IMG_MATERIAL_BOOT, "%s", "n/a"); TTRACE(PLUGIN_TAG_IMG_BUILD_BOOT, "%s", "n/a"); } if( (aii.app.nImgLength != 0xFFFFFFFF) && (aii.app.nImgCRC32 != 0xFFFFFFFF) && (*aii.app.szImgMaterialNum != 0xFF) && (*aii.app.szImgNameBuild != 0xFF)) { TTRACE(PLUGIN_TAG_IMG_LENGTH_APP, "%u", aii.app.nImgLength); TTRACE(PLUGIN_TAG_IMG_CRC32_APP, "%u", aii.app.nImgCRC32); TTRACE(PLUGIN_TAG_IMG_MATERIAL_APP, "%s", aii.app.szImgMaterialNum); TTRACE(PLUGIN_TAG_IMG_BUILD_APP, "%s", aii.app.szImgNameBuild); } else { TTRACE(PLUGIN_TAG_IMG_LENGTH_APP, "%u", 0); TTRACE(PLUGIN_TAG_IMG_CRC32_APP, "%u", 0); TTRACE(PLUGIN_TAG_IMG_MATERIAL_APP, "%s", "n/a"); TTRACE(PLUGIN_TAG_IMG_BUILD_APP, "%s", "n/a"); } } else { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } if(pcla->nExBaudrate != pcla->nInitBaudrate) { TRACE3("Setting baud-rate to %u.\n", pcla->nInitBaudrate); TTRACE3("Setting baud-rate to %u", pcla->nInitBaudrate); if((nRet = GfaBlmBootloaderSetBaudrate(hBlm, pcla->nNodeAddr, pcla->nInitBaudrate)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } } if(bStartApp) { TRACE3("Starting application.\n"); TTRACE3("Starting application"); if((nRet = GfaBlmBUCmdReset(hBlm, pcla->nNodeAddr, pcla->nInitBaudrate)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } } } return 0; } ///////////////////////////////////////////////////////////////////////////// static int _ValidateImg(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx) { int nRet; GFA_IMG_INFO ii; char szMaterial[16], szSerial[16]; UNUSED(ctx); TRACE3("Reading material number.\n"); TTRACE3("Reading material number"); if((nRet = GfaBlmReadMaterialAndSerialID(hBlm, pcla->nNodeAddr, szMaterial, sizeof(szMaterial), szSerial, sizeof(szSerial))) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } if(szMaterial[0] == '\xFF') strcpy(szMaterial, "n/a"); if(szSerial[0] == '\xFF') strcpy(szSerial, "n/a"); TTRACE(PLUGIN_TAG_IMG_MATERIAL_EEPROM, szMaterial); TTRACE(PLUGIN_TAG_IMG_SERIAL_EEPROM, szSerial); GfaTfuImageFileGetInfo(hIf, &ii); TRACE2("Material number target: \"%s\"\n", szMaterial); TRACE2("Material number file: \"%s\"\n", ii.szImgMaterialNum); TTRACE2("Material number target: \"%s\"", szMaterial); TTRACE2("Material number file: \"%s\"", ii.szImgMaterialNum); if(!GfaTfuImageFileMatchMaterialNum(hIf, szMaterial)) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return -1; } TRACE2("\"%s\" - Image is valid for target @ node 0x%02hhX!\n", pcla->pszImgFileBase, pcla->nNodeAddr); TTRACE2("\"%s\" - Image is valid for target @ node 0x%02hhX", pcla->pszImgFileBase, pcla->nNodeAddr); return 0; } ///////////////////////////////////////////////////////////////////////////// static int _ShowMatSer(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx) { int nRet; char szMaterial[GFA_APP_MAX_IMG_MATERIAL_NUM_LENGTH], szSerial[GFA_APP_MAX_IMG_SERIAL_NUM_LENGTH]; UNUSED(hIf); UNUSED(ctx); TRACE3("Reading material and serial number.\n"); TTRACE3("Reading material and serial number"); if((nRet = GfaBlmReadMaterialAndSerialID(hBlm, pcla->nNodeAddr, szMaterial, sizeof(szMaterial), szSerial, sizeof(szSerial))) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } if(szMaterial[0] == '\xFF') strcpy(szMaterial, "n/a"); if(szSerial[0] == '\xFF') strcpy(szSerial, "n/a"); TRACE2("Material and Serial number:\n"); TRACE2(" Material: \"%s\"\n", szMaterial); TRACE2(" Serial: \"%s\"\n", szSerial); TTRACE(PLUGIN_TAG_IMG_MATERIAL_EEPROM, szMaterial); TTRACE(PLUGIN_TAG_IMG_SERIAL_EEPROM, szSerial); return nRet; } ///////////////////////////////////////////////////////////////////////////// static int _SetMatSer(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx) { int nRet; bool bStartApp = false; if(ctx == GfaBlmCtx_App) { TRACE3("Starting bootloader.\n"); if((nRet = GfaBlmBootloaderExecute(hBlm, pcla->nNodeAddr, NULL, 1000)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } bStartApp = true; } TRACE3("Setting material and serial number.\n"); TRACE2(" Mat.Nr.: \"%s\"\n", pcla->pszMaterial); TRACE2(" Serial: \"%s\"\n", pcla->pszSerial); if((nRet = GfaBlmWriteMaterialAndSerialID(hBlm, pcla->nNodeAddr, pcla->pszMaterial, pcla->pszSerial)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } TRACE2("Material and serial number successfully set!\n"); if(bStartApp) { TRACE3("Starting application.\n"); if((nRet = GfaBlmBUCmdReset(hBlm, pcla->nNodeAddr, pcla->nInitBaudrate)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } } return _ShowMatSer(hIf, hBlm, pcla, ctx); } ///////////////////////////////////////////////////////////////////////////// static int _Ping(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx) { int nRet; time_t t; char szTime[64]; struct timespec tsStart, tsEnd, ts = {0, 0}; int64_t nIntervalBoot = 0, nIntervalMini = 0, nIntervalModB = 0; HGFAMINEMST hMst = GfaBlmGetMininetMasterHandle(hBlm); HMINETDEV hDev = GfaMininetMasterGetDeviceHandle(hMst); HGFAMBMST hMbm = NULL; GFA_SER_CFG_PARAMS scp, scpSave; UNUSED(hIf); if(ctx == GfaBlmCtx_ModB) { if(!(hMbm = GfaMbMstOpen(hMst, pcla->nModbBaudrate, pcla->modbParity, pcla->nModbusCtrlReg))) return -1; GfaMbMstSetVerbosity(hMbm, pcla->nVerbosity); if(GfaMininetDeviceGetConfigParams(hDev, &scp, sizeof(scp)) != sizeof(scp)) { GfaMbMstClose(hMbm); return -1; } memcpy(&scpSave, &scp, sizeof(scpSave)); scp.parity = pcla->modbParity; scp.baud = pcla->nModbBaudrate; if(GfaMininetDeviceSetConfigParams(hDev, &scp, sizeof(scp)) != 0) { GfaMbMstClose(hMbm); return -1; } } do { t = time(NULL); strftime(szTime, sizeof(szTime), "%T", localtime(&t)); GfaTfuGetClock(&tsStart); if(ctx == GfaBlmCtx_ModB) { if((nRet = GfaMbMstPing(hMbm, pcla->nModbusSlvID)) == 0) { GfaTfuGetClock(&tsEnd); nIntervalModB = GfaTfuClockDiff(&tsEnd, &tsStart); TRACE2("Slave 0x%02hhX [%s] ping success [%.1f ms] - %s.\n", pcla->nModbusSlvID, _ABBR_MODBUS, (double)nIntervalModB / 1000000.0, szTime); TTRACE2("Slave 0x%02hhX [%s] ping success [%.1f ms] - %s", pcla->nModbusSlvID, _ABBR_MODBUS, (double)nIntervalModB / 1000000.0, szTime); } else { TRACE1("Slave 0x%02hhX [%s] ping error: %s - %s.\n", pcla->nModbusSlvID, _ABBR_MODBUS, GfaTfuStrError(errno), szTime); TTRACE1("Slave 0x%02hhX [%s] ping error: %s - %s", pcla->nModbusSlvID, _ABBR_MODBUS, GfaTfuStrError(errno), szTime); } } else if((nRet = GfaBlmMininetPing(hBlm, pcla->nNodeAddr)) == 0) { GfaTfuGetClock(&tsEnd); nIntervalMini = GfaTfuClockDiff(&tsEnd, &tsStart); GfaTfuGetClock(&tsStart); if((nRet = GfaBlmBUCmdPing(hBlm, pcla->nNodeAddr)) != 0) { if( (ctx == GfaBlmCtx_Boot) || (errno != -MINET_SLAVE_STATUS_INDEX_INVALID_PARAM)) { if(errno == -MINET_SLAVE_STATUS_INDEX_ERROR) { if((nRet = GfaBlmResetSlaveIndex(hBlm, pcla->nNodeAddr)) != 0) { TRACE1("Node 0x%02hhX [%s] ping error: %s - %s.\n", pcla->nNodeAddr, (ctx == GfaBlmCtx_Boot) ? _ABBR_BOOTLOADER : _ABBR_APPLICATION, GfaTfuStrError(errno), szTime); TTRACE1("Node 0x%02hhX [%s] ping error: %s - %s", pcla->nNodeAddr, (ctx == GfaBlmCtx_Boot) ? _ABBR_BOOTLOADER : _ABBR_APPLICATION, GfaTfuStrError(errno), szTime); break; } if((ctx = GfaBlmGetExecutionContext(hBlm, pcla->nNodeAddr)) == GfaBlmCtx_Err) { TRACE1("Node 0x%02hhX ping error: %s - %s.\n", pcla->nNodeAddr, GfaTfuStrError(errno), szTime); TTRACE1("Node 0x%02hhX ping error: %s - %s", pcla->nNodeAddr, GfaTfuStrError(errno), szTime); break; } continue; } } else { nRet = 0; } } if(nRet == 0) { GfaTfuGetClock(&tsEnd); nIntervalBoot = GfaTfuClockDiff(&tsEnd, &tsStart); TRACE2("Node 0x%02hhX [%s] ping success [%.1f ms] - %s.\n", pcla->nNodeAddr, (ctx == GfaBlmCtx_Boot) ? _ABBR_BOOTLOADER : _ABBR_APPLICATION, (double)nIntervalBoot / 1000000.0, szTime); TTRACE2("Node 0x%02hhX [%s] ping success [%.1f ms] - %s", pcla->nNodeAddr, (ctx == GfaBlmCtx_Boot) ? _ABBR_BOOTLOADER : _ABBR_APPLICATION, (double)nIntervalBoot / 1000000.0, szTime); } else { TRACE2("Node 0x%02hhX [%s] ping success [%.1f ms] - %s.\n", pcla->nNodeAddr, _ABBR_MININET, (double)nIntervalMini / 1000000.0, szTime); TTRACE2("Node 0x%02hhX [%s] ping success [%.1f ms] - %s", pcla->nNodeAddr, _ABBR_MININET, (double)nIntervalMini / 1000000.0, szTime); TRACE1("Node 0x%02hhX [%s] ping error: %s - %s.\n", pcla->nNodeAddr, (ctx == GfaBlmCtx_Boot) ? _ABBR_BOOTLOADER : _ABBR_APPLICATION, GfaTfuStrError(errno), szTime); TTRACE1("Node 0x%02hhX [%s] ping error: %s - %s", pcla->nNodeAddr, (ctx == GfaBlmCtx_Boot) ? _ABBR_BOOTLOADER : _ABBR_APPLICATION, GfaTfuStrError(errno), szTime); } } else { if(errno == -MINET_SLAVE_STATUS_INDEX_ERROR) { if((nRet = GfaBlmResetSlaveIndex(hBlm, pcla->nNodeAddr)) != 0) { TRACE1("Node 0x%02hhX [%s] ping error: %s - %s.\n", pcla->nNodeAddr, (ctx == GfaBlmCtx_Boot) ? _ABBR_BOOTLOADER : _ABBR_APPLICATION, GfaTfuStrError(errno), szTime); TTRACE1("Node 0x%02hhX [%s] ping error: %s - %s", pcla->nNodeAddr, (ctx == GfaBlmCtx_Boot) ? _ABBR_BOOTLOADER : _ABBR_APPLICATION, GfaTfuStrError(errno), szTime); break; } continue; } TRACE1("Node 0x%02hhX [%s] ping error: %s - %s.\n", pcla->nNodeAddr, _ABBR_MININET, GfaTfuStrError(errno), szTime); TTRACE1("Node 0x%02hhX [%s] ping error: %s - %s", pcla->nNodeAddr, _ABBR_MININET, GfaTfuStrError(errno), szTime); } if(pcla->nPingIntervalSec > 0) { ts.tv_sec = pcla->nPingIntervalSec; if(nanosleep(&ts, NULL) < 0) break; } } while(pcla->nPingIntervalSec && !g_bSig); if(hMbm) { GfaMbMstClose(hMbm); if(GfaMininetDeviceSetConfigParams(hDev, &scpSave, sizeof(scpSave)) != 0) return -1; } return nRet; } ///////////////////////////////////////////////////////////////////////////// static int _UploadImg(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx) { int nRet; uint32_t nCntFlashPages; size_t nImgLength; const void *pImgData; if((nRet = _ValidateImg(hIf, hBlm, pcla, ctx)) == 0) { if(ctx == GfaBlmCtx_App) { TRACE3("Starting bootloader.\n"); TTRACE3("Starting bootloader"); if((nRet = GfaBlmBootloaderExecute(hBlm, pcla->nNodeAddr, NULL, 1000)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } } if(pcla->nExBaudrate != pcla->nBootBaudrate) { TRACE3("Setting baud-rate to %u.\n", pcla->nExBaudrate); TTRACE3("Setting baud-rate to %u", pcla->nExBaudrate); if((nRet = GfaBlmBootloaderSetBaudrate(hBlm, pcla->nNodeAddr, pcla->nExBaudrate)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } } pImgData = GfaTfuImageFileGetData(hIf); nImgLength = GfaTfuImageFileGetSize(hIf); nCntFlashPages = (nImgLength + GFA_BOOTLOADER_FLASH_PAGE_SIZE - 1) / GFA_BOOTLOADER_FLASH_PAGE_SIZE; if((nRet = GfaBlmBUCmdDownload(hBlm, pcla->nNodeAddr, pcla->nStartAddr, nImgLength, nCntFlashPages * pcla->nPageErsaeTime)) == 0) { if((nRet = GfaBlmBUCmdSendData(hBlm, pcla->nNodeAddr, pImgData, nImgLength, pcla->nBlockSize)) != 0) { if(pcla->nExBaudrate != pcla->nBootBaudrate) { TRACE3("Trying to reset baud-rate to %u.\n", pcla->nBootBaudrate); TTRACE3("Trying to reset baud-rate to %u", pcla->nBootBaudrate); if((nRet = GfaBlmBootloaderSetBaudrate(hBlm, pcla->nNodeAddr, pcla->nBootBaudrate)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); } } return -1; } } else { if(pcla->nExBaudrate != pcla->nBootBaudrate) { TRACE3("Trying to reset baud-rate to %u.\n", pcla->nBootBaudrate); TTRACE3("Trying to reset baud-rate to %u", pcla->nBootBaudrate); if((nRet = GfaBlmBootloaderSetBaudrate(hBlm, pcla->nNodeAddr, pcla->nBootBaudrate)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); } } return -1; } TRACE3("Starting application.\n"); TTRACE3("Starting application"); if((nRet = GfaBlmBUCmdReset(hBlm, pcla->nNodeAddr, pcla->nInitBaudrate)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } } return nRet; } ///////////////////////////////////////////////////////////////////////////// static int _ResetBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx) { int nRet; UNUSED(hIf); if(ctx == GfaBlmCtx_App) { TRACE2("Application already executing!\n"); TTRACE2("Application already executing"); return 0; // no error } TRACE3("Resetting bootloader.\n"); TTRACE3("Resetting bootloader"); if((nRet = GfaBlmBUCmdReset(hBlm, pcla->nNodeAddr, pcla->nInitBaudrate)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } return 0; } ///////////////////////////////////////////////////////////////////////////// static int _ReviveBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx, bool bImplicitCall) { int nLen, nRet = 0, nCnt = 0; uint8_t nStatus; struct timespec ts = {0, 250000000}; UNUSED(hIf); if(ctx == GfaBlmCtx_App) { TRACE1("Bootloader not executing!\n"); TTRACE1("Bootloader not executing"); return 0; } uint8_t data[128], frm[256], rx[256], cmd[256]; size_t nCbData = sizeof(data); HGFAMINEMST hMst = GfaBlmGetMininetMasterHandle(hBlm); memset(data, 0, sizeof(data)); if(!bImplicitCall) { TRACE2("Reviving bootloader.\n"); TTRACE2("Reviving bootloader"); } while(!g_bSig) { nLen = GfaBlmBuildCmdDataPacket("BU", 0, data, nCbData, cmd, sizeof(cmd), false); nLen = GfaMininetMasterBuildFrame(hMst, pcla->nNodeAddr, 0, cmd, nLen, frm, sizeof(frm)); if((nRet = GfaMininetMasterTransmitFrame(hMst, frm, nLen)) != nLen) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return -1; } if((nRet = GfaMininetMasterReceiveFrame(hMst, rx, sizeof(rx), true)) <= 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return -1; } nRet = GfaMininetMasterEvaluateSlaveResponse(hMst, pcla->nNodeAddr, rx, nRet, true, &nStatus); if(nRet == MINET_SLAVE_RESPONSE_INDEX_IS_STATUS_CODE) { TRACE1("Error: %s!\n", GfaTfuStrError(-nStatus)); TTRACE1(GfaTfuStrError(-nStatus)); return -1; } else if(nRet == MINET_SLAVE_RESPONSE_SUCCESS) { nCbData = 0; } else if(nRet == MINET_SLAVE_RESPONSE_ACK) { nCbData = sizeof(data); ++nCnt; } if(nCnt >= 3) return 0; if(nanosleep(&ts, NULL) < 0) break; } return nRet; } ///////////////////////////////////////////////////////////////////////////// static int _ModbusStartBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx) { HGFAMINEMST hMst = GfaBlmGetMininetMasterHandle(hBlm); UNUSED(hIf); UNUSED(ctx); TRACE3("Starting bootloader.\n"); TTRACE3("Starting bootloader"); if(hMst) { HGFAMBMST hMbm = GfaMbMstOpen(hMst, pcla->nModbBaudrate, pcla->modbParity, pcla->nModbusCtrlReg); if(hMbm) { int nRet; GfaMbMstSetVerbosity(hMbm, pcla->nVerbosity); if((nRet = GfaMbMstBootloaderExecute(hMbm, pcla->nModbusSlvID)) == 0) { if(pcla->nNodeAddr) GfaMininetMasterResetLocalIndex(hMst, pcla->nNodeAddr); } else { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); } GfaMbMstClose(hMbm); return nRet; } } TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return -1; } ///////////////////////////////////////////////////////////////////////////// static int _StartBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx) { int nRet; if(ctx == GfaBlmCtx_Boot) { TRACE2("Bootloader already executing!\n"); TTRACE2("Bootloader already executing"); return 0; // no error } else if(ctx == GfaBlmCtx_ModB) { return _ModbusStartBootloader(hIf, hBlm, pcla, ctx); } TRACE3("Starting bootloader.\n"); TTRACE3("Starting bootloader"); if((nRet = GfaBlmBootloaderExecute(hBlm, pcla->nNodeAddr, NULL, 1000)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); return nRet; } return 0; } ///////////////////////////////////////////////////////////////////////////// static bool _AutoBaud(HGFABLM hBlm, LPCMD_LINE_ARGS pcla) { const uint32_t nBaudrates[] = {19200, 9600, 38400, 57600, 115200, 230400, 4800, 460800/*, 921600, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400*/}; const uint8_t nParities[] = {'N', 'E', 'O'}; size_t nCntParities = pcla->bForceAllParities ? _countof(nParities) : 1; HGFAMINEMST hMst = GfaBlmGetMininetMasterHandle(hBlm); HMINETDEV hDev = GfaMininetMasterGetDeviceHandle(hMst); if(hDev) { int nRet; size_t i, j; bool bBaudrateHit = false; struct timeval tvRX = {0, 100000}; GFA_SER_CFG_PARAMS scp, scpSave; if(GfaMininetDeviceGetConfigParams(hDev, &scp, sizeof(scp)) != sizeof(scp)) return false; memcpy(&scpSave, &scp, sizeof(scpSave)); GfaMininetMasterSaveTimeouts(hMst); GfaMininetMasterSetTimeouts(hMst, &tvRX, NULL); for(i = 0; i < nCntParities; ++i) { scp.parity = nParities[i]; for(j = 0; j < _countof(nBaudrates); ++j) { scp.baud = nBaudrates[j]; TRACE4("MiniNet - Try to connect @ %u,8,1,%c\n", scp.baud, scp.parity); if(GfaMininetDeviceSetConfigParams(hDev, &scp, sizeof(scp)) != 0) { nRet = -1; break; } if((nRet = GfaBlmResetSlaveIndex(hBlm, pcla->nNodeAddr)) == 0) { pcla->nInitBaudrate = nBaudrates[j]; if(!pcla->nExBaudrate) pcla->nExBaudrate = pcla->nInitBaudrate; bBaudrateHit = true; TRACE3("Detected MiniNet connection @ %u,8,1,%c\n", scp.baud, scp.parity); TTRACE3("Detected MiniNet connection @ %u,8,1,%c", scp.baud, scp.parity); break; } usleep(10000); } if(bBaudrateHit) break; } GfaMininetMasterRestoreTimeouts(hMst); if(nRet) { GfaMininetDeviceSetConfigParams(hDev, &scpSave, sizeof(scpSave)); return false; } return true; } return false; } ///////////////////////////////////////////////////////////////////////////// static bool _ModbusAutobaud(HGFABLM hBlm, LPCMD_LINE_ARGS pcla) { const uint32_t nBaudrates[] = {19200, 9600, 38400, 57600, 115200, 230400, 4800, 460800/*, 921600, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400*/}; const uint8_t nParities[] = {'E', 'O', 'N'}; size_t nCntParities = pcla->bForceAllParities ? _countof(nParities) : 2; uint32_t i, j; HGFAMINEMST hMst = GfaBlmGetMininetMasterHandle(hBlm); HMINETDEV hDev = GfaMininetMasterGetDeviceHandle(hMst); if(hDev) { HGFAMBMST hMbm; GFA_SER_CFG_PARAMS scp, scpSave; if(GfaMininetDeviceGetConfigParams(hDev, &scp, sizeof(scp)) != sizeof(scp)) return false; memcpy(&scpSave, &scp, sizeof(scpSave)); hMbm = GfaMbMstOpen(hMst, pcla->nModbBaudrate, pcla->modbParity, pcla->nModbusCtrlReg); if(hMbm) { int nRet; bool bBaudrateHit = false; struct timeval tvRX = {0, 200000}; GfaMbMstSetVerbosity(hMbm, pcla->nVerbosity); GfaMininetMasterSaveTimeouts(hMst); GfaMininetMasterSetTimeouts(hMst, &tvRX, NULL); for(i = 0; i < nCntParities; ++i) { scp.parity = nParities[i]; for(j = 0; j < _countof(nBaudrates); ++j) { scp.baud = nBaudrates[j]; TRACE4("Modbus - Try to connect @ %u,8,1,%c\n", scp.baud, scp.parity); if(GfaMininetDeviceSetConfigParams(hDev, &scp, sizeof(scp)) != 0) { nRet = -1; break; } if((nRet = GfaMbMstPing(hMbm, pcla->nModbusSlvID)) == 0) { pcla->modbParity = nParities[i]; pcla->nModbBaudrate = nBaudrates[j]; TRACE3("Detected Modbus application @ %u,8,1,%c\n", pcla->nModbBaudrate, pcla->modbParity); TTRACE3("Detected Modbus application @ %u,8,1,%c", pcla->nModbBaudrate, pcla->modbParity); bBaudrateHit = true; GfaMbMstWaitFrameDelay(hMbm); break; } GfaMbMstWaitFrameDelay(hMbm); } if(bBaudrateHit) break; } GfaMininetMasterRestoreTimeouts(hMst); GfaMbMstClose(hMbm); if(GfaMininetDeviceSetConfigParams(hDev, &scpSave, sizeof(scpSave)) != 0) return false; return (nRet == 0); } } return false; } ///////////////////////////////////////////////////////////////////////////// static void _OnUploadProgress(const char *pszFile, int nLine, LPGFA_BLM_DL_PROGRESS_PARAMS pdlpp) { if(pdlpp) { static struct timespec tsStart, tsEnd; static uint32_t nBlockNr; static int nOldPerc; LPCCMD_LINE_ARGS pcla = (LPCCMD_LINE_ARGS)pdlpp->pParam; int64_t nInterval = 0; char szPercString[8]; int nPerc; switch(pdlpp->nCtx) { case GBDPS_Error: UNUSED(pszFile); UNUSED(nLine); TRACE1("Error: %s!\n", GfaBlmStrError(pdlpp->nErrorCode)); TTRACE1(GfaBlmStrError(pdlpp->nErrorCode)); TTRACE(PLUGIN_TAG_UPLOAD_ERROR, "%d", pdlpp->nErrorCode); break; case GBDPS_StartEraseFlash: TRACE2("Start download of %u bytes to node 0x%02hhX @ address 0x%X.\n", pdlpp->nCbTotal, pdlpp->nNodeAddr, pdlpp->nFlashStartAddr); TRACE2("Erasing %u flash pages.\n", pdlpp->nCntFlashPages); TTRACE2("Start download of %u bytes to node 0x%02hhX @ address 0x%X", pdlpp->nCbTotal, pdlpp->nNodeAddr, pdlpp->nFlashStartAddr); TTRACE2("Erasing %u flash pages", pdlpp->nCntFlashPages); TTRACE(PLUGIN_TAG_UPLOAD_START_ERASE_FLASH, "%u|%u", pdlpp->nCntFlashPages, pdlpp->nCbTotal); GfaTfuGetClock(&tsStart); break; case GBDPS_EndEraseFlash: GfaTfuGetClock(&tsEnd); nInterval = GfaTfuClockDiff(&tsEnd, &tsStart); TRACE2("Erased %u flash pages in %lld ms (%.1f ms/page).\n", pdlpp->nCntFlashPages, nInterval / 1000000, ((double)nInterval / 1000000.0) / (double)pdlpp->nCntFlashPages); TTRACE2("Erased %u flash pages in %lld ms (%.1f ms/page)", pdlpp->nCntFlashPages, nInterval / 1000000, ((double)nInterval / 1000000.0) / (double)pdlpp->nCntFlashPages); TTRACE(PLUGIN_TAG_UPLOAD_END_ERASE_FLASH, "%u|%u", pdlpp->nCntFlashPages, pdlpp->nCbTotal); break; case GBDPS_StartUploadBlocks: nBlockNr = 0; nOldPerc = -1; TRACE2("Start sending data to node 0x%02hhX - block size: %u bytes.\n", pdlpp->nNodeAddr, pdlpp->nCbBlock); TTRACE2("Start sending data to node 0x%02hhX - block size: %u bytes", pdlpp->nNodeAddr, pdlpp->nCbBlock); TTRACE(PLUGIN_TAG_UPLOAD_BLOCKS_START, "%u", pdlpp->nCbBlock); GfaTfuGetClock(&tsStart); break; case GBDPS_UploadBlock: if(!pcla || !pcla->bNoProgressBlock) { if((nPerc = GfaTfuGetPercentString(pdlpp->nCbSent, pdlpp->nCbTotal, szPercString, sizeof(szPercString)))!= nOldPerc) { if((pcla->nVerbosity < 4) && (nBlockNr > 0)) TRACE2("\b\b\b\b\b"); TRACE2("%s", szPercString); nOldPerc = nPerc; } } ++nBlockNr; TTRACE(PLUGIN_TAG_UPLOAD_BLOCK, "%u|%u", pdlpp->nCbSent, nBlockNr); break; case GBDPS_EndUploadBlocks: GfaTfuGetClock(&tsEnd); nInterval = GfaTfuClockDiff(&tsEnd, &tsStart); if(!pcla || !pcla->bNoProgressBlock) TRACE2(" - Done.\n"); TRACE2("Sent %u blocks to node 0x%02hhX in %llu ms (%.1f Kb/s).\n", nBlockNr, pdlpp->nNodeAddr, nInterval / 1000000, (double)pdlpp->nCbTotal / ((double)nInterval / 1000000000.0) / 1024.0); TTRACE2("Sent %u blocks to node 0x%02hhX in %llu ms (%.1f Kb/s)", nBlockNr, pdlpp->nNodeAddr, nInterval / 1000000, (double)pdlpp->nCbTotal / ((double)nInterval / 1000000000.0) / 1024.0); TTRACE(PLUGIN_TAG_UPLOAD_BLOCKS_END, "%u", nBlockNr); break; } } } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { int nRet = 0; HIMGFILE hIf = NULL; // Handle to the image file HGFABLM hBlm = NULL; // Handle to the bootloader master CMD_LINE_ARGS cla; // structure that receives all required options and parameters struct sigaction sa; ///////////////////////////////////////////////////////////////////////// // process command line GfaTfuCmdOptInitOpts(&cla); // set options defaults GfaTfuCmdOptParse(argc, argv, &cla); // parse the command line if((nRet = GfaTfuCmdOptProcess(&cla)) != 0) // validate the options in their context { if(nRet > 0) { if(!cla.bPluginMode) { GfaTfuCmdOptDisplayHelp(cla.nQuestionMarks == 3); // show Help and exit return 0; } else { TTRACE1("Invalid command or option"); TTRACEEXIT("1"); return 1; } } else if(nRet == (int)GFA_FU_ERROR_MISSING_COMMAND_OPT) // A required option is missing { TRACE0("Error: %s: %s!\n", GfaTfuStrError(nRet), GfaTfuCmdOpt2String(cla.nMissingOptFlags)); TTRACE1("%s: %s", GfaTfuStrError(nRet), GfaTfuCmdOpt2String(cla.nMissingOptFlags)); } else if(nRet == (int)GFA_FU_ERROR_MULTIPLE_COMMANDS) // Multiple commands were given { TRACE0("Error: %s: %s!\n", GfaTfuStrError(nRet), GfaTfuCmdOpt2String(cla.nCmdFlags)); TTRACE1("%s: %s", GfaTfuStrError(nRet), GfaTfuCmdOpt2String(cla.nCmdFlags)); } else // Another error occured { TRACE0("Error: %s!\n", GfaTfuStrError(nRet)); TTRACE1("%s", GfaTfuStrError(nRet)); } TTRACEEXIT("%d", nRet); return nRet; } // At this point all command line options have been parsed and validated and the cla structure // contains all necessary parameters for the requested command. // Dump an overview of the provided command and the explicitly set options. GfaTfuCmdOptDumpOptions(&cla); ///////////////////////////////////////////////////////////////////////// // configure signal handling memset(&sa, 0, sizeof(sa)); sa.sa_handler = _SigHandler; sigaction(SIGHUP, &sa, NULL); // handle user's terminal disconnect sigaction(SIGQUIT, &sa, NULL); // handle Ctrl + '\' sigaction(SIGTERM, &sa, NULL); // handle normal termination sigaction(SIGABRT, &sa, NULL); // handle abnormal termination (i.e. abort()) sigaction(SIGINT, &sa, NULL); // handle Ctrl + 'C' sa.sa_handler = SIG_IGN; sigaction(SIGTSTP, &sa, NULL); // ignore Ctrl + 'Z' sigaction(SIGSTOP, &sa, NULL); // ignore Stop sigaction(SIGCONT, &sa, NULL); // ignore Continue sigaction(SIGCHLD, &sa, NULL); // ignore child process termination sigaction(0, &sa, NULL); // ignore shell termination ///////////////////////////////////////////////////////////////////////// do { GFA_BLM_CFG_PARAMS blmcp; // bootloader master configuration struct memset(&blmcp, 0, sizeof(blmcp)); if(cla.bNeedImgFile) // If the given command requires an image file, open it. { // This also performs some basic validations on the file. GFA_IMG_INFO ii; TRACE3("Opening image file %s.\n", cla.pszImgFile); TTRACE3("Opening image file %s", cla.pszImgFile); if(!(hIf = GfaTfuImageFileOpen(cla.pszImgFile, cla.nStartAddr, true))) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); // Something is wrong with the file TTRACE1(GfaTfuStrError(errno)); break; } GfaTfuImageFileGetInfo(hIf, &ii); TTRACE(PLUGIN_TAG_IMG_LENGTH_FILE, "%u", ii.nImgLength); TTRACE(PLUGIN_TAG_IMG_CRC32_FILE, "%u", ii.nImgCRC32); TTRACE(PLUGIN_TAG_IMG_MATERIAL_FILE, "%s", ii.szImgMaterialNum); TTRACE(PLUGIN_TAG_IMG_BUILD_FILE, "%s", ii.szImgNameBuild); if(cla.bShowFileImgInfo) // If only the file's image information was requested, dump it and exit. { GfaTfuDumpImageInfo(cla.pszImgFile, &ii); break; } } // As of this point we require a connection to the target. // In our case we request the serial device interface, // which is in turn used by the abstract mininet device if(GfaSerialGetDeviceInterface(&blmcp.mmcp.devcfg.itf)) { GFA_SER_CFG_PARAMS scp; // serial device configuration parameters memset(&scp, 0, sizeof(scp)); ///////////////////////////////////////////////////////////////// // serial device configuration scp.baud = cla.nInitBaudrate; // open with default bootloader baud-rate scp.data = 8; // always use 8 data bits scp.stop = 1; // always use 1 stop bit scp.parity = 'N'; // open with mininet default #ifdef _TARGET_BUILD scp.bHandleTxEcho = true; // On GfA Sitara targets we have to handle transmit echo! scp.bIsRS485 = true; // Interface is a RS485 #endif // _TARGET_BUILD ///////////////////////////////////////////////////////////////// // mininet master device configuration // since the mininet master handles the opening and closing of the // mininet device, which is the abstraction layer of our physical // device and in turn handles our serial device, we provide the // appropriate parameters. blmcp.mmcp.devcfg.pszDeviceName = cla.pszDevName; // name of the interface, i.e. "/dev/ttyO4" blmcp.mmcp.devcfg.pDevParams = &scp; // pointer to the serial device configuration parameters blmcp.mmcp.devcfg.nSizeDevParams = sizeof(scp); // size of the serial device configuration parameters ///////////////////////////////////////////////////////////////// // bootloader master configuration blmcp.pfnDlProgress = _OnUploadProgress; // progress output function for the image upload blmcp.pUserParam = &cla; // will be passed to the progress function ///////////////////////////////////////////////////////////////// // open a bootloader master instance TRACE3("Opening bootloader master.\n"); TTRACE3("Opening bootloader master"); if((hBlm = GfaBlmOpen(&blmcp))) { // At this point the bootloader master, the mininet master and the mininet device // have been opened and initialized successfully GFA_BLM_EXEC_CONTEXT ctx; bool bModbusAppDetected = false; GfaBlmSetVerbosity(hBlm, cla.nVerbosity); // Set the verbosity of the bootloader master and it's descendants TRACE3("Connecting to target.\n"); TTRACE3("Connecting to target"); if(!_AutoBaud(hBlm, &cla)) // Try to detect the mininet baudrate and parity { // If no mininet connection could be detected, try to scan for a modbus connection, given that // a valid modbus slave-id has been provided. if( !MODBUS_IS_VALID_SLAVE_ID(cla.nModbusSlvID) || !(bModbusAppDetected = _ModbusAutobaud(hBlm, &cla))) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); break; } } if(bModbusAppDetected) { // A modbus application is running on the target ctx = GfaBlmCtx_ModB; // Most (but not all) of the commands require the bootloader to be started implicitly, especially when a modbus application is running, // which is not capable of any command that even a minintet application could handle if(cla.bUploadImg || cla.bShowDevImgInfo || cla.bValidateImg || cla.bShowMatSer || cla.bSetMatSer) { if((nRet = _ModbusStartBootloader(hIf, hBlm, &cla, ctx)) != 0) break; ctx = GfaBlmCtx_Boot; } else if(!cla.bPing && !cla.bStartBoot) // Ping works with a modbus application as well, so there's no need to start the bootloader. { // --start-boot is an explicit command on it's own and is handled later. TRACE2("Nothing to do.\n"); // At this point either a --reset-boot or --revive-boot command was given. Both commands make no sense when any TTRACE2("Nothing to do"); // kind of application is running, so just exit. break; } } else { // No modbus application was detected, so we are dealing either with a mininet application or the bootloader itself. Find out what ... TRACE3("Detecting running image.\n"); TTRACE3("Detecting running image"); ctx = GfaBlmGetExecutionContext(hBlm, cla.nNodeAddr); if((ctx == GfaBlmCtx_Err) || (ctx == GfaBlmCtx_Boot)) { // Either an error was returned, or the bootloader is running. // In the latter case, we don't know yet, if it is in a responsive state, because the detection // involved mininet communication only! We force the bootloader into a working state preventively. // If the bootloader has already been working correctly, nothing will happen. if((nRet = _ReviveBootloader(hIf, hBlm, &cla, ctx, true)) != 0) { TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); break; } ctx = GfaBlmCtx_Boot; } TRACE3("Currently running: %s.\n", (ctx == GfaBlmCtx_App) ? "Application" : "Bootloader"); TTRACE3("Currently running: %s", (ctx == GfaBlmCtx_App) ? "Application" : "Bootloader"); } // handle the commands if(cla.bUploadImg) nRet = _UploadImg(hIf, hBlm, &cla, ctx); // handle --upload-img else if(cla.bValidateImg) nRet = _ValidateImg(hIf, hBlm, &cla, ctx); // handle --validate-img else if(cla.bShowDevImgInfo) nRet = _ShowDevImgInfo(hIf, hBlm, &cla, ctx); // handle --show-dev-img-info else if(cla.bShowMatSer) nRet = _ShowMatSer(hIf, hBlm, &cla, ctx); // handle --show-mat-ser else if(cla.bSetMatSer) nRet = _SetMatSer(hIf, hBlm, &cla, ctx); // handle --set-mat-ser else if(cla.bPing) nRet = _Ping(hIf, hBlm, &cla, ctx); // handle --ping-target else if(cla.bStartBoot) nRet = _StartBootloader(hIf, hBlm, &cla, ctx); // handle --start-boot else if(cla.bResetBoot) nRet = _ResetBootloader(hIf, hBlm, &cla, ctx); // handle --reset-boot else if(cla.bReviveBoot) nRet = _ReviveBootloader(hIf, hBlm, &cla, ctx, false); // handle --revive-boot } else { // GfaBlmOpen failed TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); break; } } else { // GfaSerialGetDeviceInterface failed TRACE1("Error: %s!\n", GfaTfuStrError(errno)); TTRACE1(GfaTfuStrError(errno)); break; } } while(false); if(hBlm) { TRACE3("Closing bootloader master.\n"); TTRACE3("Closing bootloader master"); GfaBlmClose(hBlm); } if(hIf) { TRACE3("Closing image file.\n"); TTRACE3("Closing image file"); GfaTfuImageFileClose(hIf); } TTRACEEXIT("%d", nRet); return nRet; }