• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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