ruuvi.drivers.c ${PROJECT_VERSION}
Drivers for external sensors and peripherals on embedded systems.
Loading...
Searching...
No Matches
ruuvi_nrf5_sdk15_flash.c
Go to the documentation of this file.
1
43#if RUUVI_NRF5_SDK15_FLASH_ENABLED
44#include "ruuvi_driver_error.h"
46#include "ruuvi_interface_log.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 */
72extern uint32_t __start_storage_flash;
73extern 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 */
87extern uint32_t __start_storage_flash;
88extern 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
112static size_t m_number_of_pages = 0;
113
115static 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
122 case FDS_ERR_OPERATION_TIMEOUT:
123 return RD_ERROR_TIMEOUT;
124
125 case FDS_ERR_NOT_INITIALIZED:
127
128 case FDS_ERR_UNALIGNED_ADDR:
129 return RD_ERROR_INTERNAL;
130
131 case FDS_ERR_INVALID_ARG:
133
134 case FDS_ERR_NULL_ARG:
135 return RD_ERROR_NULL;
136
137 case FDS_ERR_NO_OPEN_RECORDS:
139
140 case FDS_ERR_NO_SPACE_IN_FLASH:
141 return RD_ERROR_NO_MEM;
142
143 case FDS_ERR_NO_SPACE_IN_QUEUES:
144 return RD_ERROR_BUSY;
145
146 case FDS_ERR_RECORD_TOO_LARGE:
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
155 case FDS_ERR_USER_LIMIT_REACHED:
156 return RD_ERROR_RESOURCES;
157
158 case FDS_ERR_CRC_CHECK_FAILED:
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. */
174static bool volatile m_fds_initialized;
175/* Flag to check fds processing status. */
176static bool volatile m_fds_processing;
177/* Flag to check fds callback registration. */
178static bool m_fds_registered;
180static 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
248rd_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 {
263 *size *= m_number_of_pages;
264 }
265
266 return err_code;
267}
268
269rd_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
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
313rd_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
348rd_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
415rd_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
544void 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
564bool ri_flash_is_busy()
565{
566 return m_fds_processing;
567}
568
569rd_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
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