• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2014-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. (normal mode)
24  * Created on: 2014-10-06
25  */
26 
27 #include <easyflash.h>
28 #include <string.h>
29 #include <stdlib.h>
30 
31 #ifdef EF_USING_ENV
32 
33 #ifndef EF_ENV_USING_WL_MODE
34 
35 /**
36  * ENV area has 2 sections
37  * 1. System section
38  *    It storage ENV parameters. (Units: Word)
39  * 2. Data section
40  *    It storage all ENV. Storage format is key=value\0.
41  *    All ENV must be 4 bytes alignment. The remaining part must fill '\0'.
42  *
43  * @note Word = 4 Bytes in this file
44  * @note It will has two ENV areas(Area0, Area1) when used power fail safeguard mode.
45  */
46 
47 /* flash ENV parameters index and size in system section */
48 enum {
49 	/* data section ENV end address index in system section */
50 	ENV_PARAM_INDEX_END_ADDR = 0,
51 
52 #ifdef EF_ENV_USING_PFS_MODE
53 	/* saved count for ENV area */
54 	ENV_PARAM_INDEX_SAVED_COUNT,
55 #endif
56 
57 	/* data section CRC32 code index in system section */
58 	ENV_PARAM_INDEX_DATA_CRC,
59 	/* flash ENV parameters word size */
60 	ENV_PARAM_WORD_SIZE,
61 	/* flash ENV parameters byte size */
62 	ENV_PARAM_BYTE_SIZE = ENV_PARAM_WORD_SIZE * 4,
63 };
64 
65 /* default ENV set, must be initialized by user */
66 static ef_env const *default_env_set;
67 /* default ENV set size, must be initialized by user */
68 static size_t default_env_set_size = 0;
69 /* ENV ram cache */
70 static uint32_t env_cache[ENV_USER_SETTING_SIZE / 4] = { 0 };
71 /* ENV start address in flash */
72 static uint32_t env_start_addr = 0;
73 /* ENV ram cache has changed when ENV created, deleted and changed value. */
74 static bool env_cache_changed = false;
75 /* initialize OK flag */
76 static bool init_ok = false;
77 
78 #ifdef EF_ENV_USING_PFS_MODE
79 /* current load ENV area address */
80 static uint32_t cur_load_area_addr = 0;
81 /* next save ENV area address */
82 static uint32_t next_save_area_addr = 0;
83 #endif
84 
85 static uint32_t get_env_system_addr(void);
86 static uint32_t get_env_data_addr(void);
87 static uint32_t get_env_end_addr(void);
88 static void set_env_end_addr(uint32_t end_addr);
89 static EfErrCode write_env(const char *key, const char *value);
90 static char *find_env(const char *key);
91 static EfErrCode del_env(const char *key);
92 static size_t get_env_data_size(void);
93 static size_t get_env_user_used_size(void);
94 static EfErrCode create_env(const char *key, const char *value);
95 static uint32_t calc_env_crc(void);
96 static bool env_crc_is_ok(void);
97 
98 /**
99  * Flash ENV initialize.
100  *
101  * @param default_env default ENV set for user
102  * @param default_env_size default ENV set size
103  *
104  * @note user_size must equal with total_size in normal mode
105  *
106  * @return result
107  */
ef_env_init(ef_env const * default_env,size_t default_env_size)108 EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size)
109 {
110 	EfErrCode result = EF_NO_ERR;
111 
112 	EF_ASSERT(ENV_AREA_SIZE);
113 	EF_ASSERT(ENV_USER_SETTING_SIZE);
114 	EF_ASSERT(EF_ERASE_MIN_SIZE);
115 	/* must be word alignment for ENV */
116 	EF_ASSERT(ENV_USER_SETTING_SIZE % 4 == 0);
117 	EF_ASSERT(ENV_AREA_SIZE % 4 == 0);
118 	EF_ASSERT(default_env);
119 	EF_ASSERT(default_env_size < ENV_USER_SETTING_SIZE);
120 
121 #ifndef EF_ENV_USING_PFS_MODE
122 	/* total_size must be aligned with erase_min_size */
123 	if (ENV_USER_SETTING_SIZE % EF_ERASE_MIN_SIZE == 0)
124 		EF_ASSERT(ENV_USER_SETTING_SIZE == ENV_AREA_SIZE);
125 	else
126 		EF_ASSERT((ENV_USER_SETTING_SIZE / EF_ERASE_MIN_SIZE + 1)*EF_ERASE_MIN_SIZE == ENV_AREA_SIZE);
127 #else
128 	/* total_size must be aligned with erase_min_size */
129 	if (ENV_USER_SETTING_SIZE % EF_ERASE_MIN_SIZE == 0) {
130 		/* it has double area when used power fail safeguard mode */
131 		EF_ASSERT(2 * ENV_USER_SETTING_SIZE == ENV_AREA_SIZE);
132 	} else {
133 		/* it has double area when used power fail safeguard mode */
134 		EF_ASSERT(2 * (ENV_USER_SETTING_SIZE / EF_ERASE_MIN_SIZE + 1)*EF_ERASE_MIN_SIZE == ENV_AREA_SIZE);
135 	}
136 #endif
137 
138 	env_start_addr = EF_START_ADDR;
139 	default_env_set = default_env;
140 	default_env_set_size = default_env_size;
141 
142 	EF_DEBUG("ENV start address is 0x%08X, size is %d bytes.\n", EF_START_ADDR, ENV_AREA_SIZE);
143 
144 	result = ef_load_env();
145 
146 	if (result == EF_NO_ERR)
147 		init_ok = true;
148 
149 	return result;
150 }
151 
152 /**
153  * ENV set default.
154  *
155  * @return result
156  */
ef_env_set_default(void)157 EfErrCode ef_env_set_default(void)
158 {
159 	EfErrCode result = EF_NO_ERR;
160 	size_t i;
161 
162 	EF_ASSERT(default_env_set);
163 	EF_ASSERT(default_env_set_size);
164 
165 	/* lock the ENV cache */
166 	ef_port_env_lock();
167 
168 	/* set environment end address is at data section start address */
169 	set_env_end_addr(get_env_data_addr());
170 
171 #ifdef EF_ENV_USING_PFS_MODE
172 	/* set saved count to default 0 */
173 	env_cache[ENV_PARAM_INDEX_SAVED_COUNT] = 0;
174 #endif
175 
176 	/* create default ENV */
177 	for (i = 0; i < default_env_set_size; i++)
178 		create_env(default_env_set[i].key, default_env_set[i].value);
179 
180 	/* unlock the ENV cache */
181 	ef_port_env_unlock();
182 
183 	result = ef_save_env();
184 
185 #ifdef EF_ENV_USING_PFS_MODE
186 	/* reset other PFS area's data */
187 	if (result == EF_NO_ERR) {
188 		env_cache_changed = true;
189 		result = ef_save_env();
190 	}
191 #endif
192 
193 
194 	return result;
195 }
196 
197 /**
198  * Get ENV system section start address.
199  *
200  * @return system section start address
201  */
get_env_system_addr(void)202 static uint32_t get_env_system_addr(void)
203 {
204 #ifndef EF_ENV_USING_PFS_MODE
205 	return env_start_addr;
206 #else
207 	return cur_load_area_addr;
208 #endif
209 }
210 
211 /**
212  * Get ENV data section start address.
213  *
214  * @return data section start address
215  */
get_env_data_addr(void)216 static uint32_t get_env_data_addr(void)
217 {
218 	return get_env_system_addr() + ENV_PARAM_BYTE_SIZE;
219 }
220 
221 /**
222  * Get ENV end address.
223  * It's the first word in ENV.
224  *
225  * @return ENV end address
226  */
get_env_end_addr(void)227 static uint32_t get_env_end_addr(void)
228 {
229 	/* it is the first word */
230 	return env_cache[ENV_PARAM_INDEX_END_ADDR];
231 }
232 
233 /**
234  * Set ENV end address.
235  * It's the first word in ENV.
236  *
237  * @param end_addr ENV end address
238  */
set_env_end_addr(uint32_t end_addr)239 static void set_env_end_addr(uint32_t end_addr)
240 {
241 	env_cache[ENV_PARAM_INDEX_END_ADDR] = end_addr;
242 }
243 
244 /**
245  * Get current ENV data section size.
246  *
247  * @return size
248  */
get_env_data_size(void)249 static size_t get_env_data_size(void)
250 {
251 	if (get_env_end_addr() > get_env_data_addr())
252 		return get_env_end_addr() - get_env_data_addr();
253 	else
254 		return 0;
255 }
256 
257 /**
258  * Get current user used ENV size.
259  *
260  * @return bytes
261  */
get_env_user_used_size(void)262 static size_t get_env_user_used_size(void)
263 {
264 	if (get_env_end_addr() > get_env_system_addr())
265 		return get_env_end_addr() - get_env_system_addr();
266 	else
267 		return 0;
268 }
269 
270 /**
271  * Get current ENV already write bytes.
272  *
273  * @return write bytes
274  */
ef_get_env_write_bytes(void)275 size_t ef_get_env_write_bytes(void)
276 {
277 #ifndef EF_ENV_USING_PFS_MODE
278 	return get_env_user_used_size();
279 #else
280 	return get_env_user_used_size() * 2;
281 #endif
282 }
283 
284 /**
285  * Write an ENV at the end of cache.
286  *
287  * @param key ENV name
288  * @param value ENV value
289  *
290  * @return result
291  */
write_env(const char * key,const char * value)292 static EfErrCode write_env(const char *key, const char *value)
293 {
294 	EfErrCode result = EF_NO_ERR;
295 	size_t key_len = strlen(key), value_len = strlen(value), env_str_len;
296 	char *env_cache_bak = (char *)env_cache;
297 
298 	/* calculate ENV storage length, contain '=' and '\0'. */
299 	env_str_len = key_len + value_len + 2;
300 	if (env_str_len % 4 != 0)
301 		env_str_len = (env_str_len / 4 + 1) * 4;
302 	/* check capacity of ENV  */
303 	if (env_str_len + get_env_user_used_size() >= ENV_USER_SETTING_SIZE)
304 		return EF_ENV_FULL;
305 
306 	/* calculate current ENV ram cache end address */
307 	env_cache_bak += get_env_user_used_size();
308 
309 	/* copy key name */
310 	memmove(env_cache_bak, key, key_len);
311 	env_cache_bak += key_len;
312 	/* copy equal sign */
313 	*env_cache_bak = '=';
314 	env_cache_bak++;
315 	/* copy value */
316 	memmove(env_cache_bak, value, value_len);
317 	env_cache_bak += value_len;
318 	/* fill '\0' for string end sign */
319 	*env_cache_bak = '\0';
320 	env_cache_bak ++;
321 	/* fill '\0' for word alignment */
322 	memset(env_cache_bak, 0, env_str_len - (key_len + value_len + 2));
323 	set_env_end_addr(get_env_end_addr() + env_str_len);
324 	/* ENV ram cache has changed */
325 	env_cache_changed = true;
326 
327 	return result;
328 }
329 
330 /**
331  * Find ENV.
332  *
333  * @param key ENV name
334  *
335  * @return found ENV in ram cache
336  */
find_env(const char * key)337 static char *find_env(const char *key)
338 {
339 	char *env_start, *env_end, *env, *found_env = NULL;
340 	size_t key_len = strlen(key), env_len;
341 
342 	if ((key == NULL) || (*key == 0)) {
343 		EF_INFO("Flash ENV name must be not empty!\n");
344 		return NULL;
345 	}
346 
347 	/* from data section start to data section end */
348 	env_start = (char *)((char *) env_cache + ENV_PARAM_BYTE_SIZE);
349 	env_end = (char *)((char *) env_cache + get_env_user_used_size());
350 
351 	/* ENV is null */
352 	if (env_start == env_end)
353 		return NULL;
354 
355 	env = env_start;
356 	while (env < env_end) {
357 		/* the key length must be equal */
358 		if (!strncmp(env, key, key_len) && (env[key_len] == '=')) {
359 			found_env = env;
360 			break;
361 		} else {
362 			/* calculate ENV length, contain '\0'. */
363 			env_len = strlen(env) + 1;
364 			/* next ENV and word alignment */
365 			if (env_len % 4 == 0)
366 				env += env_len;
367 			else
368 				env += (env_len / 4 + 1) * 4;
369 		}
370 	}
371 	return found_env;
372 }
373 
374 /**
375  * If the ENV is not exist, create it.
376  * @see flash_write_env
377  *
378  * @param key ENV name
379  * @param value ENV value
380  *
381  * @return result
382  */
create_env(const char * key,const char * value)383 static EfErrCode create_env(const char *key, const char *value)
384 {
385 	EfErrCode result = EF_NO_ERR;
386 
387 	EF_ASSERT(key);
388 	EF_ASSERT(value);
389 
390 	if ((key == NULL) || (*key == 0)) {
391 		EF_INFO("Flash ENV name must be not empty!\n");
392 		return EF_ENV_NAME_ERR;
393 	}
394 
395 	if (strchr(key, '=')) {
396 		EF_INFO("Flash ENV name can't contain '='.\n");
397 		return EF_ENV_NAME_ERR;
398 	}
399 
400 	/* find ENV */
401 	if (find_env(key)) {
402 		EF_INFO("The name of \"%s\" is already exist.\n", key);
403 		return EF_ENV_NAME_EXIST;
404 	}
405 	/* write ENV at the end of cache */
406 	result = write_env(key, value);
407 
408 	return result;
409 }
410 
411 /**
412  * Delete an ENV in cache.
413  *
414  * @param key ENV name
415  *
416  * @return result
417  */
del_env(const char * key)418 static EfErrCode del_env(const char *key)
419 {
420 	EfErrCode result = EF_NO_ERR;
421 	char *del_env = NULL;
422 	size_t del_env_length, remain_env_length;
423 
424 	EF_ASSERT(key);
425 
426 	if ((key == NULL) || (*key == 0)) {
427 		EF_INFO("Flash ENV name must be not NULL!\n");
428 		return EF_ENV_NAME_ERR;
429 	}
430 
431 	if (strchr(key, '=')) {
432 		EF_INFO("Flash ENV name or value can't contain '='.\n");
433 		return EF_ENV_NAME_ERR;
434 	}
435 
436 	/* find ENV */
437 	del_env = find_env(key);
438 
439 	if (!del_env) {
440 		EF_INFO("Not find \"%s\" in ENV.\n", key);
441 		return EF_ENV_NAME_ERR;
442 	}
443 	del_env_length = strlen(del_env);
444 	/* '\0' also must be as ENV length */
445 	del_env_length ++;
446 	/* the address must multiple of 4 */
447 	if (del_env_length % 4 != 0)
448 		del_env_length = (del_env_length / 4 + 1) * 4;
449 	/* calculate remain ENV length */
450 	remain_env_length = get_env_data_size()
451 						- (((uint32_t) del_env + del_env_length) - ((uint32_t) env_cache + ENV_PARAM_BYTE_SIZE));
452 	/* remain ENV move forward */
453 	memmove(del_env, del_env + del_env_length, remain_env_length);
454 	/* reset ENV end address */
455 	set_env_end_addr(get_env_end_addr() - del_env_length);
456 	/* ENV ram cache has changed */
457 	env_cache_changed = true;
458 
459 	return result;
460 }
461 
462 /**
463  * Set an ENV. If it value is empty, delete it.
464  * If not find it in ENV table, then create it.
465  *
466  * @param key ENV name
467  * @param value ENV value
468  *
469  * @return result
470  */
ef_set_env(const char * key,const char * value)471 EfErrCode ef_set_env(const char *key, const char *value)
472 {
473 	EfErrCode result = EF_NO_ERR;
474 	char *old_env, *old_value;
475 
476 	if (!init_ok) {
477 		EF_INFO("ENV isn't initialize OK.\n");
478 		return EF_ENV_INIT_FAILED;
479 	}
480 
481 	/* lock the ENV cache */
482 	ef_port_env_lock();
483 
484 	/* if ENV value is empty, delete it */
485 	if ((value == NULL) || (*value == 0))
486 		result = del_env(key);
487 	else {
488 		old_env = find_env(key);
489 		/* If find this ENV, then compare the new value and old value. */
490 		if (old_env) {
491 			/* find the old value address */
492 			old_env = strchr(old_env, '=');
493 			old_value = old_env + 1;
494 			/* If it is changed then delete it and recreate it  */
495 			if (strcmp(old_value, value)) {
496 				result = del_env(key);
497 				if (result == EF_NO_ERR)
498 					result = create_env(key, value);
499 			}
500 		} else
501 			result = create_env(key, value);
502 	}
503 	/* unlock the ENV cache */
504 	ef_port_env_unlock();
505 
506 	return result;
507 }
508 
509 /**
510  * Get an ENV value by key name.
511  *
512  * @param key ENV name
513  *
514  * @return value
515  */
ef_get_env(const char * key)516 char *ef_get_env(const char *key)
517 {
518 	char *env = NULL, *value = NULL;
519 
520 	if (!init_ok) {
521 		EF_INFO("ENV isn't initialize OK.\n");
522 		return NULL;
523 	}
524 
525 	/* find ENV */
526 	env = find_env(key);
527 
528 	if (env == NULL)
529 		return NULL;
530 	/* get value address */
531 	value = strchr(env, '=');
532 	if (value != NULL) {
533 		/* the equal sign next character is value */
534 		value++;
535 	}
536 	return value;
537 }
538 /**
539  * Print ENV.
540  */
ef_print_env(void)541 void ef_print_env(void)
542 {
543 	uint32_t *env_cache_data_addr = env_cache + ENV_PARAM_WORD_SIZE,
544 			  *env_cache_end_addr =
545 				  (uint32_t *)(env_cache + ENV_PARAM_WORD_SIZE + get_env_data_size() / 4);
546 	uint8_t j;
547 	char c;
548 
549 	if (!init_ok) {
550 		EF_INFO("ENV isn't initialize OK.\n");
551 		return;
552 	}
553 
554 	for (; env_cache_data_addr < env_cache_end_addr; env_cache_data_addr += 1) {
555 		for (j = 0; j < 4; j++) {
556 			c = (*env_cache_data_addr) >> (8 * j);
557 			ef_print("%c", c);
558 			if (c == 0) {
559 				ef_print("\n");
560 				break;
561 			}
562 		}
563 	}
564 
565 #ifndef EF_ENV_USING_PFS_MODE
566 	ef_print("\nENV size: %ld/%ld bytes.\n", get_env_user_used_size(), ENV_USER_SETTING_SIZE);
567 #else
568 	ef_print("\nENV size: %ld/%ld bytes, write bytes %ld/%ld, saved count: %ld, mode: power fail safeguard.\n",
569 			 get_env_user_used_size(), ENV_USER_SETTING_SIZE, ef_get_env_write_bytes(),
570 			 ENV_AREA_SIZE, env_cache[ENV_PARAM_INDEX_SAVED_COUNT]);
571 
572 #endif
573 }
574 
575 /**
576  * Load flash ENV to ram.
577  *
578  * @return result
579  */
580 #ifndef EF_ENV_USING_PFS_MODE
ef_load_env(void)581 EfErrCode ef_load_env(void)
582 {
583 	EfErrCode result = EF_NO_ERR;
584 	uint32_t *env_cache_bak, env_end_addr;
585 
586 	/* read ENV end address from flash */
587 	ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_END_ADDR * 4, &env_end_addr, 4);
588 	/* if ENV is not initialize or flash has dirty data, set default for it */
589 	if ((env_end_addr == 0xFFFFFFFF) || (env_end_addr < env_start_addr)
590 		|| (env_end_addr > env_start_addr + ENV_USER_SETTING_SIZE))
591 		result = ef_env_set_default();
592 	else {
593 		/* set ENV end address */
594 		set_env_end_addr(env_end_addr);
595 
596 		env_cache_bak = env_cache + ENV_PARAM_WORD_SIZE;
597 		/* read all ENV from flash */
598 		ef_port_read(get_env_data_addr(), env_cache_bak, get_env_data_size());
599 		/* read ENV CRC code from flash */
600 		ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_DATA_CRC * 4,
601 					 &env_cache[ENV_PARAM_INDEX_DATA_CRC], 4);
602 		/* if ENV CRC32 check is fault, set default for it */
603 		if (!env_crc_is_ok()) {
604 			EF_INFO("Warning: ENV CRC check failed. Set it to default.\n");
605 			result = ef_env_set_default();
606 		}
607 	}
608 	return result;
609 }
610 #else
ef_load_env(void)611 EfErrCode ef_load_env(void)
612 {
613 	EfErrCode result = EF_NO_ERR;
614 	uint32_t area0_start_address = env_start_addr, area1_start_address = env_start_addr
615 								   + ENV_AREA_SIZE / 2;
616 	uint32_t area0_end_addr, area1_end_addr, area0_crc, area1_crc, area0_saved_count, area1_saved_count;
617 	bool area0_is_valid = true, area1_is_valid = true;
618 	/* read ENV area end address from flash */
619 	ef_port_read(area0_start_address + ENV_PARAM_INDEX_END_ADDR * 4, &area0_end_addr, 4);
620 	ef_port_read(area1_start_address + ENV_PARAM_INDEX_END_ADDR * 4, &area1_end_addr, 4);
621 	if ((area0_end_addr == 0xFFFFFFFF) || (area0_end_addr < area0_start_address)
622 		|| (area0_end_addr > area0_start_address + ENV_USER_SETTING_SIZE))
623 		area0_is_valid = false;
624 	if ((area1_end_addr == 0xFFFFFFFF) || (area1_end_addr < area1_start_address)
625 		|| (area1_end_addr > area1_start_address + ENV_USER_SETTING_SIZE))
626 		area1_is_valid = false;
627 	/* check area0 CRC when it is valid */
628 	if (area0_is_valid) {
629 		/* read ENV area0 crc32 code from flash */
630 		ef_port_read(area0_start_address + ENV_PARAM_INDEX_DATA_CRC * 4, &area0_crc, 4);
631 		/* read ENV from ENV area0 */
632 		ef_port_read(area0_start_address, env_cache, area0_end_addr - area0_start_address);
633 		/* current load ENV area address is area0 start address */
634 		cur_load_area_addr = area0_start_address;
635 		if (!env_crc_is_ok())
636 			area0_is_valid = false;
637 	}
638 	/* check area1 CRC when it is valid */
639 	if (area1_is_valid) {
640 		/* read ENV area1 crc32 code from flash */
641 		ef_port_read(area1_start_address + ENV_PARAM_INDEX_DATA_CRC * 4, &area1_crc, 4);
642 		/* read ENV from ENV area1 */
643 		ef_port_read(area1_start_address, env_cache, area1_end_addr - area1_start_address);
644 		/* current load ENV area address is area1 start address */
645 		cur_load_area_addr = area1_start_address;
646 		if (!env_crc_is_ok())
647 			area1_is_valid = false;
648 	}
649 	/* all ENV area CRC is OK then compare saved count */
650 	if (area0_is_valid && area1_is_valid) {
651 		/* read ENV area saved count from flash */
652 		ef_port_read(area0_start_address + ENV_PARAM_INDEX_SAVED_COUNT * 4,
653 					 &area0_saved_count, 4);
654 		ef_port_read(area1_start_address + ENV_PARAM_INDEX_SAVED_COUNT * 4,
655 					 &area1_saved_count, 4);
656 		/* the bigger saved count area is valid */
657 		if ((area0_saved_count > area1_saved_count) || ((area0_saved_count == 0) && (area1_saved_count == 0xFFFFFFFF)))
658 			area1_is_valid = false;
659 		else
660 			area0_is_valid = false;
661 	}
662 	if (area0_is_valid) {
663 		/* current load ENV area address is area0 start address */
664 		cur_load_area_addr = area0_start_address;
665 		/* next save ENV area address is area1 start address */
666 		next_save_area_addr = area1_start_address;
667 		/* read all ENV from area0 */
668 		ef_port_read(area0_start_address, env_cache, area0_end_addr - area0_start_address);
669 	} else if (area1_is_valid) {
670 		/* next save ENV area address is area0 start address */
671 		next_save_area_addr = area0_start_address;
672 	} else {
673 		/* current load ENV area address is area1 start address */
674 		cur_load_area_addr = area1_start_address;
675 		/* next save ENV area address is area0 start address */
676 		next_save_area_addr = area0_start_address;
677 		/* set the ENV to default */
678 		result = ef_env_set_default();
679 	}
680 	return result;
681 }
682 #endif
683 
684 /**
685  * Save ENV to flash.
686  */
ef_save_env(void)687 EfErrCode ef_save_env(void)
688 {
689 	EfErrCode result = EF_NO_ERR;
690 	uint32_t write_addr, write_size;
691 
692 	/* ENV ram cache has not changed don't need to save */
693 	if (!env_cache_changed)
694 		return result;
695 
696 #ifndef EF_ENV_USING_PFS_MODE
697 	write_addr = get_env_system_addr();
698 	write_size = get_env_user_used_size();
699 	/* calculate and cache CRC32 code */
700 	env_cache[ENV_PARAM_INDEX_DATA_CRC] = calc_env_crc();
701 #else
702 	write_addr = next_save_area_addr;
703 	write_size = get_env_user_used_size();
704 	/* replace next_save_area_addr with cur_load_area_addr */
705 	next_save_area_addr = cur_load_area_addr;
706 	cur_load_area_addr = write_addr;
707 	/* change the ENV end address to next save area address */
708 	set_env_end_addr(write_addr + write_size);
709 	/* ENV area saved count +1 */
710 	env_cache[ENV_PARAM_INDEX_SAVED_COUNT]++;
711 	/* calculate and cache CRC32 code */
712 	env_cache[ENV_PARAM_INDEX_DATA_CRC] = calc_env_crc();
713 #endif
714 
715 	/* erase ENV */
716 	result = ef_port_erase(write_addr, write_size);
717 	switch (result) {
718 	case EF_NO_ERR: {
719 		EF_INFO("Erased ENV OK.\n");
720 		break;
721 	}
722 	case EF_ERASE_ERR: {
723 		EF_INFO("Error: Erased ENV fault! Start address is 0x%08X, size is %ld.\n", write_addr, write_size);
724 		/* will return when erase fault */
725 		return result;
726 	}
727 	default:
728 		break;
729 	}
730 
731 	/* write ENV to flash */
732 	result = ef_port_write(write_addr, env_cache, write_size);
733 	switch (result) {
734 	case EF_NO_ERR: {
735 		EF_INFO("Saved ENV OK.\n");
736 		break;
737 	}
738 	case EF_WRITE_ERR: {
739 		EF_INFO("Error: Saved ENV fault! Start address is 0x%08X, size is %ld.\n", write_addr, write_size);
740 		break;
741 	}
742 	default:
743 		break;
744 	}
745 
746 	env_cache_changed = false;
747 
748 	return result;
749 }
750 
751 /**
752  * Calculate the cached ENV CRC32 value.
753  *
754  * @return CRC32 value
755  */
calc_env_crc(void)756 static uint32_t calc_env_crc(void)
757 {
758 	uint32_t crc32 = 0;
759 
760 	/* Calculate the ENV end address CRC32. The 4 is ENV end address bytes size. */
761 	crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_INDEX_END_ADDR], 4);
762 
763 #ifdef EF_ENV_USING_PFS_MODE
764 	/* Calculate the ENV area saved count CRC32. */
765 	crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_INDEX_SAVED_COUNT], 4);
766 #endif
767 
768 	/* Calculate the all ENV data CRC32. */
769 	crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_WORD_SIZE], get_env_data_size());
770 
771 	EF_DEBUG("Calculate ENV CRC32 number is 0x%08X.\n", crc32);
772 
773 	return crc32;
774 }
775 
776 /**
777  * Check the ENV CRC32
778  *
779  * @return true is ok
780  */
env_crc_is_ok(void)781 static bool env_crc_is_ok(void)
782 {
783 	if (calc_env_crc() == env_cache[ENV_PARAM_INDEX_DATA_CRC]) {
784 		EF_DEBUG("Verify ENV CRC32 result is OK.\n");
785 		return true;
786 	} else
787 		return false;
788 }
789 
790 /**
791  * Set and save an ENV. If set ENV is success then will save it.
792  *
793  * @param key ENV name
794  * @param value ENV value
795  *
796  * @return result
797  */
ef_set_and_save_env(const char * key,const char * value)798 EfErrCode ef_set_and_save_env(const char *key, const char *value)
799 {
800 	EfErrCode result = EF_NO_ERR;
801 
802 	result = ef_set_env(key, value);
803 
804 	if (result == EF_NO_ERR)
805 		result = ef_save_env();
806 
807 	return result;
808 }
809 
810 #endif /* EF_ENV_USING_WL_MODE */
811 
812 #endif /* EF_USING_ENV */
813