/*************************************************************************** * * 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 #include #include #include "cmsis.h" #include "nvrecord_extension.h" #include "hal_trace.h" #include "crc32.h" #include "hal_sleep.h" #include "hal_norflash.h" #include "norflash_api.h" #include "norflash_drv.h" #include "nvrecord_bt.h" #include "nvrecord_env.h" #include "nvrecord_ble.h" #include "nvrecord_dma_config.h" #include "nvrecord_fp_account_key.h" #include "customparam_section.h" #include "mpu.h" #include "besbt.h" #include "hal_timer.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_MAIN_ERASING, NV_STATE_MAIN_ERASED, NV_STATE_MAIN_WRITTING, NV_STATE_MAIN_WRITTEN, NV_STATE_MAIN_DONE, NV_STATE_BAK_ERASING, NV_STATE_BAK_ERASED, NV_STATE_BAK_WRITTING, NV_STATE_BAK_WRITTEN, NV_STATE_BAK_DONE, }NV_STATE; typedef struct { bool is_update; NV_STATE state; uint32_t written_size; }NV_FLUSH_STATE; static NV_FLUSH_STATE nv_flsh_state; static bool nvrec_init = false; static uint32_t _user_data_main_start; static uint32_t _user_data_bak_start; static uint8_t _nv_burn_buf[NV_EXTENSION_PAGE_SIZE]; NV_EXTENSION_RECORD_T *nvrecord_extension_p = NULL; /* *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 #ifdef OTA_ENABLED { {0,}, }, #endif #if defined(BISTO_ENABLED) { true, }, #endif #if 1//def 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 int nv_record_extension_flush(bool is_async); #ifdef TILE_DATAPATH static void nvrecord_rebuild_tileconfig(NV_TILE_INFO_CONFIG_T *tileConfig) { memset((uint8_t*)tileConfig,0,sizeof(NV_TILE_INFO_CONFIG_T)); } #endif #ifdef OTA_ENABLED static void nvrecord_rebuild_ota_info(NV_OTA_INFO_T *ota_info) { memset((uint8_t *)ota_info, 0, OTA_DEVICE_CNT * sizeof(NV_OTA_INFO_T)); } #endif #if defined(BISTO_ENABLED) static void nvrecord_rebuild_gsound_info(NV_GSOUND_INFO_T *gsound_info) { memset((uint8_t*)gsound_info,0,sizeof(NV_GSOUND_INFO_T)); } #endif #if 1//def TX_IQ_CAL static void nvrecord_rebuild_btiqcalconfig(BT_IQ_CALIBRATION_CONFIG_T *btIqCalConfig) { memset((uint8_t*)btIqCalConfig,0,sizeof(BT_IQ_CALIBRATION_CONFIG_T)); btIqCalConfig->validityMagicNum = BT_IQ_INVALID_MAGIC_NUM; } #endif static bool nv_record_data_is_valid(NV_EXTENSION_RECORD_T *nv_record) { bool is_valid = false; NVRECORD_HEADER_T* pExtRecInFlash = &nv_record->header; uint8_t* pData = (uint8_t*)nv_record + NV_EXTENSION_HEADER_SIZE; TRACE(0,"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, pData, pExtRecInFlash->validLen); TRACE(1,"generated crc32: 0x%x, header crc: 0x%x", crc, pExtRecInFlash->crc32); if (crc == pExtRecInFlash->crc32) { // correct TRACE(2,"Nv extension is valid."); TRACE(2,"Former nv ext valid len %d", pExtRecInFlash->validLen); TRACE(2,"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," "use the default value."); } else { if(NV_EXTENSION_VALID_LEN > pExtRecInFlash->validLen) { TRACE(2, "NV extension is extended! (0x%x) -> (0x%x)", pExtRecInFlash->validLen,NV_EXTENSION_VALID_LEN); } is_valid = true; } } } } return is_valid; } static bool nv_record_items_is_valid(NV_EXTENSION_RECORD_T *nv_record) { nv_record = nv_record; #if 0 NV_RECORD_PAIRED_BT_DEV_INFO_T *pbt_pair_info; pbt_pair_info = &nv_record->bt_pair_info; TRACE(1,"%s: pairedDevNum: %d ", __func__,pbt_pair_info->pairedDevNum); for(uint8_t i = 0; i < pbt_pair_info->pairedDevNum; i++) { DUMP8("0x%x,", (uint8_t*)pbt_pair_info->pairedBtDevInfo[i].record.bdAddr.address,6); } #endif // TODO: add cheking for nv_record items. return true; } static void nv_record_set_default(NV_EXTENSION_RECORD_T *nv_record) { memset((uint8_t*)nv_record, 0 ,sizeof(NV_EXTENSION_RECORD_T)); nvrecord_rebuild_system_env(&(nv_record->system_info)); nvrecord_rebuild_paired_bt_dev_info(&(nv_record->bt_pair_info)); nvrecord_rebuild_paired_ble_dev_info(&(nv_record->ble_pair_info)); #ifdef GFPS_ENABLED nvrecord_rebuild_fp_account_key(&(nv_record->fp_account_key_rec)); #endif #ifdef NVREC_BAIDU_DATA_SECTION nvrecord_rebuild_dma_configuration(&(nv_record->dma_config)); #endif #ifdef TILE_DATAPATH nvrecord_rebuild_tileconfig(&nv_record->tileConfig); #endif #if defined(BISTO_ENABLED) nvrecord_rebuild_gsound_info(&nv_record->gsound_info); #endif #ifdef OTA_ENABLED nvrecord_rebuild_ota_info((NV_OTA_INFO_T *)&nv_record->ota_info); #endif #if 1//def TX_IQ_CAL nvrecord_rebuild_btiqcalconfig(&nv_record->btIqCalConfig); #endif nv_record->header.magicNumber = NV_EXTENSION_MAGIC_NUMBER; nv_record->header.majorVersion = NV_EXTENSION_MAJOR_VERSION; nv_record->header.minorVersion = NV_EXTENSION_MINOR_VERSION; nv_record->header.validLen = NV_EXTENSION_VALID_LEN; nv_record->header.crc32 = crc32(0, ((uint8_t *)nv_record + NV_EXTENSION_HEADER_SIZE), NV_EXTENSION_VALID_LEN); } 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, §or_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) { uint32_t lock; bool main_is_valid = false; bool bak_is_valid = false; bool data_is_valid = false; if(nvrec_init) { return; } _user_data_main_start = (uint32_t)__userdata_start; _user_data_bak_start = (uint32_t)__userdata_start + NV_EXTENSION_SIZE; lock = nv_record_pre_write_operation(); _nv_record_extension_init(); nv_flsh_state.is_update = false; nv_flsh_state.written_size = 0; nv_flsh_state.state = NV_STATE_IDLE; nvrecord_extension_p = &local_extension_data.nv_record; if(nvrecord_extension_p->header.magicNumber != NV_EXTENSION_MAGIC_NUMBER || nvrecord_extension_p->header.majorVersion != NV_EXTENSION_MAJOR_VERSION || nvrecord_extension_p->header.minorVersion != NV_EXTENSION_MINOR_VERSION || nvrecord_extension_p->header.validLen >= (NV_EXTENSION_SIZE - NV_EXTENSION_HEADER_SIZE)) { ASSERT(0, "%s: local_extension_data error!(0x%x,0x%x,0x%x,0x%x)", __func__, nvrecord_extension_p->header.magicNumber, nvrecord_extension_p->header.majorVersion, nvrecord_extension_p->header.minorVersion, nvrecord_extension_p->header.validLen); } // Check main sector. if(nv_record_data_is_valid((NV_EXTENSION_RECORD_T*)_user_data_main_start) && nv_record_items_is_valid((NV_EXTENSION_RECORD_T*)_user_data_main_start)) { TRACE(2,"%s,main sector is valid.",__func__); main_is_valid = true; } else { TRACE(1,"%s,main sector is invalid!",__func__); main_is_valid = false; } // Check bak secotr. if(nv_record_data_is_valid((NV_EXTENSION_RECORD_T*)_user_data_bak_start) && nv_record_items_is_valid((NV_EXTENSION_RECORD_T*)_user_data_main_start)) { TRACE(2,"%s,bak sector is valid.",__func__); bak_is_valid = true; } else { TRACE(1,"%s,bak sector is invalid!",__func__); bak_is_valid = false; } if(main_is_valid) { data_is_valid = true; if(!bak_is_valid) { nv_flsh_state.is_update = true; nv_flsh_state.state = NV_STATE_MAIN_DONE; } } else { if(bak_is_valid) { data_is_valid = true; nv_flsh_state.is_update = true; nv_flsh_state.state = NV_STATE_IDLE; } else { data_is_valid = false; } } if(data_is_valid) { TRACE(2,"%s,data is valid.", __func__); if(main_is_valid) { memcpy((uint8_t *)nvrecord_extension_p + NV_EXTENSION_HEADER_SIZE, (uint8_t *)_user_data_main_start + NV_EXTENSION_HEADER_SIZE, NV_EXTENSION_VALID_LEN); } else { memcpy((uint8_t *)nvrecord_extension_p + NV_EXTENSION_HEADER_SIZE, (uint8_t *)_user_data_bak_start + NV_EXTENSION_HEADER_SIZE, NV_EXTENSION_VALID_LEN); } nvrecord_extension_p->header.crc32 = crc32(0, ((uint8_t *)nvrecord_extension_p + NV_EXTENSION_HEADER_SIZE), nvrecord_extension_p->header.validLen); } else { TRACE(1,"%s,data invalid, rebuild... ",__func__); nv_record_set_default(nvrecord_extension_p); TRACE(2,"%s,rebuild crc = 0x%x. ",__func__,nvrecord_extension_p->header.crc32); // need to update the content in the flash nv_record_extension_update(); } nvrec_init = true; nv_record_post_write_operation(lock); if(nv_flsh_state.is_update) { nv_record_extension_flush(false); } TRACE(2,"%s,done.", __func__); } NV_EXTENSION_RECORD_T* nv_record_get_extension_entry_ptr(void) { return nvrecord_extension_p; } void nv_record_extension_update(void) { nv_flsh_state.is_update = true; } static int nv_record_extension_flush_main(bool is_async) { uint32_t crc; uint32_t lock; enum NORFLASH_API_RET_T ret = NORFLASH_API_OK; if(NULL == nvrecord_extension_p) { TRACE(1,"%s,nvrecord_extension_p is null.", __func__); goto _func_end; } // TRACE(3, "%s is_async %d state %d", __func__, is_async, nv_flsh_state.state); if(is_async) { if(nv_flsh_state.state == NV_STATE_IDLE && nv_flsh_state.is_update == true) { TRACE(3,"%s: async flush begin!", __func__); } hal_trace_pause(); lock = int_lock_global(); if(nv_flsh_state.state == NV_STATE_IDLE || nv_flsh_state.state == NV_STATE_MAIN_ERASING) { if(nv_flsh_state.state == NV_STATE_IDLE) { nv_flsh_state.state = NV_STATE_MAIN_ERASING; TRACE(4,"%s: NV_STATE_MAIN_ERASING", __func__); } ret = norflash_api_erase(NORFLASH_API_MODULE_ID_USERDATA_EXT, (uint32_t)(__userdata_start), NV_EXTENSION_SIZE, true); if(ret == NORFLASH_API_OK) { nv_flsh_state.state = NV_STATE_MAIN_ERASED; TRACE(4,"%s: NV_STATE_MAIN_ERASED", __func__); TRACE(4,"%s: norflash_api_erase ok,addr = 0x%x.",__func__,_user_data_main_start); } else if(ret == NORFLASH_API_BUFFER_FULL) { norflash_api_flush(); } else { ASSERT(0,"%s: norflash_api_erase err,ret = %d,addr = 0x%x.",__func__,ret,_user_data_main_start); } } else if(nv_flsh_state.state == NV_STATE_MAIN_ERASED || nv_flsh_state.state == NV_STATE_MAIN_WRITTING) { if(nv_flsh_state.state == NV_STATE_MAIN_ERASED) { nv_flsh_state.state = NV_STATE_MAIN_WRITTING; TRACE(4,"%s: NV_STATE_MAIN_WRITTING", __func__); } 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; ASSERT(nv_record_data_is_valid(nvrecord_extension_p) && nv_record_items_is_valid(nvrecord_extension_p), "%s nv_record is invalid!",__func__); 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(tmpLock); if(ret == NORFLASH_API_OK) { nv_flsh_state.is_update = false; nv_flsh_state.state = NV_STATE_MAIN_WRITTEN; TRACE(4,"%s: NV_STATE_MAIN_WRITTEN", __func__); TRACE(4,"%s: norflash_api_write ok,addr = 0x%x.",__func__,_user_data_main_start); } else if(ret == NORFLASH_API_BUFFER_FULL) { norflash_api_flush(); } else { ASSERT(0,"%s: norflash_api_write err,ret = %d,addr = 0x%x.",__func__,ret,_user_data_main_start); } } else { if(norflash_api_get_used_buffer_count(NORFLASH_API_MODULE_ID_USERDATA_EXT,NORFLASH_API_ALL) == 0) { nv_flsh_state.state = NV_STATE_MAIN_DONE; // TRACE(1,"%s: NV_STATE_MAIN_DONE", __func__); TRACE(1,"%s: async flush done.", __func__); } else { norflash_api_flush(); } } int_unlock_global(lock); hal_trace_continue(); } else { TRACE(4,"%s: sync flush begin!", __func__); if(nv_flsh_state.state == NV_STATE_IDLE || nv_flsh_state.state == NV_STATE_MAIN_ERASING) { do { hal_trace_pause(); lock = int_lock_global(); ret = norflash_api_erase(NORFLASH_API_MODULE_ID_USERDATA_EXT, (uint32_t)(__userdata_start), NV_EXTENSION_SIZE, true); int_unlock_global(lock); hal_trace_continue(); if(ret == NORFLASH_API_OK) { nv_flsh_state.state = NV_STATE_MAIN_ERASED; TRACE(4,"%s: norflash_api_erase ok,addr = 0x%x.",__func__, _user_data_main_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,_user_data_main_start); } }while(ret == NORFLASH_API_BUFFER_FULL); } if(nv_flsh_state.state == NV_STATE_MAIN_ERASED || nv_flsh_state.state == NV_STATE_MAIN_WRITTING) { do { hal_trace_pause(); 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; ASSERT(nv_record_data_is_valid(nvrecord_extension_p) && nv_record_items_is_valid(nvrecord_extension_p), "%s nv_record is invalid!",__func__); 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(tmpLock); hal_trace_continue(); if(ret == NORFLASH_API_OK) { nv_flsh_state.is_update = false; nv_flsh_state.state = NV_STATE_MAIN_WRITTEN; TRACE(4,"%s: norflash_api_write ok,addr = 0x%x.",__func__,_user_data_main_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,_user_data_main_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); nv_flsh_state.state = NV_STATE_MAIN_DONE; TRACE(1,"%s: sync flush done.", __func__); } _func_end: return (ret == NORFLASH_API_OK) ? 0:1; } static int nv_record_extension_flush_bak(bool is_async) { uint32_t lock; enum NORFLASH_API_RET_T ret = NORFLASH_API_OK; uint8_t *burn_buf = (uint8_t*)_nv_burn_buf; if(is_async) { hal_trace_pause(); lock = int_lock_global(); if(nv_flsh_state.state == NV_STATE_MAIN_DONE || nv_flsh_state.state == NV_STATE_BAK_ERASING) { if(nv_flsh_state.state == NV_STATE_MAIN_DONE) { nv_flsh_state.state = NV_STATE_BAK_ERASING; TRACE(3,"%s: async flush begin", __func__); } ret = norflash_api_erase(NORFLASH_API_MODULE_ID_USERDATA_EXT, _user_data_bak_start, NV_EXTENSION_SIZE, true); if(ret == NORFLASH_API_OK) { nv_flsh_state.state = NV_STATE_BAK_ERASED; TRACE(4,"%s: norflash_api_erase ok,addr = 0x%x.", __func__,_user_data_bak_start); // TRACE(4,"%s: NV_STATE_BAK_ERASED", __func__); } else if(ret == NORFLASH_API_BUFFER_FULL) { norflash_api_flush(); } else { ASSERT(0,"%s: norflash_api_erase err,ret = %d,addr = 0x%x.", __func__,ret,_user_data_bak_start); } } else if(nv_flsh_state.state == NV_STATE_BAK_ERASED || nv_flsh_state.state == NV_STATE_BAK_WRITTING) { if(nv_flsh_state.state == NV_STATE_BAK_ERASED) { nv_flsh_state.state = NV_STATE_BAK_WRITTING; nv_flsh_state.written_size = 0; // TRACE(4,"%s: NV_STATE_BAK_WRITTING", __func__); } do { ret = norflash_api_read(NORFLASH_API_MODULE_ID_USERDATA_EXT, _user_data_main_start + nv_flsh_state.written_size, burn_buf, NV_EXTENSION_PAGE_SIZE ); ASSERT(ret == NORFLASH_API_OK,"norflash_api_read failed! ret = %d, addr = 0x%x.", (int32_t)ret,_user_data_main_start + nv_flsh_state.written_size); ret = norflash_api_write(NORFLASH_API_MODULE_ID_USERDATA_EXT, _user_data_bak_start + nv_flsh_state.written_size, burn_buf, NV_EXTENSION_PAGE_SIZE, true ); if(ret == NORFLASH_API_OK) { nv_flsh_state.written_size += NV_EXTENSION_PAGE_SIZE; if(nv_flsh_state.written_size == NV_EXTENSION_SIZE) { nv_flsh_state.state = NV_STATE_BAK_WRITTEN; // TRACE(4,"%s: NV_STATE_BAK_WRITTEN", __func__); break; } } else if(ret == NORFLASH_API_BUFFER_FULL) { norflash_api_flush(); break; } else { ASSERT(0,"%s: norflash_api_write err,ret = %d,addr = 0x%x.", __func__,ret,_user_data_bak_start + nv_flsh_state.written_size); } }while(1); } else { if(norflash_api_get_used_buffer_count(NORFLASH_API_MODULE_ID_USERDATA_EXT,NORFLASH_API_ALL) == 0) { nv_flsh_state.state = NV_STATE_BAK_DONE; TRACE(3,"%s: async flush done.", __func__); } else { norflash_api_flush(); } } int_unlock_global(lock); hal_trace_continue(); } else { TRACE(4,"%s: sync flush begin.", __func__); if(nv_flsh_state.state == NV_STATE_MAIN_DONE || nv_flsh_state.state == NV_STATE_BAK_ERASING) { do { hal_trace_pause(); lock = int_lock_global(); ret = norflash_api_erase(NORFLASH_API_MODULE_ID_USERDATA_EXT, _user_data_bak_start, NV_EXTENSION_SIZE, true); int_unlock_global(lock); hal_trace_continue(); if(ret == NORFLASH_API_OK) { nv_flsh_state.state = NV_STATE_BAK_ERASED; TRACE(4,"%s: norflash_api_erase ok,addr = 0x%x.", __func__, _user_data_bak_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,_user_data_bak_start); } }while(ret == NORFLASH_API_BUFFER_FULL); } if(nv_flsh_state.state == NV_STATE_BAK_ERASED || nv_flsh_state.state == NV_STATE_BAK_WRITTING) { nv_flsh_state.state = NV_STATE_BAK_WRITTING; nv_flsh_state.written_size = 0; hal_trace_pause(); do { lock = int_lock_global(); ret = norflash_api_read(NORFLASH_API_MODULE_ID_USERDATA_EXT, _user_data_main_start + nv_flsh_state.written_size, burn_buf, NV_EXTENSION_PAGE_SIZE ); ASSERT(ret == NORFLASH_API_OK,"norflash_api_read failed! ret = %d, addr = 0x%x.", (int32_t)ret, _user_data_main_start + nv_flsh_state.written_size); ret = norflash_api_write(NORFLASH_API_MODULE_ID_USERDATA_EXT, _user_data_bak_start + nv_flsh_state.written_size, burn_buf, NV_EXTENSION_PAGE_SIZE, true ); int_unlock_global(lock); if(ret == NORFLASH_API_OK) { nv_flsh_state.written_size += NV_EXTENSION_PAGE_SIZE; if(nv_flsh_state.written_size == NV_EXTENSION_SIZE) { nv_flsh_state.state = NV_STATE_BAK_WRITTEN; TRACE(3,"%s: NV_STATE_BAK_WRITTEN", __func__); break; } } 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,_user_data_bak_start + nv_flsh_state.written_size); } }while(1); } hal_trace_continue(); // nv_flsh_state.state == NV_STATE_BAK_WRITTEN; do { norflash_api_flush(); }while(norflash_api_get_used_buffer_count(NORFLASH_API_MODULE_ID_USERDATA_EXT,NORFLASH_API_ALL) > 0); nv_flsh_state.state = NV_STATE_BAK_DONE; TRACE(1,"%s: sync flush done.", __func__); } if(nv_flsh_state.state == NV_STATE_BAK_DONE) { nv_flsh_state.state = NV_STATE_IDLE; TRACE(1,"%s: NV_STATE_IDLE", __func__); } return (ret == NORFLASH_API_OK) ? 0:1; } static int nv_record_extension_flush(bool is_async) { int ret = 0; // TRACE(3, "%s state %d is_update %d", __func__, nv_flsh_state.state, nv_flsh_state.is_update); do{ if(nv_flsh_state.state == NV_STATE_IDLE && nv_flsh_state.is_update == FALSE) { break; } if((nv_flsh_state.state == NV_STATE_IDLE && nv_flsh_state.is_update == TRUE) || nv_flsh_state.state == NV_STATE_MAIN_ERASING || nv_flsh_state.state == NV_STATE_MAIN_ERASED || nv_flsh_state.state == NV_STATE_MAIN_WRITTING || nv_flsh_state.state == NV_STATE_MAIN_WRITTEN ) { ret = nv_record_extension_flush_main(is_async); if(is_async) { break; } } if(nv_flsh_state.state == NV_STATE_MAIN_DONE || nv_flsh_state.state == NV_STATE_BAK_ERASING || nv_flsh_state.state == NV_STATE_BAK_ERASED || nv_flsh_state.state == NV_STATE_BAK_WRITTING || nv_flsh_state.state == NV_STATE_BAK_WRITTEN || nv_flsh_state.state == NV_STATE_BAK_DONE ) { ret = nv_record_extension_flush_bak(is_async); } }while(!is_async); return ret; } 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(); #ifdef FLASH_SUSPEND hal_sleep_set_sleep_hook(HAL_SLEEP_HOOK_USER_NVRECORD, nv_record_flash_flush_in_sleep); #else hal_sleep_set_deep_sleep_hook(HAL_DEEP_SLEEP_HOOK_USER_NVRECORD, nv_record_flash_flush_in_sleep); #endif 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); ASSERT(ret == NORFLASH_API_OK, "%s: norflash_api_erase(0x%x) failed! ret = %d.", __func__, (uint32_t)__userdata_start, (int32_t)ret); ret = norflash_api_erase(NORFLASH_API_MODULE_ID_USERDATA_EXT, (uint32_t)__userdata_start + NV_EXTENSION_SIZE, NV_EXTENSION_SIZE, false); ASSERT(ret == NORFLASH_API_OK, "%s: norflash_api_erase(0x%x) failed! ret = %d.", __func__, (uint32_t)__userdata_start + NV_EXTENSION_SIZE, (int32_t)ret); //pmu_reboot(); int_unlock_global(lock); } void nv_record_rebuild(void) { if(nvrecord_extension_p) { uint32_t nv_start; TRACE(1,"%s: begin.", __func__); uint32_t lock = int_lock_global(); mpu_clear(MPU_ID_USER_DATA_SECTION); nv_record_set_default(nvrecord_extension_p); nv_start = (uint32_t)&local_extension_data.nv_record; mpu_set(MPU_ID_USER_DATA_SECTION, nv_start, NV_EXTENSION_MIRROR_RAM_SIZE, 0, MPU_ATTR_READ); nv_record_extension_update(); nv_record_flash_flush(); int_unlock_global(lock); TRACE(1,"%s: done.", __func__); } else { TRACE(1,"%s: begin. nvrecord_extension_p = null.", __func__); uint32_t lock = int_lock_global(); nv_record_sector_clear(); nvrec_init = false; nv_record_extension_init(); int_unlock_global(lock); TRACE(1,"%s: done.", __func__); } } #define NV_RECORD_FLUSH_EXECUTION_INTERVAL_MS (20*60*1000) static uint32_t lastFlushCheckPointInMs = 0; static void nv_record_reset_flush_checkpoint(void) { lastFlushCheckPointInMs = GET_CURRENT_MS();; } static bool nv_record_is_timer_expired_to_check(void) { uint32_t passedTimerMs; uint32_t currentTimerMs = GET_CURRENT_MS(); if (0 == lastFlushCheckPointInMs) { passedTimerMs = currentTimerMs; } else { if (currentTimerMs > lastFlushCheckPointInMs) { passedTimerMs = currentTimerMs - lastFlushCheckPointInMs; } else { passedTimerMs = 0xFFFFFFFF - lastFlushCheckPointInMs + currentTimerMs + 1; } } if (passedTimerMs > NV_RECORD_FLUSH_EXECUTION_INTERVAL_MS) { return true; } else { return false; } } void nv_record_flash_flush(void) { nv_record_extension_flush(false); nv_record_reset_flush_checkpoint(); } void nv_record_execute_async_flush(void) { int ret = nv_record_extension_flush(true); if (0 == ret) { nv_record_reset_flush_checkpoint(); } } int nv_record_flash_flush_in_sleep(void) { if ((NV_STATE_IDLE == nv_flsh_state.state) && !nv_record_is_timer_expired_to_check()) { return 0; } nv_record_execute_async_flush(); return 0; } #endif // #if defined(NEW_NV_RECORD_ENABLED)