75381150fd
Formatting Pass 1 Lots of fixups to adding stdint and stdbool all over the place Formatting Pass 2 Formatting Pass 3 Formatting Pass 4 Update app_bt_stream.cpp
1443 lines
44 KiB
C++
1443 lines
44 KiB
C++
/**
|
|
* @file ota_common.cpp
|
|
* @author BES AI team
|
|
* @version 0.1
|
|
* @date 2020-04-17
|
|
*
|
|
* @copyright Copyright (c) 2015-2020 BES Technic.
|
|
* All rights reserved. All unpublished rights reserved.
|
|
*
|
|
* No part of this work may be used or reproduced in any form or by any
|
|
* means, or stored in a database or retrieval system, without prior written
|
|
* permission of BES.
|
|
*
|
|
* Use of this work is governed by a license granted by BES.
|
|
* This work contains confidential and proprietary information of
|
|
* BES. which is protected by copyright, trade secret,
|
|
* trademark and other intellectual property rights.
|
|
*/
|
|
|
|
/*****************************header include********************************/
|
|
#include "ota_common.h"
|
|
#include "app.h"
|
|
#include "app_bt.h"
|
|
#include "app_utils.h"
|
|
#include "apps.h"
|
|
#include "bt_drv_reg_op.h"
|
|
#include "cmsis.h"
|
|
#include "crc32.h"
|
|
#include "hal_timer.h"
|
|
#include "norflash_api.h"
|
|
#include "norflash_drv.h"
|
|
#include "nvrecord_ota.h"
|
|
#include "ota_dbg.h"
|
|
#include "string.h"
|
|
|
|
#ifdef IBRT
|
|
#include "app_ibrt_if.h"
|
|
#include "app_ibrt_ota_cmd.h"
|
|
#include "app_ibrt_ui.h"
|
|
#include "app_tws_ctrl_thread.h"
|
|
#include "app_tws_ibrt.h"
|
|
#include "app_tws_if.h"
|
|
#endif
|
|
|
|
/*********************external function declearation************************/
|
|
|
|
/************************private macro defination***************************/
|
|
#define OTA_BOOT_INFO_FLASH_OFFSET 0x1000
|
|
#define OTA_BREAKPOINT_STORE_GRANULARITY (256 * 1024) // must be 4KB aligned
|
|
#define LEN_OF_IMAGE_TAIL_TO_FIND_SANITY_CRC 512
|
|
#define INVALID_VERSION_STR "BESTECHNIC"
|
|
|
|
#define NORFLASH_API_MODE_ASYNC true
|
|
#define OTA_DEFAULT_NORFLASH_API_MODE NORFLASH_API_MODE_ASYNC
|
|
|
|
#define CASES(prefix, item) \
|
|
case prefix##item: \
|
|
str = #item; \
|
|
break;
|
|
|
|
/************************private type defination****************************/
|
|
|
|
/**********************private function declearation************************/
|
|
|
|
/************************private variable defination************************/
|
|
__attribute__((unused)) static const char *imageSanityKeyWord =
|
|
"CRC32_OF_IMAGE=0x";
|
|
__attribute__((unused)) static const char *oldImageSanityKeyWord =
|
|
"CRC32_OF_IMAGE=";
|
|
|
|
#ifdef IBRT
|
|
static void _ota_tws_thread(const void *arg);
|
|
osThreadDef(_ota_tws_thread, osPriorityNormal, 1, 1024, "ota_tws");
|
|
|
|
static osEvent evt;
|
|
|
|
/// tws relay OTA command/data TX thread ID
|
|
osThreadId txThreadId = NULL;
|
|
|
|
/// tws relay OTA command/data RX thread ID
|
|
osThreadId rxThreadId;
|
|
|
|
osMutexDef(twsTxQueueMutex);
|
|
osMutexDef(twsRxQueueMutex);
|
|
|
|
/// tws relay data TX queue mutex
|
|
osMutexId txQueueMutexID = NULL;
|
|
|
|
/// tws relay data RX queue mutex
|
|
osMutexId rxQueueMutexID = NULL;
|
|
|
|
uint8_t relayBuf[TWS_RELAY_DATA_MAX_SIZE];
|
|
#endif
|
|
|
|
OTA_COMMON_ENV_T otaEnv;
|
|
|
|
/****************************function defination****************************/
|
|
|
|
/**
|
|
* @brief Convert OTA command to string
|
|
*
|
|
* @param cmd @see OTA_COMMAND_E
|
|
* @return char* string of OTA_COMMAND
|
|
*/
|
|
static char *_cmd2str(OTA_COMMAND_E cmd) {
|
|
const char *str = NULL;
|
|
|
|
switch (cmd) {
|
|
CASES(OTA_COMMAND_, BEGIN);
|
|
CASES(OTA_COMMAND_, APPLY);
|
|
CASES(OTA_COMMAND_, DATA);
|
|
CASES(OTA_COMMAND_, ABORT);
|
|
#ifdef IBRT
|
|
CASES(OTA_COMMAND_, RSP);
|
|
#endif
|
|
// CASES(OTA_COMMAND_, );
|
|
|
|
default:
|
|
str = "INVALID";
|
|
break;
|
|
}
|
|
|
|
return (char *)str;
|
|
}
|
|
|
|
/**
|
|
* @brief Convert OTA user to string
|
|
*
|
|
* @param user @see OTA_USER_E
|
|
* @return char* OTA_USER string
|
|
*/
|
|
static char *_user2str(OTA_USER_E user) {
|
|
const char *str = NULL;
|
|
|
|
switch (user) {
|
|
CASES(OTA_USER_, BES);
|
|
CASES(OTA_USER_, COLORFUL);
|
|
CASES(OTA_USER_, RED);
|
|
CASES(OTA_USER_, ORANGE);
|
|
CASES(OTA_USER_, GREEN);
|
|
// CASES(OTA_USER_, );
|
|
|
|
default:
|
|
str = "INVALID";
|
|
break;
|
|
}
|
|
|
|
return (char *)str;
|
|
}
|
|
|
|
/**
|
|
* @brief Convert OTA path into string
|
|
*
|
|
* @param path @see OTA_PATH_E
|
|
* @return char* OTA_PATH string
|
|
*/
|
|
static char *_path2str(OTA_PATH_E path) {
|
|
const char *str = NULL;
|
|
|
|
switch (path) {
|
|
CASES(OTA_PATH_, BT);
|
|
CASES(OTA_PATH_, BLE);
|
|
// CASES(OTA_PATH_, );
|
|
|
|
default:
|
|
str = "INVALID";
|
|
break;
|
|
}
|
|
|
|
return (char *)str;
|
|
}
|
|
|
|
/**
|
|
* @brief Convert OTA stage into string
|
|
*
|
|
* @param stage @see OTA_STAGE_E
|
|
* @return char* OTA_STAGE string
|
|
*/
|
|
static char *_stage2str(OTA_STAGE_E stage) {
|
|
const char *str = NULL;
|
|
|
|
switch (stage) {
|
|
CASES(OTA_STAGE_, IDLE);
|
|
CASES(OTA_STAGE_, ONGOING);
|
|
CASES(OTA_STAGE_, DONE);
|
|
// CASES(OTA_STAGE_, );
|
|
|
|
default:
|
|
str = "INVALID";
|
|
break;
|
|
}
|
|
|
|
return (char *)str;
|
|
}
|
|
|
|
static char *_sts2str(OTA_STATUS_E sts) {
|
|
const char *str = NULL;
|
|
|
|
switch (sts) {
|
|
CASES(OTA_STATUS_, OK);
|
|
CASES(OTA_STATUS_, ERROR);
|
|
CASES(OTA_STATUS_, ERROR_RELAY_TIMEOUT);
|
|
CASES(OTA_STATUS_, ERROR_CHECKSUM);
|
|
CASES(OTA_STATUS_, ERROR_NOT_ALLOWED);
|
|
// CASES(OTA_STATUS_, );
|
|
|
|
default:
|
|
str = "INVALID";
|
|
break;
|
|
}
|
|
|
|
return (char *)str;
|
|
}
|
|
|
|
/**
|
|
* @brief Find key word in upgrade data.
|
|
*
|
|
* This function is used to find out the key word at the end of upgrade data,
|
|
* these key words are generated by python script by calculate the crc of whole
|
|
* upgrade image and write the result at the end of the image in string format.
|
|
* This is used to do sanity check.
|
|
*
|
|
* @param tgtArray source array to search the key word
|
|
* @param tgtArrayLen source array length
|
|
* @param keyArray key word array
|
|
* @param keyArrayLen key word length
|
|
* @return int32_t index of the key word in dstArry
|
|
*/
|
|
__attribute__((unused)) static int32_t _find_key_word(uint8_t *tgtArray,
|
|
uint32_t tgtArrayLen,
|
|
uint8_t *keyArray,
|
|
uint32_t keyArrayLen) {
|
|
if ((keyArrayLen > 0) && (tgtArrayLen >= keyArrayLen)) {
|
|
uint32_t index = 0, targetIndex = 0;
|
|
for (targetIndex = 0; targetIndex < tgtArrayLen; targetIndex++) {
|
|
for (index = 0; index < keyArrayLen; index++) {
|
|
if (tgtArray[targetIndex + index] != keyArray[index]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (index == keyArrayLen) {
|
|
return targetIndex;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Convert ASCII code into hex format.
|
|
*
|
|
* @param asciiCode ASCII code to convert
|
|
* @return uint8_t convert result
|
|
*/
|
|
__attribute__((unused)) static uint8_t _ascii2hex(uint8_t asciiCode) {
|
|
if ((asciiCode >= '0') && (asciiCode <= '9')) {
|
|
return asciiCode - '0';
|
|
} else if ((asciiCode >= 'a') && (asciiCode <= 'f')) {
|
|
return asciiCode - 'a' + 10;
|
|
} else if ((asciiCode >= 'A') && (asciiCode <= 'F')) {
|
|
return asciiCode - 'A' + 10;
|
|
} else {
|
|
return 0xff;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Update the stage of OTA progress.
|
|
*
|
|
* @param stage OTA stage to update,
|
|
* @see OTA_STAGE_E to get more details.
|
|
*/
|
|
static void _set_ota_stage(OTA_STAGE_E stage) {
|
|
LOG_D("stage update:%s->%s", _stage2str(otaEnv.currentStage),
|
|
_stage2str(stage));
|
|
|
|
otaEnv.currentStage = stage;
|
|
}
|
|
|
|
static void _update_ota_user(OTA_USER_E user) {
|
|
ASSERT((OTA_USER_NUM == otaEnv.currentUser) || (OTA_USER_NUM == user),
|
|
"try to set a ota user while the current user is not null");
|
|
|
|
LOG_I("ota user update:%s->%s", _user2str(otaEnv.currentUser),
|
|
_user2str(user));
|
|
|
|
otaEnv.currentUser = user;
|
|
}
|
|
|
|
/**
|
|
* @brief Enter OTA state handler.
|
|
*
|
|
* This function is used to require the relative system resources to gurantee
|
|
* the performance for both OTA progress and other functionalities
|
|
*
|
|
* @param path Current OTA path,
|
|
* @see OTA_PATH_E to get more details.
|
|
*/
|
|
static void _enter_ota_state(OTA_PATH_E path) {
|
|
LOG_I("%s", __func__);
|
|
|
|
if (otaEnv.isInOtaState) {
|
|
LOG_W("ALREADY in OTA state");
|
|
} else {
|
|
/// 1. guarantee performance->switch to the highest freq
|
|
app_sysfreq_req(APP_SYSFREQ_USER_OTA, APP_SYSFREQ_104M);
|
|
|
|
#ifdef IBRT
|
|
/// 2. guarantee performance->decrease the communication interval of TWS
|
|
/// connection
|
|
ibrt_ctrl_t *p_ibrt_ctrl = app_tws_ibrt_get_bt_ctrl_ctx();
|
|
btdrv_reg_op_set_tws_link_duration(IBRT_TWS_LINK_LARGE_DURATION);
|
|
btdrv_reg_op_set_private_tws_poll_interval(
|
|
p_ibrt_ctrl->config.default_private_poll_interval,
|
|
p_ibrt_ctrl->config.default_private_poll_interval_in_sco);
|
|
|
|
/// 3. guarantee performance->exit bt sniff mode
|
|
app_ibrt_ui_judge_link_policy(OTA_START_TRIGGER, BTIF_BLP_DISABLE_ALL);
|
|
|
|
if (app_tws_ibrt_tws_link_connected() &&
|
|
(p_ibrt_ctrl->nv_role == IBRT_MASTER) &&
|
|
p_ibrt_ctrl->p_tws_remote_dev) {
|
|
btif_me_stop_sniff(p_ibrt_ctrl->p_tws_remote_dev);
|
|
}
|
|
|
|
app_ibrt_if_tws_sniff_block(15);
|
|
#else
|
|
/// 3. guarantee performance->exit bt sniff mode
|
|
app_bt_active_mode_set(ACTIVE_MODE_KEEPER_OTA,
|
|
UPDATE_ACTIVE_MODE_FOR_ALL_LINKS);
|
|
#endif
|
|
|
|
/// update the OTA path
|
|
LOG_I("OTA path update:%s->%s", _path2str(otaEnv.currentPath),
|
|
_path2str(path));
|
|
otaEnv.currentPath = path;
|
|
|
|
/// 4. guarantee performance->decrease the BLE connection interval
|
|
#ifdef BLE_ENABLE
|
|
if (OTA_PATH_BLE == otaEnv.currentPath) {
|
|
app_ble_update_conn_param_mode(BLE_CONN_PARAM_MODE_OTA, true);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Exit the OTA state handler.
|
|
*
|
|
* This function is used to release the system resources which are required
|
|
* when enter the OTA state
|
|
*
|
|
*/
|
|
__attribute__((unused)) static void _exit_ota_state(void) {
|
|
LOG_I("%s", __func__);
|
|
|
|
if (otaEnv.isInOtaState) {
|
|
#ifdef IBRT
|
|
app_ibrt_ui_judge_link_policy(OTA_STOP_TRIGGER, BTIF_BLP_SNIFF_MODE);
|
|
#else
|
|
app_bt_active_mode_clear(ACTIVE_MODE_KEEPER_OTA,
|
|
UPDATE_ACTIVE_MODE_FOR_ALL_LINKS);
|
|
#endif
|
|
|
|
/// release the short TWS communication interval
|
|
#ifdef IBRT
|
|
ibrt_ctrl_t *p_ibrt_ctrl = app_tws_ibrt_get_bt_ctrl_ctx();
|
|
btdrv_reg_op_set_tws_link_duration(IBRT_TWS_LINK_DEFAULT_DURATION);
|
|
btdrv_reg_op_set_private_tws_poll_interval(
|
|
p_ibrt_ctrl->config.long_private_poll_interval,
|
|
p_ibrt_ctrl->config.default_private_poll_interval_in_sco);
|
|
#endif
|
|
|
|
/// release the short BLE connection interval
|
|
#ifdef BLE_ENABLE
|
|
app_ble_update_conn_param_mode(BLE_CONN_PARAM_MODE_OTA, false);
|
|
app_ble_update_conn_param_mode(BLE_CONN_PARAM_MODE_OTA_SLOWER, false);
|
|
#endif
|
|
|
|
/// update the OTA path to invalid
|
|
otaEnv.currentPath = OTA_PATH_INVALID;
|
|
|
|
/// release the system frequency
|
|
app_sysfreq_req(APP_SYSFREQ_USER_OTA, APP_SYSFREQ_32K);
|
|
|
|
otaEnv.isInOtaState = false;
|
|
} else {
|
|
LOG_W("NOT in OTA STATE");
|
|
}
|
|
}
|
|
|
|
#ifdef IBRT
|
|
|
|
/**
|
|
* @brief Do the sanity check for the upgrade file.
|
|
*
|
|
* Currently, this function uses the CRC that was inserted by the build process.
|
|
* This CRC check does not verify the last x bytes of the image. x varies
|
|
* depending on order of the key/value pair order but is < 512 bytes.
|
|
*
|
|
* @return true Sanity check success.
|
|
* @return false Sanity check failed.
|
|
*/
|
|
static bool _image_sanity_check(void) {
|
|
// find the location of the CRC key word string
|
|
uint8_t *ptrOfTheLast4KImage =
|
|
(uint8_t *)(OTA_FLASH_LOGIC_ADDR + NEW_IMAGE_FLASH_OFFSET +
|
|
otaEnv.totalImageSize - LEN_OF_IMAGE_TAIL_TO_FIND_SANITY_CRC);
|
|
|
|
uint32_t sanityCrc32;
|
|
uint32_t crc32ImageOffset;
|
|
int32_t sanity_crc_location =
|
|
_find_key_word(ptrOfTheLast4KImage, LEN_OF_IMAGE_TAIL_TO_FIND_SANITY_CRC,
|
|
(uint8_t *)imageSanityKeyWord, strlen(imageSanityKeyWord));
|
|
if (-1 == sanity_crc_location) {
|
|
sanity_crc_location = _find_key_word(
|
|
ptrOfTheLast4KImage, LEN_OF_IMAGE_TAIL_TO_FIND_SANITY_CRC,
|
|
(uint8_t *)oldImageSanityKeyWord, strlen(oldImageSanityKeyWord));
|
|
if (-1 == sanity_crc_location) {
|
|
// if no sanity crc, fail the check
|
|
return false;
|
|
} else {
|
|
crc32ImageOffset = sanity_crc_location + otaEnv.totalImageSize -
|
|
LEN_OF_IMAGE_TAIL_TO_FIND_SANITY_CRC +
|
|
strlen(oldImageSanityKeyWord);
|
|
sanityCrc32 = *(uint32_t *)(OTA_FLASH_LOGIC_ADDR +
|
|
NEW_IMAGE_FLASH_OFFSET + crc32ImageOffset);
|
|
}
|
|
} else {
|
|
crc32ImageOffset = sanity_crc_location + otaEnv.totalImageSize -
|
|
LEN_OF_IMAGE_TAIL_TO_FIND_SANITY_CRC +
|
|
strlen(imageSanityKeyWord);
|
|
|
|
sanityCrc32 = 0;
|
|
uint8_t *crcString = (uint8_t *)(OTA_FLASH_LOGIC_ADDR +
|
|
NEW_IMAGE_FLASH_OFFSET + crc32ImageOffset);
|
|
|
|
for (uint8_t index = 0; index < 8; index++) {
|
|
sanityCrc32 |= (_ascii2hex(crcString[index]) << (28 - 4 * index));
|
|
}
|
|
}
|
|
|
|
LOG_D("Bytes to generate crc32 is %d", crc32ImageOffset);
|
|
LOG_D("sanity_crc_location is %d", sanity_crc_location);
|
|
|
|
LOG_D("sanityCrc32 is 0x%x", sanityCrc32);
|
|
|
|
// generate the CRC from image data
|
|
uint32_t calculatedCrc32 = 0;
|
|
calculatedCrc32 =
|
|
crc32(calculatedCrc32,
|
|
(uint8_t *)(OTA_FLASH_LOGIC_ADDR + NEW_IMAGE_FLASH_OFFSET),
|
|
crc32ImageOffset);
|
|
|
|
LOG_D("calculatedCrc32 is 0x%x", calculatedCrc32);
|
|
|
|
if (sanityCrc32 == calculatedCrc32) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static enum NORFLASH_API_MODULE_ID_T
|
|
_get_flash_module_from_ota_device(OTA_DEVICE_E device) {
|
|
enum NORFLASH_API_MODULE_ID_T mod = NORFLASH_API_MODULE_ID_COUNT;
|
|
|
|
switch (device) {
|
|
case OTA_DEVICE_APP:
|
|
mod = NORFLASH_API_MODULE_ID_OTA;
|
|
break;
|
|
case OTA_DEVICE_HOTWORD:
|
|
mod = NORFLASH_API_MODULE_ID_HOTWORD_MODEL;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0, "invalid OTA device received in %s", __func__);
|
|
break;
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
static void _flush_data_to_flash(uint8_t *ptrSource, uint32_t lengthToBurn,
|
|
uint32_t offsetInFlashToProgram,
|
|
bool synWrite) {
|
|
|
|
LOG_D("flush %d bytes to flash offset 0x%x", lengthToBurn,
|
|
offsetInFlashToProgram);
|
|
enum NORFLASH_API_MODULE_ID_T mod =
|
|
_get_flash_module_from_ota_device(otaEnv.deviceId);
|
|
|
|
uint32_t preBytes = (FLASH_SECTOR_SIZE_IN_BYTES -
|
|
(offsetInFlashToProgram % FLASH_SECTOR_SIZE_IN_BYTES)) %
|
|
FLASH_SECTOR_SIZE_IN_BYTES;
|
|
|
|
if (lengthToBurn < preBytes) {
|
|
preBytes = lengthToBurn;
|
|
}
|
|
|
|
uint32_t middleBytes = 0;
|
|
if (lengthToBurn > preBytes) {
|
|
middleBytes = ((lengthToBurn - preBytes) / FLASH_SECTOR_SIZE_IN_BYTES *
|
|
FLASH_SECTOR_SIZE_IN_BYTES);
|
|
}
|
|
|
|
uint32_t postBytes = 0;
|
|
if (lengthToBurn > (preBytes + middleBytes)) {
|
|
postBytes =
|
|
(offsetInFlashToProgram + lengthToBurn) % FLASH_SECTOR_SIZE_IN_BYTES;
|
|
}
|
|
|
|
LOG_D("Prebytes is %d middlebytes is %d postbytes is %d", preBytes,
|
|
middleBytes, postBytes);
|
|
|
|
if (preBytes > 0) {
|
|
app_flash_page_program(mod, offsetInFlashToProgram, ptrSource, preBytes,
|
|
synWrite);
|
|
|
|
ptrSource += preBytes;
|
|
offsetInFlashToProgram += preBytes;
|
|
}
|
|
|
|
uint32_t sectorIndexInFlash =
|
|
offsetInFlashToProgram / FLASH_SECTOR_SIZE_IN_BYTES;
|
|
|
|
if (middleBytes > 0) {
|
|
uint32_t sectorCntToProgram = middleBytes / FLASH_SECTOR_SIZE_IN_BYTES;
|
|
for (uint32_t sector = 0; sector < sectorCntToProgram; sector++) {
|
|
app_flash_page_erase(mod,
|
|
sectorIndexInFlash * FLASH_SECTOR_SIZE_IN_BYTES);
|
|
app_flash_page_program(mod,
|
|
sectorIndexInFlash * FLASH_SECTOR_SIZE_IN_BYTES,
|
|
ptrSource + sector * FLASH_SECTOR_SIZE_IN_BYTES,
|
|
FLASH_SECTOR_SIZE_IN_BYTES, synWrite);
|
|
|
|
sectorIndexInFlash++;
|
|
}
|
|
|
|
ptrSource += middleBytes;
|
|
}
|
|
|
|
if (postBytes > 0) {
|
|
app_flash_page_erase(mod, sectorIndexInFlash * FLASH_SECTOR_SIZE_IN_BYTES);
|
|
app_flash_page_program(mod, sectorIndexInFlash * FLASH_SECTOR_SIZE_IN_BYTES,
|
|
ptrSource, postBytes, synWrite);
|
|
}
|
|
|
|
app_flush_pending_flash_op(mod, NORFLASH_API_ALL);
|
|
}
|
|
|
|
/**
|
|
* NOTE that this function is stil needed since the OTA bootloader uses the CRC
|
|
* that is passed into it to re-verify the image and that CRC is for the entire
|
|
* image.
|
|
*/
|
|
static bool _compute_whole_image_crc(void) {
|
|
uint32_t processedDataSize = 0;
|
|
uint32_t crc32Value = 0;
|
|
uint32_t bytes_to_use = 0;
|
|
uint32_t lock;
|
|
|
|
while (processedDataSize < otaEnv.totalImageSize) {
|
|
if (otaEnv.totalImageSize - processedDataSize >
|
|
OTA_DATA_CACHE_BUFFER_SIZE) {
|
|
bytes_to_use = OTA_DATA_CACHE_BUFFER_SIZE;
|
|
} else {
|
|
bytes_to_use = otaEnv.totalImageSize - processedDataSize;
|
|
}
|
|
|
|
lock = int_lock_global();
|
|
norflash_sync_read(
|
|
NORFLASH_API_MODULE_ID_OTA,
|
|
(OTA_FLASH_LOGIC_ADDR + NEW_IMAGE_FLASH_OFFSET + processedDataSize),
|
|
otaEnv.dataCacheBuffer, OTA_DATA_CACHE_BUFFER_SIZE);
|
|
// memcpy(otaEnv.dataCacheBuffer, (uint8_t *)(OTA_FLASH_LOGIC_ADDR +
|
|
// NEW_IMAGE_FLASH_OFFSET + processedDataSize),
|
|
// OTA_DATA_CACHE_BUFFER_SIZE);
|
|
int_unlock_global(lock);
|
|
|
|
if (0 == processedDataSize) {
|
|
if (*(uint32_t *)otaEnv.dataCacheBuffer != NORMAL_BOOT) {
|
|
LOG_D("first 32bit value is not NORMAL_BOOT");
|
|
return false;
|
|
} else {
|
|
*(uint32_t *)otaEnv.dataCacheBuffer = 0xFFFFFFFF;
|
|
}
|
|
}
|
|
|
|
LOG_D("bytes to verify =%d.", bytes_to_use);
|
|
|
|
crc32Value =
|
|
crc32(crc32Value, (uint8_t *)otaEnv.dataCacheBuffer, bytes_to_use);
|
|
|
|
processedDataSize += bytes_to_use;
|
|
}
|
|
|
|
LOG_D("Computed CRC32 is 0x%x.", crc32Value);
|
|
|
|
/* This crc value will be passed to the ota app in GSoundOtaApply(). */
|
|
otaEnv.crc32OfImage = crc32Value;
|
|
return true;
|
|
}
|
|
|
|
static void _update_boot_info(OTA_BOOT_INFO_T *otaBootInfo) {
|
|
ASSERT(OTA_DEVICE_APP == otaEnv.deviceId,
|
|
"illegal OTA device try to update boot info");
|
|
hal_norflash_disable_protection(HAL_NORFLASH_ID_0);
|
|
enum NORFLASH_API_MODULE_ID_T mod =
|
|
_get_flash_module_from_ota_device(otaEnv.deviceId);
|
|
app_flash_page_erase(mod, OTA_BOOT_INFO_FLASH_OFFSET);
|
|
app_flash_page_program(mod, OTA_BOOT_INFO_FLASH_OFFSET,
|
|
(uint8_t *)otaBootInfo, sizeof(OTA_BOOT_INFO_T), true);
|
|
}
|
|
|
|
static void _update_magic_number(uint32_t newMagicNumber) {
|
|
ASSERT(OTA_DEVICE_APP == otaEnv.deviceId,
|
|
"illegal device %d to update magic number", otaEnv.deviceId);
|
|
memcpy(otaEnv.dataCacheBuffer,
|
|
(uint8_t *)(OTA_FLASH_LOGIC_ADDR + otaEnv.newImageFlashOffset),
|
|
FLASH_SECTOR_SIZE_IN_BYTES);
|
|
|
|
*(uint32_t *)otaEnv.dataCacheBuffer = newMagicNumber;
|
|
enum NORFLASH_API_MODULE_ID_T mod =
|
|
_get_flash_module_from_ota_device(otaEnv.deviceId);
|
|
app_flash_page_erase(mod, otaEnv.newImageFlashOffset);
|
|
|
|
app_flash_page_program(mod, otaEnv.newImageFlashOffset,
|
|
otaEnv.dataCacheBuffer, FLASH_SECTOR_SIZE_IN_BYTES,
|
|
true);
|
|
|
|
app_flush_pending_flash_op(mod, NORFLASH_API_ALL);
|
|
}
|
|
|
|
static void _update_peer_result(OTA_STATUS_E ret) {
|
|
LOG_D("update peer result:%s->%s", _sts2str(otaEnv.peerResult),
|
|
_sts2str(ret));
|
|
|
|
otaEnv.peerResult = ret;
|
|
}
|
|
|
|
OTA_STATUS_E ota_common_get_peer_result(void) { return otaEnv.peerResult; }
|
|
|
|
static bool _relay_data_needed(void) {
|
|
bool ret = true;
|
|
|
|
if (!app_tws_ibrt_tws_link_connected()) {
|
|
ret = false;
|
|
}
|
|
|
|
if (OTA_STAGE_IDLE == otaEnv.currentStage) {
|
|
ret = false;
|
|
}
|
|
|
|
/// check the customized relay_data_needed permission
|
|
if (ret && otaEnv.customRelayNeededHandler) {
|
|
ret = otaEnv.customRelayNeededHandler();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void _ota_relay_data(OTA_COMMAND_E cmdType, const uint8_t *dataPtr,
|
|
uint16_t length) {
|
|
uint8_t frameNum = 0;
|
|
uint16_t frameLen = 0;
|
|
OTA_TWS_DATA_T tCmd = {
|
|
cmdType,
|
|
};
|
|
|
|
ASSERT(length <= ARRAY_SIZE(tCmd.data), "ILLEGAL relay packet length");
|
|
|
|
/// split packet into servel frame to fulfill the max TWS data
|
|
/// transmission length requirement
|
|
if (length % OTA_TWS_PAYLOAD_MAX_LEN) {
|
|
frameNum = length / OTA_TWS_PAYLOAD_MAX_LEN + 1;
|
|
} else {
|
|
frameNum = length / OTA_TWS_PAYLOAD_MAX_LEN;
|
|
}
|
|
|
|
LOG_D("packet len:%d, splited frame num:%d", length, frameNum);
|
|
|
|
/// push all data into queue
|
|
for (uint8_t i = 0; i < frameNum; i++) {
|
|
if ((i + 1) == frameNum) {
|
|
tCmd.magicCode = OTA_RELAY_PACKET_MAGIC_CODE_COMPLETE;
|
|
tCmd.length = length % OTA_TWS_PAYLOAD_MAX_LEN;
|
|
} else {
|
|
tCmd.magicCode = OTA_RELAY_PACKET_MAGIC_CODE_INCOMPLETE;
|
|
tCmd.length = OTA_TWS_PAYLOAD_MAX_LEN;
|
|
}
|
|
|
|
frameLen = OTA_TWS_HEAD_SIZE + tCmd.length;
|
|
|
|
memcpy(tCmd.data, dataPtr + i * OTA_TWS_PAYLOAD_MAX_LEN, tCmd.length);
|
|
|
|
osMutexWait(txQueueMutexID, osWaitForever);
|
|
ASSERT(CQ_OK == EnCQueue(&otaEnv.txQueue, (CQItemType *)&frameLen,
|
|
sizeof(frameLen)),
|
|
"%s failed to push data to queue", __func__);
|
|
ASSERT(CQ_OK == EnCQueue(&otaEnv.txQueue, (CQItemType *)&tCmd, frameLen),
|
|
"%s failed to push data to queue", __func__);
|
|
osMutexRelease(txQueueMutexID);
|
|
}
|
|
|
|
/// inform tx thread to handle the data to be transmitted
|
|
osSignalSet(txThreadId, OTA_TWS_TX_SIGNAL);
|
|
}
|
|
|
|
/**
|
|
* @brief Check the validity of received data frame.
|
|
*
|
|
* @param dataPtr Pointer of received data
|
|
* @param length length of received data
|
|
* @return true Received data frame is valid
|
|
* @return false Received data frame is invalid
|
|
*/
|
|
static bool _tws_frame_validity_check(uint8_t *dataPtr, uint32_t length) {
|
|
bool isValid = true;
|
|
|
|
OTA_TWS_DATA_T *otaTwsData = (OTA_TWS_DATA_T *)dataPtr;
|
|
|
|
ASSERT(TWS_RELAY_DATA_MAX_SIZE >= length,
|
|
"received overloaded data packet!!!");
|
|
if (OTA_RELAY_PACKET_MAGIC_CODE_INCOMPLETE == otaEnv.currentMagicCode &&
|
|
otaTwsData->cmdType != otaEnv.currentCmdType) {
|
|
ASSERT(0, "bad frame is received!!!");
|
|
}
|
|
|
|
if (length != otaTwsData->length + OTA_TWS_HEAD_SIZE) {
|
|
LOG_W("INVALID packet, dataLen:%d, expected dataLen:%d", length,
|
|
otaTwsData->length + OTA_TWS_HEAD_SIZE);
|
|
isValid = false;
|
|
}
|
|
|
|
if (OTA_RELAY_PACKET_MAGIC_CODE_COMPLETE != otaTwsData->magicCode &&
|
|
OTA_RELAY_PACKET_MAGIC_CODE_INCOMPLETE != otaTwsData->magicCode) {
|
|
LOG_W("INVALID magic code:0x%08x", otaTwsData->magicCode);
|
|
isValid = false;
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
/**
|
|
* @brief Response the master for the OTA_COMMAND received.
|
|
*
|
|
* This function will response the master after a whole packet receiving is
|
|
* done.
|
|
*
|
|
* @param status Status of receiving TWS relay data
|
|
*/
|
|
static void _tws_rsp(OTA_STATUS_E status) {
|
|
LOG_D("[%s] status:%s", __func__, _sts2str(status));
|
|
|
|
OTA_STATUS_E rsp = status;
|
|
_ota_relay_data(OTA_COMMAND_RSP, (const uint8_t *)&rsp, sizeof(rsp));
|
|
}
|
|
#endif
|
|
|
|
OTA_COMMON_ENV_T *ota_common_get_env(void) { return &otaEnv; }
|
|
|
|
/**
|
|
* @brief Reset the OTA environment.
|
|
*
|
|
*/
|
|
static void _reset_env(void) {
|
|
LOG_D("[%s]", __func__);
|
|
memset(&otaEnv, 0, sizeof(otaEnv));
|
|
|
|
#ifdef __APP_USER_DATA_NV_FLASH_OFFSET__
|
|
otaEnv.userDataNvFlashOffset = __APP_USER_DATA_NV_FLASH_OFFSET__;
|
|
#else
|
|
otaEnv.userDataNvFlashOffset =
|
|
hal_norflash_get_flash_total_size(HAL_NORFLASH_ID_0) - 2 * 4096;
|
|
#endif
|
|
|
|
otaEnv.flashOffsetOfUserDataPool = otaEnv.userDataNvFlashOffset;
|
|
_update_ota_user(OTA_USER_NUM);
|
|
|
|
#ifdef OTA_NVRAM
|
|
// gOtaCtx.cfg.clearUserData = false;
|
|
// gOtaCtx.flashOffsetOfFactoryDataPool =
|
|
// otaEnv.otaCommon->userDataNvFlashOffset + FLASH_SECTOR_SIZE_IN_BYTES;
|
|
#endif
|
|
|
|
#ifdef IBRT
|
|
/// init tws TX queue
|
|
osMutexWait(txQueueMutexID, osWaitForever);
|
|
InitCQueue(&(otaEnv.txQueue), ARRAY_SIZE(otaEnv.txBuf),
|
|
(CQItemType *)otaEnv.txBuf);
|
|
osMutexRelease(txQueueMutexID);
|
|
|
|
/// init tws RX queue
|
|
osMutexWait(rxQueueMutexID, osWaitForever);
|
|
InitCQueue(&(otaEnv.rxQueue), ARRAY_SIZE(otaEnv.rxBuf),
|
|
(CQItemType *)otaEnv.rxBuf);
|
|
osMutexRelease(rxQueueMutexID);
|
|
#endif
|
|
}
|
|
|
|
void ota_common_init_handler(void) {
|
|
LOG_D("[%s]", __func__);
|
|
|
|
/// init OTA related nvrecord pointer
|
|
nv_record_ota_init();
|
|
|
|
/// init common used flash module
|
|
ota_common_init_flash(
|
|
(uint8_t)NORFLASH_API_MODULE_ID_OTA, 0,
|
|
(OTA_FLASH_LOGIC_ADDR + (
|
|
#ifdef __APP_USER_DATA_NV_FLASH_OFFSET__
|
|
__APP_USER_DATA_NV_FLASH_OFFSET__
|
|
#else
|
|
(hal_norflash_get_flash_total_size(
|
|
HAL_NORFLASH_ID_0) -
|
|
2 * 4096)
|
|
#endif
|
|
& 0xffffff)),
|
|
0);
|
|
|
|
#ifdef IBRT
|
|
if (txThreadId == NULL) {
|
|
txThreadId = osThreadCreate(osThread(_ota_tws_thread), NULL);
|
|
}
|
|
|
|
/// init tws TX queue mutex
|
|
if (txQueueMutexID == NULL) {
|
|
txQueueMutexID = osMutexCreate((osMutex(twsTxQueueMutex)));
|
|
}
|
|
|
|
/// init tws RX queue mutex
|
|
if (rxQueueMutexID == NULL) {
|
|
rxQueueMutexID = osMutexCreate((osMutex(twsRxQueueMutex)));
|
|
}
|
|
#endif
|
|
|
|
_reset_env();
|
|
}
|
|
|
|
void ota_common_enable_sanity_check(bool enable) {
|
|
LOG_I("sanity check enable state update:%d->%d", otaEnv.sanityCheckEnable,
|
|
enable);
|
|
otaEnv.sanityCheckEnable = enable;
|
|
}
|
|
|
|
void ota_common_init_flash(uint8_t module, uint32_t baseAddr, uint32_t len,
|
|
uint32_t imageHandler) {
|
|
enum NORFLASH_API_RET_T ret;
|
|
uint32_t block_size = 0;
|
|
uint32_t sector_size = 0;
|
|
uint32_t page_size = 0;
|
|
|
|
hal_norflash_get_size(HAL_NORFLASH_ID_0, NULL, &block_size, §or_size,
|
|
&page_size);
|
|
|
|
LOG_I("%s module:%d, startAddr:0x%x, len:0x%x", __func__, module, baseAddr,
|
|
len);
|
|
ret = norflash_api_register((NORFLASH_API_MODULE_ID_T)module,
|
|
HAL_NORFLASH_ID_0, baseAddr, len, block_size,
|
|
sector_size, page_size, OTA_NORFLASH_BUFFER_LEN,
|
|
(NORFLASH_API_OPERA_CB)imageHandler);
|
|
|
|
ASSERT(ret == NORFLASH_API_OK,
|
|
"ota_init_flash: norflash_api register failed,ret = %d.", ret);
|
|
|
|
#ifdef FLASH_SUSPEND
|
|
norflash_suspend_check_irq(AUDMA_IRQn);
|
|
norflash_suspend_check_irq(ISDATA_IRQn);
|
|
norflash_suspend_check_irq(ISDATA1_IRQn);
|
|
#endif
|
|
}
|
|
|
|
bool ota_common_is_in_progress(void) {
|
|
bool ret = false;
|
|
|
|
if (OTA_STAGE_IDLE != otaEnv.currentStage) {
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void ota_common_registor_command_handler(OTA_COMMAND_E cmdType,
|
|
void *cmdHandler) {
|
|
if (otaEnv.cmdHandler[cmdType]) {
|
|
LOG_W("handler for OTA command %d is not NULL", cmdType);
|
|
}
|
|
|
|
otaEnv.cmdHandler[cmdType] = (OTA_CMD_HANDLER_T)cmdHandler;
|
|
}
|
|
|
|
OTA_STATUS_E ota_common_command_received_handler(OTA_COMMAND_E cmdType,
|
|
void *cmdInfo,
|
|
uint16_t cmdLen) {
|
|
LOG_D("cmd received:%s", _cmd2str(cmdType));
|
|
otaEnv.status = OTA_STATUS_OK;
|
|
OTA_STATUS_E temp = OTA_STATUS_OK;
|
|
|
|
/// init OTA progress when received begin command
|
|
if (OTA_COMMAND_BEGIN == cmdType) {
|
|
ASSERT(OTA_STAGE_IDLE == otaEnv.currentStage,
|
|
"Received begin command while not in IDLE stage");
|
|
OTA_BEGIN_PARAM_T *c = (OTA_BEGIN_PARAM_T *)cmdInfo;
|
|
|
|
/// update the ota user to ota common layer
|
|
_update_ota_user(c->user);
|
|
|
|
/// update the new image size according to the image size param
|
|
LOG_I("total image size update:%d->%d", otaEnv.totalImageSize,
|
|
c->imageSize);
|
|
otaEnv.totalImageSize = c->imageSize;
|
|
|
|
/// update the new image offset according to the start offset param
|
|
ASSERT(0 == (c->startOffset % FLASH_SECTOR_SIZE_IN_BYTES),
|
|
"Resumed start offset is not 4KB aligned!");
|
|
LOG_I("programOffset&receivedDataSize is update:%d->%d",
|
|
otaEnv.newImageProgramOffset, c->startOffset);
|
|
otaEnv.newImageProgramOffset = c->startOffset;
|
|
otaEnv.receivedDataSize = c->startOffset;
|
|
|
|
/// update the device index
|
|
LOG_I("deviceId update:%d->%d", otaEnv.deviceId, c->device);
|
|
otaEnv.deviceId = c->device;
|
|
|
|
/// update the version info and version length
|
|
memcpy(otaEnv.version, c->version, c->versionLen);
|
|
otaEnv.versionLen = c->versionLen;
|
|
|
|
/// update the flash offset of new image
|
|
LOG_I("newImageFlashOffset update:0x%02x->0x%02x",
|
|
otaEnv.newImageFlashOffset, c->flashOffset);
|
|
otaEnv.newImageFlashOffset = c->flashOffset;
|
|
|
|
/// set the OTA stage to ongoing
|
|
_set_ota_stage(OTA_STAGE_ONGOING);
|
|
|
|
/// system layer configurations to guarentee the performance
|
|
/// of all functionalities
|
|
_enter_ota_state(c->path);
|
|
}
|
|
|
|
/// execute the custom configuration
|
|
if (OTA_COMMAND_NUM != cmdType) {
|
|
if (otaEnv.cmdHandler[cmdType]) {
|
|
temp = otaEnv.cmdHandler[cmdType](cmdInfo, cmdLen);
|
|
LOG_I("ota command %s handle done, status:%s", _cmd2str(cmdType),
|
|
_sts2str(temp));
|
|
|
|
if (OTA_STATUS_OK != temp) {
|
|
otaEnv.status = temp;
|
|
}
|
|
} else {
|
|
LOG_W("ota cmd %s is not handled by customor", _cmd2str(cmdType));
|
|
}
|
|
} else {
|
|
LOG_E("INVALID cmd:%d", cmdType);
|
|
}
|
|
|
|
/// update the OTA progress
|
|
if (OTA_STATUS_OK == otaEnv.status) {
|
|
if (!nv_record_ota_update_info(otaEnv.currentUser, otaEnv.deviceId,
|
|
otaEnv.currentStage, otaEnv.totalImageSize,
|
|
otaEnv.version)) {
|
|
LOG_W("update info failed");
|
|
otaEnv.status = OTA_STATUS_ERROR;
|
|
}
|
|
}
|
|
|
|
/// special process for OTA_ABORT&&OTA_APPLY command
|
|
if ((OTA_COMMAND_ABORT == cmdType) || (OTA_COMMAND_APPLY == cmdType)) {
|
|
_reset_env();
|
|
}
|
|
|
|
LOG_I("%s cmd process result:%s", __func__, _sts2str(otaEnv.status));
|
|
return otaEnv.status;
|
|
}
|
|
|
|
#ifdef OTA_NVRAM
|
|
static void ota_update_nv_data(void) {
|
|
if (otaEnv.configuration.isToClearUserData) {
|
|
enum NORFLASH_API_MODULE_ID_T mod =
|
|
_get_flash_module_from_ota_device(otaEnv.deviceId);
|
|
app_flash_page_erase(mod, sectorIndexInFlash * FLASH_SECTOR_SIZE_IN_BYTES);
|
|
}
|
|
|
|
if (otaEnv.configuration.isToRenameBT || otaEnv.configuration.isToRenameBLE ||
|
|
otaEnv.configuration.isToUpdateBTAddr ||
|
|
otaEnv.configuration.isToUpdateBLEAddr) {
|
|
uint32_t *pOrgFactoryData, *pUpdatedFactoryData;
|
|
pOrgFactoryData = (uint32_t *)(OTA_FLASH_LOGIC_ADDR +
|
|
otaEnv.flashOffsetOfFactoryDataPool);
|
|
memcpy(otaEnv.dataCacheBuffer, (uint8_t *)pOrgFactoryData,
|
|
FLASH_SECTOR_SIZE_IN_BYTES);
|
|
pUpdatedFactoryData = (uint32_t *)&(otaEnv.dataCacheBuffer);
|
|
|
|
if (NVREC_DEV_VERSION_1 == nv_record_dev_rev) {
|
|
if (otaEnv.configuration.isToRenameBT) {
|
|
memset((uint8_t *)(pUpdatedFactoryData + dev_name), 0,
|
|
sizeof(uint32_t) * (dev_bt_addr - dev_name));
|
|
|
|
memcpy((uint8_t *)(pUpdatedFactoryData + dev_name),
|
|
(uint8_t *)(otaEnv.configuration.newBTName), NAME_LENGTH);
|
|
}
|
|
|
|
if (otaEnv.configuration.isToUpdateBTAddr) {
|
|
memcpy((uint8_t *)(pUpdatedFactoryData + dev_bt_addr),
|
|
(uint8_t *)(otaEnv.configuration.newBTAddr), BD_ADDR_LENGTH);
|
|
}
|
|
|
|
if (otaEnv.configuration.isToUpdateBLEAddr) {
|
|
memcpy((uint8_t *)(pUpdatedFactoryData + dev_ble_addr),
|
|
(uint8_t *)(otaEnv.configuration.newBLEAddr), BD_ADDR_LENGTH);
|
|
}
|
|
|
|
pUpdatedFactoryData[dev_crc] =
|
|
crc32(0, (uint8_t *)(&pUpdatedFactoryData[dev_reserv1]),
|
|
(dev_data_len - dev_reserv1) * sizeof(uint32_t));
|
|
} else {
|
|
if (otaEnv.configuration.isToRenameBT) {
|
|
memset((uint8_t *)(pUpdatedFactoryData + rev2_dev_name), 0,
|
|
sizeof(uint32_t) * (rev2_dev_bt_addr - rev2_dev_name));
|
|
|
|
memcpy((uint8_t *)(pUpdatedFactoryData + rev2_dev_name),
|
|
(uint8_t *)(otaEnv.configuration.newBTName), NAME_LENGTH);
|
|
}
|
|
|
|
if (otaEnv.configuration.isToRenameBLE) {
|
|
memset((uint8_t *)(pUpdatedFactoryData + rev2_dev_ble_name), 0,
|
|
sizeof(uint32_t) * (rev2_dev_section_end - rev2_dev_ble_name));
|
|
|
|
memcpy((uint8_t *)(pUpdatedFactoryData + rev2_dev_ble_name),
|
|
(uint8_t *)(otaEnv.configuration.newBleName),
|
|
BLE_NAME_LEN_IN_NV);
|
|
}
|
|
|
|
if (otaEnv.configuration.isToUpdateBTAddr) {
|
|
memcpy((uint8_t *)(pUpdatedFactoryData + rev2_dev_bt_addr),
|
|
(uint8_t *)(otaEnv.configuration.newBTAddr), BD_ADDR_LENGTH);
|
|
}
|
|
|
|
if (otaEnv.configuration.isToUpdateBLEAddr) {
|
|
memcpy((uint8_t *)(pUpdatedFactoryData + rev2_dev_ble_addr),
|
|
(uint8_t *)(otaEnv.configuration.newBLEAddr), BD_ADDR_LENGTH);
|
|
}
|
|
|
|
pUpdatedFactoryData[dev_crc] = crc32(
|
|
0, (uint8_t *)(&pUpdatedFactoryData[rev2_dev_section_start_reserved]),
|
|
pUpdatedFactoryData[rev2_dev_data_len]);
|
|
}
|
|
|
|
enum NORFLASH_API_MODULE_ID_T mod =
|
|
_get_flash_module_from_ota_device(otaEnv.deviceId);
|
|
app_flash_page_erase(mod, sectorIndexInFlash * FLASH_SECTOR_SIZE_IN_BYTES);
|
|
|
|
app_flash_page_program(mod, otaEnv.flashOffsetOfFactoryDataPool,
|
|
(uint8_t *)pUpdatedFactoryData,
|
|
FLASH_SECTOR_SIZE_IN_BYTES);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef IBRT
|
|
void ota_common_registor_relay_needed_handler(void *handler) {
|
|
if (otaEnv.customRelayNeededHandler) {
|
|
LOG_W("handler for custom relay needed judge is not NULL");
|
|
}
|
|
|
|
otaEnv.customRelayNeededHandler = (CUSTOM_RELAY_NEEDED_FUNC_T)handler;
|
|
}
|
|
|
|
void ota_common_registor_peer_cmd_received_handler(void *handler) {
|
|
if (otaEnv.peerCmdReceivedHandler) {
|
|
LOG_W("handler for peer cmd received is not NULL");
|
|
}
|
|
|
|
otaEnv.peerCmdReceivedHandler = (PEER_CMD_RECEIVED_HANDLER_T)handler;
|
|
}
|
|
|
|
OTA_STATUS_E ota_common_relay_data_to_peer(OTA_COMMAND_E cmdType,
|
|
const uint8_t *data, uint16_t len) {
|
|
OTA_STATUS_E status = OTA_STATUS_OK;
|
|
if (_relay_data_needed()) {
|
|
_ota_relay_data(cmdType, (const uint8_t *)data, len);
|
|
} else {
|
|
status = OTA_STATUS_ERROR;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
OTA_STATUS_E ota_common_receive_peer_rsp(void) {
|
|
OTA_STATUS_E status = OTA_STATUS_OK;
|
|
|
|
if (_relay_data_needed()) {
|
|
/// get current thread ID as tws rx thread
|
|
rxThreadId = osThreadGetId();
|
|
|
|
/// pending current thread to waitting for slave response
|
|
evt = osSignalWait(OTA_TWS_RX_SIGNAL, OTA_TWS_RELAY_WAITTIME);
|
|
|
|
if (evt.status == osEventTimeout) {
|
|
status = OTA_STATUS_ERROR_RELAY_TIMEOUT;
|
|
LOG_W("[%s]SignalWait TIMEOUT!", __func__);
|
|
} else if (osEventSignal == evt.status) {
|
|
status = ota_common_get_peer_result();
|
|
}
|
|
|
|
///.clear the excute result of this time
|
|
_update_peer_result(OTA_STATUS_OK);
|
|
} else {
|
|
status = OTA_STATUS_ERROR;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
OTA_STATUS_E ota_common_fw_data_write(const uint8_t *data, uint16_t len) {
|
|
OTA_STATUS_E status = OTA_STATUS_OK;
|
|
uint16_t leftDataSize = len;
|
|
uint32_t offsetInReceivedRawData = 0;
|
|
|
|
do {
|
|
uint32_t bytesToCopy;
|
|
// copy to data buffer
|
|
if ((otaEnv.dataCacheBufferOffset + leftDataSize) >
|
|
OTA_DATA_CACHE_BUFFER_SIZE) {
|
|
bytesToCopy = OTA_DATA_CACHE_BUFFER_SIZE - otaEnv.dataCacheBufferOffset;
|
|
} else {
|
|
bytesToCopy = leftDataSize;
|
|
}
|
|
|
|
leftDataSize -= bytesToCopy;
|
|
|
|
memcpy(&otaEnv.dataCacheBuffer[otaEnv.dataCacheBufferOffset],
|
|
&data[offsetInReceivedRawData], bytesToCopy);
|
|
offsetInReceivedRawData += bytesToCopy;
|
|
otaEnv.dataCacheBufferOffset += bytesToCopy;
|
|
|
|
ASSERT(otaEnv.dataCacheBufferOffset <= OTA_DATA_CACHE_BUFFER_SIZE,
|
|
"bad math in %s", __func__);
|
|
if (OTA_DATA_CACHE_BUFFER_SIZE == otaEnv.dataCacheBufferOffset) {
|
|
_flush_data_to_flash(
|
|
otaEnv.dataCacheBuffer, OTA_DATA_CACHE_BUFFER_SIZE,
|
|
(otaEnv.newImageProgramOffset + otaEnv.newImageFlashOffset), false);
|
|
otaEnv.newImageProgramOffset += OTA_DATA_CACHE_BUFFER_SIZE;
|
|
otaEnv.dataCacheBufferOffset = 0;
|
|
}
|
|
} while (offsetInReceivedRawData < len);
|
|
|
|
otaEnv.receivedDataSize += len;
|
|
|
|
// check whether all image data has been received
|
|
if (otaEnv.receivedDataSize == otaEnv.totalImageSize) {
|
|
LOG_D("The final image programming and crc32 check.");
|
|
|
|
// flush any partial buffer to flash
|
|
if (otaEnv.dataCacheBufferOffset != 0) {
|
|
_flush_data_to_flash(
|
|
otaEnv.dataCacheBuffer, otaEnv.dataCacheBufferOffset,
|
|
(otaEnv.newImageProgramOffset + otaEnv.newImageFlashOffset), true);
|
|
}
|
|
|
|
if (OTA_DEVICE_APP == otaEnv.deviceId) {
|
|
bool check = true;
|
|
|
|
/// check the sanity if required
|
|
if (otaEnv.sanityCheckEnable) {
|
|
check = _image_sanity_check();
|
|
}
|
|
|
|
if (check) {
|
|
/// update the magic code of the application image
|
|
_update_magic_number(NORMAL_BOOT);
|
|
|
|
/// check the crc32 of the received image data
|
|
if (_compute_whole_image_crc()) {
|
|
LOG_I("Whole image verification pass.");
|
|
|
|
/// update the OTA stage to OTA done
|
|
_set_ota_stage(OTA_STAGE_DONE);
|
|
} else {
|
|
LOG_W("image verification failed @%d", __LINE__);
|
|
|
|
/// update the OTA stage to OTA idle
|
|
_set_ota_stage(OTA_STAGE_IDLE);
|
|
}
|
|
} else {
|
|
/// sanity check failed
|
|
LOG_W("image verification failed @%d", __LINE__);
|
|
|
|
/// update the OTA stage to OTA idle
|
|
_set_ota_stage(OTA_STAGE_IDLE);
|
|
}
|
|
} else {
|
|
LOG_I("download finished, device:%d", otaEnv.deviceId);
|
|
|
|
/// update the OTA stage to OTA idle
|
|
_set_ota_stage(OTA_STAGE_DONE);
|
|
}
|
|
|
|
/// whole image verification failed somehow
|
|
if (OTA_STAGE_IDLE == otaEnv.currentStage) {
|
|
nv_record_ota_update_info(otaEnv.currentUser, otaEnv.deviceId,
|
|
otaEnv.currentStage, 0, INVALID_VERSION_STR);
|
|
|
|
status = OTA_STATUS_ERROR_CHECKSUM;
|
|
} else //!< whole image verification passed
|
|
{
|
|
nv_record_ota_update_info(otaEnv.currentUser, otaEnv.deviceId,
|
|
otaEnv.currentStage, otaEnv.totalImageSize,
|
|
otaEnv.version);
|
|
}
|
|
|
|
/// exit the OTA state
|
|
_exit_ota_state();
|
|
} else //!< whole image revceive not finished
|
|
{
|
|
LOG_D("Received image size:%d", otaEnv.receivedDataSize);
|
|
|
|
/// update the break point if it is changed
|
|
if ((otaEnv.receivedDataSize - otaEnv.breakPoint) >=
|
|
OTA_BREAKPOINT_STORE_GRANULARITY) {
|
|
otaEnv.breakPoint =
|
|
(otaEnv.receivedDataSize / OTA_BREAKPOINT_STORE_GRANULARITY) *
|
|
OTA_BREAKPOINT_STORE_GRANULARITY;
|
|
|
|
LOG_I("update record offset to %d", otaEnv.breakPoint);
|
|
nv_record_ota_update_breakpoint(otaEnv.currentUser, otaEnv.deviceId,
|
|
otaEnv.breakPoint);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
void ota_common_apply_current_fw(void) {
|
|
OTA_BOOT_INFO_T otaBootInfo = {COPY_NEW_IMAGE, otaEnv.totalImageSize,
|
|
otaEnv.crc32OfImage};
|
|
_update_boot_info(&otaBootInfo);
|
|
|
|
#ifdef OTA_NVRAM
|
|
ota_update_nv_data();
|
|
#endif
|
|
|
|
app_start_postponed_reset();
|
|
}
|
|
|
|
void ota_common_on_relay_data_received(uint8_t *ptrParam, uint32_t paramLen) {
|
|
ASSERT(ptrParam, "invalid data pointer received in %s", __func__);
|
|
ASSERT(paramLen <= TWS_RELAY_DATA_MAX_SIZE,
|
|
"illegal parameter length received in %s", __func__);
|
|
|
|
OTA_TWS_DATA_T *otaTwsData = (OTA_TWS_DATA_T *)ptrParam;
|
|
LOG_D("[%s] paramLen:%d, cmdType:%d|%s", __func__, paramLen,
|
|
otaTwsData->cmdType, _cmd2str(otaTwsData->cmdType));
|
|
|
|
OTA_STATUS_E status = OTA_STATUS_OK;
|
|
OTA_BEGIN_PARAM_T *beginInfo = NULL;
|
|
uint16_t packetLen = 0;
|
|
|
|
if (_tws_frame_validity_check(ptrParam, paramLen)) {
|
|
/// update the magic code and command type
|
|
otaEnv.currentMagicCode = otaTwsData->magicCode;
|
|
otaEnv.currentCmdType = otaTwsData->cmdType;
|
|
|
|
/// push received data into tws OTA data receive queue
|
|
osMutexWait(rxQueueMutexID, osWaitForever);
|
|
ASSERT(CQ_OK == EnCQueue(&otaEnv.rxQueue, (CQItemType *)otaTwsData->data,
|
|
(paramLen - OTA_TWS_HEAD_SIZE)),
|
|
"%s failed to push data to queue, avaiable:%d, push:%d", __func__,
|
|
AvailableOfCQueue(&otaEnv.rxQueue), (paramLen - OTA_TWS_HEAD_SIZE));
|
|
osMutexRelease(rxQueueMutexID);
|
|
|
|
/// whole packet from APP received done
|
|
if (OTA_RELAY_PACKET_MAGIC_CODE_COMPLETE == otaEnv.currentMagicCode) {
|
|
osMutexWait(rxQueueMutexID, osWaitForever);
|
|
packetLen = LengthOfCQueue(&otaEnv.rxQueue);
|
|
LOG_D("length of rx queue:%d", packetLen);
|
|
DeCQueue(&otaEnv.rxQueue, otaEnv.tempRxBuf, packetLen);
|
|
osMutexRelease(rxQueueMutexID);
|
|
|
|
switch (otaEnv.currentCmdType) {
|
|
case OTA_COMMAND_RSP:
|
|
/// retrieve and update the status of peer
|
|
status = ((OTA_RESPONSE_PARAM_T *)otaEnv.tempRxBuf)->status;
|
|
_update_peer_result(status);
|
|
|
|
/// inform the receiving thread to proceed
|
|
osSignalSet(rxThreadId, OTA_TWS_RX_SIGNAL);
|
|
break;
|
|
|
|
/// add other supported command here
|
|
case OTA_COMMAND_BEGIN:
|
|
beginInfo = (OTA_BEGIN_PARAM_T *)otaTwsData->data;
|
|
if (beginInfo->initializer) {
|
|
beginInfo->initializer();
|
|
} else {
|
|
LOG_E("initializer not registored");
|
|
}
|
|
case OTA_COMMAND_DATA:
|
|
case OTA_COMMAND_APPLY:
|
|
case OTA_COMMAND_ABORT:
|
|
if (otaEnv.peerCmdReceivedHandler) {
|
|
status = otaEnv.peerCmdReceivedHandler(
|
|
otaEnv.currentCmdType, (const uint8_t *)otaEnv.tempRxBuf,
|
|
packetLen);
|
|
} else {
|
|
LOG_W("peerCmdReceivedHandler is not registored");
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0, "INVALID cmd received");
|
|
status = OTA_STATUS_ERROR;
|
|
break;
|
|
}
|
|
|
|
/// response to master if needed
|
|
if (OTA_COMMAND_RSP != otaTwsData->cmdType) {
|
|
_tws_rsp(status);
|
|
}
|
|
|
|
otaEnv.currentMagicCode = OTA_RELAY_PACKET_MAGIC_CODE_INVALID;
|
|
otaEnv.currentCmdType = OTA_COMMAND_NUM;
|
|
}
|
|
} else {
|
|
LOG_W("Received data frame is invalid");
|
|
status = OTA_STATUS_ERROR;
|
|
|
|
switch (otaTwsData->cmdType) {
|
|
case OTA_COMMAND_RSP:
|
|
_update_peer_result(status);
|
|
osSignalSet(rxThreadId, OTA_TWS_RX_SIGNAL);
|
|
break;
|
|
|
|
case OTA_COMMAND_BEGIN:
|
|
case OTA_COMMAND_DATA:
|
|
case OTA_COMMAND_APPLY:
|
|
case OTA_COMMAND_ABORT:
|
|
_tws_rsp(status);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0, "INVALID command received");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
POSSIBLY_UNUSED static void _ota_tws_deinit(void) {
|
|
ResetCQueue(&otaEnv.txQueue);
|
|
ResetCQueue(&otaEnv.rxQueue);
|
|
memset(otaEnv.txBuf, 0, ARRAY_SIZE(otaEnv.txBuf));
|
|
memset(otaEnv.rxBuf, 0, ARRAY_SIZE(otaEnv.rxBuf));
|
|
memset(otaEnv.tempRxBuf, 0, ARRAY_SIZE(otaEnv.tempRxBuf));
|
|
|
|
if (txThreadId) {
|
|
osThreadTerminate(txThreadId);
|
|
txThreadId = NULL;
|
|
}
|
|
|
|
if (txQueueMutexID != NULL) {
|
|
osMutexDelete(txQueueMutexID);
|
|
txQueueMutexID = NULL;
|
|
}
|
|
|
|
if (rxQueueMutexID != NULL) {
|
|
osMutexDelete(rxQueueMutexID);
|
|
rxQueueMutexID = NULL;
|
|
}
|
|
}
|
|
|
|
static void _ota_tws_thread(const void *arg) {
|
|
volatile uint16_t qLen = 0;
|
|
volatile uint16_t packetLen = 0;
|
|
|
|
while (1) {
|
|
osMutexWait(txQueueMutexID, osWaitForever);
|
|
qLen = LengthOfCQueue(&otaEnv.txQueue);
|
|
osMutexRelease(txQueueMutexID);
|
|
LOG_D("queued data len:%d", qLen);
|
|
|
|
while (qLen) {
|
|
/// check the validity of queue length
|
|
/// ASSERT(qLen <= OTA_MAX_MTU, "invalid OTA relay data len");
|
|
|
|
osMutexWait(txQueueMutexID, osWaitForever);
|
|
/// retrive and check the validity of packet length
|
|
DeCQueue(&otaEnv.txQueue, (CQItemType *)&packetLen, 2);
|
|
ASSERT(packetLen <= TWS_RELAY_DATA_MAX_SIZE,
|
|
"invalid OTA relay data len:%d", packetLen);
|
|
|
|
/// retrieve the data to transmit
|
|
DeCQueue(&otaEnv.txQueue, (CQItemType *)relayBuf, packetLen);
|
|
osMutexRelease(txQueueMutexID);
|
|
|
|
LOG_D("send data len:%d", packetLen);
|
|
|
|
/// send data to peer
|
|
tws_ctrl_send_cmd(IBRT_COMMON_OTA, relayBuf, packetLen);
|
|
memset(relayBuf, 0, TWS_RELAY_DATA_MAX_SIZE);
|
|
|
|
osMutexWait(txQueueMutexID, osWaitForever);
|
|
qLen = LengthOfCQueue(&otaEnv.txQueue);
|
|
osMutexRelease(txQueueMutexID);
|
|
}
|
|
|
|
osSignalWait(OTA_TWS_TX_SIGNAL, osWaitForever);
|
|
}
|
|
}
|
|
|
|
static void _sync_info_prepare_handler(uint8_t *buf, uint16_t *length) {
|
|
*length = OTA_DEVICE_CNT * sizeof(NV_OTA_INFO_T);
|
|
|
|
void *otaInfo = NULL;
|
|
nv_record_ota_get_ptr(&otaInfo);
|
|
|
|
memcpy(buf, otaInfo, *length);
|
|
}
|
|
|
|
static void _sync_info_received_handler(uint8_t *buf, uint16_t length) {
|
|
// uodate gsound info
|
|
// TODO:
|
|
}
|
|
|
|
void ota_common_tws_sync_init(void) {
|
|
TWS_SYNC_USER_T userOta = {
|
|
_sync_info_prepare_handler,
|
|
_sync_info_received_handler,
|
|
_sync_info_prepare_handler,
|
|
NULL,
|
|
NULL,
|
|
};
|
|
|
|
app_tws_if_register_sync_user(TWS_SYNC_USER_OTA, &userOta);
|
|
}
|
|
#endif
|