ruuvi.drivers.c  ${PROJECT_VERSION}
Drivers for external sensors and peripherals on embedded systems.
fds.c
Go to the documentation of this file.
1 
40 #include "sdk_common.h"
41 #if NRF_MODULE_ENABLED(FDS)
42 #include "fds.h"
43 #include "fds_internal_defs.h"
44 
45 #include <stdint.h>
46 #include <string.h>
47 #include <stdbool.h>
48 #include "nrf_error.h"
49 #include "nrf_atomic.h"
50 #include "nrf_atfifo.h"
51 
52 #include "nrf_fstorage.h"
53 #if (FDS_BACKEND == NRF_FSTORAGE_SD)
54 #include "nrf_fstorage_sd.h"
55 #elif (FDS_BACKEND == NRF_FSTORAGE_NVMC)
56 #include "nrf_fstorage_nvmc.h"
57 #else
58 #error Invalid FDS backend.
59 #endif
60 
61 #if (FDS_CRC_CHECK_ON_READ)
62 #include "crc16.h"
63 #endif
64 
65 
66 static void fs_event_handler(nrf_fstorage_evt_t * evt);
67 
68 NRF_FSTORAGE_DEF(nrf_fstorage_t m_fs) =
69 {
70  // The flash area boundaries are set in fds_init().
71  .evt_handler = fs_event_handler,
72 };
73 
74 // Internal status flags.
75 static struct
76 {
77  bool volatile initialized;
78  nrf_atomic_flag_t initializing;
79 } m_flags;
80 
81 // The number of queued operations.
82 // Incremented by queue_start() and decremented by queue_has_next().
83 static nrf_atomic_u32_t m_queued_op_cnt;
84 
85 // The number of registered users and their callback functions.
86 static nrf_atomic_u32_t m_users;
87 static fds_cb_t m_cb_table[FDS_MAX_USERS];
88 
89 // The latest (largest) record ID written so far.
90 static nrf_atomic_u32_t m_latest_rec_id;
91 
92 // Queue of fds operations.
93 NRF_ATFIFO_DEF(m_queue, fds_op_t, FDS_OP_QUEUE_SIZE);
94 
95 // Structures used to hold informations about virtual pages.
96 static fds_page_t m_pages[FDS_DATA_PAGES];
97 static fds_swap_page_t m_swap_page;
98 
99 // Garbage collection data.
100 static fds_gc_data_t m_gc;
101 
102 
103 static void event_send(fds_evt_t const * const p_evt)
104 {
105  for (uint32_t user = 0; user < FDS_MAX_USERS; user++)
106  {
107  if (m_cb_table[user] != NULL)
108  {
109  m_cb_table[user](p_evt);
110  }
111  }
112 }
113 
114 
115 static void event_prepare(fds_op_t const * const p_op, fds_evt_t * const p_evt)
116 {
117  switch (p_op->op_code)
118  {
119  case FDS_OP_INIT:
120  p_evt->id = FDS_EVT_INIT;
121  break;
122 
123  case FDS_OP_WRITE:
124  p_evt->id = FDS_EVT_WRITE;
125  p_evt->write.file_id = p_op->write.header.file_id;
126  p_evt->write.record_key = p_op->write.header.record_key;
127  p_evt->write.record_id = p_op->write.header.record_id;
128  p_evt->write.is_record_updated = 0;
129  break;
130 
131  case FDS_OP_UPDATE:
132  p_evt->id = FDS_EVT_UPDATE;
133  p_evt->write.file_id = p_op->write.header.file_id;
134  p_evt->write.record_key = p_op->write.header.record_key;
135  p_evt->write.record_id = p_op->write.header.record_id;
136  p_evt->write.is_record_updated = (p_op->write.step == FDS_OP_WRITE_DONE);
137  break;
138 
139  case FDS_OP_DEL_RECORD:
140  p_evt->id = FDS_EVT_DEL_RECORD;
141  p_evt->del.file_id = p_op->del.file_id;
142  p_evt->del.record_key = p_op->del.record_key;
143  p_evt->del.record_id = p_op->del.record_to_delete;
144  break;
145 
146  case FDS_OP_DEL_FILE:
147  p_evt->id = FDS_EVT_DEL_FILE;
148  p_evt->del.file_id = p_op->del.file_id;
149  p_evt->del.record_key = FDS_RECORD_KEY_DIRTY;
150  p_evt->del.record_id = 0;
151  break;
152 
153  case FDS_OP_GC:
154  p_evt->id = FDS_EVT_GC;
155  break;
156 
157  default:
158  // Should not happen.
159  break;
160  }
161 }
162 
163 
164 static bool header_has_next(fds_header_t const * p_hdr, uint32_t const * p_page_end)
165 {
166  uint32_t const * const p_hdr32 = (uint32_t*)p_hdr;
167  return ( ( p_hdr32 < p_page_end)
168  && (*p_hdr32 != FDS_ERASED_WORD)); // Check last to be on the safe side (dereference)
169 }
170 
171 
172 // Jump to the next header.
173 static fds_header_t const * header_jump(fds_header_t const * const p_hdr)
174 {
175  return (fds_header_t*)((uint32_t*)p_hdr + FDS_HEADER_SIZE + p_hdr->length_words);
176 }
177 
178 
179 static fds_header_status_t header_check(fds_header_t const * p_hdr, uint32_t const * p_page_end)
180 {
181  if (((uint32_t*)header_jump(p_hdr) > p_page_end))
182  {
183  // The length field would jump across the page boundary.
184  // FDS won't allow writing such a header, therefore it has been corrupted.
185  return FDS_HEADER_CORRUPT;
186  }
187 
188  if ( (p_hdr->file_id == FDS_FILE_ID_INVALID)
189  || (p_hdr->record_key == FDS_RECORD_KEY_DIRTY))
190  {
191  return FDS_HEADER_DIRTY;
192  }
193 
194  return FDS_HEADER_VALID;
195 }
196 
197 
198 static bool address_is_valid(uint32_t const * const p_addr)
199 {
200  return ((p_addr != NULL) &&
201  (p_addr >= (uint32_t*)m_fs.start_addr) &&
202  (p_addr <= (uint32_t*)m_fs.end_addr) &&
203  (is_word_aligned(p_addr)));
204 }
205 
206 
207 // Reads a page tag, and determines if the page is used to store data or as swap.
208 static fds_page_type_t page_identify(uint32_t const * const p_page_addr)
209 {
210  if ( (p_page_addr == NULL) // Should never happen.
211  || (p_page_addr[FDS_PAGE_TAG_WORD_0] != FDS_PAGE_TAG_MAGIC))
212  {
213  return FDS_PAGE_UNDEFINED;
214  }
215 
216  switch (p_page_addr[FDS_PAGE_TAG_WORD_1])
217  {
218  case FDS_PAGE_TAG_SWAP:
219  return FDS_PAGE_SWAP;
220 
221  case FDS_PAGE_TAG_DATA:
222  return FDS_PAGE_DATA;
223 
224  default:
225  return FDS_PAGE_UNDEFINED;
226  }
227 }
228 
229 
230 static bool page_is_erased(uint32_t const * const p_page_addr)
231 {
232  for (uint32_t i = 0; i < FDS_PAGE_SIZE; i++)
233  {
234  if (*(p_page_addr + i) != FDS_ERASED_WORD)
235  {
236  return false;
237  }
238  }
239 
240  return true;
241 }
242 
243 
244 // NOTE: Must be called from within a critical section.
245 static bool page_has_space(uint16_t page, uint16_t length_words)
246 {
247  length_words += m_pages[page].write_offset;
248  length_words += m_pages[page].words_reserved;
249  return (length_words < FDS_PAGE_SIZE);
250 }
251 
252 
253 // Given a pointer to a record, find the index of the page on which it is stored.
254 // Returns FDS_SUCCESS if the page is found, FDS_ERR_NOT_FOUND otherwise.
255 static ret_code_t page_from_record(uint16_t * const p_page, uint32_t const * const p_rec)
256 {
257  ret_code_t ret = FDS_ERR_NOT_FOUND;
258 
260  for (uint16_t i = 0; i < FDS_DATA_PAGES; i++)
261  {
262  if ((p_rec > m_pages[i].p_addr) &&
263  (p_rec < m_pages[i].p_addr + FDS_PAGE_SIZE))
264  {
265  ret = FDS_SUCCESS;
266  *p_page = i;
267  break;
268  }
269  }
271 
272  return ret;
273 }
274 
275 
276 // Scan a page to determine how many words have been written to it.
277 // This information is used to set the page write offset during initialization.
278 // Additionally, this function updates the latest record ID as it proceeds.
279 // If an invalid record header is found, the can_gc argument is set to true.
280 static void page_scan(uint32_t const * p_addr,
281  uint16_t * const words_written,
282  bool * const can_gc)
283 {
284  uint32_t const * const p_page_end = p_addr + FDS_PAGE_SIZE;
285 
286  p_addr += FDS_PAGE_TAG_SIZE;
287  *words_written = FDS_PAGE_TAG_SIZE;
288 
289  fds_header_t const * p_header = (fds_header_t*)p_addr;
290 
291  while (header_has_next(p_header, p_page_end))
292  {
293  fds_header_status_t hdr = header_check(p_header, p_page_end);
294 
295  if (hdr == FDS_HEADER_VALID)
296  {
297  // Update the latest (largest) record ID.
298  if (p_header->record_id > m_latest_rec_id)
299  {
300  m_latest_rec_id = p_header->record_id;
301  }
302  }
303  else
304  {
305  if (can_gc != NULL)
306  {
307  *can_gc = true;
308  }
309 
310  if (hdr == FDS_HEADER_CORRUPT)
311  {
312  // It could happen that a record has a corrupt header which would set a
313  // wrong offset for this page. In such cases, update this value to its maximum,
314  // to ensure that no new records will be written to this page and to enable
315  // correct statistics reporting by fds_stat().
316  *words_written = FDS_PAGE_SIZE;
317 
318  // We can't continue to scan this page.
319  return;
320  }
321  }
322 
323  *words_written += (FDS_HEADER_SIZE + p_header->length_words);
324  p_header = header_jump(p_header);
325  }
326 }
327 
328 
329 static void page_offsets_update(fds_page_t * const p_page, fds_op_t const * p_op)
330 {
331  // If the first part of the header has been written correctly, update the offset as normal.
332  // Even if the record has not been written completely, fds is still able to continue normal
333  // operation. Incomplete records will be deleted the next time garbage collection is run.
334  // If we failed at the very beginning of the write operation, restore the offset
335  // to the previous value so that no holes will be left in the flash.
336  if (p_op->write.step > FDS_OP_WRITE_RECORD_ID)
337  {
338  p_page->write_offset += (FDS_HEADER_SIZE + p_op->write.header.length_words);
339  }
340 
341  p_page->words_reserved -= (FDS_HEADER_SIZE + p_op->write.header.length_words);
342 }
343 
344 
345 // Tags a page as swap, i.e., reserved for GC.
346 static ret_code_t page_tag_write_swap(void)
347 {
348  // The tag needs to be statically allocated since it is not buffered by fstorage.
349  static uint32_t const page_tag_swap[] = {FDS_PAGE_TAG_MAGIC, FDS_PAGE_TAG_SWAP};
350  return nrf_fstorage_write(&m_fs, (uint32_t)m_swap_page.p_addr, page_tag_swap, FDS_PAGE_TAG_SIZE * sizeof(uint32_t), NULL);
351 }
352 
353 
354 // Tags a page as data, i.e, ready for storage.
355 static ret_code_t page_tag_write_data(uint32_t const * const p_page_addr)
356 {
357  // The tag needs to be statically allocated since it is not buffered by fstorage.
358  static uint32_t const page_tag_data[] = {FDS_PAGE_TAG_MAGIC, FDS_PAGE_TAG_DATA};
359  return nrf_fstorage_write(&m_fs, (uint32_t)p_page_addr, page_tag_data, FDS_PAGE_TAG_SIZE * sizeof(uint32_t), NULL);
360 }
361 
362 
363 // Reserve space on a page.
364 // NOTE: this function takes into the account the space required for the record header.
365 static ret_code_t write_space_reserve(uint16_t length_words, uint16_t * p_page)
366 {
367  bool space_reserved = false;
368  uint16_t const total_len_words = length_words + FDS_HEADER_SIZE;
369 
370  if (total_len_words >= FDS_PAGE_SIZE - FDS_PAGE_TAG_SIZE)
371  {
373  }
374 
376  for (uint16_t page = 0; page < FDS_DATA_PAGES; page++)
377  {
378  if ((m_pages[page].page_type == FDS_PAGE_DATA) &&
379  (page_has_space(page, total_len_words)))
380  {
381  space_reserved = true;
382  *p_page = page;
383 
384  m_pages[page].words_reserved += total_len_words;
385  break;
386  }
387  }
389 
390  return (space_reserved) ? FDS_SUCCESS : FDS_ERR_NO_SPACE_IN_FLASH;
391 }
392 
393 
394 // Undo a write_space_reserve() call.
395 // NOTE: Must be called within a critical section.
396 static void write_space_free(uint16_t length_words, uint16_t page)
397 {
398  m_pages[page].words_reserved -= (length_words + FDS_HEADER_SIZE);
399 }
400 
401 
402 static uint32_t record_id_new(void)
403 {
404  return nrf_atomic_u32_add(&m_latest_rec_id, 1);
405 }
406 
407 
408 // Given a page and a record, find the next valid record on that page.
409 // If p_record is NULL, search from the beginning of the page,
410 // otherwise, resume searching from p_record.
411 // Return true if a record is found, false otherwise.
412 // If no record is found, p_record is unchanged.
413 static bool record_find_next(uint16_t page, uint32_t const ** p_record)
414 {
415  uint32_t const * p_page_end = (m_pages[page].p_addr + FDS_PAGE_SIZE);
416 
417  // If this is the first call on this page, start searching from its beginning.
418  // Otherwise, jump to the next record.
419  fds_header_t const * p_header = (fds_header_t*)(*p_record);
420 
421  if (p_header != NULL)
422  {
423  p_header = header_jump(p_header);
424  }
425  else
426  {
427  p_header = (fds_header_t*)(m_pages[page].p_addr + FDS_PAGE_TAG_SIZE);
428  }
429 
430  // Read records from the page until:
431  // - a valid record is found or
432  // - the last record on a page is found
433 
434  while (header_has_next(p_header, p_page_end))
435  {
436  switch (header_check(p_header, p_page_end))
437  {
438  case FDS_HEADER_VALID:
439  *p_record = (uint32_t*)p_header;
440  return true;
441 
442  case FDS_HEADER_DIRTY:
443  p_header = header_jump(p_header);
444  break;
445 
446  case FDS_HEADER_CORRUPT:
447  // We can't reliably jump over this record.
448  // There is nothing more we can do on this page.
449  return false;
450  }
451  }
452 
453  // No more valid records on this page.
454  return false;
455 }
456 
457 
458 // Find a record given its descriptor and retrive the page in which the record is stored.
459 // NOTE: Do not pass NULL as an argument for p_page.
460 static bool record_find_by_desc(fds_record_desc_t * const p_desc, uint16_t * const p_page)
461 {
462  // If the gc_run_count field in the descriptor matches our counter, then the record has
463  // not been moved. If the address is valid, and the record ID matches, there is no need
464  // to find the record again. Only lookup the page in which the record is stored.
465 
466  if ((address_is_valid(p_desc->p_record)) &&
467  (p_desc->gc_run_count == m_gc.run_count) &&
468  (p_desc->record_id == ((fds_header_t*)p_desc->p_record)->record_id))
469  {
470  return (page_from_record(p_page, p_desc->p_record) == FDS_SUCCESS);
471  }
472 
473  // Otherwise, find the record in flash.
474  for (*p_page = 0; *p_page < FDS_DATA_PAGES; (*p_page)++)
475  {
476  // Set p_record to NULL to make record_find_next() search from the beginning of the page.
477  uint32_t const * p_record = NULL;
478 
479  while (record_find_next(*p_page, &p_record))
480  {
481  fds_header_t const * const p_header = (fds_header_t*)p_record;
482  if (p_header->record_id == p_desc->record_id)
483  {
484  p_desc->p_record = p_record;
485  p_desc->gc_run_count = m_gc.run_count;
486  return true;
487  }
488  }
489  }
490 
491  return false;
492 }
493 
494 
495 // Search for a record and return its descriptor.
496 // If p_file_id is NULL, only the record key will be used for matching.
497 // If p_record_key is NULL, only the file ID will be used for matching.
498 // If both are NULL, it will iterate through all records.
499 static ret_code_t record_find(uint16_t const * p_file_id,
500  uint16_t const * p_record_key,
501  fds_record_desc_t * p_desc,
502  fds_find_token_t * p_token)
503 {
504  if (!m_flags.initialized)
505  {
507  }
508 
509  if (p_desc == NULL || p_token == NULL)
510  {
511  return FDS_ERR_NULL_ARG;
512  }
513 
514  // Begin (or resume) searching for a record.
515  for (; p_token->page < FDS_DATA_PAGES; p_token->page++)
516  {
517  if (m_pages[p_token->page].page_type != FDS_PAGE_DATA)
518  {
519  // It might be that the page is FDS_PAGE_UNDEFINED.
520  // Skip this page.
521  continue;
522  }
523 
524  while (record_find_next(p_token->page, &p_token->p_addr))
525  {
526  fds_header_t const * p_header = (fds_header_t*)p_token->p_addr;
527 
528  // A valid record was found, check its header for a match.
529  if ((p_file_id != NULL) &&
530  (p_header->file_id != *p_file_id))
531  {
532  continue;
533  }
534 
535  if ((p_record_key != NULL) &&
536  (p_header->record_key != *p_record_key))
537  {
538  continue;
539  }
540 
541  // Record found; update the descriptor.
542  p_desc->record_id = p_header->record_id;
543  p_desc->p_record = p_token->p_addr;
544  p_desc->gc_run_count = m_gc.run_count;
545 
546  return FDS_SUCCESS;
547  }
548 
549  // We have scanned an entire page. Set the address in the token to NULL
550  // so that it will be updated in the next iteration.
551  p_token->p_addr = NULL;
552  }
553 
554  return FDS_ERR_NOT_FOUND;
555 }
556 
557 
558 // Retrieve statistics about dirty records on a page.
559 static void records_stat(uint16_t page,
560  uint16_t * p_valid_records,
561  uint16_t * p_dirty_records,
562  uint16_t * p_freeable_words,
563  bool * p_corruption)
564 {
565  fds_header_t const * p_header = (fds_header_t*)(m_pages[page].p_addr + FDS_PAGE_TAG_SIZE);
566  uint32_t const * const p_page_end = (m_pages[page].p_addr + FDS_PAGE_SIZE);
567 
568  while (header_has_next(p_header, p_page_end))
569  {
570  switch (header_check(p_header, p_page_end))
571  {
572  case FDS_HEADER_DIRTY:
573  *p_dirty_records += 1;
574  *p_freeable_words += FDS_HEADER_SIZE + p_header->length_words;
575  p_header = header_jump(p_header);
576  break;
577 
578  case FDS_HEADER_VALID:
579  *p_valid_records += 1;
580  p_header = header_jump(p_header);
581  break;
582 
583  case FDS_HEADER_CORRUPT:
584  {
585  *p_dirty_records += 1;
586  *p_freeable_words += (p_page_end - (uint32_t*)p_header);
587  *p_corruption = true;
588  // We can't continue on this page.
589  return;
590  }
591 
592  default:
593  break;
594  }
595  }
596 }
597 
598 
599 // Get a buffer on the queue of operations.
600 static fds_op_t * queue_buf_get(nrf_atfifo_item_put_t * p_iput_ctx)
601 {
602  fds_op_t * const p_op = (fds_op_t*) nrf_atfifo_item_alloc(m_queue, p_iput_ctx);
603 
604  memset(p_op, 0x00, sizeof(fds_op_t));
605  return p_op;
606 }
607 
608 
609 // Commit a buffer to the queue of operations.
610 static void queue_buf_store(nrf_atfifo_item_put_t * p_iput_ctx)
611 {
612  (void) nrf_atfifo_item_put(m_queue, p_iput_ctx);
613 }
614 
615 
616 // Load the next operation from the queue.
617 static fds_op_t * queue_load(nrf_atfifo_item_get_t * p_iget_ctx)
618 {
619  return (fds_op_t*) nrf_atfifo_item_get(m_queue, p_iget_ctx);
620 }
621 
622 
623 // Free the currently loaded operation.
624 static void queue_free(nrf_atfifo_item_get_t * p_iget_ctx)
625 {
626  // Free the current queue element.
627  (void) nrf_atfifo_item_free(m_queue, p_iget_ctx);
628 }
629 
630 
631 static bool queue_has_next(void)
632 {
633  // Decrement the number of queued operations.
634  ASSERT(m_queued_op_cnt != 0);
635  return nrf_atomic_u32_sub(&m_queued_op_cnt, 1);
636 }
637 
638 
639 // This function is called during initialization to setup the page structure (m_pages) and
640 // provide additional information regarding eventual further initialization steps.
641 static fds_init_opts_t pages_init(void)
642 {
643  uint32_t ret = NO_PAGES;
644  uint16_t page = 0;
645  uint16_t total_pages_available = FDS_VIRTUAL_PAGES;
646  bool swap_set_but_not_found = false;
647 
648  for (uint16_t i = 0; i < FDS_VIRTUAL_PAGES; i++)
649  {
650  uint32_t const * const p_page_addr = (uint32_t*)m_fs.start_addr + (i * FDS_PAGE_SIZE);
651  fds_page_type_t const page_type = page_identify(p_page_addr);
652 
653  switch (page_type)
654  {
655  case FDS_PAGE_UNDEFINED:
656  {
657  if (page_is_erased(p_page_addr))
658  {
659  if (m_swap_page.p_addr != NULL)
660  {
661  // If a swap page is already set, flag the page as erased (in m_pages)
662  // and try to tag it as data (in flash) later on during initialization.
663  m_pages[page].page_type = FDS_PAGE_ERASED;
664  m_pages[page].p_addr = p_page_addr;
665  m_pages[page].write_offset = FDS_PAGE_TAG_SIZE;
666 
667  // This is a candidate for a potential new swap page, in case the
668  // current swap is going to be promoted to complete a GC instance.
669  m_gc.cur_page = page;
670  page++;
671  }
672  else
673  {
674  // If there is no swap page yet, use this one.
675  m_swap_page.p_addr = p_page_addr;
676  m_swap_page.write_offset = FDS_PAGE_TAG_SIZE;
677  swap_set_but_not_found = true;
678  }
679 
680  ret |= PAGE_ERASED;
681  }
682  else
683  {
684  // The page contains non-FDS data.
685  // Do not initialize or use this page.
686  total_pages_available--;
687  m_pages[page].p_addr = p_page_addr;
688  m_pages[page].page_type = FDS_PAGE_UNDEFINED;
689  page++;
690  }
691  } break;
692 
693  case FDS_PAGE_DATA:
694  {
695  m_pages[page].page_type = FDS_PAGE_DATA;
696  m_pages[page].p_addr = p_page_addr;
697 
698  // Scan the page to compute its write offset and determine whether or not the page
699  // can be garbage collected. Additionally, update the latest kwown record ID.
700  page_scan(p_page_addr, &m_pages[page].write_offset, &m_pages[page].can_gc);
701 
702  ret |= PAGE_DATA;
703  page++;
704  } break;
705 
706  case FDS_PAGE_SWAP:
707  {
708  if (swap_set_but_not_found)
709  {
710  m_pages[page].page_type = FDS_PAGE_ERASED;
711  m_pages[page].p_addr = m_swap_page.p_addr;
712  m_pages[page].write_offset = FDS_PAGE_TAG_SIZE;
713 
714  page++;
715  }
716 
717  m_swap_page.p_addr = p_page_addr;
718  // If the swap is promoted, this offset should be kept, otherwise,
719  // it should be set to FDS_PAGE_TAG_SIZE.
720  page_scan(p_page_addr, &m_swap_page.write_offset, NULL);
721 
722  ret |= (m_swap_page.write_offset == FDS_PAGE_TAG_SIZE) ?
724  } break;
725 
726  default:
727  // Shouldn't happen.
728  break;
729  }
730  }
731 
732  if (total_pages_available < 2)
733  {
734  ret &= NO_PAGES;
735  }
736 
737  return (fds_init_opts_t)ret;
738 }
739 
740 
741 // Write the first part of a record header (the key and length).
742 static ret_code_t record_header_write_begin(fds_op_t * const p_op, uint32_t * const p_addr)
743 {
744  ret_code_t ret;
745 
746  // Write the record ID next.
747  p_op->write.step = FDS_OP_WRITE_RECORD_ID;
748 
749  ret = nrf_fstorage_write(&m_fs, (uint32_t)(p_addr + FDS_OFFSET_TL),
750  &p_op->write.header.record_key, FDS_HEADER_SIZE_TL * sizeof(uint32_t), NULL);
751 
752  return (ret == NRF_SUCCESS) ? FDS_SUCCESS : FDS_ERR_BUSY;
753 }
754 
755 
756 static ret_code_t record_header_write_id(fds_op_t * const p_op, uint32_t * const p_addr)
757 {
758  ret_code_t ret;
759 
760  // If this record has no data, write the last part of the header directly.
761  // Otherwise, write the record data next.
762  p_op->write.step = (p_op->write.p_data != NULL) ?
764 
765  ret = nrf_fstorage_write(&m_fs, (uint32_t)(p_addr + FDS_OFFSET_ID),
766  &p_op->write.header.record_id, FDS_HEADER_SIZE_ID * sizeof(uint32_t), NULL);
767 
768  return (ret == NRF_SUCCESS) ? FDS_SUCCESS : FDS_ERR_BUSY;
769 }
770 
771 
772 static ret_code_t record_header_write_finalize(fds_op_t * const p_op, uint32_t * const p_addr)
773 {
774  ret_code_t ret;
775 
776  // If this is a simple write operation, then this is the last step.
777  // If this is an update instead, delete the old record next.
778  p_op->write.step = (p_op->op_code == FDS_OP_UPDATE) ?
780 
781  ret = nrf_fstorage_write(&m_fs, (uint32_t)(p_addr + FDS_OFFSET_IC),
782  &p_op->write.header.file_id, FDS_HEADER_SIZE_IC * sizeof(uint32_t), NULL);
783 
784  return (ret == NRF_SUCCESS) ? FDS_SUCCESS : FDS_ERR_BUSY;
785 }
786 
787 
788 static ret_code_t record_header_flag_dirty(uint32_t * const p_record, uint16_t page_to_gc)
789 {
790  // Used to flag a record as dirty, i.e. ready for garbage collection.
791  // Must be statically allocated since it will be written to flash.
792  __ALIGN(4) static uint32_t const dirty_header = {0xFFFF0000};
793 
794  // Flag the record as dirty.
795  ret_code_t ret;
796 
797  ret = nrf_fstorage_write(&m_fs, (uint32_t)p_record,
798  &dirty_header, FDS_HEADER_SIZE_TL * sizeof(uint32_t), NULL);
799 
800  if (ret != NRF_SUCCESS)
801  {
802  return FDS_ERR_BUSY;
803  }
804 
805  m_pages[page_to_gc].can_gc = true;
806 
807  return FDS_SUCCESS;
808 }
809 
810 
811 static ret_code_t record_find_and_delete(fds_op_t * const p_op)
812 {
813  ret_code_t ret;
814  uint16_t page;
815  fds_record_desc_t desc = {0};
816 
817  desc.record_id = p_op->del.record_to_delete;
818 
819  if (record_find_by_desc(&desc, &page))
820  {
821  fds_header_t const * const p_header = (fds_header_t const *)desc.p_record;
822 
823  // Copy the record key and file ID, so that they can be returned in the event.
824  // In case this function is run as part of an update, there is no need to copy
825  // the file ID and record key since they are present in the header stored
826  // in the queue element.
827 
828  p_op->del.file_id = p_header->file_id;
829  p_op->del.record_key = p_header->record_key;
830 
831  // Flag the record as dirty.
832  ret = record_header_flag_dirty((uint32_t*)desc.p_record, page);
833  }
834  else
835  {
836  // The record never existed, or it has already been deleted.
837  ret = FDS_ERR_NOT_FOUND;
838  }
839 
840  return ret;
841 }
842 
843 
844 // Finds a record within a file and flags it as dirty.
845 static ret_code_t file_find_and_delete(fds_op_t * const p_op)
846 {
847  ret_code_t ret;
848  fds_record_desc_t desc;
849 
850  // This token must persist across calls.
851  static fds_find_token_t tok = {0};
852 
853  // Pass NULL to ignore the record key.
854  ret = record_find(&p_op->del.file_id, NULL, &desc, &tok);
855 
856  if (ret == FDS_SUCCESS)
857  {
858  // A record was found: flag it as dirty.
859  ret = record_header_flag_dirty((uint32_t*)desc.p_record, tok.page);
860  }
861  else // FDS_ERR_NOT_FOUND
862  {
863  // No more records were found. Zero the token, so that it can be reused.
864  memset(&tok, 0x00, sizeof(fds_find_token_t));
865  }
866 
867  return ret;
868 }
869 
870 
871 // Writes record data to flash.
872 static ret_code_t record_write_data(fds_op_t * const p_op, uint32_t * const p_addr)
873 {
874  ret_code_t ret;
875 
876  p_op->write.step = FDS_OP_WRITE_HEADER_FINALIZE;
877 
878  ret = nrf_fstorage_write(&m_fs, (uint32_t)(p_addr + FDS_OFFSET_DATA),
879  p_op->write.p_data, p_op->write.header.length_words * sizeof(uint32_t), NULL);
880 
881  return (ret == NRF_SUCCESS) ? FDS_SUCCESS : FDS_ERR_BUSY;
882 }
883 
884 
885 #if (FDS_CRC_CHECK_ON_READ)
886 static bool crc_verify_success(uint16_t crc, uint16_t len_words, uint32_t const * const p_data)
887 {
888  uint16_t computed_crc;
889 
890  // The CRC is computed on the entire record, except the CRC field itself.
891  // The record header is 12 bytes, out of these we have to skip bytes 6 to 8 where the
892  // CRC itself is stored. Then we compute the CRC for the rest of the record, from byte 8 of
893  // the header (where the record ID begins) to the end of the record data.
894  computed_crc = crc16_compute((uint8_t const *)p_data, 6, NULL);
895  computed_crc = crc16_compute((uint8_t const *)p_data + 8,
896  (FDS_HEADER_SIZE_ID + len_words) * sizeof(uint32_t),
897  &computed_crc);
898 
899  return (computed_crc == crc);
900 }
901 #endif
902 
903 
904 static void gc_init(void)
905 {
906  m_gc.run_count++;
907  m_gc.cur_page = 0;
908  m_gc.resume = false;
909 
910  // Setup which pages to GC. Defer checking for open records and the can_gc flag,
911  // as other operations might change those while GC is running.
912  for (uint16_t i = 0; i < FDS_DATA_PAGES; i++)
913  {
914  m_gc.do_gc_page[i] = (m_pages[i].page_type == FDS_PAGE_DATA);
915  }
916 }
917 
918 
919 // Obtain the next page to be garbage collected.
920 // Returns true if there are pages left to garbage collect, returns false otherwise.
921 static bool gc_page_next(uint16_t * const p_next_page)
922 {
923  bool ret = false;
924 
925  for (uint16_t i = 0; i < FDS_DATA_PAGES; i++)
926  {
927  if (m_gc.do_gc_page[i])
928  {
929  // Do not attempt to GC this page again.
930  m_gc.do_gc_page[i] = false;
931 
932  // Only GC pages with no open records and with some records which have been deleted.
933  if ((m_pages[i].records_open == 0) && (m_pages[i].can_gc == true))
934  {
935  *p_next_page = i;
936  ret = true;
937  break;
938  }
939  }
940  }
941 
942  return ret;
943 }
944 
945 
946 static ret_code_t gc_swap_erase(void)
947 {
948  m_gc.state = GC_DISCARD_SWAP;
949  m_swap_page.write_offset = FDS_PAGE_TAG_SIZE;
950 
951  return nrf_fstorage_erase(&m_fs, (uint32_t)m_swap_page.p_addr, FDS_PHY_PAGES_IN_VPAGE, NULL);
952 }
953 
954 
955 // Erase the page being garbage collected, or erase the swap in case there are any open
956 // records on the page being garbage collected.
957 static ret_code_t gc_page_erase(void)
958 {
959  uint32_t ret;
960  uint16_t const gc = m_gc.cur_page;
961 
962  if (m_pages[gc].records_open == 0)
963  {
964  m_gc.state = GC_ERASE_PAGE;
965 
966  ret = nrf_fstorage_erase(&m_fs, (uint32_t)m_pages[gc].p_addr, FDS_PHY_PAGES_IN_VPAGE, NULL);
967  }
968  else
969  {
970  // If there are open records, stop garbage collection on this page.
971  // Discard the swap and try to garbage collect another page.
972  ret = gc_swap_erase();
973  }
974 
975  return ret;
976 }
977 
978 
979 // Copy the current record to swap.
980 static ret_code_t gc_record_copy(void)
981 {
982  fds_header_t const * const p_header = (fds_header_t*)m_gc.p_record_src;
983  uint32_t const * const p_dest = m_swap_page.p_addr + m_swap_page.write_offset;
984  uint16_t const record_len = FDS_HEADER_SIZE + p_header->length_words;
985 
986  m_gc.state = GC_COPY_RECORD;
987 
988  // Copy the record to swap; it is guaranteed to fit in the destination page,
989  // so there is no need to check its size. This will either succeed or timeout.
990  return nrf_fstorage_write(&m_fs, (uint32_t)p_dest, m_gc.p_record_src,
991  record_len * sizeof(uint32_t),
992  NULL);
993 }
994 
995 
996 static ret_code_t gc_record_find_next(void)
997 {
998  ret_code_t ret;
999 
1000  // Find the next valid record to copy.
1001  if (record_find_next(m_gc.cur_page, &m_gc.p_record_src))
1002  {
1003  ret = gc_record_copy();
1004  }
1005  else
1006  {
1007  // No more records left to copy on this page; swap pages.
1008  ret = gc_page_erase();
1009  }
1010 
1011  return ret;
1012 }
1013 
1014 
1015 // Promote the swap by tagging it as a data page.
1016 static ret_code_t gc_swap_promote(void)
1017 {
1018  m_gc.state = GC_PROMOTE_SWAP;
1019  return page_tag_write_data(m_pages[m_gc.cur_page].p_addr);
1020 }
1021 
1022 
1023 // Tag the page just garbage collected as swap.
1024 static ret_code_t gc_tag_new_swap(void)
1025 {
1026  m_gc.state = GC_TAG_NEW_SWAP;
1027  m_gc.p_record_src = NULL;
1028  return page_tag_write_swap();
1029 }
1030 
1031 
1032 static ret_code_t gc_next_page(void)
1033 {
1034  if (!gc_page_next(&m_gc.cur_page))
1035  {
1036  // No pages left to GC; GC has terminated. Reset the state.
1037  m_gc.state = GC_BEGIN;
1038  m_gc.cur_page = 0;
1039  m_gc.p_record_src = NULL;
1040 
1041  return FDS_OP_COMPLETED;
1042  }
1043 
1044  return gc_record_find_next();
1045 }
1046 
1047 
1048 // Update the swap page offeset after a record has been successfully copied to it.
1049 static void gc_update_swap_offset(void)
1050 {
1051  fds_header_t const * const p_header = (fds_header_t*)m_gc.p_record_src;
1052  uint16_t const record_len = FDS_HEADER_SIZE + p_header->length_words;
1053 
1054  m_swap_page.write_offset += record_len;
1055 }
1056 
1057 
1058 static void gc_swap_pages(void)
1059 {
1060  // The page being garbage collected will be the new swap page,
1061  // and the current swap will be used as a data page (promoted).
1062  uint32_t const * const p_addr = m_swap_page.p_addr;
1063 
1064  m_swap_page.p_addr = m_pages[m_gc.cur_page].p_addr;
1065  m_pages[m_gc.cur_page].p_addr = p_addr;
1066 
1067  // Keep the offset for this page, but reset it for the swap.
1068  m_pages[m_gc.cur_page].write_offset = m_swap_page.write_offset;
1069  m_swap_page.write_offset = FDS_PAGE_TAG_SIZE;
1070 }
1071 
1072 
1073 static void gc_state_advance(void)
1074 {
1075  switch (m_gc.state)
1076  {
1077  case GC_BEGIN:
1078  gc_init();
1079  m_gc.state = GC_NEXT_PAGE;
1080  break;
1081 
1082  // A record was successfully copied.
1083  case GC_COPY_RECORD:
1084  gc_update_swap_offset();
1085  m_gc.state = GC_FIND_NEXT_RECORD;
1086  break;
1087 
1088  // A page was successfully erased. Prepare to promote the swap.
1089  case GC_ERASE_PAGE:
1090  gc_swap_pages();
1091  m_gc.state = GC_PROMOTE_SWAP;
1092  break;
1093 
1094  // Swap was discarded because the page being GC'ed had open records.
1095  case GC_DISCARD_SWAP:
1096  // Swap was successfully promoted.
1097  case GC_PROMOTE_SWAP:
1098  // Prepare to tag the page just GC'ed as swap.
1099  m_gc.state = GC_TAG_NEW_SWAP;
1100  break;
1101 
1102  case GC_TAG_NEW_SWAP:
1103  m_gc.state = GC_NEXT_PAGE;
1104  break;
1105 
1106  default:
1107  // Should not happen.
1108  break;
1109  }
1110 }
1111 
1112 
1113 // Initialize the filesystem.
1114 static ret_code_t init_execute(uint32_t prev_ret, fds_op_t * const p_op)
1115 {
1116  ret_code_t ret = FDS_ERR_INTERNAL;
1117 
1118  if (prev_ret != NRF_SUCCESS)
1119  {
1120  // A previous operation has timed out.
1121  m_flags.initializing = false;
1123  }
1124 
1125  switch (p_op->init.step)
1126  {
1127  case FDS_OP_INIT_TAG_SWAP:
1128  {
1129  // The page write offset was determined previously by pages_init().
1130  p_op->init.step = FDS_OP_INIT_TAG_DATA;
1131  ret = page_tag_write_swap();
1132  } break;
1133 
1134  case FDS_OP_INIT_TAG_DATA:
1135  {
1136  // Tag remaining erased pages as data.
1137  bool write_reqd = false;
1138  for (uint16_t i = 0; i < FDS_DATA_PAGES; i++)
1139  {
1140  if (m_pages[i].page_type == FDS_PAGE_ERASED)
1141  {
1142  m_pages[i].page_type = FDS_PAGE_DATA;
1143  write_reqd = true;
1144  ret = page_tag_write_data(m_pages[i].p_addr);
1145  break;
1146  }
1147  }
1148  if (!write_reqd)
1149  {
1150  m_flags.initialized = true;
1151  m_flags.initializing = false;
1152  return FDS_OP_COMPLETED;
1153  }
1154  } break;
1155 
1157  {
1158  // If the swap is going to be discarded then reset its write_offset.
1159  p_op->init.step = FDS_OP_INIT_TAG_SWAP;
1160  m_swap_page.write_offset = FDS_PAGE_TAG_SIZE;
1161 
1162  ret = nrf_fstorage_erase(&m_fs, (uint32_t)m_swap_page.p_addr, FDS_PHY_PAGES_IN_VPAGE, NULL);
1163  } break;
1164 
1166  {
1167  p_op->init.step = FDS_OP_INIT_TAG_SWAP;
1168 
1169  // When promoting the swap, keep the write_offset set by pages_init().
1170  ret = page_tag_write_data(m_swap_page.p_addr);
1171 
1172  uint16_t const gc = m_gc.cur_page;
1173  uint32_t const * const p_old_swap = m_swap_page.p_addr;
1174 
1175  // Execute the swap.
1176  m_swap_page.p_addr = m_pages[gc].p_addr;
1177  m_pages[gc].p_addr = p_old_swap;
1178 
1179  // Copy the offset from the swap to the new page.
1180  m_pages[gc].write_offset = m_swap_page.write_offset;
1181  m_swap_page.write_offset = FDS_PAGE_TAG_SIZE;
1182 
1183  m_pages[gc].page_type = FDS_PAGE_DATA;
1184  } break;
1185 
1186  default:
1187  // Should not happen.
1188  break;
1189  }
1190 
1191  if (ret != FDS_SUCCESS)
1192  {
1193  // fstorage queue was full.
1194  m_flags.initializing = false;
1195  return FDS_ERR_BUSY;
1196  }
1197 
1198  return FDS_OP_EXECUTING;
1199 }
1200 
1201 
1202 // Executes write and update operations.
1203 static ret_code_t write_execute(uint32_t prev_ret, fds_op_t * const p_op)
1204 {
1205  ret_code_t ret;
1206  uint32_t * p_write_addr;
1207  fds_page_t * const p_page = &m_pages[p_op->write.page];
1208 
1209  // This must persist across calls.
1210  static fds_record_desc_t desc = {0};
1211  // When a record is updated, this variable will hold the page where the old
1212  // copy was stored. This will be used to set the can_gc flag when the header is
1213  // invalidated (FDS_OP_WRITE_FLAG_DIRTY).
1214  static uint16_t page;
1215 
1216  if (prev_ret != NRF_SUCCESS)
1217  {
1218  // The previous operation has timed out, update offsets.
1219  page_offsets_update(p_page, p_op);
1221  }
1222 
1223  // Compute the address where to write data.
1224  p_write_addr = (uint32_t*)(p_page->p_addr + p_page->write_offset);
1225 
1226  // Execute the current step of the operation, and set one to be executed next.
1227  switch (p_op->write.step)
1228  {
1230  {
1231  // The first step of updating a record constists of locating the copy to be deleted.
1232  // If the old copy couldn't be found for any reason then the update should fail.
1233  // This prevents duplicates when queuing multiple updates of the same record.
1234  desc.p_record = NULL;
1235  desc.record_id = p_op->write.record_to_delete;
1236 
1237  if (!record_find_by_desc(&desc, &page))
1238  {
1239  return FDS_ERR_NOT_FOUND;
1240  }
1241  // Setting the step is redundant since we are falling through.
1242  }
1243  // Fallthrough to FDS_OP_WRITE_HEADER_BEGIN.
1244 
1246  ret = record_header_write_begin(p_op, p_write_addr);
1247  break;
1248 
1250  ret = record_header_write_id(p_op, p_write_addr);
1251  break;
1252 
1253  case FDS_OP_WRITE_DATA:
1254  ret = record_write_data(p_op, p_write_addr);
1255  break;
1256 
1258  ret = record_header_write_finalize(p_op, p_write_addr);
1259  break;
1260 
1262  p_op->write.step = FDS_OP_WRITE_DONE;
1263  ret = record_header_flag_dirty((uint32_t*)desc.p_record, page);
1264  break;
1265 
1266  case FDS_OP_WRITE_DONE:
1267  ret = FDS_OP_COMPLETED;
1268 
1269 #if (FDS_CRC_CHECK_ON_WRITE)
1270  if (!crc_verify_success(p_op->write.header.crc16,
1271  p_op->write.header.length_words,
1272  p_write_addr))
1273  {
1275  }
1276 #endif
1277  break;
1278 
1279  default:
1280  ret = FDS_ERR_INTERNAL;
1281  break;
1282  }
1283 
1284  // An operation has either completed or failed. It may have failed because fstorage
1285  // ran out of memory, or because the user tried to delete a record which did not exist.
1286  if (ret != FDS_OP_EXECUTING)
1287  {
1288  // There won't be another callback for this operation, so update the page offset now.
1289  page_offsets_update(p_page, p_op);
1290  }
1291 
1292  return ret;
1293 }
1294 
1295 
1296 static ret_code_t delete_execute(uint32_t prev_ret, fds_op_t * const p_op)
1297 {
1298  ret_code_t ret;
1299 
1300  if (prev_ret != NRF_SUCCESS)
1301  {
1303  }
1304 
1305  switch (p_op->del.step)
1306  {
1308  p_op->del.step = FDS_OP_DEL_DONE;
1309  ret = record_find_and_delete(p_op);
1310  break;
1311 
1313  ret = file_find_and_delete(p_op);
1314  if (ret == FDS_ERR_NOT_FOUND)
1315  {
1316  // No more records could be found.
1317  // There won't be another callback for this operation, so return now.
1318  ret = FDS_OP_COMPLETED;
1319  }
1320  break;
1321 
1322  case FDS_OP_DEL_DONE:
1323  ret = FDS_OP_COMPLETED;
1324  break;
1325 
1326  default:
1327  ret = FDS_ERR_INTERNAL;
1328  break;
1329  }
1330 
1331  return ret;
1332 }
1333 
1334 
1335 static ret_code_t gc_execute(uint32_t prev_ret)
1336 {
1337  ret_code_t ret;
1338 
1339  if (prev_ret != NRF_SUCCESS)
1340  {
1342  }
1343 
1344  if (m_gc.resume)
1345  {
1346  m_gc.resume = false;
1347  }
1348  else
1349  {
1350  gc_state_advance();
1351  }
1352 
1353  switch (m_gc.state)
1354  {
1355  case GC_NEXT_PAGE:
1356  ret = gc_next_page();
1357  break;
1358 
1359  case GC_FIND_NEXT_RECORD:
1360  ret = gc_record_find_next();
1361  break;
1362 
1363  case GC_COPY_RECORD:
1364  ret = gc_record_copy();
1365  break;
1366 
1367  case GC_ERASE_PAGE:
1368  ret = gc_page_erase();
1369  break;
1370 
1371  case GC_PROMOTE_SWAP:
1372  ret = gc_swap_promote();
1373  break;
1374 
1375  case GC_TAG_NEW_SWAP:
1376  ret = gc_tag_new_swap();
1377  break;
1378 
1379  default:
1380  // Should not happen.
1381  ret = FDS_ERR_INTERNAL;
1382  break;
1383  }
1384 
1385  // Either FDS_OP_EXECUTING, FDS_OP_COMPLETED, FDS_ERR_BUSY or FDS_ERR_INTERNAL.
1386  return ret;
1387 }
1388 
1389 
1390 static void queue_process(ret_code_t result)
1391 {
1392  static fds_op_t * m_p_cur_op; // Current fds operation.
1393  static nrf_atfifo_item_get_t m_iget_ctx; // Queue context for the current operation.
1394 
1395  while (true)
1396  {
1397  if (m_p_cur_op == NULL)
1398  {
1399  // Load the next from the queue if no operation is being executed.
1400  m_p_cur_op = queue_load(&m_iget_ctx);
1401  }
1402 
1403  /* We can reach here in three ways:
1404  * from queue_start(): something was just queued
1405  * from the fstorage event handler: an operation is being executed
1406  * looping: we only loop if there are operations still in the queue
1407  *
1408  * In all these three cases, m_p_cur_op != NULL.
1409  */
1410  ASSERT(m_p_cur_op != NULL);
1411 
1412  switch (m_p_cur_op->op_code)
1413  {
1414  case FDS_OP_INIT:
1415  result = init_execute(result, m_p_cur_op);
1416  break;
1417 
1418  case FDS_OP_WRITE:
1419  case FDS_OP_UPDATE:
1420  result = write_execute(result, m_p_cur_op);
1421  break;
1422 
1423  case FDS_OP_DEL_RECORD:
1424  case FDS_OP_DEL_FILE:
1425  result = delete_execute(result, m_p_cur_op);
1426  break;
1427 
1428  case FDS_OP_GC:
1429  result = gc_execute(result);
1430  break;
1431 
1432  default:
1433  result = FDS_ERR_INTERNAL;
1434  break;
1435  }
1436 
1437  if (result == FDS_OP_EXECUTING)
1438  {
1439  // The operation has not completed yet. Wait for the next system event.
1440  break;
1441  }
1442 
1443  // The operation has completed (either successfully or with an error).
1444  // - send an event to the user
1445  // - free the operation buffer
1446  // - execute any other queued operations
1447 
1448  fds_evt_t evt =
1449  {
1450  // The operation might have failed for one of the following reasons:
1451  // FDS_ERR_BUSY - flash subsystem can't accept the operation
1452  // FDS_ERR_OPERATION_TIMEOUT - flash subsystem timed out
1453  // FDS_ERR_CRC_CHECK_FAILED - a CRC check failed
1454  // FDS_ERR_NOT_FOUND - no record found (delete/update)
1455  .result = (result == FDS_OP_COMPLETED) ? FDS_SUCCESS : result,
1456  };
1457 
1458  event_prepare(m_p_cur_op, &evt);
1459  event_send(&evt);
1460 
1461  // Zero the pointer to the current operation so that this function
1462  // will fetch a new one from the queue next time it is run.
1463  m_p_cur_op = NULL;
1464 
1465  // The result of the operation must be reset upon re-entering the loop to ensure
1466  // the next operation won't be affected by eventual errors in previous operations.
1467  result = NRF_SUCCESS;
1468 
1469  // Free the queue element used by the current operation.
1470  queue_free(&m_iget_ctx);
1471 
1472  if (!queue_has_next())
1473  {
1474  // No more elements left. Nothing to do.
1475  break;
1476  }
1477  }
1478 }
1479 
1480 
1481 static void queue_start(void)
1482 {
1483  if (!nrf_atomic_u32_fetch_add(&m_queued_op_cnt, 1))
1484  {
1485  queue_process(NRF_SUCCESS);
1486  }
1487 }
1488 
1489 
1490 static void fs_event_handler(nrf_fstorage_evt_t * p_evt)
1491 {
1492  queue_process(p_evt->result);
1493 }
1494 
1495 
1496 // Enqueues write and update operations.
1497 static ret_code_t write_enqueue(fds_record_desc_t * const p_desc,
1498  fds_record_t const * const p_record,
1499  fds_reserve_token_t const * const p_tok,
1500  fds_op_code_t op_code)
1501 {
1502  ret_code_t ret;
1503  uint16_t page;
1504  uint16_t crc = 0;
1505  uint16_t length_words = 0;
1506  fds_op_t * p_op;
1507  nrf_atfifo_item_put_t iput_ctx;
1508 
1509  if (!m_flags.initialized)
1510  {
1511  return FDS_ERR_NOT_INITIALIZED;
1512  }
1513 
1514  if (p_record == NULL)
1515  {
1516  return FDS_ERR_NULL_ARG;
1517  }
1518 
1519  if ((p_record->file_id == FDS_FILE_ID_INVALID) ||
1520  (p_record->key == FDS_RECORD_KEY_DIRTY))
1521  {
1522  return FDS_ERR_INVALID_ARG;
1523  }
1524 
1525  if (!is_word_aligned(p_record->data.p_data))
1526  {
1527  return FDS_ERR_UNALIGNED_ADDR;
1528  }
1529 
1530  // No space was previously reserved in flash for this operation.
1531  if (p_tok == NULL)
1532  {
1533  // Find a page where to write data.
1534  length_words = p_record->data.length_words;
1535  ret = write_space_reserve(length_words, &page);
1536 
1537  if (ret != FDS_SUCCESS)
1538  {
1539  // There is either not enough space in flash (FDS_ERR_NO_SPACE_IN_FLASH) or
1540  // the record exceeds the size of virtual page (FDS_ERR_RECORD_TOO_LARGE).
1541  return ret;
1542  }
1543  }
1544  else
1545  {
1546  page = p_tok->page;
1547  length_words = p_tok->length_words;
1548  }
1549 
1550  // Get a buffer on the queue of operations.
1551  p_op = queue_buf_get(&iput_ctx);
1552  if (p_op == NULL)
1553  {
1555  write_space_free(length_words, page);
1558  }
1559 
1560  // Initialize the operation.
1561  p_op->op_code = op_code;
1562  p_op->write.step = FDS_OP_WRITE_HEADER_BEGIN;
1563  p_op->write.page = page;
1564  p_op->write.p_data = p_record->data.p_data;
1565  p_op->write.header.record_id = record_id_new();
1566  p_op->write.header.file_id = p_record->file_id;
1567  p_op->write.header.record_key = p_record->key;
1568  p_op->write.header.length_words = length_words;
1569 
1570  if (op_code == FDS_OP_UPDATE)
1571  {
1572  p_op->write.step = FDS_OP_WRITE_FIND_RECORD;
1573  // Save the record ID of the record to be updated.
1574  p_op->write.record_to_delete = p_desc->record_id;
1575  }
1576 
1577 #if (FDS_CRC_CHECK_ON_READ)
1578  // First, compute the CRC for the first 6 bytes of the header which contain the
1579  // record key, length and file ID, then, compute the CRC of the record ID (4 bytes).
1580  crc = crc16_compute((uint8_t*)&p_op->write.header, 6, NULL);
1581  crc = crc16_compute((uint8_t*)&p_op->write.header.record_id, 4, &crc);
1582 
1583  // Compute the CRC for the record data.
1584  crc = crc16_compute((uint8_t*)p_record->data.p_data,
1585  p_record->data.length_words * sizeof(uint32_t), &crc);
1586 #endif
1587 
1588  p_op->write.header.crc16 = crc;
1589 
1590  queue_buf_store(&iput_ctx);
1591 
1592  // Initialize the record descriptor, if provided.
1593  if (p_desc != NULL)
1594  {
1595  p_desc->p_record = NULL;
1596  // Don't invoke record_id_new() again !
1597  p_desc->record_id = p_op->write.header.record_id;
1598  p_desc->record_is_open = false;
1599  p_desc->gc_run_count = m_gc.run_count;
1600  }
1601 
1602  // Start processing the queue, if necessary.
1603  queue_start();
1604 
1605  return FDS_SUCCESS;
1606 }
1607 
1608 
1609 ret_code_t fds_register(fds_cb_t cb)
1610 {
1611  ret_code_t ret;
1612 
1613  if (m_users == FDS_MAX_USERS)
1614  {
1616  }
1617  else
1618  {
1619  m_cb_table[m_users] = cb;
1620  (void) nrf_atomic_u32_add(&m_users, 1);
1621 
1622  ret = FDS_SUCCESS;
1623  }
1624 
1625  return ret;
1626 }
1627 
1628 ret_code_t flash_bounds_set(const uint32_t start_addr, const uint32_t end_addr)
1629 {
1630  ret_code_t err_code = NRF_SUCCESS;
1631  const uint32_t req_size = (FDS_PHY_PAGES * FDS_PHY_PAGE_SIZE * sizeof(uint32_t));
1632  const uint32_t flash_size = end_addr - start_addr;
1633  if(end_addr <= start_addr)
1634  {
1635  err_code |= FDS_ERR_INVALID_ARG;
1636  }
1637  else if (end_addr % FDS_PHY_PAGE_SIZE)
1638  {
1639  err_code |= FDS_ERR_UNALIGNED_ADDR;
1640  }
1641  else if (req_size > flash_size)
1642  {
1643  err_code |= FDS_ERR_NO_SPACE_IN_FLASH;
1644  }
1645  else if (FDS_BOUND_NOT_SET != m_fs.end_addr)
1646  {
1647  err_code |= FDS_ERR_INTERNAL;
1648  }
1649  else
1650  {
1651  m_fs.end_addr = end_addr;
1652  m_fs.start_addr = m_fs.end_addr - req_size;
1653  }
1654  return err_code;
1655 }
1656 
1657 
1658 static ret_code_t flash_subsystem_init(void)
1659 {
1660  if(FDS_BOUND_NOT_SET == m_fs.end_addr)
1661  {
1662  return FDS_ERR_INTERNAL;
1663  }
1664 
1665  #if (FDS_BACKEND == NRF_FSTORAGE_SD)
1666  return nrf_fstorage_init(&m_fs, &nrf_fstorage_sd, NULL);
1667  #elif (FDS_BACKEND == NRF_FSTORAGE_NVMC)
1668  return nrf_fstorage_init(&m_fs, &nrf_fstorage_nvmc, NULL);
1669  #else
1670  #error Invalid FDS_BACKEND.
1671  #endif
1672 }
1673 
1674 
1675 static void queue_init(void)
1676 {
1677  (void) NRF_ATFIFO_INIT(m_queue);
1678 }
1679 
1680 
1681 ret_code_t fds_init(void)
1682 {
1683  ret_code_t ret;
1684  fds_evt_t const evt_success =
1685  {
1686  .id = FDS_EVT_INIT,
1687  .result = FDS_SUCCESS,
1688  };
1689 
1690  if (m_flags.initialized)
1691  {
1692  // No initialization is necessary. Notify the application immediately.
1693  event_send(&evt_success);
1694  return FDS_SUCCESS;
1695  }
1696 
1697  if (nrf_atomic_flag_set_fetch(&m_flags.initializing))
1698  {
1699  // If we were already initializing, return.
1700  return FDS_SUCCESS;
1701  }
1702 
1703  // Otherwise, the flag is set and we proceed to initialization.
1704 
1705  ret = flash_subsystem_init();
1706  if (ret != NRF_SUCCESS)
1707  {
1708  return ret;
1709  }
1710 
1711  queue_init();
1712 
1713  // Initialize the page structure (m_pages), and determine which
1714  // initialization steps are required given the current state of the filesystem.
1715 
1716  fds_init_opts_t init_opts = pages_init();
1717 
1718  switch (init_opts)
1719  {
1720  case NO_PAGES:
1721  case NO_SWAP:
1722  return FDS_ERR_NO_PAGES;
1723 
1724  case ALREADY_INSTALLED:
1725  {
1726  // No initialization is necessary. Notify the application immediately.
1727  m_flags.initialized = true;
1728  m_flags.initializing = false;
1729  event_send(&evt_success);
1730  return FDS_SUCCESS;
1731  }
1732 
1733  default:
1734  break;
1735  }
1736 
1737  // A write operation is necessary to initialize the fileystem.
1738 
1739  nrf_atfifo_item_put_t iput_ctx;
1740 
1741  fds_op_t * p_op = queue_buf_get(&iput_ctx);
1742  if (p_op == NULL)
1743  {
1745  }
1746 
1747  p_op->op_code = FDS_OP_INIT;
1748 
1749  switch (init_opts)
1750  {
1751  case FRESH_INSTALL:
1752  case TAG_SWAP:
1753  p_op->init.step = FDS_OP_INIT_TAG_SWAP;
1754  break;
1755 
1756  case PROMOTE_SWAP:
1757  case PROMOTE_SWAP_INST:
1758  p_op->init.step = FDS_OP_INIT_PROMOTE_SWAP;
1759  break;
1760 
1761  case DISCARD_SWAP:
1762  p_op->init.step = FDS_OP_INIT_ERASE_SWAP;
1763  break;
1764 
1765  case TAG_DATA:
1766  case TAG_DATA_INST:
1767  p_op->init.step = FDS_OP_INIT_TAG_DATA;
1768  break;
1769 
1770  default:
1771  // Should not happen.
1772  break;
1773  }
1774 
1775  queue_buf_store(&iput_ctx);
1776  queue_start();
1777 
1778  return FDS_SUCCESS;
1779 }
1780 
1781 
1782 ret_code_t fds_record_open(fds_record_desc_t * const p_desc,
1783  fds_flash_record_t * const p_flash_rec)
1784 {
1785  uint16_t page;
1786 
1787  if ((p_desc == NULL) || (p_flash_rec == NULL))
1788  {
1789  return FDS_ERR_NULL_ARG;
1790  }
1791 
1792  // Find the record if necessary.
1793  if (record_find_by_desc(p_desc, &page))
1794  {
1795  fds_header_t const * const p_header = (fds_header_t*)p_desc->p_record;
1796 
1798  if (!crc_verify_success(p_header->crc16,
1799  p_header->length_words,
1800  p_desc->p_record))
1801  {
1802  return FDS_ERR_CRC_CHECK_FAILED;
1803  }
1804 #endif
1805 
1806  (void) nrf_atomic_u32_add(&m_pages[page].records_open, 1);
1807 
1808  // Initialize p_flash_rec.
1809  p_flash_rec->p_header = p_header;
1810  p_flash_rec->p_data = (p_desc->p_record + FDS_HEADER_SIZE);
1811 
1812  // Set the record as open in the descriptor.
1813  p_desc->record_is_open = true;
1814 
1815  return FDS_SUCCESS;
1816  }
1817 
1818  // The record could not be found.
1819  // It either never existed or it has been deleted.
1820  return FDS_ERR_NOT_FOUND;
1821 }
1822 
1823 
1824 ret_code_t fds_record_close(fds_record_desc_t * const p_desc)
1825 {
1826  ret_code_t ret;
1827  uint16_t page;
1828 
1829  if (p_desc == NULL)
1830  {
1831  return FDS_ERR_NULL_ARG;
1832  }
1833 
1834  if (record_find_by_desc((fds_record_desc_t*)p_desc, &page))
1835  {
1837  if ((m_pages[page].records_open > 0) && (p_desc->record_is_open))
1838  {
1839 
1840  m_pages[page].records_open--;
1841  p_desc->record_is_open = false;
1842 
1843  ret = FDS_SUCCESS;
1844  }
1845  else
1846  {
1848  }
1850  }
1851  else
1852  {
1853  ret = FDS_ERR_NOT_FOUND;
1854  }
1855 
1856  return ret;
1857 }
1858 
1859 
1860 ret_code_t fds_reserve(fds_reserve_token_t * const p_tok, uint16_t length_words)
1861 {
1862  ret_code_t ret;
1863  uint16_t page;
1864 
1865  if (!m_flags.initialized)
1866  {
1867  return FDS_ERR_NOT_INITIALIZED;
1868  }
1869 
1870  if (p_tok == NULL)
1871  {
1872  return FDS_ERR_NULL_ARG;
1873  }
1874 
1875  ret = write_space_reserve(length_words, &page);
1876 
1877  if (ret == FDS_SUCCESS)
1878  {
1879  p_tok->page = page;
1880  p_tok->length_words = length_words;
1881  }
1882 
1883  return ret;
1884 }
1885 
1886 
1887 ret_code_t fds_reserve_cancel(fds_reserve_token_t * const p_tok)
1888 {
1889  ret_code_t ret;
1890 
1891  if (!m_flags.initialized)
1892  {
1893  return FDS_ERR_NOT_INITIALIZED;
1894  }
1895 
1896  if (p_tok == NULL)
1897  {
1898  return FDS_ERR_NULL_ARG;
1899  }
1900 
1901  if (p_tok->page > FDS_DATA_PAGES)
1902  {
1903  // The page does not exist. This shouldn't happen.
1904  return FDS_ERR_INVALID_ARG;
1905  }
1906 
1907  fds_page_t const * const p_page = &m_pages[p_tok->page];
1908 
1910  if ((FDS_HEADER_SIZE + p_tok->length_words) <= p_page->words_reserved)
1911  {
1912  // Free reserved space.
1913  write_space_free(p_tok->length_words, p_tok->page);
1914 
1915  // Clean the token.
1916  p_tok->page = 0;
1917  p_tok->length_words = 0;
1918  ret = FDS_SUCCESS;
1919  }
1920  else
1921  {
1922  // We are trying to cancel a reservation of more words than how many are
1923  // currently reserved on the page. Clearly, this shouldn't happen.
1924  ret = FDS_ERR_INVALID_ARG;
1925  }
1927 
1928  return ret;
1929 }
1930 
1931 
1932 ret_code_t fds_record_write(fds_record_desc_t * const p_desc,
1933  fds_record_t const * const p_record)
1934 {
1935  return write_enqueue(p_desc, p_record, NULL, FDS_OP_WRITE);
1936 }
1937 
1938 
1939 ret_code_t fds_record_write_reserved(fds_record_desc_t * const p_desc,
1940  fds_record_t const * const p_record,
1941  fds_reserve_token_t const * const p_tok)
1942 {
1943  // A NULL token is not allowed when writing to a reserved space.
1944  if (p_tok == NULL)
1945  {
1946  return FDS_ERR_NULL_ARG;
1947  }
1948 
1949  return write_enqueue(p_desc, p_record, p_tok, FDS_OP_WRITE);
1950 }
1951 
1952 
1953 ret_code_t fds_record_update(fds_record_desc_t * const p_desc,
1954  fds_record_t const * const p_record)
1955 {
1956  // A NULL descriptor is not allowed when updating a record.
1957  if (p_desc == NULL)
1958  {
1959  return FDS_ERR_NULL_ARG;
1960  }
1961 
1962  return write_enqueue(p_desc, p_record, NULL, FDS_OP_UPDATE);
1963 }
1964 
1965 
1966 ret_code_t fds_record_delete(fds_record_desc_t * const p_desc)
1967 {
1968  fds_op_t * p_op;
1969  nrf_atfifo_item_put_t iput_ctx;
1970 
1971  if (!m_flags.initialized)
1972  {
1973  return FDS_ERR_NOT_INITIALIZED;
1974  }
1975 
1976  if (p_desc == NULL)
1977  {
1978  return FDS_ERR_NULL_ARG;
1979  }
1980 
1981  p_op = queue_buf_get(&iput_ctx);
1982  if (p_op == NULL)
1983  {
1985  }
1986 
1987  p_op->op_code = FDS_OP_DEL_RECORD;
1988  p_op->del.step = FDS_OP_DEL_RECORD_FLAG_DIRTY;
1989  p_op->del.record_to_delete = p_desc->record_id;
1990 
1991  queue_buf_store(&iput_ctx);
1992  queue_start();
1993 
1994  return FDS_SUCCESS;
1995 }
1996 
1997 
1998 ret_code_t fds_file_delete(uint16_t file_id)
1999 {
2000  fds_op_t * p_op;
2001  nrf_atfifo_item_put_t iput_ctx;
2002 
2003  if (!m_flags.initialized)
2004  {
2005  return FDS_ERR_NOT_INITIALIZED;
2006  }
2007 
2008  if (file_id == FDS_FILE_ID_INVALID)
2009  {
2010  return FDS_ERR_INVALID_ARG;
2011  }
2012 
2013  p_op = queue_buf_get(&iput_ctx);
2014  if (p_op == NULL)
2015  {
2017  }
2018 
2019  p_op->op_code = FDS_OP_DEL_FILE;
2020  p_op->del.step = FDS_OP_DEL_FILE_FLAG_DIRTY;
2021  p_op->del.file_id = file_id;
2022 
2023  queue_buf_store(&iput_ctx);
2024  queue_start();
2025 
2026  return FDS_SUCCESS;
2027 }
2028 
2029 
2030 ret_code_t fds_gc(void)
2031 {
2032  fds_op_t * p_op;
2033  nrf_atfifo_item_put_t iput_ctx;
2034 
2035  if (!m_flags.initialized)
2036  {
2037  return FDS_ERR_NOT_INITIALIZED;
2038  }
2039 
2040  p_op = queue_buf_get(&iput_ctx);
2041  if (p_op == NULL)
2042  {
2044  }
2045 
2046  p_op->op_code = FDS_OP_GC;
2047 
2048  queue_buf_store(&iput_ctx);
2049 
2050  if (m_gc.state != GC_BEGIN)
2051  {
2052  // Resume GC by retrying the last step.
2053  m_gc.resume = true;
2054  }
2055 
2056  queue_start();
2057 
2058  return FDS_SUCCESS;
2059 }
2060 
2061 
2062 ret_code_t fds_record_iterate(fds_record_desc_t * const p_desc,
2063  fds_find_token_t * const p_token)
2064 {
2065  return record_find(NULL, NULL, p_desc, p_token);
2066 }
2067 
2068 
2069 ret_code_t fds_record_find(uint16_t file_id,
2070  uint16_t record_key,
2071  fds_record_desc_t * const p_desc,
2072  fds_find_token_t * const p_token)
2073 {
2074  return record_find(&file_id, &record_key, p_desc, p_token);
2075 }
2076 
2077 
2078 ret_code_t fds_record_find_by_key(uint16_t record_key,
2079  fds_record_desc_t * const p_desc,
2080  fds_find_token_t * const p_token)
2081 {
2082  return record_find(NULL, &record_key, p_desc, p_token);
2083 }
2084 
2085 
2086 ret_code_t fds_record_find_in_file(uint16_t file_id,
2087  fds_record_desc_t * const p_desc,
2088  fds_find_token_t * const p_token)
2089 {
2090  return record_find(&file_id, NULL, p_desc, p_token);
2091 }
2092 
2093 
2094 ret_code_t fds_descriptor_from_rec_id(fds_record_desc_t * const p_desc,
2095  uint32_t record_id)
2096 {
2097  if (p_desc == NULL)
2098  {
2099  return FDS_ERR_NULL_ARG;
2100  }
2101 
2102  // Zero the descriptor and set the record_id field.
2103  memset(p_desc, 0x00, sizeof(fds_record_desc_t));
2104  p_desc->record_id = record_id;
2105 
2106  return FDS_SUCCESS;
2107 }
2108 
2109 
2110 ret_code_t fds_record_id_from_desc(fds_record_desc_t const * const p_desc,
2111  uint32_t * const p_record_id)
2112 {
2113  if ((p_desc == NULL) || (p_record_id == NULL))
2114  {
2115  return FDS_ERR_NULL_ARG;
2116  }
2117 
2118  *p_record_id = p_desc->record_id;
2119 
2120  return FDS_SUCCESS;
2121 }
2122 
2123 
2124 ret_code_t fds_stat(fds_stat_t * const p_stat)
2125 {
2126  uint16_t const words_in_page = FDS_PAGE_SIZE;
2127  // The largest number of free contiguous words on any page.
2128  uint16_t contig_words = 0;
2129 
2130  if (!m_flags.initialized)
2131  {
2132  return FDS_ERR_NOT_INITIALIZED;
2133  }
2134 
2135  if (p_stat == NULL)
2136  {
2137  return FDS_ERR_NULL_ARG;
2138  }
2139 
2140  memset(p_stat, 0x00, sizeof(fds_stat_t));
2141 
2143 
2144  for (uint16_t page = 0; page < FDS_DATA_PAGES; page++)
2145  {
2146  uint16_t const words_used = m_pages[page].write_offset + m_pages[page].words_reserved;
2147 
2148  if (page_identify(m_pages[page].p_addr) == FDS_PAGE_UNDEFINED)
2149  {
2150  p_stat->pages_available--;
2151  }
2152 
2153  p_stat->open_records += m_pages[page].records_open;
2154  p_stat->words_reserved += m_pages[page].words_reserved;
2155  p_stat->words_used += words_used;
2156 
2157  contig_words = (words_in_page - words_used);
2158  if (contig_words > p_stat->largest_contig)
2159  {
2160  p_stat->largest_contig = contig_words;
2161  }
2162 
2163  records_stat(page,
2164  &p_stat->valid_records,
2165  &p_stat->dirty_records,
2166  &p_stat->freeable_words,
2167  &p_stat->corruption);
2168  }
2169 
2170  return FDS_SUCCESS;
2171 }
2172 
2173 #endif //NRF_MODULE_ENABLED(FDS)
#define CRITICAL_SECTION_ENTER()
#define FDS_PAGE_SIZE
#define FDS_PAGE_TAG_MAGIC
#define FDS_OP_EXECUTING
fds_init_opts_t
@ TAG_SWAP
@ DISCARD_SWAP
@ ALREADY_INSTALLED
@ NO_PAGES
@ TAG_DATA
@ FRESH_INSTALL
@ TAG_DATA_INST
@ PROMOTE_SWAP
@ NO_SWAP
@ PROMOTE_SWAP_INST
#define CRITICAL_SECTION_EXIT()
#define FDS_OFFSET_DATA
#define FDS_PAGE_TAG_SIZE
#define FDS_PHY_PAGES_IN_VPAGE
#define FDS_OFFSET_ID
#define FDS_PAGE_TAG_SWAP
#define FDS_HEADER_SIZE_TL
#define FDS_PAGE_TAG_WORD_1
@ GC_PROMOTE_SWAP
@ GC_TAG_NEW_SWAP
@ GC_COPY_RECORD
@ GC_ERASE_PAGE
@ GC_NEXT_PAGE
@ GC_FIND_NEXT_RECORD
@ GC_BEGIN
@ GC_DISCARD_SWAP
fds_op_code_t
@ FDS_OP_DEL_FILE
@ FDS_OP_WRITE
@ FDS_OP_UPDATE
@ FDS_OP_INIT
@ FDS_OP_GC
@ FDS_OP_DEL_RECORD
#define FDS_PAGE_TAG_DATA
fds_header_status_t
@ FDS_HEADER_DIRTY
@ FDS_HEADER_VALID
@ FDS_HEADER_CORRUPT
#define FDS_HEADER_SIZE
@ PAGE_SWAP_CLEAN
@ PAGE_DATA
@ PAGE_SWAP_DIRTY
@ PAGE_ERASED
@ FDS_OP_INIT_ERASE_SWAP
@ FDS_OP_INIT_PROMOTE_SWAP
@ FDS_OP_INIT_TAG_DATA
@ FDS_OP_INIT_TAG_SWAP
@ FDS_OP_DEL_RECORD_FLAG_DIRTY
@ FDS_OP_DEL_DONE
@ FDS_OP_DEL_FILE_FLAG_DIRTY
#define FDS_OFFSET_TL
#define FDS_DATA_PAGES
#define FDS_PHY_PAGES
#define FDS_ERASED_WORD
fds_page_type_t
@ FDS_PAGE_ERASED
@ FDS_PAGE_SWAP
@ FDS_PAGE_UNDEFINED
@ FDS_PAGE_DATA
#define FDS_OP_COMPLETED
#define FDS_HEADER_SIZE_IC
#define FDS_PHY_PAGE_SIZE
#define FDS_OFFSET_IC
@ FDS_OP_WRITE_FLAG_DIRTY
@ FDS_OP_WRITE_HEADER_FINALIZE
@ FDS_OP_WRITE_DONE
@ FDS_OP_WRITE_RECORD_ID
@ FDS_OP_WRITE_HEADER_BEGIN
@ FDS_OP_WRITE_DATA
@ FDS_OP_WRITE_FIND_RECORD
#define FDS_PAGE_TAG_WORD_0
#define FDS_HEADER_SIZE_ID
ret_code_t fds_record_write(fds_record_desc_t *p_desc, fds_record_t const *p_record)
Function for writing a record to flash.
#define FDS_BOUND_NOT_SET
Value for unconfigured FDS physical bound.
Definition: fds.h:83
ret_code_t fds_record_find(uint16_t file_id, uint16_t record_key, fds_record_desc_t *p_desc, fds_find_token_t *p_token)
Function for searching for records with a given record key in a file.
ret_code_t fds_init(void)
Function for initializing the module.
ret_code_t fds_record_write_reserved(fds_record_desc_t *p_desc, fds_record_t const *p_record, fds_reserve_token_t const *p_token)
Function for writing a record to a space in flash that was reserved using fds_reserve.
ret_code_t fds_reserve(fds_reserve_token_t *p_token, uint16_t length_words)
Function for reserving space in flash.
ret_code_t fds_reserve_cancel(fds_reserve_token_t *p_token)
Function for canceling an fds_reserve operation.
ret_code_t fds_stat(fds_stat_t *p_stat)
Function for retrieving file system statistics.
ret_code_t fds_record_id_from_desc(fds_record_desc_t const *p_desc, uint32_t *p_record_id)
Function for obtaining a record ID from a record descriptor.
void(* fds_cb_t)(fds_evt_t const *p_evt)
FDS event handler function prototype.
Definition: fds.h:273
ret_code_t flash_bounds_set(const uint32_t start_addr, const uint32_t end_addr)
Set physical boundaries for FDS.
ret_code_t fds_descriptor_from_rec_id(fds_record_desc_t *p_desc, uint32_t record_id)
Function for obtaining a descriptor from a record ID.
ret_code_t fds_record_iterate(fds_record_desc_t *p_desc, fds_find_token_t *p_token)
Function for iterating through all records in flash.
ret_code_t fds_file_delete(uint16_t file_id)
Function for deleting all records in a file.
ret_code_t fds_gc(void)
Function for running garbage collection.
ret_code_t fds_record_find_in_file(uint16_t file_id, fds_record_desc_t *p_desc, fds_find_token_t *p_token)
Function for searching for any record in a file.
ret_code_t fds_record_update(fds_record_desc_t *p_desc, fds_record_t const *p_record)
Function for updating a record.
ret_code_t fds_record_open(fds_record_desc_t *p_desc, fds_flash_record_t *p_flash_record)
Function for opening a record for reading.
#define FDS_RECORD_KEY_DIRTY
Record key for deleted records.
Definition: fds.h:78
ret_code_t fds_record_find_by_key(uint16_t record_key, fds_record_desc_t *p_desc, fds_find_token_t *p_token)
Function for searching for records with a given record key.
ret_code_t fds_register(fds_cb_t cb)
Function for registering an FDS event handler.
#define FDS_FILE_ID_INVALID
Invalid file ID.
Definition: fds.h:70
ret_code_t fds_record_close(fds_record_desc_t *p_desc)
Function for closing a record.
ret_code_t fds_record_delete(fds_record_desc_t *p_desc)
Function for deleting a record.
@ FDS_ERR_NOT_FOUND
Error. The record was not found.
Definition: fds.h:100
@ FDS_ERR_INTERNAL
Error. An internal error occurred.
Definition: fds.h:105
@ FDS_ERR_INVALID_ARG
Error. The parameter contains invalid data.
Definition: fds.h:94
@ FDS_ERR_NO_PAGES
Error. No flash pages are available.
Definition: fds.h:101
@ FDS_ERR_USER_LIMIT_REACHED
Error. The maximum number of users has been reached.
Definition: fds.h:102
@ FDS_ERR_NO_SPACE_IN_QUEUES
Error. There is no space in the internal queues.
Definition: fds.h:98
@ FDS_ERR_RECORD_TOO_LARGE
Error. The record exceeds the maximum allowed size.
Definition: fds.h:99
@ FDS_ERR_NULL_ARG
Error. The parameter is NULL.
Definition: fds.h:95
@ FDS_ERR_NO_OPEN_RECORDS
Error. The record is not open, so it cannot be closed.
Definition: fds.h:96
@ FDS_ERR_UNALIGNED_ADDR
Error. The input data is not aligned to a word boundary.
Definition: fds.h:93
@ FDS_ERR_BUSY
Error. The underlying flash subsystem was busy.
Definition: fds.h:104
@ FDS_ERR_NO_SPACE_IN_FLASH
Error. There is no space in flash memory.
Definition: fds.h:97
@ FDS_ERR_OPERATION_TIMEOUT
Error. The operation timed out.
Definition: fds.h:91
@ FDS_SUCCESS
The operation completed successfully.
Definition: fds.h:90
@ FDS_ERR_CRC_CHECK_FAILED
Error. The CRC check failed.
Definition: fds.h:103
@ FDS_ERR_NOT_INITIALIZED
Error. The module has not been initialized.
Definition: fds.h:92
@ FDS_EVT_DEL_FILE
Event for fds_file_delete.
Definition: fds.h:199
@ FDS_EVT_INIT
Event for fds_init.
Definition: fds.h:195
@ FDS_EVT_GC
Event for fds_gc.
Definition: fds.h:200
@ FDS_EVT_DEL_RECORD
Event for fds_record_delete.
Definition: fds.h:198
@ FDS_EVT_UPDATE
Event for fds_record_update.
Definition: fds.h:197
@ FDS_EVT_WRITE
Event for fds_record_write and fds_record_write_reserved.
Definition: fds.h:196
#define FDS_OP_QUEUE_SIZE
Definition: sdk_config.h:7400
#define FDS_VIRTUAL_PAGES
Definition: sdk_config.h:7346
#define FDS_MAX_USERS
Definition: sdk_config.h:7440
#define FDS_CRC_CHECK_ON_READ
Definition: sdk_config.h:7416
An FDS event.
Definition: fds.h:208
struct fds_evt_t::@2::@4 write
Information for FDS_EVT_WRITE and FDS_EVT_UPDATE events.
fds_evt_id_t id
The event ID. See fds_evt_id_t.
Definition: fds.h:209
ret_code_t result
The result of the operation related to this event.
Definition: fds.h:210
struct fds_evt_t::@2::@5 del
Information for FDS_EVT_DEL_RECORD and FDS_EVT_DEL_FILE events.
A token to keep information about the progress of fds_record_find, fds_record_find_by_key,...
Definition: fds.h:185
uint32_t const * p_addr
Definition: fds.h:186
uint16_t page
Definition: fds.h:187
Structure that can be used to read the contents of a record stored in flash.
Definition: fds.h:147
fds_header_t const * p_header
Location of the record header in flash.
Definition: fds.h:148
void const * p_data
Location of the record data in flash.
Definition: fds.h:149
fds_gc_state_t state
uint32_t const * p_record_src
bool do_gc_page[FDS_DATA_PAGES]
The record metadata as stored in flash.
Definition: fds.h:113
uint16_t crc16
CRC16-CCITT check value.
Definition: fds.h:117
uint32_t record_id
The unique record ID (32 bits).
Definition: fds.h:121
uint16_t file_id
The ID of the file that the record belongs to.
Definition: fds.h:116
uint16_t length_words
The length of the record data (in 4-byte words).
Definition: fds.h:115
uint16_t record_key
The record key.
Definition: fds.h:114
struct fds_op_t::@7::@10 write
struct fds_op_t::@7::@11 del
fds_op_code_t op_code
struct fds_op_t::@7::@9 init
uint16_t words_reserved
uint32_t volatile records_open
uint32_t const * p_addr
uint16_t write_offset
fds_page_type_t page_type
The record descriptor structure that is used to manipulate records.
Definition: fds.h:133
uint16_t gc_run_count
Number of times garbage collection has been run.
Definition: fds.h:136
uint32_t record_id
The unique record ID.
Definition: fds.h:134
bool record_is_open
Whether the record is currently open.
Definition: fds.h:137
uint32_t const * p_record
The last known location of the record in flash.
Definition: fds.h:135
A record to be written to flash.
Definition: fds.h:155
uint32_t length_words
Definition: fds.h:161
struct fds_record_t::@1 data
uint16_t file_id
The ID of the file that the record belongs to.
Definition: fds.h:156
void const * p_data
Definition: fds.h:160
uint16_t key
The record key.
Definition: fds.h:157
A token to a reserved space in flash, created by fds_reserve.
Definition: fds.h:172
uint16_t length_words
The amount of space reserved (in 4-byte words).
Definition: fds.h:174
uint16_t page
The logical ID of the page where space was reserved.
Definition: fds.h:173
File system statistics.
Definition: fds.h:234
uint16_t words_reserved
The number of words reserved by fds_reserve().
Definition: fds.h:239
bool corruption
Filesystem corruption has been detected.
Definition: fds.h:265
uint16_t open_records
The number of open records.
Definition: fds.h:236
uint16_t freeable_words
The largest number of words that can be reclaimed by garbage collection.
Definition: fds.h:256
uint16_t valid_records
The number of valid records.
Definition: fds.h:237
uint16_t pages_available
The number of pages available.
Definition: fds.h:235
uint16_t largest_contig
The largest number of free contiguous words in the file system.
Definition: fds.h:249
uint16_t words_used
The number of words written to flash, including those reserved for future writes.
Definition: fds.h:242
uint16_t dirty_records
The number of deleted ("dirty") records.
Definition: fds.h:238
uint32_t const * p_addr