ChibiOS/HAL  7.0.3
hal_mfs.c
Go to the documentation of this file.
1 /*
2  ChibiOS - Copyright (C) 2006..2018 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; either version 3 of the License, or
9  (at your option) any later version.
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 
88 static 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 
127 uint16_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 
138 static 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 
152 static flash_offset_t mfs_flash_get_bank_offset(MFSDriver *mfsp,
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;
179  return MFS_ERR_FLASH_FAILURE;
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;
207  return MFS_ERR_FLASH_FAILURE;
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->buffer.data8));
216 
217  if (memcmp((void *)mfsp->buffer.data8, (void *)wp, chunk)) {
218  mfsp->state = MFS_ERROR;
219  return MFS_ERR_FLASH_FAILURE;
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->buffer.data8));
259  RET_ON_ERROR(mfs_flash_write(mfsp, doffset, chunk, mfsp->buffer.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;
297  return MFS_ERR_FLASH_FAILURE;
298  }
299  ferr = flashWaitErase(mfsp->config->flashp);
300  if (ferr != FLASH_NO_ERROR) {
301  mfsp->state = MFS_ERROR;
302  return MFS_ERR_FLASH_FAILURE;
303  }
304  ferr = flashVerifyErase(mfsp->config->flashp, sector);
305  if (ferr != FLASH_NO_ERROR) {
306  mfsp->state = MFS_ERROR;
307  return MFS_ERR_FLASH_FAILURE;
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;
346  return MFS_ERR_FLASH_FAILURE;
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  mfs_bank_header_t bhdr;
370 
371  if (bank == MFS_BANK_0) {
372  sector = mfsp->config->bank0_start;
373  }
374  else {
375  sector = mfsp->config->bank1_start;
376  }
377 
378  bhdr.fields.magic1 = MFS_BANK_MAGIC_1;
379  bhdr.fields.magic2 = MFS_BANK_MAGIC_2;
380  bhdr.fields.counter = cnt;
381  bhdr.fields.reserved1 = (uint16_t)mfsp->config->erased;
382  bhdr.fields.crc = crc16(0xFFFFU, 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  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->buffer.bhdr.hdr32[0] == mfsp->config->erased) &&
403  (mfsp->buffer.bhdr.hdr32[1] == mfsp->config->erased) &&
404  (mfsp->buffer.bhdr.hdr32[2] == mfsp->config->erased) &&
405  (mfsp->buffer.bhdr.hdr32[3] == mfsp->config->erased)) {
406  return MFS_BANK_ERASED;
407  }
408 
409  /* Checking header fields integrity.*/
410  if ((mfsp->buffer.bhdr.fields.magic1 != MFS_BANK_MAGIC_1) ||
411  (mfsp->buffer.bhdr.fields.magic2 != MFS_BANK_MAGIC_2) ||
412  (mfsp->buffer.bhdr.fields.counter == mfsp->config->erased) ||
413  (mfsp->buffer.bhdr.fields.reserved1 != (uint16_t)mfsp->config->erased)) {
414  return MFS_BANK_GARBAGE;
415  }
416 
417  /* Verifying header CRC.*/
418  crc = crc16(0xFFFFU, mfsp->buffer.bhdr.hdr8,
419  sizeof (mfs_bank_header_t) - sizeof (uint16_t));
420  if (crc != mfsp->buffer.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) {
455  union {
456  mfs_data_header_t dhdr;
457  uint8_t data8[ALIGNED_SIZEOF(mfs_data_header_t)];
458  } u;
459  uint16_t crc;
460 
461  /* Reading the current record header.*/
462  RET_ON_ERROR(mfs_flash_read(mfsp, hdr_offset,
463  sizeof (mfs_data_header_t),
464  u.data8));
465 
466  /* Checking if the found header is in erased state.*/
467  if ((u.dhdr.hdr32[0] == mfsp->config->erased) &&
468  (u.dhdr.hdr32[1] == mfsp->config->erased) &&
469  (u.dhdr.hdr32[2] == mfsp->config->erased)) {
470  break;
471  }
472 
473  /* It is not erased so checking for integrity.*/
474  if ((u.dhdr.fields.magic1 != MFS_HEADER_MAGIC_1) ||
475  (u.dhdr.fields.magic2 != MFS_HEADER_MAGIC_2) ||
476  (u.dhdr.fields.id < 1U) ||
477  (u.dhdr.fields.id > (uint32_t)MFS_CFG_MAX_RECORDS) ||
478  (u.dhdr.fields.size > end_offset - hdr_offset)) {
479  *wflagp = true;
480  break;
481  }
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 (u.dhdr.fields.size > 0U) {
487  flash_offset_t data = hdr_offset + sizeof (mfs_data_header_t);
488  uint32_t total = u.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->buffer.data8));
496 
497  /* CRC on the read data chunk.*/
498  crc = crc16(crc, &mfsp->buffer.data8[0], chunk);
499 
500  /* Next chunk.*/
501  data += chunk;
502  total -= chunk;
503  }
504  }
505  if (crc != u.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 (u.dhdr.fields.size == 0U) {
513  mfsp->descriptors[u.dhdr.fields.id - 1U].offset = 0U;
514  mfsp->descriptors[u.dhdr.fields.id - 1U].size = 0U;
515  }
516  else {
517  mfsp->descriptors[u.dhdr.fields.id - 1U].offset = hdr_offset;
518  mfsp->descriptors[u.dhdr.fields.id - 1U].size = u.dhdr.fields.size;
519  }
520  }
521 
522  /* On the next header.*/
523  hdr_offset = hdr_offset + ALIGNED_REC_SIZE(u.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  * .
544  * @param[out] cntp bank counter
545  * @return The operation status.
546  *
547  * @notapi
548  */
550  mfs_bank_t bank,
551  mfs_bank_state_t *statep,
552  uint32_t *cntp) {
553 
554  /* Reading the current bank header.*/
555  RET_ON_ERROR(mfs_flash_read(mfsp, mfs_flash_get_bank_offset(mfsp, bank),
556  sizeof (mfs_bank_header_t),
557  mfsp->buffer.data8));
558 
559  /* Getting the counter regardless of the bank state, it is only valid if
560  the state is MFS_BANK_OK.*/
561  *cntp = mfsp->buffer.bhdr.fields.counter;
562 
563  /* Checking just the header.*/
564  *statep = mfs_bank_check_header(mfsp);
565  if (*statep == MFS_BANK_ERASED) {
566  mfs_error_t err;
567 
568  /* Checking if the bank is really all erased.*/
569  err = mfs_bank_verify_erase(mfsp, bank);
570  if (err == MFS_ERR_NOT_ERASED) {
571  *statep = MFS_BANK_GARBAGE;
572  }
573  }
574 
575  return MFS_NO_ERROR;
576 }
577 
578 /**
579  * @brief Enforces a garbage collection.
580  * @details Storage data is compacted into a single bank.
581  *
582  * @param[out] mfsp pointer to the @p MFSDriver object
583  * @return The operation status.
584  *
585  * @notapi
586  */
588  unsigned i;
589  mfs_bank_t sbank, dbank;
590  flash_offset_t dest_offset;
591 
592  sbank = mfsp->current_bank;
593  if (sbank == MFS_BANK_0) {
594  dbank = MFS_BANK_1;
595  }
596  else {
597  dbank = MFS_BANK_0;
598  }
599 
600  /* Write address.*/
601  dest_offset = mfs_flash_get_bank_offset(mfsp, dbank) +
603 
604  /* Copying the most recent record instances only.*/
605  for (i = 0; i < MFS_CFG_MAX_RECORDS; i++) {
606  uint32_t totsize = ALIGNED_REC_SIZE(mfsp->descriptors[i].size);
607  if (mfsp->descriptors[i].offset != 0) {
608  RET_ON_ERROR(mfs_flash_copy(mfsp, dest_offset,
609  mfsp->descriptors[i].offset,
610  totsize));
611  mfsp->descriptors[i].offset = dest_offset;
612  dest_offset += totsize;
613  }
614  }
615 
616  /* New current bank.*/
617  mfsp->current_bank = dbank;
618  mfsp->current_counter += 1U;
619  mfsp->next_offset = dest_offset;
620 
621  /* The header is written after the data.*/
623 
624  /* The source bank is erased last.*/
625  RET_ON_ERROR(mfs_bank_erase(mfsp, sbank));
626 
627  return MFS_NO_ERROR;
628 }
629 
630 /**
631  * @brief Performs a flash partition mount attempt.
632  *
633  * @param[in] mfsp pointer to the @p MFSDriver object
634  * @return The operation status.
635  *
636  * @api
637  */
639  mfs_bank_state_t sts0, sts1;
640  mfs_bank_t bank;
641  uint32_t cnt0 = 0, cnt1 = 0;
642  bool w1 = false, w2 = false;
643 
644  /* Resetting the bank state.*/
645  mfs_state_reset(mfsp);
646 
647  /* Assessing the state of the two banks.*/
648  RET_ON_ERROR(mfs_bank_get_state(mfsp, MFS_BANK_0, &sts0, &cnt0));
649  RET_ON_ERROR(mfs_bank_get_state(mfsp, MFS_BANK_1, &sts1, &cnt1));
650 
651  /* Handling all possible scenarios, each one requires its own recovery
652  strategy.*/
653  switch (PAIR(sts0, sts1)) {
654 
655  case PAIR(MFS_BANK_ERASED, MFS_BANK_ERASED):
656  /* Both banks erased, first initialization.*/
657  RET_ON_ERROR(mfs_bank_write_header(mfsp, MFS_BANK_0, 1));
658  bank = MFS_BANK_0;
659  break;
660 
661  case PAIR(MFS_BANK_OK, MFS_BANK_OK):
662  /* Both banks appear to be valid but one must be newer, erasing the
663  older one.*/
664  if (cnt0 > cnt1) {
665  /* Bank 0 is newer.*/
666  RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_1));
667  bank = MFS_BANK_0;
668  }
669  else {
670  /* Bank 1 is newer.*/
671  RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_0));
672  bank = MFS_BANK_1;
673  }
674  w1 = true;
675  break;
676 
677  case PAIR(MFS_BANK_GARBAGE, MFS_BANK_GARBAGE):
678  /* Both banks are unreadable, reinitializing.*/
679  RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_0));
680  RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_1));
681  RET_ON_ERROR(mfs_bank_write_header(mfsp, MFS_BANK_0, 1));
682  bank = MFS_BANK_0;
683  w1 = true;
684  break;
685 
686  case PAIR(MFS_BANK_ERASED, MFS_BANK_OK):
687  /* Normal situation, bank one is used.*/
688  bank = MFS_BANK_1;
689  break;
690 
691  case PAIR(MFS_BANK_OK, MFS_BANK_ERASED):
692  /* Normal situation, bank zero is used.*/
693  bank = MFS_BANK_0;
694  break;
695 
696  case PAIR(MFS_BANK_ERASED, MFS_BANK_GARBAGE):
697  /* Bank zero is erased, bank one is not readable.*/
698  RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_1));
699  RET_ON_ERROR(mfs_bank_write_header(mfsp, MFS_BANK_0, 1));
700  bank = MFS_BANK_0;
701  w1 = true;
702  break;
703 
704  case PAIR(MFS_BANK_GARBAGE, MFS_BANK_ERASED):
705  /* Bank zero is not readable, bank one is erased.*/
706  RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_0));
707  RET_ON_ERROR(mfs_bank_write_header(mfsp, MFS_BANK_1, 1));
708  bank = MFS_BANK_1;
709  w1 = true;
710  break;
711 
712  case PAIR(MFS_BANK_OK, MFS_BANK_GARBAGE):
713  /* Bank zero is normal, bank one is unreadable.*/
714  RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_1));
715  bank = MFS_BANK_0;
716  w1 = true;
717  break;
718 
719  case PAIR(MFS_BANK_GARBAGE, MFS_BANK_OK):
720  /* Bank zero is unreadable, bank one is normal.*/
721  RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_0));
722  bank = MFS_BANK_1;
723  w1 = true;
724  break;
725 
726  default:
727  return MFS_ERR_INTERNAL;
728  }
729 
730  /* Mounting the bank.*/
731  {
732  unsigned i;
733 
734  /* Reading the bank header again.*/
735  RET_ON_ERROR(mfs_flash_read(mfsp, mfs_flash_get_bank_offset(mfsp, bank),
736  sizeof (mfs_bank_header_t),
737  mfsp->buffer.data8));
738 
739  /* Checked again for extra safety.*/
740  if (mfs_bank_check_header(mfsp) != MFS_BANK_OK) {
741  return MFS_ERR_INTERNAL;
742  }
743 
744  /* Storing the bank data.*/
745  mfsp->current_bank = bank;
746  mfsp->current_counter = mfsp->buffer.bhdr.fields.counter;
747 
748  /* Scanning for the most recent instance of all records.*/
749  RET_ON_ERROR(mfs_bank_scan_records(mfsp, bank, &w2));
750 
751  /* Calculating the effective used size.*/
753  for (i = 0; i < MFS_CFG_MAX_RECORDS; i++) {
754  if (mfsp->descriptors[i].offset != 0U) {
755  mfsp->used_space += ALIGNED_REC_SIZE(mfsp->descriptors[i].size);
756  }
757  }
758  }
759 
760  /* In case of detected problems then a garbage collection is performed in
761  order to repair/remove anomalies.*/
762  if (w2) {
764  }
765 
766  return (w1 || w2) ? MFS_WARN_REPAIR : MFS_NO_ERROR;
767 }
768 
769 /**
770  * @brief Configures and activates a MFS driver.
771  *
772  * @param[in] mfsp pointer to the @p MFSDriver object
773  * @return The operation status.
774  * @retval MFS_NO_ERROR if the operation has been successfully
775  * completed.
776  * @retval MFS_WARN_GC if the operation triggered a garbage
777  * collection.
778  * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
779  * failures. Makes the driver enter the
780  * @p MFS_ERROR state.
781  * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
782  *
783  * @api
784  */
786  unsigned i;
787 
788  /* Resetting previous state.*/
789  mfs_state_reset(mfsp);
790 
791  /* Attempting to mount the managed partition.*/
792  for (i = 0; i < MFS_CFG_MAX_REPAIR_ATTEMPTS; i++) {
793  mfs_error_t err;
794 
795  err = mfs_try_mount(mfsp);
796  if (err == MFS_ERR_INTERNAL) {
797  /* Special case, do not retry on internal errors but report
798  immediately.*/
799  mfsp->state = MFS_ERROR;
800  return err;
801  }
802  if (!MFS_IS_ERROR(err)) {
803  mfsp->state = MFS_READY;
804  return err;
805  }
806  }
807 
808  /* Driver start failed.*/
809  mfsp->state = MFS_ERROR;
810  return MFS_ERR_FLASH_FAILURE;
811 }
812 
813 /*===========================================================================*/
814 /* Driver exported functions. */
815 /*===========================================================================*/
816 
817 /**
818  * @brief Initializes an instance.
819  *
820  * @param[out] mfsp pointer to the @p MFSDriver object
821  *
822  * @init
823  */
825 
826  osalDbgCheck(mfsp != NULL);
827 
828  mfsp->state = MFS_STOP;
829  mfsp->config = NULL;
830 }
831 
832 /**
833  * @brief Configures and activates a MFS driver.
834  *
835  * @param[in] mfsp pointer to the @p MFSDriver object
836  * @param[in] config pointer to the configuration
837  * @return The operation status.
838  * @retval MFS_NO_ERROR if the operation has been
839  * completed.
840  * @retval MFS_WARN_GC if the operation triggered a garbage
841  * collection.
842  * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
843  * failures. Makes the driver enter the
844  * @p MFS_ERROR state.
845  * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
846  *
847  * @api
848  */
849 mfs_error_t mfsStart(MFSDriver *mfsp, const MFSConfig *config) {
850 
851  osalDbgCheck((mfsp != NULL) && (config != NULL));
852  osalDbgAssert((mfsp->state == MFS_STOP) || (mfsp->state == MFS_READY) ||
853  (mfsp->state == MFS_ERROR), "invalid state");
854 
855  /* Storing configuration.*/
856  mfsp->config = config;
857 
858  return mfs_mount(mfsp);
859 }
860 
861 /**
862  * @brief Deactivates a MFS driver.
863  *
864  * @param[in] mfsp pointer to the @p MFSDriver object
865  *
866  * @api
867  */
868 void mfsStop(MFSDriver *mfsp) {
869 
870  osalDbgCheck(mfsp != NULL);
871  osalDbgAssert((mfsp->state == MFS_STOP) || (mfsp->state == MFS_READY) ||
872  (mfsp->state == MFS_ERROR), "invalid state");
873 
874  mfsp->config = NULL;
875  mfsp->state = MFS_STOP;
876 }
877 
878 /**
879  * @brief Destroys the state of the managed storage by erasing the flash.
880  *
881  * @param[in] mfsp pointer to the @p MFSDriver object
882  * @return The operation status.
883  * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY
884  * state.
885  * @retval MFS_NO_ERROR if the operation has been successfully
886  * completed.
887  * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
888  * failures. Makes the driver enter the
889  * @p MFS_ERROR state.
890  * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
891  *
892  * @api
893  */
895 
896  osalDbgCheck(mfsp != NULL);
897 
898  if (mfsp->state != MFS_READY) {
899  return MFS_ERR_INV_STATE;
900  }
901 
902  RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_0));
903  RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_1));
904 
905  return mfs_mount(mfsp);
906 }
907 
908 /**
909  * @brief Retrieves and reads a data record.
910  *
911  * @param[in] mfsp pointer to the @p MFSDriver object
912  * @param[in] id record numeric identifier, the valid range is between
913  * @p 1 and @p MFS_CFG_MAX_RECORDS
914  * @param[in,out] np on input is the maximum buffer size, on return it is
915  * the size of the data copied into the buffer
916  * @param[out] buffer pointer to a buffer for record data
917  * @return The operation status.
918  * @retval MFS_NO_ERROR if the operation has been successfully
919  * completed.
920  * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY
921  * state.
922  * @retval MFS_ERR_INV_SIZE if the passed buffer is not large enough to
923  * contain the record data.
924  * @retval MFS_ERR_NOT_FOUND if the specified id does not exists.
925  * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
926  * failures. Makes the driver enter the
927  * @p MFS_ERROR state.
928  * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
929  *
930  * @api
931  */
933  size_t *np, uint8_t *buffer) {
934  uint16_t crc;
935 
936  osalDbgCheck((mfsp != NULL) &&
937  (id >= 1U) && (id <= (mfs_id_t)MFS_CFG_MAX_RECORDS) &&
938  (np != NULL) && (*np > 0U) && (buffer != NULL));
939 
940  if ((mfsp->state != MFS_READY) && (mfsp->state != MFS_TRANSACTION)) {
941  return MFS_ERR_INV_STATE;
942  }
943 
944  /* Checking if the requested record actually exists.*/
945  if (mfsp->descriptors[id - 1U].offset == 0U) {
946  return MFS_ERR_NOT_FOUND;
947  }
948 
949  /* Making sure to not overflow the buffer.*/
950  if (*np < mfsp->descriptors[id - 1U].size) {
951  return MFS_ERR_INV_SIZE;
952  }
953 
954  /* Header read from flash.*/
956  mfsp->descriptors[id - 1U].offset,
957  sizeof (mfs_data_header_t),
958  mfsp->buffer.data8));
959 
960  /* Data read from flash.*/
961  *np = mfsp->descriptors[id - 1U].size;
963  mfsp->descriptors[id - 1U].offset + sizeof (mfs_data_header_t),
964  *np,
965  buffer));
966 
967  /* Checking CRC.*/
968  crc = crc16(0xFFFFU, buffer, *np);
969  if (crc != mfsp->buffer.dhdr.fields.crc) {
970  mfsp->state = MFS_ERROR;
971  return MFS_ERR_FLASH_FAILURE;
972  }
973 
974  return MFS_NO_ERROR;
975 }
976 
977 /**
978  * @brief Creates or updates a data record.
979  *
980  * @param[in] mfsp pointer to the @p MFSDriver object
981  * @param[in] id record numeric identifier, the valid range is between
982  * @p 1 and @p MFS_CFG_MAX_RECORDS
983  * @param[in] n size of data to be written, it cannot be zero
984  * @param[in] buffer pointer to a buffer for record data
985  * @return The operation status.
986  * @retval MFS_NO_ERROR if the operation has been successfully
987  * completed.
988  * @retval MFS_WARN_GC if the operation triggered a garbage
989  * collection.
990  * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY
991  * state.
992  * @retval MFS_ERR_OUT_OF_MEM if there is not enough flash space for the
993  * operation.
994  * @retval MFS_ERR_TRANSACTION_NUM if the transaction operations buffer space
995  * has been exceeded.
996  * @retval MFS_ERR_TRANSACTION_SIZE if the transaction allocated space
997  * has been exceeded.
998  * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
999  * failures. Makes the driver enter the
1000  * @p MFS_ERROR state.
1001  * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
1002  *
1003  * @api
1004  */
1006  size_t n, const uint8_t *buffer) {
1007  flash_offset_t free, asize, rspace;
1008 
1009  osalDbgCheck((mfsp != NULL) &&
1010  (id >= 1U) && (id <= (mfs_id_t)MFS_CFG_MAX_RECORDS) &&
1011  (n > 0U) && (buffer != NULL));
1012 
1013  /* Aligned record size.*/
1014  asize = ALIGNED_REC_SIZE(n);
1015 
1016  /* Normal mode code path.*/
1017  if (mfsp->state == MFS_READY) {
1018  bool warning = false;
1019 
1020  /* If the required space is beyond the available (compacted) block
1021  size then an error is returned.
1022  NOTE: The space for one extra header is reserved in order to allow
1023  for an erase operation after the space has been fully allocated.*/
1024  rspace = ALIGNED_DHDR_SIZE + asize;
1025  if (rspace > mfsp->config->bank_size - mfsp->used_space) {
1026  return MFS_ERR_OUT_OF_MEM;
1027  }
1028 
1029  /* Checking for immediately (not compacted) available space.*/
1030  free = (mfs_flash_get_bank_offset(mfsp, mfsp->current_bank) +
1031  mfsp->config->bank_size) - mfsp->next_offset;
1032  if (rspace > free) {
1033  /* We need to perform a garbage collection, there is enough space
1034  but it has to be freed.*/
1035  warning = true;
1037  }
1038 
1039  /* Writing the data header without the magic, it will be written last.*/
1040  mfsp->buffer.dhdr.fields.id = (uint16_t)id;
1041  mfsp->buffer.dhdr.fields.size = (uint32_t)n;
1042  mfsp->buffer.dhdr.fields.crc = crc16(0xFFFFU, buffer, n);
1044  mfsp->next_offset + (sizeof (uint32_t) * 2U),
1045  sizeof (mfs_data_header_t) - (sizeof (uint32_t) * 2U),
1046  mfsp->buffer.data8 + (sizeof (uint32_t) * 2U)));
1047 
1048  /* Writing the data part.*/
1050  mfsp->next_offset + sizeof (mfs_data_header_t),
1051  n,
1052  buffer));
1053 
1054  /* Finally writing the magic number, it seals the operation.*/
1055  mfsp->buffer.dhdr.fields.magic1 = (uint32_t)MFS_HEADER_MAGIC_1;
1056  mfsp->buffer.dhdr.fields.magic2 = (uint32_t)MFS_HEADER_MAGIC_2;
1058  mfsp->next_offset,
1059  sizeof (uint32_t) * 2U,
1060  mfsp->buffer.data8));
1061 
1062  /* The size of the old record instance, if present, must be subtracted
1063  to the total used size.*/
1064  if (mfsp->descriptors[id - 1U].offset != 0U) {
1065  mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[id - 1U].size);
1066  }
1067 
1068  /* Adjusting bank-related metadata.*/
1069  mfsp->descriptors[id - 1U].offset = mfsp->next_offset;
1070  mfsp->descriptors[id - 1U].size = (uint32_t)n;
1071  mfsp->next_offset += asize;
1072  mfsp->used_space += asize;
1073 
1074  return warning ? MFS_WARN_GC : MFS_NO_ERROR;
1075  }
1076 
1077 #if MFS_CFG_TRANSACTION_MAX > 0
1078  /* Transaction mode code path.*/
1079  if (mfsp->state == MFS_TRANSACTION) {
1080  mfs_transaction_op_t *top;
1081 
1082  /* Checking if the maximum number of operations in a transaction is
1083  Exceeded.*/
1084  if (mfsp->tr_nops >= MFS_CFG_TRANSACTION_MAX) {
1085  return MFS_ERR_TRANSACTION_NUM;
1086  }
1087 
1088  /* If the required space is greater than the space allocated for the
1089  transaction then error.*/
1090  rspace = asize;
1091  if (rspace > mfsp->tr_limit_offet - mfsp->tr_next_offset) {
1092  return MFS_ERR_TRANSACTION_SIZE;
1093  }
1094 
1095  /* Writing the data header without the magic, it will be written last.*/
1096  mfsp->buffer.dhdr.fields.id = (uint16_t)id;
1097  mfsp->buffer.dhdr.fields.size = (uint32_t)n;
1098  mfsp->buffer.dhdr.fields.crc = crc16(0xFFFFU, buffer, n);
1100  mfsp->tr_next_offset + (sizeof (uint32_t) * 2U),
1101  sizeof (mfs_data_header_t) - (sizeof (uint32_t) * 2U),
1102  mfsp->buffer.data8 + (sizeof (uint32_t) * 2U)));
1103 
1104  /* Writing the data part.*/
1106  mfsp->tr_next_offset + sizeof (mfs_data_header_t),
1107  n,
1108  buffer));
1109 
1110  /* Adding a transaction operation record.*/
1111  top = &mfsp->tr_ops[mfsp->tr_nops];
1112  top->offset = mfsp->tr_next_offset;
1113  top->size = n;
1114  top->id = id;
1115 
1116  /* Number of records and next write position updated.*/
1117  mfsp->tr_nops++;
1118  mfsp->tr_next_offset += asize;
1119 
1120  return MFS_NO_ERROR;
1121  }
1122 #endif /* MFS_CFG_TRANSACTION_MAX > 0 */
1123 
1124  /* Invalid state.*/
1125  return MFS_ERR_INV_STATE;
1126 }
1127 
1128 /**
1129  * @brief Erases a data record.
1130  *
1131  * @param[in] mfsp pointer to the @p MFSDriver object
1132  * @param[in] id record numeric identifier, the valid range is between
1133  * @p 1 and @p MFS_CFG_MAX_RECORDS
1134  * @return The operation status.
1135  * @retval MFS_NO_ERROR if the operation has been successfully
1136  * completed.
1137  * @retval MFS_WARN_GC if the operation triggered a garbage
1138  * collection.
1139  * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY
1140  * state.
1141  * @retval MFS_ERR_OUT_OF_MEM if there is not enough flash space for the
1142  * operation.
1143  * @retval MFS_ERR_TRANSACTION_NUM if the transaction operations buffer space
1144  * has been exceeded.
1145  * @retval MFS_ERR_TRANSACTION_SIZE if the transaction allocated space
1146  * has been exceeded.
1147  * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
1148  * failures. Makes the driver enter the
1149  * @p MFS_ERROR state.
1150  * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
1151  *
1152  * @api
1153  */
1155  flash_offset_t free, asize, rspace;
1156 
1157  osalDbgCheck((mfsp != NULL) &&
1158  (id >= 1U) && (id <= (mfs_id_t)MFS_CFG_MAX_RECORDS));
1159 
1160  /* Aligned record size.*/
1161  asize = ALIGNED_DHDR_SIZE;
1162 
1163  /* Normal mode code path.*/
1164  if (mfsp->state == MFS_READY) {
1165  bool warning = false;
1166 
1167  /* Checking if the requested record actually exists.*/
1168  if (mfsp->descriptors[id - 1U].offset == 0U) {
1169  return MFS_ERR_NOT_FOUND;
1170  }
1171 
1172  /* If the required space is beyond the available (compacted) block
1173  size then an internal error is returned, it should never happen.*/
1174  rspace = asize;
1175  if (rspace > mfsp->config->bank_size - mfsp->used_space) {
1176  return MFS_ERR_INTERNAL;
1177  }
1178 
1179  /* Checking for immediately (not compacted) available space.*/
1180  free = (mfs_flash_get_bank_offset(mfsp, mfsp->current_bank) +
1181  mfsp->config->bank_size) - mfsp->next_offset;
1182  if (rspace > free) {
1183  /* We need to perform a garbage collection, there is enough space
1184  but it has to be freed.*/
1185  warning = true;
1187  }
1188 
1189  /* Writing the data header with size set to zero, it means that the
1190  record is logically erased.*/
1191  mfsp->buffer.dhdr.fields.magic1 = (uint32_t)MFS_HEADER_MAGIC_1;
1192  mfsp->buffer.dhdr.fields.magic2 = (uint32_t)MFS_HEADER_MAGIC_2;
1193  mfsp->buffer.dhdr.fields.id = (uint16_t)id;
1194  mfsp->buffer.dhdr.fields.size = (uint32_t)0;
1195  mfsp->buffer.dhdr.fields.crc = (uint16_t)0xFFFF;
1197  mfsp->next_offset,
1198  sizeof (mfs_data_header_t),
1199  mfsp->buffer.data8));
1200 
1201  /* Adjusting bank-related metadata.*/
1202  mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[id - 1U].size);
1203  mfsp->next_offset += sizeof (mfs_data_header_t);
1204  mfsp->descriptors[id - 1U].offset = 0U;
1205  mfsp->descriptors[id - 1U].size = 0U;
1206 
1207  return warning ? MFS_WARN_GC : MFS_NO_ERROR;
1208  }
1209 
1210 #if MFS_CFG_TRANSACTION_MAX > 0
1211  /* Transaction mode code path.*/
1212  if (mfsp->state == MFS_TRANSACTION) {
1213  mfs_transaction_op_t *top;
1214 
1215  /* Checking if the requested record actually exists.*/
1216  if (mfsp->descriptors[id - 1U].offset == 0U) {
1217  return MFS_ERR_NOT_FOUND;
1218  }
1219 
1220  /* Checking if the maximum number of operations in a transaction is
1221  Exceeded.*/
1222  if (mfsp->tr_nops >= MFS_CFG_TRANSACTION_MAX) {
1223  return MFS_ERR_TRANSACTION_NUM;
1224  }
1225 
1226  /* If the required space is greater than the space allocated for the
1227  transaction then error.*/
1228  rspace = asize;
1229  if (rspace > mfsp->tr_limit_offet - mfsp->tr_next_offset) {
1230  return MFS_ERR_TRANSACTION_SIZE;
1231  }
1232 
1233  /* Writing the data header with size set to zero, it means that the
1234  record is logically erased. Note, the magic number is not set.*/
1235  mfsp->buffer.dhdr.fields.id = (uint16_t)id;
1236  mfsp->buffer.dhdr.fields.size = (uint32_t)0;
1237  mfsp->buffer.dhdr.fields.crc = (uint16_t)0xFFFF;
1239  mfsp->tr_next_offset + (sizeof (uint32_t) * 2U),
1240  sizeof (mfs_data_header_t) - (sizeof (uint32_t) * 2U),
1241  mfsp->buffer.data8 + (sizeof (uint32_t) * 2U)));
1242 
1243  /* Adding a transaction operation record.*/
1244  top = &mfsp->tr_ops[mfsp->tr_nops];
1245  top->offset = mfsp->tr_next_offset;
1246  top->size = 0U;
1247  top->id = id;
1248 
1249  /* Number of records and next write position updated.*/
1250  mfsp->tr_nops++;
1251  mfsp->tr_next_offset += asize;
1252 
1253  return MFS_NO_ERROR;
1254  }
1255 #endif /* MFS_CFG_TRANSACTION_MAX > 0 */
1256 
1257  return MFS_ERR_INV_STATE;
1258 }
1259 
1260 /**
1261  * @brief Enforces a garbage collection operation.
1262  * @details Garbage collection involves: integrity check, optionally repairs,
1263  * obsolete data removal, data compaction and a flash bank swap.
1264  *
1265  * @param[in] mfsp pointer to the @p MFSDriver object
1266  * @return The operation status.
1267  * @retval MFS_NO_ERROR if the operation has been successfully
1268  * completed.
1269  * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY
1270  * state.
1271  * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW
1272  * failures. Makes the driver enter the
1273  * @p MFS_ERROR state.
1274  * @retval MFS_ERR_INTERNAL if an internal logic failure is detected.
1275  *
1276  * @api
1277  */
1279 
1280  osalDbgCheck(mfsp != NULL);
1281 
1282  if (mfsp->state != MFS_READY) {
1283  return MFS_ERR_INV_STATE;
1284  }
1285 
1286  return mfs_garbage_collect(mfsp);
1287 }
1288 
1289 #if (MFS_CFG_TRANSACTION_MAX > 0) || defined(__DOXYGEN__)
1290 /**
1291  * @brief Puts the driver in transaction mode.
1292  * @note The parameters @p n and @p size are used to make an
1293  * estimation of the space required for the transaction to succeed.
1294  * Note that the estimated size must include also the extra space
1295  * required by alignment enforcement option. If the estimated size
1296  * is wrong (by defect) what could happen is that there is a failure
1297  * in the middle of a transaction and a roll-back would be required.
1298  * @note The conditions for starting a transaction are:
1299  * - The driver must be started.
1300  * - There must be enough compacted storage to accommodate the whole
1301  * transaction. If the required space is available but it is not
1302  * compacted then a garbage collect operation is performed.
1303  * .
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  */
1377  mfs_transaction_op_t *top;
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->buffer.dhdr.fields.magic1 = (uint32_t)MFS_HEADER_MAGIC_1;
1388  mfsp->buffer.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->buffer.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 /** @} */
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:932
uint16_t crc
Data CRC.
Definition: hal_mfs.h:258
void mfsObjectInit(MFSDriver *mfsp)
Initializes an instance.
Definition: hal_mfs.c:824
Type of a buffered write/erase operation within a transaction.
Definition: hal_mfs.h:321
uint32_t flash_sector_t
Type of a flash sector number.
Definition: hal_flash.h:88
uint32_t erased
Erased value.
Definition: hal_mfs.h:291
union MFSDriver::@2 buffer
Transient buffer.
static mfs_error_t mfs_try_mount(MFSDriver *mfsp)
Performs a flash partition mount attempt.
Definition: hal_mfs.c:638
static mfs_error_t mfs_garbage_collect(MFSDriver *mfsp)
Enforces a garbage collection.
Definition: hal_mfs.c:587
#define MFS_CFG_TRANSACTION_MAX
Maximum number of objects writable in a single transaction.
Definition: hal_mfs.h:108
mfs_record_descriptor_t descriptors[MFS_CFG_MAX_RECORDS]
Offsets of the most recent instance of the records.
Definition: hal_mfs.h:368
#define ALIGNED_SIZEOF(t)
Aligned size of a type.
Definition: hal_mfs.c:62
mfs_state_t state
Driver state.
Definition: hal_mfs.h:343
#define flashProgram(ip, offset, n, pp)
Program operation.
Definition: hal_flash.h:252
mfs_id_t id
Record identifier.
Definition: hal_mfs.h:333
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:549
size_t size
Written data size.
Definition: hal_mfs.h:329
#define flashVerifyErase(ip, sector)
Returns the erase state of a sector.
Definition: hal_flash.h:314
Type of a MFS configuration structure.
Definition: hal_mfs.h:283
uint32_t mfs_id_t
Type of a record identifier.
Definition: hal_mfs.h:202
void mfsStop(MFSDriver *mfsp)
Deactivates a MFS driver.
Definition: hal_mfs.c:868
mfs_transaction_op_t tr_ops[MFS_CFG_TRANSACTION_MAX]
Buffered operations in current transaction.
Definition: hal_mfs.h:385
flash_offset_t used_space
Used space in the current bank without considering erased records.
Definition: hal_mfs.h:363
Type of a data block header.
Definition: hal_mfs.h:241
mfs_error_t mfsRollbackTransaction(MFSDriver *mfsp)
A transaction is rolled back atomically.
Definition: hal_mfs.c:1457
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
HAL subsystem header.
uint32_t tr_nops
Number of buffered operations in current transaction.
Definition: hal_mfs.h:381
flash_offset_t tr_next_offset
Next write offset for current transaction.
Definition: hal_mfs.h:373
uint32_t size
Data size.
Definition: hal_mfs.h:263
mfs_error_t mfsPerformGarbageCollection(MFSDriver *mfsp)
Enforces a garbage collection operation.
Definition: hal_mfs.c:1278
mfs_bank_state_t
Type of a bank state assessment.
Definition: hal_mfs.h:193
uint16_t crc
Header CRC.
Definition: hal_mfs.h:231
mfs_error_t mfsStart(MFSDriver *mfsp, const MFSConfig *config)
Configures and activates a MFS driver.
Definition: hal_mfs.c:849
mfs_error_t mfsCommitTransaction(MFSDriver *mfsp)
A transaction is committed and finalized atomically.
Definition: hal_mfs.c:1376
uint32_t magic1
Bank magic 1.
Definition: hal_mfs.h:213
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
flash_offset_t bank_size
Banks size.
Definition: hal_mfs.h:295
#define ALIGNED_DHDR_SIZE
Data record header size aligned.
Definition: hal_mfs.c:56
flash_offset_t offset
Written header offset.
Definition: hal_mfs.h:325
#define MFS_CFG_MAX_REPAIR_ATTEMPTS
Maximum number of repair attempts on partition mount.
Definition: hal_mfs.h:62
#define ALIGNED_REC_SIZE(n)
Data record size aligned.
Definition: hal_mfs.c:50
#define MFS_CFG_BUFFER_SIZE
Size of the buffer used for data copying.
Definition: hal_mfs.h:90
uint16_t reserved1
Reserved field.
Definition: hal_mfs.h:227
mfs_error_t mfsEraseRecord(MFSDriver *mfsp, mfs_id_t id)
Erases a data record.
Definition: hal_mfs.c:1154
#define RET_ON_ERROR(err)
Error check helper.
Definition: hal_mfs.c:73
mfs_error_t mfs_mount(MFSDriver *mfsp)
Configures and activates a MFS driver.
Definition: hal_mfs.c:785
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
flash_sector_t bank1_start
Base sector index for bank 1.
Definition: hal_mfs.h:309
#define flashRead(ip, offset, n, rp)
Read operation.
Definition: hal_flash.h:234
flash_error_t
Type of a flash error code.
Definition: hal_flash.h:70
#define osalDbgCheck(c)
Function parameters check.
Definition: osal.h:278
flash_sector_t bank0_sectors
Number of sectors for bank 0.
Definition: hal_mfs.h:305
flash_error_t flashWaitErase(BaseFlash *devp)
Waits until the current erase operation is finished.
Definition: hal_flash.c:59
mfs_error_t mfsStartTransaction(MFSDriver *mfsp, size_t size)
Puts the driver in transaction mode.
Definition: hal_mfs.c:1320
#define flashStartEraseSector(ip, sector)
Starts an sector erase operation.
Definition: hal_flash.h:281
flash_sector_t bank1_sectors
Number of sectors for bank 1.
Definition: hal_mfs.h:315
flash_offset_t next_offset
Pointer to the next free position in the current bank.
Definition: hal_mfs.h:359
mfs_error_t mfsErase(MFSDriver *mfsp)
Destroys the state of the managed storage by erasing the flash.
Definition: hal_mfs.c:894
Type of a bank header.
Definition: hal_mfs.h:208
uint32_t flash_offset_t
Type of a flash offset.
Definition: hal_flash.h:83
#define MFS_CFG_MAX_RECORDS
Maximum number of indexed records in the managed storage.
Definition: hal_mfs.h:55
Managed Flash Storage module header.
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:1005
uint32_t counter
Usage counter of the bank.
Definition: hal_mfs.h:223
Type of an MFS instance.
Definition: hal_mfs.h:339
uint32_t magic1
Data header magic 1.
Definition: hal_mfs.h:246
mfs_error_t
Type of an MFS error code.
Definition: hal_mfs.h:175
mfs_bank_t current_bank
Bank currently in use.
Definition: hal_mfs.h:351
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_flash_copy(MFSDriver *mfsp, flash_offset_t doffset, flash_offset_t soffset, uint32_t n)
Flash copy.
Definition: hal_mfs.c:243
uint32_t current_counter
Usage counter of the current bank.
Definition: hal_mfs.h:355
#define osalDbgAssert(c, remark)
Condition assertion.
Definition: osal.h:258
flash_offset_t flashGetSectorOffset(BaseFlash *devp, flash_sector_t sector)
Returns the offset of a sector.
Definition: hal_flash.c:84
uint32_t magic2
Data header magic 2.
Definition: hal_mfs.h:250
flash_sector_t bank0_start
Base sector index for bank 0.
Definition: hal_mfs.h:299
BaseFlash * flashp
Flash driver associated to this MFS instance.
Definition: hal_mfs.h:287
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
mfs_bank_t
Type of a flash bank.
Definition: hal_mfs.h:154
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
#define PAIR(a, b)
Combines two values (0..3) in one (0..15).
Definition: hal_mfs.c:68
flash_offset_t tr_limit_offet
Maximum offset for the transaction.
Definition: hal_mfs.h:377
uint32_t magic2
Bank magic 2.
Definition: hal_mfs.h:217
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
const MFSConfig * config
Current configuration data.
Definition: hal_mfs.h:347
uint16_t id
Record identifier.
Definition: hal_mfs.h:254