/** * @file * @author chipsea * @brief * @version 0.1 * @date 2020-11-30 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd. * @note */ /******************************************************************************* * @file fs.c * @brief Contains all functions support for spi driver * @version 0.0 * @date 18. Oct. 2017 * @author * * *******************************************************************************/ #include "sdk_config.h" #include "rom_sym_def.h" #include "osal.h" #include "fs.h" #include "flash.h" #include "error.h" #include "log.h" //#define FS_DBBUG #ifdef FS_DBBUG #define FS_LOG LOG #else #define FS_LOG(...) #endif static uint8_t fs_sector_num; static uint32_t fs_offset_address; #define FS_ITEM_LEN_16BYTE 0 #define FS_ITEM_LEN_32BYTE 1 #define FS_ITEM_LEN_64BYTE 2 #ifndef FS_SETTING #define FS_SETTING FS_ITEM_LEN_16BYTE #endif #if (FS_SETTING == FS_ITEM_LEN_16BYTE) #define FS_ITEM_LEN 16 #elif (FS_SETTING == FS_ITEM_LEN_32BYTE) #define FS_ITEM_LEN 32 #elif (FS_SETTING == FS_ITEM_LEN_64BYTE) #define FS_ITEM_LEN 64 #else #error please check your config parameter #endif /* fs struct: sector0 sector_head file_head(4byte)+file_data ... file_head(4byte)+file_data */ //please do not modify the following parameters #define FS_ITEM_HEAD_LEN 4 #define FS_ITEM_DATA_LEN (FS_ITEM_LEN - FS_ITEM_HEAD_LEN) #define FS_SECTOR_ITEM_NUM (4096/FS_ITEM_LEN - 1) #define FS_SECTOR_NUM_BUFFER_SIZE (312/4) #define FS_ABSOLUTE_ADDR(offset) (fs.cfg.sector_addr + offset) typedef enum{ ITEM_DEL = 0x00,//zone is deleted ITEM_UNUSED = 0x03,//zone is free ITEM_USED = 0x02,//zone is used ITEM_RESERVED = 0x01//to be extend }item_pro; typedef enum{ ITEM_SF = 0x03,//single frame file ITEM_MF_F = 0x01,//multiple frame file,first frame ITEM_MF_C = 0x02,//multiple frame file,continue frame ITEM_MF_E = 0x00//multiple frame file,end frame }item_frame; typedef enum{ FLASH_UNCHECK = 0,//before analysis fs FLASH_NEW = 1,//new fs,its are 0xFF FLASH_ORIGINAL_ORDER = 2,//fs has data,its order is the original FLASH_NEW_ORDER = 3,//fs has data,its order is not the original FLASH_CONTEXT_ERROR = 4,//fs has data,but data is broken }FS_FLASH_TYPE; /* file head struct: len(12bit)+frame(2bit)+pro(2bit)+id(16bit) */ typedef union { struct { uint32_t id:16;//file id uint32_t pro:2;//file property uint32_t frame:2;//file frame uint32_t len:12;//file length } b; uint32_t reg; } fs_item_t; /* sector head struct: sector_addr(one word)+(ff+index+item_len+sector_num)(one word)+(0xffffffff)(one word)~(0xffffffff)(one word) */ typedef struct{ uint32_t sector_addr;//fs start address uint8_t sector_num;//fs sector number uint8_t item_len;//item length uint8_t index;//sector index uint8_t reserved[FS_ITEM_LEN-7]; }fs_cfg_t; typedef struct{ fs_cfg_t cfg; uint8_t current_sector;//free sector index uint8_t exchange_sector;//exchange sector,only use it when garbage collect uint16_t offset;//free position in free sector index }fs_t; static fs_t fs; static bool fs_init_flag = false; typedef enum{ SEARCH_FREE_ITEM = 0, SEARCH_APPOINTED_ITEM = 1, SEARCH_DELETED_ITEMS = 2, }search_type; extern uint32_t __psr(void);//check if in int process static void fs_erase_ucds_all_sector(void) { int i; for(i=0;i= (fs_offset_address+fs_sector_num*4096)) || ((addr&0x03)>0)) { return ERR_FS_PARAMETER; } FS_LOG("fs wr:%x,%x\n",addr, len); return(HalFlashWrite(addr,value,(uint32_t)len)); } static uint32_t fs_spif_read(uint32_t addr,uint8_t* buf,uint32_t len) { if ((addr < fs_offset_address) || (addr >= (fs_offset_address + fs_sector_num*4096)) || (buf == NULL) || (len == 0)) { return ERR_FS_PARAMETER; } FS_LOG("fs rd:%x,%x\n",addr, len); HalFlashRead(addr,buf,len); return( ERR_NONE ); } static void check_addr(uint32_t* addr) { if((*addr % 4096) == 0) { *addr += sizeof(fs_cfg_t); if(*addr >= 4096 *fs_sector_num) *addr -= 4096 *fs_sector_num; } } static int fs_search_items(search_type type,uint32_t* para1,uint32_t* para2) { uint8_t m,n; uint16_t j,g_offset = 1; uint32_t sector_addr,ab_addr; fs_item_t i1; bool from_last_sector = false; for(m = 1;m < fs_sector_num;m++) { n = (m + fs.exchange_sector) % fs.cfg.sector_num; if(g_offset >= FS_SECTOR_ITEM_NUM){ g_offset -= FS_SECTOR_ITEM_NUM; if(g_offset >= FS_SECTOR_ITEM_NUM){ continue; } } if(SEARCH_FREE_ITEM == type) fs.current_sector = (m + fs.exchange_sector) % fs.cfg.sector_num; sector_addr = n * 4096; for(j = 1;j <= FS_SECTOR_ITEM_NUM;j++) { if(from_last_sector == true){ from_last_sector = false; j += g_offset; } else{ if(g_offset > 1){ if((j - 2 + g_offset) < FS_SECTOR_ITEM_NUM){ j = j - 1 + g_offset; } else{ g_offset -= (FS_SECTOR_ITEM_NUM + 2 - j); from_last_sector = true; break; } } } ab_addr = sector_addr + (j * FS_ITEM_LEN); fs_spif_read(FS_ABSOLUTE_ADDR(ab_addr),(uint8_t*)&i1,FS_ITEM_HEAD_LEN); switch(type) { case SEARCH_FREE_ITEM: { switch(i1.b.pro) { case ITEM_DEL: case ITEM_USED: { if(i1.b.frame == ITEM_MF_F) g_offset = (i1.b.len/FS_ITEM_DATA_LEN) + ((i1.b.len%FS_ITEM_DATA_LEN)?1:0); else g_offset = 1; } break; case ITEM_UNUSED: *para1 = ab_addr%4096; return ERR_NONE; default: break; } } break; case SEARCH_APPOINTED_ITEM: { switch(i1.b.pro) { case ITEM_DEL: case ITEM_USED: if((ITEM_USED == i1.b.pro) && (i1.b.id == (uint16)(*para1))) { *para2 = ab_addr; return ERR_NONE; } else { if(i1.b.frame == ITEM_MF_F) g_offset = (i1.b.len/FS_ITEM_DATA_LEN) + ((i1.b.len%FS_ITEM_DATA_LEN)?1:0); else g_offset = 1; } break; case ITEM_UNUSED: return ERR_FS_NOT_FIND_ID; default: break; } } break; case SEARCH_DELETED_ITEMS: { switch(i1.b.pro) { case ITEM_DEL: { if(i1.b.frame == ITEM_MF_F){ g_offset = (i1.b.len/FS_ITEM_DATA_LEN) + ((i1.b.len%FS_ITEM_DATA_LEN)?1:0); *para1 += g_offset*FS_ITEM_DATA_LEN; } else{ g_offset = 1; *para1 += FS_ITEM_DATA_LEN; } *para2 += 1; } break; case ITEM_USED: { if(i1.b.frame == ITEM_MF_F){ g_offset = (i1.b.len/FS_ITEM_DATA_LEN) + ((i1.b.len%FS_ITEM_DATA_LEN)?1:0); } else{ g_offset = 1; } } break; case ITEM_UNUSED: return ERR_NONE; default: break; } } break; default: return ERR_INVALID_PARAM; } } } switch(type) { case SEARCH_FREE_ITEM: return ERR_FS_FULL; case SEARCH_APPOINTED_ITEM: return ERR_FS_NOT_FIND_ID; default: return ERR_NONE; } } static int fs_get_free_item(void) { int ret; uint32_t posiztion = 0; if(fs_init_flag == false) return ERR_FS_UNINITIALIZED; ret = fs_search_items(SEARCH_FREE_ITEM,&posiztion,0); if(ERR_NONE == ret){ fs.offset = posiztion; }else if(ERR_FS_FULL == ret){ fs.offset = 4096; } return ret; } static int fs_init(void) { uint8_t i = 0,sector_order[FS_SECTOR_NUM_BUFFER_SIZE],ret = ERR_FS_UNINITIALIZED; FS_FLASH_TYPE flash = FLASH_UNCHECK; fs_cfg_t flash_rd_cfg; fs.cfg.sector_addr = fs_offset_address; fs.cfg.sector_num = fs_sector_num;; fs.cfg.index = 0xff; fs.cfg.item_len = FS_ITEM_LEN; osal_memset((fs.cfg.reserved),0xff,(FS_ITEM_LEN-7)*sizeof(uint8_t)); osal_memset((sector_order),0x00,FS_SECTOR_NUM_BUFFER_SIZE); FS_LOG("fs_init:\n"); for(i = 0;i < fs.cfg.sector_num;i++) { fs_spif_read(FS_ABSOLUTE_ADDR(4096*i),(uint8_t*)(&flash_rd_cfg),sizeof(fs_cfg_t)); FS_LOG("flash_rd_cfg.sector_addr:%x\n",flash_rd_cfg.sector_addr); FS_LOG("flash_rd_cfg.sector_num:%x\n",flash_rd_cfg.sector_num); FS_LOG("flash_rd_cfg.item_len:%x\n",flash_rd_cfg.item_len); FS_LOG("flash_rd_cfg.index:%x\n",flash_rd_cfg.index); if((flash_rd_cfg.sector_addr == fs.cfg.sector_addr) && (flash_rd_cfg.sector_num == fs.cfg.sector_num) && (flash_rd_cfg.item_len == fs.cfg.item_len)) { if(flash_rd_cfg.index < (fs_sector_num - 1)) { if(i == flash_rd_cfg.index){ flash = FLASH_ORIGINAL_ORDER; FS_LOG("FLASH_ORIGINAL_ORDER\n"); } else{ flash = FLASH_NEW_ORDER; FS_LOG("FLASH_NEW_ORDER\n"); } sector_order[i] = flash_rd_cfg.index; fs.cfg.index = flash_rd_cfg.index; } else { flash = FLASH_CONTEXT_ERROR; FS_LOG("FLASH_CONTEXT_ERROR1\n"); break; } } else if((flash_rd_cfg.sector_addr == 0xffffffff) && (flash_rd_cfg.sector_num == 0xff) && (flash_rd_cfg.item_len == 0xff)) { sector_order[i] = 0xff; } else { flash = FLASH_CONTEXT_ERROR; FS_LOG("FLASH_CONTEXT_ERROR2\n"); break; } } if(flash == FLASH_CONTEXT_ERROR){ return ERR_FS_CONTEXT; } if(fs.cfg.index == 0xff) flash = FLASH_NEW; if(flash == FLASH_NEW) { for(i = 0;i < (fs.cfg.sector_num - 1);i++) { fs.cfg.index = i; if(ERR_NONE != fs_spif_write((FS_ABSOLUTE_ADDR(4096*i)),(uint8_t*)(&(fs.cfg)),sizeof(fs_cfg_t))){ FS_LOG("ERR_FS_WRITE_FAILED\n"); return ERR_FS_WRITE_FAILED; } } fs.current_sector = 0; fs.exchange_sector = fs.cfg.sector_num - 1; fs.offset = sizeof(fs_cfg_t); fs_init_flag = TRUE; } else { for(i = 0;i < fs.cfg.sector_num;i++){ if(sector_order[i] == 0) break; } if(i < fs.cfg.sector_num) { fs.exchange_sector = (i + fs.cfg.sector_num - 1) % fs.cfg.sector_num; fs_init_flag = TRUE; ret = fs_get_free_item(); if((ret != ERR_FS_FULL) && (ret != ERR_NONE)){ FS_LOG("ERR_FS_RESERVED_ERROR\n"); return ERR_FS_RESERVED_ERROR; } } } FS_LOG("ERR_NONE\n"); return ERR_NONE; } uint32_t hal_fs_get_free_size(void) { uint32_t size = 0; if(fs_init_flag == false){ //LOG("fs_init_flag = false,free\n"); return 0; } if(fs.offset < 4096) { size = ((fs.exchange_sector + fs.cfg.sector_num - fs.current_sector - 1)%fs.cfg.sector_num)*(4096-sizeof(fs_cfg_t)); size += (4096 - fs.offset); size = size*FS_ITEM_DATA_LEN/FS_ITEM_LEN; } return size; } int hal_fs_get_garbage_size(uint32_t* garbage_file_num) { uint32_t garbage_size = 0,garbage_count = 0; int ret; if(fs_init_flag == false) return ERR_FS_UNINITIALIZED; ret = fs_search_items(SEARCH_DELETED_ITEMS,&garbage_size,&garbage_count); if(ERR_NONE == ret){ if(NULL != garbage_file_num) *garbage_file_num = garbage_count; return garbage_size; } return ERR_FATAL; } int hal_fs_item_find_id(uint16_t id,uint32_t* id_addr) { int ret; uint32_t file_id = 0; if(fs_init_flag == false) return ERR_FS_UNINITIALIZED; file_id = id & 0xffff; ret = fs_search_items(SEARCH_APPOINTED_ITEM,&file_id,id_addr); return ret; } int hal_fs_item_write(uint16_t id,uint8_t* buf,uint16_t len) { uint8_t frame_len,wr_buf[FS_ITEM_LEN]; uint16_t i,item_len; uint32_t addr; fs_item_t i1; if(__psr()&0x3f){ return ERR_FS_IN_INT; } if(fs_init_flag == false){ //LOG("fs_init_flag = false,write\n"); return ERR_FS_UNINITIALIZED; } if((buf == NULL) || (len == 0)||(len > 4095)) return ERR_FS_PARAMETER; if(len > hal_fs_get_free_size()) return ERR_FS_NOT_ENOUGH_SIZE; //if(hal_fs_item_find_id(id,&addr) == ERR_NONE) // return ERR_FS_EXIST_SAME_ID; if(hal_fs_item_find_id(id,&addr) == ERR_NONE){ if(ERR_NONE != hal_fs_item_del(id)) return ERR_FATAL; } item_len = len; i1.b.len = len; i1.b.id = id; i1.b.pro = ITEM_USED; if(len <= FS_ITEM_DATA_LEN) i1.b.frame = ITEM_SF; i = 0; while(len > 0) { if(len > FS_ITEM_DATA_LEN) { if(item_len == len) i1.b.frame = ITEM_MF_F; else i1.b.frame = ITEM_MF_C; frame_len = FS_ITEM_DATA_LEN; len -= FS_ITEM_DATA_LEN; } else { if((i1.b.frame == ITEM_MF_C) || (i1.b.frame == ITEM_MF_F)) i1.b.frame = ITEM_MF_E; frame_len = len; len = 0; } addr = FS_ABSOLUTE_ADDR((fs.current_sector * 4096) + fs.offset); osal_memcpy(wr_buf,(uint8_t*)(&i1.reg),FS_ITEM_HEAD_LEN); osal_memcpy((wr_buf + FS_ITEM_HEAD_LEN),(buf + i),frame_len); if(ERR_NONE != fs_spif_write(addr,wr_buf,(frame_len+FS_ITEM_HEAD_LEN))) return ERR_FS_WRITE_FAILED; i += frame_len; fs.offset += FS_ITEM_LEN; if(fs.offset == 4096) { if(((fs.current_sector + 1) % fs.cfg.sector_num) != fs.exchange_sector) { fs.offset = sizeof(fs_cfg_t); fs.current_sector = (fs.current_sector + 1) % fs.cfg.sector_num; } } } return ERR_NONE; } int hal_fs_item_read(uint16_t id,uint8_t* buf,uint16_t buf_len,uint16_t* len) { uint8_t rd_len; uint16_t i = 0,temp_len; uint32_t addr; fs_item_t i1; if(__psr()&0x3f){ return ERR_FS_IN_INT; } if(fs_init_flag == false) return ERR_FS_UNINITIALIZED; if((buf == NULL) || (buf_len == 0)) return ERR_FS_PARAMETER; if(hal_fs_item_find_id(id,&addr) == ERR_NONE) { fs_spif_read(FS_ABSOLUTE_ADDR(addr),(uint8_t*)&i1,FS_ITEM_HEAD_LEN); if(len != NULL){ *len = i1.b.len; } temp_len = i1.b.len; if(buf_len >= i1.b.len) { while(temp_len > 0) { rd_len = (temp_len >= FS_ITEM_DATA_LEN) ? FS_ITEM_DATA_LEN : temp_len; fs_spif_read(FS_ABSOLUTE_ADDR(addr + FS_ITEM_HEAD_LEN),(buf + i),rd_len); temp_len -= rd_len; addr += FS_ITEM_LEN; i += rd_len; check_addr(&addr); } return ERR_NONE; } return ERR_FS_BUFFER_TOO_SMALL; } return ERR_FS_NOT_FIND_ID; } int hal_fs_item_del(uint16_t id) { uint16_t i = 0,count = 1; uint32_t addr = 0; fs_item_t i1; if(__psr()&0x3f){ return ERR_FS_IN_INT; } if(fs_init_flag == FALSE) return ERR_FS_UNINITIALIZED; if(hal_fs_item_find_id(id,&addr) == ERR_NONE) { fs_spif_read(FS_ABSOLUTE_ADDR(addr),(uint8_t*)&i1,FS_ITEM_HEAD_LEN); count = i1.b.len/FS_ITEM_DATA_LEN + ((i1.b.len % FS_ITEM_DATA_LEN)?1:0); for(i = 0;i < count;i++) { fs_spif_read(FS_ABSOLUTE_ADDR(addr),(uint8_t*)&i1,FS_ITEM_HEAD_LEN); i1.b.pro = ITEM_DEL; if(ERR_NONE != fs_spif_write(FS_ABSOLUTE_ADDR(addr),(uint8_t*)(&i1.reg),FS_ITEM_HEAD_LEN)) return ERR_FS_WRITE_FAILED; addr += FS_ITEM_LEN; check_addr(&addr); } return ERR_NONE; } else { return ERR_FS_NOT_FIND_ID; } } int hal_fs_garbage_collect(void) { uint8_t i,j,buf[FS_ITEM_DATA_LEN]; uint8_t from_sector_index = 0,to_sector_index = 0; uint32_t addr_rd=0,addr_wr=0,addr_erase; fs_item_t i1; if(__psr()&0x3f){ return ERR_FS_IN_INT; } if(fs_init_flag == FALSE) return ERR_FS_UNINITIALIZED; to_sector_index = fs.exchange_sector; from_sector_index = (fs.exchange_sector + 1) % fs.cfg.sector_num; addr_wr = 4096*to_sector_index; for(i = 0;i < (fs.cfg.sector_num - 1);i++) { addr_rd = 4096*((from_sector_index + i) % fs.cfg.sector_num); addr_erase = addr_rd; fs.cfg.index = i; if(ERR_NONE != fs_spif_write(FS_ABSOLUTE_ADDR((4096*((to_sector_index + i) % fs.cfg.sector_num))),(uint8_t*)(&(fs.cfg)),sizeof(fs_cfg_t))) return ERR_FS_WRITE_FAILED; if(i == 0) addr_wr += sizeof(fs_cfg_t); addr_rd += sizeof(fs_cfg_t); for(j = 0;j < FS_SECTOR_ITEM_NUM;j++) { fs_spif_read(FS_ABSOLUTE_ADDR(addr_rd),(uint8_t*)&i1,FS_ITEM_HEAD_LEN); if(i1.b.pro == ITEM_USED) { fs_spif_read(FS_ABSOLUTE_ADDR(addr_rd + FS_ITEM_HEAD_LEN),buf,FS_ITEM_DATA_LEN); if(ERR_NONE != fs_spif_write(FS_ABSOLUTE_ADDR(addr_wr),(uint8_t*)(&i1.reg),FS_ITEM_HEAD_LEN)) return ERR_FS_WRITE_FAILED; if(ERR_NONE != fs_spif_write(FS_ABSOLUTE_ADDR(addr_wr + FS_ITEM_HEAD_LEN),buf,FS_ITEM_DATA_LEN)) return ERR_FS_WRITE_FAILED; addr_wr += FS_ITEM_LEN; check_addr(&addr_wr); } else if(i1.b.pro == ITEM_UNUSED) { break; } addr_rd += FS_ITEM_LEN; } fs_erase_ucds_one_sector(addr_erase); } return fs_init(); } int hal_fs_format(uint32_t fs_start_address,uint8_t sector_num) { if(__psr()&0x3f){ return ERR_FS_IN_INT; } fs_init_flag = FALSE; if((fs_start_address % 0x1000) || (sector_num < 3)){ return ERR_INVALID_PARAM; } fs_sector_num = sector_num; fs_offset_address = fs_start_address; fs_erase_ucds_all_sector(); return hal_fs_init(fs_start_address,sector_num); } int hal_fs_init(uint32_t fs_start_address,uint8_t sector_num) { if(fs_init_flag == TRUE){ return ERR_FS_UNINITIALIZED; } if((fs_start_address % 0x1000) || (sector_num < 2)){ return ERR_INVALID_PARAM; } fs_sector_num = sector_num; fs_offset_address = fs_start_address; return fs_init(); } bool hal_fs_initialized(void) { return fs_init_flag; }