MFS development.
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@9620 35acf78f-673a-0410-8e92-d51de3d6d3f4
This commit is contained in:
parent
b1d15cdc5a
commit
82631a20b9
|
@ -20,6 +20,15 @@
|
|||
/**
|
||||
* @file mfs.c
|
||||
* @brief Managed Flash Storage module code.
|
||||
* @details This module manages a flash partition as a generic storage where
|
||||
* arbitrary data records can be created, updated, deleted and
|
||||
* retrieved.<br>
|
||||
* A managed partition is composed of two banks of equal size, a
|
||||
* bank is composed of one or more erasable sectors, a sector is
|
||||
* divided in writable pages.<br>
|
||||
* The module handles flash wear leveling and recovery of damaged
|
||||
* banks (where possible) caused by power loss during operations.
|
||||
* Both operations are transparent to the user.
|
||||
*
|
||||
* @addtogroup mfs
|
||||
* @{
|
||||
|
@ -37,6 +46,18 @@
|
|||
#define MFS_BANK_MAGIC2 0x0FE14991
|
||||
#define MFS_HEADER_MAGIC 0x1AC7002E
|
||||
|
||||
#define PAIR(a, b) (((unsigned)(a) << 2U) | (unsigned)(b))
|
||||
|
||||
/**
|
||||
* @brief Error check helper.
|
||||
*/
|
||||
#define RET_ON_ERROR(err) do { \
|
||||
mfs_error_t e = (err); \
|
||||
if (e != MFS_NO_ERROR) { \
|
||||
return e; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
@ -76,6 +97,214 @@ void mfs_cache_erase_id(MFSDriver *devp, uint32_t id) {
|
|||
}
|
||||
#endif /* MFS_CFG_ID_CACHE_SIZE > 0 */
|
||||
|
||||
/**
|
||||
* @brief Determines the state of a flash bank.
|
||||
*
|
||||
* @param[in] bank the bank identifier
|
||||
* @param[out] cntp bank counter value, only valid if the bank is not
|
||||
* in the @p MFS_BANK_GARBAGE or @p MFS_BANK_ERASED
|
||||
* states.
|
||||
*
|
||||
* @return The bank state.
|
||||
* @retval MFS_BANK_ERASED if the bank is fully erased.
|
||||
* @retval MFS_BANK_OK if the bank contains valid data.
|
||||
* @retval MFS_BANK_PARTIAL if the bank contains errors but the data is still
|
||||
* readable.
|
||||
* @retval MFS_BANK_GARBAGE if the bank contains unreadable garbage.
|
||||
*/
|
||||
static mfs_bank_state_t mfs_get_bank_state(mfs_bank_t bank, uint32_t *cntp) {
|
||||
|
||||
return MFS_BANK_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a flash partition mount attempt.
|
||||
*
|
||||
* @param[in] devp pointer to the @p MFSDriver object
|
||||
* @return The operation status.
|
||||
* @retval MFS_NO_ERROR if the operation has been successfully completed.
|
||||
* @retval MFS_REPAIR_WARNING if the operation has been completed but a
|
||||
* repair has been performed.
|
||||
* @retval MFS_FLASH_FAILURE if the flash memory is unusable because HW
|
||||
* failures.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
static mfs_error_t mfs_try_mount(MFSDriver *devp) {
|
||||
mfs_bank_state_t sts0, sts1;
|
||||
uint32_t cnt0 = 0, cnt1 = 0;
|
||||
|
||||
/* Assessing the state of the two banks.*/
|
||||
sts0 = mfs_get_bank_state(MFS_BANK_0, &cnt0);
|
||||
sts1 = mfs_get_bank_state(MFS_BANK_1, &cnt1);
|
||||
|
||||
/* Handling all possible scenarios, each one requires its own recovery
|
||||
strategy.*/
|
||||
switch (PAIR(sts0, sts1)) {
|
||||
|
||||
case PAIR(MFS_BANK_ERASED, MFS_BANK_ERASED):
|
||||
/* Both banks erased, first initialization.*/
|
||||
RET_ON_ERROR(mfs_validate(MFS_BANK_0, 1));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_0));
|
||||
return MFS_NO_ERROR;
|
||||
|
||||
case PAIR(MFS_BANK_ERASED, MFS_BANK_OK):
|
||||
/* Normal situation, bank one is used.*/
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_1));
|
||||
return MFS_NO_ERROR;
|
||||
|
||||
case PAIR(MFS_BANK_ERASED, MFS_BANK_PARTIAL):
|
||||
/* Bank zero is erased, bank one has problems.*/
|
||||
RET_ON_ERROR(mfs_copy_bank(MFS_BANK_1, MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_validate(MFS_BANK_0, cnt1 + 1));
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_0));
|
||||
return MFS_REPAIR_WARNING;
|
||||
|
||||
case PAIR(MFS_BANK_ERASED, MFS_BANK_GARBAGE):
|
||||
/* Bank zero is erased, bank one is not readable.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_validate(MFS_BANK_0, 1));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_0));
|
||||
return MFS_REPAIR_WARNING;
|
||||
|
||||
case PAIR(MFS_BANK_OK, MFS_BANK_ERASED):
|
||||
/* Normal situation, bank zero is used.*/
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_0));
|
||||
return MFS_NO_ERROR;
|
||||
|
||||
case PAIR(MFS_BANK_OK, MFS_BANK_OK):
|
||||
/* Both banks appear to be valid but one must be newer, erasing the
|
||||
older one.*/
|
||||
if (cnt0 > cnt1) {
|
||||
/* Bank 0 is newer.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_0));
|
||||
}
|
||||
else {
|
||||
/* Bank 1 is newer.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_1));
|
||||
}
|
||||
return MFS_REPAIR_WARNING;
|
||||
|
||||
case PAIR(MFS_BANK_OK, MFS_BANK_PARTIAL):
|
||||
/* Bank zero is normal, bank one has problems.*/
|
||||
if (cnt0 > cnt1) {
|
||||
/* Normal bank zero is more recent than the partial bank one, the
|
||||
partial bank needs to be erased.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_0));
|
||||
}
|
||||
else {
|
||||
/* Partial bank one is more recent than the normal bank zero.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_copy_bank(MFS_BANK_1, MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_validate(MFS_BANK_0, cnt1 + 1));
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_0));
|
||||
}
|
||||
return MFS_REPAIR_WARNING;
|
||||
|
||||
case PAIR(MFS_BANK_OK, MFS_BANK_GARBAGE):
|
||||
/* Bank zero is normal, bank one is unreadable.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_0));
|
||||
return MFS_REPAIR_WARNING;
|
||||
|
||||
case PAIR(MFS_BANK_PARTIAL, MFS_BANK_ERASED):
|
||||
/* Bank zero has problems, bank one is erased.*/
|
||||
RET_ON_ERROR(mfs_copy_bank(MFS_BANK_0, MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_validate(MFS_BANK_1, cnt0 + 1));
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_1));
|
||||
return MFS_REPAIR_WARNING;
|
||||
|
||||
case PAIR(MFS_BANK_PARTIAL, MFS_BANK_OK):
|
||||
/* Bank zero has problems, bank one is normal.*/
|
||||
if (cnt1 > cnt0) {
|
||||
/* Normal bank one is more recent than the partial bank zero, the
|
||||
partial bank has to be erased.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_1));
|
||||
}
|
||||
else {
|
||||
/* Partial bank zero is more recent than the normal bank one.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_copy_bank(MFS_BANK_0, MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_validate(MFS_BANK_1, cnt0 + 1));
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_1));
|
||||
}
|
||||
return MFS_REPAIR_WARNING;
|
||||
|
||||
case PAIR(MFS_BANK_PARTIAL, MFS_BANK_PARTIAL):
|
||||
/* Both banks have problems.*/
|
||||
if (cnt0 > cnt1) {
|
||||
/* Bank zero is newer, copying in bank one and using it.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_copy_bank(MFS_BANK_0, MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_validate(MFS_BANK_1, cnt0 + 1));
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_1));
|
||||
}
|
||||
else {
|
||||
/* Bank one is newer, copying in bank zero and using it.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_copy_bank(MFS_BANK_1, MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_validate(MFS_BANK_0, cnt1 + 1));
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_0));
|
||||
}
|
||||
return MFS_REPAIR_WARNING;
|
||||
|
||||
case PAIR(MFS_BANK_PARTIAL, MFS_BANK_GARBAGE):
|
||||
/* Bank zero has problems, bank one is unreadable.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_copy_bank(MFS_BANK_0, MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_validate(MFS_BANK_1, cnt0 + 1));
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_1));
|
||||
return MFS_REPAIR_WARNING;
|
||||
|
||||
case PAIR(MFS_BANK_GARBAGE, MFS_BANK_ERASED):
|
||||
/* Bank zero is unreadable, bank one is erased.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_validate(MFS_BANK_0, 1));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_0));
|
||||
return MFS_REPAIR_WARNING;
|
||||
|
||||
case PAIR(MFS_BANK_GARBAGE, MFS_BANK_OK):
|
||||
/* Bank zero is unreadable, bank one is normal.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_1));
|
||||
return MFS_REPAIR_WARNING;
|
||||
|
||||
case PAIR(MFS_BANK_GARBAGE, MFS_BANK_PARTIAL):
|
||||
/* Bank zero is unreadable, bank one has problems.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_copy_bank(MFS_BANK_1, MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_validate(MFS_BANK_0, cnt0 + 1));
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_0));
|
||||
return MFS_REPAIR_WARNING;
|
||||
|
||||
case PAIR(MFS_BANK_GARBAGE, MFS_BANK_GARBAGE):
|
||||
/* Both banks are unreadable, reinitializing.*/
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_0));
|
||||
RET_ON_ERROR(mfs_bank_erase(MFS_BANK_1));
|
||||
RET_ON_ERROR(mfs_validate(MFS_BANK_0, 1));
|
||||
RET_ON_ERROR(mfs_mount(MFS_BANK_0));
|
||||
return MFS_REPAIR_WARNING;
|
||||
|
||||
default:
|
||||
osalSysHalt("internal error");
|
||||
}
|
||||
|
||||
/* Never reached.*/
|
||||
return MFS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
@ -110,7 +339,8 @@ void mfsStart(MFSDriver *devp, const MFSConfig *config) {
|
|||
|
||||
if (devp->state == MFS_STOP) {
|
||||
|
||||
devp->state = MFS_READY;
|
||||
devp->config = config;
|
||||
devp->state = MFS_READY;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,14 +368,28 @@ void mfsStop(MFSDriver *devp) {
|
|||
* performs the required initialization or repair operations.
|
||||
*
|
||||
* @param[in] devp pointer to the @p MFSDriver object
|
||||
* @return The operation status.
|
||||
* @retval MFS_NO_ERROR if the operation has been successfully completed.
|
||||
* @retval MFS_REPAIR_WARNING if the operation has been completed but a
|
||||
* repair has been performed.
|
||||
* @retval MFS_FLASH_FAILURE if the flash memory is unusable because HW
|
||||
* failures.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
mfs_error_t mfsMount(MFSDriver *devp) {
|
||||
unsigned i;
|
||||
|
||||
(void)devp;
|
||||
/* Attempting to mount the managed partition.*/
|
||||
for (i = 0; i < MFS_MAX_REPAIR_ATTEMPTS; i++) {
|
||||
mfs_error_t err;
|
||||
|
||||
return MFS_NOERROR;
|
||||
err = mfs_try_mount(devp);
|
||||
if (!MFS_IS_ERROR(err))
|
||||
return err;
|
||||
}
|
||||
|
||||
return MFS_FLASH_FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -155,7 +399,7 @@ mfs_error_t mfsUnmount(MFSDriver *devp) {
|
|||
|
||||
(void)devp;
|
||||
|
||||
return MFS_NOERROR;
|
||||
return MFS_NO_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,7 +421,7 @@ mfs_error_t mfsReadRecord(MFSDriver *devp, uint32_t id,
|
|||
(void)np;
|
||||
(void)buffer;
|
||||
|
||||
return MFS_NOERROR;
|
||||
return MFS_NO_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -198,7 +442,7 @@ mfs_error_t mfsUpdateRecord(MFSDriver *devp, uint32_t id,
|
|||
(void)n;
|
||||
(void)buffer;
|
||||
|
||||
return MFS_NOERROR;
|
||||
return MFS_NO_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -214,7 +458,7 @@ mfs_error_t mfsEraseRecord(MFSDriver *devp, uint32_t id) {
|
|||
(void)devp;
|
||||
(void)id;
|
||||
|
||||
return MFS_NOERROR;
|
||||
return MFS_NO_ERROR;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -35,7 +35,8 @@
|
|||
|
||||
#define MFS_BANK_MAGIC_0 0xEC705ADEU
|
||||
#define MFS_BANK_MAGIC_1 0xF0339CC5U
|
||||
#define MFS_RECORD_MAGIC 0x5FAEU
|
||||
#define MFS_HEADER_MAGIC 0x5FAEU
|
||||
#define MFS_FOOTER_MAGIC 0xEAF5U
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
|
@ -53,6 +54,13 @@
|
|||
#if !defined(MFS_CFG_ID_CACHE_SIZE) || defined(__DOXIGEN__)
|
||||
#define MFS_CFG_ID_CACHE_SIZE 16
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum number of repair attempts on partition mount.
|
||||
*/
|
||||
#if !defined(MFS_MAX_REPAIR_ATTEMPTS) || defined(__DOXIGEN__)
|
||||
#define MFS_MAX_REPAIR_ATTEMPTS 3
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
|
@ -63,6 +71,10 @@
|
|||
#error "invalid MFS_CFG_ID_CACHE_SIZE value"
|
||||
#endif
|
||||
|
||||
#if (MFS_MAX_REPAIR_ATTEMPTS < 1) || (MFS_MAX_REPAIR_ATTEMPTS > 10)
|
||||
#error "invalid MFS_MAX_REPAIR_ATTEMPTS value"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
@ -92,21 +104,22 @@ typedef enum {
|
|||
* integers.
|
||||
*/
|
||||
typedef enum {
|
||||
MFS_NOERROR = 0,
|
||||
MFS_NO_ERROR = 0,
|
||||
MFS_REPAIR_WARNING = 1,
|
||||
MFS_GC_WARNING = 2,
|
||||
MFS_ID_NOT_FOUND = -1,
|
||||
MFS_CRC_ERROR = -2,
|
||||
MFS_FLASH_FAILURE = -3
|
||||
MFS_FLASH_FAILURE = -3,
|
||||
MFS_INTERNAL_ERROR = -4
|
||||
} mfs_error_t;
|
||||
|
||||
/**
|
||||
* @brief Type of a bank state assessment.
|
||||
*/
|
||||
typedef enum {
|
||||
MFS_BANK_OK = 0,
|
||||
MFS_BANK_ERASED = 1,
|
||||
MFS_BANK_PROBLEMS = 2,
|
||||
MFS_BANK_ERASED = 0,
|
||||
MFS_BANK_OK = 1,
|
||||
MFS_BANK_PARTIAL = 2,
|
||||
MFS_BANK_GARBAGE = 3
|
||||
} mfs_bank_state_t;
|
||||
|
||||
|
@ -138,30 +151,42 @@ typedef struct {
|
|||
|
||||
/**
|
||||
* @brief Type of a data block header.
|
||||
* @details This structure is placed before each written data block.
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/**
|
||||
* @brief Data header magic.
|
||||
*/
|
||||
uint16_t magic;
|
||||
/**
|
||||
* @brief Data CRC.
|
||||
*/
|
||||
uint16_t crc;
|
||||
/**
|
||||
* @brief Data identifier.
|
||||
*/
|
||||
uint32_t id;
|
||||
/**
|
||||
* @brief Data size.
|
||||
*/
|
||||
uint32_t size;
|
||||
} fields;
|
||||
uint8_t h8[16];
|
||||
uint32_t h32[4];
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Data header magic.
|
||||
*/
|
||||
uint16_t magic;
|
||||
/**
|
||||
* @brief Data identifier.
|
||||
*/
|
||||
uint32_t id;
|
||||
/**
|
||||
* @brief Data size for forward scan.
|
||||
*/
|
||||
uint32_t size;
|
||||
} mfs_data_header_t;
|
||||
|
||||
/**
|
||||
* @brief Type of a data block footer.
|
||||
* @details This structure is placed after each written data block.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Data size for backward scan.
|
||||
*/
|
||||
uint32_t size;
|
||||
/**
|
||||
* @brief Data CRC.
|
||||
*/
|
||||
uint16_t crc;
|
||||
/**
|
||||
* @brief Data footer magic.
|
||||
*/
|
||||
uint16_t magic;
|
||||
} mfs_data_footer_t;
|
||||
|
||||
#if (MFS_CFG_ID_CACHE_SIZE > 0) || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Type of an element of the record identifiers cache.
|
||||
|
@ -276,6 +301,14 @@ typedef struct {
|
|||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Error codes handling macros
|
||||
* @{
|
||||
*/
|
||||
#define MFS_IS_ERROR(err) ((err) < MFS_NO_ERROR)
|
||||
#define MFS_IS_WARNING(err) ((err) > MFS_NO_ERROR)
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
|
Loading…
Reference in New Issue