From e5049e103d5cf64ca047cb33f5ff3a9f16139dec Mon Sep 17 00:00:00 2001 From: Giovanni Di Sirio Date: Sun, 31 Mar 2019 14:30:25 +0000 Subject: Updates from trunk. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/stable_19.1.x@12719 27425a3e-05d8-49a3-a47f-9c15f0e5edd8 --- os/hal/lib/complex/mfs/hal_mfs.c | 970 ++++++++++++++------- os/hal/lib/complex/mfs/hal_mfs.h | 100 ++- readme.txt | 2 + test/lib/ch_test.c | 17 + test/lib/ch_test.h | 7 + test/mfs/configuration.xml | 746 ++++++++++++++-- test/mfs/mfs_test.mk | 3 +- test/mfs/source/test/mfs_test_root.c | 50 ++ test/mfs/source/test/mfs_test_root.h | 5 + test/mfs/source/test/mfs_test_sequence_001.c | 156 ++-- test/mfs/source/test/mfs_test_sequence_002.c | 506 ++++++++++- test/mfs/source/test/mfs_test_sequence_003.c | 186 ++++ test/mfs/source/test/mfs_test_sequence_003.h | 27 + .../processors/unittest/test/test_sequence.c.ftl | 1 + 14 files changed, 2197 insertions(+), 579 deletions(-) create mode 100644 test/mfs/source/test/mfs_test_sequence_003.c create mode 100644 test/mfs/source/test/mfs_test_sequence_003.h diff --git a/os/hal/lib/complex/mfs/hal_mfs.c b/os/hal/lib/complex/mfs/hal_mfs.c index eadd3c9b4..2687fa8f8 100644 --- a/os/hal/lib/complex/mfs/hal_mfs.c +++ b/os/hal/lib/complex/mfs/hal_mfs.c @@ -44,6 +44,27 @@ /* Driver local definitions. */ /*===========================================================================*/ +/** + * @brief Data record size aligned. + */ +#define ALIGNED_REC_SIZE(n) \ + (flash_offset_t)MFS_ALIGN_NEXT(sizeof (mfs_data_header_t) + (size_t)(n)) + +/** + * @brief Data record header size aligned. + */ +#define ALIGNED_DHDR_SIZE \ + ALIGNED_REC_SIZE(0) + +/** + * @brief Aligned size of a type. + */ +#define ALIGNED_SIZEOF(t) \ + (((sizeof (t) - 1U) | MFS_ALIGN_MASK) + 1U) + +/** + * @brief Combines two values (0..3) in one (0..15). + */ #define PAIR(a, b) (((unsigned)(a) << 2U) | (unsigned)(b)) /** @@ -190,7 +211,9 @@ static mfs_error_t mfs_flash_write(MFSDriver *mfsp, /* Verifying the written data by reading it back and comparing.*/ while (n > 0U) { size_t chunk = n <= MFS_CFG_BUFFER_SIZE ? n : MFS_CFG_BUFFER_SIZE; + RET_ON_ERROR(mfs_flash_read(mfsp, offset, chunk, mfsp->buffer.data8)); + if (memcmp((void *)mfsp->buffer.data8, (void *)wp, chunk)) { mfsp->state = MFS_ERROR; return MFS_ERR_FLASH_FAILURE; @@ -244,52 +267,6 @@ static mfs_error_t mfs_flash_copy(MFSDriver *mfsp, return MFS_NO_ERROR; } -/** - * @brief Verifies integrity of a record. - * - * @param[in] mfsp pointer to the @p MFSDriver object - * @param[in] dhdrp pointer to the header to be checked - * @param[in] offset flash offset of the header to be checked - * @param[in] limit flash limit offset - * @param[out] sts assessed record state - * @return The operation status. - * - * @notapi - */ -static mfs_error_t mfs_record_check(MFSDriver *mfsp, - mfs_data_header_t *dhdrp, - flash_offset_t offset, - flash_offset_t limit, - mfs_record_state_t *sts) { - unsigned i; - - for (i = 0; i < 3; i++) { - if (dhdrp->hdr32[i] != mfsp->config->erased) { - /* Not erased must verify the header.*/ - if ((dhdrp->fields.magic != MFS_HEADER_MAGIC) || - (dhdrp->fields.id < (uint16_t)1) || - (dhdrp->fields.id > (uint16_t)MFS_CFG_MAX_RECORDS) || - (dhdrp->fields.size + sizeof (mfs_data_header_t) > limit - offset)) { - *sts = MFS_RECORD_GARBAGE; - return MFS_NO_ERROR; - } -#if MFS_CFG_STRONG_CHECKING == TRUE - { - /* TODO: Checking the CRC while reading the record data.*/ -/* *sts = MFS_RECORD_CRC; - return MFS_NO_ERROR;*/ - } -#endif - *sts = MFS_RECORD_OK; - return MFS_NO_ERROR; - } - } - - /* It is fully erased.*/ - *sts = MFS_RECORD_ERASED; - return MFS_NO_ERROR; -} - /** * @brief Erases and verifies all sectors belonging to a bank. * @@ -382,7 +359,6 @@ static mfs_error_t mfs_bank_verify_erase(MFSDriver *mfsp, mfs_bank_t bank) { * @param[in] bank bank to be validated * @param[in] cnt value for the flash usage counter * @return The operation status. - * @retval MFS_NO_ERROR if the operation has been successfully completed. * * @notapi */ @@ -412,16 +388,50 @@ static mfs_error_t mfs_bank_write_header(MFSDriver *mfsp, bhdr.hdr8); } +/** + * @brief Checks integrity of the header in the shared buffer. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] bank bank identifier + * @return The header state. + * + * @notapi + */ +static mfs_bank_state_t mfs_bank_check_header(MFSDriver *mfsp) { + uint16_t crc; + + if ((mfsp->buffer.bhdr.hdr32[0] == mfsp->config->erased) && + (mfsp->buffer.bhdr.hdr32[1] == mfsp->config->erased) && + (mfsp->buffer.bhdr.hdr32[2] == mfsp->config->erased) && + (mfsp->buffer.bhdr.hdr32[3] == mfsp->config->erased)) { + return MFS_BANK_ERASED; + } + + /* Checking header fields integrity.*/ + if ((mfsp->buffer.bhdr.fields.magic1 != MFS_BANK_MAGIC_1) || + (mfsp->buffer.bhdr.fields.magic2 != MFS_BANK_MAGIC_2) || + (mfsp->buffer.bhdr.fields.counter == mfsp->config->erased) || + (mfsp->buffer.bhdr.fields.reserved1 != (uint16_t)mfsp->config->erased)) { + return MFS_BANK_GARBAGE; + } + + /* Verifying header CRC.*/ + crc = crc16(0xFFFFU, mfsp->buffer.bhdr.hdr8, + sizeof (mfs_bank_header_t) - sizeof (uint16_t)); + if (crc != mfsp->buffer.bhdr.fields.crc) { + return MFS_BANK_GARBAGE; + } + + return MFS_BANK_OK; +} + /** * @brief Scans blocks searching for records. * @note The block integrity is strongly checked. * * @param[in] mfsp pointer to the @p MFSDriver object * @param[in] bank the bank identifier - * @param[out] statep bank state, it can be: - * - MFS_BANK_PARTIAL - * - MFS_BANK_OK - * . + * @param[out] wflagp warning flag on anomalies * * @return The operation status. * @@ -429,75 +439,93 @@ static mfs_error_t mfs_bank_write_header(MFSDriver *mfsp, */ static mfs_error_t mfs_bank_scan_records(MFSDriver *mfsp, mfs_bank_t bank, - mfs_bank_state_t *statep) { + bool *wflagp) { flash_offset_t hdr_offset, start_offset, end_offset; - mfs_record_state_t sts; - bool warning = false; + /* No warning by default.*/ + *wflagp = false; + + /* Boundaries.*/ start_offset = mfs_flash_get_bank_offset(mfsp, bank); + hdr_offset = start_offset + (flash_offset_t)ALIGNED_SIZEOF(mfs_bank_header_t); end_offset = start_offset + mfsp->config->bank_size; - /* Scanning records.*/ - hdr_offset = start_offset + (flash_offset_t)sizeof(mfs_bank_header_t); - while (hdr_offset < end_offset) { - uint32_t size; + /* Scanning records until there is there is not enough space left for an + header.*/ + while (hdr_offset < end_offset - ALIGNED_DHDR_SIZE) { + union { + mfs_data_header_t dhdr; + uint8_t data8[ALIGNED_SIZEOF(mfs_data_header_t)]; + } u; + uint16_t crc; /* Reading the current record header.*/ RET_ON_ERROR(mfs_flash_read(mfsp, hdr_offset, sizeof (mfs_data_header_t), - (void *)&mfsp->buffer.dhdr)); + u.data8)); - /* Checking header/data integrity.*/ - RET_ON_ERROR(mfs_record_check(mfsp, &mfsp->buffer.dhdr, - hdr_offset, end_offset, &sts)); - if (sts == MFS_RECORD_ERASED) { - /* Record area fully erased, stopping scan.*/ + /* Checking if the found header is in erased state.*/ + if ((u.dhdr.hdr32[0] == mfsp->config->erased) && + (u.dhdr.hdr32[1] == mfsp->config->erased) && + (u.dhdr.hdr32[2] == mfsp->config->erased)) { break; } - else if (sts == MFS_RECORD_OK) { - /* Record OK.*/ - size = mfsp->buffer.dhdr.fields.size; - /* Zero-sized records are erase markers.*/ - if (size == 0U) { - mfsp->descriptors[mfsp->buffer.dhdr.fields.id - 1U].offset = 0U; - mfsp->descriptors[mfsp->buffer.dhdr.fields.id - 1U].size = 0U; - } - else { - mfsp->descriptors[mfsp->buffer.dhdr.fields.id - 1U].offset = hdr_offset; - mfsp->descriptors[mfsp->buffer.dhdr.fields.id - 1U].size = size; + /* It is not erased so checking for integrity.*/ + if ((u.dhdr.fields.magic != MFS_HEADER_MAGIC) || + (u.dhdr.fields.id < 1U) || + (u.dhdr.fields.id > (uint32_t)MFS_CFG_MAX_RECORDS) || + (u.dhdr.fields.size > end_offset - hdr_offset)) { + *wflagp = true; + break; + } + + /* Finally checking the CRC, we need to perform it in chunks because + we have a limited buffer.*/ + crc = 0xFFFFU; + if (u.dhdr.fields.size > 0U) { + flash_offset_t data = hdr_offset + sizeof (mfs_data_header_t); + uint32_t total = u.dhdr.fields.size; + + while (total > 0U) { + uint32_t chunk = total > MFS_CFG_BUFFER_SIZE ? MFS_CFG_BUFFER_SIZE : + total; + + /* Reading the data chunk.*/ + RET_ON_ERROR(mfs_flash_read(mfsp, data, chunk, mfsp->buffer.data8)); + + /* CRC on the read data chunk.*/ + crc = crc16(crc, &mfsp->buffer.data8[0], chunk); + + /* Next chunk.*/ + data += chunk; + total -= chunk; } } - else if (sts == MFS_RECORD_CRC) { - /* Record payload corrupted, scan can continue because the header - is OK.*/ - size = mfsp->buffer.dhdr.fields.size; - warning = true; + if (crc != u.dhdr.fields.crc) { + /* If the CRC is invalid then this record is ignored but scanning + continues because there could be more valid records afterward.*/ + *wflagp = true; } else { - /* Unrecognized header, scanning cannot continue.*/ - warning = true; - break; + /* Zero-sized records are erase markers.*/ + if (u.dhdr.fields.size == 0U) { + mfsp->descriptors[u.dhdr.fields.id - 1U].offset = 0U; + mfsp->descriptors[u.dhdr.fields.id - 1U].size = 0U; + } + else { + mfsp->descriptors[u.dhdr.fields.id - 1U].offset = hdr_offset; + mfsp->descriptors[u.dhdr.fields.id - 1U].size = u.dhdr.fields.size; + } } - hdr_offset = hdr_offset + - (flash_offset_t)sizeof(mfs_data_header_t) + - (flash_offset_t)size; - } - if (hdr_offset > end_offset) { - return MFS_ERR_INTERNAL; + /* On the next header.*/ + hdr_offset = hdr_offset + ALIGNED_REC_SIZE(u.dhdr.fields.size); } - /* Final.*/ + /* Next writable offset.*/ mfsp->next_offset = hdr_offset; - if (warning) { - *statep = MFS_BANK_PARTIAL; - } - else { - *statep = MFS_BANK_OK; - } - return MFS_NO_ERROR; } @@ -521,89 +549,26 @@ static mfs_error_t mfs_bank_scan_records(MFSDriver *mfsp, static mfs_error_t mfs_bank_get_state(MFSDriver *mfsp, mfs_bank_t bank, mfs_bank_state_t *statep, - uint32_t * cntp) { - unsigned i; - mfs_error_t err; - uint16_t crc; - - /* Worst case is default.*/ - *statep = MFS_BANK_GARBAGE; - *cntp = 0U; + uint32_t *cntp) { /* Reading the current bank header.*/ RET_ON_ERROR(mfs_flash_read(mfsp, mfs_flash_get_bank_offset(mfsp, bank), sizeof (mfs_bank_header_t), - (void *)&mfsp->buffer.bhdr)); - - /* Checking the special case where the header is erased.*/ - for (i = 0; i < 4; i++) { - if (mfsp->buffer.bhdr.hdr32[i] != mfsp->config->erased) { - - /* Checking header fields integrity.*/ - if ((mfsp->buffer.bhdr.fields.magic1 != MFS_BANK_MAGIC_1) || - (mfsp->buffer.bhdr.fields.magic2 != MFS_BANK_MAGIC_2) || - (mfsp->buffer.bhdr.fields.counter == mfsp->config->erased) || - (mfsp->buffer.bhdr.fields.reserved1 != (uint16_t)mfsp->config->erased)) { - return MFS_NO_ERROR; - } - - /* Verifying header CRC.*/ - crc = crc16(0xFFFFU, mfsp->buffer.bhdr.hdr8, - sizeof (mfs_bank_header_t) - sizeof (uint16_t)); - if (crc != mfsp->buffer.bhdr.fields.crc) { - return MFS_NO_ERROR; - } - - *statep = MFS_BANK_OK; - *cntp = mfsp->buffer.bhdr.fields.counter; - - return MFS_NO_ERROR; - } - } - - /* If the header is erased then it could be the whole block erased.*/ - err = mfs_bank_verify_erase(mfsp, bank); - if (err == MFS_NO_ERROR) { - *statep = MFS_BANK_ERASED; - } - - return err; -} - -/** - * @brief Selects a bank as current. - * @note The bank header is assumed to be valid. - * - * @param[in] mfsp pointer to the @p MFSDriver object - * @param[in] bank bank to be scanned - * @param[out] statep bank state, it can be: - * - MFS_BANK_ERASED - * - MFS_BANK_GARBAGE - * - MFS_BANK_PARTIAL - * - MFS_BANK_OK - * . - * @return The operation status. - * - * @notapi - */ -static mfs_error_t mfs_bank_mount(MFSDriver *mfsp, - mfs_bank_t bank, - mfs_bank_state_t *statep) { - unsigned i; + mfsp->buffer.data8)); - /* Resetting the bank state, then reading the required header data.*/ - mfs_state_reset(mfsp); - RET_ON_ERROR(mfs_bank_get_state(mfsp, bank, statep, &mfsp->current_counter)); - mfsp->current_bank = bank; + /* Getting the counter regardless of the bank state, it is only valid if + the state is MFS_BANK_OK.*/ + *cntp = mfsp->buffer.bhdr.fields.counter; - /* Scanning for the most recent instance of all records.*/ - RET_ON_ERROR(mfs_bank_scan_records(mfsp, bank, statep)); + /* Checking just the header.*/ + *statep = mfs_bank_check_header(mfsp); + if (*statep == MFS_BANK_ERASED) { + mfs_error_t err; - /* Calculating the effective used size.*/ - mfsp->used_space = sizeof (mfs_bank_header_t); - for (i = 0; i < MFS_CFG_MAX_RECORDS; i++) { - if (mfsp->descriptors[i].offset != 0U) { - mfsp->used_space += mfsp->descriptors[i].size + sizeof (mfs_data_header_t); + /* Checking if the bank is really all erased.*/ + err = mfs_bank_verify_erase(mfsp, bank); + if (err == MFS_ERR_NOT_ERASED) { + *statep = MFS_BANK_GARBAGE; } } @@ -634,11 +599,11 @@ static mfs_error_t mfs_garbage_collect(MFSDriver *mfsp) { /* Write address.*/ dest_offset = mfs_flash_get_bank_offset(mfsp, dbank) + - sizeof (mfs_bank_header_t); + ALIGNED_SIZEOF(mfs_bank_header_t); /* Copying the most recent record instances only.*/ for (i = 0; i < MFS_CFG_MAX_RECORDS; i++) { - uint32_t totsize = mfsp->descriptors[i].size + sizeof (mfs_data_header_t); + uint32_t totsize = ALIGNED_REC_SIZE(mfsp->descriptors[i].size); if (mfsp->descriptors[i].offset != 0) { RET_ON_ERROR(mfs_flash_copy(mfsp, dest_offset, mfsp->descriptors[i].offset, @@ -671,10 +636,13 @@ static mfs_error_t mfs_garbage_collect(MFSDriver *mfsp) { * @api */ static mfs_error_t mfs_try_mount(MFSDriver *mfsp) { - mfs_bank_state_t sts, sts0, sts1; + mfs_bank_state_t sts0, sts1; mfs_bank_t bank; uint32_t cnt0 = 0, cnt1 = 0; - bool warning = false; + bool w1 = false, w2 = false; + + /* Resetting the bank state.*/ + mfs_state_reset(mfsp); /* Assessing the state of the two banks.*/ RET_ON_ERROR(mfs_bank_get_state(mfsp, MFS_BANK_0, &sts0, &cnt0)); @@ -703,7 +671,7 @@ static mfs_error_t mfs_try_mount(MFSDriver *mfsp) { RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_0)); bank = MFS_BANK_1; } - warning = true; + w1 = true; break; case PAIR(MFS_BANK_GARBAGE, MFS_BANK_GARBAGE): @@ -712,7 +680,7 @@ static mfs_error_t mfs_try_mount(MFSDriver *mfsp) { RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_1)); RET_ON_ERROR(mfs_bank_write_header(mfsp, MFS_BANK_0, 1)); bank = MFS_BANK_0; - warning = true; + w1 = true; break; case PAIR(MFS_BANK_ERASED, MFS_BANK_OK): @@ -730,7 +698,7 @@ static mfs_error_t mfs_try_mount(MFSDriver *mfsp) { RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_1)); RET_ON_ERROR(mfs_bank_write_header(mfsp, MFS_BANK_0, 1)); bank = MFS_BANK_0; - warning = true; + w1 = true; break; case PAIR(MFS_BANK_GARBAGE, MFS_BANK_ERASED): @@ -738,21 +706,21 @@ static mfs_error_t mfs_try_mount(MFSDriver *mfsp) { RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_0)); RET_ON_ERROR(mfs_bank_write_header(mfsp, MFS_BANK_1, 1)); bank = MFS_BANK_1; - warning = true; + w1 = true; break; case PAIR(MFS_BANK_OK, MFS_BANK_GARBAGE): /* Bank zero is normal, bank one is unreadable.*/ RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_1)); bank = MFS_BANK_0; - warning = true; + w1 = true; break; case PAIR(MFS_BANK_GARBAGE, MFS_BANK_OK): /* Bank zero is unreadable, bank one is normal.*/ RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_0)); bank = MFS_BANK_1; - warning = true; + w1 = true; break; default: @@ -760,21 +728,42 @@ static mfs_error_t mfs_try_mount(MFSDriver *mfsp) { } /* Mounting the bank.*/ - RET_ON_ERROR(mfs_bank_mount(mfsp, bank, &sts)); + { + unsigned i; - /* This condition should not occur, the bank has just been repaired.*/ - if ((sts == MFS_BANK_ERASED) || (sts == MFS_BANK_GARBAGE)) { - return MFS_ERR_INTERNAL; + /* Reading the bank header again.*/ + RET_ON_ERROR(mfs_flash_read(mfsp, mfs_flash_get_bank_offset(mfsp, bank), + sizeof (mfs_bank_header_t), + mfsp->buffer.data8)); + + /* Checked again for extra safety.*/ + if (mfs_bank_check_header(mfsp) != MFS_BANK_OK) { + return MFS_ERR_INTERNAL; + } + + /* Storing the bank data.*/ + mfsp->current_bank = bank; + mfsp->current_counter = mfsp->buffer.bhdr.fields.counter; + + /* Scanning for the most recent instance of all records.*/ + RET_ON_ERROR(mfs_bank_scan_records(mfsp, bank, &w2)); + + /* Calculating the effective used size.*/ + mfsp->used_space = ALIGNED_SIZEOF(mfs_bank_header_t); + for (i = 0; i < MFS_CFG_MAX_RECORDS; i++) { + if (mfsp->descriptors[i].offset != 0U) { + mfsp->used_space += ALIGNED_REC_SIZE(mfsp->descriptors[i].size); + } + } } /* In case of detected problems then a garbage collection is performed in order to repair/remove anomalies.*/ - if (sts == MFS_BANK_PARTIAL) { + if (w2) { RET_ON_ERROR(mfs_garbage_collect(mfsp)); - warning = true; } - return warning ? MFS_WARN_REPAIR : MFS_NO_ERROR; + return (w1 || w2) ? MFS_WARN_REPAIR : MFS_NO_ERROR; } /** @@ -782,11 +771,14 @@ static mfs_error_t mfs_try_mount(MFSDriver *mfsp) { * * @param[in] mfsp pointer to the @p MFSDriver object * @return The operation status. - * @retval MFS_NO_ERROR if the operation has been successfully completed. - * @retval MFS_WARN_GC if the operation triggered a garbage collection. - * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW - * failures. Makes the driver enter the @p MFS_ERROR state. - * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_WARN_GC if the operation triggered a garbage + * collection. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. * * @api */ @@ -843,11 +835,14 @@ void mfsObjectInit(MFSDriver *mfsp) { * @param[in] mfsp pointer to the @p MFSDriver object * @param[in] config pointer to the configuration * @return The operation status. - * @retval MFS_NO_ERROR if the operation has been successfully completed. - * @retval MFS_WARN_GC if the operation triggered a garbage collection. - * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW - * failures. Makes the driver enter the @p MFS_ERROR state. - * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * @retval MFS_NO_ERROR if the operation has been + * completed. + * @retval MFS_WARN_GC if the operation triggered a garbage + * collection. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. * * @api */ @@ -885,11 +880,14 @@ void mfsStop(MFSDriver *mfsp) { * * @param[in] mfsp pointer to the @p MFSDriver object * @return The operation status. - * @retval MFS_ERR_INV_STATE if the driver is in not in @p MSG_READY state. - * @retval MFS_NO_ERROR if the operation has been successfully completed. - * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW - * failures. Makes the driver enter the @p MFS_ERROR state. - * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY + * state. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. * * @api */ @@ -917,14 +915,17 @@ mfs_error_t mfsErase(MFSDriver *mfsp) { * the size of the data copied into the buffer * @param[out] buffer pointer to a buffer for record data * @return The operation status. - * @retval MFS_NO_ERROR if the operation has been successfully completed. - * @retval MFS_ERR_INV_STATE if the driver is in not in @p MSG_READY state. - * @retval MFS_ERR_INV_SIZE if the passed buffer is not large enough to - * contain the record data. - * @retval MFS_ERR_NOT_FOUND if the specified id does not exists. - * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW - * failures. Makes the driver enter the @p MFS_ERROR state. - * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY + * state. + * @retval MFS_ERR_INV_SIZE if the passed buffer is not large enough to + * contain the record data. + * @retval MFS_ERR_NOT_FOUND if the specified id does not exists. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. * * @api */ @@ -934,9 +935,9 @@ mfs_error_t mfsReadRecord(MFSDriver *mfsp, mfs_id_t id, osalDbgCheck((mfsp != NULL) && (id >= 1U) && (id <= (mfs_id_t)MFS_CFG_MAX_RECORDS) && - (np != NULL) && (buffer != NULL)); + (np != NULL) && (*np > 0U) && (buffer != NULL)); - if (mfsp->state != MFS_READY) { + if ((mfsp->state != MFS_READY) && (mfsp->state != MFS_TRANSACTION)) { return MFS_ERR_INV_STATE; } @@ -982,87 +983,147 @@ mfs_error_t mfsReadRecord(MFSDriver *mfsp, mfs_id_t id, * @param[in] n size of data to be written, it cannot be zero * @param[in] buffer pointer to a buffer for record data * @return The operation status. - * @retval MFS_NO_ERROR if the operation has been successfully completed. - * @retval MFS_WARN_GC if the operation triggered a garbage collection. - * @retval MFS_ERR_INV_STATE if the driver is in not in @p MSG_READY state. - * @retval MFS_ERR_OUT_OF_MEM if there is not enough flash space for the - * operation. - * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW - * failures. Makes the driver enter the @p MFS_ERROR state. - * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_WARN_GC if the operation triggered a garbage + * collection. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY + * state. + * @retval MFS_ERR_OUT_OF_MEM if there is not enough flash space for the + * operation. + * @retval MFS_ERR_TRANSACTION_NUM if the transaction operations buffer space + * has been exceeded. + * @retval MFS_ERR_TRANSACTION_SIZE if the transaction allocated space + * has been exceeded. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. * * @api */ mfs_error_t mfsWriteRecord(MFSDriver *mfsp, mfs_id_t id, size_t n, const uint8_t *buffer) { - flash_offset_t free, required; - bool warning = false; + flash_offset_t free, asize, rspace; osalDbgCheck((mfsp != NULL) && (id >= 1U) && (id <= (mfs_id_t)MFS_CFG_MAX_RECORDS) && (n > 0U) && (buffer != NULL)); - if (mfsp->state != MFS_READY) { - return MFS_ERR_INV_STATE; - } + /* Aligned record size.*/ + asize = ALIGNED_REC_SIZE(n); - /* If the required space is beyond the available (compacted) block - size then an error is returned. - NOTE: The space for one extra header is reserved in order to allow - for an erase operation after the space has been fully allocated.*/ - required = ((flash_offset_t)sizeof (mfs_data_header_t) * 2U) + - (flash_offset_t)n; - if (required > mfsp->config->bank_size - mfsp->used_space) { - return MFS_ERR_OUT_OF_MEM; - } + /* Normal mode code path.*/ + if (mfsp->state == MFS_READY) { + bool warning = false; - /* Checking for immediately (not compacted) available space.*/ - free = (mfs_flash_get_bank_offset(mfsp, mfsp->current_bank) + - mfsp->config->bank_size) - mfsp->next_offset; - if (required > free) { - /* We need to perform a garbage collection, there is enough space - but it has to be freed.*/ - warning = true; - RET_ON_ERROR(mfs_garbage_collect(mfsp)); - } + /* If the required space is beyond the available (compacted) block + size then an error is returned. + NOTE: The space for one extra header is reserved in order to allow + for an erase operation after the space has been fully allocated.*/ + rspace = ALIGNED_DHDR_SIZE + asize; + if (rspace > mfsp->config->bank_size - mfsp->used_space) { + return MFS_ERR_OUT_OF_MEM; + } - /* Writing the data header without the magic, it will be written last.*/ - mfsp->buffer.dhdr.fields.magic = (uint32_t)mfsp->config->erased; - mfsp->buffer.dhdr.fields.id = (uint16_t)id; - mfsp->buffer.dhdr.fields.size = (uint32_t)n; - mfsp->buffer.dhdr.fields.crc = crc16(0xFFFFU, buffer, n); - RET_ON_ERROR(mfs_flash_write(mfsp, - mfsp->next_offset, - sizeof (mfs_data_header_t), - mfsp->buffer.data8)); - - /* Writing the data part.*/ - RET_ON_ERROR(mfs_flash_write(mfsp, - mfsp->next_offset + sizeof (mfs_data_header_t), - n, - buffer)); - - /* Finally writing the magic number, it seals the transaction.*/ - mfsp->buffer.dhdr.fields.magic = (uint32_t)MFS_HEADER_MAGIC; - RET_ON_ERROR(mfs_flash_write(mfsp, - mfsp->next_offset, - sizeof (uint32_t), - mfsp->buffer.data8)); - - /* The size of the old record instance, if present, must be subtracted - to the total used size.*/ - if (mfsp->descriptors[id - 1U].offset != 0U) { - mfsp->used_space -= sizeof (mfs_data_header_t) + - mfsp->descriptors[id - 1U].size; + /* Checking for immediately (not compacted) available space.*/ + free = (mfs_flash_get_bank_offset(mfsp, mfsp->current_bank) + + mfsp->config->bank_size) - mfsp->next_offset; + if (rspace > free) { + /* We need to perform a garbage collection, there is enough space + but it has to be freed.*/ + warning = true; + RET_ON_ERROR(mfs_garbage_collect(mfsp)); + } + + /* Writing the data header without the magic, it will be written last.*/ + mfsp->buffer.dhdr.fields.magic = (uint32_t)mfsp->config->erased; + mfsp->buffer.dhdr.fields.id = (uint16_t)id; + mfsp->buffer.dhdr.fields.size = (uint32_t)n; + mfsp->buffer.dhdr.fields.crc = crc16(0xFFFFU, buffer, n); + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->next_offset, + sizeof (mfs_data_header_t), + mfsp->buffer.data8)); + + /* Writing the data part.*/ + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->next_offset + sizeof (mfs_data_header_t), + n, + buffer)); + + /* Finally writing the magic number, it seals the operation.*/ + mfsp->buffer.dhdr.fields.magic = (uint32_t)MFS_HEADER_MAGIC; + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->next_offset, + sizeof (uint32_t), + mfsp->buffer.data8)); + + /* The size of the old record instance, if present, must be subtracted + to the total used size.*/ + if (mfsp->descriptors[id - 1U].offset != 0U) { + mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[id - 1U].size); + } + + /* Adjusting bank-related metadata.*/ + mfsp->descriptors[id - 1U].offset = mfsp->next_offset; + mfsp->descriptors[id - 1U].size = (uint32_t)n; + mfsp->next_offset += asize; + mfsp->used_space += asize; + + return warning ? MFS_WARN_GC : MFS_NO_ERROR; } - /* Adjusting bank-related metadata.*/ - mfsp->descriptors[id - 1U].offset = mfsp->next_offset; - mfsp->descriptors[id - 1U].size = (uint32_t)n; - mfsp->next_offset += sizeof (mfs_data_header_t) + n; - mfsp->used_space += sizeof (mfs_data_header_t) + n; +#if MFS_CFG_TRANSACTION_MAX > 0 + /* Transaction mode code path.*/ + if (mfsp->state == MFS_TRANSACTION) { + mfs_transaction_op_t *top; + + /* Checking if the maximum number of operations in a transaction is + Exceeded.*/ + if (mfsp->tr_nops >= MFS_CFG_TRANSACTION_MAX) { + return MFS_ERR_TRANSACTION_NUM; + } + + /* If the required space is greater than the space allocated for the + transaction then error.*/ + rspace = asize; + if (rspace > mfsp->tr_limit_offet - mfsp->tr_next_offset) { + return MFS_ERR_TRANSACTION_SIZE; + } + + /* Writing the data header without the magic, it will be written last.*/ + mfsp->buffer.dhdr.fields.magic = (uint32_t)mfsp->config->erased; + mfsp->buffer.dhdr.fields.id = (uint16_t)id; + mfsp->buffer.dhdr.fields.size = (uint32_t)n; + mfsp->buffer.dhdr.fields.crc = crc16(0xFFFFU, buffer, n); + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->tr_next_offset, + sizeof (mfs_data_header_t), + mfsp->buffer.data8)); + + /* Writing the data part.*/ + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->tr_next_offset + sizeof (mfs_data_header_t), + n, + buffer)); + + /* Adding a transaction operation record.*/ + top = &mfsp->tr_ops[mfsp->tr_nops]; + top->offset = mfsp->tr_next_offset; + top->size = n; + top->id = id; + + /* Number of records and next write position updated.*/ + mfsp->tr_nops++; + mfsp->tr_next_offset += asize; + + return MFS_NO_ERROR; + } +#endif /* MFS_CFG_TRANSACTION_MAX > 0 */ - return warning ? MFS_WARN_GC : MFS_NO_ERROR; + /* Invalid state.*/ + return MFS_ERR_INV_STATE; } /** @@ -1072,93 +1133,350 @@ mfs_error_t mfsWriteRecord(MFSDriver *mfsp, mfs_id_t id, * @param[in] id record numeric identifier, the valid range is between * @p 1 and @p MFS_CFG_MAX_RECORDS * @return The operation status. - * @retval MFS_NO_ERROR if the operation has been successfully completed. - * @retval MFS_WARN_GC if the operation triggered a garbage collection. - * @retval MFS_ERR_INV_STATE if the driver is in not in @p MSG_READY state. - * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW - * failures. Makes the driver enter the @p MFS_ERROR state. - * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_WARN_GC if the operation triggered a garbage + * collection. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY + * state. + * @retval MFS_ERR_OUT_OF_MEM if there is not enough flash space for the + * operation. + * @retval MFS_ERR_TRANSACTION_NUM if the transaction operations buffer space + * has been exceeded. + * @retval MFS_ERR_TRANSACTION_SIZE if the transaction allocated space + * has been exceeded. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. * * @api */ mfs_error_t mfsEraseRecord(MFSDriver *mfsp, mfs_id_t id) { - flash_offset_t free, required; - bool warning = false; + flash_offset_t free, asize, rspace; osalDbgCheck((mfsp != NULL) && (id >= 1U) && (id <= (mfs_id_t)MFS_CFG_MAX_RECORDS)); + /* Aligned record size.*/ + asize = ALIGNED_DHDR_SIZE; + + /* Normal mode code path.*/ + if (mfsp->state == MFS_READY) { + bool warning = false; + + /* Checking if the requested record actually exists.*/ + if (mfsp->descriptors[id - 1U].offset == 0U) { + return MFS_ERR_NOT_FOUND; + } + + /* If the required space is beyond the available (compacted) block + size then an internal error is returned, it should never happen.*/ + rspace = asize; + if (rspace > mfsp->config->bank_size - mfsp->used_space) { + return MFS_ERR_INTERNAL; + } + + /* Checking for immediately (not compacted) available space.*/ + free = (mfs_flash_get_bank_offset(mfsp, mfsp->current_bank) + + mfsp->config->bank_size) - mfsp->next_offset; + if (rspace > free) { + /* We need to perform a garbage collection, there is enough space + but it has to be freed.*/ + warning = true; + RET_ON_ERROR(mfs_garbage_collect(mfsp)); + } + + /* Writing the data header with size set to zero, it means that the + record is logically erased.*/ + mfsp->buffer.dhdr.fields.magic = (uint32_t)MFS_HEADER_MAGIC; + mfsp->buffer.dhdr.fields.id = (uint16_t)id; + mfsp->buffer.dhdr.fields.size = (uint32_t)0; + mfsp->buffer.dhdr.fields.crc = (uint16_t)0xFFFF; + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->next_offset, + sizeof (mfs_data_header_t), + mfsp->buffer.data8)); + + /* Adjusting bank-related metadata.*/ + mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[id - 1U].size); + mfsp->next_offset += sizeof (mfs_data_header_t); + mfsp->descriptors[id - 1U].offset = 0U; + mfsp->descriptors[id - 1U].size = 0U; + + return warning ? MFS_WARN_GC : MFS_NO_ERROR; + } + +#if MFS_CFG_TRANSACTION_MAX > 0 + /* Transaction mode code path.*/ + if (mfsp->state == MFS_TRANSACTION) { + mfs_transaction_op_t *top; + + /* Checking if the requested record actually exists.*/ + if (mfsp->descriptors[id - 1U].offset == 0U) { + return MFS_ERR_NOT_FOUND; + } + + /* Checking if the maximum number of operations in a transaction is + Exceeded.*/ + if (mfsp->tr_nops >= MFS_CFG_TRANSACTION_MAX) { + return MFS_ERR_TRANSACTION_NUM; + } + + /* If the required space is greater than the space allocated for the + transaction then error.*/ + rspace = asize; + if (rspace > mfsp->tr_limit_offet - mfsp->tr_next_offset) { + return MFS_ERR_TRANSACTION_SIZE; + } + + /* Writing the data header with size set to zero, it means that the + record is logically erased. Note, the magic number is not set.*/ + mfsp->buffer.dhdr.fields.magic = mfsp->config->erased; + mfsp->buffer.dhdr.fields.id = (uint16_t)id; + mfsp->buffer.dhdr.fields.size = (uint32_t)0; + mfsp->buffer.dhdr.fields.crc = (uint16_t)0xFFFF; + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->next_offset, + sizeof (mfs_data_header_t), + mfsp->buffer.data8)); + + /* Adding a transaction operation record.*/ + top = &mfsp->tr_ops[mfsp->tr_nops]; + top->offset = mfsp->tr_next_offset; + top->size = 0U; + top->id = id; + + /* Number of records and next write position updated.*/ + mfsp->tr_nops++; + mfsp->tr_next_offset += asize; + + return MFS_NO_ERROR; + } +#endif /* MFS_CFG_TRANSACTION_MAX > 0 */ + + return MFS_ERR_INV_STATE; +} + +/** + * @brief Enforces a garbage collection operation. + * @details Garbage collection involves: integrity check, optionally repairs, + * obsolete data removal, data compaction and a flash bank swap. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @return The operation status. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY + * state. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * + * @api + */ +mfs_error_t mfsPerformGarbageCollection(MFSDriver *mfsp) { + + osalDbgCheck(mfsp != NULL); + if (mfsp->state != MFS_READY) { return MFS_ERR_INV_STATE; } - /* Checking if the requested record actually exists.*/ - if (mfsp->descriptors[id - 1U].offset == 0U) { - return MFS_ERR_NOT_FOUND; + return mfs_garbage_collect(mfsp); +} + +#if (MFS_CFG_TRANSACTION_MAX > 0) || defined(__DOXYGEN__) +/** + * @brief Puts the driver in transaction mode. + * @note The parameters @p n and @p size are used to make an + * estimation of the space required for the transaction to succeed. + * Note that the estimated size must include also the extra space + * required by alignment enforcement option. If the estimated size + * is wrong (by defect) what could happen is that there is a failure + * in the middle of a transaction and a roll-back would be required. + * @note The conditions for starting a transaction are: + * - The driver must be started. + * - There must be enough compacted storage to accommodate the whole + * transaction. If the required space is available but it is not + * compacted then a garbage collect operation is performed. + * . + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] size estimated total size of written records in transaction, + * this includes, data, headers and alignment gaps + * @return The operation status. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY + * state. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * + * @api + */ +mfs_error_t mfsStartTransaction(MFSDriver *mfsp, size_t size) { + flash_offset_t free, tspace, rspace; + + osalDbgCheck((mfsp != NULL) && (size > ALIGNED_DHDR_SIZE)); + + /* The driver must be in ready mode.*/ + if (mfsp->state != MFS_READY) { + return MFS_ERR_INV_STATE; } + /* Estimating the required contiguous compacted space.*/ + tspace = (flash_offset_t)MFS_ALIGN_NEXT(size); + rspace = tspace + ALIGNED_DHDR_SIZE; + /* If the required space is beyond the available (compacted) block - size then an internal error is returned, it should never happen.*/ - required = (flash_offset_t)sizeof (mfs_data_header_t); - if (required > mfsp->config->bank_size - mfsp->used_space) { - return MFS_ERR_INTERNAL; + size then an error is returned.*/ + if (rspace > mfsp->config->bank_size - mfsp->used_space) { + return MFS_ERR_OUT_OF_MEM; } /* Checking for immediately (not compacted) available space.*/ free = (mfs_flash_get_bank_offset(mfsp, mfsp->current_bank) + mfsp->config->bank_size) - mfsp->next_offset; - if (required > free) { + if (rspace > free) { /* We need to perform a garbage collection, there is enough space but it has to be freed.*/ - warning = true; RET_ON_ERROR(mfs_garbage_collect(mfsp)); } - /* Writing the data header with size set to zero, it means that the - record is logically erased.*/ + /* Entering transaction mode.*/ + mfsp->state = MFS_TRANSACTION; + + /* Initializing transaction state.*/ + mfsp->tr_next_offset = mfsp->next_offset; + mfsp->tr_nops = 0U; + mfsp->tr_limit_offet = mfsp->tr_next_offset + tspace; + + return MFS_NO_ERROR; +} + +/** + * @brief A transaction is committed and finalized atomically. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @return The operation status. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_TRANSACTION + * state. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * + * @api + */ +mfs_error_t mfsCommitTransaction(MFSDriver *mfsp) { + mfs_transaction_op_t *top; + + osalDbgCheck(mfsp != NULL); + + /* The driver must be in transaction mode.*/ + if (mfsp->state != MFS_TRANSACTION) { + return MFS_ERR_INV_STATE; + } + + /* Scanning all buffered operations in reverse order.*/ mfsp->buffer.dhdr.fields.magic = (uint32_t)MFS_HEADER_MAGIC; - mfsp->buffer.dhdr.fields.id = (uint16_t)id; - mfsp->buffer.dhdr.fields.size = (uint32_t)0; - mfsp->buffer.dhdr.fields.crc = (uint16_t)0; - RET_ON_ERROR(mfs_flash_write(mfsp, - mfsp->next_offset, - sizeof (mfs_data_header_t), - mfsp->buffer.data8)); - - /* Adjusting bank-related metadata.*/ - mfsp->used_space -= sizeof (mfs_data_header_t) + - mfsp->descriptors[id - 1U].size; - mfsp->next_offset += sizeof (mfs_data_header_t); - mfsp->descriptors[id - 1U].offset = 0U; - mfsp->descriptors[id - 1U].size = 0U; - - return warning ? MFS_WARN_GC : MFS_NO_ERROR; + top = &mfsp->tr_ops[mfsp->tr_nops]; + while (top > &mfsp->tr_ops[0]) { + /* On the previous element.*/ + top--; + + /* Finalizing the operation by writing the magic number.*/ + RET_ON_ERROR(mfs_flash_write(mfsp, + top->offset, + sizeof (uint32_t), + mfsp->buffer.data8)); + } + + /* Transaction fully committed by writing the last (first in transaction) + magic number, now updating the internal state using the buffered data.*/ + mfsp->next_offset = mfsp->tr_next_offset; + while (top < &mfsp->tr_ops[mfsp->tr_nops]) { + unsigned i = (unsigned)top->id - 1U; + + /* The calculation is a bit different depending on write or erase record + operations.*/ + if (top->size > 0U) { + /* It is a write.*/ + if (mfsp->descriptors[i].offset != 0U) { + /* The size of the old record instance, if present, must be subtracted + to the total used size.*/ + mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[i].size); + } + + /* Adjusting bank-related metadata.*/ + mfsp->used_space += ALIGNED_REC_SIZE(top->size); + mfsp->descriptors[i].offset = top->offset; + mfsp->descriptors[i].size = top->size; + } + else { + /* It is an erase.*/ + mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[i].size); + mfsp->descriptors[i].offset = 0U; + mfsp->descriptors[i].size = 0U; + } + + /* On the next element.*/ + top++; + } + + /* Returning to ready mode.*/ + mfsp->state = MFS_READY; + + return MFS_NO_ERROR; } /** - * @brief Enforces a garbage collection operation. - * @details Garbage collection involves: integrity check, optionally repairs, - * obsolete data removal, data compaction and a flash bank swap. + * @brief A transaction is rolled back atomically. + * @details This function performs a garbage collection in order to discard + * all written data that has not been finalized. * * @param[in] mfsp pointer to the @p MFSDriver object * @return The operation status. - * @retval MFS_NO_ERROR if the operation has been successfully completed. - * @retval MFS_ERR_INV_STATE if the driver is in not in @p MSG_READY state. - * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW - * failures. Makes the driver enter the @p MFS_ERROR state. - * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_TRANSACTION + * state. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. * * @api */ -mfs_error_t mfsPerformGarbageCollection(MFSDriver *mfsp) { +mfs_error_t mfsRollbackTransaction(MFSDriver *mfsp) { + mfs_error_t err; osalDbgCheck(mfsp != NULL); - if (mfsp->state != MFS_READY) { + if (mfsp->state != MFS_TRANSACTION) { return MFS_ERR_INV_STATE; } - return mfs_garbage_collect(mfsp); + /* Returning to ready mode.*/ + mfsp->state = MFS_READY; + + /* If no operations have been performed then there is no need to perform + a garbage collection.*/ + if (mfsp->tr_nops > 0U) { + err = mfs_garbage_collect(mfsp); + } + else { + err = MFS_NO_ERROR; + } + + return err; } +#endif /* MFS_CFG_TRANSACTION_MAX > 0 */ /** @} */ diff --git a/os/hal/lib/complex/mfs/hal_mfs.h b/os/hal/lib/complex/mfs/hal_mfs.h index 56ff0b966..14dfc75ee 100644 --- a/os/hal/lib/complex/mfs/hal_mfs.h +++ b/os/hal/lib/complex/mfs/hal_mfs.h @@ -48,7 +48,7 @@ */ /** * @brief Maximum number of indexed records in the managed storage. - * @note Record indexes go from 0 to @p MFS_CFG_MAX_RECORDS - 1. + * @note Record indexes go from 1 to @p MFS_CFG_MAX_RECORDS. */ #if !defined(MFS_CFG_MAX_RECORDS) || defined(__DOXYGEN__) #define MFS_CFG_MAX_RECORDS 32 @@ -95,15 +95,16 @@ * for records in the flash array. This is required when alignment * constraints exist, for example when using a DTR mode on OSPI * devices. - * @note When enforcing an alignment you need to use buffers with size - * aligned to the specified value. For example, if you need to - * write a 5 bytes object with alignment of 4 then you need to - * use a 8 bytes data buffer, the last 3 bytes are used as filler - * so ==initialize== those to zero (buffer->DDDDD000) or garbage - * will be written after data. */ #if !defined(MFS_CFG_MEMORY_ALIGNMENT) || defined(__DOXYGEN__) -#define MFS_CFG_MEMORY_ALIGNMENT 1 +#define MFS_CFG_MEMORY_ALIGNMENT 2 +#endif + +/** + * @brief Maximum number of objects writable in a single transaction. + */ +#if !defined(MFS_CFG_TRANSACTION_MAX) || defined(__DOXYGEN__) +#define MFS_CFG_TRANSACTION_MAX 16 #endif /** @} */ @@ -115,7 +116,8 @@ #error "invalid MFS_CFG_MAX_RECORDS value" #endif -#if (MFS_CFG_MAX_REPAIR_ATTEMPTS < 1) || (MFS_CFG_MAX_REPAIR_ATTEMPTS > 10) +#if (MFS_CFG_MAX_REPAIR_ATTEMPTS < 1) || \ + (MFS_CFG_MAX_REPAIR_ATTEMPTS > 10) #error "invalid MFS_MAX_REPAIR_ATTEMPTS value" #endif @@ -127,7 +129,8 @@ #error "MFS_CFG_BUFFER_SIZE is not a power of two" #endif -#if MFS_CFG_MEMORY_ALIGNMENT < 1 +#if (MFS_CFG_MEMORY_ALIGNMENT < 1) || \ + (MFS_CFG_MEMORY_ALIGNMENT > MFS_CFG_BUFFER_SIZE) #error "invalid MFS_CFG_MEMORY_ALIGNMENT value" #endif @@ -135,6 +138,11 @@ #error "MFS_CFG_MEMORY_ALIGNMENT is not a power of two" #endif +#if (MFS_CFG_TRANSACTION_MAX < 0) || \ + (MFS_CFG_TRANSACTION_MAX > MFS_CFG_MAX_RECORDS) +#error "invalid MFS_CFG_TRANSACTION_MAX value" +#endif + /*===========================================================================*/ /* Driver data structures and types. */ /*===========================================================================*/ @@ -154,7 +162,8 @@ typedef enum { MFS_UNINIT = 0, MFS_STOP = 1, MFS_READY = 2, - MFS_ERROR = 3 + MFS_TRANSACTION = 3, + MFS_ERROR = 4 } mfs_state_t; /** @@ -170,9 +179,11 @@ typedef enum { MFS_ERR_INV_SIZE = -2, MFS_ERR_NOT_FOUND = -3, MFS_ERR_OUT_OF_MEM = -4, - MFS_ERR_NOT_ERASED = -5, - MFS_ERR_FLASH_FAILURE = -6, - MFS_ERR_INTERNAL = -7 + MFS_ERR_TRANSACTION_NUM = -5, + MFS_ERR_TRANSACTION_SIZE = -6, + MFS_ERR_NOT_ERASED = -7, + MFS_ERR_FLASH_FAILURE = -8, + MFS_ERR_INTERNAL = -9 } mfs_error_t; /** @@ -181,20 +192,9 @@ typedef enum { typedef enum { MFS_BANK_ERASED = 0, MFS_BANK_OK = 1, - MFS_BANK_PARTIAL = 2, - MFS_BANK_GARBAGE = 3 + MFS_BANK_GARBAGE = 2 } mfs_bank_state_t; -/** - * @brief Type of a record state assessment. - */ -typedef enum { - MFS_RECORD_ERASED = 0, - MFS_RECORD_OK = 1, - MFS_RECORD_CRC = 2, - MFS_RECORD_GARBAGE = 3 -} mfs_record_state_t; - /** * @brief Type of a record identifier. */ @@ -244,7 +244,7 @@ typedef union { */ uint32_t magic; /** - * @brief Data identifier. + * @brief Record identifier. */ uint16_t id; /** @@ -311,8 +311,24 @@ typedef struct { } MFSConfig; /** - * @extends BaseFlash - * + * @brief Type of a buffered write/erase operation within a transaction. + */ +typedef struct { + /** + * @brief Written header offset. + */ + flash_offset_t offset; + /** + * @brief Written data size. + */ + size_t size; + /** + * @brief Record identifier. + */ + mfs_id_t id; +} mfs_transaction_op_t; + +/** * @brief Type of an MFS instance. */ typedef struct { @@ -345,6 +361,24 @@ typedef struct { * @note Zero means that there is not a record with that id. */ mfs_record_descriptor_t descriptors[MFS_CFG_MAX_RECORDS]; +#if (MFS_CFG_TRANSACTION_MAX > 0) || defined(__DOXYGEN__) + /** + * @brief Next write offset for current transaction. + */ + flash_offset_t tr_next_offset; + /** + * @brief Maximum offset for the transaction. + */ + flash_offset_t tr_limit_offet; + /** + * @brief Number of buffered operations in current transaction. + */ + uint32_t tr_nops; + /** + * @brief Buffered operations in current transaction. + */ + mfs_transaction_op_t tr_ops[MFS_CFG_TRANSACTION_MAX]; +#endif /** * @brief Transient buffer. */ @@ -376,7 +410,8 @@ typedef struct { #define MFS_ALIGN_MASK ((uint32_t)MFS_CFG_MEMORY_ALIGNMENT - 1U) #define MFS_IS_ALIGNED(v) (((uint32_t)(v) & MFS_ALIGN_MASK) == 0U) #define MFS_ALIGN_PREV(v) ((uint32_t)(v) & ~MFS_ALIGN_MASK) -#define MFS_ALIGN_NEXT(v) MFS_ALIGN_PREV((size_t)(v) + MFS_ALIGN_MASK) +#define MFS_ALIGN_NEXT(v) (MFS_ALIGN_PREV(((uint32_t)(v) - 1U)) + \ + MFS_CFG_MEMORY_ALIGNMENT) /** @} */ /*===========================================================================*/ @@ -396,6 +431,11 @@ extern "C" { size_t n, const uint8_t *buffer); mfs_error_t mfsEraseRecord(MFSDriver *devp, mfs_id_t id); mfs_error_t mfsPerformGarbageCollection(MFSDriver *mfsp); +#if MFS_CFG_TRANSACTION_MAX > 0 + mfs_error_t mfsStartTransaction(MFSDriver *mfsp, size_t size); + mfs_error_t mfsCommitTransaction(MFSDriver *mfsp); + mfs_error_t mfsRollbackTransaction(MFSDriver *mfsp); +#endif /* MFS_CFG_TRANSACTION_MAX > 0 */ #ifdef __cplusplus } #endif diff --git a/readme.txt b/readme.txt index 6e1614568..c1f58f535 100644 --- a/readme.txt +++ b/readme.txt @@ -74,6 +74,8 @@ ***************************************************************************** *** 19.1.2 *** +- NEW: updates to MFS from trunk code. +- NEW: updates to test library from trunk code. - HAL: Added H753 to all H7 mcuconf.h files. - FIX: Fixed invalid EXTI registry constant for STM32L4+ (bug #1024). - FIX: Fixed missing RTC definitions in STM32L1xx registry (bug #1023). diff --git a/test/lib/ch_test.c b/test/lib/ch_test.c index 859ecdf9f..7c6253674 100644 --- a/test/lib/ch_test.c +++ b/test/lib/ch_test.c @@ -255,6 +255,23 @@ msg_t test_execute(BaseSequentialStream *stream, const testsuite_t *tsp) { test_print("*** Test Board: "); test_println(BOARD_NAME); #endif +#if defined(TEST_SIZE_REPORT) + { + extern uint8_t __text_base, __text_end, + _data_start, _data_end, + _bss_start, _bss_end; + test_println("***"); + test_print("*** Text size: "); + test_printn((uint32_t)(&__text_end - &__text_base)); + test_println(" bytes"); + test_print("*** Data size: "); + test_printn((uint32_t)(&_data_end - &_data_start)); + test_println(" bytes"); + test_print("*** BSS size: "); + test_printn((uint32_t)(&_bss_end - &_bss_start)); + test_println(" bytes"); + } +#endif #if defined(TEST_REPORT_HOOK_HEADER) TEST_REPORT_HOOK_HEADER #endif diff --git a/test/lib/ch_test.h b/test/lib/ch_test.h index b78591689..3b51085cb 100644 --- a/test/lib/ch_test.h +++ b/test/lib/ch_test.h @@ -104,6 +104,13 @@ typedef struct { */ #define test_set_step(step) test_step = (step) +/** + * @brief End step marker. + * + * @param[in] step the step number + */ +#define test_end_step(step) (void)(step); + /** * @brief Test failure enforcement. * @note This function can only be called from test_case execute context. diff --git a/test/mfs/configuration.xml b/test/mfs/configuration.xml index efc7b5b04..070066b52 100644 --- a/test/mfs/configuration.xml +++ b/test/mfs/configuration.xml @@ -47,6 +47,10 @@ extern const MFSConfig mfscfg1; extern MFSDriver mfs1; extern uint8_t mfs_buffer[512]; +extern const uint8_t mfs_pattern16[16]; +extern const uint8_t mfs_pattern32[32]; +extern const uint8_t mfs_pattern10[10]; +extern const uint8_t mfs_pattern512[512]; flash_error_t bank_erase(mfs_bank_t bank); flash_error_t bank_verify_erased(mfs_bank_t bank); @@ -57,6 +61,54 @@ void test_print_mfs_info(void);]]> MFSDriver mfs1; uint8_t mfs_buffer[512]; + +const uint8_t mfs_pattern16[16] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; + +const uint8_t mfs_pattern32[32] = { + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 +}; + +const uint8_t mfs_pattern10[10] = { + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 +}; + +const uint8_t mfs_pattern512[512] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 +}; void test_print_mfs_info(void) { @@ -128,55 +180,7 @@ flash_error_t bank_verify_erased(mfs_bank_t bank) { -#include "hal_mfs.h" - -static const uint8_t pattern1[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 -}; - -static const uint8_t pattern2[] = { - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 -}; - -static const uint8_t pattern3[] = { - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 -}; - -static const uint8_t pattern512[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 -};]]> +#include "hal_mfs.h"]]> @@ -336,13 +340,13 @@ test_assert(err == MFS_ERR_NOT_FOUND , "record was already present");]]> +test_assert(size == sizeof mfs_pattern16, "unexpected record length"); +test_assert(memcmp(mfs_pattern16, mfs_buffer, size) == 0, "wrong record content");]]> @@ -355,13 +359,13 @@ test_assert(memcmp(pattern1, mfs_buffer, size) == 0, "wrong record content");]]> +test_assert(size == sizeof mfs_pattern32, "unexpected record length"); +test_assert(memcmp(mfs_pattern32, mfs_buffer, size) == 0, "wrong record content");]]> @@ -417,11 +421,11 @@ mfsStart(&mfs1, &mfscfg1);]]> @@ -530,21 +534,21 @@ mfsErase(&mfs1);]]> @@ -559,9 +563,9 @@ for (id = 1; id <= id_max; id++) { @@ -583,7 +587,7 @@ test_assert(remaining >= sizeof (mfs_data_header_t), "not enough space"); if (remaining > sizeof (mfs_data_header_t) * 2) { err = mfsWriteRecord(&mfs1, MFS_CFG_MAX_RECORDS, remaining - (sizeof (mfs_data_header_t) * 2), - pattern512); + mfs_pattern512); test_assert(err == MFS_NO_ERROR, "error filling remaining space"); err = mfsEraseRecord(&mfs1, MFS_CFG_MAX_RECORDS); test_assert(err == MFS_NO_ERROR, "error filling remaining space"); @@ -641,21 +645,21 @@ mfsErase(&mfs1);]]> @@ -690,14 +694,15 @@ test_assert(err == MFS_ERR_NOT_FOUND, "record not erased");]]> size_t size; test_assert(mfs1.current_counter == 1, "not first instance"); -err = mfsWriteRecord(&mfs1, 1, sizeof pattern512, pattern512); +err = mfsWriteRecord(&mfs1, 1, sizeof mfs_pattern512, mfs_pattern512); test_assert(err == MFS_WARN_GC, "error creating the record"); test_assert(mfs1.current_counter == 2, "not second instance"); size = sizeof mfs_buffer; err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); test_assert(err == MFS_NO_ERROR, "record not found"); -test_assert(size == sizeof pattern512, "unexpected record length"); -test_assert(memcmp(pattern512, mfs_buffer, size) == 0, "wrong record content"); +test_assert(size == sizeof mfs_pattern512, "unexpected record length"); +test_assert(memcmp(mfs_pattern512, mfs_buffer, size) == 0, + "wrong record content"); test_assert(mfs1.current_bank == MFS_BANK_1, "unexpected bank"); test_assert(bank_verify_erased(MFS_BANK_0) == FLASH_NO_ERROR, "bank 0 not erased");]]> @@ -712,7 +717,7 @@ test_assert(bank_verify_erased(MFS_BANK_0) == FLASH_NO_ERROR, "bank 0 not erased size_t size; test_assert(mfs1.current_counter == 2, "not second instance"); -err = mfsWriteRecord(&mfs1, 1, sizeof pattern512, pattern512); +err = mfsWriteRecord(&mfs1, 1, sizeof mfs_pattern512, mfs_pattern512); test_assert(err == MFS_WARN_GC, "error creating the record"); test_assert(mfs1.current_counter == 3, "not third instance"); size = sizeof mfs_buffer; err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); test_assert(err == MFS_NO_ERROR, "record not found"); -test_assert(size == sizeof pattern512, "unexpected record length"); -test_assert(memcmp(pattern512, mfs_buffer, size) == 0, "wrong record content"); +test_assert(size == sizeof mfs_pattern512, "unexpected record length"); +test_assert(memcmp(mfs_pattern512, mfs_buffer, size) == 0, + "wrong record content"); test_assert(mfs1.current_bank == MFS_BANK_0, "unexpected bank"); test_assert(bank_verify_erased(MFS_BANK_1) == FLASH_NO_ERROR, "bank 1 not erased");]]> @@ -785,7 +792,7 @@ test_assert(bank_verify_erased(MFS_BANK_1) == FLASH_NO_ERROR, "bank 1 not erased @@ -869,9 +878,9 @@ for (id = 1; id <= id_max; id++) { size_t size; mfs_id_t id; mfs_id_t id_max = (mfscfg1.bank_size - sizeof (mfs_bank_header_t)) / - (sizeof (mfs_data_header_t) + (sizeof pattern512 / 2)); + (sizeof (mfs_data_header_t) + (sizeof mfs_pattern512 / 2)); mfs_id_t n = ((mfscfg1.bank_size - sizeof (mfs_bank_header_t)) - - (id_max * (sizeof (mfs_data_header_t) + (sizeof pattern512 / 2)))) / + (id_max * (sizeof (mfs_data_header_t) + (sizeof mfs_pattern512 / 2)))) / sizeof (mfs_data_header_t); for (id = 1; id <= n; id++) { @@ -894,7 +903,7 @@ for (id = 1; id <= n; id++) { + + + Internal Tests + + + Transaction Mode tests. + + + This sequence tests the MFS behavior when used in transaction mode, correct cases and expected error cases are tested. + + + + + + +#include "hal_mfs.h"]]> + + + + + Committing a transaction + + + A set of new/existing records are written/erased within a transaction then the transaction is committed, the state is checked afterward. + + + + + + + + + + + + + + + + + + + Records 1, 2 and 3 are created, MFS_NO_ERROR is expected. + + + + + + + + + + + Presence of records 1, 2 and 3 is verified, MFS_NO_ERROR is expected. + + + + + + + + + + + Starting a transaction with sufficient pre-allocated space, MFS_NO_ERROR is expected. + + + + + + + + + + + Atomically erasing record 1, updating record 2, reading record 3. + + + + + + + + + + + Committing the transaction, MFS_NO_ERROR is expected. + + + + + + + + + + + Testing outcome, records 1 must not be present, record 2 must contain the new value and record 3 must be unchanged. + + + + + + + + + + + Re-mounting the manage storage, MFS_NO_ERROR is expected. + + + + + + + + + + + Testing outcome again after re-start. + + + + + + + + + + + Performing a garbage collection, the result must + not change. + + + + + + + + + + + Testing outcome again after garbage collection. + + + + + + + + + + + + + Rolling back a transaction. + + + A set of new/existing records are written/erased within a transaction then the transaction is rolled back, the state is checked afterward. + + + + + + + + + + + + + + + + + + + Records 1, 2 and 3 are created, MFS_NO_ERROR is expected. + + + + + + + + + + + Presence of records 1, 2 and 3 is verified, MFS_NO_ERROR is expected. + + + + + + + + + + + Starting a transaction with sufficient pre-allocated space, MFS_NO_ERROR is expected.. + + + + + + + + + + + Atomically erasing record 1, updating record 2, reading record 3. + + + + + + + + + + + Rolling back the transaction, MFS_NO_ERROR is expected. + + + + + + + + + + + State must not have changed, records 1, 2 and 3 must still be there unchanged. + + + + + + + + + + + + + Transaction triggering an early garbage collect. + + + A transaction is started with sufficient space but not contiguous, a garbage collection is triggered. + + + + + + + + + + + + + + + + + + + Filling up the storage by writing records with increasing IDs, MFS_NO_ERROR is expected. + + + + + + + + + + + Erasing one record, MFS_NO_ERROR is expected. + + + + + + + + + + + Starting a transaction with the whole remaining space, MFS_ERR_OUT_OF_MEM is expected. + + + + + + bank_size - mfs1.used_space; + +err = mfsStartTransaction(&mfs1, size); +test_assert(err == MFS_ERR_OUT_OF_MEM, "invalid error code");]]> + + + + + Starting a transaction with insufficient space for one more header, MFS_ERR_OUT_OF_MEM is expected. + + + + + + bank_size - mfs1.used_space) - sizeof (mfs_data_header_t)) + 1; + +err = mfsStartTransaction(&mfs1, size); +test_assert(err == MFS_ERR_OUT_OF_MEM, "invalid error code");]]> + + + + + Starting a transaction with just enough space for one more header, MFS_NO_ERROR is expected. + + + + + + bank_size - mfs1.used_space) - sizeof (mfs_data_header_t); + +err = mfsStartTransaction(&mfs1, size); +test_assert(err == MFS_NO_ERROR, "error starting transaction");]]> + + + + + Rolling back, MFS_NO_ERROR is expected. + + + + + + + + + + + + Internal Tests diff --git a/test/mfs/mfs_test.mk b/test/mfs/mfs_test.mk index ae6fbf6a2..499ebb47b 100644 --- a/test/mfs/mfs_test.mk +++ b/test/mfs/mfs_test.mk @@ -1,7 +1,8 @@ # List of all the ChibiOS/HAL MFS test files. TESTSRC += ${CHIBIOS}/test/mfs/source/test/mfs_test_root.c \ ${CHIBIOS}/test/mfs/source/test/mfs_test_sequence_001.c \ - ${CHIBIOS}/test/mfs/source/test/mfs_test_sequence_002.c + ${CHIBIOS}/test/mfs/source/test/mfs_test_sequence_002.c \ + ${CHIBIOS}/test/mfs/source/test/mfs_test_sequence_003.c # Required include directories TESTINC += ${CHIBIOS}/test/mfs/source/test diff --git a/test/mfs/source/test/mfs_test_root.c b/test/mfs/source/test/mfs_test_root.c index 447278610..422a41f06 100644 --- a/test/mfs/source/test/mfs_test_root.c +++ b/test/mfs/source/test/mfs_test_root.c @@ -23,6 +23,7 @@ *

