1 /*
2 * Copyright (c) 2015-2018, Armink, <armink.ztl@gmail.com>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * 'Software'), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 * Function: Environment variables operating interface. (wear leveling mode)
24 * Created on: 2015-02-11
25 */
26
27 #include <easyflash.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #ifdef EF_USING_ENV
32
33 #ifdef EF_ENV_USING_WL_MODE
34
35 /**
36 * ENV area has 2 sections
37 * 1. System section
38 * Storage ENV current using data section address.
39 * Units: Word. Total size: @see EF_ERASE_MIN_SIZE.
40 * 2. Data section
41 * The data section storage ENV's parameters and detail.
42 * When an exception has occurred on flash erase or write. The current using data section
43 * address will move to next available position. This position depends on EF_ERASE_MIN_SIZE.
44 * 2.1 ENV parameters part
45 * It storage ENV's parameters.
46 * 2.2 ENV detail part
47 * It storage all ENV. Storage format is key=value\0.
48 * All ENV must be 4 bytes alignment. The remaining part must fill '\0'.
49 *
50 * @note Word = 4 Bytes in this file
51 * @note It will has two ENV areas(Area0, Area1) in data section when used power fail safeguard mode.
52 */
53
54 /* flash ENV parameters part index and size */
55 enum {
56 /* data section ENV detail part end address index */
57 ENV_PARAM_PART_INDEX_END_ADDR = 0,
58
59 #ifdef EF_ENV_USING_PFS_MODE
60 /* saved count for ENV area */
61 ENV_PARAM_PART_INDEX_SAVED_COUNT,
62 #endif
63
64 /* data section CRC32 code index */
65 ENV_PARAM_PART_INDEX_DATA_CRC,
66 /* ENV parameters part word size */
67 ENV_PARAM_PART_WORD_SIZE,
68 /* ENV parameters part byte size */
69 ENV_PARAM_PART_BYTE_SIZE = ENV_PARAM_PART_WORD_SIZE * 4,
70 };
71
72 /* default ENV set, must be initialized by user */
73 static ef_env const *default_env_set;
74 /* default ENV set size, must be initialized by user */
75 static size_t default_env_set_size = 0;
76 /* flash ENV data section size */
77 static size_t env_data_section_size = 0;
78 /* ENV ram cache */
79 static uint32_t env_cache[ENV_USER_SETTING_SIZE / 4] = { 0 };
80 /* ENV start address in flash */
81 static uint32_t env_start_addr = 0;
82 /* current using data section address */
83 static uint32_t cur_using_data_addr = 0;
84 /* ENV ram cache has changed when ENV created, deleted and changed value. */
85 static bool env_cache_changed = false;
86 /* initialize OK flag */
87 static bool init_ok = false;
88
89 #ifdef EF_ENV_USING_PFS_MODE
90 /* next save ENV area address */
91 static uint32_t next_save_area_addr = 0;
92 #endif
93
94 static uint32_t get_env_start_addr(void);
95 static uint32_t get_cur_using_data_addr(void);
96 static uint32_t get_env_detail_addr(void);
97 static uint32_t get_env_detail_end_addr(void);
98 static void set_cur_using_data_addr(uint32_t using_data_addr);
99 static void set_env_detail_end_addr(uint32_t end_addr);
100 static EfErrCode write_env(const char *key, const char *value);
101 static char *find_env(const char *key);
102 static size_t get_env_detail_size(void);
103 static size_t get_env_user_used_size(void);
104 static EfErrCode create_env(const char *key, const char *value);
105 static EfErrCode del_env(const char *key);
106 static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr);
107 static uint32_t calc_env_crc(void);
108 static bool env_crc_is_ok(void);
109
110 /**
111 * Flash ENV initialize.
112 *
113 * @param default_env default ENV set for user
114 * @param default_env_size default ENV set size
115 *
116 * @return result
117 */
ef_env_init(ef_env const * default_env,size_t default_env_size)118 EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size)
119 {
120 EfErrCode result = EF_NO_ERR;
121
122 EF_ASSERT(ENV_AREA_SIZE);
123 EF_ASSERT(ENV_USER_SETTING_SIZE);
124 /* must be word alignment for ENV */
125 EF_ASSERT(ENV_USER_SETTING_SIZE % 4 == 0);
126 EF_ASSERT(ENV_AREA_SIZE % 4 == 0);
127 EF_ASSERT(default_env);
128 EF_ASSERT(default_env_size < ENV_USER_SETTING_SIZE);
129
130 #ifndef EF_ENV_USING_PFS_MODE
131 /* system section size is erase_min_size, so last part is data section */
132 env_data_section_size = ENV_AREA_SIZE - EF_ERASE_MIN_SIZE;
133 #else
134 /* system section size is erase_min_size, so last part is data section */
135 env_data_section_size = ENV_AREA_SIZE / 2 - EF_ERASE_MIN_SIZE;
136 EF_ASSERT((ENV_AREA_SIZE / EF_ERASE_MIN_SIZE) % 2 == 0);
137 #endif
138 EF_ASSERT(env_data_section_size >= ENV_USER_SETTING_SIZE);
139 /* the ENV data section size should be an integral multiple of erase minimum size. */
140 EF_ASSERT(env_data_section_size % EF_ERASE_MIN_SIZE == 0);
141
142
143 env_start_addr = EF_START_ADDR;
144 default_env_set = default_env;
145 default_env_set_size = default_env_size;
146
147 EF_DEBUG("ENV start address is 0x%08X, size is %d bytes.\n", EF_START_ADDR, ENV_AREA_SIZE);
148
149 result = ef_load_env();
150
151 if (result == EF_NO_ERR)
152 init_ok = true;
153
154 return result;
155 }
156
157 /**
158 * ENV set default.
159 *
160 * @return result
161 */
ef_env_set_default(void)162 EfErrCode ef_env_set_default(void)
163 {
164 EfErrCode result = EF_NO_ERR;
165 size_t i;
166
167 EF_ASSERT(default_env_set);
168 EF_ASSERT(default_env_set_size);
169
170 /* lock the ENV cache */
171 ef_port_env_lock();
172
173 /* set ENV detail part end address is at ENV detail part start address */
174 set_env_detail_end_addr(get_env_detail_addr());
175
176 #ifdef EF_ENV_USING_PFS_MODE
177 /* set saved count to default 0 */
178 env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT] = 0;
179 #endif
180
181 /* create default ENV */
182 for (i = 0; i < default_env_set_size; i++)
183 create_env(default_env_set[i].key, default_env_set[i].value);
184
185 /* unlock the ENV cache */
186 ef_port_env_unlock();
187
188 result = ef_save_env();
189
190 #ifdef EF_ENV_USING_PFS_MODE
191 /* reset other PFS area's data */
192 if (result == EF_NO_ERR) {
193 env_cache_changed = true;
194 result = ef_save_env();
195 }
196 #endif
197
198 return result;
199 }
200
201 /**
202 * Get ENV start address in flash.
203 *
204 * @return ENV start address in flash
205 */
get_env_start_addr(void)206 static uint32_t get_env_start_addr(void)
207 {
208 return env_start_addr;
209 }
210 /**
211 * Get current using data section address.
212 *
213 * @return current using data section address
214 */
get_cur_using_data_addr(void)215 static uint32_t get_cur_using_data_addr(void)
216 {
217 return cur_using_data_addr;
218 }
219
220 /**
221 * Set current using data section address.
222 *
223 * @param using_data_addr current using data section address
224 */
set_cur_using_data_addr(uint32_t using_data_addr)225 static void set_cur_using_data_addr(uint32_t using_data_addr)
226 {
227 cur_using_data_addr = using_data_addr;
228 }
229
230 /**
231 * Get ENV detail part start address.
232 *
233 * @return detail part start address
234 */
get_env_detail_addr(void)235 static uint32_t get_env_detail_addr(void)
236 {
237 return get_cur_using_data_addr() + ENV_PARAM_PART_BYTE_SIZE;
238 }
239
240 /**
241 * Get ENV detail part end address.
242 * It's the first word in ENV.
243 *
244 * @return ENV end address
245 */
get_env_detail_end_addr(void)246 static uint32_t get_env_detail_end_addr(void)
247 {
248 /* it is the first word */
249 return env_cache[ENV_PARAM_PART_INDEX_END_ADDR];
250 }
251
252 /**
253 * Set ENV detail part end address.
254 * It's the first word in ENV.
255 *
256 * @param end_addr ENV end address
257 */
set_env_detail_end_addr(uint32_t end_addr)258 static void set_env_detail_end_addr(uint32_t end_addr)
259 {
260 env_cache[ENV_PARAM_PART_INDEX_END_ADDR] = end_addr;
261 }
262
263 /**
264 * Get current ENV detail part size.
265 *
266 * @return size
267 */
get_env_detail_size(void)268 static size_t get_env_detail_size(void)
269 {
270 if (get_env_detail_end_addr() > get_env_detail_addr())
271 return get_env_detail_end_addr() - get_env_detail_addr();
272 else
273 return 0;
274 }
275
276 /**
277 * Get current user used ENV size.
278 *
279 * @see ENV_USER_SETTING_SIZE
280 *
281 * @return size
282 */
283 /* must be initialized */
get_env_user_used_size(void)284 static size_t get_env_user_used_size(void)
285 {
286 if (get_env_detail_end_addr() > get_cur_using_data_addr())
287 return get_env_detail_end_addr() - get_cur_using_data_addr();
288 else
289 return 0;
290 }
291
292 /**
293 * Get current ENV already write bytes.
294 *
295 * @return write bytes
296 */
ef_get_env_write_bytes(void)297 size_t ef_get_env_write_bytes(void)
298 {
299 #ifndef EF_ENV_USING_PFS_MODE
300 return get_env_detail_end_addr() - get_env_start_addr();
301 #else
302 return EF_ERASE_MIN_SIZE + get_env_detail_end_addr() - get_cur_using_data_addr();
303 #endif
304 }
305
306 /**
307 * Write an ENV at the end of cache.
308 *
309 * @param key ENV name
310 * @param value ENV value
311 *
312 * @return result
313 */
write_env(const char * key,const char * value)314 static EfErrCode write_env(const char *key, const char *value)
315 {
316 EfErrCode result = EF_NO_ERR;
317 size_t ker_len = strlen(key), value_len = strlen(value), env_str_len;
318 char *env_cache_bak = (char *)env_cache;
319
320 /* calculate ENV storage length, contain '=' and '\0'. */
321 env_str_len = ker_len + value_len + 2;
322 if (env_str_len % 4 != 0)
323 env_str_len = (env_str_len / 4 + 1) * 4;
324 /* check capacity of ENV */
325 if (env_str_len + get_env_user_used_size() >= ENV_USER_SETTING_SIZE)
326 return EF_ENV_FULL;
327 /* calculate current ENV ram cache end address */
328 env_cache_bak += ENV_PARAM_PART_BYTE_SIZE + get_env_detail_size();
329 /* copy key name */
330 memcpy(env_cache_bak, key, ker_len);
331 env_cache_bak += ker_len;
332 /* copy equal sign */
333 *env_cache_bak = '=';
334 env_cache_bak++;
335 /* copy value */
336 memcpy(env_cache_bak, value, value_len);
337 env_cache_bak += value_len;
338 /* fill '\0' for string end sign */
339 *env_cache_bak = '\0';
340 env_cache_bak ++;
341 /* fill '\0' for word alignment */
342 memset(env_cache_bak, 0, env_str_len - (ker_len + value_len + 2));
343 set_env_detail_end_addr(get_env_detail_end_addr() + env_str_len);
344 /* ENV ram cache has changed */
345 env_cache_changed = true;
346
347 return result;
348 }
349
350 /**
351 * Find ENV.
352 *
353 * @param key ENV name
354 *
355 * @return found ENV in ram cache
356 */
find_env(const char * key)357 static char *find_env(const char *key)
358 {
359 char *env_start, *env_end, *env, *found_env = NULL;
360 size_t key_len = strlen(key), env_len;
361
362 if (*key == NULL) {
363 EF_INFO("Flash ENV name must be not empty!\n");
364 return NULL;
365 }
366
367 /* from data section start to data section end */
368 env_start = (char *)((char *) env_cache + ENV_PARAM_PART_BYTE_SIZE);
369 env_end = (char *)((char *) env_cache + ENV_PARAM_PART_BYTE_SIZE + get_env_detail_size());
370
371 /* ENV is null */
372 if (env_start == env_end)
373 return NULL;
374
375 env = env_start;
376 while (env < env_end) {
377 /* the key length must be equal */
378 if (!strncmp(env, key, key_len) && (env[key_len] == '=')) {
379 found_env = env;
380 break;
381 } else {
382 /* calculate ENV length, contain '\0'. */
383 env_len = strlen(env) + 1;
384 /* next ENV and word alignment */
385 if (env_len % 4 == 0)
386 env += env_len;
387 else
388 env += (env_len / 4 + 1) * 4;
389 }
390 }
391
392 return found_env;
393 }
394
395 /**
396 * If the ENV is not exist, create it.
397 * @see flash_write_env
398 *
399 * @param key ENV name
400 * @param value ENV value
401 *
402 * @return result
403 */
create_env(const char * key,const char * value)404 static EfErrCode create_env(const char *key, const char *value)
405 {
406 EfErrCode result = EF_NO_ERR;
407
408 EF_ASSERT(key);
409 EF_ASSERT(value);
410
411 if (*key == NULL) {
412 EF_INFO("Flash ENV name must be not empty!\n");
413 return EF_ENV_NAME_ERR;
414 }
415
416 if (strchr(key, '=')) {
417 EF_INFO("Flash ENV name can't contain '='.\n");
418 return EF_ENV_NAME_ERR;
419 }
420
421 /* find ENV */
422 if (find_env(key)) {
423 EF_INFO("The name of \"%s\" is already exist.\n", key);
424 return EF_ENV_NAME_EXIST;
425 }
426 /* write ENV at the end of cache */
427 result = write_env(key, value);
428
429 return result;
430 }
431
432 /**
433 * Delete an ENV in cache.
434 *
435 * @param key ENV name
436 *
437 * @return result
438 */
del_env(const char * key)439 static EfErrCode del_env(const char *key)
440 {
441 EfErrCode result = EF_NO_ERR;
442 char *del_env = NULL;
443 size_t del_env_length, remain_env_length;
444
445 EF_ASSERT(key);
446
447 if (*key == NULL) {
448 EF_INFO("Flash ENV name must be not NULL!\n");
449 return EF_ENV_NAME_ERR;
450 }
451
452 if (strchr(key, '=')) {
453 EF_INFO("Flash ENV name or value can't contain '='.\n");
454 return EF_ENV_NAME_ERR;
455 }
456
457 /* find ENV */
458 del_env = find_env(key);
459
460 if (!del_env) {
461 EF_INFO("Not find \"%s\" in ENV.\n", key);
462 return EF_ENV_NAME_ERR;
463 }
464 del_env_length = strlen(del_env);
465 /* '\0' also must be as ENV length */
466 del_env_length ++;
467 /* the address must multiple of 4 */
468 if (del_env_length % 4 != 0)
469 del_env_length = (del_env_length / 4 + 1) * 4;
470 /* calculate remain ENV length */
471 remain_env_length = get_env_detail_size()
472 - (((uint32_t) del_env + del_env_length) - ((uint32_t) env_cache + ENV_PARAM_PART_BYTE_SIZE));
473 /* remain ENV move forward */
474 memcpy(del_env, del_env + del_env_length, remain_env_length);
475 /* reset ENV end address */
476 set_env_detail_end_addr(get_env_detail_end_addr() - del_env_length);
477 /* ENV ram cache has changed */
478 env_cache_changed = true;
479
480 return result;
481 }
482
483 /**
484 * Set an ENV. If it value is empty, delete it.
485 * If not find it in ENV table, then create it.
486 *
487 * @param key ENV name
488 * @param value ENV value
489 *
490 * @return result
491 */
ef_set_env(const char * key,const char * value)492 EfErrCode ef_set_env(const char *key, const char *value)
493 {
494 EfErrCode result = EF_NO_ERR;
495 char *old_env, *old_value;
496
497 if (!init_ok) {
498 EF_INFO("ENV isn't initialize OK.\n");
499 return EF_ENV_INIT_FAILED;
500 }
501
502 /* lock the ENV cache */
503 ef_port_env_lock();
504
505 /* if ENV value is empty, delete it */
506 if (*value == NULL)
507 result = del_env(key);
508 else {
509 old_env = find_env(key);
510 /* If find this ENV, then compare the new value and old value. */
511 if (old_env) {
512 /* find the old value address */
513 old_env = strchr(old_env, '=');
514 old_value = old_env + 1;
515 /* If it is changed then delete it and recreate it */
516 if (strcmp(old_value, value)) {
517 result = del_env(key);
518 if (result == EF_NO_ERR)
519 result = create_env(key, value);
520 }
521 } else
522 result = create_env(key, value);
523 }
524 /* unlock the ENV cache */
525 ef_port_env_unlock();
526
527 return result;
528 }
529
530 /**
531 * Get an ENV value by key name.
532 *
533 * @param key ENV name
534 *
535 * @return value
536 */
ef_get_env(const char * key)537 char *ef_get_env(const char *key)
538 {
539 char *env = NULL, *value = NULL;
540
541 if (!init_ok) {
542 EF_INFO("ENV isn't initialize OK.\n");
543 return NULL;
544 }
545
546 /* find ENV */
547 env = find_env(key);
548
549 if (env == NULL)
550 return NULL;
551 /* get value address */
552 value = strchr(env, '=');
553 if (value != NULL) {
554 /* the equal sign next character is value */
555 value++;
556 }
557 return value;
558 }
559 /**
560 * Print ENV.
561 */
ef_print_env(void)562 void ef_print_env(void)
563 {
564 uint32_t *env_cache_detail_addr = env_cache + ENV_PARAM_PART_WORD_SIZE, *env_cache_end_addr =
565 (uint32_t *)(env_cache + ENV_PARAM_PART_WORD_SIZE + get_env_detail_size() / 4);
566 uint8_t j;
567 char c;
568
569 if (!init_ok) {
570 EF_INFO("ENV isn't initialize OK.\n");
571 return;
572 }
573
574 for (; env_cache_detail_addr < env_cache_end_addr; env_cache_detail_addr += 1) {
575 for (j = 0; j < 4; j++) {
576 c = (*env_cache_detail_addr) >> (8 * j);
577 ef_print("%c", c);
578 if (c == NULL) {
579 ef_print("\n");
580 break;
581 }
582 }
583 }
584
585 #ifndef EF_ENV_USING_PFS_MODE
586 ef_print("\nENV size: %ld/%ld bytes, write bytes %ld/%ld, mode: wear leveling.\n",
587 get_env_user_used_size(), ENV_USER_SETTING_SIZE, ef_get_env_write_bytes(),
588 ENV_AREA_SIZE);
589 #else
590 ef_print("\nENV size: %ld/%ld bytes, write bytes %ld/%ld, saved count: %ld, mode: wear leveling and power fail safeguard.\n",
591 get_env_user_used_size(), ENV_USER_SETTING_SIZE, ef_get_env_write_bytes(),
592 ENV_AREA_SIZE / 2, env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT]);
593 #endif
594 }
595
596 /**
597 * Load flash ENV to ram.
598 *
599 * @return result
600 */
601 #ifndef EF_ENV_USING_PFS_MODE
ef_load_env(void)602 EfErrCode ef_load_env(void)
603 {
604 EfErrCode result = EF_NO_ERR;
605 uint32_t *env_cache_bak, env_end_addr, using_data_addr;
606
607 /* read current using data section address */
608 ef_port_read(get_env_start_addr(), &using_data_addr, 4);
609 /* if ENV is not initialize or flash has dirty data, set default for it */
610 if ((using_data_addr == 0xFFFFFFFF)
611 || (using_data_addr > get_env_start_addr() + ENV_AREA_SIZE)
612 || (using_data_addr < get_env_start_addr() + EF_ERASE_MIN_SIZE)) {
613 /* initialize current using data section address */
614 set_cur_using_data_addr(get_env_start_addr() + EF_ERASE_MIN_SIZE);
615 /* save current using data section address to flash*/
616 if ((result = save_cur_using_data_addr(get_cur_using_data_addr())) == EF_NO_ERR) {
617 /* set default ENV */
618 result = ef_env_set_default();
619 }
620 } else {
621 /* set current using data section address */
622 set_cur_using_data_addr(using_data_addr);
623 /* read ENV detail part end address from flash */
624 ef_port_read(get_cur_using_data_addr() + ENV_PARAM_PART_INDEX_END_ADDR * 4, &env_end_addr, 4);
625 /* if ENV end address has error, set default for ENV */
626 if (env_end_addr > get_env_start_addr() + ENV_AREA_SIZE) {
627 /* initialize current using data section address */
628 set_cur_using_data_addr(get_env_start_addr() + EF_ERASE_MIN_SIZE);
629 /* save current using data section address to flash*/
630 if ((result = save_cur_using_data_addr(get_cur_using_data_addr())) == EF_NO_ERR) {
631 EF_INFO("Warning: ENV end address has error. Set it to default.\n");
632 result = ef_env_set_default();
633 }
634 } else {
635 /* set ENV detail part end address */
636 set_env_detail_end_addr(env_end_addr);
637
638 env_cache_bak = env_cache + ENV_PARAM_PART_WORD_SIZE;
639 /* read all ENV from flash */
640 ef_port_read(get_env_detail_addr(), env_cache_bak, get_env_detail_size());
641 /* read ENV CRC code from flash */
642 ef_port_read(get_cur_using_data_addr() + ENV_PARAM_PART_INDEX_DATA_CRC * 4,
643 &env_cache[ENV_PARAM_PART_INDEX_DATA_CRC], 4);
644 /* if ENV CRC32 check is fault, set default for it */
645 if (!env_crc_is_ok()) {
646 EF_INFO("Warning: ENV CRC check failed. Set it to default.\n");
647 result = ef_env_set_default();
648 }
649 }
650
651 }
652 return result;
653 }
654 #else
ef_load_env(void)655 EfErrCode ef_load_env(void)
656 {
657 EfErrCode result = EF_NO_ERR;
658 /* ENV area0 current using address default value */
659 uint32_t area0_default_cur_using_addr = get_env_start_addr() + EF_ERASE_MIN_SIZE;
660 /* ENV area1 current using address default value */
661 uint32_t area1_default_cur_using_addr = area0_default_cur_using_addr + ENV_AREA_SIZE / 2;
662 uint32_t area0_cur_using_addr, area1_cur_using_addr, area0_end_addr, area1_end_addr;
663 uint32_t area0_crc, area1_crc, area0_saved_count, area1_saved_count;
664 bool area0_is_valid = true, area1_is_valid = true;
665
666 /* read ENV area0 and area1 current using data section address */
667 ef_port_read(get_env_start_addr(), &area0_cur_using_addr, 4);
668 ef_port_read(get_env_start_addr() + ENV_AREA_SIZE / 2, &area1_cur_using_addr, 4);
669 /* if ENV is not initialize or flash has dirty data, set it isn't valid */
670 if ((area0_cur_using_addr == 0xFFFFFFFF)
671 || (area0_cur_using_addr > get_env_start_addr() + ENV_AREA_SIZE / 2)
672 || (area0_cur_using_addr < get_env_start_addr() + EF_ERASE_MIN_SIZE))
673 area0_is_valid = false;
674 if ((area1_cur_using_addr == 0xFFFFFFFF)
675 || (area1_cur_using_addr > get_env_start_addr() + ENV_AREA_SIZE)
676 || (area1_cur_using_addr < get_env_start_addr() + ENV_AREA_SIZE / 2 + EF_ERASE_MIN_SIZE))
677 area1_is_valid = false;
678 /* check area0 end address when it is valid */
679 if (area0_is_valid) {
680 /* read ENV area end address from flash */
681 ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_END_ADDR * 4, &area0_end_addr, 4);
682 if ((area0_end_addr == 0xFFFFFFFF) || (area0_end_addr < area0_cur_using_addr)
683 || (area0_end_addr > area0_cur_using_addr + ENV_USER_SETTING_SIZE))
684 area0_is_valid = false;
685 }
686 /* check area1 end address when it is valid */
687 if (area1_is_valid) {
688 /* read ENV area end address from flash */
689 ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_END_ADDR * 4, &area1_end_addr, 4);
690 if ((area1_end_addr == 0xFFFFFFFF) || (area1_end_addr < area1_cur_using_addr)
691 || (area1_end_addr > area1_cur_using_addr + ENV_USER_SETTING_SIZE))
692 area1_is_valid = false;
693 }
694 /* check area0 CRC when it is valid */
695 if (area0_is_valid) {
696 /* read ENV area0 crc32 code from flash */
697 ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_DATA_CRC * 4, &area0_crc, 4);
698 /* read ENV from ENV area0 */
699 ef_port_read(area0_cur_using_addr, env_cache, area0_end_addr - area0_cur_using_addr);
700 /* current using data section address is area0 current using data section address */
701 set_cur_using_data_addr(area0_cur_using_addr);
702 if (!env_crc_is_ok())
703 area0_is_valid = false;
704 }
705 /* check area1 CRC when it is valid */
706 if (area1_is_valid) {
707 /* read ENV area1 crc32 code from flash */
708 ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_DATA_CRC * 4, &area1_crc, 4);
709 /* read ENV from ENV area1 */
710 ef_port_read(area1_cur_using_addr, env_cache, area1_end_addr - area1_cur_using_addr);
711 /* current using data section address is area1 current using data section address */
712 set_cur_using_data_addr(area1_cur_using_addr);
713 if (!env_crc_is_ok())
714 area1_is_valid = false;
715 }
716 /* all ENV area CRC is OK then compare saved count */
717 if (area0_is_valid && area1_is_valid) {
718 /* read ENV area saved count from flash */
719 ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_SAVED_COUNT * 4,
720 &area0_saved_count, 4);
721 ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_SAVED_COUNT * 4,
722 &area1_saved_count, 4);
723 /* the bigger saved count area is valid */
724 if ((area0_saved_count > area1_saved_count) || ((area0_saved_count == 0) && (area1_saved_count == 0xFFFFFFFF)))
725 area1_is_valid = false;
726 else
727 area0_is_valid = false;
728 }
729 if (area0_is_valid) {
730 /* current using data section address is area0 current using data section address */
731 set_cur_using_data_addr(area0_cur_using_addr);
732 /* next save ENV area address is area1 current using address value */
733 next_save_area_addr = area1_cur_using_addr;
734 /* read all ENV from area0 */
735 ef_port_read(area0_cur_using_addr, env_cache, area0_end_addr - area0_cur_using_addr);
736 } else if (area1_is_valid) {
737 /* already read data section and set_cur_using_data_addr above current code,
738 * so just set next save ENV area address is area0 current using address value */
739 next_save_area_addr = area0_cur_using_addr;
740 } else {
741 /* current using data section address is area1 current using address default value */
742 set_cur_using_data_addr(area1_default_cur_using_addr);
743 /* next save ENV area address default is area0 current using address default value */
744 next_save_area_addr = area0_default_cur_using_addr;
745 /* save current using data section address to flash*/
746 if (((result = save_cur_using_data_addr(area0_default_cur_using_addr)) == EF_NO_ERR)
747 && ((result = save_cur_using_data_addr(area1_default_cur_using_addr)) == EF_NO_ERR)) {
748 /* set the ENV to default */
749 result = ef_env_set_default();
750 }
751 }
752 return result;
753 }
754 #endif
755
756 /**
757 * Save ENV to flash.
758 */
ef_save_env(void)759 EfErrCode ef_save_env(void)
760 {
761 EfErrCode result = EF_NO_ERR;
762 uint32_t cur_using_addr_bak, move_offset_addr;
763 size_t env_used_size = get_env_user_used_size();
764 uint32_t data_sec_end_addr;
765
766 /* ENV ram cache has not changed don't need to save */
767 if (!env_cache_changed)
768 return result;
769
770 #ifndef EF_ENV_USING_PFS_MODE
771 data_sec_end_addr = get_env_start_addr() + ENV_AREA_SIZE - 4;
772 cur_using_addr_bak = get_cur_using_data_addr();
773 #else
774 cur_using_addr_bak = next_save_area_addr;
775 /* replace next_save_area_addr with cur_using_data_addr */
776 next_save_area_addr = get_cur_using_data_addr();
777 set_cur_using_data_addr(cur_using_addr_bak);
778 /* change the ENV detail end address to next save area address */
779 set_env_detail_end_addr(get_cur_using_data_addr() + env_used_size);
780 /* area0 or area1 */
781 if (get_cur_using_data_addr() < get_env_start_addr() + ENV_AREA_SIZE / 2)
782 data_sec_end_addr = get_env_start_addr() + ENV_AREA_SIZE / 2 - 4;
783 else
784 data_sec_end_addr = get_env_start_addr() + ENV_AREA_SIZE - 4;
785 /* ENV area saved count +1 */
786 env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT]++;
787 #endif
788
789 /* wear leveling process, automatic move ENV to next available position */
790 while (get_cur_using_data_addr() + env_used_size < data_sec_end_addr) {
791 /* calculate and cache CRC32 code */
792 env_cache[ENV_PARAM_PART_INDEX_DATA_CRC] = calc_env_crc();
793 /* erase ENV */
794 result = ef_port_erase(get_cur_using_data_addr(), env_used_size);
795 switch (result) {
796 case EF_NO_ERR: {
797 EF_INFO("Erased ENV OK.\n");
798 break;
799 }
800 case EF_ERASE_ERR: {
801 EF_INFO("Warning: Erased ENV fault! Start address is 0x%08X, size is %ld.\n",
802 get_cur_using_data_addr(), env_used_size);
803 EF_INFO("Moving ENV to next available position.\n");
804 /* Calculate move offset address.
805 * Current strategy is optimistic. It will offset the flash erasure minimum size.
806 */
807 move_offset_addr = EF_ERASE_MIN_SIZE;
808 /* calculate and set next available data section address */
809 set_cur_using_data_addr(get_cur_using_data_addr() + move_offset_addr);
810 /* calculate and set next available ENV detail part end address */
811 set_env_detail_end_addr(get_env_detail_end_addr() + move_offset_addr);
812 continue;
813 }
814 }
815 /* write ENV to flash */
816 result = ef_port_write(get_cur_using_data_addr(), env_cache, env_used_size);
817 switch (result) {
818 case EF_NO_ERR: {
819 EF_INFO("Saved ENV OK.\n");
820 break;
821 }
822 case EF_WRITE_ERR: {
823 EF_INFO("Warning: Saved ENV fault! Start address is 0x%08X, size is %ld.\n",
824 get_cur_using_data_addr(), env_used_size);
825 EF_INFO("Moving ENV to next available position.\n");
826 /* Calculate move offset address.
827 * Current strategy is optimistic. It will offset the flash erasure minimum size.
828 */
829 move_offset_addr = EF_ERASE_MIN_SIZE;
830 /* calculate and set next available data section address */
831 set_cur_using_data_addr(get_cur_using_data_addr() + move_offset_addr);
832 /* calculate and set next available ENV detail part end address */
833 set_env_detail_end_addr(get_env_detail_end_addr() + move_offset_addr);
834 continue;
835 }
836 }
837 /* save ENV success */
838 if (result == EF_NO_ERR)
839 break;
840 }
841
842 if (get_cur_using_data_addr() + env_used_size < data_sec_end_addr) {
843 /* current using data section address has changed, save it */
844 if (get_cur_using_data_addr() != cur_using_addr_bak)
845 result = save_cur_using_data_addr(get_cur_using_data_addr());
846 } else {
847 result = EF_ENV_FULL;
848 EF_INFO("Error: The flash has no available space to save ENV.\n");
849 }
850
851 env_cache_changed = false;
852
853 return result;
854 }
855
856 /**
857 * Calculate the cached ENV CRC32 value.
858 *
859 * @return CRC32 value
860 */
calc_env_crc(void)861 static uint32_t calc_env_crc(void)
862 {
863 uint32_t crc32 = 0;
864
865 /* Calculate the ENV end address and all ENV data CRC32.
866 * The 4 is ENV end address bytes size. */
867 crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_PART_INDEX_END_ADDR], 4);
868 crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_PART_WORD_SIZE], get_env_detail_size());
869 EF_DEBUG("Calculate ENV CRC32 number is 0x%08X.\n", crc32);
870
871 return crc32;
872 }
873
874 /**
875 * Check the ENV CRC32
876 *
877 * @return true is ok
878 */
env_crc_is_ok(void)879 static bool env_crc_is_ok(void)
880 {
881 if (calc_env_crc() == env_cache[ENV_PARAM_PART_INDEX_DATA_CRC]) {
882 EF_DEBUG("Verify ENV CRC32 result is OK.\n");
883 return true;
884 } else
885 return false;
886 }
887
888 /**
889 * Save current using data section address to flash.
890 *
891 * @param cur_data_addr current using data section address
892 *
893 * @return result
894 */
895 #ifndef EF_ENV_USING_PFS_MODE
save_cur_using_data_addr(uint32_t cur_data_addr)896 static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr)
897 {
898 EfErrCode result = EF_NO_ERR;
899
900 /* erase ENV system section */
901 result = ef_port_erase(get_env_start_addr(), 4);
902 if (result == EF_NO_ERR) {
903 /* write current using data section address to flash */
904 result = ef_port_write(get_env_start_addr(), &cur_data_addr, 4);
905 if (result == EF_WRITE_ERR) {
906 EF_INFO("Error: Write system section fault! Start address is 0x%08X, size is %ld.\n",
907 get_env_start_addr(), 4);
908 EF_INFO("Note: The ENV can not be used.\n");
909 }
910 } else {
911 EF_INFO("Error: Erased system section fault! Start address is 0x%08X, size is %ld.\n",
912 get_env_start_addr(), 4);
913 EF_INFO("Note: The ENV can not be used\n");
914 }
915 return result;
916 }
917 #else
save_cur_using_data_addr(uint32_t cur_data_addr)918 static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr)
919 {
920 EfErrCode result = EF_NO_ERR;
921 uint32_t cur_system_sec_addr;
922
923 if (cur_data_addr < get_env_start_addr() + ENV_AREA_SIZE / 2) {
924 /* current using system section is in ENV area0 */
925 cur_system_sec_addr = get_env_start_addr();
926 } else {
927 /* current using system section is in ENV area1 */
928 cur_system_sec_addr = get_env_start_addr() + ENV_AREA_SIZE / 2;
929 }
930 /* erase ENV system section */
931 result = ef_port_erase(cur_system_sec_addr, 4);
932 if (result == EF_NO_ERR) {
933 /* write area0 and area1 current using data section address to flash */
934 result = ef_port_write(cur_system_sec_addr, &cur_data_addr, 4);
935 if (result == EF_WRITE_ERR) {
936 EF_INFO("Error: Write system section fault! Start address is 0x%08X, size is %ld.\n",
937 cur_system_sec_addr, 4);
938 EF_INFO("Note: The ENV can not be used.\n");
939 }
940 } else {
941 EF_INFO("Error: Erased system section fault! Start address is 0x%08X, size is %ld.\n",
942 cur_system_sec_addr, 4);
943 EF_INFO("Note: The ENV can not be used\n");
944 }
945 return result;
946 }
947 #endif
948
949 /**
950 * Set and save an ENV. If set ENV is success then will save it.
951 *
952 * @param key ENV name
953 * @param value ENV value
954 *
955 * @return result
956 */
ef_set_and_save_env(const char * key,const char * value)957 EfErrCode ef_set_and_save_env(const char *key, const char *value)
958 {
959 EfErrCode result = EF_NO_ERR;
960
961 result = ef_set_env(key, value);
962
963 if (result == EF_NO_ERR)
964 result = ef_save_env();
965
966 return result;
967 }
968
969 #endif /* EF_ENV_USING_WL_MODE */
970
971 #endif /* EF_USING_ENV */
972