// // Created by cfif on 22.08.23. // #include "GetGsmFirmware.h" #include "string.h" #include "stdlib.h" #include "FirmwareLoader.h" #include "ld_adr.h" #include "InternalFlashPage.h" #include "AtGsm_NetworkRegistrationStatus.h" #include "AtGsmOperatorSelection.h" #include "SystemDelayInterface.h" #include "AtGsmSimComA7600_DefinePdpContext.h" #define LOG_SIGN "UPDATE" #define LOGGER &env->log->logger #define FIRMWARE_MAIN_ADDR (0x08000000 + BOOT_AREA_LENGTH) #define FIRMWARE_MAIN_RECOVERY_ADDR (FIRMWARE_MAIN_ADDR + FIRMWARE_MAIN_AREA_LENGTH) #define FIRMWARE_TELE_MAIN_ADDR (FIRMWARE_MAIN_RECOVERY_ADDR + FIRMWARE_MAIN_UPDATE_AREA_LENGTH) #define FIRMWARE_TELE_RECOVERY_ADDR (FIRMWARE_MAIN_ADDR + FIRMWARE_MAIN_AREA_LENGTH) tFirmwareLoader FIRMWARE_UVEOS_LOADER; static uint32_t GetGsmFirmwareCrc32(const uint8_t *buf, size_t size) { uint32_t crc = 0; for (size_t i = 0; i < size; ++i) { crc += buf[i]; } return crc; } static bool WaitNetworkRegistration(tGsmFirmware *env, tAtCmd *gsmAt, uint16_t timeReg) { uint32_t timeEnd = SystemGetMs() + timeReg; tAtGsm_NetworkRegistrationReportMode mode; tAtGsm_NetworkRegistrationState state; while (timeEnd > SystemGetMs()) { if (AtGsm_NetworkRegistrationStatus(gsmAt, &mode, &state)) { if (state == AT_NETWORK_REGISTRATION_STATE_REGISTERED_HOME || state == AT_NETWORK_REGISTRATION_STATE_REGISTERED_ROAMING) { LoggerInfoStatic(LOGGER, LOG_SIGN, "Получена регистрация") return true; } } SystemDelayMs(60); } return false; } static bool NetworkRequire(tGsmFirmware *env, tAtCmd *gsmAt) { bool result = false; if (osMutexAcquire(gsmAt->access, 2000) == osOK) { if (!WaitNetworkRegistration(env, gsmAt, 2000)) { LoggerInfoStatic(LOGGER, LOG_SIGN, "Включение регистрации в сети"); for (uint8_t i = 0; i < 5; ++i) { AtGsm_OperatorSelectionAutomatic(gsmAt); SystemDelayMs(150); } LoggerInfoStatic(LOGGER, LOG_SIGN, "Ждем сеть..."); if (WaitNetworkRegistration(env, gsmAt, 2000)) { result = true; LoggerInfoStatic(LOGGER, LOG_SIGN, "Регистрация в сети получена"); } else { LoggerInfoStatic(LOGGER, LOG_SIGN, "Регистрация в сети не получена"); } } else { result = true; LoggerInfoStatic(LOGGER, LOG_SIGN, "Регистрация в сети присутствует"); } osMutexRelease(gsmAt->access); } return result; } static int getFileSizeFirmware(tGsmFirmware *env, tAtCmd *gsmAt, char *firmware_php_path_req, uint8_t *firmware_buf) { char url[128]; url[0] = '\0'; strcat(url, firmware_php_path_req); strcat(url, "?file="); if (env->firmwareType == FIRMWARE_UVEOS) { strcat(url, firmware_php_uveos_req); } if (env->firmwareType == FIRMWARE_TELE) { strcat(url, firmware_php_tele_req); } strcat(url, "&return_size=1"); int FileSizeCurrent = 0; int connection_step = 0; while (connection_step < connect_http_retry) { Gsm_HttpGet_Close(gsmAt); size_t fileSize = Gsm_HttpGet_Open(gsmAt, url, strlen(url)); if (fileSize > 0) { size_t read = Gsm_HttpGet_Read(gsmAt, firmware_buf, 2048); if (read > 0) { FileSizeCurrent = atoi((char *) firmware_buf); break; } else { ++connection_step; } } else { ++connection_step; } } return FileSizeCurrent; } static int getMetadataFirmware(tGsmFirmware *env, tAtCmd *gsmAt, char *firmware_php_path_req, int sizeFileFirmware, uint8_t *firmware_buf) { char buf[12]; char url[128]; url[0] = '\0'; strcat(url, firmware_php_path_req); strcat(url, "?offset="); int offsetMeta = sizeFileFirmware - FIRMWARE_META_LENGTH; utoa(offsetMeta, buf, 10); strcat(url, buf); strcat(url, "&limit="); utoa(FIRMWARE_META_LENGTH, buf, 10); strcat(url, buf); strcat(url, "&file="); if (env->firmwareType == FIRMWARE_UVEOS) { strcat(url, firmware_php_uveos_req); } if (env->firmwareType == FIRMWARE_TELE) { strcat(url, firmware_php_tele_req); } int connection_step = 0; size_t fileSizeMeta = 0; while (connection_step < connect_http_retry) { if (env->startUpdateFirmware == false) return 0; Gsm_HttpGet_Close(gsmAt); fileSizeMeta = Gsm_HttpGet_Open(gsmAt, url, strlen(url)); if (fileSizeMeta > 0) { size_t read = Gsm_HttpGet_Read(gsmAt, firmware_buf, 2048); if (read > 0) { break; } else { ++connection_step; } } else { ++connection_step; } } return (int) fileSizeMeta; } static bool getFileBodyFirmware(tGsmFirmware *env, bool isDoGsmFirmware) { if (env->Server.length < 1) { LoggerStrInfoStatic(LOGGER, LOG_SIGN, "Ошибка. Не введен адреса сервера обновления"); return false; } if (NetworkRequire(env, env->gsmAt)) { LoggerStrInfoStatic(LOGGER, LOG_SIGN, "Начало обновления прошивки"); } else { LoggerStrInfoStatic(LOGGER, LOG_SIGN, "Ошибка. Нет регистрации в сети"); return false; } memset(env->firmwareStats, 0, sizeof(tFirmwareStats)); AtCommandResult res = AtGsmSimComA7600_PdpActivate(env->gsmAt, 2); res = AtGsmSimComA7600_DefinePdpContext(env->gsmAt, 2, AtGsmSimComA7600_PdpType_IP, env->Apn.data, env->Apn.length); res = AtGsmSimComA7600_PdpActivate(env->gsmAt, 2); uint32_t connection_step = 0; size_t FileSizeCurrent = 0; while ((FileSizeCurrent == 0) && (connection_step < connect_http_retry)) { if (env->startUpdateFirmware == false) return false; FileSizeCurrent = getFileSizeFirmware(env, env->gsmAt, env->Server.data, env->firmware_buf); if (FileSizeCurrent == 0) { ++env->firmwareStats->stat_err_open; ++connection_step; LoggerStrFormatInfo(LOGGER, LOG_SIGN, "Получение размера прошивки. Ошибка соединения с сервером. Попытка: %d из %d", connection_step, connect_http_retry); SystemDelayMs(3000); } } if (FileSizeCurrent == 0) { return false; } connection_step = 0; size_t FileSizeMeta = 0; while ((FileSizeMeta == 0) && (connection_step < connect_http_retry)) { if (env->startUpdateFirmware == false) return false; FileSizeMeta = getMetadataFirmware(env, env->gsmAt, env->Server.data, (int) FileSizeCurrent, env->firmware_buf); if (FileSizeMeta == 0) { ++env->firmwareStats->stat_err_open; ++connection_step; LoggerStrFormatInfo(LOGGER, LOG_SIGN, "Получение метаданных прошивки. Ошибка соединения с сервером. Попытка: %d из %d", connection_step, connect_http_retry); SystemDelayMs(3000); } } if (FileSizeMeta == 0) { return false; } // Проверка нужно ли обновляться if (env->isExaminationFirmware) { tFirmwareLoader FIRMWARE_HTTP; FirmwareLoader_Init(&FIRMWARE_HTTP, FIRMWARE_META_LENGTH, 0, (uint32_t) &env->firmware_buf[0]); FirmwareLoader_LoadMetadata(&FIRMWARE_HTTP, &FIRMWARE_HTTP.update); uint32_t httpCrc32 = *FIRMWARE_HTTP.update.metadata.crc; uint32_t uveosCrc32 = *FIRMWARE_UVEOS_LOADER.main.metadata.crc; bool sameNames = (*FIRMWARE_UVEOS_LOADER.main.metadata.nameLength == *FIRMWARE_HTTP.update.metadata.nameLength) && memcmp( FIRMWARE_UVEOS_LOADER.main.metadata.name, FIRMWARE_HTTP.update.metadata.name, *FIRMWARE_HTTP.update.metadata.nameLength) == 0; if ((uveosCrc32 == httpCrc32) && sameNames) { env->isAbortOld = true; LoggerStrInfoStatic(LOGGER, LOG_SIGN, "Прошивка в обновлении не нуждается"); return true; } } if (!isDoGsmFirmware) { if (osMutexAcquire(env->access, 5000) == osOK) { if (FirmwareLoader_ClearUpdateFlash(&FIRMWARE_UVEOS_LOADER)) { LoggerInfoStatic(LOGGER, LOG_SIGN, "ПЗУ очищено"); } else { LoggerInfoStatic(LOGGER, LOG_SIGN, "Ошибка очистки ПЗУ"); ++env->firmwareStats->stat_err_write; osMutexRelease(env->access); return false; } osMutexRelease(env->access); } } else { LoggerInfoStatic(LOGGER, LOG_SIGN, "Очистка ПЗУ не требуется"); } char url[128]; char buf[12]; uint32_t portion_full = FileSizeCurrent / portion_size; uint32_t portion_size_tail = FileSizeCurrent % portion_size; if (portion_size_tail > 0) ++portion_full; uint32_t limit = portion_size; connection_step = 0; if (!isDoGsmFirmware) { env->offset = 0; env->portion_step = 0; } env->firmwareStats->stat_err_open = 0; env->firmwareStats->stat_err_read = 0; env->firmwareStats->stat_err_crc = 0; env->firmwareStats->stat_err_write = 0; // Цикл по всем порциям while ((env->portion_step < portion_full) && (connection_step < connect_http_retry)) { if (env->startUpdateFirmware == false) return false; Gsm_HttpGet_Close(env->gsmAt); url[0] = '\0'; strcat(url, env->Server.data); strcat(url, "?offset="); utoa(env->offset, buf, 10); strcat(url, buf); strcat(url, "&limit="); utoa(limit, buf, 10); strcat(url, buf); strcat(url, "&file="); if (env->firmwareType == FIRMWARE_UVEOS) { strcat(url, firmware_php_uveos_req); } if (env->firmwareType == FIRMWARE_TELE) { strcat(url, firmware_php_tele_req); } size_t fileSize = Gsm_HttpGet_Open(env->gsmAt, url, strlen(url)); if (fileSize == limit + 4) { size_t read = Gsm_HttpGet_Read(env->gsmAt, env->firmware_buf, fileSize); if (read == fileSize) { uint32_t crc_recv = ((uint32_t *) &env->firmware_buf[limit])[0]; uint32_t crc_calc = GetGsmFirmwareCrc32(env->firmware_buf, limit); if (crc_recv != crc_calc) { ++env->firmwareStats->stat_err_crc; ++connection_step; LoggerStrFormatInfo(LOGGER, LOG_SIGN, "Ошибка контрольной суммы данных от сервера. Попытка: %d из %d", connection_step, connect_http_retry); continue; } connection_step = 0; ++env->portion_step; if (osMutexAcquire(env->access, 5000) == osOK) { if (sInternalFlashPage_Write(FIRMWARE_UVEOS_LOADER.update.address, env->offset, env->firmware_buf, limit) == limit) { LoggerStrFormatInfo(LOGGER, LOG_SIGN, "Загружено: %d из %d", env->portion_step, portion_full); } else { LoggerInfoStatic(LOGGER, LOG_SIGN, "Ошибка записи в ПЗУ"); ++env->firmwareStats->stat_err_write; osMutexRelease(env->access); return false; } osMutexRelease(env->access); } if ((portion_size_tail > 0) && (env->portion_step == portion_full - 1)) { env->offset += portion_size; limit = portion_size_tail; } else { env->offset += portion_size; } } else { ++env->firmwareStats->stat_err_read; ++connection_step; LoggerStrFormatInfo(LOGGER, LOG_SIGN, "Ошибка получения данных от сервера. Попытка: %d из %d", connection_step, connect_http_retry); SystemDelayMs(500); } } else { ++env->firmwareStats->stat_err_open; ++connection_step; LoggerStrFormatInfo(LOGGER, LOG_SIGN, "Получение данных прошивки. Ошибка соединения с сервером. Попытка: %d из %d", connection_step, connect_http_retry); SystemDelayMs(3000); } } if ((connection_step == connect_http_retry) || (env->firmwareStats->stat_err_write != 0)) { return false; } return true; } bool GetDoGsmFirmware(tGsmFirmware *env) { bool isLoadFirmware = getFileBodyFirmware(env, true); return isLoadFirmware; } bool GetGsmFirmware(tGsmFirmware *env) { bool isLoadFirmware = getFileBodyFirmware(env, false); return isLoadFirmware; } void StartUpdateFirmware(tGsmFirmware *env) { env->doUpdateFirmware = false; env->startUpdateFirmware = true; env->isAbortOld = false; } void _Noreturn FirmwareProcessingTask(tGsmFirmware *env) { // uint32_t attempts; for (;;) { if (env->startUpdateFirmware) { if (env->doUpdateFirmware) { bool result = GetDoGsmFirmware(env); if (env->startUpdateFirmware == false) { LoggerInfoStatic(LOGGER, LOG_SIGN, "Выход из режима обновления УВЭОС"); continue; } if (result) { env->startUpdateFirmware = false; env->doUpdateFirmware = false; LoggerInfoStatic(LOGGER, LOG_SIGN, "Успех. Обновление завершено без ошибок"); env->eventUpdateFunc(env->envUpdateFunc, true, env->isAbortOld); } else { LoggerInfoStatic(LOGGER, LOG_SIGN, "Ошибка. Обновление не завершено"); --env->attempts; env->eventUpdateFunc(env->envUpdateFunc, false, env->isAbortOld); if (env->attempts > 0) { LoggerStrFormatInfo(LOGGER, LOG_SIGN, "Обновление будет завершено позже, через %d сек. Осталось попыток %d из %d", env->attempts_period, env->attempts, env->attemptsAll); for (uint32_t i = 0; i < env->attempts_period; ++i) { if (env->startUpdateFirmware == false) { break; } SystemDelayMs(1000); } if (env->startUpdateFirmware == false) { LoggerInfoStatic(LOGGER, LOG_SIGN, "Выход из режима обновления УВЭОС"); continue; } } else { env->startUpdateFirmware = false; env->doUpdateFirmware = false; LoggerInfoStatic(LOGGER, LOG_SIGN, "Попытки исчерпаны. Обновление будет завершено после получения команды на обновление"); } } } else { bool result = GetGsmFirmware(env); if (env->startUpdateFirmware == false) { LoggerInfoStatic(LOGGER, LOG_SIGN, "Выход из режима обновления УВЭОС"); continue; } if (result) { env->startUpdateFirmware = false; LoggerInfoStatic(LOGGER, LOG_SIGN, "Успех. Обновление завершено без ошибок"); env->eventUpdateFunc(env->envUpdateFunc, true, env->isAbortOld); } else { // attempts = env->attempts; env->doUpdateFirmware = true; LoggerInfoStatic(LOGGER, LOG_SIGN, "Ошибка. Обновление не завершено"); env->eventUpdateFunc(env->envUpdateFunc, false, env->isAbortOld); if (env->attempts > 0) { LoggerStrFormatInfo(LOGGER, LOG_SIGN, "Обновление будет завершено позже, через %d сек. Осталось попыток %d из %d", env->attempts_period, env->attempts, env->attemptsAll); for (uint32_t i = 0; i < env->attempts_period; ++i) { if (env->startUpdateFirmware == false) { break; } SystemDelayMs(1000); } if (env->startUpdateFirmware == false) { LoggerInfoStatic(LOGGER, LOG_SIGN, "Выход из режима обновления УВЭОС"); continue; } } else { /* LoggerInfoStatic(LOGGER, LOG_SIGN, "Дополнительные 100 попыток"); env->attempts = 100; */ for (uint32_t i = 0; i < env->attempts_period; ++i) { if (env->startUpdateFirmware == false) { break; } SystemDelayMs(1000); } if (env->startUpdateFirmware == false) { LoggerInfoStatic(LOGGER, LOG_SIGN, "Выход из режима обновления УВЭОС"); continue; } env->startUpdateFirmware = false; env->doUpdateFirmware = false; LoggerInfoStatic(LOGGER, LOG_SIGN, "Попытки исчерпаны. Обновление будет завершено после получения команды на обновление"); } } } } SystemDelayMs(1000); } } void InitGsmFirmware(tGsmFirmware *env, tAtCmd *gsmAt, eFirmwareType firmwareType, uint8_t *firmware_buf, tFirmwareStats *firmwareStats, tString64 *Apn, tString64 *Server, uint32_t attempts, uint32_t attempts_period, osMutexId_t access, bool isExaminationFirmware, EventUpdateFunc *eventUpdateFunc, void *envUpdateFunc, tLoggerToSerialPort *log) { if (firmwareType == FIRMWARE_UVEOS) { FirmwareLoader_Init(&FIRMWARE_UVEOS_LOADER, FIRMWARE_MAIN_AREA_LENGTH, FIRMWARE_MAIN_ADDR, FIRMWARE_MAIN_RECOVERY_ADDR); } if (firmwareType == FIRMWARE_TELE) { FirmwareLoader_Init(&FIRMWARE_UVEOS_LOADER, FIRMWARE_TELE_AREA_LENGTH, FIRMWARE_TELE_MAIN_ADDR, FIRMWARE_MAIN_RECOVERY_ADDR); } bool mainFwOk = FirmwareLoader_CheckBlock(&FIRMWARE_UVEOS_LOADER, &FIRMWARE_UVEOS_LOADER.main); env->isExaminationFirmware = isExaminationFirmware; env->access = access; env->firmwareType = firmwareType; env->doUpdateFirmware = false; env->startUpdateFirmware = false; env->eventUpdateFunc = eventUpdateFunc; env->envUpdateFunc = envUpdateFunc; env->attemptsAll = attempts; env->attempts = attempts; env->attempts_period = attempts_period; env->gsmAt = gsmAt; env->firmware_buf = firmware_buf; env->firmwareStats = firmwareStats; memset(env->Apn.data, 0, sizeof(env->Apn.data)); env->Apn.length = Apn->length; memcpy(&env->Apn.data, Apn->data, Apn->length); memset(env->Server.data, 0, sizeof(env->Server.data)); env->Server.length = Server->length; memcpy(&env->Server.data, Server->data, Server->length); env->log = log; InitThreadBlock(env->T_processing_Firmware, "Firmware", osPriorityNormal); } void GsmFirmware_Start(tGsmFirmware *env) { ThreadBlock_Start(env->T_processing_Firmware, env, FirmwareProcessingTask); }