Test Sequences

* - @subpage mfs_test_sequence_001 * - @subpage mfs_test_sequence_002 + * - @subpage mfs_test_sequence_003 * . */ @@ -46,6 +47,7 @@ const testsequence_t * const mfs_test_suite_array[] = { &mfs_test_sequence_001, &mfs_test_sequence_002, + &mfs_test_sequence_003, NULL }; @@ -66,6 +68,54 @@ const testsuite_t mfs_test_suite = { MFSDriver mfs1; uint8_t mfs_buffer[512]; +const uint8_t mfs_pattern16[16] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; + +const uint8_t mfs_pattern32[32] = { + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 +}; + +const uint8_t mfs_pattern10[10] = { + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 +}; + +const uint8_t mfs_pattern512[512] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 +}; + void test_print_mfs_info(void) { } diff --git a/test/mfs/source/test/mfs_test_root.h b/test/mfs/source/test/mfs_test_root.h index 672b7da80..22f85634f 100644 --- a/test/mfs/source/test/mfs_test_root.h +++ b/test/mfs/source/test/mfs_test_root.h @@ -26,6 +26,7 @@ #include "mfs_test_sequence_001.h" #include "mfs_test_sequence_002.h" +#include "mfs_test_sequence_003.h" #if !defined(__DOXYGEN__) @@ -55,6 +56,10 @@ extern "C" { extern const MFSConfig mfscfg1; extern MFSDriver mfs1; extern uint8_t mfs_buffer[512]; +extern const uint8_t mfs_pattern16[16]; +extern const uint8_t mfs_pattern32[32]; +extern const uint8_t mfs_pattern10[10]; +extern const uint8_t mfs_pattern512[512]; flash_error_t bank_erase(mfs_bank_t bank); flash_error_t bank_verify_erased(mfs_bank_t bank); diff --git a/test/mfs/source/test/mfs_test_sequence_001.c b/test/mfs/source/test/mfs_test_sequence_001.c index 20b4d8d89..7c6d929ee 100644 --- a/test/mfs/source/test/mfs_test_sequence_001.c +++ b/test/mfs/source/test/mfs_test_sequence_001.c @@ -47,54 +47,6 @@ #include #include "hal_mfs.h" -static const uint8_t pattern1[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 -}; - -static const uint8_t pattern2[] = { - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 -}; - -static const uint8_t pattern3[] = { - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 -}; - -static const uint8_t pattern512[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 -}; - /**************************************************************************** * Test cases. ****************************************************************************/ @@ -136,6 +88,7 @@ static void mfs_test_001_001_execute(void) { ferr = bank_erase(MFS_BANK_1); test_assert(ferr == FLASH_NO_ERROR, "Bank 1 erase failure"); } + test_end_step(1); /* [1.1.2] Calling mfsStart() on an uninitialized flash array, MFS_NO_ERROR is expected.*/ @@ -146,6 +99,7 @@ static void mfs_test_001_001_execute(void) { err = mfsStart(&mfs1, &mfscfg1); test_assert(err == MFS_NO_ERROR, "initialization error with erased flash"); } + test_end_step(2); /* [1.1.3] Calling mfsStart() on a newly initialized flash array, MFS_NO_ERROR is expected.*/ @@ -156,6 +110,7 @@ static void mfs_test_001_001_execute(void) { err = mfsStart(&mfs1, &mfscfg1); test_assert(err == MFS_NO_ERROR, "initialization error with initialized flash"); } + test_end_step(3); } static const testcase_t mfs_test_001_001 = { @@ -203,6 +158,7 @@ static void mfs_test_001_002_execute(void) { "found a record that should not exists"); } } + test_end_step(1); } static const testcase_t mfs_test_001_002 = { @@ -253,6 +209,7 @@ static void mfs_test_001_003_execute(void) { mfs_error_t err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); test_assert(err == MFS_ERR_NOT_FOUND , "record was already present"); } + test_end_step(1); /* [1.3.2] Creating the record then retrieving it again, MFS_NO_ERROR is expected, record content and size are compared with the @@ -261,14 +218,15 @@ static void mfs_test_001_003_execute(void) { { mfs_error_t err; - err = mfsWriteRecord(&mfs1, 1, sizeof pattern1, pattern1); + err = mfsWriteRecord(&mfs1, 1, sizeof mfs_pattern16, mfs_pattern16); test_assert(err == MFS_NO_ERROR, "error creating the record"); size = sizeof mfs_buffer; err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); test_assert(err == MFS_NO_ERROR, "record not found"); - test_assert(size == sizeof pattern1, "unexpected record length"); - test_assert(memcmp(pattern1, mfs_buffer, size) == 0, "wrong record content"); + test_assert(size == sizeof mfs_pattern16, "unexpected record length"); + test_assert(memcmp(mfs_pattern16, mfs_buffer, size) == 0, "wrong record content"); } + test_end_step(2); /* [1.3.3] Updating the record then retrieving it again, MFS_NO_ERROR is expected, record content and size are compared with the @@ -277,14 +235,15 @@ static void mfs_test_001_003_execute(void) { { mfs_error_t err; - err = mfsWriteRecord(&mfs1, 1, sizeof pattern2, pattern2); + err = mfsWriteRecord(&mfs1, 1, sizeof mfs_pattern32, mfs_pattern32); test_assert(err == MFS_NO_ERROR, "error updating the record"); size = sizeof mfs_buffer; err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); test_assert(err == MFS_NO_ERROR, "record not found"); - test_assert(size == sizeof pattern2, "unexpected record length"); - test_assert(memcmp(pattern2, mfs_buffer, size) == 0, "wrong record content"); + test_assert(size == sizeof mfs_pattern32, "unexpected record length"); + test_assert(memcmp(mfs_pattern32, mfs_buffer, size) == 0, "wrong record content"); } + test_end_step(3); /* [1.3.4] Erasing the record then trying to retrieve it again, MFS_NO_ERROR is expected on erase, MFS_ERR_NOT_FOUND is expected @@ -299,6 +258,7 @@ static void mfs_test_001_003_execute(void) { err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); test_assert(err == MFS_ERR_NOT_FOUND, "record not erased"); } + test_end_step(4); } static const testcase_t mfs_test_001_003 = { @@ -341,13 +301,14 @@ static void mfs_test_001_004_execute(void) { { mfs_error_t err; - err = mfsWriteRecord(&mfs1, 1, sizeof pattern1, pattern1); + err = mfsWriteRecord(&mfs1, 1, sizeof mfs_pattern16, mfs_pattern16); test_assert(err == MFS_NO_ERROR, "error creating record 1"); - err = mfsWriteRecord(&mfs1, 2, sizeof pattern2, pattern2); + err = mfsWriteRecord(&mfs1, 2, sizeof mfs_pattern32, mfs_pattern32); test_assert(err == MFS_NO_ERROR, "error creating record 2"); - err = mfsWriteRecord(&mfs1, 3, sizeof pattern3, pattern3); + err = mfsWriteRecord(&mfs1, 3, sizeof mfs_pattern10, mfs_pattern10); test_assert(err == MFS_NO_ERROR, "error creating record 3"); } + test_end_step(1); /* [1.4.2] Records must exist.*/ test_set_step(2); @@ -365,6 +326,7 @@ static void mfs_test_001_004_execute(void) { err = mfsReadRecord(&mfs1, 3, &size, mfs_buffer); test_assert(err == MFS_NO_ERROR, "record 2 not present"); } + test_end_step(2); /* [1.4.3] Re-mounting, records must still exist.*/ test_set_step(3); @@ -384,6 +346,7 @@ static void mfs_test_001_004_execute(void) { err = mfsReadRecord(&mfs1, 3, &size, mfs_buffer); test_assert(err == MFS_NO_ERROR, "record 2 not present"); } + test_end_step(3); /* [1.4.4] Erasing storage and verify that the records have been removed, MFS_NO_ERROR is expected on erase, MFS_ERR_NOT_FOUND is @@ -405,6 +368,7 @@ static void mfs_test_001_004_execute(void) { err = mfsReadRecord(&mfs1, 3, &size, mfs_buffer); test_assert(err == MFS_ERR_NOT_FOUND, "record 2 still present"); } + test_end_step(4); } static const testcase_t mfs_test_001_004 = { @@ -448,24 +412,25 @@ static void mfs_test_001_005_execute(void) { { mfs_id_t id; mfs_id_t id_max = (mfscfg1.bank_size - sizeof (mfs_bank_header_t)) / - (sizeof (mfs_data_header_t) + sizeof pattern512); + (sizeof (mfs_data_header_t) + sizeof mfs_pattern512); for (id = 1; id <= id_max; id++) { mfs_error_t err; size_t size; - err = mfsWriteRecord(&mfs1, id, sizeof pattern512, pattern512); + err = mfsWriteRecord(&mfs1, id, sizeof mfs_pattern512, mfs_pattern512); test_assert(err == MFS_NO_ERROR, "error creating the record"); size = sizeof mfs_buffer; err = mfsReadRecord(&mfs1, id, &size, mfs_buffer); test_assert(err == MFS_NO_ERROR, "record not found"); - test_assert(size == sizeof pattern512, + test_assert(size == sizeof mfs_pattern512, "unexpected record length"); - test_assert(memcmp(pattern512, mfs_buffer, size) == 0, + test_assert(memcmp(mfs_pattern512, mfs_buffer, size) == 0, "wrong record content"); } } + test_end_step(1); /* [1.5.2] Creating one more record, should fail, MFS_ERR_OUT_OF_MEM is expected.*/ @@ -473,11 +438,12 @@ static void mfs_test_001_005_execute(void) { { mfs_error_t err; mfs_id_t id_max = (mfscfg1.bank_size - sizeof (mfs_bank_header_t)) / - (sizeof (mfs_data_header_t) + sizeof pattern512); + (sizeof (mfs_data_header_t) + sizeof mfs_pattern512); - err = mfsWriteRecord(&mfs1, id_max, sizeof pattern512 , pattern512); + err = mfsWriteRecord(&mfs1, id_max, sizeof mfs_pattern512 , mfs_pattern512); test_assert(err == MFS_ERR_OUT_OF_MEM, "creation didn't fail"); } + test_end_step(2); /* [1.5.3] Adding a smaller record to fill the final gap. A reinitialization is performed and MFS_NO_ERROR is expected.*/ @@ -493,7 +459,7 @@ static void mfs_test_001_005_execute(void) { if (remaining > sizeof (mfs_data_header_t) * 2) { err = mfsWriteRecord(&mfs1, MFS_CFG_MAX_RECORDS, remaining - (sizeof (mfs_data_header_t) * 2), - pattern512); + mfs_pattern512); test_assert(err == MFS_NO_ERROR, "error filling remaining space"); err = mfsEraseRecord(&mfs1, MFS_CFG_MAX_RECORDS); test_assert(err == MFS_NO_ERROR, "error filling remaining space"); @@ -515,6 +481,7 @@ static void mfs_test_001_005_execute(void) { err = mfsStart(&mfs1, &mfscfg1); test_assert(err == MFS_NO_ERROR, "initialization error"); } + test_end_step(3); } static const testcase_t mfs_test_001_005 = { @@ -566,24 +533,25 @@ static void mfs_test_001_006_execute(void) { { mfs_id_t id; mfs_id_t id_max = (mfscfg1.bank_size - sizeof (mfs_bank_header_t)) / - (sizeof (mfs_data_header_t) + sizeof pattern512); + (sizeof (mfs_data_header_t) + sizeof mfs_pattern512); for (id = 1; id <= id_max; id++) { mfs_error_t err; size_t size; - err = mfsWriteRecord(&mfs1, id, sizeof pattern512, pattern512); + err = mfsWriteRecord(&mfs1, id, sizeof mfs_pattern512, mfs_pattern512); test_assert(err == MFS_NO_ERROR, "error creating the record"); size = sizeof mfs_buffer; err = mfsReadRecord(&mfs1, id, &size, mfs_buffer); test_assert(err == MFS_NO_ERROR, "record not found"); - test_assert(size == sizeof pattern512, + test_assert(size == sizeof mfs_pattern512, "unexpected record length"); - test_assert(memcmp(pattern512, mfs_buffer, size) == 0, + test_assert(memcmp(mfs_pattern512, mfs_buffer, size) == 0, "wrong record content"); } } + test_end_step(1); /* [1.6.2] Erasing one record, MFS_NO_ERROR is expected.*/ test_set_step(2); @@ -597,6 +565,7 @@ static void mfs_test_001_006_execute(void) { err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); test_assert(err == MFS_ERR_NOT_FOUND, "record not erased"); } + test_end_step(2); /* [1.6.3] Writing one more record triggers garbage collection, MFS_WARN_GC is expected, KS state is checked for correctness after @@ -607,17 +576,19 @@ static void mfs_test_001_006_execute(void) { size_t size; test_assert(mfs1.current_counter == 1, "not first instance"); - err = mfsWriteRecord(&mfs1, 1, sizeof pattern512, pattern512); + err = mfsWriteRecord(&mfs1, 1, sizeof mfs_pattern512, mfs_pattern512); test_assert(err == MFS_WARN_GC, "error creating the record"); test_assert(mfs1.current_counter == 2, "not second instance"); size = sizeof mfs_buffer; err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); test_assert(err == MFS_NO_ERROR, "record not found"); - test_assert(size == sizeof pattern512, "unexpected record length"); - test_assert(memcmp(pattern512, mfs_buffer, size) == 0, "wrong record content"); + test_assert(size == sizeof mfs_pattern512, "unexpected record length"); + test_assert(memcmp(mfs_pattern512, mfs_buffer, size) == 0, + "wrong record content"); test_assert(mfs1.current_bank == MFS_BANK_1, "unexpected bank"); test_assert(bank_verify_erased(MFS_BANK_0) == FLASH_NO_ERROR, "bank 0 not erased"); } + test_end_step(3); /* [1.6.4] Checking for all records in the new bank, MFS_NOERROR is expected for each record.*/ @@ -625,7 +596,7 @@ static void mfs_test_001_006_execute(void) { { mfs_id_t id; mfs_id_t id_max = (mfscfg1.bank_size - sizeof (mfs_bank_header_t)) / - (sizeof (mfs_data_header_t) + sizeof pattern512); + (sizeof (mfs_data_header_t) + sizeof mfs_pattern512); for (id = 1; id <= MFS_CFG_MAX_RECORDS; id++) { mfs_error_t err; @@ -635,8 +606,9 @@ static void mfs_test_001_006_execute(void) { size = sizeof mfs_buffer; err = mfsReadRecord(&mfs1, id, &size, mfs_buffer); test_assert(err == MFS_NO_ERROR, "record not found"); - test_assert(size == sizeof pattern512, "unexpected record length"); - test_assert(memcmp(pattern512, mfs_buffer, size) == 0, "wrong record content"); + test_assert(size == sizeof mfs_pattern512, "unexpected record length"); + test_assert(memcmp(mfs_pattern512, mfs_buffer, size) == 0, + "wrong record content"); } else { size = sizeof mfs_buffer; @@ -645,6 +617,7 @@ static void mfs_test_001_006_execute(void) { } } } + test_end_step(4); /* [1.6.5] Erasing one record, MFS_NO_ERROR is expected.*/ test_set_step(5); @@ -658,6 +631,7 @@ static void mfs_test_001_006_execute(void) { err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); test_assert(err == MFS_ERR_NOT_FOUND, "record not erased"); } + test_end_step(5); /* [1.6.6] Writing one more record triggers garbage collection, MFS_WARN_GC is expected, MFS object state is checked for @@ -668,17 +642,19 @@ static void mfs_test_001_006_execute(void) { size_t size; test_assert(mfs1.current_counter == 2, "not second instance"); - err = mfsWriteRecord(&mfs1, 1, sizeof pattern512, pattern512); + err = mfsWriteRecord(&mfs1, 1, sizeof mfs_pattern512, mfs_pattern512); test_assert(err == MFS_WARN_GC, "error creating the record"); test_assert(mfs1.current_counter == 3, "not third instance"); size = sizeof mfs_buffer; err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); test_assert(err == MFS_NO_ERROR, "record not found"); - test_assert(size == sizeof pattern512, "unexpected record length"); - test_assert(memcmp(pattern512, mfs_buffer, size) == 0, "wrong record content"); + test_assert(size == sizeof mfs_pattern512, "unexpected record length"); + test_assert(memcmp(mfs_pattern512, mfs_buffer, size) == 0, + "wrong record content"); test_assert(mfs1.current_bank == MFS_BANK_0, "unexpected bank"); test_assert(bank_verify_erased(MFS_BANK_1) == FLASH_NO_ERROR, "bank 1 not erased"); } + test_end_step(6); /* [1.6.7] Checking for all records in the new bank, MFS_NO_ERROR is expected for each record.*/ @@ -686,7 +662,7 @@ static void mfs_test_001_006_execute(void) { { mfs_id_t id; mfs_id_t id_max = (mfscfg1.bank_size - sizeof (mfs_bank_header_t)) / - (sizeof (mfs_data_header_t) + sizeof pattern512); + (sizeof (mfs_data_header_t) + sizeof mfs_pattern512); for (id = 1; id <= MFS_CFG_MAX_RECORDS; id++) { mfs_error_t err; @@ -696,8 +672,9 @@ static void mfs_test_001_006_execute(void) { size = sizeof mfs_buffer; err = mfsReadRecord(&mfs1, id, &size, mfs_buffer); test_assert(err == MFS_NO_ERROR, "record not found"); - test_assert(size == sizeof pattern512, "unexpected record length"); - test_assert(memcmp(pattern512, mfs_buffer, size) == 0, "wrong record content"); + test_assert(size == sizeof mfs_pattern512, "unexpected record length"); + test_assert(memcmp(mfs_pattern512, mfs_buffer, size) == 0, + "wrong record content"); } else { size = sizeof mfs_buffer; @@ -706,6 +683,7 @@ static void mfs_test_001_006_execute(void) { } } } + test_end_step(7); } static const testcase_t mfs_test_001_006 = { @@ -749,21 +727,23 @@ static void mfs_test_001_007_execute(void) { { mfs_id_t id; mfs_id_t id_max = (mfscfg1.bank_size - sizeof (mfs_bank_header_t)) / - (sizeof (mfs_data_header_t) + (sizeof pattern512 / 2)); + (sizeof (mfs_data_header_t) + (sizeof mfs_pattern512 / 2)); for (id = 1; id <= id_max; id++) { mfs_error_t err; size_t size; - err = mfsWriteRecord(&mfs1, id, (sizeof pattern512 / 2), pattern512); + err = mfsWriteRecord(&mfs1, id, (sizeof mfs_pattern512 / 2), mfs_pattern512); test_assert(err == MFS_NO_ERROR, "error creating the record"); size = sizeof mfs_buffer; err = mfsReadRecord(&mfs1, id, &size, mfs_buffer); test_assert(err == MFS_NO_ERROR, "record not found"); - test_assert(size == (sizeof pattern512 / 2), "unexpected record length"); - test_assert(memcmp(pattern512, mfs_buffer, size) == 0, "wrong record content"); + test_assert(size == (sizeof mfs_pattern512 / 2), "unexpected record length"); + test_assert(memcmp(mfs_pattern512, mfs_buffer, size) == 0, + "wrong record content"); } } + test_end_step(1); /* [1.7.2] Erase records until the flash bank is filled entirely.*/ test_set_step(2); @@ -772,9 +752,9 @@ static void mfs_test_001_007_execute(void) { size_t size; mfs_id_t id; mfs_id_t id_max = (mfscfg1.bank_size - sizeof (mfs_bank_header_t)) / - (sizeof (mfs_data_header_t) + (sizeof pattern512 / 2)); + (sizeof (mfs_data_header_t) + (sizeof mfs_pattern512 / 2)); mfs_id_t n = ((mfscfg1.bank_size - sizeof (mfs_bank_header_t)) - - (id_max * (sizeof (mfs_data_header_t) + (sizeof pattern512 / 2)))) / + (id_max * (sizeof (mfs_data_header_t) + (sizeof mfs_pattern512 / 2)))) / sizeof (mfs_data_header_t); for (id = 1; id <= n; id++) { @@ -785,6 +765,7 @@ static void mfs_test_001_007_execute(void) { test_assert(err == MFS_ERR_NOT_FOUND, "record not erased"); } } + test_end_step(2); /* [1.7.3] Erasing one more record triggers garbage collection, MFS_WARN_GC is expected, KS state is checked for correctness after @@ -794,7 +775,7 @@ static void mfs_test_001_007_execute(void) { mfs_error_t err; size_t size; mfs_id_t id_max = (mfscfg1.bank_size - sizeof (mfs_bank_header_t)) / - (sizeof (mfs_data_header_t) + (sizeof pattern512 / 2)); + (sizeof (mfs_data_header_t) + (sizeof mfs_pattern512 / 2)); test_assert(mfs1.current_counter == 1, "not first instance"); err = mfsEraseRecord(&mfs1, id_max); @@ -806,6 +787,7 @@ static void mfs_test_001_007_execute(void) { test_assert(mfs1.current_bank == MFS_BANK_1, "unexpected bank"); test_assert(bank_verify_erased(MFS_BANK_0) == FLASH_NO_ERROR, "bank 0 not erased"); } + test_end_step(3); } static const testcase_t mfs_test_001_007 = { diff --git a/test/mfs/source/test/mfs_test_sequence_002.c b/test/mfs/source/test/mfs_test_sequence_002.c index 735b359df..c75858ea2 100644 --- a/test/mfs/source/test/mfs_test_sequence_002.c +++ b/test/mfs/source/test/mfs_test_sequence_002.c @@ -21,17 +21,18 @@ * @file mfs_test_sequence_002.c * @brief Test Sequence 002 code. * - * @page mfs_test_sequence_002 [2] API Invalid Cases tests + * @page mfs_test_sequence_002 [2] Transaction Mode tests * * File: @ref mfs_test_sequence_002.c * *

