/*========================================================================================================== Copyright (c) 2021, Quectel Wireless Solutions Co., Ltd. All rights reserved. Quectel Wireless Solutions Proprietary and Confidential. ===========================================================================================================*/ /*========================================================================================================== EDIT HISTORY FOR MODULE This section contains comments describing changes made to the module. Notice that changes are listed in reverse chronological order. WHEN WHO WHAT, WHERE, WHY ------------ ------- ------------------------------------------------------------------------------- 10/04/2022 Sharon.li 此文件是操作内部Nor Flash的(QSPI)的接口INTERNAL_NORFLASH ===========================================================================================================*/ #include "loadTable.h" #include "quec_debug.h" #include #include "loadTable.h" #include "quec_flash.h" #include "UART.h" #include "qspi_nor.h" #include #include "Flash_wk.h" #define quec_flash_printf(msg, ...) custom_log("Flash_LOG", msg, ##__VA_ARGS__) #ifdef QUEC_LIMIT_FLASH_ERASE //分区擦除保护功能,用于实现当前分区是否可以被擦除 typedef struct { UINT32 start_addr; //addr not 0x80000000 UINT32 end_addr; }QUEC_PART_ADDR_S; typedef struct { char name[16];//分区名称 char erase_flag;//分区是否能被擦除 1,可以被擦除;0,不能被擦除 QUEC_PART_ADDR_S AddrInfo;//分区的结束地址和起始地址 }QUEC_PART_ADDR_TYPE_S; QUEC_PART_ADDR_TYPE_S QUEC_PART_ADDR_TYPE_AERA[]={ {"nvm",1,NULL}, {"quec_cfg",1,NULL}, {"fota_param",1,NULL}, {"factory_a",1,NULL}, {"factory_b",1,NULL}, {"erase_rd",1,NULL}, {"bootloader",NULL}, {"ptable",0,NULL}, {"fwcerts",0,NULL}, {"rd",0,NULL}, {"cp",0,NULL}, {"dsp",0,NULL}, {"rfbin",0,NULL}, {"customer_app",1,NULL}, {"reserved",0,NULL}, {"logo",0,NULL}, }; #define QFTEST_PARTITION_COUNT_MAX (sizeof(QUEC_PART_ADDR_TYPE_AERA)/sizeof(QUEC_PART_ADDR_TYPE_AERA[0])) UINT32 G_QftestEraseCount[QFTEST_PARTITION_COUNT_MAX] = {0}; //初始化分区信息 UINT32 CHECK_FLASH_AREA_ADDR_CONFIG(void) { int len = QFTEST_PARTITION_COUNT_MAX; _ptentry *pnt; int i; for(i=0;istart; QUEC_PART_ADDR_TYPE_AERA[i].AddrInfo.end_addr=pnt->start + pnt->size; // uart_printf("Egan_flash_name=%s flash_addr=0x%x flash_end=0x%x \r\n",QUEC_PART_ADDR_TYPE_AERA[i].name,QUEC_PART_ADDR_TYPE_AERA[i].AddrInfo.start_addr,QUEC_PART_ADDR_TYPE_AERA[i].AddrInfo.end_addr); } return 0; } //检查指定的Flash地址和大小是否可以被擦除 //flash_addr:要擦除Flash地址 //flash_size:擦除的大小 //rw_flag:是否记录此次擦除,0,不记录;1,记录 UINT32 FLASH_LIMIT_ALLOW_AREA(UINT32 flash_addr,UINT32 flash_size,BOOL rw_flag) { int len = QFTEST_PARTITION_COUNT_MAX; int i; QUEC_PART_ADDR_TYPE_S *ptr=QUEC_PART_ADDR_TYPE_AERA; for(i=0;i=ptr[i].AddrInfo.start_addr)&&((flash_addr + flash_size) <= ptr[i].AddrInfo.end_addr)) { if((ptr[i].erase_flag==1)&&(flash_size!=NULL)) { if(rw_flag) G_QftestEraseCount[i]++; break; } else{ return NULL; } } } return 1; } /***************************************************************** * Function: QUEC_PART_ADDR_TYPE_AERA_GET_CNT * * Description: * 获取数组QUEC_PART_ADDR_TYPE_AERA的下标值 * Parameters: * name [in] partition name * * Return: * -1 failure *****************************************************************/ int QUEC_PART_ADDR_TYPE_AERA_GET_CNT(const char *name) { int len = QFTEST_PARTITION_COUNT_MAX; int i; for(i=0;i=pnt->start) { if((flash_addr+ flash_size)<= (pnt->start + pnt->size)) { return 1; } } return 0; } #define CRANE_NVM_ON_FLASH_BASE_ADD (nvm_flash_start_addr&0xF0000000) #define CRANE_COUSTOMER_FS_ON_FLASH_BASE_ADD (customer_fs_flash_start_addr&0XF0000000) //记录flash_addr所在分区的第几个block,被擦除的次数 UINT32 Flash_erase_count_Set(UINT32 flash_addr,UINT32 size) { int i=0; UINT32 nvm_block_id=(CRANE_NVM_ON_FLASH_BASE_ADD+flash_addr-nvm_flash_start_addr)/CHIP_SECTOR_SIZE; if(g_FlashBlkEraseCnt == NULL) return 0; if(CHECK_FLASH_AREA("nvm",flash_addr,size)) { for(i=0;i*CHIP_SECTOR_SIZEend_address) { uart_printf("error address param \n"); return FLASH_RW_ERROR; } free_size=end_address-addr; if(size<=free_size) { ret = spi_nor_do_read(relative_addr, buf_addr, size, 0); } else { uart_printf("no enough free space \n"); ret = FLASH_RW_ERROR; } return ret; } unsigned int quec_norflash_do_write(unsigned char *patition_name, unsigned int addr, unsigned int buf_addr, unsigned int size) { unsigned int ret=FLASH_RW_ERROR; unsigned int start_address=0; unsigned int end_address=0; unsigned int free_size=0; unsigned int relative_addr = 0; unsigned char buf[10] = {0}; relative_addr = addr&0x7FFFFFFF; start_address=quec_get_patition_start_address((const char *)patition_name); end_address=quec_get_patition_end_address((const char *)patition_name); if(addrend_address) { uart_printf("error address param \n"); return FLASH_RW_ERROR; } free_size=end_address-addr; if(size<=free_size) { if(size >= 8) { ret = spi_nor_do_write(relative_addr, buf_addr, size, 0); } else { memset(buf, 0xFF, 10); memcpy(buf,(const void *)buf_addr, size); ret = spi_nor_do_write(relative_addr, (unsigned int)&buf, 8, 0); } } else { uart_printf("no enough free space \n"); ret = FLASH_RW_ERROR; } return ret; } //erase a block for internal test int quec_erase_block(int blk_index, unsigned int disable_protect) { int flash_err = 0; lfsBlockInfo * p_blk_prop = NULL; p_blk_prop = GetLfsBlkInfo(0); //fatal_printf("EraseFlash_4k 0x%x 0x%x\r\n", flash_addr, size); if(blk_index*p_blk_prop->FlashBlockSize > quec_get_patition_end_address("all")) { return -1; } return spi_nor_do_erase_4k(blk_index*p_blk_prop->FlashBlockSize,p_blk_prop->FlashBlockSize, BOOT_FLASH); } //擦除,实现了非4K对齐的擦除 int quec_norflash_do_erase(unsigned char *patition_name, unsigned int addr, unsigned int size) { unsigned int ret = 0; unsigned int sector_size = 4*1024;//4K unsigned int back_recovery_size=0; unsigned int start_address = 0; unsigned int end_address = 0; unsigned int free_size = 0; unsigned int erase_size = 0; volatile unsigned int sector_size_num = 0; volatile unsigned int sector_addr_num = 0; unsigned int relative_addr = 0; unsigned char *recovery_buf1 = NULL; unsigned char *recovery_buf2 = NULL; relative_addr = addr&0x7FFFFFFF; start_address = quec_get_patition_start_address((const char *)patition_name); end_address = quec_get_patition_end_address((const char *)patition_name); if(addrend_address) { uart_printf("error address param \n"); return FLASH_RW_ERROR; } free_size=end_address-addr; if(size <= free_size) { if(relative_addr%sector_size == 0) { if((size%sector_size == 0)) { ret = spi_nor_do_erase_4k(relative_addr, size, 0); } else { recovery_buf1 = malloc(sector_size); sector_size_num = (relative_addr+size)/sector_size; back_recovery_size = size%sector_size; spi_nor_do_read((sector_size_num*sector_size),(unsigned int)recovery_buf1, sector_size, 0); ret = spi_nor_do_erase_4k(relative_addr, (size - (size%sector_size) + sector_size), 0); spi_nor_do_write((relative_addr+size),(unsigned int)(recovery_buf1+back_recovery_size), (sector_size-back_recovery_size), 0); free(recovery_buf1); } } else { if((relative_addr+size)%sector_size ==0) { recovery_buf1 = malloc(sector_size); sector_addr_num = relative_addr/sector_size; spi_nor_do_read((sector_addr_num*sector_size), (unsigned int)recovery_buf1, sector_size, 0); ret = spi_nor_do_erase_4k((sector_addr_num*sector_size), (size + (relative_addr%sector_size)), 0); spi_nor_do_write((sector_addr_num*sector_size), (unsigned int)recovery_buf1, (relative_addr%sector_size), 0); free(recovery_buf1); } else { recovery_buf1 = malloc(sector_size); recovery_buf2 = malloc(sector_size); sector_size_num = (relative_addr+size)/sector_size; sector_addr_num = relative_addr/sector_size; back_recovery_size = (relative_addr+size)%sector_size; erase_size = (size + relative_addr%sector_size); spi_nor_do_read((sector_addr_num*sector_size), (unsigned int)recovery_buf1, sector_size, 0); spi_nor_do_read((sector_size_num*sector_size), (unsigned int)recovery_buf2, sector_size, 0); ret = spi_nor_do_erase_4k((sector_addr_num*sector_size), (erase_size - (erase_size%sector_size) +sector_size), 0); spi_nor_do_write((sector_addr_num*sector_size), (unsigned int)recovery_buf1, (relative_addr%sector_size), 0); spi_nor_do_write((relative_addr+size), (unsigned int)(recovery_buf2+back_recovery_size), (sector_size-back_recovery_size), 0); free(recovery_buf1); free(recovery_buf2); } } } else { uart_printf("no enough free space \n"); ret = FLASH_RW_ERROR; } return ret; } extern unsigned int nor_flash_id; //获取内部QSPI Nor Flash的id int quec_norflash_read_id(void) { return nor_flash_id; } UINT32 get_quec_cfg_start_address(void) { _ptentry * QUEC_CFG_Entry= ptable_find_entry("quec_cfg"); return QUEC_CFG_Entry->vstart; } #ifdef QUEC_NORFLASH_ERASE_OPTIMIZE //补擦的实现 unsigned int Quec_EraseFlashExt(unsigned int addr, unsigned int size, unsigned char flashType); unsigned int Quec_ReadFlashExt(unsigned int addr, unsigned int buf_addr, unsigned int size, unsigned char flashType); unsigned int Quec_WriteFlashExt(unsigned int addr, unsigned int buf_addr,unsigned int size, unsigned char flashType); UINT32 get_erase_rd_start_address(void) { _ptentry * erase_rd_start_Entry= ptable_find_entry("erase_rd"); if(erase_rd_start_Entry==NULL) return NULL; return erase_rd_start_Entry->start; } UINT32 get_erase_rd_bin_size(void) { _ptentry * erase_rd_begin_Entry= ptable_find_entry("erase_rd"); if(erase_rd_begin_Entry==NULL) return NULL; return erase_rd_begin_Entry->vsize; } extern const char USER_ERASE_RECORD_AREA[2][16]; //user 补擦区域 char ERASE_RECORD_AREA[][16]={ "nvm", };//此数组+USER_ERASE_RECORD_AREA需要与boot33同步 int quec_repair_erase_get_parti_name_valid(char* parti_name) { int len =sizeof(ERASE_RECORD_AREA)/sizeof(ERASE_RECORD_AREA[0]); _ptentry *pnt = NULL; int i = 0; for(i=0;ivstart & 0xF0000000) == 0x80000000)) { return 0; } } len =sizeof(USER_ERASE_RECORD_AREA)/sizeof(USER_ERASE_RECORD_AREA[0]); for(i=0;ivstart & 0xF0000000) == 0x80000000)) { return 0; } } return -1; } UINT32 FLASH_ERASE_RECORD_ALLOW_AREA(UINT32 flash_addr,UINT32 size) { int len =sizeof(ERASE_RECORD_AREA)/sizeof(ERASE_RECORD_AREA[0]); _ptentry *pnt; int i; for(i=0;ivstart & 0xF0000000) == 0x80000000)) { if(flash_addr >= pnt->start) { if((flash_addr+size) <= (pnt->start + pnt->size)) { return 1; } } } } len =sizeof(USER_ERASE_RECORD_AREA)/sizeof(USER_ERASE_RECORD_AREA[0]); for(i=0;ivstart & 0xF0000000) == 0x80000000)) { if(flash_addr >= pnt->start) { if((flash_addr+size) <= (pnt->start + pnt->size)) { return 1; } } } } return NULL; } #define NoError 0x0 #define QUEC_RECORD_MAGIC1 (0x78E5D4C2) #define QUEC_RECORD_MAGIC2 (0x54F7D60E) // only record cell 0 used , for mark record area is erase successed #define CHIP_SECTOR_SIZE 0x1000 //erase 4k each times #define CHIP_BLOCK_SIZE 0x10000 //erase 4k each times typedef struct { UINT32 flag;// 0xAAA55AA55 UINT32 reserve; }quec_Eraseflag_s; typedef struct { UINT32 RecordMagic; UINT32 PageId; // recored the eraseing block id , UINT32 Record_type; // recored the eraseing block or page }quec_RecordInfo_s; typedef struct { quec_RecordInfo_s ReInfo; quec_Eraseflag_s Eflag; }quec_flashErase_record_s; UINT32 g_RecordCell = 0xffffffff; UINT8 g_IsfirstTimeReadRecordblock = 1; void Quec_FlashEraseRecord_step1(UINT32 flash_addr,quec_Record_type_enum type) { UINT32 Retval = NoError; quec_flashErase_record_s *p_BlkEraseRecordCell = NULL; quec_RecordInfo_s *p_BlkERCellInfo = NULL; quec_Eraseflag_s *p_BlkERCellflag = NULL; quec_RecordInfo_s BlkERecordCellInfo; char *Record_buff = NULL; int i=0, flash_BlkSize = 0; UINT32 QUEC_RECORD_BLOCKREASE_AREA_SIZE=get_erase_rd_bin_size(); UINT32 QUEC_RECORD_BLOCKREASE_AREA_ADD=get_erase_rd_start_address(); //如果是AT补擦测试,把当前cell的reserve记录为0X12345678,需先正常记录,此次必须为第二次记录 #ifdef QUEC_FLASH_TEST_REPAIR_ERASE if(type == QUEC_FLASH_TEST_REPAIR_ERASE) { quec_flashErase_record_s* now_record = (quec_flashErase_record_s*)(QUEC_RECORD_BLOCKREASE_AREA_ADD+g_RecordCell*(sizeof(quec_flashErase_record_s))); uint32_t test_reserve_fg = 0X12345678; Retval = Quec_WriteFlashExt((unsigned int)&now_record->Eflag.reserve, (unsigned int)&test_reserve_fg, sizeof(uint32_t), 0); if(NoError != Retval) { RTI_LOG("WriteFlash failed, retval=%d, g_RecordCell=%d need Record EraseblockId=%d !!!!\r\n", Retval, g_RecordCell,BlkERecordCellInfo.PageId); return; } return; } #endif if((!QUEC_RECORD_BLOCKREASE_AREA_SIZE)||(!QUEC_RECORD_BLOCKREASE_AREA_ADD)) { quec_flash_printf("not found erase_rd !!!"); return ; } flash_BlkSize=type?CHIP_SECTOR_SIZE:CHIP_BLOCK_SIZE; if(0 == g_IsfirstTimeReadRecordblock) { g_RecordCell++; } else { Record_buff = malloc(QUEC_RECORD_BLOCKREASE_AREA_SIZE); if(NULL == Record_buff) { quec_flash_printf("malloc failed !!!!!!!!"); return; } Retval = Quec_ReadFlashExt(QUEC_RECORD_BLOCKREASE_AREA_ADD,(unsigned int)Record_buff,QUEC_RECORD_BLOCKREASE_AREA_SIZE, 0); if( NoError != Retval) { if( NULL !=Record_buff ) { free(Record_buff); Record_buff = NULL; } quec_flash_printf("Flash read failed !!"); return; } // check record block is erase successed p_BlkEraseRecordCell = (quec_flashErase_record_s*)Record_buff; // check the 0 cell which is mark for record area not erase p_BlkERCellInfo = (quec_RecordInfo_s*)Record_buff; if(QUEC_RECORD_MAGIC2 == p_BlkERCellInfo->RecordMagic && 0x00 == p_BlkERCellInfo->PageId) { // scan for 1 . because cell 0 is a flag which mark the block is not erase for( i=1; i*sizeof(quec_flashErase_record_s)ReInfo)); if(( 0xffffffff == p_BlkERCellInfo->PageId ) && ( 0xffffffff == p_BlkERCellflag->flag)) // this record cell is not used { break; } } g_RecordCell = i; quec_flash_printf("g_RecordCall =%d\r\n",g_RecordCell); g_IsfirstTimeReadRecordblock = 0; if( NULL !=Record_buff ) { free(Record_buff); Record_buff = NULL; } } else { Retval = NoError; if( 0xffffffff != p_BlkERCellInfo->RecordMagic ) { Retval = Quec_EraseFlashExt(QUEC_RECORD_BLOCKREASE_AREA_ADD, QUEC_RECORD_BLOCKREASE_AREA_SIZE, 0); } if(NoError ==Retval) { BlkERecordCellInfo.RecordMagic = QUEC_RECORD_MAGIC2; BlkERecordCellInfo.PageId = 0x00; Retval = Quec_WriteFlashExt(QUEC_RECORD_BLOCKREASE_AREA_ADD, (unsigned int)&BlkERecordCellInfo, sizeof(BlkERecordCellInfo),0); if(NoError !=Retval) { quec_flash_printf("[Ramos], write cell0 EraseRecord area erase flag faild !!!!!"); return ; } } else { quec_flash_printf("[Ramos], erase record area fail !!!!!!"); return ; } g_RecordCell = 1; // record block erase cell form cell 1. because cell 0 used record RecordArea erase success flag } if( NULL !=Record_buff ) { free(Record_buff); Record_buff = NULL; } } //quec_flashErase_record_s 20 Byte 最后一个cell不止要小于尾地址,而且范围也不能超过 if( (g_RecordCell*sizeof(quec_flashErase_record_s) >= QUEC_RECORD_BLOCKREASE_AREA_SIZE) || (g_RecordCell*sizeof(quec_flashErase_record_s)+sizeof(quec_flashErase_record_s) > QUEC_RECORD_BLOCKREASE_AREA_SIZE) ) { Retval = Quec_EraseFlashExt(QUEC_RECORD_BLOCKREASE_AREA_ADD, QUEC_RECORD_BLOCKREASE_AREA_SIZE, 0); if(NoError ==Retval) { BlkERecordCellInfo.RecordMagic = QUEC_RECORD_MAGIC2; BlkERecordCellInfo.PageId = 0x00; Retval = Quec_WriteFlashExt(QUEC_RECORD_BLOCKREASE_AREA_ADD, (unsigned int)&BlkERecordCellInfo, sizeof(BlkERecordCellInfo),0); if(NoError !=Retval) { quec_flash_printf("[Ramos], write cell0 EraseRecord area erase flag faild !!!!!"); return ; } } else { quec_flash_printf("[Ramos], erase record area fail !!!!!!"); return ; } g_RecordCell = 1; // record block erase cell form cell 1. because cell 0 used record RecordArea erase success flag } BlkERecordCellInfo.RecordMagic = QUEC_RECORD_MAGIC1; BlkERecordCellInfo.PageId = flash_addr/flash_BlkSize; BlkERecordCellInfo.Record_type=type; quec_flash_printf("addr %x type %d",flash_addr,type); Retval = Quec_WriteFlashExt((QUEC_RECORD_BLOCKREASE_AREA_ADD+g_RecordCell*(sizeof(quec_flashErase_record_s))), (unsigned int)&(BlkERecordCellInfo), sizeof(BlkERecordCellInfo),0); if(NoError != Retval) { quec_flash_printf("WriteFlash failed, retval=%d, g_RecordCell=%d need Record EraseblockId=%d !!!!", Retval, g_RecordCell,BlkERecordCellInfo.PageId); } memset(&BlkERecordCellInfo,0,sizeof(BlkERecordCellInfo)); Retval = Quec_ReadFlashExt((QUEC_RECORD_BLOCKREASE_AREA_ADD+g_RecordCell*(sizeof(quec_flashErase_record_s))), (unsigned int)&(BlkERecordCellInfo), sizeof(BlkERecordCellInfo),0); if(NoError != Retval) { quec_flash_printf("readFlash failed, retval=%d, g_RecordCell=%d need Record EraseblockId=%d !!!!", Retval, g_RecordCell,BlkERecordCellInfo.PageId); } uint32_t read_record_addr = BlkERecordCellInfo.PageId * (flash_BlkSize=type?CHIP_SECTOR_SIZE:CHIP_BLOCK_SIZE); if( (BlkERecordCellInfo.RecordMagic != QUEC_RECORD_MAGIC1) || (!FLASH_ERASE_RECORD_ALLOW_AREA(read_record_addr, flash_BlkSize)) || (BlkERecordCellInfo.Record_type != type)) { quec_Eraseflag_s BlkERecordCellflag; BlkERecordCellflag.flag=0xAAAA5555; BlkERecordCellflag.reserve = 0xffffffff; if(0xffffffff != g_RecordCell) { //quec_debug("[Ramos] write cell(%d) RecordEraseblock(%d) flag 0xAA55!!",g_RecordCell, flash_addr/flash_BlkSize); Retval = Quec_WriteFlashExt((QUEC_RECORD_BLOCKREASE_AREA_ADD+g_RecordCell*sizeof(quec_flashErase_record_s)+sizeof(quec_RecordInfo_s)), (unsigned int)&BlkERecordCellflag, sizeof(BlkERecordCellflag),0); if(NoError != Retval) { quec_flash_printf("WriteFlash failed, retval=%d, g_RecordCell=%d, need Record EraseblockId=%d !!", Retval,g_RecordCell, flash_addr/flash_BlkSize); } quec_flash_printf("magic %x addr %x type %x %x",BlkERecordCellInfo.RecordMagic,BlkERecordCellInfo.PageId * (flash_BlkSize=type?CHIP_SECTOR_SIZE:CHIP_BLOCK_SIZE),BlkERecordCellInfo.Record_type,type); } else { quec_flash_printf("g_RecordCall(0x%x) not found !! need Record EraseblockId=%d!", g_RecordCell, flash_addr/flash_BlkSize); } } return; } void Quec_FlashEraseRecord_step2(UINT32 flash_addr,quec_Record_type_enum type) { UINT32 Retval = NoError, flash_BlkSize = 0; quec_Eraseflag_s BlkERecordCellflag; UINT32 QUEC_RECORD_BLOCKREASE_AREA_SIZE=get_erase_rd_bin_size(); UINT32 QUEC_RECORD_BLOCKREASE_AREA_ADD=get_erase_rd_start_address(); if((!QUEC_RECORD_BLOCKREASE_AREA_SIZE)||(!QUEC_RECORD_BLOCKREASE_AREA_ADD)) { quec_flash_printf("not found erase_rd !!!"); return ; } flash_BlkSize=type?CHIP_SECTOR_SIZE:CHIP_BLOCK_SIZE;; if(0xffffffff != g_RecordCell) { Retval = Quec_ReadFlashExt((QUEC_RECORD_BLOCKREASE_AREA_ADD+g_RecordCell*sizeof(quec_flashErase_record_s)+sizeof(quec_RecordInfo_s)), (unsigned int)&BlkERecordCellflag, sizeof(BlkERecordCellflag),0); if(NoError != Retval) { quec_flash_printf("readFlash failed, retval=%d, g_RecordCell=%d!!!!", Retval, g_RecordCell); } if(BlkERecordCellflag.flag == 0xAAAA5555) { quec_flash_printf("flag is already write,jump it!!!"); } else { BlkERecordCellflag.flag=0xAAAA5555; BlkERecordCellflag.reserve = 0xffffffff; //quec_debug("[Ramos] write cell(%d) RecordEraseblock(%d) flag 0xAA55!!",g_RecordCell, flash_addr/flash_BlkSize); Retval = Quec_WriteFlashExt((QUEC_RECORD_BLOCKREASE_AREA_ADD+g_RecordCell*sizeof(quec_flashErase_record_s)+sizeof(quec_RecordInfo_s)), (unsigned int)&BlkERecordCellflag, sizeof(BlkERecordCellflag),0); if(NoError != Retval) { quec_flash_printf("WriteFlash failed, retval=%d, g_RecordCell=%d, need Record EraseblockId=%d !!", Retval,g_RecordCell, flash_addr/flash_BlkSize); } } } else { quec_flash_printf("g_RecordCall(0x%x) not found !! need Record EraseblockId=%d!", g_RecordCell, flash_addr/flash_BlkSize); } } #endif