diff --git a/easyflash/src/ef_env_ng.c b/easyflash/src/ef_env_ng.c index 39a1897..9358b4c 100644 --- a/easyflash/src/ef_env_ng.c +++ b/easyflash/src/ef_env_ng.c @@ -81,6 +81,8 @@ * would return 12. */ #define EF_ALIGN_DOWN(size, align) ((size) & ~((align) - 1)) +/* align down by write granularity */ +#define EF_WG_ALIGN_DOWN(size) (EF_ALIGN_DOWN(size, (EF_WRITE_GRAN + 7)/8)) #if (EF_WRITE_GRAN == 1) #define STATUS_TABLE_SIZE(status_number) ((status_number * EF_WRITE_GRAN + 7)/8) @@ -276,7 +278,6 @@ static uint32_t get_next_env_addr(sector_meta_data_t sector, env_meta_data_t pre uint8_t status_table[ENV_STATUS_TABLE_SIZE]; uint32_t addr = GET_ADDR_FAILED; - //TODO 可否直接共用 empty env addr if (sector->status.store == SECTOR_STORE_EMPTY) { return GET_ADDR_FAILED; @@ -334,7 +335,7 @@ static EfErrCode read_env(env_meta_data_t env) size = crc_data_len - len; } - ef_port_read(env->addr.start + ENV_NAME_LEN_OFFSET + len, (uint32_t *) buf, size); + ef_port_read(env->addr.start + ENV_NAME_LEN_OFFSET + len, (uint32_t *) buf, EF_WG_ALIGN(size)); calc_crc32 = ef_calc_crc32(calc_crc32, buf, size); } /* check CRC32 */ @@ -345,7 +346,7 @@ static EfErrCode read_env(env_meta_data_t env) env->crc_is_ok = true; /* the name is behind aligned ENV header */ env_name_addr = env->addr.start + ENV_HDR_DATA_SIZE; - ef_port_read(env_name_addr, (uint32_t *) env->name, env_hdr.name_len); + ef_port_read(env_name_addr, (uint32_t *) env->name, EF_WG_ALIGN(env_hdr.name_len)); /* the value is behind aligned name */ env->addr.value = env_name_addr + EF_WG_ALIGN(env_hdr.name_len); env->value_len = env_hdr.value_len; @@ -533,7 +534,7 @@ char *ef_get_env(const char *key) } if (find_env(key, &env)) { - ef_port_read(env.addr.value, (uint32_t *)value, env.value_len); + ef_port_read(env.addr.value, (uint32_t *)value, EF_WG_ALIGN(env.value_len)); /* the return value must be string */ if (ef_is_str((uint8_t *)value, env.value_len)) { return value; @@ -597,7 +598,7 @@ static EfErrCode copy_env(uint32_t to, uint32_t from, size_t env_len) } else { size = env_len - len; } - ef_port_read(from + ENV_LEN_OFFSET + len, (uint32_t *) buf, size); + ef_port_read(from + ENV_LEN_OFFSET + len, (uint32_t *) buf, EF_WG_ALIGN(size)); result = ef_port_write(to + ENV_LEN_OFFSET + len, (uint32_t *) buf, size); if (result != EF_NO_ERR) { return result; @@ -609,19 +610,23 @@ static EfErrCode copy_env(uint32_t to, uint32_t from, size_t env_len) return result; } -static void sector_iterator(sector_meta_data_t sector, void *arg1, void *arg2, - bool (*callback)(sector_meta_data_t sector, void *arg1, void *arg2), bool traversal_env) -{ +static void sector_iterator(sector_meta_data_t sector, sector_store_status_t status, void *arg1, void *arg2, + bool (*callback)(sector_meta_data_t sector, void *arg1, void *arg2), bool traversal_env) { uint32_t sec_addr; /* search all sectors */ sector->addr = GET_ADDR_FAILED; while ((sec_addr = get_next_sector_addr(sector)) != GET_ADDR_FAILED) { //TODO 检查所有扇区的剩余空间,是否有合适的扇区,优先 using_sec_table - read_sector_meta_data(sec_addr, sector, traversal_env); - /* iterator is interrupted when callback return true */ - if (callback && callback(sector, arg1, arg2)) { - return; + read_sector_meta_data(sec_addr, sector, false); + if (status == SECTOR_STORE_UNUSED || status == sector->status.store) { + if (traversal_env) { + read_sector_meta_data(sec_addr, sector, traversal_env); + } + /* iterator is interrupted when callback return true */ + if (callback && callback(sector, arg1, arg2)) { + return; + } } } } @@ -644,16 +649,44 @@ static uint32_t alloc_env(sector_meta_data_t sector, size_t env_size) { uint32_t empty_env = GET_ADDR_FAILED; - sector_iterator(sector, &env_size, &empty_env, alloc_env_cb, true); + /* alloc the ENV from the using status sector first */ + sector_iterator(sector, SECTOR_STORE_USING, &env_size, &empty_env, alloc_env_cb, true); + if (empty_env == GET_ADDR_FAILED) { + sector_iterator(sector, SECTOR_STORE_EMPTY, &env_size, &empty_env, alloc_env_cb, true); + } return empty_env; } +static EfErrCode update_sec_status(sector_meta_data_t sector, size_t new_env_len, bool *is_full) +{ + uint8_t status_table[STORE_STATUS_TABLE_SIZE]; + EfErrCode result = EF_NO_ERR; + /* change the current sector status */ + if (sector->status.store == SECTOR_STORE_EMPTY) { + /* change the sector status to using */ + result = write_status(sector->addr, status_table, SECTOR_STORE_STATUS_NUM, SECTOR_STORE_USING); + } else if (sector->status.store == SECTOR_STORE_USING) { + /* check remain size */ + if (sector->remain - new_env_len < EF_SEC_REMAIN_THRESHOLD) { + /* change the sector status to full */ + result = write_status(sector->addr, status_table, SECTOR_STORE_STATUS_NUM, SECTOR_STORE_FULL); + if (is_full) { + *is_full = true; + } + } else if (is_full) { + *is_full = false; + } + } + + return result; +} + static bool gc_check_cb(sector_meta_data_t sector, void *arg1, void *arg2) { size_t *empty_sec = arg1; - if (sector->check_ok && sector->status.store == SECTOR_STORE_EMPTY) { + if (sector->check_ok) { *empty_sec = *empty_sec + 1; } @@ -665,8 +698,7 @@ static bool do_gc(sector_meta_data_t sector, void *arg1, void *arg2) { struct env_meta_data env; - if (sector->check_ok && sector->status.store == SECTOR_STORE_FULL - && (sector->status.dirty == SECTOR_DIRTY_TRUE || sector->status.dirty == SECTOR_DIRTY_GC)) { + if (sector->check_ok && (sector->status.dirty == SECTOR_DIRTY_TRUE || sector->status.dirty == SECTOR_DIRTY_GC)) { #if (ENV_STATUS_TABLE_SIZE >= DIRTY_STATUS_TABLE_SIZE) uint8_t status_table[ENV_STATUS_TABLE_SIZE]; #else @@ -683,10 +715,12 @@ static bool do_gc(sector_meta_data_t sector, void *arg1, void *arg2) uint32_t env_addr; struct sector_meta_data new_sector; //TODO 异常掉电测试 - //TODO 修改新扇区状态 + /* change the current ENV status to prepare delete */ write_status(env.addr.start, status_table, ENV_STATUS_NUM, ENV_PRE_DELETE); /* alloc new space for move the old ENV */ if ((env_addr = alloc_env(&new_sector, env.len)) != GET_ADDR_FAILED) { + /* update the new ENV sector status */ + update_sec_status(&new_sector, env.len, NULL); /* copy the old ENV to new space */ copy_env(env_addr, env.addr.start, env.len); } @@ -706,15 +740,33 @@ static void gc_collect(void) size_t empty_sec = 0; /* GC check the empty sector number */ - sector_iterator(§or, &empty_sec, NULL, gc_check_cb, false); + sector_iterator(§or, SECTOR_STORE_EMPTY, &empty_sec, NULL, gc_check_cb, false); /* do GC collect */ if (empty_sec <= EF_GC_EMPTY_SEC_THRESHOLD) { EF_DEBUG("The remain empty sector is %d, GC threshold is %d.\n", empty_sec, EF_GC_EMPTY_SEC_THRESHOLD); - sector_iterator(§or, NULL, NULL, do_gc, false); + sector_iterator(§or, SECTOR_STORE_FULL, NULL, NULL, do_gc, false); } } +static EfErrCode align_write(uint32_t addr, const uint32_t *buf, size_t size) +{ + EfErrCode result = EF_NO_ERR; + uint8_t align_data[EF_WRITE_GRAN / 8]; + size_t align_remain; + + memset(align_data, 0xFF, sizeof(align_data)); + result = ef_port_write(addr, buf, EF_WG_ALIGN_DOWN(size)); + + align_remain = size - EF_WG_ALIGN_DOWN(size); + if (result == EF_NO_ERR && align_remain) { + memcpy(align_data, (uint8_t *)buf + EF_WG_ALIGN_DOWN(size), align_remain); + result = ef_port_write(addr + EF_WG_ALIGN_DOWN(size), (uint32_t *) align_data, sizeof(align_data)); + } + + return result; +} + static EfErrCode create_env_blob(const char *key, const void *value, size_t len) { EfErrCode result = EF_NO_ERR; @@ -741,21 +793,9 @@ static EfErrCode create_env_blob(const char *key, const void *value, size_t len) if ((env_addr = alloc_env(§or, env_hdr.len)) != GET_ADDR_FAILED) { size_t align_remain; uint32_t sec_addr = EF_ALIGN_DOWN(env_addr, SECTOR_SIZE); - /* change the sector status to SECTOR_STORE_USING */ + /* update the sector status */ if (result == EF_NO_ERR) { - uint8_t status_table[STORE_STATUS_TABLE_SIZE]; - /* change the current sector status */ - if (sector.status.store == SECTOR_STORE_EMPTY) { - /* change the sector status to using */ - result = write_status(sec_addr, status_table, SECTOR_STORE_STATUS_NUM, SECTOR_STORE_USING); - } else if (sector.status.store == SECTOR_STORE_USING) { - /* check remain size */ - if (sector.remain - env_hdr.len < EF_SEC_REMAIN_THRESHOLD) { - /* change the sector status to full */ - result = write_status(sec_addr, status_table, SECTOR_STORE_STATUS_NUM, SECTOR_STORE_FULL); - is_full = true; - } - } + result = update_sec_status(§or, env_hdr.len, &is_full); } if (result == EF_NO_ERR) { uint8_t ff = 0xFF; @@ -776,11 +816,11 @@ static EfErrCode create_env_blob(const char *key, const void *value, size_t len) } /* write key name */ if (result == EF_NO_ERR) { - result = ef_port_write(env_addr + ENV_HDR_DATA_SIZE, (uint32_t *) key, env_hdr.name_len); + result = align_write(env_addr + ENV_HDR_DATA_SIZE, (uint32_t *) key, env_hdr.name_len); } /* write value */ if (result == EF_NO_ERR) { - result = ef_port_write(env_addr + ENV_HDR_DATA_SIZE + EF_WG_ALIGN(env_hdr.name_len), value, + result = align_write(env_addr + ENV_HDR_DATA_SIZE + EF_WG_ALIGN(env_hdr.name_len), value, env_hdr.value_len); } /* change the ENV status to ENV_WRITE */ @@ -993,7 +1033,7 @@ __reload: } else { size = env->value_len - len; } - ef_port_read(env->addr.value + len, (uint32_t *) buf, size); + ef_port_read(env->addr.value + len, (uint32_t *) buf, EF_WG_ALIGN(size)); if (print_value) { ef_print("%.*s", size, buf); } else if (!ef_is_str(buf, size)) { @@ -1130,7 +1170,7 @@ EfErrCode ef_load_env(void) //TODO 装载环境变量元数据,using_sec_table //TODO 检查是否存在未完成的垃圾回收工作,存在则垃圾回收 /* check all sector header */ - sector_iterator(§or, NULL, NULL, check_sec_hdr_cb, false); + sector_iterator(§or, SECTOR_DIRTY_UNUSED, NULL, NULL, check_sec_hdr_cb, false); /* check all ENV for recovery */ env_iterator(&env, NULL, NULL, check_and_recovery_env_cb); @@ -1159,6 +1199,8 @@ EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size) { /* must be aligned with write granularity */ EF_ASSERT((EF_STR_ENV_VALUE_MAX_SIZE * 8) % EF_WRITE_GRAN == 0); + //TODO GC 扇区数量的检查 + env_start_addr = EF_START_ADDR; default_env_set = default_env; default_env_set_size = default_env_size;