px-fwlib 0.10.0
Cross-platform embedded library and documentation for 8/16/32-bit microcontrollers generated with Doxygen 1.9.2
px_log_fs.c
1/* =============================================================================
2 ____ ___ ____ ___ _ _ ___ __ __ ___ __ __ TM
3 | _ \ |_ _| / ___| / _ \ | \ | | / _ \ | \/ | |_ _| \ \/ /
4 | |_) | | | | | | | | | | \| | | | | | | |\/| | | | \ /
5 | __/ | | | |___ | |_| | | |\ | | |_| | | | | | | | / \
6 |_| |___| \____| \___/ |_| \_| \___/ |_| |_| |___| /_/\_\
7
8 Copyright (c) 2019 Pieter Conradie <https://piconomix.com>
9
10 License: MIT
11 https://github.com/piconomix/px-fwlib/blob/master/LICENSE.md
12
13 Title: px_log_fs.h : Record-based file system for Serial Flash
14 Author(s): Pieter Conradie
15 Creation Date: 2019-09-09
16
17============================================================================= */
18
19/* _____STANDARD INCLUDES____________________________________________________ */
20#include <string.h>
21#include <stddef.h>
22
23/* _____PROJECT INCLUDES_____________________________________________________ */
24#include "px_log_fs.h"
25#include "px_log_fs_glue.h"
26#include "px_log.h"
27
28/* _____LOCAL DEFINITIONS____________________________________________________ */
29PX_LOG_NAME("px_log_fs");
30
31/// Invalid page value
32#define PX_LOG_FS_PAGE_INVALID 0xffff
33
34/// @name Marker values
35/// @{
36#define PX_LOG_FS_MARKER_FREE 0xff /* 1111_1111b */
37#define PX_LOG_FS_MARKER_PAGE 0x5f /* 0101_1111b */
38#define PX_LOG_FS_MARKER_PAGE_A 0x55 /* 0101_0101b */
39#define PX_LOG_FS_MARKER_RECORD 0xaf /* 1010_1111b */
40#define PX_LOG_FS_MARKER_RECORD_A 0xaa /* 1010_1010b */
41#define PX_LOG_FS_MARKER_BAD 0x00 /* 0000_0000b */
42/// @}
43
44/// Definition of header structure that is stored at the start of each page
45typedef struct
46{
47 uint8_t marker; ///< FREE, PAGE, PAGE_A or BAD
48 uint16_t nr; ///< Rolling number to find first and last marked page
49 uint8_t crc; ///< Checksum calculated over nr (excludes marker)
50} PX_ATTR_PACKED px_log_fs_header_t;
51
52/// Definition of a record structure
53typedef struct
54{
55 uint8_t marker; ///< RECORD or RECORD_A
56 uint8_t data[PX_LOG_FS_CFG_REC_DATA_SIZE]; ///< Record data content
57 uint8_t crc; ///< Checksum calculated over data (excludes marker)
58} PX_ATTR_PACKED px_log_fs_record_t;
59
60/// Page data size (page size - page header)
61#define PX_LOG_FS_PAGE_DATA_SIZE (PX_LOG_FS_CFG_PAGE_SIZE - sizeof(px_log_fs_header_t))
62/// Records per page
63#define PX_LOG_FS_RECORDS_PER_PAGE (PX_LOG_FS_PAGE_DATA_SIZE / sizeof(px_log_fs_record_t))
64/// First record offset
65#define PX_LOG_FS_REC_OFFSET_FIRST (sizeof(px_log_fs_header_t))
66/// Last record offset
67#define PX_LOG_FS_REC_OFFSET_LAST (sizeof(px_log_fs_header_t) + (PX_LOG_FS_RECORDS_PER_PAGE - 1) * sizeof(px_log_fs_record_t))
68
69/* _____MACROS_______________________________________________________________ */
70
71/* _____GLOBAL VARIABLES_____________________________________________________ */
72
73/* _____LOCAL VARIABLES______________________________________________________ */
74
75/* _____LOCAL FUNCTION DECLARATIONS__________________________________________ */
76
77/* _____LOCAL FUNCTIONS______________________________________________________ */
78static inline bool px_log_fs_marker_is_free(uint8_t marker)
79{
80 if(marker == PX_LOG_FS_MARKER_FREE)
81 {
82 return true;
83 }
84 else
85 {
86 return false;
87 }
88}
89
90static inline bool px_log_fs_marker_is_page(uint8_t marker)
91{
92 if( (marker == PX_LOG_FS_MARKER_PAGE )
93 ||(marker == PX_LOG_FS_MARKER_PAGE_A) )
94 {
95 return true;
96 }
97 else
98 {
99 return false;
100 }
101}
102
103static inline bool px_log_fs_marker_is_record(uint8_t marker)
104{
105 if( (marker == PX_LOG_FS_MARKER_RECORD )
106 ||(marker == PX_LOG_FS_MARKER_RECORD_A) )
107 {
108 return true;
109 }
110 else
111 {
112 return false;
113 }
114}
115
116static inline bool px_log_fs_marker_is_bad(uint8_t marker)
117{
118 if(marker == PX_LOG_FS_MARKER_BAD)
119 {
120 return true;
121 }
122 else
123 {
124 return false;
125 }
126}
127
128static uint16_t px_log_fs_page_next(const px_log_fs_handle_t * handle,
129 uint16_t page)
130{
131 // End?
132 if(page == handle->fs_page_end)
133 {
134 // Wrap to start
135 return handle->fs_page_start;
136 }
137 else
138 {
139 // Return next page number
140 return ++page;
141 }
142}
143
144static uint16_t px_log_fs_page_previous(const px_log_fs_handle_t * handle,
145 uint16_t page)
146{
147 // Start?
148 if(page == handle->fs_page_start)
149 {
150 // Wrap to end
151 return handle->fs_page_end;
152 }
153 else
154 {
155 // Return previous page number
156 return --page;
157 }
158}
159
160static void px_log_fs_record_adr_next(const px_log_fs_handle_t * handle,
161 px_log_fs_adr_t * adr)
162{
163 // Last record on page?
164 if(adr->offset == PX_LOG_FS_REC_OFFSET_LAST)
165 {
166 // First record on next page
167 adr->page = px_log_fs_page_next(handle, adr->page);
168 adr->offset = PX_LOG_FS_REC_OFFSET_FIRST;
169 }
170 else
171 {
172 // Next record
173 adr->offset += sizeof(px_log_fs_record_t);
174 }
175}
176
177static void px_log_fs_record_adr_previous(const px_log_fs_handle_t * handle,
178 px_log_fs_adr_t * adr)
179{
180 // First record on page?
181 if(adr->offset == PX_LOG_FS_REC_OFFSET_FIRST)
182 {
183 // Last record on previous page
184 adr->page = px_log_fs_page_previous(handle, adr->page);
185 adr->offset = PX_LOG_FS_REC_OFFSET_LAST;
186 }
187 else
188 {
189 // Previous record
190 adr->offset -= sizeof(px_log_fs_record_t);
191 }
192}
193
194static uint8_t px_log_fs_crc(const void * data, size_t nr_of_bytes)
195{
196 uint8_t i;
197 uint8_t * data_u8 = (uint8_t *)data;
198 uint8_t crc = 0xff;
199
200 // http://www.ece.cmu.edu/~koopman/roses/dsn04/koopman04_crc_poly_embedded.pdf
201 // width=8 poly=0x4d init=0xff refin=true refout=true xorout=0xff check=0xd8
202 // name="CRC-8/KOOP"
203
204 // Repeat until all the data bytes have been processed...
205 while(nr_of_bytes != 0)
206 {
207 nr_of_bytes--;
208
209 // XOR CRC with 8-bit data
210 crc = crc ^ (*data_u8++);
211
212 // Repeat 8 times (for each bit)
213 for(i = 8; i != 0; i--)
214 {
215 // Is lowest bit set?
216 if((crc & 1) != 0)
217 {
218 // Shift right and XOR with reverse of polynomial x^8 + x^6 + x^3 + x^2 + x^0
219 crc = (crc >> 1) ^ 0xb2;
220 }
221 else
222 {
223 // Shift right
224 crc = (crc >> 1);
225 }
226 }
227 }
228 return crc;
229}
230
231static uint8_t px_log_fs_marker_rd(const px_log_fs_adr_t * adr)
232{
233 uint8_t marker_rd;
234
235 px_log_fs_glue_rd(&marker_rd, adr->page, adr->offset, sizeof(marker_rd));
236
237 return marker_rd;
238}
239
240static bool px_log_fs_marker_wr(uint8_t marker,
241 const px_log_fs_adr_t * adr)
242{
243 uint8_t marker_wr = marker;
244 uint8_t marker_rd;
245
246 px_log_fs_glue_wr(&marker_wr, adr->page, adr->offset, sizeof(marker_wr));
247 px_log_fs_glue_rd(&marker_rd, adr->page, adr->offset, sizeof(marker_rd));
248 if(marker_rd == marker_wr)
249 {
250 // Success
251 return true;
252 }
253 else
254 {
255 // Failure
256 return false;
257 }
258}
259
260static uint8_t px_log_fs_header_rd(px_log_fs_header_t * header, uint16_t page)
261{
262 uint8_t crc;
263
264 // Read page header
265 px_log_fs_glue_rd(header,
266 page,
267 0,
268 sizeof(px_log_fs_header_t));
269 // Page FREE or BAD?
270 if( px_log_fs_marker_is_free(header->marker)
271 || px_log_fs_marker_is_bad(header->marker ) )
272 {
273 return header->marker;
274 }
275 // Invalid marker?
276 if(!px_log_fs_marker_is_page(header->marker))
277 {
278 // Mark as BAD
279 PX_LOG_E("Invalid marker 0x%02X @ page %u", header->marker, page);
280 header->marker = PX_LOG_FS_MARKER_BAD;
281 px_log_fs_glue_wr(&header->marker, page, 0, 1);
282 return header->marker;
283 }
284 // Check CRC
285 crc = px_log_fs_crc(&header->nr, sizeof(header->nr));
286 if(crc != header->crc)
287 {
288 // Mark as BAD
289 PX_LOG_E("Header CRC check failed @ page %u (0x%02X != 0x%02X)",
290 page, header->crc, crc);
291 header->marker = PX_LOG_FS_MARKER_BAD;
292 px_log_fs_glue_wr(&header->marker, page, 0, 1);
293 return header->marker;
294 }
295 return header->marker;
296}
297
298static uint16_t px_log_fs_header_rd_first(const px_log_fs_handle_t * handle,
299 px_log_fs_header_t * header)
300{
301 uint8_t marker;
302 uint16_t page;
303
304 // Start at first page
305 page = handle->fs_page_start;
306 while(true)
307 {
308 // Read page header. Is it marked?
309 marker = px_log_fs_header_rd(header, page);
310 if(px_log_fs_marker_is_page(marker))
311 {
312 // Marked page found
313 return page;
314 }
315 // Last page read?
316 if(page == handle->fs_page_end)
317 {
318 // Marker not found
319 return PX_LOG_FS_PAGE_INVALID;
320 }
321 // Next page
322 page = px_log_fs_page_next(handle, page);
323 }
324}
325
326static uint16_t px_log_fs_header_rd_next(const px_log_fs_handle_t * handle,
327 px_log_fs_header_t * header,
328 uint16_t page_current)
329{
330 uint8_t marker;
331 uint16_t page;
332
333 // Start at current page
334 page = page_current;
335 while(true)
336 {
337 // Next page
338 page = px_log_fs_page_next(handle, page);
339 // Wrapped all the way around and now back at current page?
340 if(page == page_current)
341 {
342 // Marker not found
343 return PX_LOG_FS_PAGE_INVALID;
344 }
345 // Read page header. Is it a marked page?
346 marker = px_log_fs_header_rd(header, page);
347 if(px_log_fs_marker_is_page(marker))
348 {
349 // Marked page found
350 return page;
351 }
352 }
353}
354
355static bool px_log_fs_header_wr(px_log_fs_header_t * header, uint16_t page)
356{
357 px_log_fs_header_t header_rd;
358
359 // Set CRC
360 header->crc = px_log_fs_crc(&header->nr, sizeof(header->nr));
361 // Write header
362 px_log_fs_glue_wr(header, page, 0, sizeof(*header));
363 // Read back header
364 px_log_fs_glue_rd(&header_rd, page, 0, sizeof(*header));
365 // Match?
366 if(memcmp(header, &header_rd, sizeof(*header)) != 0)
367 {
368 // Mark header as BAD
369 PX_LOG_E("Header write failed @ page %u", page);
370 header->marker = PX_LOG_FS_MARKER_BAD;
371 px_log_fs_glue_wr(&header->marker, page, 0, 1);
372 // Failure
373 return false;
374 }
375 // Success
376 return true;
377}
378
379static uint8_t px_log_fs_record_rd(px_log_fs_record_t * record,
380 const px_log_fs_adr_t * adr)
381{
382 uint8_t crc;
383
384 // Read record
385 px_log_fs_glue_rd(record, adr->page, adr->offset, sizeof(*record));
386 // FREE or BAD?
387 if( px_log_fs_marker_is_free(record->marker)
388 || px_log_fs_marker_is_bad (record->marker) )
389 {
390 return record->marker;
391 }
392 // Marker correct?
393 if(!px_log_fs_marker_is_record(record->marker))
394 {
395 // No. Mark as BAD
396 PX_LOG_W("Invalid record marker %02X @ page %u offset %u",
397 record->marker, adr->page, adr->offset);
398 record->marker = PX_LOG_FS_MARKER_BAD;
399 px_log_fs_glue_wr(&record->marker, adr->page, adr->offset, 1);
400 return record->marker;
401 }
402 // CRC correct?
403 crc = px_log_fs_crc(&record->data, sizeof(record->data));
404 if(crc != record->crc)
405 {
406 // Mark record as BAD
407 PX_LOG_E("Record CRC check failed @ page %u offset %u (0x%02X != 0x%02X)",
408 adr->page, adr->offset, record->crc != crc);
409 record->marker = PX_LOG_FS_MARKER_BAD;
410 px_log_fs_glue_wr(&record->marker, adr->page, adr->offset, 1);
411 return record->marker;
412 }
413 // Record is good
414 return record->marker;
415}
416
417static bool px_log_fs_record_wr(px_log_fs_record_t * record,
418 const px_log_fs_adr_t * adr)
419{
420 px_log_fs_record_t record_rd;
421
422 // Sanity check
424 {
425 PX_LOG_E("Record will overflow the page");
426 return false;
427 }
428
429 // Set marker
430 record->marker = PX_LOG_FS_MARKER_RECORD;
431 // Set CRC
432 record->crc = px_log_fs_crc(&record->data, sizeof(record->data));
433 // Write record
434 px_log_fs_glue_wr(record, adr->page, adr->offset, sizeof(*record));
435 // Read back record
436 px_log_fs_glue_rd(&record_rd, adr->page, adr->offset, sizeof(*record));
437 // Match?
438 if(memcmp(record, &record_rd, sizeof(*record)) != 0)
439 {
440 // Mark record as BAD
441 PX_LOG_E("Record write failed @ page %u offset %u", adr->page, adr->offset);
442 record->marker = PX_LOG_FS_MARKER_BAD;
443 px_log_fs_glue_wr(&record->marker, adr->page, adr->offset, 1);
444 // Failure
445 return false;
446 }
447 // Success
448 return true;
449}
450
451/* _____GLOBAL FUNCTIONS_____________________________________________________ */
453 uint16_t fs_page_start,
454 uint16_t fs_page_end)
455{
456 px_log_fs_header_t header;
457 px_log_fs_record_t record;
458 uint16_t page_first;
459 uint16_t page;
460 uint16_t page_next;
461 uint16_t page_nr;
462 uint16_t page_nr_next;
463 uint16_t page_nr_diff;
464 uint16_t page_nr_diff_largest = 0;
465
466 // Sanity checks
467 PX_LOG_ASSERT(fs_page_start != PX_LOG_FS_PAGE_INVALID); // Must not be reserved invalid value
468 PX_LOG_ASSERT(fs_page_end != PX_LOG_FS_PAGE_INVALID); // Must not be reserved invalid value
469 PX_LOG_ASSERT((fs_page_start % PX_LOG_FS_CFG_ERASE_BLOCK_SIZE) == 0); // Must be at the start of an erase block
470 PX_LOG_ASSERT(((fs_page_end + 1) % PX_LOG_FS_CFG_ERASE_BLOCK_SIZE) == 0); // Must be at the end of an erase block
471 PX_LOG_ASSERT(((fs_page_end + 1 - fs_page_start) / PX_LOG_FS_CFG_ERASE_BLOCK_SIZE) >= 2); // Must be at least 2 erase blocks in size (more is better)
472 PX_LOG_ASSERT(sizeof(px_log_fs_record_t) <= PX_LOG_FS_PAGE_DATA_SIZE); // A record must fit in a page
473 if(PX_LOG_FS_PAGE_DATA_SIZE % sizeof(px_log_fs_record_t) != 0)
474 {
475 PX_LOG_W("%u bytes will be wasted per page", PX_LOG_FS_PAGE_DATA_SIZE % sizeof(px_log_fs_record_t));
476 }
477
478 // Save file system start and end page
479 handle->fs_page_start = fs_page_start;
480 handle->fs_page_end = fs_page_end;
481 // Read first marked page
482 page_first = px_log_fs_header_rd_first(handle, &header);
483 if(page_first == PX_LOG_FS_PAGE_INVALID)
484 {
485 // None found. File system is empty
486 PX_LOG_D("No marked pages found");
487 handle->page_first = PX_LOG_FS_PAGE_INVALID;
488 handle->page_last = PX_LOG_FS_PAGE_INVALID;
489 handle->page_nr_next = 0;
490 handle->adr_wr.page = handle->fs_page_start;
491 handle->adr_wr.offset = PX_LOG_FS_REC_OFFSET_FIRST;
492 return PX_LOG_FS_ERR_NONE;
493 }
494 // Continue from first marked page...
495 page = page_first;
496 do
497 {
498 // Save page number
499 page_nr = header.nr;
500 // Read next marked page
501 page_next = px_log_fs_header_rd_next(handle, &header, page);
502 if(page_next == PX_LOG_FS_PAGE_INVALID)
503 {
504 // Only one marked page found
505 handle->page_first = page;
506 handle->page_last = page;
507 handle->page_nr_next = ++page_nr;
508 break;
509 }
510 // Calculate difference
511 page_nr_next = header.nr;
512 page_nr_diff = page_nr_next - page_nr;
513 // Largest difference so far?
514 if(page_nr_diff_largest < page_nr_diff)
515 {
516 PX_LOG_D("Diff=%u, page %u (nr %u) , page_next %u (nr %u)",
517 page_nr_diff, page, page_nr, page_next, page_nr_next);
518 // Save largest difference so far
519 page_nr_diff_largest = page_nr_diff;
520 // Save candidates for first and last marked page
521 handle->page_first = page_next;
522 handle->page_last = page;
523 handle->page_nr_next = ++page_nr;
524 }
525 // Next page
526 page = page_next;
527 }
528 while(page != page_first); // Finished?
529
530 // Find first FREE record
531 handle->adr_wr.page = handle->page_last;
532 handle->adr_wr.offset = PX_LOG_FS_REC_OFFSET_FIRST;
533 while(!px_log_fs_marker_is_free(px_log_fs_record_rd(&record, &handle->adr_wr)))
534 {
535 px_log_fs_record_adr_next(handle, &handle->adr_wr);
536 if(handle->adr_wr.offset == PX_LOG_FS_REC_OFFSET_FIRST)
537 {
538 break;
539 }
540 }
541
542 // Report first and last page
543 PX_LOG_D("First marked page %u", handle->page_first);
544 PX_LOG_D("Last marked page %u", handle->page_last);
545 PX_LOG_D("Next page nr %u", handle->page_nr_next);
546
547 return PX_LOG_FS_ERR_NONE;
548}
549
551 uint16_t fs_page_start,
552 uint16_t fs_page_end)
553{
554 px_log_fs_header_t header;
555 uint16_t page;
556 uint8_t marker;
557
558 // Sanity checks
559 PX_LOG_ASSERT(fs_page_start != PX_LOG_FS_PAGE_INVALID); // Must not be reserved invalid value
560 PX_LOG_ASSERT(fs_page_end != PX_LOG_FS_PAGE_INVALID); // Must not be reserved invalid value
561 PX_LOG_ASSERT((fs_page_start % PX_LOG_FS_CFG_ERASE_BLOCK_SIZE) == 0); // Must be at the start of an erase block
562 PX_LOG_ASSERT(((fs_page_end + 1) % PX_LOG_FS_CFG_ERASE_BLOCK_SIZE) == 0); // Must be at the end of an erase block
563 PX_LOG_ASSERT(((fs_page_end + 1 - fs_page_start) / PX_LOG_FS_CFG_ERASE_BLOCK_SIZE) >= 2); // Must be at least 2 erase blocks in size (more is better)
564 PX_LOG_ASSERT(sizeof(px_log_fs_record_t) <= PX_LOG_FS_PAGE_DATA_SIZE); // A record must fit in a page
565 if(PX_LOG_FS_PAGE_DATA_SIZE % sizeof(px_log_fs_record_t) != 0)
566 {
567 PX_LOG_W("%u bytes will be wasted per page", PX_LOG_FS_PAGE_DATA_SIZE % sizeof(px_log_fs_record_t));
568 }
569
570 // Save file system start and end page
571 handle->fs_page_start = fs_page_start;
572 handle->fs_page_end = fs_page_end;
573
574 // Mark all used pages as BAD
575 for(page = fs_page_start; page <= fs_page_end; page++)
576 {
577 marker = px_log_fs_header_rd(&header, page);
578 if(px_log_fs_marker_is_page(marker))
579 {
580 header.marker = PX_LOG_FS_MARKER_BAD;
581 px_log_fs_glue_wr(&header.marker, page, 0, 1);
582 }
583 }
584 // Find first FREE page on the start of an erase block
585 for(page = fs_page_start; page <= fs_page_end; page += PX_LOG_FS_CFG_ERASE_BLOCK_SIZE)
586 {
587 if(px_log_fs_marker_is_free(px_log_fs_header_rd(&header, page)))
588 {
589 break;
590 }
591 }
592 // No FREE page found?
593 if(page > fs_page_end)
594 {
595 // Start at beginning
596 page = fs_page_start;
597 }
598 // Reset fields
599 handle->page_first = PX_LOG_FS_PAGE_INVALID;
600 handle->page_last = PX_LOG_FS_PAGE_INVALID;
601 handle->page_nr_next = 0;
602 handle->adr_wr.page = page;
603 handle->adr_wr.offset = PX_LOG_FS_REC_OFFSET_FIRST;
604 return PX_LOG_FS_ERR_NONE;
605}
606
608 void * data,
609 size_t nr_of_bytes)
610{
611 // No marked pages?
612 if(handle->page_first == PX_LOG_FS_PAGE_INVALID)
613 {
614 return PX_LOG_FS_ERR_EMPTY;
615 }
616 // Start before first record
617 handle->adr_rd.page = handle->page_first;
618 handle->adr_rd.offset = PX_LOG_FS_REC_OFFSET_FIRST;
619 px_log_fs_record_adr_previous(handle, &handle->adr_rd);
620 // Return first valid record
621 return px_log_fs_rd_next(handle, data, nr_of_bytes);
622}
623
625 void * data,
626 size_t nr_of_bytes)
627{
628 px_log_fs_header_t header;
629 uint8_t marker;
630
631 // No marked pages?
632 if(handle->page_first == PX_LOG_FS_PAGE_INVALID)
633 {
634 return PX_LOG_FS_ERR_EMPTY;
635 }
636 // Find first unarchived page with records
637 handle->adr_rd.page = handle->page_first;
638 handle->adr_rd.offset = PX_LOG_FS_REC_OFFSET_FIRST;
639 while(handle->adr_rd.page != handle->adr_wr.page)
640 {
641 // Read page marker
642 marker = px_log_fs_header_rd(&header, handle->adr_rd.page);
643 // First unarchived page?
644 if(marker == PX_LOG_FS_MARKER_PAGE)
645 {
646 // First unarchived page found. Stop
647 break;
648 }
649 else if(marker == PX_LOG_FS_MARKER_FREE)
650 {
651 PX_LOG_E("Unexpected free page found");
652 return PX_LOG_FS_ERR_FATAL;
653 }
654 // Next page
655 handle->adr_rd.page = px_log_fs_page_next(handle, handle->adr_rd.page);
656 }
657 // Start before first record
658 px_log_fs_record_adr_previous(handle, &handle->adr_rd);
659 // Find first unarchived record
660 do
661 {
662 // Read next record
663 if(px_log_fs_rd_next(handle, data, nr_of_bytes) != PX_LOG_FS_ERR_NONE)
664 {
665 // No unarchived records found
667 }
668 }
669 while(handle->archive_flag);
670
671 // Success
672 return PX_LOG_FS_ERR_NONE;
673}
674
676 void * data,
677 size_t nr_of_bytes)
678{
679 px_log_fs_header_t header;
680 px_log_fs_record_t record;
681 uint8_t marker;
682
683 // More bytes requested than can be stored in record?
684 if(nr_of_bytes > PX_LOG_FS_CFG_REC_DATA_SIZE)
685 {
686 PX_LOG_E("More bytes requested than can be stored in a record");
687 // Clip number of bytes that will be copied
688 nr_of_bytes = PX_LOG_FS_CFG_REC_DATA_SIZE;
689 }
690
691 // Find next valid record
692 while(true)
693 {
694 // Next record address
695 px_log_fs_record_adr_next(handle, &handle->adr_rd);
696 // Has last record been read?
697 if( (handle->adr_rd.page == handle->adr_wr.page )
698 &&(handle->adr_rd.offset == handle->adr_wr.offset) )
699 {
700 // Stop
701 px_log_fs_record_adr_previous(handle, &handle->adr_rd);
703 }
704 // Next page?
705 if(handle->adr_rd.offset == PX_LOG_FS_REC_OFFSET_FIRST)
706 {
707 // Find next marked page
708 while(!px_log_fs_marker_is_page(px_log_fs_header_rd(&header, handle->adr_rd.page)))
709 {
710 if(handle->adr_rd.page == handle->page_last)
711 {
712 PX_LOG_E("File system must be corrupt");
713 return PX_LOG_FS_ERR_FATAL;
714 }
715 handle->adr_rd.page = px_log_fs_page_next(handle, handle->adr_rd.page);
716 }
717 }
718 // Read record
719 marker = px_log_fs_record_rd(&record, &handle->adr_rd);
720 if(px_log_fs_marker_is_record(marker))
721 {
722 // Archived?
723 if(marker == PX_LOG_FS_MARKER_RECORD_A)
724 {
725 handle->archive_flag = true;
726 }
727 else
728 {
729 handle->archive_flag = false;
730 }
731 // Copy content of record to user supplied buffer
732 memcpy(data, &record.data, nr_of_bytes);
733 // Valid record
734 return PX_LOG_FS_ERR_NONE;
735 }
736 }
737}
738
740 void * data,
741 size_t nr_of_bytes)
742{
743 // No marked pages?
744 if(handle->page_first == PX_LOG_FS_PAGE_INVALID)
745 {
746 return PX_LOG_FS_ERR_EMPTY;
747 }
748 // Start after last record
749 handle->adr_rd.page = handle->adr_wr.page;
750 handle->adr_rd.offset = handle->adr_wr.offset;
751 // Return last valid record
752 return px_log_fs_rd_previous(handle, data, nr_of_bytes);
753}
754
756 void * data,
757 size_t nr_of_bytes)
758{
759 px_log_fs_header_t header;
760 px_log_fs_record_t record;
761 uint8_t marker;
762
763 // More bytes requested than can be stored in record?
764 if(nr_of_bytes > PX_LOG_FS_CFG_REC_DATA_SIZE)
765 {
766 PX_LOG_E("More bytes requested than can be stored in a record");
767 // Clip number of bytes that will be copied
768 nr_of_bytes = PX_LOG_FS_CFG_REC_DATA_SIZE;
769 }
770
771 // Find previous valid record
772 while(true)
773 {
774 // Has first record been read?
775 if( (handle->adr_rd.page == handle->page_first )
776 &&(handle->adr_rd.offset == PX_LOG_FS_REC_OFFSET_FIRST) )
777 {
778 // Stop
780 }
781 // Previous record address
782 px_log_fs_record_adr_previous(handle, &handle->adr_rd);
783 // Previous page?
784 if(handle->adr_rd.offset == PX_LOG_FS_REC_OFFSET_LAST)
785 {
786 // Find previous marked page
787 while(!px_log_fs_marker_is_page(px_log_fs_header_rd(&header, handle->adr_rd.page)))
788 {
789 if(handle->adr_rd.page == handle->page_first)
790 {
791 PX_LOG_E("File system must be corrupt");
792 return PX_LOG_FS_ERR_FATAL;
793 }
794 handle->adr_rd.page = px_log_fs_page_previous(handle, handle->adr_rd.page);
795 }
796 }
797 // Read record
798 marker = px_log_fs_record_rd(&record, &handle->adr_rd);
799 if(px_log_fs_marker_is_record(marker))
800 {
801 // Archived?
802 if(marker == PX_LOG_FS_MARKER_RECORD_A)
803 {
804 handle->archive_flag = true;
805 }
806 else
807 {
808 handle->archive_flag = false;
809 }
810 // Copy content of record to user supplied buffer
811 memcpy(data, &record.data, nr_of_bytes);
812 // Valid record
813 return PX_LOG_FS_ERR_NONE;
814 }
815 }
816}
817
819{
820 uint8_t marker;
821 px_log_fs_adr_t adr;
822
823 // Sanity check
824 if(handle->archive_flag)
825 {
826 PX_LOG_W("Record is already archived");
827 return PX_LOG_FS_ERR_NONE;
828 }
829 // Read record marker first (sanity check)
830 marker = px_log_fs_marker_rd(&handle->adr_rd);
831 if(marker == PX_LOG_FS_MARKER_RECORD_A)
832 {
833 PX_LOG_E("Record is already archived but archive flag was not set");
834 return PX_LOG_FS_ERR_NONE;
835 }
836 else if(marker != PX_LOG_FS_MARKER_RECORD)
837 {
838 PX_LOG_E("Invalid marker");
840 }
841 // Write record marker
842 if(!px_log_fs_marker_wr(PX_LOG_FS_MARKER_RECORD_A, &handle->adr_rd))
843 {
844 PX_LOG_E("Failed to write archive marker");
846 }
847 // Is this the last record in the page?
848 adr.page = handle->adr_rd.page;
849 adr.offset = handle->adr_rd.offset;
850 while(true)
851 {
852 // Next record address
853 px_log_fs_record_adr_next(handle, &adr);
854 // Next page?
855 if(adr.offset == PX_LOG_FS_REC_OFFSET_FIRST)
856 {
857 // This means that last unarchived record on page has been marked as archived.
858 // Mark whole page as archived.
859 adr.page = handle->adr_rd.page;
860 adr.offset = 0;
861 if(!px_log_fs_marker_wr(PX_LOG_FS_MARKER_PAGE_A, &adr))
862 {
863 PX_LOG_E("Failed to mark page as archived. Lost whole page of records!");
865 }
866 // Success
867 return PX_LOG_FS_ERR_NONE;
868 }
869 // Has last record been checked?
870 if( (adr.page == handle->adr_wr.page )
871 &&(adr.offset == handle->adr_wr.offset) )
872 {
873 // Do nothing
874 return PX_LOG_FS_ERR_NONE;
875 }
876 // Read record marker
877 marker = px_log_fs_marker_rd(&adr);
878 if(marker == PX_LOG_FS_MARKER_RECORD)
879 {
880 // Do nothing. There are still unarchived records in page.
881 return PX_LOG_FS_ERR_NONE;
882 }
883 else if(marker == PX_LOG_FS_MARKER_RECORD_A)
884 {
885 // Error! Records must be archived from oldest to newest
886 PX_LOG_E("Out of sequence archived record detected");
887 return PX_LOG_FS_ERR_NONE;
888 }
889 else if(marker == PX_LOG_FS_MARKER_FREE)
890 {
891 // Do nothing. There is still free space in page.
892 return PX_LOG_FS_ERR_NONE;
893 }
894 }
895}
896
898 const void * data,
899 size_t nr_of_bytes)
900{
901 px_log_fs_header_t header;
902 px_log_fs_record_t record;
903 uint16_t page;
904 uint8_t marker;
905
906 // Record too small?
907 if(nr_of_bytes > PX_LOG_FS_CFG_REC_DATA_SIZE)
908 {
909 PX_LOG_E("Record size too small and %u bytes will be discarded",
910 (nr_of_bytes - PX_LOG_FS_CFG_REC_DATA_SIZE));
911 // Clip number of bytes that will be copied
912 nr_of_bytes = PX_LOG_FS_CFG_REC_DATA_SIZE;
913 }
914 // Set unused data to 0xFF
915 memset(&record.data, 0xff, sizeof(record.data));
916 // Copy data
917 memcpy(&record.data, data, nr_of_bytes);
918
919 // New page?
920 if(handle->adr_wr.offset == PX_LOG_FS_REC_OFFSET_FIRST)
921 {
922 // Find next FREE page
923 while(!px_log_fs_marker_is_free(px_log_fs_header_rd(&header, handle->adr_wr.page)))
924 {
925 // Next page
926 handle->adr_wr.page = px_log_fs_page_next(handle, handle->adr_wr.page);
927#if PX_LOG_FS_CFG_STOP_WR_WHEN_FULL
928 if(handle->adr_wr.page == handle->page_first)
929 {
930 return PX_LOG_FS_ERR_FULL;
931 }
932#endif
933 // Start of new erase block?
934 if((handle->adr_wr.page % PX_LOG_FS_CFG_ERASE_BLOCK_SIZE) == 0)
935 {
936 // Erase block
938 // Has oldest RECORD pages been erased?
939 if( (handle->page_first >= handle->adr_wr.page)
940 &&(handle->page_first < handle->adr_wr.page + PX_LOG_FS_CFG_ERASE_BLOCK_SIZE) )
941 {
942 // Find new first page
944 while(true)
945 {
946 // Is page marked?
947 marker = px_log_fs_header_rd(&header, handle->adr_wr.page);
948 if(px_log_fs_marker_is_page(marker))
949 {
950 // Save new first page
951 handle->page_first = page;
952 PX_LOG_D("New first page is %u", handle->page_first);
953 break;
954 }
955 if(page == handle->page_last)
956 {
957 PX_LOG_E("File system must be corrupt");
958 return PX_LOG_FS_ERR_FATAL;
959 }
960 // Next page
961 page = px_log_fs_page_next(handle, page);
962 }
963 }
964 break;
965 }
966 }
967 // Mark page
968 header.marker = PX_LOG_FS_MARKER_PAGE;
969 header.nr = handle->page_nr_next++;
970 if(!px_log_fs_header_wr(&header, handle->adr_wr.page))
971 {
972 // Header write failed. Try next page
973 handle->adr_wr.page = px_log_fs_page_next(handle, handle->adr_wr.page);
975 }
976 }
977
978 // Write new record
979 if(!px_log_fs_record_wr(&record, &handle->adr_wr))
980 {
981 // Record write failed. Try next record address
982 px_log_fs_record_adr_next(handle, &handle->adr_wr);
984 }
985 // First record?
986 if(handle->page_first == PX_LOG_FS_PAGE_INVALID)
987 {
988 // Save first page
989 handle->page_first = handle->adr_wr.page;
990 }
991 // Update last page
992 handle->page_last = handle->adr_wr.page;
993 // Advance write address to next position
994 px_log_fs_record_adr_next(handle, &handle->adr_wr);
995
996 // Success
997 return PX_LOG_FS_ERR_NONE;
998}
999
1001{
1002 px_log_fs_header_t header;
1003 uint16_t page;
1004 uint8_t i;
1005
1006 printf("Page start: 0x%04X\n", handle->fs_page_start);
1007 printf("Page end: 0x%04X\n", handle->fs_page_end);
1008 printf("Page size: %u\n", PX_LOG_FS_CFG_PAGE_SIZE);
1009 printf("Record size: %u\n", sizeof(px_log_fs_record_t));
1010 printf("Record data size: %u\n", PX_LOG_FS_CFG_REC_DATA_SIZE);
1011 printf("Records per page: %u\n", PX_LOG_FS_RECORDS_PER_PAGE);
1012 printf("First page: 0x%04X\n", handle->page_first);
1013 printf("Last page: 0x%04X\n", handle->page_last);
1014
1015 printf("\n ");
1016 for(i = 0; i < 16; i++)
1017 {
1018 printf("%01X ", i);
1019 }
1020 printf("\n");
1021
1022 i = 0;
1023 for(page = handle->fs_page_start; page <= handle->fs_page_end; page++)
1024 {
1025 if(i == 0)
1026 {
1027 printf("\n0x%04X: ", page);
1028 }
1029 px_log_fs_header_rd(&header, page);
1030 switch(header.marker)
1031 {
1032 case PX_LOG_FS_MARKER_FREE:
1033 printf("x ");
1034 break;
1035 case PX_LOG_FS_MARKER_PAGE:
1036 printf("P %04x ", header.nr);
1037 break;
1038 case PX_LOG_FS_MARKER_PAGE_A:
1039 printf("P*%04x ", header.nr);
1040 break;
1041 case PX_LOG_FS_MARKER_BAD:
1042 // Fall through...
1043 default:
1044 printf("B ");
1045 break;
1046 }
1047 if(++i == 16)
1048 {
1049 i = 0;
1050 }
1051 }
1052 printf("\n");
1053}
uint16_t fs_page_start
First page in file system (must be on the start of an erase block)
Definition: px_log_fs.h:254
uint16_t page
Page.
Definition: px_log_fs.h:248
uint16_t offset
Offset inside page.
Definition: px_log_fs.h:249
uint16_t page_nr_next
Next page number to use (starts at 0)
Definition: px_log_fs.h:258
bool archive_flag
Flag is set if current read record is archived.
Definition: px_log_fs.h:261
px_log_fs_adr_t adr_rd
Current read address.
Definition: px_log_fs.h:260
uint16_t page_last
Last page with records (PX_LOG_FS_PAGE_INVALID if empty)
Definition: px_log_fs.h:257
uint16_t page_first
First page with records (PX_LOG_FS_PAGE_INVALID if empty)
Definition: px_log_fs.h:256
px_log_fs_adr_t adr_wr
Next write address (open position)
Definition: px_log_fs.h:259
uint16_t fs_page_end
Last page in file system (must be on the end of an erase block)
Definition: px_log_fs.h:255
px_log_fs_err_t px_log_fs_reset(px_log_fs_handle_t *handle, uint16_t fs_page_start, uint16_t fs_page_end)
Reset log file system.
Definition: px_log_fs.c:550
void px_log_fs_glue_wr(const void *buffer, uint16_t page, uint16_t start_byte_in_page, uint16_t nr_of_bytes)
Writes data to Serial Flash.
#define PX_LOG_FS_CFG_REC_DATA_SIZE
Record data size (total record size = PX_LOG_FS_CFG_REC_DATA_SIZE + 3 bytes overhead)
px_log_fs_err_t px_log_fs_init(px_log_fs_handle_t *handle, uint16_t fs_page_start, uint16_t fs_page_end)
Initialise log file system.
Definition: px_log_fs.c:452
#define PX_LOG_FS_CFG_ERASE_BLOCK_SIZE
Erase block size (in pages) for file system.
px_log_fs_err_t px_log_fs_rd_next(px_log_fs_handle_t *handle, void *data, size_t nr_of_bytes)
Read next (newer) record.
Definition: px_log_fs.c:675
px_log_fs_err_t px_log_fs_rd_first(px_log_fs_handle_t *handle, void *data, size_t nr_of_bytes)
Read first (oldest) record.
Definition: px_log_fs.c:607
px_log_fs_err_t px_log_fs_rd_previous(px_log_fs_handle_t *handle, void *data, size_t nr_of_bytes)
Read previous (older) record.
Definition: px_log_fs.c:755
px_log_fs_err_t px_log_fs_wr(px_log_fs_handle_t *handle, const void *data, size_t nr_of_bytes)
Write a record to the file.
Definition: px_log_fs.c:897
px_log_fs_err_t
Error codes.
Definition: px_log_fs.h:236
#define PX_LOG_FS_CFG_PAGE_SIZE
Page size for file system.
px_log_fs_err_t px_log_fs_rd_first_unarchived(px_log_fs_handle_t *handle, void *data, size_t nr_of_bytes)
Read first (oldest) unarchived record.
Definition: px_log_fs.c:624
void px_log_fs_dbg_report_info(px_log_fs_handle_t *handle)
Report log file system info.
Definition: px_log_fs.c:1000
void px_log_fs_glue_rd(void *buffer, uint16_t page, uint16_t start_byte_in_page, uint16_t nr_of_bytes)
Read data from Serial Flash.
px_log_fs_err_t px_log_fs_rd_rec_set_archive(px_log_fs_handle_t *handle)
Set currently read record as archived.
Definition: px_log_fs.c:818
void px_log_fs_glue_erase_block(uint16_t page)
Erases data in Serial Flash.
px_log_fs_err_t px_log_fs_rd_last(px_log_fs_handle_t *handle, void *data, size_t nr_of_bytes)
Read last (newest) record.
Definition: px_log_fs.c:739
@ PX_LOG_FS_ERR_NONE
No error.
Definition: px_log_fs.h:237
@ PX_LOG_FS_ERR_WRITE_FAIL
Failed to write.
Definition: px_log_fs.h:240
@ PX_LOG_FS_ERR_NO_RECORD
No record found.
Definition: px_log_fs.h:241
@ PX_LOG_FS_ERR_EMPTY
File is empty.
Definition: px_log_fs.h:238
@ PX_LOG_FS_ERR_FATAL
Fatal file system error.
Definition: px_log_fs.h:242
@ PX_LOG_FS_ERR_FULL
File is full.
Definition: px_log_fs.h:239
Specification of data address in Serial Flash.
Definition: px_log_fs.h:247
#define PX_LOG_D(format,...)
Macro to display a formatted DEBUG message.
Definition: px_log.h:410
#define PX_LOG_NAME(name)
Macro to declare a log name string once for each file to reduce code size.
Definition: px_log.h:349
#define PX_LOG_W(format,...)
Macro to display a formatted WARNING message.
Definition: px_log.h:388
#define PX_LOG_E(format,...)
Macro to display a formatted ERROR message.
Definition: px_log.h:377
#define PX_LOG_ASSERT(expression)
Macro that will test an expression, and block indefinitely if false.
Definition: px_log.h:440
Definition of header structure that is stored at the start of each page.
Definition: px_log_fs.c:46
uint8_t marker
FREE, PAGE, PAGE_A or BAD.
Definition: px_log_fs.c:47
uint8_t crc
Checksum calculated over nr (excludes marker)
Definition: px_log_fs.c:49
uint16_t nr
Rolling number to find first and last marked page.
Definition: px_log_fs.c:48
Definition of a record structure.
Definition: px_log_fs.c:54
uint8_t data[PX_LOG_FS_CFG_REC_DATA_SIZE]
Record data content.
Definition: px_log_fs.c:56
uint8_t marker
RECORD or RECORD_A.
Definition: px_log_fs.c:55
uint8_t crc
Checksum calculated over data (excludes marker)
Definition: px_log_fs.c:57