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