• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2015-2017, 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: Save logs to flash.
24  * Created on: 2015-06-04
25  */
26 
27 #include <easyflash.h>
28 
29 #ifdef EF_USING_LOG
30 
31 /* magic code on every sector header. 'EF' is 0x4546 */
32 #define LOG_SECTOR_MAGIC               0x4546
33 /* sector header size, include the sector magic code and status magic code */
34 #define LOG_SECTOR_HEADER_SIZE         4
35 
36 /**
37  * Sector status magic code
38  * The sector status is 16-Bits after LOG_SECTOR_MAGIC at every sector header.
39  * =======================
40  * | header(4B) | status |
41  * -----------------------
42  * | 0x4546FFFF |  empty |
43  * | 0x4546FFFE |  using |
44  * | 0x4546FFFC |  full  |
45  * =======================
46  *
47  * State transition relationship: empty->using->full
48  * The FULL status will change to EMPTY after sector clean.
49  */
50 enum {
51 	SECTOR_STATUS_MAGIC_EMPUT = 0xFFFF,
52 	SECTOR_STATUS_MAGIC_USING = 0xFFFE,
53 	SECTOR_STATUS_MAGIC_FULL = 0xFFFC,
54 };
55 
56 typedef enum {
57 	SECTOR_STATUS_EMPUT,
58 	SECTOR_STATUS_USING,
59 	SECTOR_STATUS_FULL,
60 	SECTOR_STATUS_HEADER_ERROR,
61 } SectorStatus;
62 
63 /* the stored logs start address and end address. It's like a ring buffer which implement by flash. */
64 static uint32_t log_start_addr = 0, log_end_addr = 0;
65 /* saved log area address for flash */
66 static uint32_t log_area_start_addr = 0;
67 /* initialize OK flag */
68 static bool init_ok = false;
69 
70 static void find_start_and_end_addr(void);
71 static uint32_t get_next_flash_sec_addr(uint32_t cur_addr);
72 
73 /**
74  * The flash save log function initialize.
75  *
76  * @return result
77  */
ef_log_init(void)78 EfErrCode ef_log_init(void)
79 {
80 	EfErrCode result = EF_NO_ERR;
81 
82 	EF_ASSERT(LOG_AREA_SIZE);
83 	EF_ASSERT(EF_ERASE_MIN_SIZE);
84 	/* the log area size must be an integral multiple of erase minimum size. */
85 	EF_ASSERT(LOG_AREA_SIZE % EF_ERASE_MIN_SIZE == 0);
86 	/* the log area size must be more than twice of EF_ERASE_MIN_SIZE */
87 	EF_ASSERT(LOG_AREA_SIZE / EF_ERASE_MIN_SIZE >= 2);
88 
89 #ifdef EF_USING_ENV
90 	log_area_start_addr = EF_START_ADDR + ENV_AREA_SIZE;
91 #else
92 	log_area_start_addr = EF_START_ADDR;
93 #endif
94 
95 	/* find the log store start address and end address */
96 	find_start_and_end_addr();
97 	/* initialize OK */
98 	init_ok = true;
99 
100 	return result;
101 }
102 
103 /**
104  * Get flash sector current status.
105  *
106  * @param addr sector address, this function will auto calculate the sector header address by this address.
107  *
108  * @return the flash sector current status
109  */
get_sector_status(uint32_t addr)110 static SectorStatus get_sector_status(uint32_t addr)
111 {
112 	uint32_t header = 0, header_addr = 0;
113 	uint16_t sector_magic = 0, status_magic = 0;
114 
115 	/* calculate the sector header address */
116 	header_addr = addr / EF_ERASE_MIN_SIZE * EF_ERASE_MIN_SIZE;
117 
118 	if (ef_port_read(header_addr, &header, sizeof(header)) == EF_NO_ERR) {
119 		sector_magic = header >> 16;
120 		status_magic = header;
121 	} else {
122 		EF_DEBUG("Error: Read sector header data error.\n");
123 		return SECTOR_STATUS_HEADER_ERROR;
124 	}
125 	/* compare header magic code */
126 	if (sector_magic == LOG_SECTOR_MAGIC) {
127 		switch (status_magic) {
128 		case SECTOR_STATUS_MAGIC_EMPUT:
129 			return SECTOR_STATUS_EMPUT;
130 		case SECTOR_STATUS_MAGIC_USING:
131 			return SECTOR_STATUS_USING;
132 		case SECTOR_STATUS_MAGIC_FULL:
133 			return SECTOR_STATUS_FULL;
134 		default:
135 			return SECTOR_STATUS_HEADER_ERROR;
136 		}
137 	} else
138 		return SECTOR_STATUS_HEADER_ERROR;
139 }
140 
141 /**
142  * Write flash sector current status.
143  *
144  * @param addr sector address, this function will auto calculate the sector header address by this address.
145  * @param status sector cur status
146  *
147  * @return result
148  */
write_sector_status(uint32_t addr,SectorStatus status)149 static EfErrCode write_sector_status(uint32_t addr, SectorStatus status)
150 {
151 	uint32_t header = 0, header_addr = 0;
152 	uint16_t status_magic;
153 
154 	/* calculate the sector header address */
155 	header_addr = addr / EF_ERASE_MIN_SIZE * EF_ERASE_MIN_SIZE;
156 
157 	switch (status) {
158 	case SECTOR_STATUS_EMPUT: {
159 		status_magic = SECTOR_STATUS_MAGIC_EMPUT;
160 		break;
161 	}
162 	case SECTOR_STATUS_USING: {
163 		status_magic = SECTOR_STATUS_MAGIC_USING;
164 		break;
165 	}
166 	case SECTOR_STATUS_FULL: {
167 		status_magic = SECTOR_STATUS_MAGIC_FULL;
168 		break;
169 	}
170 	}
171 	header = (LOG_SECTOR_MAGIC << 16) | status_magic;
172 
173 	return ef_port_write(header_addr, &header, sizeof(header));
174 }
175 
176 /**
177  * Find the current flash sector using end address by continuous 0xFF.
178  *
179  * @param addr sector address
180  *
181  * @return current flash sector using end address
182  */
find_sec_using_end_addr(uint32_t addr)183 static uint32_t find_sec_using_end_addr(uint32_t addr)
184 {
185 	/* read section data buffer size */
186 #define READ_BUF_SIZE                32
187 
188 	uint32_t sector_start = addr, data_start = addr, continue_ff = 0, read_buf_size = 0, i;
189 	uint8_t buf[READ_BUF_SIZE];
190 
191 	EF_ASSERT(READ_BUF_SIZE % 4 == 0);
192 	/* calculate the sector start and data start address */
193 	sector_start = addr / EF_ERASE_MIN_SIZE * EF_ERASE_MIN_SIZE;
194 	data_start = sector_start + LOG_SECTOR_HEADER_SIZE;
195 
196 	/* counts continuous 0xFF which is end of sector */
197 	while (data_start < sector_start + EF_ERASE_MIN_SIZE) {
198 		if (data_start + READ_BUF_SIZE < sector_start + EF_ERASE_MIN_SIZE)
199 			read_buf_size = READ_BUF_SIZE;
200 		else
201 			read_buf_size = sector_start + EF_ERASE_MIN_SIZE - data_start;
202 		ef_port_read(data_start, (uint32_t *)buf, read_buf_size);
203 		for (i = 0; i < read_buf_size; i++) {
204 			if (buf[i] == 0xFF)
205 				continue_ff++;
206 			else
207 				continue_ff = 0;
208 		}
209 		data_start += read_buf_size;
210 	}
211 	/* calculate current flash sector using end address */
212 	if (continue_ff >= EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE) {
213 		/* from 0 to sec_size all sector is 0xFF, so the sector is empty */
214 		return sector_start + LOG_SECTOR_HEADER_SIZE;
215 	} else if (continue_ff >= 4) {
216 		/* form end_addr - 4 to sec_size length all area is 0xFF, so it's used part of the sector.
217 		 * the address must be word alignment. */
218 		if (continue_ff % 4 != 0)
219 			continue_ff = (continue_ff / 4 + 1) * 4;
220 		return sector_start + EF_ERASE_MIN_SIZE - continue_ff;
221 	} else {
222 		/* all sector not has continuous 0xFF, so the sector is full */
223 		return sector_start + EF_ERASE_MIN_SIZE;
224 	}
225 }
226 
227 /**
228  * Find the log store start address and end address.
229  * It's like a ring buffer which implement by flash.
230  * The flash log area has two state when find start address and end address.
231  *                       state 1                                state 2
232  *                   |============|                         |============|
233  * log area start--> |############| <-- start address       |############| <-- end address
234  *                   |############|                         |   empty    |
235  *                   |------------|                         |------------|
236  *                   |############|                         |############| <-- start address
237  *                   |############|                         |############|
238  *                   |------------|                         |------------|
239  *                   |     .      |                         |     .      |
240  *                   |     .      |                         |     .      |
241  *                   |     .      |                         |     .      |
242  *                   |------------|                         |------------|
243  *                   |############| <-- end address         |############|
244  *                   |   empty    |                         |############|
245  *  log area end --> |============|                         |============|
246  *
247  *  LOG_AREA_SIZE = log area end - log area star
248  *
249  */
find_start_and_end_addr(void)250 static void find_start_and_end_addr(void)
251 {
252 	size_t cur_size = 0;
253 	SectorStatus cur_sec_status, last_sec_status;
254 	uint32_t cur_using_sec_addr = 0;
255 	/* all status sector counts */
256 	size_t empty_sec_counts = 0, using_sec_counts = 0, full_sector_counts = 0;
257 	/* total sector number */
258 	size_t total_sec_num = LOG_AREA_SIZE / EF_ERASE_MIN_SIZE;
259 	/* see comment of find_start_and_end_addr function */
260 	uint8_t cur_log_sec_state = 0;
261 
262 	/* get the first sector status */
263 	cur_sec_status = get_sector_status(log_area_start_addr);
264 	last_sec_status = cur_sec_status;
265 
266 	for (cur_size = EF_ERASE_MIN_SIZE; cur_size < LOG_AREA_SIZE; cur_size += EF_ERASE_MIN_SIZE) {
267 		/* get current sector status */
268 		cur_sec_status = get_sector_status(log_area_start_addr + cur_size);
269 		/* compare last and current status */
270 		switch (last_sec_status) {
271 		case SECTOR_STATUS_EMPUT: {
272 			switch (cur_sec_status) {
273 			case SECTOR_STATUS_EMPUT:
274 				break;
275 			case SECTOR_STATUS_USING:
276 				EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
277 				ef_log_clean();
278 				return;
279 			case SECTOR_STATUS_FULL:
280 				EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
281 				ef_log_clean();
282 				return;
283 			}
284 			empty_sec_counts++;
285 			break;
286 		}
287 		case SECTOR_STATUS_USING: {
288 			switch (cur_sec_status) {
289 			case SECTOR_STATUS_EMPUT:
290 				/* like state 1 */
291 				cur_log_sec_state = 1;
292 				log_start_addr = log_area_start_addr;
293 				cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE;
294 				break;
295 			case SECTOR_STATUS_USING:
296 				EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
297 				ef_log_clean();
298 				return;
299 			case SECTOR_STATUS_FULL:
300 				/* like state 2 */
301 				cur_log_sec_state = 2;
302 				log_start_addr = log_area_start_addr + cur_size;
303 				cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE;
304 				break;
305 			}
306 			using_sec_counts++;
307 			break;
308 		}
309 		case SECTOR_STATUS_FULL: {
310 			switch (cur_sec_status) {
311 			case SECTOR_STATUS_EMPUT:
312 				/* like state 1 */
313 				if (cur_log_sec_state == 2) {
314 					EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
315 					ef_log_clean();
316 					return;
317 				} else {
318 					cur_log_sec_state = 1;
319 					log_start_addr = log_area_start_addr;
320 					log_end_addr = log_area_start_addr + cur_size;
321 					cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE;
322 				}
323 				break;
324 			case SECTOR_STATUS_USING:
325 				if (total_sec_num <= 2) {
326 					/* like state 1 */
327 					cur_log_sec_state = 1;
328 					log_start_addr = log_area_start_addr;
329 					cur_using_sec_addr = log_area_start_addr + cur_size;
330 				} else {
331 					/* like state 2 when the sector is the last one */
332 					if (cur_size + EF_ERASE_MIN_SIZE >= LOG_AREA_SIZE) {
333 						cur_log_sec_state = 2;
334 						log_start_addr = log_area_start_addr + cur_size;
335 						cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE;
336 					}
337 				}
338 				break;
339 			case SECTOR_STATUS_FULL:
340 				break;
341 			}
342 			full_sector_counts++;
343 			break;
344 		}
345 		case SECTOR_STATUS_HEADER_ERROR:
346 			EF_DEBUG("Error: Log sector header error! Now will clean all log area.\n");
347 			ef_log_clean();
348 			return;
349 		}
350 		last_sec_status = cur_sec_status;
351 	}
352 
353 	/* the last sector status counts */
354 	if (cur_sec_status == SECTOR_STATUS_EMPUT)
355 		empty_sec_counts++;
356 	else if (cur_sec_status == SECTOR_STATUS_USING)
357 		using_sec_counts++;
358 	else if (cur_sec_status == SECTOR_STATUS_FULL)
359 		full_sector_counts++;
360 	else if (cur_sec_status == SECTOR_STATUS_HEADER_ERROR) {
361 		EF_DEBUG("Error: Log sector header error! Now will clean all log area.\n");
362 		ef_log_clean();
363 		return;
364 	}
365 
366 	if (using_sec_counts != 1) {
367 		/* this state is almost impossible */
368 		EF_DEBUG("Error: There must be only one sector status is USING! Now will clean all log area.\n");
369 		ef_log_clean();
370 	} else {
371 		/* find the end address */
372 		log_end_addr = find_sec_using_end_addr(cur_using_sec_addr);
373 	}
374 }
375 
376 /**
377  * Get log used flash total size.
378  *
379  * @return log used flash total size. @note NOT contain sector headers
380  */
ef_log_get_used_size(void)381 size_t ef_log_get_used_size(void)
382 {
383 	size_t header_total_num = 0, physical_size = 0;
384 	/* must be call this function after initialize OK */
385 	if (!init_ok)
386 		return 0;
387 
388 	if (log_start_addr < log_end_addr)
389 		physical_size = log_end_addr - log_start_addr;
390 	else
391 		physical_size = LOG_AREA_SIZE - (log_start_addr - log_end_addr);
392 
393 	header_total_num = physical_size / EF_ERASE_MIN_SIZE + 1;
394 
395 	return physical_size - header_total_num * LOG_SECTOR_HEADER_SIZE;
396 }
397 
398 /**
399  * Sequential reading log data. It will ignore sector headers.
400  *
401  * @param addr address
402  * @param log log buffer
403  * @param size log size, not contain sector headers.
404  *
405  * @return result
406  */
log_seq_read(uint32_t addr,uint32_t * log,size_t size)407 static EfErrCode log_seq_read(uint32_t addr, uint32_t *log, size_t size)
408 {
409 	EfErrCode result = EF_NO_ERR;
410 	size_t read_size = 0, read_size_temp = 0;
411 
412 	while (size) {
413 		/* move to sector data address */
414 		if ((addr + read_size) % EF_ERASE_MIN_SIZE == 0)
415 			addr += LOG_SECTOR_HEADER_SIZE;
416 		/* calculate current sector last data size */
417 		read_size_temp = EF_ERASE_MIN_SIZE - (addr % EF_ERASE_MIN_SIZE);
418 		if (size < read_size_temp)
419 			read_size_temp = size;
420 		result = ef_port_read(addr + read_size, log + read_size / 4, read_size_temp);
421 		if (result != EF_NO_ERR)
422 			return result;
423 		read_size += read_size_temp;
424 		size -= read_size_temp;
425 	}
426 
427 	return result;
428 }
429 
430 /**
431  * Calculate flash physical address by log index.
432  *
433  * @param index log index
434  *
435  * @return flash physical address
436  */
log_index2addr(size_t index)437 static uint32_t log_index2addr(size_t index)
438 {
439 	size_t header_total_offset = 0;
440 	/* total include sector number */
441 	size_t sector_num = index / (EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE) + 1;
442 
443 	header_total_offset = sector_num * LOG_SECTOR_HEADER_SIZE;
444 	if (log_start_addr < log_end_addr)
445 		return log_start_addr + index + header_total_offset;
446 	else {
447 		if (log_start_addr + index + header_total_offset < log_area_start_addr + LOG_AREA_SIZE)
448 			return log_start_addr + index + header_total_offset;
449 		else
450 			return log_start_addr + index + header_total_offset - LOG_AREA_SIZE;
451 
452 	}
453 }
454 
455 /**
456  * Read log from flash.
457  *
458  * @param index index for saved log.
459  *        Minimum index is 0.
460  *        Maximum index is ef_log_get_used_size() - 1.
461  * @param log the log which will read from flash
462  * @param size read bytes size
463  *
464  * @return result
465  */
ef_log_read(size_t index,uint32_t * log,size_t size)466 EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size)
467 {
468 	EfErrCode result = EF_NO_ERR;
469 	size_t cur_using_size = ef_log_get_used_size();
470 	size_t read_size_temp = 0;
471 	size_t header_total_num = 0;
472 
473 	if (!size)
474 		return result;
475 
476 	EF_ASSERT(size % 4 == 0);
477 	EF_ASSERT(index < cur_using_size);
478 
479 	if (index + size > cur_using_size) {
480 		EF_DEBUG("Warning: Log read size out of bound. Cut read size.\n");
481 		size = cur_using_size - index;
482 	}
483 	/* must be call this function after initialize OK */
484 	if (!init_ok)
485 		return EF_ENV_INIT_FAILED;
486 
487 	if (log_start_addr < log_end_addr)
488 		log_seq_read(log_index2addr(index), log, size);
489 	else {
490 		if (log_index2addr(index) + size <= log_area_start_addr + LOG_AREA_SIZE) {
491 			/*                          Flash log area
492 			 *                         |--------------|
493 			 * log_area_start_addr --> |##############|
494 			 *                         |##############|
495 			 *                         |##############|
496 			 *                         |--------------|
497 			 *                         |##############|
498 			 *                         |##############|
499 			 *                         |##############| <-- log_end_addr
500 			 *                         |--------------|
501 			 *      log_start_addr --> |##############|
502 			 *          read start --> |**************| <-- read end
503 			 *                         |##############|
504 			 *                         |--------------|
505 			 *
506 			 * read from (log_start_addr + log_index2addr(index)) to (log_start_addr + index + log_index2addr(index))
507 			 */
508 			result = log_seq_read(log_index2addr(index), log, size);
509 		} else if (log_index2addr(index) < log_area_start_addr + LOG_AREA_SIZE) {
510 			/*                          Flash log area
511 			 *                         |--------------|
512 			 * log_area_start_addr --> |**************| <-- read end
513 			 *                         |##############|
514 			 *                         |##############|
515 			 *                         |--------------|
516 			 *                         |##############|
517 			 *                         |##############|
518 			 *                         |##############| <-- log_end_addr
519 			 *                         |--------------|
520 			 *      log_start_addr --> |##############|
521 			 *          read start --> |**************|
522 			 *                         |**************|
523 			 *                         |--------------|
524 			 * read will by 2 steps
525 			 * step1: read from (log_start_addr + log_index2addr(index)) to flash log area end address
526 			 * step2: read from flash log area start address to read size's end address
527 			 */
528 			read_size_temp = (log_area_start_addr + LOG_AREA_SIZE) - log_index2addr(index);
529 			header_total_num = read_size_temp / EF_ERASE_MIN_SIZE;
530 			/* Minus some ignored bytes */
531 			read_size_temp -= header_total_num * LOG_SECTOR_HEADER_SIZE;
532 			result = log_seq_read(log_index2addr(index), log, read_size_temp);
533 			if (result == EF_NO_ERR)
534 				result = log_seq_read(log_area_start_addr, log + read_size_temp / 4, size - read_size_temp);
535 		} else {
536 			/*                          Flash log area
537 			 *                         |--------------|
538 			 * log_area_start_addr --> |##############|
539 			 *          read start --> |**************|
540 			 *                         |**************| <-- read end
541 			 *                         |--------------|
542 			 *                         |##############|
543 			 *                         |##############|
544 			 *                         |##############| <-- log_end_addr
545 			 *                         |--------------|
546 			 *      log_start_addr --> |##############|
547 			 *                         |##############|
548 			 *                         |##############|
549 			 *                         |--------------|
550 			 * read from (log_start_addr + log_index2addr(index) - LOG_AREA_SIZE) to read size's end address
551 			 */
552 			result = log_seq_read(log_index2addr(index) - LOG_AREA_SIZE, log, size);
553 		}
554 	}
555 
556 	return result;
557 }
558 
559 /**
560  * Write log to flash.
561  *
562  * @param log the log which will be write to flash
563  * @param size write bytes size
564  *
565  * @return result
566  */
ef_log_write(const uint32_t * log,size_t size)567 EfErrCode ef_log_write(const uint32_t *log, size_t size)
568 {
569 	EfErrCode result = EF_NO_ERR;
570 	size_t write_size = 0, writable_size = 0;
571 	uint32_t write_addr = log_end_addr, erase_addr;
572 	SectorStatus sector_status;
573 
574 	EF_ASSERT(size % 4 == 0);
575 	/* must be call this function after initialize OK */
576 	if (!init_ok)
577 		return EF_ENV_INIT_FAILED;
578 
579 	if ((sector_status = get_sector_status(write_addr)) == SECTOR_STATUS_HEADER_ERROR)
580 		return EF_WRITE_ERR;
581 	/* write some log when current sector status is USING and EMPTY */
582 	if ((sector_status == SECTOR_STATUS_USING) || (sector_status == SECTOR_STATUS_EMPUT)) {
583 		/* write the already erased but not used area */
584 		writable_size = EF_ERASE_MIN_SIZE - ((write_addr - log_area_start_addr) % EF_ERASE_MIN_SIZE);
585 		if (size >= writable_size) {
586 			result = ef_port_write(write_addr, log, writable_size);
587 			if (result != EF_NO_ERR)
588 				goto exit;
589 			/* change the current sector status to FULL */
590 			result = write_sector_status(write_addr, SECTOR_STATUS_FULL);
591 			if (result != EF_NO_ERR)
592 				goto exit;
593 			write_size += writable_size;
594 		} else {
595 			result = ef_port_write(write_addr, log, size);
596 			log_end_addr = write_addr + size;
597 			goto exit;
598 		}
599 	}
600 	/* erase and write remain log */
601 	while (true) {
602 		/* calculate next available sector address */
603 		erase_addr = write_addr = get_next_flash_sec_addr(write_addr - 4);
604 		/* move the flash log start address to next available sector address */
605 		if (log_start_addr == erase_addr)
606 			log_start_addr = get_next_flash_sec_addr(log_start_addr);
607 		/* erase sector */
608 		result = ef_port_erase(erase_addr, EF_ERASE_MIN_SIZE);
609 		if (result != EF_NO_ERR)
610 			goto exit;
611 		/* change the sector status to USING when write begin sector start address */
612 		result = write_sector_status(write_addr, SECTOR_STATUS_USING);
613 		if (result == EF_NO_ERR)
614 			write_addr += LOG_SECTOR_HEADER_SIZE;
615 		else
616 			goto exit;
617 		/* calculate current sector writable data size */
618 		writable_size = EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE;
619 		if (size - write_size >= writable_size) {
620 			result = ef_port_write(write_addr, log + write_size / 4, writable_size);
621 			if (result != EF_NO_ERR)
622 				goto exit;
623 			/* change the current sector status to FULL */
624 			result = write_sector_status(write_addr, SECTOR_STATUS_FULL);
625 			if (result != EF_NO_ERR)
626 				goto exit;
627 			log_end_addr = write_addr + writable_size;
628 			write_size += writable_size;
629 			write_addr += writable_size;
630 		} else {
631 			result = ef_port_write(write_addr, log + write_size / 4, size - write_size);
632 			if (result != EF_NO_ERR)
633 				goto exit;
634 			log_end_addr = write_addr + (size - write_size);
635 			break;
636 		}
637 	}
638 
639 exit:
640 	return result;
641 }
642 
643 /**
644  * Get next flash sector address.The log total sector like ring buffer which implement by flash.
645  *
646  * @param cur_addr cur flash address
647  *
648  * @return next flash sector address
649  */
get_next_flash_sec_addr(uint32_t cur_addr)650 static uint32_t get_next_flash_sec_addr(uint32_t cur_addr)
651 {
652 	size_t cur_sec_id = (cur_addr - log_area_start_addr) / EF_ERASE_MIN_SIZE;
653 	size_t sec_total_num = LOG_AREA_SIZE / EF_ERASE_MIN_SIZE;
654 
655 	if (cur_sec_id + 1 >= sec_total_num) {
656 		/* return to ring head */
657 		return log_area_start_addr;
658 	} else
659 		return log_area_start_addr + (cur_sec_id + 1) * EF_ERASE_MIN_SIZE;
660 }
661 
662 /**
663  * Clean all log which in flash.
664  *
665  * @return result
666  */
ef_log_clean(void)667 EfErrCode ef_log_clean(void)
668 {
669 	EfErrCode result = EF_NO_ERR;
670 	uint32_t write_addr = log_area_start_addr;
671 
672 	/* clean address */
673 	log_start_addr = log_area_start_addr;
674 	log_end_addr = log_start_addr + LOG_SECTOR_HEADER_SIZE;
675 	/* erase log flash area */
676 	result = ef_port_erase(log_area_start_addr, LOG_AREA_SIZE);
677 	if (result != EF_NO_ERR)
678 		goto exit;
679 	/* setting first sector is USING */
680 	write_sector_status(write_addr, SECTOR_STATUS_USING);
681 	if (result != EF_NO_ERR)
682 		goto exit;
683 	write_addr += EF_ERASE_MIN_SIZE;
684 	/* add sector header */
685 	while (true) {
686 		write_sector_status(write_addr, SECTOR_STATUS_EMPUT);
687 		if (result != EF_NO_ERR)
688 			goto exit;
689 		write_addr += EF_ERASE_MIN_SIZE;
690 		if (write_addr >= log_area_start_addr + LOG_AREA_SIZE)
691 			break;
692 	}
693 
694 exit:
695 	return result;
696 }
697 
698 #endif /* EF_USING_LOG */
699