pinebuds/services/ota/ota_common.cpp

1444 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, &sector_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