/* * This file is part of the EasyFlash Library. * * Copyright (c) 2015-2018, Armink, * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * 'Software'), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Function: Environment variables operating interface. (wear leveling mode) * Created on: 2015-02-11 */ #include #include #include #ifdef EF_USING_ENV #ifdef EF_ENV_USING_WL_MODE /** * ENV area has 2 sections * 1. System section * Storage ENV current using data section address. * Units: Word. Total size: @see EF_ERASE_MIN_SIZE. * 2. Data section * The data section storage ENV's parameters and detail. * When an exception has occurred on flash erase or write. The current using data section * address will move to next available position. This position depends on EF_ERASE_MIN_SIZE. * 2.1 ENV parameters part * It storage ENV's parameters. * 2.2 ENV detail part * It storage all ENV. Storage format is key=value\0. * All ENV must be 4 bytes alignment. The remaining part must fill '\0'. * * @note Word = 4 Bytes in this file * @note It will has two ENV areas(Area0, Area1) in data section when used power fail safeguard mode. */ /* flash ENV parameters part index and size */ enum { /* data section ENV detail part end address index */ ENV_PARAM_PART_INDEX_END_ADDR = 0, #ifdef EF_ENV_USING_PFS_MODE /* saved count for ENV area */ ENV_PARAM_PART_INDEX_SAVED_COUNT, #endif #ifdef EF_ENV_AUTO_UPDATE /* current version number for ENV */ ENV_PARAM_INDEX_VER_NUM, #endif /* data section CRC32 code index */ ENV_PARAM_PART_INDEX_DATA_CRC, /* ENV parameters part word size */ ENV_PARAM_PART_WORD_SIZE, /* ENV parameters part byte size */ ENV_PARAM_PART_BYTE_SIZE = ENV_PARAM_PART_WORD_SIZE * 4, }; /* default ENV set, must be initialized by user */ static ef_env const *default_env_set; /* default ENV set size, must be initialized by user */ static size_t default_env_set_size = 0; /* flash ENV data section size */ static size_t env_data_section_size = 0; /* ENV ram cache */ static uint32_t env_cache[ENV_USER_SETTING_SIZE / 4] = { 0 }; /* ENV start address in flash */ static uint32_t env_start_addr = 0; /* current using data section address */ static uint32_t cur_using_data_addr = 0; /* ENV ram cache has changed when ENV created, deleted and changed value. */ static bool env_cache_changed = false; /* initialize OK flag */ static bool init_ok = false; #ifdef EF_ENV_USING_PFS_MODE /* next save ENV area address */ static uint32_t next_save_area_addr = 0; #endif static uint32_t get_env_start_addr(void); static uint32_t get_cur_using_data_addr(void); static uint32_t get_env_detail_addr(void); static uint32_t get_env_detail_end_addr(void); static void set_cur_using_data_addr(uint32_t using_data_addr); static void set_env_detail_end_addr(uint32_t end_addr); static EfErrCode write_env(const char *key, const char *value); static char *find_env(const char *key); static size_t get_env_detail_size(void); static size_t get_env_user_used_size(void); static EfErrCode create_env(const char *key, const char *value); static EfErrCode del_env(const char *key); static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr); static uint32_t calc_env_crc(void); static bool env_crc_is_ok(void); static EfErrCode env_auto_update(void); /** * Flash ENV initialize. * * @param default_env default ENV set for user * @param default_env_size default ENV set size * * @return result */ EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size) { EfErrCode result = EF_NO_ERR; EF_ASSERT(ENV_AREA_SIZE); EF_ASSERT(ENV_USER_SETTING_SIZE); /* must be word alignment for ENV */ EF_ASSERT(ENV_USER_SETTING_SIZE % 4 == 0); EF_ASSERT(ENV_AREA_SIZE % 4 == 0); EF_ASSERT(default_env); EF_ASSERT(default_env_size < ENV_USER_SETTING_SIZE); #ifndef EF_ENV_USING_PFS_MODE /* system section size is erase_min_size, so last part is data section */ env_data_section_size = ENV_AREA_SIZE - EF_ERASE_MIN_SIZE; #else /* system section size is erase_min_size, so last part is data section */ env_data_section_size = ENV_AREA_SIZE / 2 - EF_ERASE_MIN_SIZE; EF_ASSERT((ENV_AREA_SIZE / EF_ERASE_MIN_SIZE) % 2 == 0); #endif EF_ASSERT(env_data_section_size >= ENV_USER_SETTING_SIZE); /* the ENV data section size should be an integral multiple of erase minimum size. */ EF_ASSERT(env_data_section_size % EF_ERASE_MIN_SIZE == 0); env_start_addr = EF_START_ADDR; default_env_set = default_env; default_env_set_size = default_env_size; EF_DEBUG("ENV start address is 0x%08X, size is %d bytes.\n", EF_START_ADDR, ENV_AREA_SIZE); result = ef_load_env(); #ifdef EF_ENV_AUTO_UPDATE if (result == EF_NO_ERR) { env_auto_update(); } #endif if (result == EF_NO_ERR) { init_ok = true; } return result; } /** * ENV set default. * * @return result */ EfErrCode ef_env_set_default(void) { EfErrCode result = EF_NO_ERR; size_t i; EF_ASSERT(default_env_set); EF_ASSERT(default_env_set_size); /* lock the ENV cache */ ef_port_env_lock(); /* set ENV detail part end address is at ENV detail part start address */ set_env_detail_end_addr(get_env_detail_addr()); #ifdef EF_ENV_USING_PFS_MODE /* set saved count to default 0 */ env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT] = 0; #endif #ifdef EF_ENV_AUTO_UPDATE /* initialize version number */ env_cache[ENV_PARAM_INDEX_VER_NUM] = EF_ENV_VER_NUM; #endif /* create default ENV */ for (i = 0; i < default_env_set_size; i++) { create_env(default_env_set[i].key, default_env_set[i].value); } /* unlock the ENV cache */ ef_port_env_unlock(); result = ef_save_env(); #ifdef EF_ENV_USING_PFS_MODE /* reset other PFS area's data */ if (result == EF_NO_ERR) { env_cache_changed = true; result = ef_save_env(); } #endif return result; } /** * Get ENV start address in flash. * * @return ENV start address in flash */ static uint32_t get_env_start_addr(void) { return env_start_addr; } /** * Get current using data section address. * * @return current using data section address */ static uint32_t get_cur_using_data_addr(void) { return cur_using_data_addr; } /** * Set current using data section address. * * @param using_data_addr current using data section address */ static void set_cur_using_data_addr(uint32_t using_data_addr) { cur_using_data_addr = using_data_addr; } /** * Get ENV detail part start address. * * @return detail part start address */ static uint32_t get_env_detail_addr(void) { return get_cur_using_data_addr() + ENV_PARAM_PART_BYTE_SIZE; } /** * Get ENV detail part end address. * It's the first word in ENV. * * @return ENV end address */ static uint32_t get_env_detail_end_addr(void) { /* it is the first word */ return env_cache[ENV_PARAM_PART_INDEX_END_ADDR]; } /** * Set ENV detail part end address. * It's the first word in ENV. * * @param end_addr ENV end address */ static void set_env_detail_end_addr(uint32_t end_addr) { env_cache[ENV_PARAM_PART_INDEX_END_ADDR] = end_addr; } /** * Get current ENV detail part size. * * @return size */ static size_t get_env_detail_size(void) { if (get_env_detail_end_addr() > get_env_detail_addr()) { return get_env_detail_end_addr() - get_env_detail_addr(); } else { return 0; } } /** * Get current user used ENV size. * * @see ENV_USER_SETTING_SIZE * * @return size */ /* must be initialized */ static size_t get_env_user_used_size(void) { if (get_env_detail_end_addr() > get_cur_using_data_addr()) { return get_env_detail_end_addr() - get_cur_using_data_addr(); } else { return 0; } } /** * Get current ENV already write bytes. * * @return write bytes */ size_t ef_get_env_write_bytes(void) { #ifndef EF_ENV_USING_PFS_MODE return get_env_detail_end_addr() - get_env_start_addr(); #else return EF_ERASE_MIN_SIZE + get_env_detail_end_addr() - get_cur_using_data_addr(); #endif } /** * Write an ENV at the end of cache. * * @param key ENV name * @param value ENV value * * @return result */ static EfErrCode write_env(const char *key, const char *value) { EfErrCode result = EF_NO_ERR; size_t ker_len = strlen(key), value_len = strlen(value), env_str_len; char *env_cache_bak = (char *)env_cache; /* calculate ENV storage length, contain '=' and '\0'. */ env_str_len = ker_len + value_len + 2; if (env_str_len % 4 != 0) { env_str_len = (env_str_len / 4 + 1) * 4; } /* check capacity of ENV */ if (env_str_len + get_env_user_used_size() >= ENV_USER_SETTING_SIZE) { return EF_ENV_FULL; } /* calculate current ENV ram cache end address */ env_cache_bak += ENV_PARAM_PART_BYTE_SIZE + get_env_detail_size(); /* copy key name */ memcpy(env_cache_bak, key, ker_len); env_cache_bak += ker_len; /* copy equal sign */ *env_cache_bak = '='; env_cache_bak++; /* copy value */ memcpy(env_cache_bak, value, value_len); env_cache_bak += value_len; /* fill '\0' for string end sign */ *env_cache_bak = '\0'; env_cache_bak ++; /* fill '\0' for word alignment */ memset(env_cache_bak, 0, env_str_len - (ker_len + value_len + 2)); set_env_detail_end_addr(get_env_detail_end_addr() + env_str_len); /* ENV ram cache has changed */ env_cache_changed = true; return result; } /** * Find ENV. * * @param key ENV name * * @return found ENV in ram cache */ static char *find_env(const char *key) { char *env_start, *env_end, *env, *found_env = NULL; size_t key_len = strlen(key), env_len; if ((key == NULL) || (*key == '\0')) { EF_INFO("Flash ENV name must be not empty!\n"); return NULL; } /* from data section start to data section end */ env_start = (char *) ((char *) env_cache + ENV_PARAM_PART_BYTE_SIZE); env_end = (char *) ((char *) env_cache + ENV_PARAM_PART_BYTE_SIZE + get_env_detail_size()); /* ENV is null */ if (env_start == env_end) { return NULL; } env = env_start; while (env < env_end) { /* the key length must be equal */ if (!strncmp(env, key, key_len) && (env[key_len] == '=')) { found_env = env; break; } else { /* calculate ENV length, contain '\0'. */ env_len = strlen(env) + 1; /* next ENV and word alignment */ if (env_len % 4 == 0) { env += env_len; } else { env += (env_len / 4 + 1) * 4; } } } return found_env; } /** * If the ENV is not exist, create it. * @see flash_write_env * * @param key ENV name * @param value ENV value * * @return result */ static EfErrCode create_env(const char *key, const char *value) { EfErrCode result = EF_NO_ERR; EF_ASSERT(key); EF_ASSERT(value); if ((key == NULL) || (*key == '\0')) { EF_INFO("Flash ENV name must be not empty!\n"); return EF_ENV_NAME_ERR; } if (strchr(key, '=')) { EF_INFO("Flash ENV name can't contain '='.\n"); return EF_ENV_NAME_ERR; } /* find ENV */ if (find_env(key)) { EF_INFO("The name of \"%s\" is already exist.\n", key); return EF_ENV_NAME_EXIST; } /* write ENV at the end of cache */ result = write_env(key, value); return result; } /** * Delete an ENV in cache. * * @param key ENV name * * @return result */ static EfErrCode del_env(const char *key) { EfErrCode result = EF_NO_ERR; char *del_env = NULL; size_t del_env_length, remain_env_length; EF_ASSERT(key); if ((key == NULL) || (*key == '\0')) { EF_INFO("Flash ENV name must be not NULL!\n"); return EF_ENV_NAME_ERR; } if (strchr(key, '=')) { EF_INFO("Flash ENV name or value can't contain '='.\n"); return EF_ENV_NAME_ERR; } /* find ENV */ del_env = find_env(key); if (!del_env) { EF_INFO("Not find \"%s\" in ENV.\n", key); return EF_ENV_NAME_ERR; } del_env_length = strlen(del_env); /* '\0' also must be as ENV length */ del_env_length ++; /* the address must multiple of 4 */ if (del_env_length % 4 != 0) { del_env_length = (del_env_length / 4 + 1) * 4; } /* calculate remain ENV length */ remain_env_length = get_env_detail_size() - (((uint32_t) del_env + del_env_length) - ((uint32_t) env_cache + ENV_PARAM_PART_BYTE_SIZE)); /* remain ENV move forward */ memcpy(del_env, del_env + del_env_length, remain_env_length); /* reset ENV end address */ set_env_detail_end_addr(get_env_detail_end_addr() - del_env_length); /* ENV ram cache has changed */ env_cache_changed = true; return result; } /** * Set an ENV.If it value is NULL, delete it. * If not find it in ENV table, then create it. * * @param key ENV name * @param value ENV value * * @return result */ EfErrCode ef_set_env(const char *key, const char *value) { EfErrCode result = EF_NO_ERR; char *old_env, *old_value; if (!init_ok) { EF_INFO("ENV isn't initialize OK.\n"); return EF_ENV_INIT_FAILED; } /* lock the ENV cache */ ef_port_env_lock(); /* if ENV value is NULL, delete it */ if (value == NULL) { result = del_env(key); } else { old_env = find_env(key); /* If find this ENV, then compare the new value and old value. */ if (old_env) { /* find the old value address */ old_env = strchr(old_env, '='); old_value = old_env + 1; /* If it is changed then delete it and recreate it */ if (strcmp(old_value, value)) { result = del_env(key); if (result == EF_NO_ERR) { result = create_env(key, value); } } } else { result = create_env(key, value); } } /* unlock the ENV cache */ ef_port_env_unlock(); return result; } /** * Del an ENV. * * @param key ENV name * * @return result */ EfErrCode ef_del_env(const char *key) { EfErrCode result = EF_NO_ERR; if (!init_ok) { EF_INFO("ENV isn't initialize OK.\n"); return EF_ENV_INIT_FAILED; } /* lock the ENV cache */ ef_port_env_lock(); result = del_env(key); /* unlock the ENV cache */ ef_port_env_unlock(); return result; } /** * Get an ENV value by key name. * * @param key ENV name * * @return value */ char *ef_get_env(const char *key) { char *env = NULL, *value = NULL; if (!init_ok) { EF_INFO("ENV isn't initialize OK.\n"); return NULL; } /* find ENV */ env = find_env(key); if (env == NULL) { return NULL; } /* get value address */ value = strchr(env, '='); if (value != NULL) { /* the equal sign next character is value */ value++; } return value; } /** * Print ENV. */ void ef_print_env(void) { uint32_t *env_cache_detail_addr = env_cache + ENV_PARAM_PART_WORD_SIZE, *env_cache_end_addr = (uint32_t *) (env_cache + ENV_PARAM_PART_WORD_SIZE + get_env_detail_size() / 4); uint8_t j; char c; if (!init_ok) { EF_INFO("ENV isn't initialize OK.\n"); return; } for (; env_cache_detail_addr < env_cache_end_addr; env_cache_detail_addr += 1) { for (j = 0; j < 4; j++) { c = (*env_cache_detail_addr) >> (8 * j); ef_print("%c", c); if (c == NULL) { ef_print("\n"); break; } } } #ifndef EF_ENV_USING_PFS_MODE ef_print("\nmode: wear leveling\n"); ef_print("size: %ld/%ld bytes, write bytes %ld/%ld.\n", get_env_user_used_size(), ENV_USER_SETTING_SIZE, ef_get_env_write_bytes(), ENV_AREA_SIZE); #else ef_print("\nmode: wear leveling and power fail safeguard\n"); ef_print("size: %ld/%ld bytes, write bytes %ld/%ld.\n", get_env_user_used_size(), ENV_USER_SETTING_SIZE, ef_get_env_write_bytes(), ENV_AREA_SIZE / 2); ef_print("saved count: %ld\n", env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT]); #endif #ifdef EF_ENV_AUTO_UPDATE ef_print("ver num: %d\n", env_cache[ENV_PARAM_INDEX_VER_NUM]); #endif } /** * Load flash ENV to ram. * * @return result */ #ifndef EF_ENV_USING_PFS_MODE EfErrCode ef_load_env(void) { EfErrCode result = EF_NO_ERR; uint32_t *env_cache_bak, env_end_addr, using_data_addr; /* read current using data section address */ ef_port_read(get_env_start_addr(), &using_data_addr, 4); /* if ENV is not initialize or flash has dirty data, set default for it */ if ((using_data_addr == 0xFFFFFFFF) || (using_data_addr > get_env_start_addr() + ENV_AREA_SIZE) || (using_data_addr < get_env_start_addr() + EF_ERASE_MIN_SIZE)) { /* initialize current using data section address */ set_cur_using_data_addr(get_env_start_addr() + EF_ERASE_MIN_SIZE); /* save current using data section address to flash*/ if ((result = save_cur_using_data_addr(get_cur_using_data_addr())) == EF_NO_ERR) { /* set default ENV */ result = ef_env_set_default(); } } else { /* set current using data section address */ set_cur_using_data_addr(using_data_addr); /* read ENV detail part end address from flash */ ef_port_read(get_cur_using_data_addr() + ENV_PARAM_PART_INDEX_END_ADDR * 4, &env_end_addr, 4); /* if ENV end address has error, set default for ENV */ if (env_end_addr > get_env_start_addr() + ENV_AREA_SIZE) { /* initialize current using data section address */ set_cur_using_data_addr(get_env_start_addr() + EF_ERASE_MIN_SIZE); /* save current using data section address to flash*/ if ((result = save_cur_using_data_addr(get_cur_using_data_addr())) == EF_NO_ERR) { EF_INFO("Warning: ENV end address has error. Set it to default.\n"); result = ef_env_set_default(); } } else { /* set ENV detail part end address */ set_env_detail_end_addr(env_end_addr); env_cache_bak = env_cache + ENV_PARAM_PART_WORD_SIZE; /* read all ENV from flash */ ef_port_read(get_env_detail_addr(), env_cache_bak, get_env_detail_size()); /* read ENV CRC code from flash */ ef_port_read(get_cur_using_data_addr() + ENV_PARAM_PART_INDEX_DATA_CRC * 4, &env_cache[ENV_PARAM_PART_INDEX_DATA_CRC], 4); /* if ENV CRC32 check is fault, set default for it */ if (!env_crc_is_ok()) { EF_INFO("Warning: ENV CRC check failed. Set it to default.\n"); result = ef_env_set_default(); } } } return result; } #else EfErrCode ef_load_env(void) { EfErrCode result = EF_NO_ERR; /* ENV area0 current using address default value */ uint32_t area0_default_cur_using_addr = get_env_start_addr() + EF_ERASE_MIN_SIZE; /* ENV area1 current using address default value */ uint32_t area1_default_cur_using_addr = area0_default_cur_using_addr + ENV_AREA_SIZE / 2; uint32_t area0_cur_using_addr, area1_cur_using_addr, area0_end_addr, area1_end_addr; uint32_t area0_crc, area1_crc, area0_saved_count, area1_saved_count; bool area0_is_valid = true, area1_is_valid = true; /* read ENV area0 and area1 current using data section address */ ef_port_read(get_env_start_addr(), &area0_cur_using_addr, 4); ef_port_read(get_env_start_addr() + ENV_AREA_SIZE / 2, &area1_cur_using_addr, 4); /* if ENV is not initialize or flash has dirty data, set it isn't valid */ if ((area0_cur_using_addr == 0xFFFFFFFF) || (area0_cur_using_addr > get_env_start_addr() + ENV_AREA_SIZE / 2) || (area0_cur_using_addr < get_env_start_addr() + EF_ERASE_MIN_SIZE)) { area0_is_valid = false; } if ((area1_cur_using_addr == 0xFFFFFFFF) || (area1_cur_using_addr > get_env_start_addr() + ENV_AREA_SIZE) || (area1_cur_using_addr < get_env_start_addr() + ENV_AREA_SIZE / 2 + EF_ERASE_MIN_SIZE)) { area1_is_valid = false; } /* check area0 end address when it is valid */ if (area0_is_valid) { /* read ENV area end address from flash */ ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_END_ADDR * 4, &area0_end_addr, 4); if ((area0_end_addr == 0xFFFFFFFF) || (area0_end_addr < area0_cur_using_addr) || (area0_end_addr > area0_cur_using_addr + ENV_USER_SETTING_SIZE)) { area0_is_valid = false; } } /* check area1 end address when it is valid */ if (area1_is_valid) { /* read ENV area end address from flash */ ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_END_ADDR * 4, &area1_end_addr, 4); if ((area1_end_addr == 0xFFFFFFFF) || (area1_end_addr < area1_cur_using_addr) || (area1_end_addr > area1_cur_using_addr + ENV_USER_SETTING_SIZE)) { area1_is_valid = false; } } /* check area0 CRC when it is valid */ if (area0_is_valid) { /* read ENV area0 crc32 code from flash */ ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_DATA_CRC * 4, &area0_crc, 4); /* read ENV from ENV area0 */ ef_port_read(area0_cur_using_addr, env_cache, area0_end_addr - area0_cur_using_addr); /* current using data section address is area0 current using data section address */ set_cur_using_data_addr(area0_cur_using_addr); if (!env_crc_is_ok()) { area0_is_valid = false; } } /* check area1 CRC when it is valid */ if (area1_is_valid) { /* read ENV area1 crc32 code from flash */ ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_DATA_CRC * 4, &area1_crc, 4); /* read ENV from ENV area1 */ ef_port_read(area1_cur_using_addr, env_cache, area1_end_addr - area1_cur_using_addr); /* current using data section address is area1 current using data section address */ set_cur_using_data_addr(area1_cur_using_addr); if (!env_crc_is_ok()) { area1_is_valid = false; } } /* all ENV area CRC is OK then compare saved count */ if (area0_is_valid && area1_is_valid) { /* read ENV area saved count from flash */ ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_SAVED_COUNT * 4, &area0_saved_count, 4); ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_SAVED_COUNT * 4, &area1_saved_count, 4); /* the bigger saved count area is valid */ if ((area0_saved_count > area1_saved_count) || ((area0_saved_count == 0) && (area1_saved_count == 0xFFFFFFFF))) { area1_is_valid = false; } else { area0_is_valid = false; } } if (area0_is_valid) { /* current using data section address is area0 current using data section address */ set_cur_using_data_addr(area0_cur_using_addr); /* next save ENV area address is area1 current using address value */ next_save_area_addr = area1_cur_using_addr; /* read all ENV from area0 */ ef_port_read(area0_cur_using_addr, env_cache, area0_end_addr - area0_cur_using_addr); } else if (area1_is_valid) { /* already read data section and set_cur_using_data_addr above current code, * so just set next save ENV area address is area0 current using address value */ next_save_area_addr = area0_cur_using_addr; } else { /* current using data section address is area1 current using address default value */ set_cur_using_data_addr(area1_default_cur_using_addr); /* next save ENV area address default is area0 current using address default value */ next_save_area_addr = area0_default_cur_using_addr; /* save current using data section address to flash*/ if (((result = save_cur_using_data_addr(area0_default_cur_using_addr)) == EF_NO_ERR) && ((result = save_cur_using_data_addr(area1_default_cur_using_addr)) == EF_NO_ERR)) { /* set the ENV to default */ result = ef_env_set_default(); } } return result; } #endif /** * Save ENV to flash. */ EfErrCode ef_save_env(void) { EfErrCode result = EF_NO_ERR; uint32_t cur_using_addr_bak, move_offset_addr; size_t env_used_size = get_env_user_used_size(); uint32_t data_sec_end_addr; /* ENV ram cache has not changed don't need to save */ if (!env_cache_changed) { return result; } #ifndef EF_ENV_USING_PFS_MODE data_sec_end_addr = get_env_start_addr() + ENV_AREA_SIZE - 4; cur_using_addr_bak = get_cur_using_data_addr(); #else cur_using_addr_bak = next_save_area_addr; /* replace next_save_area_addr with cur_using_data_addr */ next_save_area_addr = get_cur_using_data_addr(); set_cur_using_data_addr(cur_using_addr_bak); /* change the ENV detail end address to next save area address */ set_env_detail_end_addr(get_cur_using_data_addr() + env_used_size); /* area0 or area1 */ if (get_cur_using_data_addr() < get_env_start_addr() + ENV_AREA_SIZE / 2) { data_sec_end_addr = get_env_start_addr() + ENV_AREA_SIZE / 2 - 4; } else { data_sec_end_addr = get_env_start_addr() + ENV_AREA_SIZE - 4; } /* ENV area saved count +1 */ env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT]++; #endif /* wear leveling process, automatic move ENV to next available position */ while (get_cur_using_data_addr() + env_used_size < data_sec_end_addr) { /* calculate and cache CRC32 code */ env_cache[ENV_PARAM_PART_INDEX_DATA_CRC] = calc_env_crc(); /* erase ENV */ result = ef_port_erase(get_cur_using_data_addr(), env_used_size); switch (result) { case EF_NO_ERR: { EF_INFO("Erased ENV OK.\n"); break; } case EF_ERASE_ERR: { EF_INFO("Warning: Erased ENV fault! Start address is 0x%08X, size is %ld.\n", get_cur_using_data_addr(), env_used_size); EF_INFO("Moving ENV to next available position.\n"); /* Calculate move offset address. * Current strategy is optimistic. It will offset the flash erasure minimum size. */ move_offset_addr = EF_ERASE_MIN_SIZE; /* calculate and set next available data section address */ set_cur_using_data_addr(get_cur_using_data_addr() + move_offset_addr); /* calculate and set next available ENV detail part end address */ set_env_detail_end_addr(get_env_detail_end_addr() + move_offset_addr); continue; } } /* write ENV to flash */ result = ef_port_write(get_cur_using_data_addr(), env_cache, env_used_size); switch (result) { case EF_NO_ERR: { EF_INFO("Saved ENV OK.\n"); break; } case EF_WRITE_ERR: { EF_INFO("Warning: Saved ENV fault! Start address is 0x%08X, size is %ld.\n", get_cur_using_data_addr(), env_used_size); EF_INFO("Moving ENV to next available position.\n"); /* Calculate move offset address. * Current strategy is optimistic. It will offset the flash erasure minimum size. */ move_offset_addr = EF_ERASE_MIN_SIZE; /* calculate and set next available data section address */ set_cur_using_data_addr(get_cur_using_data_addr() + move_offset_addr); /* calculate and set next available ENV detail part end address */ set_env_detail_end_addr(get_env_detail_end_addr() + move_offset_addr); continue; } } /* save ENV success */ if (result == EF_NO_ERR) { break; } } if (get_cur_using_data_addr() + env_used_size < data_sec_end_addr) { /* current using data section address has changed, save it */ if (get_cur_using_data_addr() != cur_using_addr_bak) { result = save_cur_using_data_addr(get_cur_using_data_addr()); } } else { result = EF_ENV_FULL; EF_INFO("Error: The flash has no available space to save ENV.\n"); } env_cache_changed = false; return result; } /** * Calculate the cached ENV CRC32 value. * * @return CRC32 value */ static uint32_t calc_env_crc(void) { uint32_t crc32 = 0; /* Calculate the ENV end address and all ENV data CRC32. * The 4 is ENV end address bytes size. */ crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_PART_INDEX_END_ADDR], 4); crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_PART_WORD_SIZE], get_env_detail_size()); EF_DEBUG("Calculate ENV CRC32 number is 0x%08X.\n", crc32); return crc32; } /** * Check the ENV CRC32 * * @return true is ok */ static bool env_crc_is_ok(void) { if (calc_env_crc() == env_cache[ENV_PARAM_PART_INDEX_DATA_CRC]) { EF_DEBUG("Verify ENV CRC32 result is OK.\n"); return true; } else { return false; } } /** * Save current using data section address to flash. * * @param cur_data_addr current using data section address * * @return result */ #ifndef EF_ENV_USING_PFS_MODE static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr) { EfErrCode result = EF_NO_ERR; /* erase ENV system section */ result = ef_port_erase(get_env_start_addr(), 4); if (result == EF_NO_ERR) { /* write current using data section address to flash */ result = ef_port_write(get_env_start_addr(), &cur_data_addr, 4); if (result == EF_WRITE_ERR) { EF_INFO("Error: Write system section fault! Start address is 0x%08X, size is %ld.\n", get_env_start_addr(), 4); EF_INFO("Note: The ENV can not be used.\n"); } } else { EF_INFO("Error: Erased system section fault! Start address is 0x%08X, size is %ld.\n", get_env_start_addr(), 4); EF_INFO("Note: The ENV can not be used\n"); } return result; } #else static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr) { EfErrCode result = EF_NO_ERR; uint32_t cur_system_sec_addr; if (cur_data_addr < get_env_start_addr() + ENV_AREA_SIZE / 2) { /* current using system section is in ENV area0 */ cur_system_sec_addr = get_env_start_addr(); } else { /* current using system section is in ENV area1 */ cur_system_sec_addr = get_env_start_addr() + ENV_AREA_SIZE / 2; } /* erase ENV system section */ result = ef_port_erase(cur_system_sec_addr, 4); if (result == EF_NO_ERR) { /* write area0 and area1 current using data section address to flash */ result = ef_port_write(cur_system_sec_addr, &cur_data_addr, 4); if (result == EF_WRITE_ERR) { EF_INFO("Error: Write system section fault! Start address is 0x%08X, size is %ld.\n", cur_system_sec_addr, 4); EF_INFO("Note: The ENV can not be used.\n"); } } else { EF_INFO("Error: Erased system section fault! Start address is 0x%08X, size is %ld.\n", cur_system_sec_addr, 4); EF_INFO("Note: The ENV can not be used\n"); } return result; } #endif /** * Set and save an ENV. If set ENV is success then will save it. * * @param key ENV name * @param value ENV value * * @return result */ EfErrCode ef_set_and_save_env(const char *key, const char *value) { EfErrCode result = EF_NO_ERR; result = ef_set_env(key, value); if (result == EF_NO_ERR) { result = ef_save_env(); } return result; } /** * Del and save an ENV. If del ENV is success then will save it. * * @param key ENV name * * @return result */ EfErrCode ef_del_and_save_env(const char *key) { EfErrCode result = EF_NO_ERR; result = ef_del_env(key); if (result == EF_NO_ERR) { result = ef_save_env(); } return result; } #ifdef EF_ENV_AUTO_UPDATE /** * Auto update ENV to latest default when current EF_ENV_VER is changed. * * @return result */ static EfErrCode env_auto_update(void) { size_t i; /* lock the ENV cache */ ef_port_env_lock(); /* read ENV version number from flash*/ ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_VER_NUM * 4, &env_cache[ENV_PARAM_INDEX_VER_NUM] , 4); /* check version number */ if (env_cache[ENV_PARAM_INDEX_VER_NUM] != EF_ENV_VER_NUM) { env_cache_changed = true; /* update version number */ env_cache[ENV_PARAM_INDEX_VER_NUM] = EF_ENV_VER_NUM; /* add a new ENV when it's not found */ for (i = 0; i < default_env_set_size; i++) { if (find_env(default_env_set[i].key) == NULL) { create_env(default_env_set[i].key, default_env_set[i].value); } } } /* unlock the ENV cache */ ef_port_env_unlock(); return ef_save_env(); } #endif /* EF_ENV_AUTO_UPDATE */ #endif /* EF_ENV_USING_WL_MODE */ #endif /* EF_USING_ENV */