pinebuds/services/nv_section/fpga_section/nvrecord_externsion.c

482 lines
16 KiB
C

/***************************************************************************
*
* Copyright 2015-2019 BES.
* 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.
*
****************************************************************************/
#ifdef NEW_NV_RECORD_ENABLED
#include "besbt.h"
#include "cmsis.h"
#include "crc32.h"
#include "customparam_section.h"
#include "hal_norflash.h"
#include "hal_trace.h"
#include "mpu.h"
#include "norflash_api.h"
#include "norflash_drv.h"
#include "nvrecord_ble.h"
#include "nvrecord_bt.h"
#include "nvrecord_dma_config.h"
#include "nvrecord_env.h"
#include "nvrecord_extension.h"
#include "nvrecord_fp_account_key.h"
#include <assert.h>
#include <stdbool.h>
#include <string.h>
extern uint32_t __userdata_start[];
extern uint32_t __userdata_end[];
extern void nvrecord_rebuild_system_env(struct nvrecord_env_t *pSystemEnv);
extern void nvrecord_rebuild_paired_bt_dev_info(
NV_RECORD_PAIRED_BT_DEV_INFO_T *pPairedBtInfo);
#ifdef GFPS_ENABLED
extern void
nvrecord_rebuild_fp_account_key(NV_FP_ACCOUNT_KEY_RECORD_T *pFpAccountKey);
#endif
#ifdef NVREC_BAIDU_DATA_SECTION
extern void
nvrecord_rebuild_dma_configuration(NV_DMA_CONFIGURATION_T *pDmaConfig);
#endif
typedef enum {
NV_STATE_IDLE,
NV_STATE_ERASED,
} NV_STATE;
NV_EXTENSION_RECORD_T *nvrecord_extension_p = NULL;
static uint8_t isNvExtentionPendingForUpdate = false;
static NV_STATE isNvExtentionState = NV_STATE_IDLE;
/*
*Note: the NV_EXTENSION_MIRROR_RAM_SIZE must be power of 2
*/
#if defined(__ARM_ARCH_8M_MAIN__)
#define __NV_BUF_MPU_ALIGNED __ALIGNED(0x20)
#else
/*
* armv7 mpu require the address must be aligned to the section size and
* the section size must be algined to power of 2
*/
#define __NV_BUF_MPU_ALIGNED __ALIGNED(NV_EXTENSION_MIRROR_RAM_SIZE)
#endif
static NV_MIRROR_BUF_T local_extension_data __NV_BUF_MPU_ALIGNED __attribute__((
section(".sram_data"))) = {
.nv_record = {
{
// header
NV_EXTENSION_MAGIC_NUMBER,
NV_EXTENSION_MAJOR_VERSION,
NV_EXTENSION_MINOR_VERSION,
NV_EXTENSION_VALID_LEN,
0,
},
{
// system info
},
{// bt_pair_info
0},
{
// ble_pair_info
},
#ifdef TWS_SYSTEM_ENABLED
{
// tws_info
},
#endif
#ifdef GFPS_ENABLED
{// fp_account_key_rec
0},
#endif
#ifdef NVREC_BAIDU_DATA_SECTION
{
// dma_config
BAIDU_DATA_DEF_FM_FREQ,
},
#endif
#ifdef TILE_DATAPATH
{{0}},
#endif
#if defined(BISTO_ENABLED)
{
true,
GSOUND_OTA_STATUS_NONE,
0,
0,
},
#endif
#ifdef TX_IQ_CAL
{
BT_IQ_INVALID_MAGIC_NUM,
{0},
{0},
},
#endif
// TODO:
// If want to extend the nvrecord while keeping the history information,
// append the new items to the tail of NV_EXTENSION_RECORD_T and
// set their intial content here
}};
STATIC_ASSERT(sizeof(local_extension_data) <= NV_EXTENSION_MIRROR_RAM_SIZE,
"NV local buffer too small");
static void _nv_record_extension_init(void) {
enum NORFLASH_API_RET_T result;
uint32_t sector_size = 0;
uint32_t block_size = 0;
uint32_t page_size = 0;
hal_norflash_get_size(HAL_NORFLASH_ID_0, NULL, &block_size, &sector_size,
&page_size);
result = norflash_api_register(
NORFLASH_API_MODULE_ID_USERDATA_EXT, HAL_NORFLASH_ID_0,
((uint32_t)__userdata_start),
((uint32_t)__userdata_end - (uint32_t)__userdata_start), block_size,
sector_size, page_size, NV_EXTENSION_SIZE * 2, nv_extension_callback);
ASSERT(result == NORFLASH_API_OK,
"_nv_record_extension_init: module register failed! result = %d.",
result);
}
uint32_t nv_record_pre_write_operation(void) {
uint32_t lock = int_lock_global();
mpu_clear(MPU_ID_USER_DATA_SECTION);
return lock;
}
void nv_record_post_write_operation(uint32_t lock) {
int ret = 0;
uint32_t nv_start = (uint32_t)&local_extension_data.nv_record;
uint32_t len = NV_EXTENSION_MIRROR_RAM_SIZE;
ret = mpu_set(MPU_ID_USER_DATA_SECTION, nv_start, len, 0, MPU_ATTR_READ);
int_unlock_global(lock);
TRACE(2, "set mpu 0x%x len %d result %d", nv_start, len, ret);
}
static void nv_record_extension_init(void) {
if (NULL == nvrecord_extension_p) {
uint32_t lock = nv_record_pre_write_operation();
_nv_record_extension_init();
nvrecord_extension_p = &local_extension_data.nv_record;
NVRECORD_HEADER_T *pExtRecInFlash =
&(((NV_EXTENSION_RECORD_T *)__userdata_start)->header);
TRACE(2, "nv ext magic 0x%x valid len %d", pExtRecInFlash->magicNumber,
pExtRecInFlash->validLen);
if ((NV_EXTENSION_MAJOR_VERSION == pExtRecInFlash->majorVersion) &&
(NV_EXTENSION_MAGIC_NUMBER == pExtRecInFlash->magicNumber)) {
// check whether the data length is valid
if (pExtRecInFlash->validLen <=
(NV_EXTENSION_SIZE - NV_EXTENSION_HEADER_SIZE)) {
// check crc32
uint32_t crc =
crc32(0, ((uint8_t *)pExtRecInFlash + NV_EXTENSION_HEADER_SIZE),
pExtRecInFlash->validLen);
TRACE(2, "generated crc32 0x%x crc32 in flash 0x%x", crc,
pExtRecInFlash->crc32);
if (crc == pExtRecInFlash->crc32) {
// correct
TRACE(0, "Nv extension is valid.");
TRACE(1, "Former nv ext valid len %d", pExtRecInFlash->validLen);
TRACE(1, "Current FW version nv ext valid len %d",
NV_EXTENSION_VALID_LEN);
if (NV_EXTENSION_VALID_LEN < pExtRecInFlash->validLen) {
TRACE(0, "Valid length of extension must be increased,"
"jump over the recovery but use the default value.");
} else {
memcpy(((uint8_t *)nvrecord_extension_p) + NV_EXTENSION_HEADER_SIZE,
((uint8_t *)pExtRecInFlash) + NV_EXTENSION_HEADER_SIZE,
pExtRecInFlash->validLen);
// frimware updates the nv extension data structure
if (pExtRecInFlash->validLen < NV_EXTENSION_VALID_LEN) {
// update the version number
nvrecord_extension_p->header.minorVersion =
NV_EXTENSION_MINOR_VERSION;
// re-calculate the crc32
nvrecord_extension_p->header.crc32 = crc32(
0,
((uint8_t *)nvrecord_extension_p + NV_EXTENSION_HEADER_SIZE),
NV_EXTENSION_VALID_LEN);
// need to update the content in the flash
nv_record_extension_update();
}
goto exit;
}
}
}
}
// the nv extension is invalid, should be recreated
nvrecord_rebuild_system_env(&(nvrecord_extension_p->system_info));
nvrecord_rebuild_paired_bt_dev_info(&(nvrecord_extension_p->bt_pair_info));
nvrecord_rebuild_paired_ble_dev_info(
&(nvrecord_extension_p->ble_pair_info));
#ifdef GFPS_ENABLED
nvrecord_rebuild_fp_account_key(
&(nvrecord_extension_p->fp_account_key_rec));
#endif
#ifdef NVREC_BAIDU_DATA_SECTION
nvrecord_rebuild_dma_configuration(&(nvrecord_extension_p->dma_config));
#endif
pExtRecInFlash->crc32 =
crc32(0, ((uint8_t *)nvrecord_extension_p + NV_EXTENSION_HEADER_SIZE),
NV_EXTENSION_VALID_LEN);
// need to update the content in the flash
nv_record_extension_update();
exit:
nv_record_post_write_operation(lock);
}
}
NV_EXTENSION_RECORD_T *nv_record_get_extension_entry_ptr(void) {
return nvrecord_extension_p;
}
void nv_record_extension_update(void) { isNvExtentionPendingForUpdate = true; }
static void nv_record_extension_flush(bool is_async) {
enum NORFLASH_API_RET_T ret;
uint32_t lock;
uint32_t crc;
#if defined(FLASH_SUSPEND) && defined(NVREC_BURN_TEST)
static uint32_t stime = 0;
if (hal_sys_timer_get() - stime > MS_TO_TICKS(20000)) {
stime = hal_sys_timer_get();
isNvExtentionPendingForUpdate = true;
}
#endif
if (isNvExtentionPendingForUpdate) {
if (is_async) {
lock = int_lock_global();
if (isNvExtentionState == NV_STATE_IDLE) {
ret = norflash_api_erase(NORFLASH_API_MODULE_ID_USERDATA_EXT,
(uint32_t)(__userdata_start),
NV_EXTENSION_SIZE, true);
if (ret == NORFLASH_API_OK) {
isNvExtentionState = NV_STATE_ERASED;
TRACE(2, "%s: norflash_api_erase ok,addr = 0x%x.", __func__,
(uint32_t)__userdata_start);
} else if (ret == NORFLASH_API_BUFFER_FULL) {
// do nothing.
} else {
ASSERT(0, "%s: norflash_api_erase err,ret = %d,addr = 0x%x.",
__func__, ret, (uint32_t)__userdata_start);
}
}
if (isNvExtentionState == NV_STATE_ERASED) {
uint32_t tmpLock = nv_record_pre_write_operation();
crc =
crc32(0, (uint8_t *)nvrecord_extension_p + NV_EXTENSION_HEADER_SIZE,
NV_EXTENSION_VALID_LEN);
nvrecord_extension_p->header.crc32 = crc;
nv_record_post_write_operation(tmpLock);
ret = norflash_api_write(
NORFLASH_API_MODULE_ID_USERDATA_EXT, (uint32_t)(__userdata_start),
(uint8_t *)nvrecord_extension_p, NV_EXTENSION_SIZE, true);
if (ret == NORFLASH_API_OK) {
isNvExtentionPendingForUpdate = false;
isNvExtentionState = NV_STATE_IDLE;
TRACE(2, "%s: norflash_api_write ok,addr = 0x%x.", __func__,
(uint32_t)__userdata_start);
} else if (ret == NORFLASH_API_BUFFER_FULL) {
// do nothing.
} else {
ASSERT(0, "%s: norflash_api_write err,ret = %d,addr = 0x%x.",
__func__, ret, (uint32_t)__userdata_start);
}
}
int_unlock_global(lock);
} else {
if (isNvExtentionState == NV_STATE_IDLE) {
do {
lock = int_lock_global();
ret = norflash_api_erase(NORFLASH_API_MODULE_ID_USERDATA_EXT,
(uint32_t)(__userdata_start),
NV_EXTENSION_SIZE, false);
int_unlock_global(lock);
if (ret == NORFLASH_API_OK) {
isNvExtentionState = NV_STATE_ERASED;
TRACE(2, "%s: norflash_api_erase ok,addr = 0x%x.", __func__,
(uint32_t)__userdata_start);
} else if (ret == NORFLASH_API_BUFFER_FULL) {
do {
norflash_api_flush();
} while (norflash_api_get_free_buffer_count(NORFLASH_API_ERASING) ==
0);
} else {
ASSERT(0, "%s: norflash_api_erase err,ret = %d,addr = 0x%x.",
__func__, ret, (uint32_t)__userdata_start);
}
} while (ret == NORFLASH_API_BUFFER_FULL);
}
if (isNvExtentionState == NV_STATE_ERASED) {
do {
lock = nv_record_pre_write_operation();
crc = crc32(
0, (uint8_t *)nvrecord_extension_p + NV_EXTENSION_HEADER_SIZE,
NV_EXTENSION_VALID_LEN);
nvrecord_extension_p->header.crc32 = crc;
ret = norflash_api_write(
NORFLASH_API_MODULE_ID_USERDATA_EXT, (uint32_t)(__userdata_start),
(uint8_t *)nvrecord_extension_p, NV_EXTENSION_SIZE, true);
nv_record_post_write_operation(lock);
if (ret == NORFLASH_API_OK) {
isNvExtentionPendingForUpdate = false;
isNvExtentionState = NV_STATE_IDLE;
TRACE(2, "%s: norflash_api_write ok,addr = 0x%x.", __func__,
(uint32_t)__userdata_start);
} else if (ret == NORFLASH_API_BUFFER_FULL) {
do {
norflash_api_flush();
} while (
norflash_api_get_free_buffer_count(NORFLASH_API_WRITTING) == 0);
} else {
ASSERT(0, "%s: norflash_api_write err,ret = %d,addr = 0x%x.",
__func__, ret, (uint32_t)__userdata_start);
}
} while (ret == NORFLASH_API_BUFFER_FULL);
do {
norflash_api_flush();
} while (norflash_api_get_used_buffer_count(
NORFLASH_API_MODULE_ID_USERDATA_EXT, NORFLASH_API_ALL) >
0);
}
}
}
}
void nv_extension_callback(void *param) {
NORFLASH_API_OPERA_RESULT *opera_result;
opera_result = (NORFLASH_API_OPERA_RESULT *)param;
TRACE(6, "%s:type = %d, addr = 0x%x,len = 0x%x,remain = %d,result = %d.",
__func__, opera_result->type, opera_result->addr, opera_result->len,
opera_result->remain_num, opera_result->result);
}
void nv_record_init(void) {
nv_record_open(section_usrdata_ddbrecord);
nv_custom_parameter_section_init();
}
bt_status_t nv_record_open(SECTIONS_ADP_ENUM section_id) {
nv_record_extension_init();
return BT_STS_SUCCESS;
}
void nv_record_update_runtime_userdata(void) { nv_record_extension_update(); }
int nv_record_touch_cause_flush(void) {
nv_record_update_runtime_userdata();
return 0;
}
void nv_record_sector_clear(void) {
uint32_t lock;
enum NORFLASH_API_RET_T ret;
lock = int_lock_global();
ret =
norflash_api_erase(NORFLASH_API_MODULE_ID_USERDATA_EXT,
(uint32_t)__userdata_start, NV_EXTENSION_SIZE, false);
nvrecord_extension_p = NULL;
int_unlock_global(lock);
nv_record_update_runtime_userdata();
ASSERT(ret == NORFLASH_API_OK,
"nv_record_sector_clear: norflash_api_erase failed! ret = %d.", ret);
}
void nv_record_rebuild(void) {
nv_record_sector_clear();
nv_record_extension_init();
}
void nv_record_flash_flush(void) {}
int nv_record_flash_flush_in_sleep(void) {
nv_record_extension_flush(true);
return 0;
}
#if defined(OTA_ENABLED)
void nv_record_extension_update_gsound_ota_session(uint8_t gsoundOtaStatus,
uint32_t totalImageSize,
const char *session) {
NV_EXTENSION_RECORD_T *pNvExtRec = nv_record_get_extension_entry_ptr();
TRACE(2, "gsound ota status is %d, is start is %d",
pNvExtRec->gsound_info.gsoundOtaStatus, gsoundOtaStatus);
if (pNvExtRec->gsound_info.gsoundOtaStatus != gsoundOtaStatus) {
if (GSOUND_OTA_STATUS_NONE == gsoundOtaStatus) {
pNvExtRec->gsound_info.gsoundOtaOffset = 0;
memset(pNvExtRec->gsound_info.gsoundOtaSessionString, 0,
sizeof(pNvExtRec->gsound_info.gsoundOtaSessionString));
} else if (GSOUND_OTA_STAGE_ONGOING == gsoundOtaStatus) {
ASSERT(totalImageSize, "Received total image size is 0.");
ASSERT(session, "Received session pointer is NULL.");
pNvExtRec->gsound_info.gsoundOtaImageSize = totalImageSize;
memcpy(pNvExtRec->gsound_info.gsoundOtaSessionString, session,
strlen(session) + 1);
} else if (GSOUND_OTA_STATUS_COMPLETE == gsoundOtaStatus) {
ASSERT(GSOUND_OTA_STAGE_ONGOING == pNvExtRec->gsound_info.gsoundOtaStatus,
"Wrong status transmission.");
ASSERT(totalImageSize == pNvExtRec->gsound_info.gsoundOtaImageSize,
"total image size changed.");
pNvExtRec->gsound_info.gsoundOtaOffset = totalImageSize;
}
pNvExtRec->gsound_info.gsoundOtaStatus = gsoundOtaStatus;
TRACE(1, "update gsound ota status to %d",
pNvExtRec->gsound_info.gsoundOtaStatus);
nv_record_extension_update();
nv_record_flash_flush();
}
}
void nv_record_extension_update_gsound_ota_progress(uint32_t otaOffset) {
NV_EXTENSION_RECORD_T *pNvExtRec = nv_record_get_extension_entry_ptr();
if ((GSOUND_OTA_STAGE_ONGOING == pNvExtRec->gsound_info.gsoundOtaStatus) &&
(otaOffset <= pNvExtRec->gsound_info.gsoundOtaImageSize)) {
pNvExtRec->gsound_info.gsoundOtaOffset = otaOffset;
nv_record_extension_update();
nv_record_flash_flush();
}
}
#endif
#endif // #if defined(NEW_NV_RECORD_ENABLED)