Description

- * This test sequence tests the error coded returned by the various - * APIs when called when the system is not initialized. + * This sequence tests the MFS behavior when used in transaction mode, + * correct cases and expected error cases are tested. * *

Test Cases

* - @subpage mfs_test_002_001 * - @subpage mfs_test_002_002 + * - @subpage mfs_test_002_003 * . */ @@ -39,6 +40,7 @@ * Shared code. ****************************************************************************/ +#include #include "hal_mfs.h" /**************************************************************************** @@ -46,91 +48,277 @@ ****************************************************************************/ /** - * @page mfs_test_002_001 [2.1] Initialization error from APIs + * @page mfs_test_002_001 [2.1] Committing a transaction * *

Description

- * The API functions are invoked without prior initialization. + * A set of new/existing records are written/erased within a + * transaction then the transaction is committed, the state is checked + * afterward. * *

Test Steps

- * - [2.1.1] The function mfsErase() is called, MFS_ERR_INV_STATE is - * expected. - * - [2.1.2] The function mfsWriteRecord() is called, MFS_ERR_INV_STATE + * - [2.1.1] Records 1, 2 and 3 are created, MFS_NO_ERROR is expected. + * - [2.1.2] Presence of records 1, 2 and 3 is verified, MFS_NO_ERROR * is expected. - * - [2.1.3] The function mfsEraseRecord() is called, MFS_ERR_INV_STATE - * is expected. - * - [2.1.4] The function mfsReadRecord() is called, MFS_ERR_INV_STATE - * is expected. - * - [2.1.5] The function mfsPerformGarbageCollection() is called, - * MFS_ERR_INV_STATE is expected. + * - [2.1.3] Starting a transaction with sufficient pre-allocated + * space, MFS_NO_ERROR is expected. + * - [2.1.4] Atomically erasing record 1, updating record 2, reading + * record 3. + * - [2.1.5] Committing the transaction, MFS_NO_ERROR is expected. + * - [2.1.6] Testing outcome, records 1 must not be present, record 2 + * must contain the new value and record 3 must be unchanged. + * - [2.1.7] Re-mounting the manage storage, MFS_NO_ERROR is expected. + * - [2.1.8] Testing outcome again after re-start. + * - [2.1.9] Performing a garbage collection, the result must not + * change. + * - [2.1.10] Testing outcome again after garbage collection. * . */ +static void mfs_test_002_001_setup(void) { + bank_erase(MFS_BANK_0); + bank_erase(MFS_BANK_1); + mfsStart(&mfs1, &mfscfg1); +} + +static void mfs_test_002_001_teardown(void) { + mfsStop(&mfs1); +} + static void mfs_test_002_001_execute(void) { + uint32_t current_counter; + uint32_t used_space; - /* [2.1.1] The function mfsErase() is called, MFS_ERR_INV_STATE is + /* [2.1.1] Records 1, 2 and 3 are created, MFS_NO_ERROR is expected.*/ test_set_step(1); { - mfs_error_t err = mfsErase(&mfs1); - test_assert(err == MFS_ERR_INV_STATE, "mfsErase() returned wrong status"); + mfs_error_t err; + + err = mfsWriteRecord(&mfs1, 1, sizeof mfs_pattern16, mfs_pattern16); + test_assert(err == MFS_NO_ERROR, "error creating record 1"); + err = mfsWriteRecord(&mfs1, 2, sizeof mfs_pattern16, mfs_pattern16); + test_assert(err == MFS_NO_ERROR, "error creating record 2"); + err = mfsWriteRecord(&mfs1, 3, sizeof mfs_pattern16, mfs_pattern16); + test_assert(err == MFS_NO_ERROR, "error creating record 3"); } + test_end_step(1); - /* [2.1.2] The function mfsWriteRecord() is called, MFS_ERR_INV_STATE + /* [2.1.2] Presence of records 1, 2 and 3 is verified, MFS_NO_ERROR is expected.*/ test_set_step(2); { - mfs_error_t err = mfsWriteRecord(&mfs1, 1, 16, mfs_buffer); - test_assert(err == MFS_ERR_INV_STATE, "mfsWriteRecord() returned wrong status"); + mfs_error_t err; + size_t size; + + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 2, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 3, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); } + test_end_step(2); - /* [2.1.3] The function mfsEraseRecord() is called, MFS_ERR_INV_STATE - is expected.*/ + /* [2.1.3] Starting a transaction with sufficient pre-allocated + space, MFS_NO_ERROR is expected.*/ test_set_step(3); { - mfs_error_t err = mfsEraseRecord(&mfs1, 1); - test_assert(err == MFS_ERR_INV_STATE, "mfsEraseRecord() returned wrong status"); + mfs_error_t err; + + err = mfsStartTransaction(&mfs1, 1024U); + test_assert(err == MFS_NO_ERROR, "error starting transaction"); } + test_end_step(3); - /* [2.1.4] The function mfsReadRecord() is called, MFS_ERR_INV_STATE - is expected.*/ + /* [2.1.4] Atomically erasing record 1, updating record 2, reading + record 3.*/ test_set_step(4); { - size_t size = sizeof mfs_buffer; - mfs_error_t err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); - test_assert(err == MFS_ERR_INV_STATE, "mfsReadRecord() returned wrong status"); + mfs_error_t err; + size_t size; + + err = mfsEraseRecord(&mfs1, 1); + test_assert(err == MFS_NO_ERROR, "error erasing record 1"); + err = mfsWriteRecord(&mfs1, 2, sizeof mfs_pattern32, mfs_pattern32); + test_assert(err == MFS_NO_ERROR, "error writing record 2"); + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 3, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + test_assert(size == sizeof mfs_pattern16, "unexpected record length"); + test_assert(memcmp(mfs_pattern16, mfs_buffer, size) == 0, "wrong record content"); } + test_end_step(4); - /* [2.1.5] The function mfsPerformGarbageCollection() is called, - MFS_ERR_INV_STATE is expected.*/ + /* [2.1.5] Committing the transaction, MFS_NO_ERROR is expected.*/ test_set_step(5); { - mfs_error_t err = mfsPerformGarbageCollection(&mfs1); - test_assert(err == MFS_ERR_INV_STATE, "mfsPerformGarbageCollection() returned wrong status"); + mfs_error_t err; + + err = mfsCommitTransaction(&mfs1); + test_assert(err == MFS_NO_ERROR, "error committing transaction"); + + /* Saving some internal state for successive checks.*/ + current_counter = mfs1.current_counter; + used_space = mfs1.used_space; + } + test_end_step(5); + + /* [2.1.6] Testing outcome, records 1 must not be present, record 2 + must contain the new value and record 3 must be unchanged.*/ + test_set_step(6); + { + mfs_error_t err; + size_t size; + + /* Record 1 must not be present.*/ + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); + test_assert(err == MFS_ERR_NOT_FOUND, "record found"); + + /* Record 2 must contain the new value.*/ + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 2, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + test_assert(size == sizeof mfs_pattern32, "unexpected record length"); + test_assert(memcmp(mfs_pattern32, mfs_buffer, size) == 0, "wrong record content"); + + /* Record 3 must be unchanged.*/ + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 3, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + test_assert(size == sizeof mfs_pattern16, "unexpected record length"); + test_assert(memcmp(mfs_pattern16, mfs_buffer, size) == 0, "wrong record content"); + + /* Checking internal data.*/ + test_assert(MFS_BANK_0 == mfs1.current_bank, "internal data mismatch"); + test_assert(current_counter == mfs1.current_counter, "internal data mismatch"); + test_assert(used_space == mfs1.used_space, "internal data mismatch"); + } + test_end_step(6); + + /* [2.1.7] Re-mounting the manage storage, MFS_NO_ERROR is + expected.*/ + test_set_step(7); + { + mfs_error_t err; + + err = mfsStart(&mfs1, &mfscfg1); + test_assert(err == MFS_NO_ERROR, "re-start failed"); + } + test_end_step(7); + + /* [2.1.8] Testing outcome again after re-start.*/ + test_set_step(8); + { + mfs_error_t err; + size_t size; + + /* Record 1 must not be present.*/ + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); + test_assert(err == MFS_ERR_NOT_FOUND, "record found"); + + /* Record 2 must contain the new value.*/ + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 2, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + test_assert(size == sizeof mfs_pattern32, "unexpected record length"); + test_assert(memcmp(mfs_pattern32, mfs_buffer, size) == 0, "wrong record content"); + + /* Record 3 must be unchanged.*/ + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 3, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + test_assert(size == sizeof mfs_pattern16, "unexpected record length"); + test_assert(memcmp(mfs_pattern16, mfs_buffer, size) == 0, "wrong record content"); + + /* Checking internal data.*/ + test_assert(MFS_BANK_0 == mfs1.current_bank, "internal data mismatch"); + test_assert(current_counter == mfs1.current_counter, "internal data mismatch"); + test_assert(used_space == mfs1.used_space, "internal data mismatch"); + } + test_end_step(8); + + /* [2.1.9] Performing a garbage collection, the result must not + change.*/ + test_set_step(9); + { + mfs_error_t err; + + err = mfsPerformGarbageCollection(&mfs1); + test_assert(err == MFS_NO_ERROR, "garbage collection failed"); + } + test_end_step(9); + + /* [2.1.10] Testing outcome again after garbage collection.*/ + test_set_step(10); + { + mfs_error_t err; + size_t size; + + /* Record 1 must not be present.*/ + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); + test_assert(err == MFS_ERR_NOT_FOUND, "record found"); + + /* Record 2 must contain the new value.*/ + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 2, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + test_assert(size == sizeof mfs_pattern32, "unexpected record length"); + test_assert(memcmp(mfs_pattern32, mfs_buffer, size) == 0, "wrong record content"); + + /* Record 3 must be unchanged.*/ + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 3, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + test_assert(size == sizeof mfs_pattern16, "unexpected record length"); + test_assert(memcmp(mfs_pattern16, mfs_buffer, size) == 0, "wrong record content"); + + /* Checking internal data.*/ + test_assert(MFS_BANK_1 == mfs1.current_bank, "internal data mismatch"); + test_assert(current_counter == mfs1.current_counter - 1, "internal data mismatch"); + test_assert(used_space == mfs1.used_space, "internal data mismatch"); } + test_end_step(10); } static const testcase_t mfs_test_002_001 = { - "Initialization error from APIs", - NULL, - NULL, + "Committing a transaction", + mfs_test_002_001_setup, + mfs_test_002_001_teardown, mfs_test_002_001_execute }; /** - * @page mfs_test_002_002 [2.2] Erasing non existing record + * @page mfs_test_002_002 [2.2] Rolling back a transaction * *

Description

- * An erase operation is attempted on an non-existing record. + * A set of new/existing records are written/erased within a + * transaction then the transaction is rolled back, the state is + * checked afterward. * *

Test Steps

- * - [2.2.1] Record one is erased, the error MFS_ERR_NOT_FOUND is - * expected. + * - [2.2.1] Records 1, 2 and 3 are created, MFS_NO_ERROR is expected. + * - [2.2.2] Presence of records 1, 2 and 3 is verified, MFS_NO_ERROR + * is expected. + * - [2.2.3] Starting a transaction with sufficient pre-allocated + * space, MFS_NO_ERROR is expected.. + * - [2.2.4] Atomically erasing record 1, updating record 2, reading + * record 3. + * - [2.2.5] Rolling back the transaction, MFS_NO_ERROR is expected. + * - [2.2.6] State must not have changed, records 1, 2 and 3 must still + * be there unchanged. * . */ static void mfs_test_002_002_setup(void) { + bank_erase(MFS_BANK_0); + bank_erase(MFS_BANK_1); mfsStart(&mfs1, &mfscfg1); - mfsErase(&mfs1); } static void mfs_test_002_002_teardown(void) { @@ -138,26 +326,249 @@ static void mfs_test_002_002_teardown(void) { } static void mfs_test_002_002_execute(void) { + uint32_t current_counter; + uint32_t used_space; - /* [2.2.1] Record one is erased, the error MFS_ERR_NOT_FOUND is + /* [2.2.1] Records 1, 2 and 3 are created, MFS_NO_ERROR is expected.*/ test_set_step(1); { mfs_error_t err; + err = mfsWriteRecord(&mfs1, 1, sizeof mfs_pattern16, mfs_pattern16); + test_assert(err == MFS_NO_ERROR, "error creating record 1"); + err = mfsWriteRecord(&mfs1, 2, sizeof mfs_pattern16, mfs_pattern16); + test_assert(err == MFS_NO_ERROR, "error creating record 2"); + err = mfsWriteRecord(&mfs1, 3, sizeof mfs_pattern16, mfs_pattern16); + test_assert(err == MFS_NO_ERROR, "error creating record 3"); + } + test_end_step(1); + + /* [2.2.2] Presence of records 1, 2 and 3 is verified, MFS_NO_ERROR + is expected.*/ + test_set_step(2); + { + mfs_error_t err; + size_t size; + + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 2, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 3, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + } + test_end_step(2); + + /* [2.2.3] Starting a transaction with sufficient pre-allocated + space, MFS_NO_ERROR is expected..*/ + test_set_step(3); + { + mfs_error_t err; + + err = mfsStartTransaction(&mfs1, 1024U); + test_assert(err == MFS_NO_ERROR, "error starting transaction"); + } + test_end_step(3); + + /* [2.2.4] Atomically erasing record 1, updating record 2, reading + record 3.*/ + test_set_step(4); + { + mfs_error_t err; + size_t size; + err = mfsEraseRecord(&mfs1, 1); - test_assert(err != MFS_NO_ERROR, "record was present"); - test_assert(err == MFS_ERR_NOT_FOUND, "invalid error code"); + test_assert(err == MFS_NO_ERROR, "error erasing record 1"); + err = mfsWriteRecord(&mfs1, 2, sizeof mfs_pattern32, mfs_pattern32); + test_assert(err == MFS_NO_ERROR, "error writing record 2"); + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 3, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + test_assert(size == sizeof mfs_pattern16, "unexpected record length"); + test_assert(memcmp(mfs_pattern16, mfs_buffer, size) == 0, "wrong record content"); + + /* Saving some internal state for successive checks.*/ + current_counter = mfs1.current_counter; + used_space = mfs1.used_space; + } + test_end_step(4); + + /* [2.2.5] Rolling back the transaction, MFS_NO_ERROR is expected.*/ + test_set_step(5); + { + mfs_error_t err; + + err = mfsRollbackTransaction(&mfs1); + test_assert(err == MFS_NO_ERROR, "error rolling back transaction"); + } + test_end_step(5); + + /* [2.2.6] State must not have changed, records 1, 2 and 3 must still + be there unchanged.*/ + test_set_step(6); + { + mfs_error_t err; + size_t size; + + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + test_assert(size == sizeof mfs_pattern16, "size changed"); + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 2, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + test_assert(size == sizeof mfs_pattern16, "size changed"); + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 3, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, "record not found"); + test_assert(size == sizeof mfs_pattern16, "size changed"); + + /* Checking internal data.*/ + test_assert(MFS_BANK_1 == mfs1.current_bank, "internal data mismatch"); + test_assert(current_counter == mfs1.current_counter - 1, "internal data mismatch"); + test_assert(used_space == mfs1.used_space, "internal data mismatch"); } + test_end_step(6); } static const testcase_t mfs_test_002_002 = { - "Erasing non existing record", + "Rolling back a transaction", mfs_test_002_002_setup, mfs_test_002_002_teardown, mfs_test_002_002_execute }; +/** + * @page mfs_test_002_003 [2.3] Transaction triggering an early garbage collect + * + *

Description

+ * A transaction is started with sufficient space but not contiguous, a + * garbage collection is triggered. + * + *

Test Steps

+ * - [2.3.1] Filling up the storage by writing records with increasing + * IDs, MFS_NO_ERROR is expected. + * - [2.3.2] Erasing one record, MFS_NO_ERROR is expected. + * - [2.3.3] Starting a transaction with the whole remaining space, + * MFS_ERR_OUT_OF_MEM is expected. + * - [2.3.4] Starting a transaction with insufficient space for one + * more header, MFS_ERR_OUT_OF_MEM is expected. + * - [2.3.5] Starting a transaction with just enough space for one more + * header, MFS_NO_ERROR is expected. + * - [2.3.6] Rolling back, MFS_NO_ERROR is expected. + * . + */ + +static void mfs_test_002_003_setup(void) { + bank_erase(MFS_BANK_0); + bank_erase(MFS_BANK_1); + mfsStart(&mfs1, &mfscfg1); +} + +static void mfs_test_002_003_teardown(void) { + mfsStop(&mfs1); +} + +static void mfs_test_002_003_execute(void) { + + /* [2.3.1] Filling up the storage by writing records with increasing + IDs, MFS_NO_ERROR is expected.*/ + test_set_step(1); + { + mfs_id_t id; + mfs_id_t id_max = (mfscfg1.bank_size - sizeof (mfs_bank_header_t)) / + (sizeof (mfs_data_header_t) + sizeof mfs_pattern512); + + for (id = 1; id <= id_max; id++) { + mfs_error_t err; + size_t size; + + err = mfsWriteRecord(&mfs1, id, sizeof mfs_pattern512, mfs_pattern512); + test_assert(err == MFS_NO_ERROR, "error creating the record"); + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, id, &size, mfs_buffer); + test_assert(err == MFS_NO_ERROR, + "record not found"); + test_assert(size == sizeof mfs_pattern512, + "unexpected record length"); + test_assert(memcmp(mfs_pattern512, mfs_buffer, size) == 0, + "wrong record content"); + } + } + test_end_step(1); + + /* [2.3.2] Erasing one record, MFS_NO_ERROR is expected.*/ + test_set_step(2); + { + mfs_error_t err; + size_t size; + + err = mfsEraseRecord(&mfs1, 1); + test_assert(err == MFS_NO_ERROR, "error erasing the record"); + size = sizeof mfs_buffer; + err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); + test_assert(err == MFS_ERR_NOT_FOUND, "record not erased"); + } + test_end_step(2); + + /* [2.3.3] Starting a transaction with the whole remaining space, + MFS_ERR_OUT_OF_MEM is expected.*/ + test_set_step(3); + { + mfs_error_t err; + size_t size = mfs1.config->bank_size - mfs1.used_space; + + err = mfsStartTransaction(&mfs1, size); + test_assert(err == MFS_ERR_OUT_OF_MEM, "invalid error code"); + } + test_end_step(3); + + /* [2.3.4] Starting a transaction with insufficient space for one + more header, MFS_ERR_OUT_OF_MEM is expected.*/ + test_set_step(4); + { + mfs_error_t err; + size_t size = ((mfs1.config->bank_size - mfs1.used_space) - sizeof (mfs_data_header_t)) + 1; + + err = mfsStartTransaction(&mfs1, size); + test_assert(err == MFS_ERR_OUT_OF_MEM, "invalid error code"); + } + test_end_step(4); + + /* [2.3.5] Starting a transaction with just enough space for one more + header, MFS_NO_ERROR is expected.*/ + test_set_step(5); + { + mfs_error_t err; + size_t size = (mfs1.config->bank_size - mfs1.used_space) - sizeof (mfs_data_header_t); + + err = mfsStartTransaction(&mfs1, size); + test_assert(err == MFS_NO_ERROR, "error starting transaction"); + } + test_end_step(5); + + /* [2.3.6] Rolling back, MFS_NO_ERROR is expected.*/ + test_set_step(6); + { + mfs_error_t err; + + err = mfsRollbackTransaction(&mfs1); + test_assert(err == MFS_NO_ERROR, "error rolling back transaction"); + } + test_end_step(6); +} + +static const testcase_t mfs_test_002_003 = { + "Transaction triggering an early garbage collect", + mfs_test_002_003_setup, + mfs_test_002_003_teardown, + mfs_test_002_003_execute +}; + /**************************************************************************** * Exported data. ****************************************************************************/ @@ -168,13 +579,14 @@ static const testcase_t mfs_test_002_002 = { const testcase_t * const mfs_test_sequence_002_array[] = { &mfs_test_002_001, &mfs_test_002_002, + &mfs_test_002_003, NULL }; /** - * @brief API Invalid Cases tests. + * @brief Transaction Mode tests. */ const testsequence_t mfs_test_sequence_002 = { - "API Invalid Cases tests", + "Transaction Mode tests", mfs_test_sequence_002_array }; diff --git a/test/mfs/source/test/mfs_test_sequence_003.c b/test/mfs/source/test/mfs_test_sequence_003.c new file mode 100644 index 000000000..e31f53bab --- /dev/null +++ b/test/mfs/source/test/mfs_test_sequence_003.c @@ -0,0 +1,186 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "hal.h" +#include "mfs_test_root.h" + +/** + * @file mfs_test_sequence_003.c + * @brief Test Sequence 003 code. + * + * @page mfs_test_sequence_003 [3] API Invalid Cases tests + * + * File: @ref mfs_test_sequence_003.c + * + *

Description

+ * This test sequence tests the error coded returned by the various + * APIs when called when the system is not initialized. + * + *

Test Cases

+ * - @subpage mfs_test_003_001 + * - @subpage mfs_test_003_002 + * . + */ + +/**************************************************************************** + * Shared code. + ****************************************************************************/ + +#include "hal_mfs.h" + +/**************************************************************************** + * Test cases. + ****************************************************************************/ + +/** + * @page mfs_test_003_001 [3.1] Initialization error from APIs + * + *

Description

+ * The API functions are invoked without prior initialization. + * + *

Test Steps

+ * - [3.1.1] The function mfsErase() is called, MFS_ERR_INV_STATE is + * expected. + * - [3.1.2] The function mfsWriteRecord() is called, MFS_ERR_INV_STATE + * is expected. + * - [3.1.3] The function mfsEraseRecord() is called, MFS_ERR_INV_STATE + * is expected. + * - [3.1.4] The function mfsReadRecord() is called, MFS_ERR_INV_STATE + * is expected. + * - [3.1.5] The function mfsPerformGarbageCollection() is called, + * MFS_ERR_INV_STATE is expected. + * . + */ + +static void mfs_test_003_001_execute(void) { + + /* [3.1.1] The function mfsErase() is called, MFS_ERR_INV_STATE is + expected.*/ + test_set_step(1); + { + mfs_error_t err = mfsErase(&mfs1); + test_assert(err == MFS_ERR_INV_STATE, "mfsErase() returned wrong status"); + } + test_end_step(1); + + /* [3.1.2] The function mfsWriteRecord() is called, MFS_ERR_INV_STATE + is expected.*/ + test_set_step(2); + { + mfs_error_t err = mfsWriteRecord(&mfs1, 1, 16, mfs_buffer); + test_assert(err == MFS_ERR_INV_STATE, "mfsWriteRecord() returned wrong status"); + } + test_end_step(2); + + /* [3.1.3] The function mfsEraseRecord() is called, MFS_ERR_INV_STATE + is expected.*/ + test_set_step(3); + { + mfs_error_t err = mfsEraseRecord(&mfs1, 1); + test_assert(err == MFS_ERR_INV_STATE, "mfsEraseRecord() returned wrong status"); + } + test_end_step(3); + + /* [3.1.4] The function mfsReadRecord() is called, MFS_ERR_INV_STATE + is expected.*/ + test_set_step(4); + { + size_t size = sizeof mfs_buffer; + mfs_error_t err = mfsReadRecord(&mfs1, 1, &size, mfs_buffer); + test_assert(err == MFS_ERR_INV_STATE, "mfsReadRecord() returned wrong status"); + } + test_end_step(4); + + /* [3.1.5] The function mfsPerformGarbageCollection() is called, + MFS_ERR_INV_STATE is expected.*/ + test_set_step(5); + { + mfs_error_t err = mfsPerformGarbageCollection(&mfs1); + test_assert(err == MFS_ERR_INV_STATE, "mfsPerformGarbageCollection() returned wrong status"); + } + test_end_step(5); +} + +static const testcase_t mfs_test_003_001 = { + "Initialization error from APIs", + NULL, + NULL, + mfs_test_003_001_execute +}; + +/** + * @page mfs_test_003_002 [3.2] Erasing non existing record + * + *

Description

+ * An erase operation is attempted on an non-existing record. + * + *

Test Steps

+ * - [3.2.1] Record one is erased, the error MFS_ERR_NOT_FOUND is + * expected. + * . + */ + +static void mfs_test_003_002_setup(void) { + mfsStart(&mfs1, &mfscfg1); + mfsErase(&mfs1); +} + +static void mfs_test_003_002_teardown(void) { + mfsStop(&mfs1); +} + +static void mfs_test_003_002_execute(void) { + + /* [3.2.1] Record one is erased, the error MFS_ERR_NOT_FOUND is + expected.*/ + test_set_step(1); + { + mfs_error_t err; + + err = mfsEraseRecord(&mfs1, 1); + test_assert(err != MFS_NO_ERROR, "record was present"); + test_assert(err == MFS_ERR_NOT_FOUND, "invalid error code"); + } + test_end_step(1); +} + +static const testcase_t mfs_test_003_002 = { + "Erasing non existing record", + mfs_test_003_002_setup, + mfs_test_003_002_teardown, + mfs_test_003_002_execute +}; + +/**************************************************************************** + * Exported data. + ****************************************************************************/ + +/** + * @brief Array of test cases. + */ +const testcase_t * const mfs_test_sequence_003_array[] = { + &mfs_test_003_001, + &mfs_test_003_002, + NULL +}; + +/** + * @brief API Invalid Cases tests. + */ +const testsequence_t mfs_test_sequence_003 = { + "API Invalid Cases tests", + mfs_test_sequence_003_array +}; diff --git a/test/mfs/source/test/mfs_test_sequence_003.h b/test/mfs/source/test/mfs_test_sequence_003.h new file mode 100644 index 000000000..336f8bff6 --- /dev/null +++ b/test/mfs/source/test/mfs_test_sequence_003.h @@ -0,0 +1,27 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file mfs_test_sequence_003.h + * @brief Test Sequence 003 header. + */ + +#ifndef MFS_TEST_SEQUENCE_003_H +#define MFS_TEST_SEQUENCE_003_H + +extern const testsequence_t mfs_test_sequence_003; + +#endif /* MFS_TEST_SEQUENCE_003_H */ diff --git a/tools/ftl/processors/unittest/test/test_sequence.c.ftl b/tools/ftl/processors/unittest/test/test_sequence.c.ftl index b6217a55d..abbbc174f 100755 --- a/tools/ftl/processors/unittest/test/test_sequence.c.ftl +++ b/tools/ftl/processors/unittest/test/test_sequence.c.ftl @@ -179,6 +179,7 @@ static void ${prefix_lower}test_${(sequence_index + 1)?string("000")}_${(case_in [@utils.EmitIndentedCCode " " 2 step.code.value[0] /] [/#if] } + test_end_step(${(step_index + 1)?string}); [/#list] } -- cgit v1.2.3