【完善】不同写粒度情况下读写状态功能。

Signed-off-by: armink <armink.ztl@gmail.com>
This commit is contained in:
armink 2019-02-05 16:53:38 +08:00
parent 7f5217dde9
commit c42c306a39

View File

@ -71,16 +71,24 @@
#define EF_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1)) #define EF_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1))
/* align by write granularity */ /* align by write granularity */
#define EF_WG_ALIGN(size) (EF_ALIGN(size, (EF_WRITE_GRAN + 7)/8)) #define EF_WG_ALIGN(size) (EF_ALIGN(size, (EF_WRITE_GRAN + 7)/8))
/**
* Return the down number of aligned at specified width. RT_ALIGN_DOWN(13, 4)
* would return 12.
*/
#define EF_ALIGN_DOWN(size, align) ((size) & ~((align) - 1))
#define STATUS_MASK ((1UL << EF_WRITE_GRAN) - 1) #if (EF_WRITE_GRAN == 1)
#define STATUS_TABLE_SIZE(status_number) ((status_number * EF_WRITE_GRAN + 7)/8)
#define STATUS_TABLE_SIZE(status_number) ((status_number * EF_WRITE_GRAN + 7) / 8) #else
#define STATUS_TABLE_SIZE(status_number) (((status_number - 1) * EF_WRITE_GRAN + 7)/8)
#endif
#define STORE_STATUS_TABLE_SIZE STATUS_TABLE_SIZE(SECTOR_STORE_STATUS_NUM) #define STORE_STATUS_TABLE_SIZE STATUS_TABLE_SIZE(SECTOR_STORE_STATUS_NUM)
#define DIRTY_STATUS_TABLE_SIZE STATUS_TABLE_SIZE(SECTOR_DIRTY_STATUS_NUM) #define DIRTY_STATUS_TABLE_SIZE STATUS_TABLE_SIZE(SECTOR_DIRTY_STATUS_NUM)
#define ENV_STATUS_TABLE_SIZE STATUS_TABLE_SIZE(ENV_STATUS_NUM) #define ENV_STATUS_TABLE_SIZE STATUS_TABLE_SIZE(ENV_STATUS_NUM)
#define SECTOR_HDR_DATA_SIZE (EF_WG_ALIGN(sizeof(struct sector_hdr_data))) #define SECTOR_HDR_DATA_SIZE (EF_WG_ALIGN(sizeof(struct sector_hdr_data)))
#define SECTOR_DIRTY_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->status_table.dirty))
#define SECTOR_SIZE EF_ERASE_MIN_SIZE #define SECTOR_SIZE EF_ERASE_MIN_SIZE
#define ENV_HDR_DATA_SIZE (EF_WG_ALIGN(sizeof(struct env_hdr_data))) #define ENV_HDR_DATA_SIZE (EF_WG_ALIGN(sizeof(struct env_hdr_data)))
#define ENV_NAME_LEN_OFFSET ((unsigned long)(&((struct env_hdr_data *)0)->name_len)) #define ENV_NAME_LEN_OFFSET ((unsigned long)(&((struct env_hdr_data *)0)->name_len))
@ -218,9 +226,42 @@ static size_t get_status(uint8_t status_table[], size_t status_num)
return status_num_bak - i; return status_num_bak - i;
} }
static EfErrCode write_status(uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index)
{
EfErrCode result = EF_NO_ERR;
size_t byte_index;
EF_ASSERT(status_index < status_num);
EF_ASSERT(status_table);
/* set the status first */
set_status(status_table, status_num, status_index);
#if (EF_WRITE_GRAN == 1)
byte_index = status_index / 8;
result = ef_port_write(addr + byte_index, (uint32_t *)&status_table[byte_index], 1);
#else /* (EF_WRITE_GRAN == 8) || (EF_WRITE_GRAN == 32) || (EF_WRITE_GRAN == 64) */
byte_index = (status_num - 1 - status_index) * (EF_WRITE_GRAN / 8);
/* write the status by write granularity
* some flash (like stm32 onchip) NOT supported repeated write before erase */
result = ef_port_write(addr + byte_index, (uint32_t *) &status_table[byte_index], EF_WRITE_GRAN / 8);
#endif /* EF_WRITE_GRAN == 1 */
return result;
}
static size_t read_status(uint32_t addr, uint8_t status_table[], size_t total_num)
{
EF_ASSERT(status_table);
ef_port_read(addr, (uint32_t *) status_table, total_num);
return get_status(status_table, total_num);
}
static uint32_t get_next_env_addr(sector_meta_data_t sector, env_meta_data_t pre_env) static uint32_t get_next_env_addr(sector_meta_data_t sector, env_meta_data_t pre_env)
{ {
struct env_hdr_data env_hdr; uint8_t status_table[ENV_STATUS_TABLE_SIZE];
uint32_t addr = GET_NEXT_FAILED; uint32_t addr = GET_NEXT_FAILED;
if (sector->status.store == SECTOR_STORE_EMPTY) { if (sector->status.store == SECTOR_STORE_EMPTY) {
@ -239,10 +280,8 @@ static uint32_t get_next_env_addr(sector_meta_data_t sector, env_meta_data_t pre
return GET_NEXT_FAILED; return GET_NEXT_FAILED;
} }
} }
/* read ENV header raw data */
ef_port_read(addr, (uint32_t *) &env_hdr, sizeof(struct env_hdr_data));
/* check ENV status, it's ENV_UNUSED when not using */ /* check ENV status, it's ENV_UNUSED when not using */
if (get_status(env_hdr.status_table, ENV_STATUS_NUM) != ENV_UNUSED) { if (read_status(addr, status_table, ENV_STATUS_NUM) != ENV_UNUSED) {
return addr; return addr;
} else { } else {
/* no ENV */ /* no ENV */
@ -261,6 +300,11 @@ static EfErrCode read_env(env_meta_data_t env)
EfErrCode result = EF_NO_ERR; EfErrCode result = EF_NO_ERR;
/* read ENV header raw data */ /* read ENV header raw data */
ef_port_read(env->addr.start, (uint32_t *)&env_hdr, sizeof(struct env_hdr_data)); ef_port_read(env->addr.start, (uint32_t *)&env_hdr, sizeof(struct env_hdr_data));
if (env_hdr.len > ENV_AREA_SIZE) {
EF_INFO("Error: The ENV length is too big. May be the flash data has some errors.\n");
env->crc_is_ok = false;
return EF_READ_ERR;
}
env->len = env_hdr.len; env->len = env_hdr.len;
env->status = get_status(env_hdr.status_table, ENV_STATUS_NUM); env->status = get_status(env_hdr.status_table, ENV_STATUS_NUM);
/* CRC32 data len(header.name_len + header.value_len + name + value) */ /* CRC32 data len(header.name_len + header.value_len + name + value) */
@ -284,7 +328,7 @@ static EfErrCode read_env(env_meta_data_t env)
env->crc_is_ok = true; env->crc_is_ok = true;
/* the name is behind aligned ENV header */ /* the name is behind aligned ENV header */
env_name_addr = env->addr.start + ENV_HDR_DATA_SIZE; env_name_addr = env->addr.start + ENV_HDR_DATA_SIZE;
ef_port_read(env_name_addr, (uint32_t *) env->name, EF_ENV_NAME_MAX); ef_port_read(env_name_addr, (uint32_t *) env->name, env_hdr.name_len);
/* the value is behind aligned name */ /* the value is behind aligned name */
env->addr.value = env_name_addr + EF_WG_ALIGN(env_hdr.name_len); env->addr.value = env_name_addr + EF_WG_ALIGN(env_hdr.name_len);
env->value_len = env_hdr.value_len; env->value_len = env_hdr.value_len;
@ -333,7 +377,7 @@ static EfErrCode read_sector_meta_data(uint32_t addr, sector_meta_data_t sector,
sector->remain -= env_meta.len; sector->remain -= env_meta.len;
//TODO 大块数据占用连续扇区情形的处理 //TODO 大块数据占用连续扇区情形的处理
} else { } else {
EF_INFO("ERROR: The ENV (@0x%08x) CRC32 check failed!\n", env_meta.addr.start); EF_INFO("Error: The ENV (@0x%08x) CRC32 check failed!\n", env_meta.addr.start);
sector->remain = 0; sector->remain = 0;
result = EF_READ_ERR; result = EF_READ_ERR;
//TODO 完善 CRC 校验出错后的处理,比如标记扇区已经损坏 //TODO 完善 CRC 校验出错后的处理,比如标记扇区已经损坏
@ -370,12 +414,11 @@ static uint32_t get_next_sector_addr(sector_meta_data_t pre_sec)
} }
static void env_iterator(env_meta_data_t env, void *arg1, void *arg2, static void env_iterator(env_meta_data_t env, void *arg1, void *arg2,
bool *(callback)(env_meta_data_t env, void *arg1, void *arg2)) bool (*callback)(env_meta_data_t env, void *arg1, void *arg2))
{ {
struct sector_meta_data sector; struct sector_meta_data sector;
uint32_t sec_addr; uint32_t sec_addr;
//TODO 遍历所有正在使用及已满扇区
//TODO 支持从缓存中进行遍历 //TODO 支持从缓存中进行遍历
sector.addr = GET_NEXT_FAILED; sector.addr = GET_NEXT_FAILED;
@ -403,8 +446,13 @@ static bool find_env_cb(env_meta_data_t env, void *arg1, void *arg2)
{ {
const char *key = arg1; const char *key = arg1;
bool *find_ok = arg2; bool *find_ok = arg2;
uint8_t max_len = strlen(key);
if (max_len < env->name_len) {
max_len = env->name_len;
}
/* check ENV */ /* check ENV */
if (env->crc_is_ok && env->status == ENV_WRITE && !strncmp(env->name, key, EF_ENV_NAME_MAX)) { if (env->crc_is_ok && env->status == ENV_WRITE && !strncmp(env->name, key, max_len)) {
*find_ok = true; *find_ok = true;
return true; return true;
} }
@ -415,7 +463,7 @@ static bool find_env(const char *key, env_meta_data_t env)
{ {
bool find_ok = false; bool find_ok = false;
env_iterator(env, key, &find_ok, find_env_cb); env_iterator(env, (void *)key, &find_ok, find_env_cb);
return find_ok; return find_ok;
} }
@ -447,7 +495,7 @@ char *ef_get_env(const char *key)
static char value[EF_STR_ENV_VALUE_MAX_SIZE]; static char value[EF_STR_ENV_VALUE_MAX_SIZE];
struct env_meta_data env; struct env_meta_data env;
if (find_env(key, &env) == true) { 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, env.value_len);
/* the return value must be string */ /* the return value must be string */
if (ef_is_str((uint8_t *)value, env.value_len)) { if (ef_is_str((uint8_t *)value, env.value_len)) {
@ -461,37 +509,10 @@ char *ef_get_env(const char *key)
return NULL; return NULL;
} }
static EfErrCode write_status(uint32_t env_addr, uint8_t status_table[], size_t total_num, size_t status_index)
{
EfErrCode result = EF_NO_ERR;
size_t byte_index;
//TODO 1位的处理
//TODO 写字节情况的处理
//TODO 检查状态
EF_ASSERT(status_index < total_num);
EF_ASSERT(status_table);
/* set the status first */
set_status(status_table, total_num, status_index);
#if (EF_WRITE_GRAN == 1)
byte_index = status_index / 8;
result = ef_port_write(env_addr + byte_index, (uint32_t *)&status_table[byte_index], 1);
#else /* (EF_WRITE_GRAN == 8) || (EF_WRITE_GRAN == 32) || (EF_WRITE_GRAN == 64) */
byte_index = total_num - status_index * (EF_WRITE_GRAN / 8);
/* write the status by write granularity
* some flash (like stm32 onchip) NOT supported repeated write before erase */
result = ef_port_write(env_addr + byte_index, (uint32_t *) &status_table[byte_index], EF_WRITE_GRAN / 8);
#endif /* EF_WRITE_GRAN == 1 */
return result;
}
static EfErrCode write_env_hdr(uint32_t addr, env_hdr_data_t env_hdr) { static EfErrCode write_env_hdr(uint32_t addr, env_hdr_data_t env_hdr) {
EfErrCode result = EF_NO_ERR; EfErrCode result = EF_NO_ERR;
/* write the status will by write granularity */ /* write the status will by write granularity */
result = write_status(addr, env_hdr->status_table, ENV_STATUS_TABLE_SIZE, ENV_PRE_WRITE); result = write_status(addr, env_hdr->status_table, ENV_STATUS_NUM, ENV_PRE_WRITE);
if (result != EF_NO_ERR) { if (result != EF_NO_ERR) {
return result; return result;
} }
@ -508,13 +529,18 @@ static EfErrCode create_env_blob(const char *key, const void *value, size_t len)
static struct sector_meta_data sector; static struct sector_meta_data sector;
uint32_t sec_addr; uint32_t sec_addr;
if (strlen(key) > EF_ENV_NAME_MAX) {
EF_INFO("Error: The ENV name length is more than %d\n", EF_ENV_NAME_MAX);
return EF_ENV_NAME_ERR;
}
memset(&env_hdr, 0xFF, sizeof(struct env_hdr_data)); memset(&env_hdr, 0xFF, sizeof(struct env_hdr_data));
env_hdr.name_len = strlen(key); env_hdr.name_len = strlen(key);
env_hdr.value_len = len; env_hdr.value_len = len;
env_hdr.len = ENV_HDR_DATA_SIZE + EF_WG_ALIGN(env_hdr.name_len) + EF_WG_ALIGN(env_hdr.value_len); env_hdr.len = ENV_HDR_DATA_SIZE + EF_WG_ALIGN(env_hdr.name_len) + EF_WG_ALIGN(env_hdr.value_len);
if (env_hdr.len > SECTOR_SIZE - SECTOR_HDR_DATA_SIZE) { if (env_hdr.len > SECTOR_SIZE - SECTOR_HDR_DATA_SIZE) {
EF_INFO("ERROR: The ENV size is too big\n"); EF_INFO("Error: The ENV size is too big\n");
return EF_ENV_FULL; return EF_ENV_FULL;
} }
@ -535,12 +561,12 @@ static EfErrCode create_env_blob(const char *key, const void *value, size_t len)
/* change the current sector status */ /* change the current sector status */
if (sector.status.store == SECTOR_STORE_EMPTY) { if (sector.status.store == SECTOR_STORE_EMPTY) {
/* change the sector status to using */ /* change the sector status to using */
result = write_status(sec_addr, status_table, STORE_STATUS_TABLE_SIZE, SECTOR_STORE_USING); result = write_status(sec_addr, status_table, SECTOR_STORE_STATUS_NUM, SECTOR_STORE_USING);
} else if (sector.status.store == SECTOR_STORE_USING) { } else if (sector.status.store == SECTOR_STORE_USING) {
/* check remain size */ /* check remain size */
if (sector.remain - env_hdr.len < EF_SEC_REMAIN_THRESHOLD) { if (sector.remain - env_hdr.len < EF_SEC_REMAIN_THRESHOLD) {
/* change the sector status to full */ /* change the sector status to full */
result = write_status(sec_addr, status_table, STORE_STATUS_TABLE_SIZE, SECTOR_STORE_FULL); result = write_status(sec_addr, status_table, SECTOR_STORE_STATUS_NUM, SECTOR_STORE_FULL);
} }
} }
} }
@ -550,12 +576,12 @@ static EfErrCode create_env_blob(const char *key, const void *value, size_t len)
env_hdr.crc32 = ef_calc_crc32(0, &env_hdr.name_len, ENV_HDR_DATA_SIZE - ENV_NAME_LEN_OFFSET); env_hdr.crc32 = ef_calc_crc32(0, &env_hdr.name_len, ENV_HDR_DATA_SIZE - ENV_NAME_LEN_OFFSET);
env_hdr.crc32 = ef_calc_crc32(env_hdr.crc32, key, env_hdr.name_len); env_hdr.crc32 = ef_calc_crc32(env_hdr.crc32, key, env_hdr.name_len);
align_remain = EF_WG_ALIGN(env_hdr.name_len) - env_hdr.name_len; align_remain = EF_WG_ALIGN(env_hdr.name_len) - env_hdr.name_len;
while (align_remain) { while (align_remain--) {
env_hdr.crc32 = ef_calc_crc32(env_hdr.crc32, &ff, 1); env_hdr.crc32 = ef_calc_crc32(env_hdr.crc32, &ff, 1);
} }
env_hdr.crc32 = ef_calc_crc32(env_hdr.crc32, value, env_hdr.value_len); env_hdr.crc32 = ef_calc_crc32(env_hdr.crc32, value, env_hdr.value_len);
align_remain = EF_WG_ALIGN(env_hdr.value_len) - env_hdr.value_len; align_remain = EF_WG_ALIGN(env_hdr.value_len) - env_hdr.value_len;
while (align_remain) { while (align_remain--) {
env_hdr.crc32 = ef_calc_crc32(env_hdr.crc32, &ff, 1); env_hdr.crc32 = ef_calc_crc32(env_hdr.crc32, &ff, 1);
} }
/* write ENV header data */ /* write ENV header data */
@ -572,7 +598,7 @@ static EfErrCode create_env_blob(const char *key, const void *value, size_t len)
} }
/* change the ENV status to ENV_WRITE */ /* change the ENV status to ENV_WRITE */
if (result == EF_NO_ERR) { if (result == EF_NO_ERR) {
result = write_status(env_addr, env_hdr.status_table, ENV_STATUS_TABLE_SIZE, ENV_WRITE); result = write_status(env_addr, env_hdr.status_table, ENV_STATUS_NUM, ENV_WRITE);
} }
//TODO 更新 using_sec_table //TODO 更新 using_sec_table
break; break;
@ -584,26 +610,38 @@ static EfErrCode create_env_blob(const char *key, const void *value, size_t len)
static EfErrCode del_env(const char *key, env_meta_data_t old_env, bool pre_del) { static EfErrCode del_env(const char *key, env_meta_data_t old_env, bool pre_del) {
EfErrCode result = EF_NO_ERR; EfErrCode result = EF_NO_ERR;
struct env_meta_data env; uint32_t dirty_status_addr;
#if (ENV_STATUS_TABLE_SIZE >= DIRTY_STATUS_TABLE_SIZE)
uint8_t status_table[ENV_STATUS_TABLE_SIZE]; uint8_t status_table[ENV_STATUS_TABLE_SIZE];
#else
uint8_t status_table[ENV_STATUS_TABLE_SIZE];
#endif
/* need find ENV */ /* need find ENV */
if (!old_env) { if (!old_env) {
struct env_meta_data env;
/* find ENV */ /* find ENV */
if (find_env(key, &env)) { if (find_env(key, &env)) {
old_env = &env; old_env = &env;
} else { } else {
EF_INFO("Error: Not found '%s' in ENV.\n", key); EF_DEBUG("Not found '%s' in ENV.\n", key);
return EF_ENV_NAME_ERR; return EF_ENV_NAME_ERR;
} }
} }
set_status(status_table, ENV_STATUS_TABLE_SIZE, old_env->status);
/* change and save the new status */ /* change and save the new status */
if (pre_del) { if (pre_del) {
result = write_status(old_env->addr.start, status_table, ENV_STATUS_TABLE_SIZE, ENV_PRE_DELETE); result = write_status(old_env->addr.start, status_table, ENV_STATUS_NUM, ENV_PRE_DELETE);
} else { } else {
result = write_status(old_env->addr.start, status_table, ENV_STATUS_TABLE_SIZE, ENV_DELETED); result = write_status(old_env->addr.start, status_table, ENV_STATUS_NUM, ENV_DELETED);
}
dirty_status_addr = EF_ALIGN_DOWN(old_env->addr.start, SECTOR_SIZE) + SECTOR_DIRTY_OFFSET;
/* read and change the sector dirty status */
if (result == EF_NO_ERR
&& read_status(dirty_status_addr, status_table, SECTOR_DIRTY_STATUS_NUM) == SECTOR_DIRTY_FALSE) {
result = write_status(dirty_status_addr, status_table, SECTOR_DIRTY_STATUS_NUM, SECTOR_DIRTY_TRUE);
} }
//TODO 标记脏
return result; return result;
} }
@ -620,7 +658,7 @@ EfErrCode ef_del_env(const char *key)
EfErrCode result = EF_NO_ERR; EfErrCode result = EF_NO_ERR;
if (!init_ok) { if (!init_ok) {
EF_INFO("ERROR: ENV isn't initialize OK.\n"); EF_INFO("Error: ENV isn't initialize OK.\n");
return EF_ENV_INIT_FAILED; return EF_ENV_INIT_FAILED;
} }
@ -648,6 +686,7 @@ EfErrCode ef_set_env(const char *key, const char *value)
{ {
EfErrCode result = EF_NO_ERR; EfErrCode result = EF_NO_ERR;
struct env_meta_data env; struct env_meta_data env;
bool env_is_found = false;
if (!init_ok) { if (!init_ok) {
EF_INFO("ENV isn't initialize OK.\n"); EF_INFO("ENV isn't initialize OK.\n");
@ -660,8 +699,9 @@ EfErrCode ef_set_env(const char *key, const char *value)
if (value == NULL) { if (value == NULL) {
result = del_env(key, NULL, false); result = del_env(key, NULL, false);
} else { } else {
env_is_found = find_env(key, &env);
/* prepare to delete the old ENV */ /* prepare to delete the old ENV */
if (find_env(key, &env) == true) { if (env_is_found) {
result = del_env(key, &env, true); result = del_env(key, &env, true);
} }
/* create the new ENV */ /* create the new ENV */
@ -669,7 +709,7 @@ EfErrCode ef_set_env(const char *key, const char *value)
result = create_env_blob(key, value, strlen(value)); result = create_env_blob(key, value, strlen(value));
} }
/* delete the old ENV */ /* delete the old ENV */
if (result == EF_NO_ERR) { if (env_is_found && result == EF_NO_ERR) {
result = del_env(key, &env, false); result = del_env(key, &env, false);
} }
} }
@ -860,7 +900,6 @@ EfErrCode ef_load_env(void)
if (env.crc_is_ok && env.status == ENV_PRE_DELETE) { if (env.crc_is_ok && env.status == ENV_PRE_DELETE) {
//TODO 增加新节点存储旧 ENV //TODO 增加新节点存储旧 ENV
//TODO 标记旧 ENV 为已删除 //TODO 标记旧 ENV 为已删除
return true;
} }
} }
/* calculate next sector address */ /* calculate next sector address */