ruuvi.drivers.c  ${PROJECT_VERSION}
Drivers for external sensors and peripherals on embedded systems.
ruuvi_nrf5_sdk15_flash.c
Go to the documentation of this file.
1 
42 #include "ruuvi_interface_flash.h"
43 #if RUUVI_NRF5_SDK15_FLASH_ENABLED
44 #include "ruuvi_driver_error.h"
45 #include "ruuvi_nrf5_sdk15_error.h"
46 #include "ruuvi_interface_log.h"
47 #include "ruuvi_interface_yield.h"
48 
49 #include "fds.h"
50 #include "nrf_sdh.h"
51 #include "nrf_sdh_ble.h"
52 #include "sdk_config.h"
53 #include "nrf_fstorage.h"
54 #include "fds_internal_defs.h"
55 #if (FDS_BACKEND == NRF_FSTORAGE_SD)
56 #include "nrf_fstorage_sd.h"
57 #elif (FDS_BACKEND == NRF_FSTORAGE_NVMC)
58 #include "nrf_fstorage_nvmc.h"
59 #else
60 #error Invalid FDS backend.
61 #endif
62 
63 /* Inspired by CODE_* macros in app_util.h */
64 #if defined(__SES_ARM)
65 /* In flash_placement.xml
66  *
67  * <ProgramSection alignment="4" keep="Yes" load="No" name=".storage_flash"
68  * start="0x60000" size="0x15000" address_symbol="__start_storage_flash"
69  * end_symbol="__stop_storage_flash" />
70  *
71  */
72 extern uint32_t __start_storage_flash;
73 extern uint32_t __stop_storage_flash;
74 
75 #define FSTORAGE_SECTION_START ((uint32_t)&__start_storage_flash)
76 #define FSTORAGE_SECTION_END ((uint32_t)&__stop_storage_flash)
77 #elif defined ( __GNUC__ )
78 /* In linker:
79  *
80  * .storage_flash 0x60000 (NOLOAD) :
81  * {
82  * __start_storage_flash = .;
83  * KEEP(*(SORT(.storage_flash*)))
84  * __stop_storage_flash = . + 0x15000;
85  * } > FLASH
86  */
87 extern uint32_t __start_storage_flash;
88 extern uint32_t __stop_storage_flash;
89 #define FSTORAGE_SECTION_START ((uint32_t)&__start_storage_flash)
90 #define FSTORAGE_SECTION_END ((uint32_t)&__stop_storage_flash)
91 #endif
92 
93 #include <string.h>
94 
110 #define LOG_LEVEL RI_LOG_LEVEL_DEBUG
111 
112 static size_t m_number_of_pages = 0;
113 
115 static rd_status_t fds_to_ruuvi_error (ret_code_t err_code)
116 {
117  switch (err_code)
118  {
119  case FDS_SUCCESS:
120  return RD_SUCCESS;
121 
123  return RD_ERROR_TIMEOUT;
124 
126  return RD_ERROR_INVALID_STATE;
127 
129  return RD_ERROR_INTERNAL;
130 
131  case FDS_ERR_INVALID_ARG:
132  return RD_ERROR_INVALID_PARAM;
133 
134  case FDS_ERR_NULL_ARG:
135  return RD_ERROR_NULL;
136 
138  return RD_ERROR_INVALID_STATE;
139 
141  return RD_ERROR_NO_MEM;
142 
144  return RD_ERROR_BUSY;
145 
147  return RD_ERROR_DATA_SIZE;
148 
149  case FDS_ERR_NOT_FOUND:
150  return RD_ERROR_NOT_FOUND;
151 
152  case FDS_ERR_NO_PAGES:
153  return RD_ERROR_NO_MEM;
154 
156  return RD_ERROR_RESOURCES;
157 
159  return RD_ERROR_SELFTEST;
160 
161  case FDS_ERR_BUSY:
162  return RD_ERROR_BUSY;
163 
164  case FDS_ERR_INTERNAL:
165  default:
166  return RD_ERROR_INTERNAL;
167  }
168 
169  return RD_ERROR_INTERNAL;
170 }
171 
172 
173 /* Flag to check fds initialization. */
174 static bool volatile m_fds_initialized;
175 /* Flag to check fds processing status. */
176 static bool volatile m_fds_processing;
177 /* Flag to check fds callback registration. */
178 static bool m_fds_registered;
180 static void fds_evt_handler (fds_evt_t const * p_evt)
181 {
182  switch (p_evt->id)
183  {
184  case FDS_EVT_INIT:
185  if (p_evt->result == FDS_SUCCESS)
186  {
187  m_fds_initialized = true;
188  ri_log (LOG_LEVEL, "FDS init\r\n");
189  }
190 
191  break;
192 
193  case FDS_EVT_WRITE:
194  {
195  if (p_evt->result == FDS_SUCCESS)
196  {
197  ri_log (LOG_LEVEL, "Record written\r\n");
198  m_fds_processing = false;
199  }
200  }
201  break;
202 
203  case FDS_EVT_UPDATE:
204  {
205  if (p_evt->result == FDS_SUCCESS)
206  {
207  ri_log (LOG_LEVEL, "Record updated\r\n");
208  m_fds_processing = false;
209  }
210  }
211  break;
212 
213  case FDS_EVT_DEL_RECORD:
214  {
215  if (p_evt->result == FDS_SUCCESS)
216  {
217  ri_log (LOG_LEVEL, "Record deleted\r\n");
218  m_fds_processing = false;
219  }
220  }
221  break;
222 
223  case FDS_EVT_DEL_FILE:
224  {
225  if (p_evt->result == FDS_SUCCESS)
226  {
227  ri_log (LOG_LEVEL, "File deleted\r\n");
228  m_fds_processing = false;
229  }
230  }
231  break;
232 
233  case FDS_EVT_GC:
234  {
235  if (p_evt->result == FDS_SUCCESS)
236  {
237  ri_log (LOG_LEVEL, "Garbage collected\r\n");
238  m_fds_processing = false;
239  }
240  }
241  break;
242 
243  default:
244  break;
245  }
246 }
247 
248 rd_status_t ri_flash_total_size_get (size_t * const size)
249 {
250  rd_status_t err_code = RD_SUCCESS;
251 
252  if (NULL == size)
253  {
254  err_code |= RD_ERROR_NULL;
255  }
256  else if (false == m_fds_initialized)
257  {
258  err_code |= RD_ERROR_INVALID_STATE;
259  }
260  else
261  {
262  ri_flash_page_size_get (size);
263  *size *= m_number_of_pages;
264  }
265 
266  return err_code;
267 }
268 
269 rd_status_t ri_flash_free_size_get (size_t * const size)
270 {
271  rd_status_t err_code = RD_SUCCESS;
272 
273  if (NULL == size)
274  {
275  err_code |= RD_ERROR_NULL;
276  }
277  else if (false == m_fds_initialized)
278  {
279  err_code |= RD_ERROR_INVALID_STATE;
280  }
281  else
282  {
283  // Read filesystem status
284  fds_stat_t stat = {0};
285  ret_code_t rc = fds_stat (&stat);
286  *size = stat.largest_contig * sizeof (uint32_t);
287  err_code |= fds_to_ruuvi_error (rc);
288  }
289 
290  return err_code;
291 }
292 
293 rd_status_t ri_flash_page_size_get (size_t * size)
294 {
295  rd_status_t err_code = RD_SUCCESS;
296 
297  if (NULL == size)
298  {
299  err_code |= RD_ERROR_NULL;
300  }
301  else if (false == m_fds_initialized)
302  {
303  err_code |= RD_ERROR_INVALID_STATE;
304  }
305  else
306  {
307  *size = FDS_VIRTUAL_PAGE_SIZE;
308  }
309 
310  return err_code;
311 }
312 
313 rd_status_t ri_flash_record_delete (const uint32_t page_id,
314  const uint32_t record_id)
315 {
316  rd_status_t err_code = RD_SUCCESS;
317 
318  if (false == m_fds_initialized)
319  {
320  err_code |= RD_ERROR_INVALID_STATE;
321  }
322  else
323  {
324  fds_record_desc_t desc = {0};
325  fds_find_token_t tok = {0};
326  ret_code_t rc = fds_record_find (page_id, record_id, &desc, &tok);
327 
328  if (FDS_SUCCESS == rc)
329  {
330  // If there is room in FDS queue, it will get executed right away and
331  // processing flag is reset when record_delete exits.
332  m_fds_processing = true;
333  rc = fds_record_delete (&desc);
334 
335  // If operation was not queued, mark processing as false.
336  if (FDS_SUCCESS != rc)
337  {
338  m_fds_processing = false;
339  }
340  }
341 
342  err_code |= fds_to_ruuvi_error (rc);
343  }
344 
345  return err_code;
346 }
347 
348 rd_status_t ri_flash_record_set (const uint32_t page_id,
349  const uint32_t record_id, const size_t data_size, const void * const data)
350 {
351  rd_status_t err_code = RD_SUCCESS;
352 
353  if (NULL == data)
354  {
355  err_code |= RD_ERROR_NULL;
356  }
357  else if (false == m_fds_initialized)
358  {
359  err_code |= RD_ERROR_INVALID_STATE;
360  }
361  else if (m_fds_processing)
362  {
363  err_code |= RD_ERROR_BUSY;
364  }
365  else
366  {
367  rd_status_t err_code = RD_SUCCESS;
368  fds_record_desc_t desc = {0};
369  fds_find_token_t tok = {0};
370  /* A record structure. */
371  fds_record_t const record =
372  {
373  .file_id = page_id,
374  .key = record_id,
375  .data.p_data = data,
376  /* The length of a record is always expressed in 4-byte units (words). */
377  .data.length_words = (data_size + 3) / sizeof (uint32_t),
378  };
379  ret_code_t rc = fds_record_find (page_id, record_id, &desc, &tok);
380 
381  // If record was found
382  if (FDS_SUCCESS == rc)
383  {
384  /* Start write */
385  m_fds_processing = true;
386  rc = fds_record_update (&desc, &record);
387  err_code |= fds_to_ruuvi_error (rc);
388 
389  if (RD_SUCCESS != err_code)
390  {
391  m_fds_processing = false;
392  return err_code;
393  }
394  }
395  // If record was not found
396  else
397  {
398  /* Start write */
399  m_fds_processing = true;
400  desc.record_id = record_id;
401  rc = fds_record_write (&desc, &record);
402  err_code |= fds_to_ruuvi_error (rc);
403 
404  if (RD_SUCCESS != err_code)
405  {
406  m_fds_processing = false;
407  return err_code;
408  }
409  }
410  }
411 
412  return err_code;
413 }
414 
415 rd_status_t ri_flash_record_get (const uint32_t page_id,
416  const uint32_t record_id, const size_t data_size, void * const data)
417 {
418  rd_status_t err_code = RD_SUCCESS;
419  ret_code_t rc = NRF_SUCCESS;
420 
421  if (NULL == data)
422  {
423  err_code |= RD_ERROR_NULL;
424  }
425  else if (false == m_fds_initialized)
426  {
427  err_code |= RD_ERROR_INVALID_STATE;
428  }
429  else if (m_fds_processing)
430  {
431  err_code |= RD_ERROR_BUSY;
432  }
433  else
434  {
435  rd_status_t err_code = RD_SUCCESS;
436  fds_record_desc_t desc = {0};
437  fds_find_token_t tok = {0};
438  rc = fds_record_find (page_id, record_id, &desc, &tok);
439  err_code |= fds_to_ruuvi_error (rc);
440 
441  // If file was found
442  if (FDS_SUCCESS == rc)
443  {
444  fds_flash_record_t record = {0};
445  /* Open the record and read its contents. */
446  rc = fds_record_open (&desc, &record);
447 
448  // Translate FDS error to Ruuvi error if any
449  if (FDS_SUCCESS != rc)
450  {
451  err_code |= fds_to_ruuvi_error (rc);
452  }
453  // Check length if record was read
454  else if (record.p_header->length_words * 4 > data_size)
455  {
456  err_code |= RD_ERROR_DATA_SIZE;
457  ri_log (RI_LOG_LEVEL_ERROR, "Flash record does not fit in buffer\n");
458  }
459  else
460  {
461  /* Copy the data from flash into RAM. */
462  memcpy (data, record.p_data, record.p_header->length_words * 4);
463  }
464 
465  /* Close the record when done reading. */
466  rc = fds_record_close (&desc);
467  }
468  }
469 
470  return err_code | fds_to_ruuvi_error (rc);
471 }
472 
474 {
475  rd_status_t err_code = RD_SUCCESS;
476 
477  if (false == m_fds_initialized)
478  {
479  err_code |= RD_ERROR_INVALID_STATE;
480  }
481  else if (m_fds_processing)
482  {
483  err_code |= RD_ERROR_BUSY;
484  }
485  else
486  {
487  m_fds_processing = true;
488  ret_code_t rc = fds_gc();
489  err_code |= fds_to_ruuvi_error (rc);
490  }
491 
492  return err_code;
493 }
494 
496 {
497  rd_status_t err_code = RD_SUCCESS;
498  ret_code_t rc = NRF_SUCCESS;
499 
500  if (m_fds_initialized)
501  {
502  err_code |= RD_ERROR_INVALID_STATE;
503  }
504  else
505  {
506  /* Register first to receive an event when initialization is complete. */
507  if (!m_fds_registered)
508  {
509  rc |= flash_bounds_set (FSTORAGE_SECTION_START, FSTORAGE_SECTION_END);
510  (void) fds_register (fds_evt_handler);
511  m_fds_registered = true;
512  }
513 
514  rc = fds_init();
515  err_code |= fds_to_ruuvi_error (rc);
516 
517  if (RD_SUCCESS == err_code)
518  {
519  // Wait for init ok
520  while (!m_fds_initialized) {};
521 
522  // Read filesystem status
523  fds_stat_t stat = {0};
524 
525  rc = fds_stat (&stat);
526 
527  m_number_of_pages = stat.pages_available;
528  }
529  }
530 
531  err_code |= fds_to_ruuvi_error (rc);
532  return err_code;
533 }
534 
536 {
537  rd_status_t err_code = RD_SUCCESS;
538  m_fds_initialized = false;
539  m_fds_processing = false;
540  return err_code;
541 }
542 
544 void ri_flash_purge (void)
545 {
546  ret_code_t rc = NRF_SUCCESS;
547 #if defined(NRF51)
548  const int erase_unit = 1024;
549 #elif defined(NRF52_SERIES)
550  const int erase_unit = 4096;
551 #endif
552  const int total_pages = (FSTORAGE_SECTION_END - FSTORAGE_SECTION_START)
553  / erase_unit;
554 
555  for (int p = 0; (p < total_pages) && (NRF_SUCCESS == rc); p++)
556  {
557  int page = (FSTORAGE_SECTION_START / erase_unit) + p; // erase unit == virtual page size
558  rc = sd_flash_page_erase (page);
559  ri_delay_ms (200);
561  }
562 }
563 
564 bool ri_flash_is_busy()
565 {
566  return m_fds_processing;
567 }
568 
569 rd_status_t ri_flash_protect (const size_t page)
570 {
571  /* nRF52832_xxaa only supported */
572  const uint8_t register_num = page / 32U;
573  const uint8_t page_num = page % 32U;
574  rd_status_t err_code = RD_SUCCESS;
575 
576  if ( (register_num > 3) || (page_num > 31U))
577  {
578  err_code |= RD_ERROR_INVALID_PARAM;
579  }
580  else switch (register_num)
581  {
582  case 0:
583  NRF_BPROT->CONFIG0 = 1 << page_num;
584  break;
585 
586  case 1:
587  NRF_BPROT->CONFIG1 = 1 << page_num;
588  break;
589 
590  case 2:
591  NRF_BPROT->CONFIG2 = 1 << page_num;
592  break;
593 
594  case 3:
595  NRF_BPROT->CONFIG3 = 1 << page_num;
596  break;
597 
598  default:
599  break;
600  }
601 
602  return err_code;
603 }
604 
605 #endif
#define RD_ERROR_INVALID_PARAM
Invalid Parameter.
#define RD_ERROR_NULL
Null Pointer.
uint32_t rd_status_t
bitfield for representing errors
#define RD_ERROR_CHECK(error, mask)
Shorthand macro for calling the rd_error_check with current file & line.
#define RD_ERROR_RESOURCES
Not enough resources for operation.
#define RD_SUCCESS
Internal Error.
#define RD_ERROR_DATA_SIZE
Invalid Data size.
#define RD_ERROR_NO_MEM
No Memory for operation.
#define RD_ERROR_TIMEOUT
Operation timed out.
#define RD_ERROR_SELFTEST
Self-test fail.
#define RD_ERROR_NOT_FOUND
Not found.
#define RD_ERROR_INVALID_STATE
Invalid state, operation disallowed in this state.
#define RD_ERROR_INTERNAL
Internal Error.
#define RD_ERROR_BUSY
Busy.
rd_status_t ri_flash_gc_run(void)
Run garbage collection.
bool ri_flash_is_busy()
Check if flash is busy.
rd_status_t ri_flash_uninit(void)
Unintialize flash. After uninitialization only initialization can be used.
rd_status_t ri_flash_page_size_get(size_t *size)
Get size of usable page, excluding any overhead bytes If returned value is N, a record of N bytes mus...
rd_status_t ri_flash_total_size_get(size_t *size)
Get total size of usable flash, excluding any overhead bytes.
void ri_flash_purge(void)
Purge flash.
rd_status_t ri_flash_record_set(const uint32_t page_id, const uint32_t record_id, const size_t data_size, const void *const data)
Set data to record in page.
rd_status_t ri_flash_init(void)
rd_status_t ri_flash_record_delete(const uint32_t file_id, const uint32_t record_id)
Mark a record for deletion.
rd_status_t ri_flash_free_size_get(size_t *size)
Get total size of free flash.
rd_status_t ri_flash_protect(const size_t page)
rd_status_t ri_flash_record_get(const uint32_t page_id, const uint32_t record_id, const size_t data_size, void *const data)
Get data from record in page.
void ri_log(const ri_log_severity_t severity, const char *const message)
Queues messages into log.
@ RI_LOG_LEVEL_ERROR
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.
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_stat(fds_stat_t *p_stat)
Function for retrieving file system statistics.
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_gc(void)
Function for running garbage collection.
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.
ret_code_t fds_register(fds_cb_t cb)
Function for registering an FDS event handler.
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
Header to enable and disable module compilation.
Ruuvi error codes and error check function.
Interface functions to persistent flash storage.
rd_status_t ri_delay_ms(uint32_t time)
Delay a given number of milliseconds.
#define FDS_VIRTUAL_PAGE_SIZE
Definition: sdk_config.h:7359
An FDS event.
Definition: fds.h:208
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
A token to keep information about the progress of fds_record_find, fds_record_find_by_key,...
Definition: fds.h:185
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
uint16_t length_words
The length of the record data (in 4-byte words).
Definition: fds.h:115
The record descriptor structure that is used to manipulate records.
Definition: fds.h:133
uint32_t record_id
The unique record ID.
Definition: fds.h:134
A record to be written to flash.
Definition: fds.h:155
uint16_t file_id
The ID of the file that the record belongs to.
Definition: fds.h:156
File system statistics.
Definition: fds.h:234
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