ChibiOS 21.11.5
hal_mfs.c
Go to the documentation of this file.
1/*
2 ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
3
4 This file is part of ChibiOS.
5
6 ChibiOS is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation version 3 of the License.
9
10 ChibiOS is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file hal_mfs.c
21 * @brief Managed Flash Storage module code.
22 * @details This module manages a flash partition as a generic storage where
23 * arbitrary data records can be created, updated, deleted and
24 * retrieved.<br>
25 * A managed partition is composed of two banks of equal size, a
26 * bank is composed of one or more erasable sectors, a sector is
27 * divided in writable pages.<br>
28 * The module handles flash wear leveling and recovery of damaged
29 * banks (where possible) caused by power loss during operations.
30 * Both operations are transparent to the user.
31 *
32 * @addtogroup HAL_MFS
33 * @{
34 */
35
36#include <string.h>
37
38#include "hal.h"
39
40#include "hal_mfs.h"
41
42/*===========================================================================*/
43/* Driver local definitions. */
44/*===========================================================================*/
45
46/**
47 * @brief Data record size aligned.
48 */
49#define ALIGNED_REC_SIZE(n) \
50 (flash_offset_t)MFS_ALIGN_NEXT(sizeof (mfs_data_header_t) + (size_t)(n))
51
52/**
53 * @brief Data record header size aligned.
54 */
55#define ALIGNED_DHDR_SIZE \
56 ALIGNED_REC_SIZE(0)
57
58/**
59 * @brief Aligned size of a type.
60 */
61#define ALIGNED_SIZEOF(t) \
62 (((sizeof (t) - 1U) | MFS_ALIGN_MASK) + 1U)
63
64/**
65 * @brief Combines two values (0..3) in one (0..15).
66 */
67#define PAIR(a, b) (((unsigned)(a) << 2U) | (unsigned)(b))
68
69/**
70 * @brief Error check helper.
71 */
72#define RET_ON_ERROR(err) do { \
73 mfs_error_t e = (err); \
74 if (e != MFS_NO_ERROR) { \
75 return e; \
76 } \
77} while (false)
78
79#if (MFS_USE_FLASH_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__)
80static void mfs_flash_acquire(MFSDriver *mfsp) {
81 flash_error_t ferr;
82
83 ferr = flashAcquireExclusive(mfsp->config->flashp);
84 osalDbgAssert(ferr == FLASH_NO_ERROR, "flash exclusive access failed");
85}
86
87static void mfs_flash_release(MFSDriver *mfsp) {
88 flash_error_t ferr;
89
90 ferr = flashReleaseExclusive(mfsp->config->flashp);
91 osalDbgAssert(ferr == FLASH_NO_ERROR, "flash exclusive release failed");
92}
93
94#else
95#define mfs_flash_acquire(mfsp) (void)(mfsp)
96#define mfs_flash_release(mfsp) (void)(mfsp)
97#endif
98
99/*===========================================================================*/
100/* Driver exported variables. */
101/*===========================================================================*/
102
103/*===========================================================================*/
104/* Driver local variables and types. */
105/*===========================================================================*/
106
107static const uint16_t crc16_table[256] = {
108 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
109 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
110 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
111 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
112 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
113 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
114 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
115 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
116 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
117 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
118 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
119 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
120 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
121 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
122 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
123 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
124 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
125 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
126 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
127 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
128 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
129 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
130 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
131 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
132 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
133 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
134 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
135 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
136 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
137 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
138 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
139 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
140};
141
142/*===========================================================================*/
143/* Driver local functions. */
144/*===========================================================================*/
145
146uint16_t crc16(uint16_t crc, const uint8_t *data, size_t n) {
147
148 while (n > 0U) {
149 crc = (crc << 8U) ^ crc16_table[(crc >> 8U) ^ (uint16_t)*data];
150 data++;
151 n--;
152 }
153
154 return crc;
155}
156
157static void mfs_state_reset(MFSDriver *mfsp) {
158 unsigned i;
159
160 mfsp->current_bank = MFS_BANK_0;
161 mfsp->current_counter = 0U;
162 mfsp->next_offset = 0U;
163 mfsp->used_space = 0U;
164
165#if (MFS_CFG_TRANSACTION_MAX > 0)
166 mfsp->tr_nops = 0U;
167 mfsp->tr_next_offset = 0U;
168 mfsp->tr_limit_offset = 0U;
169#endif
170
171 for (i = 0; i < MFS_CFG_MAX_RECORDS; i++) {
172 mfsp->descriptors[i].offset = 0U;
173 mfsp->descriptors[i].size = 0U;
174 }
175}
176
178 mfs_bank_t bank) {
179
180 return bank == MFS_BANK_0 ? flashGetSectorOffset(mfsp->config->flashp,
181 mfsp->config->bank0_start) :
183 mfsp->config->bank1_start);
184}
185
186/**
187 * @brief Flash read.
188 *
189 * @param[in] mfsp pointer to the @p MFSDriver object
190 * @param[in] offset flash offset
191 * @param[in] n number of bytes to be read
192 * @param[out] rp pointer to the data buffer
193 * @return The operation status.
194 *
195 * @notapi
196 */
198 size_t n, uint8_t *rp) {
199 flash_error_t ferr;
200 mfs_error_t err;
201
202 mfs_flash_acquire(mfsp);
203
204 ferr = flashRead(mfsp->config->flashp, offset, n, rp);
205 if (ferr != FLASH_NO_ERROR) {
206 mfsp->state = MFS_ERROR;
208 }
209 else {
210 err = MFS_NO_ERROR;
211 }
212
213 mfs_flash_release(mfsp);
214
215 return err;
216}
217
218/**
219 * @brief Flash write.
220 * @note If the option @p MFS_CFG_WRITE_VERIFY is enabled then the flash
221 * is also read back for verification.
222 *
223 * @param[in] mfsp pointer to the @p MFSDriver object
224 * @param[in] offset flash offset
225 * @param[in] n number of bytes to be written
226 * @param[in] wp pointer to the data buffer
227 * @return The operation status.
228 *
229 * @notapi
230 */
232 flash_offset_t offset,
233 size_t n,
234 const uint8_t *wp) {
235 flash_error_t ferr;
236
237 mfs_flash_acquire(mfsp);
238
239 ferr = flashProgram(mfsp->config->flashp, offset, n, wp);
240 if (ferr != FLASH_NO_ERROR) {
241 mfsp->state = MFS_ERROR;
242 mfs_flash_release(mfsp);
244 }
245
246 mfs_flash_release(mfsp);
247
248#if MFS_CFG_WRITE_VERIFY == TRUE
249 /* Verifying the written data by reading it back and comparing.*/
250 while (n > 0U) {
251 size_t chunk = n <= MFS_CFG_BUFFER_SIZE ? n : MFS_CFG_BUFFER_SIZE;
252
253 RET_ON_ERROR(mfs_flash_read(mfsp, offset, chunk, mfsp->ncbuf->data8));
254
255 if (memcmp((void *)mfsp->ncbuf->data8, (void *)wp, chunk)) {
256 mfsp->state = MFS_ERROR;
258 }
259 n -= chunk;
260 offset += (flash_offset_t)chunk;
261 wp += chunk;
262 }
263#endif
264
265 return MFS_NO_ERROR;
266}
267
268/**
269 * @brief Flash copy.
270 * @note If the option @p MFS_CFG_WRITE_VERIFY is enabled then the flash
271 * is also read back for verification.
272 *
273 * @param[in] mfsp pointer to the @p MFSDriver object
274 * @param[in] doffset destination flash offset
275 * @param[in] soffset source flash offset
276 * @param[in] n number of bytes to be copied
277 * @return The operation status.
278 *
279 * @notapi
280 */
282 flash_offset_t doffset,
283 flash_offset_t soffset,
284 uint32_t n) {
285
286 /* Splitting the operation in smaller operations because the buffer is
287 small.*/
288 while (n > 0U) {
289 /* Data size that can be written in a single program page operation.*/
290 size_t chunk = (size_t)(((doffset | (MFS_CFG_BUFFER_SIZE - 1U)) + 1U) -
291 doffset);
292 if (chunk > n) {
293 chunk = n;
294 }
295
296 RET_ON_ERROR(mfs_flash_read(mfsp, soffset, chunk, mfsp->ncbuf->data8));
297 RET_ON_ERROR(mfs_flash_write(mfsp, doffset, chunk, mfsp->ncbuf->data8));
298
299 /* Next page.*/
300 soffset += chunk;
301 doffset += chunk;
302 n -= chunk;
303 }
304
305 return MFS_NO_ERROR;
306}
307
308/**
309 * @brief Erases and verifies all sectors belonging to a bank.
310 *
311 * @param[in] mfsp pointer to the @p MFSDriver object
312 * @param[in] bank bank to be erased
313 * @return The operation status.
314 *
315 * @notapi
316 */
318 flash_sector_t sector, end;
319
320 if (bank == MFS_BANK_0) {
321 sector = mfsp->config->bank0_start;
322 end = mfsp->config->bank0_start + mfsp->config->bank0_sectors;
323 }
324 else {
325 sector = mfsp->config->bank1_start;
326 end = mfsp->config->bank1_start + mfsp->config->bank1_sectors;
327 }
328
329 while (sector < end) {
330 flash_error_t ferr;
331
332 mfs_flash_acquire(mfsp);
333
334 ferr = flashStartEraseSector(mfsp->config->flashp, sector);
335 if (ferr != FLASH_NO_ERROR) {
336 mfsp->state = MFS_ERROR;
337 mfs_flash_release(mfsp);
339 }
340 ferr = flashWaitErase(mfsp->config->flashp);
341 if (ferr != FLASH_NO_ERROR) {
342 mfsp->state = MFS_ERROR;
343 mfs_flash_release(mfsp);
345 }
346 ferr = flashVerifyErase(mfsp->config->flashp, sector);
347 if (ferr != FLASH_NO_ERROR) {
348 mfsp->state = MFS_ERROR;
349 mfs_flash_release(mfsp);
351 }
352
353 mfs_flash_release(mfsp);
354
355 sector++;
356 }
357
358 return MFS_NO_ERROR;
359}
360
361/**
362 * @brief Erases and verifies all sectors belonging to a bank.
363 *
364 * @param[in] mfsp pointer to the @p MFSDriver object
365 * @param[in] bank bank to be verified
366 * @return The operation status.
367 *
368 * @notapi
369 */
371 flash_sector_t sector, end;
372
373 if (bank == MFS_BANK_0) {
374 sector = mfsp->config->bank0_start;
375 end = mfsp->config->bank0_start + mfsp->config->bank0_sectors;
376 }
377 else {
378 sector = mfsp->config->bank1_start;
379 end = mfsp->config->bank1_start + mfsp->config->bank1_sectors;
380 }
381
382 while (sector < end) {
383 flash_error_t ferr;
384
385 mfs_flash_acquire(mfsp);
386
387 ferr = flashVerifyErase(mfsp->config->flashp, sector);
388 if (ferr == FLASH_ERROR_VERIFY) {
389 mfs_flash_release(mfsp);
390 return MFS_ERR_NOT_ERASED;
391 }
392 if (ferr != FLASH_NO_ERROR) {
393 mfsp->state = MFS_ERROR;
394 mfs_flash_release(mfsp);
396 }
397
398 mfs_flash_release(mfsp);
399
400 sector++;
401 }
402
403 return MFS_NO_ERROR;
404}
405
406/**
407 * @brief Writes the validation header in a bank.
408 *
409 * @param[in] mfsp pointer to the @p MFSDriver object
410 * @param[in] bank bank to be validated
411 * @param[in] cnt value for the flash usage counter
412 * @return The operation status.
413 *
414 * @notapi
415 */
417 mfs_bank_t bank,
418 uint32_t cnt) {
419 flash_sector_t sector;
420
421 if (bank == MFS_BANK_0) {
422 sector = mfsp->config->bank0_start;
423 }
424 else {
425 sector = mfsp->config->bank1_start;
426 }
427
430 mfsp->ncbuf->bhdr.fields.counter = cnt;
431 mfsp->ncbuf->bhdr.fields.reserved1 = (uint16_t)mfsp->config->erased;
432 mfsp->ncbuf->bhdr.fields.crc = crc16(0xFFFFU,
433 mfsp->ncbuf->bhdr.hdr8,
434 sizeof (mfs_bank_header_t) - sizeof (uint16_t));
435
436 return mfs_flash_write(mfsp,
437 flashGetSectorOffset(mfsp->config->flashp, sector),
438 sizeof (mfs_bank_header_t),
439 mfsp->ncbuf->bhdr.hdr8);
440}
441
442/**
443 * @brief Checks integrity of the header in the shared buffer.
444 *
445 * @param[in] mfsp pointer to the @p MFSDriver object
446 * @return The header state.
447 *
448 * @notapi
449 */
451 uint16_t crc;
452
453 if ((mfsp->ncbuf->bhdr.hdr32[0] == mfsp->config->erased) &&
454 (mfsp->ncbuf->bhdr.hdr32[1] == mfsp->config->erased) &&
455 (mfsp->ncbuf->bhdr.hdr32[2] == mfsp->config->erased) &&
456 (mfsp->ncbuf->bhdr.hdr32[3] == mfsp->config->erased)) {
457 return MFS_BANK_ERASED;
458 }
459
460 /* Checking header fields integrity.*/
461 if ((mfsp->ncbuf->bhdr.fields.magic1 != MFS_BANK_MAGIC_1) ||
463 (mfsp->ncbuf->bhdr.fields.counter == mfsp->config->erased) ||
464 (mfsp->ncbuf->bhdr.fields.reserved1 != (uint16_t)mfsp->config->erased)) {
465 return MFS_BANK_GARBAGE;
466 }
467
468 /* Verifying header CRC.*/
469 crc = crc16(0xFFFFU, mfsp->ncbuf->bhdr.hdr8,
470 sizeof (mfs_bank_header_t) - sizeof (uint16_t));
471 if (crc != mfsp->ncbuf->bhdr.fields.crc) {
472 return MFS_BANK_GARBAGE;
473 }
474
475 return MFS_BANK_OK;
476}
477
478/**
479 * @brief Scans blocks searching for records.
480 * @note The block integrity is strongly checked.
481 *
482 * @param[in] mfsp pointer to the @p MFSDriver object
483 * @param[in] bank the bank identifier
484 * @param[out] wflagp warning flag on anomalies
485 *
486 * @return The operation status.
487 *
488 * @notapi
489 */
491 mfs_bank_t bank,
492 bool *wflagp) {
493 flash_offset_t hdr_offset, start_offset, end_offset;
494
495 /* No warning by default.*/
496 *wflagp = false;
497
498 /* Boundaries.*/
499 start_offset = mfs_flash_get_bank_offset(mfsp, bank);
500 hdr_offset = start_offset + (flash_offset_t)ALIGNED_SIZEOF(mfs_bank_header_t);
501 end_offset = start_offset + mfsp->config->bank_size;
502
503 /* Scanning records until there is there is not enough space left for an
504 header.*/
505 while (hdr_offset < end_offset - ALIGNED_DHDR_SIZE) {
507 uint16_t crc;
508 flash_offset_t data_offset;
509 flash_offset_t data_available;
510
511 /* Reading the current record header.*/
512 RET_ON_ERROR(mfs_flash_read(mfsp, hdr_offset,
513 sizeof (mfs_data_header_t),
514 mfsp->ncbuf->data8));
515
516 /* Checking if the found header is in erased state.*/
517 if ((mfsp->ncbuf->data32[0] == mfsp->config->erased) &&
518 (mfsp->ncbuf->data32[1] == mfsp->config->erased) &&
519 (mfsp->ncbuf->data32[2] == mfsp->config->erased)) {
520 break;
521 }
522
523 data_offset = hdr_offset + (flash_offset_t)sizeof (mfs_data_header_t);
524 if (data_offset > end_offset) {
525 *wflagp = true;
526 break;
527 }
528 data_available = end_offset - data_offset;
529
530 /* It is not erased so checking for integrity.*/
531 if ((mfsp->ncbuf->dhdr.fields.magic1 != MFS_HEADER_MAGIC_1) ||
533 (mfsp->ncbuf->dhdr.fields.id < 1U) ||
534 (mfsp->ncbuf->dhdr.fields.id > (uint32_t)MFS_CFG_MAX_RECORDS) ||
535 (mfsp->ncbuf->dhdr.fields.size > data_available)) {
536 *wflagp = true;
537 break;
538 }
539
540 /* Copying the non-cached buffer locally.*/
541 dhdr = mfsp->ncbuf->dhdr;
542
543 /* Finally checking the CRC, we need to perform it in chunks because
544 we have a limited buffer.*/
545 crc = 0xFFFFU;
546 if (dhdr.fields.size > 0U) {
547 uint32_t total = dhdr.fields.size;
548
549 while (total > 0U) {
550 uint32_t chunk = total > MFS_CFG_BUFFER_SIZE ? MFS_CFG_BUFFER_SIZE :
551 total;
552
553 /* Reading the data chunk.*/
554 RET_ON_ERROR(mfs_flash_read(mfsp, data_offset, chunk,
555 mfsp->ncbuf->data8));
556
557 /* CRC on the read data chunk.*/
558 crc = crc16(crc, &mfsp->ncbuf->data8[0], chunk);
559
560 /* Next chunk.*/
561 data_offset += chunk;
562 total -= chunk;
563 }
564 }
565 if (crc != dhdr.fields.crc) {
566 /* If the CRC is invalid then this record is ignored but scanning
567 continues because there could be more valid records afterward.*/
568 *wflagp = true;
569 }
570 else {
571 /* Zero-sized records are erase markers.*/
572 if (dhdr.fields.size == 0U) {
573 mfsp->descriptors[dhdr.fields.id - 1U].offset = 0U;
574 mfsp->descriptors[dhdr.fields.id - 1U].size = 0U;
575 }
576 else {
577 mfsp->descriptors[dhdr.fields.id - 1U].offset = hdr_offset;
578 mfsp->descriptors[dhdr.fields.id - 1U].size = dhdr.fields.size;
579 }
580 }
581
582 /* On the next header.*/
583 hdr_offset = hdr_offset + ALIGNED_REC_SIZE(dhdr.fields.size);
584 }
585
586 /* Next writable offset.*/
587 mfsp->next_offset = hdr_offset;
588
589 return MFS_NO_ERROR;
590}
591
592/**
593 * @brief Determines the state of a bank.
594 * @note This function does not test the bank integrity by scanning
595 * the data area, it just checks the header.
596 *
597 * @param[in] mfsp pointer to the @p MFSDriver object
598 * @param[in] bank bank to be checked
599 * @param[out] statep bank state, it can be:
600 * - MFS_BANK_ERASED
601 * - MFS_BANK_GARBAGE
602 * - MFS_BANK_OK
603 * @param[out] cntp bank counter
604 * @return The operation status.
605 *
606 * @notapi
607 */
609 mfs_bank_t bank,
610 mfs_bank_state_t *statep,
611 uint32_t *cntp) {
612
613 /* Reading the current bank header.*/
615 sizeof (mfs_bank_header_t),
616 mfsp->ncbuf->data8));
617
618 /* Getting the counter regardless of the bank state, it is only valid if
619 the state is MFS_BANK_OK.*/
620 *cntp = mfsp->ncbuf->bhdr.fields.counter;
621
622 /* Checking just the header.*/
623 *statep = mfs_bank_check_header(mfsp);
624 if (*statep == MFS_BANK_ERASED) {
625 mfs_error_t err;
626
627 /* Checking if the bank is really all erased.*/
628 err = mfs_bank_verify_erase(mfsp, bank);
629 if (err == MFS_ERR_NOT_ERASED) {
630 *statep = MFS_BANK_GARBAGE;
631 }
632 }
633
634 return MFS_NO_ERROR;
635}
636
637/**
638 * @brief Enforces a garbage collection.
639 * @details Storage data is compacted into a single bank.
640 *
641 * @param[out] mfsp pointer to the @p MFSDriver object
642 * @return The operation status.
643 *
644 * @notapi
645 */
647 unsigned i;
648 mfs_bank_t sbank, dbank;
649 flash_offset_t dest_offset;
650
651 sbank = mfsp->current_bank;
652 if (sbank == MFS_BANK_0) {
653 dbank = MFS_BANK_1;
654 }
655 else {
656 dbank = MFS_BANK_0;
657 }
658
659 /* Write address.*/
660 dest_offset = mfs_flash_get_bank_offset(mfsp, dbank) +
662
663 /* Copying the most recent record instances only.*/
664 for (i = 0; i < MFS_CFG_MAX_RECORDS; i++) {
665 uint32_t totsize = ALIGNED_REC_SIZE(mfsp->descriptors[i].size);
666 if (mfsp->descriptors[i].offset != 0) {
667 RET_ON_ERROR(mfs_flash_copy(mfsp, dest_offset,
668 mfsp->descriptors[i].offset,
669 totsize));
670 mfsp->descriptors[i].offset = dest_offset;
671 dest_offset += totsize;
672 }
673 }
674
675 /* New current bank.*/
676 mfsp->current_bank = dbank;
677 mfsp->current_counter += 1U;
678 mfsp->next_offset = dest_offset;
679
680 /* The header is written after the data.*/
682
683 /* The source bank is erased last.*/
684 RET_ON_ERROR(mfs_bank_erase(mfsp, sbank));
685
686 return MFS_NO_ERROR;
687}
688
689/**
690 * @brief Performs a flash partition mount attempt.
691 *
692 * @param[in] mfsp pointer to the @p MFSDriver object
693 * @return The operation status.
694 *
695 * @api
696 */
698 mfs_bank_state_t sts0, sts1;
699 mfs_bank_t bank;
700 uint32_t cnt0 = 0, cnt1 = 0;
701 bool w1 = false, w2 = false;
702
703 /* Resetting the bank state.*/
704 mfs_state_reset(mfsp);
705
706 /* Assessing the state of the two banks.*/
707 RET_ON_ERROR(mfs_bank_get_state(mfsp, MFS_BANK_0, &sts0, &cnt0));
708 RET_ON_ERROR(mfs_bank_get_state(mfsp, MFS_BANK_1, &sts1, &cnt1));
709
710 /* Handling all possible scenarios, each one requires its own recovery
711 strategy.*/
712 switch (PAIR(sts0, sts1)) {
713
715 /* Both banks erased, first initialization.*/
717 bank = MFS_BANK_0;
718 break;
719
721 /* Both banks appear to be valid but one must be newer, erasing the
722 older one.*/
723 if (cnt0 > cnt1) {
724 /* Bank 0 is newer.*/
726 bank = MFS_BANK_0;
727 }
728 else {
729 /* Bank 1 is newer.*/
731 bank = MFS_BANK_1;
732 }
733 w1 = true;
734 break;
735
737 /* Both banks are unreadable, reinitializing.*/
741 bank = MFS_BANK_0;
742 w1 = true;
743 break;
744
746 /* Normal situation, bank one is used.*/
747 bank = MFS_BANK_1;
748 break;
749
751 /* Normal situation, bank zero is used.*/
752 bank = MFS_BANK_0;
753 break;
754
756 /* Bank zero is erased, bank one is not readable.*/
759 bank = MFS_BANK_0;
760 w1 = true;
761 break;
762
764 /* Bank zero is not readable, bank one is erased.*/
767 bank = MFS_BANK_1;
768 w1 = true;
769 break;
770
772 /* Bank zero is normal, bank one is unreadable.*/
774 bank = MFS_BANK_0;
775 w1 = true;
776 break;
777
779 /* Bank zero is unreadable, bank one is normal.*/
781 bank = MFS_BANK_1;
782 w1 = true;
783 break;
784
785 default:
786 return MFS_ERR_INTERNAL;
787 }
788
789 /* Mounting the bank.*/
790 {
791 unsigned i;
792
793 /* Reading the bank header again.*/
795 sizeof (mfs_bank_header_t),
796 mfsp->ncbuf->data8));
797
798 /* Checked again for extra safety.*/
799 if (mfs_bank_check_header(mfsp) != MFS_BANK_OK) {
800 return MFS_ERR_INTERNAL;
801 }
802
803 /* Storing the bank data.*/
804 mfsp->current_bank = bank;
805 mfsp->current_counter = mfsp->ncbuf->bhdr.fields.counter;
806
807 /* Scanning for the most recent instance of all records.*/
808 RET_ON_ERROR(mfs_bank_scan_records(mfsp, bank, &w2));
809
810 /* Calculating the effective used size.*/
812 for (i = 0; i < MFS_CFG_MAX_RECORDS; i++) {
813 if (mfsp->descriptors[i].offset != 0U) {
814 mfsp->used_space += ALIGNED_REC_SIZE(mfsp->descriptors[i].size);
815 }
816 }
817 }
818
819 /* In case of detected problems then a garbage collection is performed in
820 order to repair/remove anomalies.*/
821 if (w2) {
823 }
824
825 return (w1 || w2) ? MFS_WARN_REPAIR : MFS_NO_ERROR;
826}
827
828/**
829 * @brief Configures and activates a MFS driver.
830 *
831 * @param[in] mfsp pointer to the @p MFSDriver object
832 * @return The operation status.
833 * @retval MFS_NO_ERROR if the operation has been successfully
834 * completed.
835 * @retval MFS_WARN_GC if the operation triggered a garbage
836 * collection.
837 * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
838 * failures. Makes the driver enter the
839 * @p MFS_ERROR state.
840 * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
841 *
842 * @api
843 */
845 unsigned i;
846
847 /* Resetting previous state.*/
848 mfs_state_reset(mfsp);
849
850 /* Attempting to mount the managed partition.*/
851 for (i = 0; i < MFS_CFG_MAX_REPAIR_ATTEMPTS; i++) {
852 mfs_error_t err;
853
854 err = mfs_try_mount(mfsp);
855 if (err == MFS_ERR_INTERNAL) {
856 /* Special case, do not retry on internal errors but report
857 immediately.*/
858 mfsp->state = MFS_ERROR;
859 return err;
860 }
861 if (!MFS_IS_ERROR(err)) {
862 mfsp->state = MFS_READY;
863 return err;
864 }
865 }
866
867 /* Driver start failed.*/
868 mfsp->state = MFS_ERROR;
870}
871
872/*===========================================================================*/
873/* Driver exported functions. */
874/*===========================================================================*/
875
876/**
877 * @brief Initializes an instance.
878 *
879 * @param[out] mfsp pointer to the @p MFSDriver object
880 * @param[in] ncbuf pointer to an @p mfs_nocache_buffer_t buffer structure
881 *
882 * @init
883 */
885
886 osalDbgCheck(mfsp != NULL);
887
888 mfsp->state = MFS_STOP;
889 mfsp->config = NULL;
890 mfsp->ncbuf = ncbuf;
891}
892
893/**
894 * @brief Configures and activates a MFS driver.
895 *
896 * @param[in] mfsp pointer to the @p MFSDriver object
897 * @param[in] config pointer to the configuration
898 * @return The operation status.
899 * @retval MFS_NO_ERROR if the operation has been
900 * completed.
901 * @retval MFS_WARN_GC if the operation triggered a garbage
902 * collection.
903 * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
904 * failures. Makes the driver enter the
905 * @p MFS_ERROR state.
906 * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
907 *
908 * @api
909 */
911
912 osalDbgCheck((mfsp != NULL) && (config != NULL));
913 osalDbgAssert((mfsp->state == MFS_STOP) || (mfsp->state == MFS_READY) ||
914 (mfsp->state == MFS_ERROR), "invalid state");
915
916 /* Storing configuration.*/
917 mfsp->config = config;
918
919 return mfs_mount(mfsp);
920}
921
922/**
923 * @brief Deactivates a MFS driver.
924 *
925 * @param[in] mfsp pointer to the @p MFSDriver object
926 *
927 * @api
928 */
929void mfsStop(MFSDriver *mfsp) {
930
931 osalDbgCheck(mfsp != NULL);
932 osalDbgAssert((mfsp->state == MFS_STOP) || (mfsp->state == MFS_READY) ||
933 (mfsp->state == MFS_ERROR), "invalid state");
934
935 mfsp->config = NULL;
936 mfsp->state = MFS_STOP;
937}
938
939/**
940 * @brief Destroys the state of the managed storage by erasing the flash.
941 *
942 * @param[in] mfsp pointer to the @p MFSDriver object
943 * @return The operation status.
944 * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY
945 * state.
946 * @retval MFS_NO_ERROR if the operation has been successfully
947 * completed.
948 * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
949 * failures. Makes the driver enter the
950 * @p MFS_ERROR state.
951 * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
952 *
953 * @api
954 */
956
957 osalDbgCheck(mfsp != NULL);
958
959 if (mfsp->state != MFS_READY) {
960 return MFS_ERR_INV_STATE;
961 }
962
965
966 return mfs_mount(mfsp);
967}
968
969/**
970 * @brief Retrieves and reads a data record.
971 *
972 * @param[in] mfsp pointer to the @p MFSDriver object
973 * @param[in] id record numeric identifier, the valid range is between
974 * @p 1 and @p MFS_CFG_MAX_RECORDS
975 * @param[in,out] np on input is the maximum buffer size, on return it is
976 * the size of the data copied into the buffer
977 * @param[out] buffer pointer to a buffer for record data
978 * @return The operation status.
979 * @retval MFS_NO_ERROR if the operation has been successfully
980 * completed.
981 * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY
982 * state.
983 * @retval MFS_ERR_INV_SIZE if the passed buffer is not large enough to
984 * contain the record data.
985 * @retval MFS_ERR_NOT_FOUND if the specified id does not exists.
986 * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
987 * failures. Makes the driver enter the
988 * @p MFS_ERROR state.
989 * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
990 *
991 * @api
992 */
994 size_t *np, uint8_t *buffer) {
995 uint16_t crc;
996
997 osalDbgCheck((mfsp != NULL) &&
998 (id >= 1U) && (id <= (mfs_id_t)MFS_CFG_MAX_RECORDS) &&
999 (np != NULL) && (*np > 0U) && (buffer != NULL));
1000
1001 if ((mfsp->state != MFS_READY) && (mfsp->state != MFS_TRANSACTION)) {
1002 return MFS_ERR_INV_STATE;
1003 }
1004
1005 /* Checking if the requested record actually exists.*/
1006 if (mfsp->descriptors[id - 1U].offset == 0U) {
1007 return MFS_ERR_NOT_FOUND;
1008 }
1009
1010 /* Making sure to not overflow the buffer.*/
1011 if (*np < mfsp->descriptors[id - 1U].size) {
1012 return MFS_ERR_INV_SIZE;
1013 }
1014
1015 /* Header read from flash.*/
1017 mfsp->descriptors[id - 1U].offset,
1018 sizeof (mfs_data_header_t),
1019 mfsp->ncbuf->data8));
1020
1021 /* Data read from flash.*/
1022 *np = mfsp->descriptors[id - 1U].size;
1024 mfsp->descriptors[id - 1U].offset + sizeof (mfs_data_header_t),
1025 *np,
1026 buffer));
1027
1028 /* Checking CRC.*/
1029 crc = crc16(0xFFFFU, buffer, *np);
1030 if (crc != mfsp->ncbuf->dhdr.fields.crc) {
1031 mfsp->state = MFS_ERROR;
1032 return MFS_ERR_FLASH_FAILURE;
1033 }
1034
1035 return MFS_NO_ERROR;
1036}
1037
1038/**
1039 * @brief Creates or updates a data record.
1040 *
1041 * @param[in] mfsp pointer to the @p MFSDriver object
1042 * @param[in] id record numeric identifier, the valid range is between
1043 * @p 1 and @p MFS_CFG_MAX_RECORDS
1044 * @param[in] n size of data to be written, it cannot be zero
1045 * @param[in] buffer pointer to a buffer for record data
1046 * @return The operation status.
1047 * @retval MFS_NO_ERROR if the operation has been successfully
1048 * completed.
1049 * @retval MFS_WARN_GC if the operation triggered a garbage
1050 * collection.
1051 * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY
1052 * state.
1053 * @retval MFS_ERR_OUT_OF_MEM if there is not enough flash space for the
1054 * operation.
1055 * @retval MFS_ERR_TRANSACTION_NUM if the transaction operations buffer space
1056 * has been exceeded.
1057 * @retval MFS_ERR_TRANSACTION_SIZE if the transaction allocated space
1058 * has been exceeded.
1059 * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
1060 * failures. Makes the driver enter the
1061 * @p MFS_ERROR state.
1062 * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
1063 *
1064 * @api
1065 */
1067 size_t n, const uint8_t *buffer) {
1068 flash_offset_t free, asize, rspace;
1069
1070 osalDbgCheck((mfsp != NULL) &&
1071 (id >= 1U) && (id <= (mfs_id_t)MFS_CFG_MAX_RECORDS) &&
1072 (n > (size_t)0) && (n <= (size_t)MFS_CFG_MAX_RECORD_SIZE) &&
1073 (buffer != NULL));
1074
1075 /* Aligned record size.*/
1076 asize = ALIGNED_REC_SIZE(n);
1077
1078 /* Normal mode code path.*/
1079 if (mfsp->state == MFS_READY) {
1080 bool warning = false;
1081
1082 /* If the required space is beyond the available (compacted) block
1083 size then an error is returned.
1084 NOTE: The space for one extra header is reserved in order to allow
1085 for an erase operation after the space has been fully allocated.*/
1086 rspace = ALIGNED_DHDR_SIZE + asize;
1087 if (rspace > mfsp->config->bank_size - mfsp->used_space) {
1088 return MFS_ERR_OUT_OF_MEM;
1089 }
1090
1091 /* Checking for immediately (not compacted) available space.*/
1092 free = (mfs_flash_get_bank_offset(mfsp, mfsp->current_bank) +
1093 mfsp->config->bank_size) - mfsp->next_offset;
1094 if (rspace > free) {
1095 /* We need to perform a garbage collection, there is enough space
1096 but it has to be freed.*/
1097 warning = true;
1099 }
1100
1101 /* Writing the data header without the magic, it will be written last.*/
1102 mfsp->ncbuf->dhdr.fields.id = (uint16_t)id;
1103 mfsp->ncbuf->dhdr.fields.size = (uint32_t)n;
1104 mfsp->ncbuf->dhdr.fields.crc = crc16(0xFFFFU, buffer, n);
1106 mfsp->next_offset + (sizeof (uint32_t) * 2U),
1107 sizeof (mfs_data_header_t) - (sizeof (uint32_t) * 2U),
1108 mfsp->ncbuf->data8 + (sizeof (uint32_t) * 2U)));
1109
1110 /* Writing the data part.*/
1112 mfsp->next_offset + sizeof (mfs_data_header_t),
1113 n,
1114 buffer));
1115
1116 /* Finally writing the magic number, it seals the operation.*/
1117 mfsp->ncbuf->dhdr.fields.magic1 = (uint32_t)MFS_HEADER_MAGIC_1;
1118 mfsp->ncbuf->dhdr.fields.magic2 = (uint32_t)MFS_HEADER_MAGIC_2;
1120 mfsp->next_offset,
1121 sizeof (uint32_t) * 2U,
1122 mfsp->ncbuf->data8));
1123
1124 /* The size of the old record instance, if present, must be subtracted
1125 to the total used size.*/
1126 if (mfsp->descriptors[id - 1U].offset != 0U) {
1127 mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[id - 1U].size);
1128 }
1129
1130 /* Adjusting bank-related metadata.*/
1131 mfsp->descriptors[id - 1U].offset = mfsp->next_offset;
1132 mfsp->descriptors[id - 1U].size = (uint32_t)n;
1133 mfsp->next_offset += asize;
1134 mfsp->used_space += asize;
1135
1136 return warning ? MFS_WARN_GC : MFS_NO_ERROR;
1137 }
1138
1139#if MFS_CFG_TRANSACTION_MAX > 0
1140 /* Transaction mode code path.*/
1141 if (mfsp->state == MFS_TRANSACTION) {
1143
1144 /* Checking if the maximum number of operations in a transaction is
1145 Exceeded.*/
1146 if (mfsp->tr_nops >= MFS_CFG_TRANSACTION_MAX) {
1148 }
1149
1150 /* If the required space is greater than the space allocated for the
1151 transaction then error.
1152 Note, the condition check is written in a defensive way.*/
1153 rspace = asize;
1154 if ((mfsp->tr_next_offset > mfsp->tr_limit_offset) ||
1155 (rspace > mfsp->tr_limit_offset - mfsp->tr_next_offset)) {
1157 }
1158
1159 /* Writing the data header without the magic, it will be written last.*/
1160 mfsp->ncbuf->dhdr.fields.id = (uint16_t)id;
1161 mfsp->ncbuf->dhdr.fields.size = (uint32_t)n;
1162 mfsp->ncbuf->dhdr.fields.crc = crc16(0xFFFFU, buffer, n);
1164 mfsp->tr_next_offset + (sizeof (uint32_t) * 2U),
1165 sizeof (mfs_data_header_t) - (sizeof (uint32_t) * 2U),
1166 mfsp->ncbuf->data8 + (sizeof (uint32_t) * 2U)));
1167
1168 /* Writing the data part.*/
1170 mfsp->tr_next_offset + sizeof (mfs_data_header_t),
1171 n,
1172 buffer));
1173
1174 /* Adding a transaction operation record.*/
1175 top = &mfsp->tr_ops[mfsp->tr_nops];
1176 top->offset = mfsp->tr_next_offset;
1177 top->size = n;
1178 top->id = id;
1179
1180 /* Number of records and next write position updated.*/
1181 mfsp->tr_nops++;
1182 mfsp->tr_next_offset += asize;
1183
1184 return MFS_NO_ERROR;
1185 }
1186#endif /* MFS_CFG_TRANSACTION_MAX > 0 */
1187
1188 /* Invalid state.*/
1189 return MFS_ERR_INV_STATE;
1190}
1191
1192/**
1193 * @brief Erases a data record.
1194 *
1195 * @param[in] mfsp pointer to the @p MFSDriver object
1196 * @param[in] id record numeric identifier, the valid range is between
1197 * @p 1 and @p MFS_CFG_MAX_RECORDS
1198 * @return The operation status.
1199 * @retval MFS_NO_ERROR if the operation has been successfully
1200 * completed.
1201 * @retval MFS_WARN_GC if the operation triggered a garbage
1202 * collection.
1203 * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY
1204 * state.
1205 * @retval MFS_ERR_OUT_OF_MEM if there is not enough flash space for the
1206 * operation.
1207 * @retval MFS_ERR_TRANSACTION_NUM if the transaction operations buffer space
1208 * has been exceeded.
1209 * @retval MFS_ERR_TRANSACTION_SIZE if the transaction allocated space
1210 * has been exceeded.
1211 * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
1212 * failures. Makes the driver enter the
1213 * @p MFS_ERROR state.
1214 * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
1215 *
1216 * @api
1217 */
1219 flash_offset_t free, asize, rspace;
1220
1221 osalDbgCheck((mfsp != NULL) &&
1222 (id >= 1U) && (id <= (mfs_id_t)MFS_CFG_MAX_RECORDS));
1223
1224 /* Aligned record size.*/
1225 asize = ALIGNED_DHDR_SIZE;
1226
1227 /* Normal mode code path.*/
1228 if (mfsp->state == MFS_READY) {
1229 bool warning = false;
1230
1231 /* Checking if the requested record actually exists.*/
1232 if (mfsp->descriptors[id - 1U].offset == 0U) {
1233 return MFS_ERR_NOT_FOUND;
1234 }
1235
1236 /* If the required space is beyond the available (compacted) block
1237 size then an internal error is returned, it should never happen.*/
1238 rspace = asize;
1239 if (rspace > mfsp->config->bank_size - mfsp->used_space) {
1240 return MFS_ERR_INTERNAL;
1241 }
1242
1243 /* Checking for immediately (not compacted) available space.*/
1244 free = (mfs_flash_get_bank_offset(mfsp, mfsp->current_bank) +
1245 mfsp->config->bank_size) - mfsp->next_offset;
1246 if (rspace > free) {
1247 /* We need to perform a garbage collection, there is enough space
1248 but it has to be freed.*/
1249 warning = true;
1251 }
1252
1253 /* Writing the data header with size set to zero, it means that the
1254 record is logically erased.*/
1255 mfsp->ncbuf->dhdr.fields.magic1 = (uint32_t)MFS_HEADER_MAGIC_1;
1256 mfsp->ncbuf->dhdr.fields.magic2 = (uint32_t)MFS_HEADER_MAGIC_2;
1257 mfsp->ncbuf->dhdr.fields.id = (uint16_t)id;
1258 mfsp->ncbuf->dhdr.fields.size = (uint32_t)0;
1259 mfsp->ncbuf->dhdr.fields.crc = (uint16_t)0xFFFF;
1261 mfsp->next_offset,
1262 sizeof (mfs_data_header_t),
1263 mfsp->ncbuf->data8));
1264
1265 /* Adjusting bank-related metadata.*/
1266 mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[id - 1U].size);
1267 mfsp->next_offset += asize;
1268 mfsp->descriptors[id - 1U].offset = 0U;
1269 mfsp->descriptors[id - 1U].size = 0U;
1270
1271 return warning ? MFS_WARN_GC : MFS_NO_ERROR;
1272 }
1273
1274#if MFS_CFG_TRANSACTION_MAX > 0
1275 /* Transaction mode code path.*/
1276 if (mfsp->state == MFS_TRANSACTION) {
1278
1279 /* Checking if the requested record actually exists.*/
1280 if (mfsp->descriptors[id - 1U].offset == 0U) {
1281 return MFS_ERR_NOT_FOUND;
1282 }
1283
1284 /* Checking if the maximum number of operations in a transaction is
1285 Exceeded.*/
1286 if (mfsp->tr_nops >= MFS_CFG_TRANSACTION_MAX) {
1288 }
1289
1290 /* If the required space is greater than the space allocated for the
1291 transaction then error.
1292 Note, the condition check is written in a defensive way.*/
1293 rspace = asize;
1294 if ((mfsp->tr_next_offset > mfsp->tr_limit_offset) ||
1295 (rspace > mfsp->tr_limit_offset - mfsp->tr_next_offset)) {
1297 }
1298
1299 /* Writing the data header with size set to zero, it means that the
1300 record is logically erased. Note, the magic number is not set.*/
1301 mfsp->ncbuf->dhdr.fields.id = (uint16_t)id;
1302 mfsp->ncbuf->dhdr.fields.size = (uint32_t)0;
1303 mfsp->ncbuf->dhdr.fields.crc = (uint16_t)0xFFFF;
1305 mfsp->tr_next_offset + (sizeof (uint32_t) * 2U),
1306 sizeof (mfs_data_header_t) - (sizeof (uint32_t) * 2U),
1307 mfsp->ncbuf->data8 + (sizeof (uint32_t) * 2U)));
1308
1309 /* Adding a transaction operation record.*/
1310 top = &mfsp->tr_ops[mfsp->tr_nops];
1311 top->offset = mfsp->tr_next_offset;
1312 top->size = 0U;
1313 top->id = id;
1314
1315 /* Number of records and next write position updated.*/
1316 mfsp->tr_nops++;
1317 mfsp->tr_next_offset += asize;
1318
1319 return MFS_NO_ERROR;
1320 }
1321#endif /* MFS_CFG_TRANSACTION_MAX > 0 */
1322
1323 return MFS_ERR_INV_STATE;
1324}
1325
1326/**
1327 * @brief Enforces a garbage collection operation.
1328 * @details Garbage collection involves: integrity check, optionally repairs,
1329 * obsolete data removal, data compaction and a flash bank swap.
1330 *
1331 * @param[in] mfsp pointer to the @p MFSDriver object
1332 * @return The operation status.
1333 * @retval MFS_NO_ERROR if the operation has been successfully
1334 * completed.
1335 * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY
1336 * state.
1337 * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
1338 * failures. Makes the driver enter the
1339 * @p MFS_ERROR state.
1340 * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
1341 *
1342 * @api
1343 */
1345
1346 osalDbgCheck(mfsp != NULL);
1347
1348 if (mfsp->state != MFS_READY) {
1349 return MFS_ERR_INV_STATE;
1350 }
1351
1352 return mfs_garbage_collect(mfsp);
1353}
1354
1355#if (MFS_CFG_TRANSACTION_MAX > 0) || defined(__DOXYGEN__)
1356/**
1357 * @brief Puts the driver in transaction mode.
1358 * @note The parameters @p n and @p size are used to make an
1359 * estimation of the space required for the transaction to succeed.
1360 * Note that the estimated size must include also the extra space
1361 * required by alignment enforcement option. If the estimated size
1362 * is wrong (by defect) what could happen is that there is a failure
1363 * in the middle of a transaction and a roll-back would be required.
1364 * @note The conditions for starting a transaction are:
1365 * - The driver must be started.
1366 * - There must be enough compacted storage to accommodate the whole
1367 * transaction. If the required space is available but it is not
1368 * compacted then a garbage collect operation is performed.
1369 *
1370 * @param[in] mfsp pointer to the @p MFSDriver object
1371 * @param[in] size estimated total size of written records in transaction,
1372 * this includes, data, headers and alignment gaps
1373 * @return The operation status.
1374 * @retval MFS_NO_ERROR if the operation has been successfully
1375 * completed.
1376 * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY
1377 * state.
1378 * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
1379 * failures. Makes the driver enter the
1380 * @p MFS_ERROR state.
1381 * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
1382 *
1383 * @api
1384 */
1386 flash_offset_t free, tspace, rspace;
1387
1388 osalDbgCheck((mfsp != NULL) && (size > ALIGNED_DHDR_SIZE));
1389
1390 /* The driver must be in ready mode.*/
1391 if (mfsp->state != MFS_READY) {
1392 return MFS_ERR_INV_STATE;
1393 }
1394
1395 /* Estimating the required contiguous compacted space.*/
1396 tspace = (flash_offset_t)MFS_ALIGN_NEXT(size);
1397 rspace = tspace + ALIGNED_DHDR_SIZE;
1398
1399 /* If the required space is beyond the available (compacted) block
1400 size then an error is returned.*/
1401 if (rspace > mfsp->config->bank_size - mfsp->used_space) {
1402 return MFS_ERR_OUT_OF_MEM;
1403 }
1404
1405 /* Checking for immediately (not compacted) available space.*/
1406 free = (mfs_flash_get_bank_offset(mfsp, mfsp->current_bank) +
1407 mfsp->config->bank_size) - mfsp->next_offset;
1408 if (rspace > free) {
1409 /* We need to perform a garbage collection, there is enough space
1410 but it has to be freed.*/
1412 }
1413
1414 /* Entering transaction mode.*/
1415 mfsp->state = MFS_TRANSACTION;
1416
1417 /* Initializing transaction state.*/
1418 mfsp->tr_next_offset = mfsp->next_offset;
1419 mfsp->tr_nops = 0U;
1420 mfsp->tr_limit_offset = mfsp->tr_next_offset + tspace;
1421
1422 return MFS_NO_ERROR;
1423}
1424
1425/**
1426 * @brief A transaction is committed and finalized atomically.
1427 *
1428 * @param[in] mfsp pointer to the @p MFSDriver object
1429 * @return The operation status.
1430 * @retval MFS_NO_ERROR if the operation has been successfully
1431 * completed.
1432 * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_TRANSACTION
1433 * state.
1434 * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
1435 * failures. Makes the driver enter the
1436 * @p MFS_ERROR state.
1437 * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
1438 *
1439 * @api
1440 */
1443
1444 osalDbgCheck(mfsp != NULL);
1445
1446 /* The driver must be in transaction mode.*/
1447 if (mfsp->state != MFS_TRANSACTION) {
1448 return MFS_ERR_INV_STATE;
1449 }
1450
1451 /* Scanning all buffered operations in reverse order.*/
1452 mfsp->ncbuf->dhdr.fields.magic1 = (uint32_t)MFS_HEADER_MAGIC_1;
1453 mfsp->ncbuf->dhdr.fields.magic2 = (uint32_t)MFS_HEADER_MAGIC_2;
1454 top = &mfsp->tr_ops[mfsp->tr_nops];
1455 while (top > &mfsp->tr_ops[0]) {
1456 /* On the previous element.*/
1457 top--;
1458
1459 /* Finalizing the operation by writing the magic number.*/
1461 top->offset,
1462 sizeof (uint32_t) * 2U,
1463 mfsp->ncbuf->data8));
1464 }
1465
1466 /* Transaction fully committed by writing the last (first in transaction)
1467 magic number, now updating the internal state using the buffered data.*/
1468 mfsp->next_offset = mfsp->tr_next_offset;
1469 while (top < &mfsp->tr_ops[mfsp->tr_nops]) {
1470 unsigned i = (unsigned)top->id - 1U;
1471
1472 /* The calculation is a bit different depending on write or erase record
1473 operations.*/
1474 if (top->size > 0U) {
1475 /* It is a write.*/
1476 if (mfsp->descriptors[i].offset != 0U) {
1477 /* The size of the old record instance, if present, must be subtracted
1478 to the total used size.*/
1479 mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[i].size);
1480 }
1481
1482 /* Adjusting bank-related metadata.*/
1483 mfsp->used_space += ALIGNED_REC_SIZE(top->size);
1484 mfsp->descriptors[i].offset = top->offset;
1485 mfsp->descriptors[i].size = top->size;
1486 }
1487 else {
1488 /* It is an erase.*/
1489 mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[i].size);
1490 mfsp->descriptors[i].offset = 0U;
1491 mfsp->descriptors[i].size = 0U;
1492 }
1493
1494 /* On the next element.*/
1495 top++;
1496 }
1497
1498 /* Returning to ready mode.*/
1499 mfsp->state = MFS_READY;
1500
1501 return MFS_NO_ERROR;
1502}
1503
1504/**
1505 * @brief A transaction is rolled back atomically.
1506 * @details This function performs a garbage collection in order to discard
1507 * all written data that has not been finalized.
1508 *
1509 * @param[in] mfsp pointer to the @p MFSDriver object
1510 * @return The operation status.
1511 * @retval MFS_NO_ERROR if the operation has been successfully
1512 * completed.
1513 * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_TRANSACTION
1514 * state.
1515 * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
1516 * failures. Makes the driver enter the
1517 * @p MFS_ERROR state.
1518 * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
1519 *
1520 * @api
1521 */
1523 mfs_error_t err;
1524
1525 osalDbgCheck(mfsp != NULL);
1526
1527 if (mfsp->state != MFS_TRANSACTION) {
1528 return MFS_ERR_INV_STATE;
1529 }
1530
1531 /* Returning to ready mode.*/
1532 mfsp->state = MFS_READY;
1533
1534 /* If no operations have been performed then there is no need to perform
1535 a garbage collection.*/
1536 if (mfsp->tr_nops > 0U) {
1537 err = mfs_garbage_collect(mfsp);
1538 }
1539 else {
1540 err = MFS_NO_ERROR;
1541 }
1542
1543 return err;
1544}
1545#endif /* MFS_CFG_TRANSACTION_MAX > 0 */
1546
1547/** @} */
#define flashStartEraseSector(ip, sector)
Starts an sector erase operation.
Definition hal_flash.h:318
flash_offset_t flashGetSectorOffset(BaseFlash *devp, flash_sector_t sector)
Returns the offset of a sector.
Definition hal_flash.c:87
#define flashReleaseExclusive(ip)
Releases exclusive access to flash.
Definition hal_flash.h:376
uint32_t flash_sector_t
Type of a flash sector number.
Definition hal_flash.h:117
flash_error_t flashWaitErase(BaseFlash *devp)
Waits until the current erase operation is finished.
Definition hal_flash.c:61
#define flashRead(ip, offset, n, rp)
Read operation.
Definition hal_flash.h:271
#define flashAcquireExclusive(ip)
Acquires exclusive access to flash.
Definition hal_flash.h:364
uint32_t flash_offset_t
Type of a flash offset.
Definition hal_flash.h:112
#define flashVerifyErase(ip, sector)
Returns the erase state of a sector.
Definition hal_flash.h:352
flash_error_t
Type of a flash error code.
Definition hal_flash.h:98
#define flashProgram(ip, offset, n, pp)
Program operation.
Definition hal_flash.h:289
@ FLASH_ERROR_VERIFY
Definition hal_flash.h:104
@ FLASH_NO_ERROR
Definition hal_flash.h:99
#define RET_ON_ERROR(err)
Error check helper.
Definition hal_mfs.c:72
union mfs_nocache_buffer mfs_nocache_buffer_t
Type of an non-cacheable MFS buffer.
mfs_error_t mfsEraseRecord(MFSDriver *mfsp, mfs_id_t id)
Erases a data record.
Definition hal_mfs.c:1218
#define MFS_HEADER_MAGIC_1
Definition hal_mfs.h:42
#define MFS_CFG_MAX_RECORDS
Maximum number of indexed records in the managed storage.
Definition hal_mfs.h:58
#define MFS_CFG_BUFFER_SIZE
Size of the buffer used for data copying.
Definition hal_mfs.h:100
static const uint16_t crc16_table[256]
Definition hal_mfs.c:107
mfs_error_t mfsReadRecord(MFSDriver *mfsp, mfs_id_t id, size_t *np, uint8_t *buffer)
Retrieves and reads a data record.
Definition hal_mfs.c:993
static mfs_error_t mfs_garbage_collect(MFSDriver *mfsp)
Enforces a garbage collection.
Definition hal_mfs.c:646
mfs_error_t
Type of an MFS error code.
Definition hal_mfs.h:196
static void mfs_flash_release(MFSDriver *mfsp)
Definition hal_mfs.c:87
#define MFS_BANK_MAGIC_2
Definition hal_mfs.h:41
static mfs_error_t mfs_try_mount(MFSDriver *mfsp)
Performs a flash partition mount attempt.
Definition hal_mfs.c:697
static flash_offset_t mfs_flash_get_bank_offset(MFSDriver *mfsp, mfs_bank_t bank)
Definition hal_mfs.c:177
mfs_error_t mfsStart(MFSDriver *mfsp, const MFSConfig *config)
Configures and activates a MFS driver.
Definition hal_mfs.c:910
static void mfs_flash_acquire(MFSDriver *mfsp)
Definition hal_mfs.c:80
#define MFS_CFG_TRANSACTION_MAX
Maximum number of objects writable in a single transaction.
Definition hal_mfs.h:118
static mfs_error_t mfs_bank_scan_records(MFSDriver *mfsp, mfs_bank_t bank, bool *wflagp)
Scans blocks searching for records.
Definition hal_mfs.c:490
#define MFS_CFG_MAX_REPAIR_ATTEMPTS
Maximum number of repair attempts on partition mount.
Definition hal_mfs.h:72
#define ALIGNED_SIZEOF(t)
Aligned size of a type.
Definition hal_mfs.c:61
#define MFS_CFG_MAX_RECORD_SIZE
Maximum record size in the managed storage.
Definition hal_mfs.h:65
#define MFS_HEADER_MAGIC_2
Definition hal_mfs.h:43
static mfs_error_t mfs_bank_verify_erase(MFSDriver *mfsp, mfs_bank_t bank)
Erases and verifies all sectors belonging to a bank.
Definition hal_mfs.c:370
static mfs_error_t mfs_flash_write(MFSDriver *mfsp, flash_offset_t offset, size_t n, const uint8_t *wp)
Flash write.
Definition hal_mfs.c:231
mfs_error_t mfsPerformGarbageCollection(MFSDriver *mfsp)
Enforces a garbage collection operation.
Definition hal_mfs.c:1344
void mfsObjectInit(MFSDriver *mfsp, mfs_nocache_buffer_t *ncbuf)
Initializes an instance.
Definition hal_mfs.c:884
mfs_error_t mfs_mount(MFSDriver *mfsp)
Configures and activates a MFS driver.
Definition hal_mfs.c:844
static mfs_error_t mfs_flash_copy(MFSDriver *mfsp, flash_offset_t doffset, flash_offset_t soffset, uint32_t n)
Flash copy.
Definition hal_mfs.c:281
mfs_error_t mfsWriteRecord(MFSDriver *mfsp, mfs_id_t id, size_t n, const uint8_t *buffer)
Creates or updates a data record.
Definition hal_mfs.c:1066
mfs_error_t mfsStartTransaction(MFSDriver *mfsp, size_t size)
Puts the driver in transaction mode.
Definition hal_mfs.c:1385
uint32_t mfs_id_t
Type of a record identifier.
Definition hal_mfs.h:223
mfs_error_t mfsRollbackTransaction(MFSDriver *mfsp)
A transaction is rolled back atomically.
Definition hal_mfs.c:1522
static mfs_error_t mfs_flash_read(MFSDriver *mfsp, flash_offset_t offset, size_t n, uint8_t *rp)
Flash read.
Definition hal_mfs.c:197
static mfs_error_t mfs_bank_write_header(MFSDriver *mfsp, mfs_bank_t bank, uint32_t cnt)
Writes the validation header in a bank.
Definition hal_mfs.c:416
static mfs_error_t mfs_bank_erase(MFSDriver *mfsp, mfs_bank_t bank)
Erases and verifies all sectors belonging to a bank.
Definition hal_mfs.c:317
static void mfs_state_reset(MFSDriver *mfsp)
Definition hal_mfs.c:157
#define MFS_ALIGN_NEXT(v)
Definition hal_mfs.h:444
mfs_bank_t
Type of a flash bank.
Definition hal_mfs.h:175
#define MFS_BANK_MAGIC_1
Definition hal_mfs.h:40
#define ALIGNED_DHDR_SIZE
Data record header size aligned.
Definition hal_mfs.c:55
mfs_error_t mfsCommitTransaction(MFSDriver *mfsp)
A transaction is committed and finalized atomically.
Definition hal_mfs.c:1441
static mfs_bank_state_t mfs_bank_check_header(MFSDriver *mfsp)
Checks integrity of the header in the shared buffer.
Definition hal_mfs.c:450
#define MFS_IS_ERROR(err)
Definition hal_mfs.h:433
void mfsStop(MFSDriver *mfsp)
Deactivates a MFS driver.
Definition hal_mfs.c:929
static mfs_error_t mfs_bank_get_state(MFSDriver *mfsp, mfs_bank_t bank, mfs_bank_state_t *statep, uint32_t *cntp)
Determines the state of a bank.
Definition hal_mfs.c:608
mfs_error_t mfsErase(MFSDriver *mfsp)
Destroys the state of the managed storage by erasing the flash.
Definition hal_mfs.c:955
#define PAIR(a, b)
Combines two values (0..3) in one (0..15).
Definition hal_mfs.c:67
uint16_t crc16(uint16_t crc, const uint8_t *data, size_t n)
Definition hal_mfs.c:146
mfs_bank_state_t
Type of a bank state assessment.
Definition hal_mfs.h:214
#define ALIGNED_REC_SIZE(n)
Data record size aligned.
Definition hal_mfs.c:49
@ MFS_ERR_INV_SIZE
Definition hal_mfs.h:201
@ MFS_ERR_FLASH_FAILURE
Definition hal_mfs.h:207
@ MFS_ERR_NOT_ERASED
Definition hal_mfs.h:206
@ MFS_ERR_INTERNAL
Definition hal_mfs.h:208
@ MFS_ERR_TRANSACTION_SIZE
Definition hal_mfs.h:205
@ MFS_ERR_NOT_FOUND
Definition hal_mfs.h:202
@ MFS_WARN_GC
Definition hal_mfs.h:199
@ MFS_ERR_TRANSACTION_NUM
Definition hal_mfs.h:204
@ MFS_NO_ERROR
Definition hal_mfs.h:197
@ MFS_ERR_OUT_OF_MEM
Definition hal_mfs.h:203
@ MFS_WARN_REPAIR
Definition hal_mfs.h:198
@ MFS_ERR_INV_STATE
Definition hal_mfs.h:200
@ MFS_TRANSACTION
Definition hal_mfs.h:187
@ MFS_ERROR
Definition hal_mfs.h:188
@ MFS_STOP
Definition hal_mfs.h:185
@ MFS_READY
Definition hal_mfs.h:186
@ MFS_BANK_1
Definition hal_mfs.h:177
@ MFS_BANK_0
Definition hal_mfs.h:176
@ MFS_BANK_GARBAGE
Definition hal_mfs.h:217
@ MFS_BANK_ERASED
Definition hal_mfs.h:215
@ MFS_BANK_OK
Definition hal_mfs.h:216
#define osalDbgAssert(c, remark)
Condition assertion.
Definition osal.h:264
#define osalDbgCheck(c)
Function parameters check.
Definition osal.h:284
HAL subsystem header.
Managed Flash Storage module header.
Type of a MFS configuration structure.
Definition hal_mfs.h:304
uint32_t erased
Erased value.
Definition hal_mfs.h:312
flash_sector_t bank1_start
Base sector index for bank 1.
Definition hal_mfs.h:330
flash_sector_t bank0_start
Base sector index for bank 0.
Definition hal_mfs.h:320
flash_sector_t bank0_sectors
Number of sectors for bank 0.
Definition hal_mfs.h:326
flash_offset_t bank_size
Banks size.
Definition hal_mfs.h:316
BaseFlash * flashp
Flash driver associated to this MFS instance.
Definition hal_mfs.h:308
flash_sector_t bank1_sectors
Number of sectors for bank 1.
Definition hal_mfs.h:336
Type of an MFS instance.
Definition hal_mfs.h:371
uint32_t current_counter
Usage counter of the current bank.
Definition hal_mfs.h:387
flash_offset_t next_offset
Pointer to the next free position in the current bank.
Definition hal_mfs.h:391
mfs_transaction_op_t tr_ops[MFS_CFG_TRANSACTION_MAX]
Buffered operations in current transaction.
Definition hal_mfs.h:417
mfs_bank_t current_bank
Bank currently in use.
Definition hal_mfs.h:383
mfs_record_descriptor_t descriptors[MFS_CFG_MAX_RECORDS]
Offsets of the most recent instance of the records.
Definition hal_mfs.h:400
flash_offset_t tr_limit_offset
Maximum offset for the transaction.
Definition hal_mfs.h:409
const MFSConfig * config
Current configuration data.
Definition hal_mfs.h:379
uint32_t tr_nops
Number of buffered operations in current transaction.
Definition hal_mfs.h:413
flash_offset_t tr_next_offset
Next write offset for current transaction.
Definition hal_mfs.h:405
mfs_nocache_buffer_t * ncbuf
Associated non-cacheable buffer.
Definition hal_mfs.h:422
flash_offset_t used_space
Used space in the current bank without considering erased records.
Definition hal_mfs.h:395
mfs_state_t state
Driver state.
Definition hal_mfs.h:375
flash_offset_t offset
Offset of the record header.
Definition hal_mfs.h:294
uint32_t size
Record data size.
Definition hal_mfs.h:298
Type of a buffered write/erase operation within a transaction.
Definition hal_mfs.h:342
flash_offset_t offset
Written header offset.
Definition hal_mfs.h:346
size_t size
Written data size.
Definition hal_mfs.h:350
mfs_id_t id
Record identifier.
Definition hal_mfs.h:354
Type of a bank header.
Definition hal_mfs.h:229
uint32_t hdr32[4]
Definition hal_mfs.h:255
uint16_t crc
Header CRC.
Definition hal_mfs.h:252
struct mfs_bank_header_t::@372004363225213175116173204155205133052276266314 fields
uint16_t reserved1
Reserved field.
Definition hal_mfs.h:248
uint32_t magic2
Bank magic 2.
Definition hal_mfs.h:238
uint32_t counter
Usage counter of the bank.
Definition hal_mfs.h:244
uint8_t hdr8[16]
Definition hal_mfs.h:254
uint32_t magic1
Bank magic 1.
Definition hal_mfs.h:234
Type of a data block header.
Definition hal_mfs.h:262
uint16_t id
Record identifier.
Definition hal_mfs.h:275
uint32_t magic1
Data header magic 1.
Definition hal_mfs.h:267
uint32_t magic2
Data header magic 2.
Definition hal_mfs.h:271
uint16_t crc
Data CRC.
Definition hal_mfs.h:279
struct mfs_data_header_t::@063253077140057234174353206371011131201242010010 fields
uint32_t size
Data size.
Definition hal_mfs.h:284
mfs_bank_header_t bhdr
Definition hal_mfs.h:362
uint8_t data8[MFS_CFG_BUFFER_SIZE]
Definition hal_mfs.h:363
uint32_t data32[MFS_CFG_BUFFER_SIZE/sizeof(uint32_t)]
Definition hal_mfs.h:365
mfs_data_header_t dhdr
Definition hal_mfs.h:361