mirror of https://github.com/ARMmbed/mbed-os.git
This commit includes the following CFSTORE/Flash-Journal/Storage updates and fixes:
- flash-journal basicAPI fix for ARM toolchain - Updated storage-abstraction with version 0.4.7 (commit c7c4a8c52298bbc006a6f53a059fb2599cad73cc). - https://github.com/ARMmbed/storage-volume-manager at version v0.2.10. - https://github.com/ARMmbed/mtd-k64f v0.4.2 version of flash.c (imported as storage_driver.c). - update to CFSTORE to use the storage-volume-manager API to initialize volume manager and add a volume for CFSTORE to use. - https://github.com/ARMmbed/flash-journal at version v0.5.3 (commit 4c58165e2fa02c6ed2b9d166a9c96967e81f458f) including readFrom() support. - Taking flash-journal-strategy-sequential v0.6.7 strategy.c (commit b11a718761aa9f33679956968a21aaef9179bde1). - GCC_ARM, ARM and IAR compiler warning fixes for new versions of flash-journal code. - Fix storage-volume-manager test cases for concurrent access from 2 volumes to use addresses within the 512-1024kB address range, which is within the cfstore added volume. - Fix cfstore/storage-volume-manager IAR warnings when building with verbose flag.pull/2701/head
parent
24e1218da7
commit
5e22db842f
|
@ -32,8 +32,8 @@
|
|||
|
||||
using namespace utest::v1;
|
||||
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0);
|
||||
ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0);
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F;
|
||||
ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F;
|
||||
|
||||
/* temporary buffer to hold data for testing. */
|
||||
static const unsigned BUFFER_SIZE = 16384;
|
||||
|
@ -112,6 +112,7 @@ void test_getInfo()
|
|||
|
||||
TEST_ASSERT_EQUAL(0, info.security.reserved1);
|
||||
TEST_ASSERT_EQUAL(0, info.security.reserved2);
|
||||
TEST_ASSERT((info.program_cycles == ARM_STORAGE_PROGRAM_CYCLES_INFINITE) || (info.program_cycles > 0));
|
||||
TEST_ASSERT(info.total_storage > 0);
|
||||
}
|
||||
|
||||
|
@ -373,7 +374,7 @@ control_t test_programDataUsingProgramUnit(const size_t call_count)
|
|||
return (call_count < REPEAT_INSTANCES) ? CaseTimeout(200) + CaseRepeatAll: CaseTimeout(200);
|
||||
} else {
|
||||
TEST_ASSERT_EQUAL(firstBlock.attributes.erase_unit, rc);
|
||||
verifyBytePattern(addr, firstBlock.attributes.erase_unit, (uint8_t)0xFF);
|
||||
verifyBytePattern(addr, firstBlock.attributes.erase_unit, info.erased_value ? (uint8_t)0xFF : (uint8_t)0);
|
||||
|
||||
static const uint32_t BYTE_PATTERN = 0xAA551122;
|
||||
size_t sizeofData = info.program_unit;
|
||||
|
@ -495,7 +496,7 @@ control_t test_programDataUsingOptimalProgramUnit(const size_t call_count)
|
|||
return (call_count < REPEAT_INSTANCES) ? CaseTimeout(200) + CaseRepeatAll: CaseTimeout(200);
|
||||
} else {
|
||||
TEST_ASSERT_EQUAL(firstBlock.attributes.erase_unit, rc);
|
||||
verifyBytePattern(addr, firstBlock.attributes.erase_unit, (uint8_t)0xFF);
|
||||
verifyBytePattern(addr, firstBlock.attributes.erase_unit, info.erased_value ? (uint8_t)0xFF : (uint8_t)0);
|
||||
|
||||
static const uint8_t BYTE_PATTERN = 0xAA;
|
||||
size_t sizeofData = info.optimal_program_unit;
|
||||
|
@ -579,10 +580,12 @@ void eraseCompleteCallback(int32_t status, ARM_STORAGE_OPERATION operation)
|
|||
const uint64_t addr = firstBlock.addr + eraseIteration * ERASE_UNITS_PER_ITERATION * firstBlock.attributes.erase_unit;
|
||||
++eraseIteration;
|
||||
|
||||
#ifndef __CC_ARM
|
||||
printf("testing erased sector at addr %lu\n", (uint32_t)addr);
|
||||
#endif
|
||||
verifyBytePattern(addr, ERASE_UNITS_PER_ITERATION * firstBlock.attributes.erase_unit, (uint8_t)0xFF);
|
||||
ARM_STORAGE_INFO info;
|
||||
int32_t rc = drv->GetInfo(&info);
|
||||
TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc);
|
||||
|
||||
//printf("testing erased sector at addr %lu", (uint32_t)addr);
|
||||
verifyBytePattern(addr, ERASE_UNITS_PER_ITERATION * firstBlock.attributes.erase_unit, info.erased_value ? (uint8_t)0xFF : (uint8_t)0);
|
||||
|
||||
Harness::validate_callback();
|
||||
}
|
||||
|
@ -628,6 +631,10 @@ control_t test_erase(const size_t call_count)
|
|||
} else {
|
||||
TEST_ASSERT_EQUAL(ERASE_UNITS_PER_ITERATION * firstBlock.attributes.erase_unit, rc);
|
||||
|
||||
ARM_STORAGE_INFO info;
|
||||
rc = drv->GetInfo(&info);
|
||||
TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc);
|
||||
|
||||
/* test that the actual sector has been erased */
|
||||
printf("testing erased sector at addr %lu\n", (uint32_t)addr);
|
||||
verifyBytePattern(addr, ERASE_UNITS_PER_ITERATION * firstBlock.attributes.erase_unit, (uint8_t)0xFF);
|
||||
|
@ -708,8 +715,12 @@ control_t test_eraseAll(const size_t call_count)
|
|||
unsigned index = 0;
|
||||
static const unsigned MAX_VERIFY_ITERATIONS = 5;
|
||||
while ((index < MAX_VERIFY_ITERATIONS) && (addr < (firstBlock.addr + firstBlock.size))) {
|
||||
printf("testing erased chip at addr %lu\n", (uint32_t)addr);
|
||||
verifyBytePattern(addr, firstBlock.attributes.erase_unit, (uint8_t)0xFF);
|
||||
//printf("testing erased chip at addr %lu", (uint32_t)addr);
|
||||
ARM_STORAGE_INFO info;
|
||||
rc = drv->GetInfo(&info);
|
||||
TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc);
|
||||
|
||||
verifyBytePattern(addr, firstBlock.attributes.erase_unit, info.erased_value ? (uint8_t)0xFF : (uint8_t)0);
|
||||
|
||||
index++;
|
||||
addr += firstBlock.attributes.erase_unit;
|
||||
|
|
|
@ -279,7 +279,7 @@ control_t cfstore_add_del_test_04(const size_t call_count)
|
|||
return CaseNext;
|
||||
}
|
||||
|
||||
/** @brief Delete and attribute after an internal realloc of the cfstore memory area
|
||||
/** @brief Delete an attribute after an internal realloc of the cfstore memory area
|
||||
*
|
||||
* This test case goes through the following steps:
|
||||
* 1. Creates attribute att_1 of size x, and write some data. This causes an internal
|
||||
|
|
|
@ -234,7 +234,6 @@ typedef enum cfstore_ex_state_t {
|
|||
CFSTORE_EX_STATE_UNINIT_DONE
|
||||
} cfstore_ex_state_t;
|
||||
|
||||
|
||||
typedef struct cfstore_example1_ctx_t
|
||||
{
|
||||
ARM_CFSTORE_CAPABILITIES caps;
|
||||
|
@ -263,7 +262,87 @@ static void cfstore_ex_fms_update(cfstore_example1_ctx_t* ctx);
|
|||
/// @endcond
|
||||
|
||||
|
||||
/* @brief test startup code to reset flash
|
||||
#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED
|
||||
|
||||
#define CFSTORE_FLASH_START_FORMAT (FLASH_JOURNAL_OPCODE_RESET + 0x00010000)
|
||||
|
||||
typedef enum cfstore_ex_flash_state_t {
|
||||
CFSTORE_EX_FLASH_STATE_STARTING = 1,
|
||||
CFSTORE_EX_FLASH_STATE_FORMATTING,
|
||||
CFSTORE_EX_FLASH_STATE_INITIALIZING,
|
||||
CFSTORE_EX_FLASH_STATE_RESETTING,
|
||||
CFSTORE_EX_FLASH_STATE_READY,
|
||||
} cfstore_ex_flash_state_t;
|
||||
|
||||
typedef struct cfstore_example1_flash_ctx_t
|
||||
{
|
||||
volatile cfstore_ex_flash_state_t state;
|
||||
} cfstore_example1_flash_ctx_t;
|
||||
|
||||
static cfstore_example1_flash_ctx_t cfstore_example1_flash_ctx_g;
|
||||
|
||||
|
||||
static void cfstore_ex_flash_journal_callback(int32_t status, FlashJournal_OpCode_t cmd_code)
|
||||
{
|
||||
static FlashJournal_t jrnl;
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F;
|
||||
const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F;
|
||||
|
||||
|
||||
if(cmd_code == (FlashJournal_OpCode_t) CFSTORE_FLASH_START_FORMAT) {
|
||||
CFSTORE_EX1_LOG("FORMATTING%s", "\n");
|
||||
status = flashJournalStrategySequential_format(drv, 4, cfstore_ex_flash_journal_callback);
|
||||
CFSTORE_EX1_TEST_ASSERT_MSG(status >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_format() failed (status=%d)\r\n", __func__, (int) status);
|
||||
if(status == 0) {
|
||||
/* async completion pending */
|
||||
return;
|
||||
}
|
||||
/* status > 0 implies operation completed synchronously
|
||||
* intentional fall through */
|
||||
}
|
||||
|
||||
switch(cmd_code)
|
||||
{
|
||||
case FLASH_JOURNAL_OPCODE_FORMAT:
|
||||
/* format done */
|
||||
CFSTORE_EX1_TEST_ASSERT_MSG(status > JOURNAL_STATUS_OK, "%s:Error: FlashJournal_format() failed (status=%d)\r\n", __func__, (int) status);
|
||||
cfstore_example1_flash_ctx_g.state = CFSTORE_EX_FLASH_STATE_INITIALIZING;
|
||||
|
||||
CFSTORE_EX1_LOG("FLASH INITIALIZING%s", "\n");
|
||||
status = FlashJournal_initialize(&jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, NULL);
|
||||
CFSTORE_EX1_TEST_ASSERT_MSG(status >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_initialize() failed (status=%d)\r\n", __func__, (int) status);
|
||||
if(status == 0) {
|
||||
/* async completion pending */
|
||||
break;
|
||||
}
|
||||
/* status > 0 implies operation completed synchronously
|
||||
* intentional fall through */
|
||||
case FLASH_JOURNAL_OPCODE_INITIALIZE:
|
||||
/* initialize done */
|
||||
CFSTORE_EX1_TEST_ASSERT_MSG(status > JOURNAL_STATUS_OK, "%s:Error: FlashJournal_initialize() failed (status=%d)\r\n", __func__, (int) status);
|
||||
cfstore_example1_flash_ctx_g.state = CFSTORE_EX_FLASH_STATE_RESETTING;
|
||||
|
||||
CFSTORE_EX1_LOG("FLASH RESETTING%s", "\n");
|
||||
status = FlashJournal_reset(&jrnl);
|
||||
CFSTORE_EX1_TEST_ASSERT_MSG(status >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_reset() failed (status=%d)\r\n", __func__, (int) status);
|
||||
/* intentional fall through */
|
||||
case FLASH_JOURNAL_OPCODE_RESET:
|
||||
/* reset done */
|
||||
CFSTORE_EX1_LOG("FLASH RESET DONE%s", "\n");
|
||||
cfstore_example1_flash_ctx_g.state = CFSTORE_EX_FLASH_STATE_READY;
|
||||
break;
|
||||
|
||||
default:
|
||||
CFSTORE_EX1_LOG("%s:Error: notification of unsupported cmd_code event (status=%d, cmd_code=%d)\n", __func__, (int) status, (int) cmd_code);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */
|
||||
|
||||
|
||||
|
||||
/* @brief test startup code to reset flash for testing purposes.
|
||||
*/
|
||||
static int32_t cfstore_test_startup(void)
|
||||
{
|
||||
|
@ -272,16 +351,13 @@ static int32_t cfstore_test_startup(void)
|
|||
|
||||
#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED
|
||||
|
||||
int32_t ret = ARM_DRIVER_ERROR;
|
||||
static FlashJournal_t jrnl;
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0);
|
||||
const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0);
|
||||
memset(&cfstore_example1_flash_ctx_g, 0, sizeof(cfstore_example1_flash_ctx_g));
|
||||
cfstore_example1_flash_ctx_g.state = CFSTORE_EX_FLASH_STATE_STARTING;
|
||||
cfstore_ex_flash_journal_callback(JOURNAL_STATUS_OK, (FlashJournal_OpCode_t) CFSTORE_FLASH_START_FORMAT);
|
||||
while(cfstore_example1_flash_ctx_g.state != CFSTORE_EX_FLASH_STATE_READY) {
|
||||
/* spin */
|
||||
}
|
||||
|
||||
ret = FlashJournal_initialize(&jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, NULL);
|
||||
CFSTORE_EX1_TEST_ASSERT_MSG(ret >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_initialize() failed (ret=%d)\r\n", __func__, (int) ret);
|
||||
|
||||
ret = FlashJournal_reset(&jrnl);
|
||||
CFSTORE_EX1_TEST_ASSERT_MSG(ret >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_reset() failed (ret=%d)\r\n", __func__, (int) ret);
|
||||
#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */
|
||||
|
||||
return ARM_DRIVER_OK;
|
||||
|
|
|
@ -268,7 +268,7 @@ static control_t cfstore_example3_app_start(const size_t call_count)
|
|||
ctx->hkey_prev = ctx->hkey_prev_buf;
|
||||
ctx->caps = cfstore_drv->GetCapabilities();
|
||||
CFSTORE_EX1_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, ctx->caps.asynchronous_ops);
|
||||
if(ctx->caps.asynchronous_ops == true){
|
||||
if(ctx->caps.asynchronous_ops == 1){
|
||||
/* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true
|
||||
* This means the test will conveniently pass when run in CI as part of async mode testing */
|
||||
CFSTORE_EX1_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n");
|
||||
|
|
|
@ -74,7 +74,7 @@ static control_t cfstore_example4_test_00(const size_t call_count)
|
|||
/* initialise the context */
|
||||
caps = gCfStoreDriver->GetCapabilities();
|
||||
CFSTORE_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, caps.asynchronous_ops);
|
||||
if(caps.asynchronous_ops == true){
|
||||
if(caps.asynchronous_ops == 1){
|
||||
/* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true
|
||||
* This means the test will conveniently pass when run in CI as part of async mode testing */
|
||||
CFSTORE_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n");
|
||||
|
|
|
@ -167,8 +167,8 @@ int32_t cfstore_test_startup(void)
|
|||
#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED
|
||||
int32_t ret = ARM_DRIVER_ERROR;
|
||||
static FlashJournal_t jrnl;
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0);
|
||||
const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0);
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F;
|
||||
const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F;
|
||||
|
||||
ret = FlashJournal_initialize(&jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, NULL);
|
||||
CFSTORE_EX5_TEST_ASSERT_MSG(ret >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_initialize() failed (ret=%d)\r\n", __func__, (int) ret);
|
||||
|
@ -278,7 +278,7 @@ static control_t cfstore_EXAMPLE5_app_start(const size_t call_count)
|
|||
ctx->hkey_prev = ctx->hkey_prev_buf;
|
||||
ctx->caps = cfstore_drv->GetCapabilities();
|
||||
CFSTORE_EX5_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, ctx->caps.asynchronous_ops);
|
||||
if(ctx->caps.asynchronous_ops == true){
|
||||
if(ctx->caps.asynchronous_ops){
|
||||
/* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true
|
||||
* This means the test will conveniently pass when run in CI as part of async mode testing */
|
||||
CFSTORE_EX5_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n");
|
||||
|
|
|
@ -88,7 +88,8 @@ char cfstore_flash_utest_msg_g[CFSTORE_FLASH_UTEST_MSG_BUF_SIZE];
|
|||
|
||||
#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED
|
||||
uint16_t cfstore_flash_mtd_async_ops_g = 0;
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0);
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F;
|
||||
|
||||
|
||||
/* KV data for test_01 */
|
||||
static cfstore_kv_data_t cfstore_flush_test_01_kv_data[] = {
|
||||
|
@ -279,7 +280,7 @@ void cfstore_flash_test_01_callback(int32_t status, FlashJournal_OpCode_t cmd_co
|
|||
static void cfstore_flash_fsm_init_on_entry(void* context)
|
||||
{
|
||||
/* round up cfstore_flash_data_blob_t to nearest k64f program unit size */
|
||||
const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0);
|
||||
const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F;
|
||||
FlashJournal_Info_t info;
|
||||
FlashJournal_Status_t status = JOURNAL_STATUS_ERROR;
|
||||
cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context;
|
||||
|
|
|
@ -844,7 +844,7 @@ static control_t cfstore_flush3_test_00(const size_t call_count)
|
|||
/* initialise the context */
|
||||
caps = drv->GetCapabilities();
|
||||
CFSTORE_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, caps.asynchronous_ops);
|
||||
if(caps.asynchronous_ops == true){
|
||||
if(caps.asynchronous_ops == 1){
|
||||
/* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true
|
||||
* This means the test will conveniently pass when run in CI as part of async mode testing */
|
||||
CFSTORE_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n");
|
||||
|
|
|
@ -103,7 +103,7 @@ static control_t cfstore_init_app_start(const size_t call_count)
|
|||
memset(ctx, 0, sizeof(cfstore_init_ctx_t));
|
||||
ctx->caps = cfstore_drv->GetCapabilities();
|
||||
CFSTORE_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, ctx->caps.asynchronous_ops);
|
||||
if(ctx->caps.asynchronous_ops == true){
|
||||
if(ctx->caps.asynchronous_ops == 1){
|
||||
/* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true
|
||||
* This means the test will conveniently pass when run in CI as part of async mode testing */
|
||||
CFSTORE_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n");
|
||||
|
|
|
@ -272,7 +272,7 @@ control_t cfstore_misc_test_04_start(const size_t call_count)
|
|||
|
||||
status = drv->GetStatus();
|
||||
CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: GetStatus() before initialisation should have reported error, but reported no error.\r\n", __func__);
|
||||
TEST_ASSERT_MESSAGE(status.error == true, cfstore_misc_utest_msg_g);
|
||||
TEST_ASSERT_MESSAGE(status.error == 1, cfstore_misc_utest_msg_g);
|
||||
|
||||
ret = drv->Initialize(cfstore_utest_default_callback, NULL);
|
||||
CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialize CFSTORE (ret=%d)\n", __func__, (int) ret);
|
||||
|
@ -295,10 +295,10 @@ control_t cfstore_misc_test_04_end(const size_t call_count)
|
|||
|
||||
status = drv->GetStatus();
|
||||
CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: GetStatus() but reported error.\r\n", __func__);
|
||||
TEST_ASSERT_MESSAGE(status.error == false, cfstore_misc_utest_msg_g);
|
||||
TEST_ASSERT_MESSAGE(status.error == 0, cfstore_misc_utest_msg_g);
|
||||
|
||||
CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: GetStatus() reported operation in progress.\r\n", __func__);
|
||||
TEST_ASSERT_MESSAGE(status.in_progress == false, cfstore_misc_utest_msg_g);
|
||||
TEST_ASSERT_MESSAGE(status.in_progress == 0, cfstore_misc_utest_msg_g);
|
||||
|
||||
ret = drv->Uninitialize();
|
||||
CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__);
|
||||
|
|
|
@ -15,10 +15,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if !DEVICE_STORAGE
|
||||
#error [NOT_SUPPORTED] Storage not supported for this target
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_LIKE_POSIX
|
||||
#define AVOID_GREENTEA
|
||||
#endif
|
||||
|
@ -29,6 +25,7 @@
|
|||
#include "utest/utest.h"
|
||||
#include "unity/unity.h"
|
||||
|
||||
#include "flash-journal-strategy-sequential/flash_journal_crc.h"
|
||||
#include "flash-journal-strategy-sequential/flash_journal_strategy_sequential.h"
|
||||
#include "flash-journal-strategy-sequential/flash_journal_private.h"
|
||||
#include <string.h>
|
||||
|
@ -36,8 +33,8 @@
|
|||
|
||||
using namespace utest::v1;
|
||||
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0);
|
||||
ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0);
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F;
|
||||
const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F;
|
||||
|
||||
FlashJournal_t journal;
|
||||
|
||||
|
@ -54,32 +51,64 @@ void callbackHandler(int32_t status, FlashJournal_OpCode_t cmd_code)
|
|||
|
||||
switch (cmd_code) {
|
||||
case FLASH_JOURNAL_OPCODE_INITIALIZE:
|
||||
// printf("journal_callbackHandler: callback for init with status %" PRId32 "\n", status);
|
||||
//printf("journal_callbackHandler: callback for init with status %" PRId32 "\n", status);
|
||||
break;
|
||||
|
||||
case FLASH_JOURNAL_OPCODE_READ_BLOB:
|
||||
// printf("journal_callbackHandler: callback for read with status %" PRId32 "\n", status);
|
||||
//printf("journal_callbackHandler: callback for read with status %" PRId32 "\n", status);
|
||||
break;
|
||||
|
||||
case FLASH_JOURNAL_OPCODE_LOG_BLOB:
|
||||
// printf("journal_callbackHandler: callback for log with status %" PRId32 "\n", status);
|
||||
//printf("journal_callbackHandler: callback for log with status %" PRId32 "\n", status);
|
||||
break;
|
||||
|
||||
case FLASH_JOURNAL_OPCODE_COMMIT:
|
||||
// printf("journal_callbackHandler: callback for commit with status %" PRId32 "\n", status);
|
||||
//printf("journal_callbackHandler: callback for commit with status %" PRId32 "\n", status);
|
||||
break;
|
||||
|
||||
case FLASH_JOURNAL_OPCODE_RESET:
|
||||
// printf("journal_callbackHandler: callback for reset with status %" PRId32 "\n", status);
|
||||
//printf("journal_callbackHandler: callback for reset with status %" PRId32 "\n", status);
|
||||
break;
|
||||
|
||||
case FLASH_JOURNAL_OPCODE_FORMAT:
|
||||
//printf("journal_callbackHandler: callback for format with status %" PRId32 "\n", status);
|
||||
break;
|
||||
|
||||
default:
|
||||
// printf("journal_callbackHandler: callback for opcode %u with status %" PRId32 "\n", cmd_code, status);
|
||||
//printf("journal_callbackHandler: callback for opcode %u with status %" PRId32 "\n", cmd_code, status);
|
||||
break;
|
||||
}
|
||||
Harness::validate_callback(); // Validate the callback
|
||||
}
|
||||
|
||||
control_t test_format(const size_t call_count)
|
||||
{
|
||||
int32_t rc;
|
||||
//printf("test_format: entered with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
|
||||
ARM_STORAGE_INFO mtdInfo;
|
||||
rc = drv->GetInfo(&mtdInfo);
|
||||
TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc);
|
||||
TEST_ASSERT(mtdInfo.total_storage > 0);
|
||||
|
||||
if (call_count == 1) {
|
||||
rc = flashJournalStrategySequential_format(drv, 4 /* numSlots */, callbackHandler);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
return CaseTimeout(200) + CaseRepeatAll;
|
||||
}
|
||||
TEST_ASSERT_EQUAL(1, rc); /* synchronous completion is expected to return 1. */
|
||||
}
|
||||
|
||||
return CaseNext;
|
||||
}
|
||||
|
||||
void test_initializeBeforeCreate()
|
||||
{
|
||||
int32_t rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler);
|
||||
TEST_ASSERT((rc == 1) || (rc == JOURNAL_STATUS_NOT_FORMATTED));
|
||||
}
|
||||
|
||||
control_t test_initialize()
|
||||
{
|
||||
int32_t rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler);
|
||||
|
@ -115,7 +144,7 @@ control_t test_resetAndInitialize(const size_t call_count)
|
|||
NEEDS_VERIFICATION_FOLLOWING_INITIALIZE,
|
||||
} state;
|
||||
|
||||
printf("test_resetAndInitialize: entered with call_count %u\n", call_count);
|
||||
//printf("test_resetAndInitialize: entered with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
if (call_count == 1) {
|
||||
state = NEEDS_INITIAL_RESET;
|
||||
}
|
||||
|
@ -127,7 +156,7 @@ control_t test_resetAndInitialize(const size_t call_count)
|
|||
TEST_ASSERT(info.capacity > 0);
|
||||
previousCapacity = info.capacity;
|
||||
|
||||
printf("test_resetAndInitialize: calling reset()\n");
|
||||
//printf("test_resetAndInitialize: calling reset()\n");
|
||||
rc = FlashJournal_reset(&journal);
|
||||
TEST_ASSERT_NOT_EQUAL(JOURNAL_STATUS_UNSUPPORTED, rc);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
|
@ -152,7 +181,7 @@ control_t test_resetAndInitialize(const size_t call_count)
|
|||
TEST_ASSERT_EQUAL(0, info.sizeofJournaledBlob);
|
||||
|
||||
/* attempt an initialize following reset() */
|
||||
printf("test_resetAndInitialize: calling initialize() after reset\n");
|
||||
//printf("test_resetAndInitialize: calling initialize() after reset\n");
|
||||
rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
state = NEEDS_VERIFICATION_FOLLOWING_INITIALIZE;
|
||||
|
@ -164,7 +193,7 @@ control_t test_resetAndInitialize(const size_t call_count)
|
|||
/* fall through */
|
||||
case NEEDS_VERIFICATION_FOLLOWING_INITIALIZE:
|
||||
default:
|
||||
printf("test_resetAndInitialize: verification\n");
|
||||
//printf("test_resetAndInitialize: verification\n");
|
||||
TEST_ASSERT_EQUAL(0, sequentialJournal->nextSequenceNumber);
|
||||
TEST_ASSERT_EQUAL((uint32_t)-1, sequentialJournal->currentBlobIndex);
|
||||
TEST_ASSERT_EQUAL(SEQUENTIAL_JOURNAL_STATE_INITIALIZED, sequentialJournal->state);
|
||||
|
@ -184,7 +213,7 @@ control_t test_commitWithoutLogs(const size_t call_count)
|
|||
{
|
||||
int32_t rc;
|
||||
|
||||
printf("test_commitWithoutLogs: entered with call_count %u\n", call_count);
|
||||
//printf("test_commitWithoutLogs: entered with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
|
||||
switch (call_count) {
|
||||
case 1:
|
||||
|
@ -199,7 +228,7 @@ control_t test_commitWithoutLogs(const size_t call_count)
|
|||
|
||||
case 2:
|
||||
rc = FlashJournal_commit(&journal);
|
||||
// printf("commit returned %" PRId32 "\r\n", rc);
|
||||
//printf("commit returned %" PRId32 "\r\n", rc);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops);
|
||||
|
@ -221,7 +250,7 @@ control_t test_logSmallWithoutCommit(const size_t call_count)
|
|||
{
|
||||
int32_t rc;
|
||||
|
||||
printf("test_logSmallWithoutCommit: entered with call_count %u\n", call_count);
|
||||
//printf("test_logSmallWithoutCommit: entered with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
|
||||
switch (call_count) {
|
||||
case 1:
|
||||
|
@ -257,7 +286,7 @@ control_t test_logSmallAndCommit(const size_t call_count)
|
|||
{
|
||||
int32_t rc;
|
||||
|
||||
printf("test_logSmallAndCommit: entered with call_count %u\n", call_count);
|
||||
//printf("test_logSmallAndCommit: entered with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
|
||||
switch (call_count) {
|
||||
case 1:
|
||||
|
@ -311,13 +340,13 @@ control_t test_initializeAfterLogSmallAndCommit(const size_t call_count)
|
|||
{
|
||||
int32_t rc;
|
||||
|
||||
printf("test_initializeAfterLogSmallAndCommit: entered with call_count %u\n", call_count);
|
||||
//printf("test_initializeAfterLogSmallAndCommit: entered with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
|
||||
if (call_count == 1) {
|
||||
rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
printf("asynchronous_ops for init\n");
|
||||
//printf("asynchronous_ops for init\n");
|
||||
return CaseTimeout(200) + CaseRepeatAll;
|
||||
}
|
||||
TEST_ASSERT_EQUAL(1, rc); /* synchronous completion of initialize() is expected to return 1 */
|
||||
|
@ -335,7 +364,7 @@ control_t test_logLargeWithoutCommit(const size_t call_count)
|
|||
{
|
||||
int32_t rc;
|
||||
|
||||
printf("test_logLargeWithoutCommit: entered with call_count %u\n", call_count);
|
||||
//printf("test_logLargeWithoutCommit: entered with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
|
||||
switch (call_count) {
|
||||
case 1:
|
||||
|
@ -370,7 +399,7 @@ control_t test_logLargeAndCommit(const size_t call_count)
|
|||
{
|
||||
int32_t rc;
|
||||
|
||||
printf("test_logLargeAndCommit: entered with call_count %u\n", call_count);
|
||||
//printf("test_logLargeAndCommit: entered with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
|
||||
switch (call_count) {
|
||||
case 1:
|
||||
|
@ -424,13 +453,13 @@ control_t test_initializeAfterLogLargeAndCommit(const size_t call_count)
|
|||
{
|
||||
int32_t rc;
|
||||
|
||||
printf("test_initializeAfterLogLargeAndCommit: entered with call_count %u\n", call_count);
|
||||
//printf("test_initializeAfterLogLargeAndCommit: entered with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
|
||||
if (call_count == 1) {
|
||||
rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
printf("test_initializeAfterLogLargeAndCommit: asynchronous_ops for init\n");
|
||||
//printf("test_initializeAfterLogLargeAndCommit: asynchronous_ops for init\n");
|
||||
return CaseTimeout(200) + CaseRepeatAll;
|
||||
}
|
||||
TEST_ASSERT_EQUAL(1, rc); /* synchronous completion of initialize() is expected to return 1 */
|
||||
|
@ -449,7 +478,7 @@ control_t test_logLargeAndReadSmallChunks(const size_t call_count)
|
|||
{
|
||||
int32_t rc;
|
||||
|
||||
printf("test_logLargeAndReadSmallChunks: entered with call_count %u\n", call_count);
|
||||
//printf("test_logLargeAndReadSmallChunks: entered with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
|
||||
static const size_t SMALL_CHUNK_COUNT = 4;
|
||||
|
||||
|
@ -501,7 +530,7 @@ control_t test_logLargeAndReadSmallChunks(const size_t call_count)
|
|||
}
|
||||
|
||||
while ((rc = FlashJournal_read(&journal, buffer, SIZEOF_LARGE_WRITE / SMALL_CHUNK_COUNT)) != JOURNAL_STATUS_EMPTY) {
|
||||
// printf("read returned %ld\n", rc);
|
||||
// printf("read returned %" PRId32 "\n", rc);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops);
|
||||
|
@ -523,7 +552,7 @@ control_t test_readLargeInSmallOddChunks(const size_t call_count)
|
|||
{
|
||||
int32_t rc;
|
||||
|
||||
printf("test_readLargeInSmallOddChunks<0x%02x, %u>: entered with call_count %u\n", PATTERN, SIZEOF_READS, call_count);
|
||||
//printf("test_readLargeInSmallOddChunks<0x%02x, %" PRIu32 ">: entered with call_count %" PRIu32 "\n", PATTERN, (uint32_t)SIZEOF_READS, (uint32_t)call_count);
|
||||
|
||||
if (call_count == 1) {
|
||||
FlashJournal_Info_t info;
|
||||
|
@ -546,7 +575,7 @@ control_t test_readLargeInSmallOddChunks(const size_t call_count)
|
|||
}
|
||||
|
||||
while ((rc = FlashJournal_read(&journal, buffer, SIZEOF_READS)) != JOURNAL_STATUS_EMPTY) {
|
||||
// printf("read returned %ld\n", rc);
|
||||
// printf("read returned %" PRId32 "\n", rc);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops);
|
||||
|
@ -563,6 +592,205 @@ control_t test_readLargeInSmallOddChunks(const size_t call_count)
|
|||
return CaseNext;
|
||||
}
|
||||
|
||||
template<uint8_t PATTERN>
|
||||
control_t test_logPattern(size_t call_count)
|
||||
{
|
||||
int32_t rc = JOURNAL_STATUS_OK;
|
||||
|
||||
//printf("test_logpattern: entered with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
|
||||
switch (call_count) {
|
||||
case 1:
|
||||
for (unsigned index = 0; index < SIZEOF_LARGE_WRITE; index++) {
|
||||
buffer[index] = (uint8_t)(PATTERN ^ index);
|
||||
}
|
||||
rc = FlashJournal_log(&journal, buffer, SIZEOF_LARGE_WRITE);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops);
|
||||
return CaseTimeout(500) + CaseRepeatAll;
|
||||
}
|
||||
/* intentional fall-through */
|
||||
call_count = 2;
|
||||
|
||||
case 2:
|
||||
rc = FlashJournal_commit(&journal);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops);
|
||||
return CaseTimeout(500) + CaseRepeatAll;
|
||||
}
|
||||
callbackStatus = rc;
|
||||
/* intentional fall-through */
|
||||
call_count = 3;
|
||||
|
||||
case 3:
|
||||
{
|
||||
FlashJournal_Info_t info;
|
||||
rc = FlashJournal_getInfo(&journal, &info);
|
||||
TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc);
|
||||
TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE, info.sizeofJournaledBlob);
|
||||
}
|
||||
/* intentional fall-through */
|
||||
call_count = 4;
|
||||
|
||||
case 4:
|
||||
TEST_ASSERT_EQUAL(1, callbackStatus);
|
||||
|
||||
rc = FlashJournal_read(&journal, buffer, SIZEOF_LARGE_WRITE);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops);
|
||||
return CaseTimeout(500) + CaseRepeatAll;
|
||||
}
|
||||
callbackStatus = rc;
|
||||
/* intentional fall-through */
|
||||
call_count = 4;
|
||||
|
||||
case 5:
|
||||
TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE, rc);
|
||||
for (unsigned i = 0; i < SIZEOF_LARGE_WRITE; i++) {
|
||||
// printf("index %u value %x\n", i, buffer[i]);
|
||||
TEST_ASSERT_EQUAL((uint8_t)(PATTERN ^ i), buffer[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
TEST_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return CaseNext;
|
||||
}
|
||||
|
||||
template<uint8_t PATTERN, size_t SIZEOF_READS>
|
||||
control_t test_readFromInReverse(const size_t call_count)
|
||||
{
|
||||
int32_t rc;
|
||||
static size_t offset = SIZEOF_LARGE_WRITE;
|
||||
|
||||
//printf("test_readFrom<0x%02x, %" PRIu32 ">: entered with call_count %" PRIu32 "\n", PATTERN, (uint32_t)SIZEOF_READS, (uint32_t)call_count);
|
||||
|
||||
if (call_count == 1) {
|
||||
FlashJournal_Info_t info;
|
||||
rc = FlashJournal_getInfo(&journal, &info);
|
||||
TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc);
|
||||
TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE, info.sizeofJournaledBlob);
|
||||
TEST_ASSERT(SIZEOF_READS <= info.sizeofJournaledBlob);
|
||||
|
||||
rc = FlashJournal_readFrom(&journal, offset + 1, buffer, SIZEOF_READS);
|
||||
TEST_ASSERT_EQUAL(JOURNAL_STATUS_EMPTY, rc);
|
||||
rc = FlashJournal_readFrom(&journal, offset, buffer, SIZEOF_READS);
|
||||
TEST_ASSERT_EQUAL(JOURNAL_STATUS_EMPTY, rc);
|
||||
offset -= SIZEOF_READS;
|
||||
} else {
|
||||
if (drv->GetCapabilities().asynchronous_ops) {
|
||||
if (callbackStatus == 0) {
|
||||
return CaseNext; /* termination condition */
|
||||
}
|
||||
TEST_ASSERT_EQUAL(SIZEOF_READS, callbackStatus);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < SIZEOF_READS; i++) {
|
||||
// printf("index %u value %x\n", i, buffer[i]);
|
||||
TEST_ASSERT_EQUAL((uint8_t)(PATTERN ^ (offset + i)), buffer[i]);
|
||||
}
|
||||
if (offset == 0) {
|
||||
return CaseNext;
|
||||
}
|
||||
if (offset >= SIZEOF_READS) {
|
||||
offset -= SIZEOF_READS;
|
||||
} else {
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// printf("test_readFrom: issuing read at offset %lu\n", offset);
|
||||
while ((rc = FlashJournal_readFrom(&journal, offset, buffer, SIZEOF_READS)) != JOURNAL_STATUS_EMPTY) {
|
||||
// printf("read returned %" PRId32 "\n", rc);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops);
|
||||
return CaseTimeout(500) + CaseRepeatAll;
|
||||
}
|
||||
|
||||
TEST_ASSERT(rc <= (int32_t)SIZEOF_READS);
|
||||
for (unsigned i = 0; i < (unsigned)rc; i++) {
|
||||
// printf("index %u value %x\n", i, buffer[i]);
|
||||
TEST_ASSERT_EQUAL((uint8_t)(PATTERN ^ (offset + i)), buffer[i]);
|
||||
}
|
||||
if (offset == 0) {
|
||||
return CaseNext;
|
||||
}
|
||||
if (offset >= SIZEOF_READS) {
|
||||
offset -= SIZEOF_READS;
|
||||
} else {
|
||||
offset = 0;
|
||||
}
|
||||
// printf("test_readFrom: issuing read at offset %lu\n", offset);
|
||||
};
|
||||
|
||||
return CaseNext;
|
||||
}
|
||||
|
||||
template<uint8_t PATTERN, size_t SIZEOF_READS>
|
||||
control_t test_readFromFollowedByReads(size_t call_count)
|
||||
{
|
||||
//printf("test_readFrom<0x%02x, %" PRIu32 ">: entered with call_count %" PRIu32 "\n", PATTERN, (uint32_t)SIZEOF_READS, (uint32_t)call_count);
|
||||
|
||||
int32_t rc;
|
||||
static size_t offset = SIZEOF_LARGE_WRITE / 2;
|
||||
|
||||
FlashJournal_Info_t info;
|
||||
switch (call_count) {
|
||||
case 1:
|
||||
rc = FlashJournal_getInfo(&journal, &info);
|
||||
TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc);
|
||||
TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE, info.sizeofJournaledBlob);
|
||||
TEST_ASSERT(SIZEOF_READS <= (info.sizeofJournaledBlob - offset));
|
||||
|
||||
rc = FlashJournal_readFrom(&journal, offset, buffer, SIZEOF_READS);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops);
|
||||
return CaseTimeout(500) + CaseRepeatAll;
|
||||
}
|
||||
callbackStatus = rc;
|
||||
/* intentional fall-through */
|
||||
call_count = 2;
|
||||
|
||||
case 2:
|
||||
/* verify the previous readFrom */
|
||||
TEST_ASSERT_EQUAL(SIZEOF_READS, callbackStatus);
|
||||
for (unsigned i = 0; i < (unsigned)callbackStatus; i++) {
|
||||
// printf("index %u value %x\n", i, buffer[i]);
|
||||
TEST_ASSERT_EQUAL((uint8_t)(PATTERN ^ (offset + i)), buffer[i]);
|
||||
}
|
||||
|
||||
/* issue a sequential read to follow the previous readFrom */
|
||||
rc = FlashJournal_read(&journal, buffer, SIZEOF_READS);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops);
|
||||
return CaseTimeout(500) + CaseRepeatAll;
|
||||
}
|
||||
callbackStatus = rc;
|
||||
/* intentional fall-through */
|
||||
call_count = 3;
|
||||
|
||||
case 3:
|
||||
TEST_ASSERT_EQUAL(SIZEOF_READS, callbackStatus);
|
||||
|
||||
for (unsigned i = 0; i < (unsigned)callbackStatus; i++) {
|
||||
// printf("index %u value %x\n", i, buffer[i]);
|
||||
TEST_ASSERT_EQUAL((uint8_t)(PATTERN ^ (offset + SIZEOF_READS + i)), buffer[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return CaseNext;
|
||||
}
|
||||
|
||||
template<size_t SIZEOF_ODD_CHUNK, size_t N_WRITES>
|
||||
control_t test_logSeveralOddSizedChunks(size_t call_count)
|
||||
{
|
||||
|
@ -573,7 +801,7 @@ control_t test_logSeveralOddSizedChunks(size_t call_count)
|
|||
static const uint8_t PATTERN = 0xAA;
|
||||
static size_t totalDataLogged = 0;
|
||||
|
||||
printf("test_logSeveralOddSizedChunks<%u, %u>: entered with call_count %u\n", SIZEOF_ODD_CHUNK, N_WRITES, call_count);
|
||||
//printf("test_logSeveralOddSizedChunks<%" PRIu32 ", %" PRIu32 ">: entered with call_count %" PRIu32 "\n", (uint32_t)SIZEOF_ODD_CHUNK, (uint32_t)N_WRITES, (uint32_t)call_count);
|
||||
TEST_ASSERT(SIZEOF_ODD_CHUNK <= BUFFER_SIZE);
|
||||
|
||||
/* check the status of the previous asynchronous operation */
|
||||
|
@ -584,7 +812,7 @@ control_t test_logSeveralOddSizedChunks(size_t call_count)
|
|||
rc = FlashJournal_getInfo(&journal, &info);
|
||||
TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc);
|
||||
TEST_ASSERT(SIZEOF_ODD_CHUNK < info.program_unit);
|
||||
printf("test_logSeveralOddSizedChunks: RETURNING CaseNext\n");
|
||||
//printf("test_logSeveralOddSizedChunks: RETURNING CaseNext\n");
|
||||
return CaseNext;
|
||||
}
|
||||
|
||||
|
@ -600,7 +828,7 @@ control_t test_logSeveralOddSizedChunks(size_t call_count)
|
|||
}
|
||||
|
||||
while (call_count <= N_WRITES) {
|
||||
printf("test_logSeveralOddSizedChunks: iteration with call_count %u\n", call_count);
|
||||
//printf("test_logSeveralOddSizedChunks: iteration with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
memset(buffer, PATTERN, SIZEOF_ODD_CHUNK);
|
||||
rc = FlashJournal_log(&journal, buffer, SIZEOF_ODD_CHUNK);
|
||||
// printf("test_logSeveralOddSizedChunks: called FlashJournal_log(): rc = %" PRId32 "\n", rc);
|
||||
|
@ -663,13 +891,13 @@ control_t test_multipleWritesFollowedByCommitFollowedByMultipleReads(const size_
|
|||
static const size_t SIZEOF_WRITE = BUFFER_SIZE / N_WRITES;
|
||||
static const size_t SIZEOF_READ = BUFFER_SIZE / N_READS;
|
||||
|
||||
printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: entered with call_count %u\n", call_count);
|
||||
//printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: entered with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
|
||||
if (call_count <= N_WRITES) {
|
||||
printf("writing pattern %02x\n", PATTERN ^ call_count);
|
||||
//printf("writing pattern %02" PRIx8 "\n", (uint8_t)(PATTERN ^ call_count));
|
||||
memset(buffer, (PATTERN ^ call_count), SIZEOF_WRITE);
|
||||
rc = FlashJournal_log(&journal, buffer, SIZEOF_WRITE);
|
||||
// printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: log returned %" PRId32 "\n", rc);
|
||||
//printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: log returned %" PRId32 "\n", rc);
|
||||
TEST_ASSERT(rc >= JOURNAL_STATUS_OK);
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops);
|
||||
|
@ -684,12 +912,12 @@ control_t test_multipleWritesFollowedByCommitFollowedByMultipleReads(const size_
|
|||
TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops);
|
||||
return CaseTimeout(500) + CaseRepeatAll;
|
||||
}
|
||||
// printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: commit returned %" PRId32 "\n", rc);
|
||||
//printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: commit returned %" PRId32 "\n", rc);
|
||||
callbackStatus = rc; /* pass forward the return value so that the next iteration can check callbackStatus */
|
||||
return CaseRepeatAll;
|
||||
} else if (call_count < (N_WRITES + 1 + N_READS + 1)) {
|
||||
unsigned readIteration = call_count - (N_WRITES + 1);
|
||||
printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: read iteration %u\n", readIteration);
|
||||
//printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: read iteration %u\n", readIteration);
|
||||
if (call_count == (N_WRITES + 1 /* commit */ + 1 /* first iteration after commit */)) {
|
||||
TEST_ASSERT_EQUAL(1, callbackStatus);
|
||||
|
||||
|
@ -706,7 +934,7 @@ control_t test_multipleWritesFollowedByCommitFollowedByMultipleReads(const size_
|
|||
}
|
||||
|
||||
while ((rc = FlashJournal_read(&journal, buffer, SIZEOF_READ)) != JOURNAL_STATUS_EMPTY) {
|
||||
// printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: read returned %ld\n", rc);
|
||||
// printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: read returned %" PRId32 "\n", rc);
|
||||
TEST_ASSERT((rc == JOURNAL_STATUS_OK) || (rc == SIZEOF_READ));
|
||||
if (rc == JOURNAL_STATUS_OK) {
|
||||
TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops);
|
||||
|
@ -714,7 +942,7 @@ control_t test_multipleWritesFollowedByCommitFollowedByMultipleReads(const size_
|
|||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(SIZEOF_READ, rc);
|
||||
printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: checking for pattern %02x\n", PATTERN ^ readIteration);
|
||||
//printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: checking for pattern %02x\n", PATTERN ^ readIteration);
|
||||
for (unsigned i = 0; i < SIZEOF_READ; i++) {
|
||||
// printf("index %u value %x\n", i, buffer[i]);
|
||||
TEST_ASSERT_EQUAL(PATTERN ^ readIteration, buffer[i]);
|
||||
|
@ -733,7 +961,7 @@ control_t test_failedSmallWriteFollowedByPaddedWrite(const size_t call_count)
|
|||
|
||||
static const uint8_t PATTERN = 0xAA;
|
||||
|
||||
printf("test_failedSmallWriteFollowedByPaddedWrite: entered with call_count %u\n", call_count);
|
||||
//printf("test_failedSmallWriteFollowedByPaddedWrite: entered with call_count %" PRIu32 "\n", (uint32_t)call_count);
|
||||
|
||||
FlashJournal_Info_t info;
|
||||
rc = FlashJournal_getInfo(&journal, &info);
|
||||
|
@ -799,9 +1027,35 @@ control_t test_failedSmallWriteFollowedByPaddedWrite(const size_t call_count)
|
|||
return CaseNext;
|
||||
}
|
||||
|
||||
void test_crc32()
|
||||
{
|
||||
const unsigned char dummyMsg[] = "ahello world";
|
||||
flashJournalCrcReset(); TEST_ASSERT_EQUAL(0xe8b7be43, flashJournalCrcCummulative(dummyMsg, 1));
|
||||
flashJournalCrcReset(); TEST_ASSERT_EQUAL(0x7e56a173, flashJournalCrcCummulative(dummyMsg, 2));
|
||||
flashJournalCrcReset(); TEST_ASSERT_EQUAL(0x26a80c7d, flashJournalCrcCummulative(dummyMsg, 3));
|
||||
flashJournalCrcReset(); TEST_ASSERT_EQUAL(0xb8946773, flashJournalCrcCummulative(dummyMsg, 4));
|
||||
flashJournalCrcReset(); TEST_ASSERT_EQUAL(0x5fb2761f, flashJournalCrcCummulative(dummyMsg, 5));
|
||||
flashJournalCrcReset(); TEST_ASSERT_EQUAL(0x82582cc7, flashJournalCrcCummulative(dummyMsg, 6));
|
||||
flashJournalCrcReset(); TEST_ASSERT_EQUAL(0xeceec07a, flashJournalCrcCummulative(dummyMsg, 7));
|
||||
flashJournalCrcReset(); TEST_ASSERT_EQUAL(0xac5f7df0, flashJournalCrcCummulative(dummyMsg, 8));
|
||||
flashJournalCrcReset(); TEST_ASSERT_EQUAL(0xb21e3e25, flashJournalCrcCummulative(dummyMsg, 9));
|
||||
flashJournalCrcReset(); TEST_ASSERT_EQUAL(0x27bf35e4, flashJournalCrcCummulative(dummyMsg, 10));
|
||||
flashJournalCrcReset(); TEST_ASSERT_EQUAL(0x31465baa, flashJournalCrcCummulative(dummyMsg, 11));
|
||||
flashJournalCrcReset(); TEST_ASSERT_EQUAL(0xaeef4661, flashJournalCrcCummulative(dummyMsg, 12));
|
||||
|
||||
/* check for composability */
|
||||
uint32_t crc;
|
||||
for (unsigned msgLen = 1; msgLen < strlen((const char *)dummyMsg); msgLen++) {
|
||||
for (unsigned partitionIndex = 1; partitionIndex < msgLen; partitionIndex++) {
|
||||
flashJournalCrcReset(); crc = flashJournalCrcCummulative(dummyMsg, partitionIndex); crc = flashJournalCrcCummulative(dummyMsg + partitionIndex, msgLen - partitionIndex);
|
||||
flashJournalCrcReset(); TEST_ASSERT_EQUAL(flashJournalCrcCummulative(dummyMsg, msgLen), crc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef AVOID_GREENTEA
|
||||
// Custom setup handler required for proper Greentea support
|
||||
utest::v1::status_t greentea_setup(const size_t number_of_cases)
|
||||
status_t greentea_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(60, "default_auto");
|
||||
// Call the default reporting function
|
||||
|
@ -816,6 +1070,9 @@ status_t default_setup(const size_t)
|
|||
|
||||
// Specify all your test cases here
|
||||
Case cases[] = {
|
||||
|
||||
Case("initializeBeforeCreate", test_initializeBeforeCreate),
|
||||
Case("format", test_format),
|
||||
Case("initialize", test_initialize),
|
||||
Case("reset and initialize1", test_resetAndInitialize),
|
||||
|
||||
|
@ -861,6 +1118,17 @@ Case cases[] = {
|
|||
Case("read large item in small, odd-sized chunks3", test_readLargeInSmallOddChunks<0xAA, 1021>),
|
||||
Case("read large item in small, odd-sized chunks4", test_readLargeInSmallOddChunks<0xAA, 2401>),
|
||||
|
||||
Case("log pattern", test_logPattern<0x55>),
|
||||
Case("readFrom", test_readFromInReverse<0x55, 255>),
|
||||
Case("readFrom", test_readFromInReverse<0x55, 512>),
|
||||
Case("readFrom", test_readFromInReverse<0x55, ((BUFFER_SIZE / 2) - 1)>),
|
||||
Case("readFrom", test_readFromInReverse<0x55, BUFFER_SIZE>),
|
||||
Case("readFrom followed by sequential reads", test_readFromFollowedByReads<0x55, 255>),
|
||||
Case("readFrom followed by sequential reads", test_readFromFollowedByReads<0x55, 511>),
|
||||
Case("readFrom followed by sequential reads", test_readFromFollowedByReads<0x55, 512>),
|
||||
Case("readFrom followed by sequential reads", test_readFromFollowedByReads<0x55, 513>),
|
||||
Case("readFrom followed by sequential reads", test_readFromFollowedByReads<0x55, 1024>),
|
||||
|
||||
/* log odd-sized blocks which wouldn't align with program_unit at the tail */
|
||||
Case("initialize3", test_initialize),
|
||||
Case("log odd-sized chunk", test_logSeveralOddSizedChunks<1, 1>),
|
||||
|
@ -876,6 +1144,7 @@ Case cases[] = {
|
|||
Case("failed small write followed by padded write", test_failedSmallWriteFollowedByPaddedWrite),
|
||||
|
||||
Case("reset and initialize6", test_resetAndInitialize),
|
||||
Case("crc32", test_crc32),
|
||||
// Case("uninitialize", test_uninitialize),
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -56,9 +56,8 @@
|
|||
#define CFSTORE_CONFIG_BACKEND_FLASH_ENABLED
|
||||
#endif
|
||||
|
||||
// todo: fixup for storage driver using more than the STORAGE_xxx namespace:
|
||||
#if defined DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS
|
||||
#define CFSTORE_STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS
|
||||
#if defined STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS
|
||||
#define CFSTORE_STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
printf(_fmt, __VA_ARGS__); \
|
||||
}while(0);
|
||||
|
||||
//todo: restore #define noCFSTORE_DEBUG
|
||||
#define noCFSTORE_DEBUG
|
||||
//#define CFSTORE_DEBUG
|
||||
#ifdef CFSTORE_DEBUG
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* mbed Microcontroller Library
|
||||
* Copyright (c) 2006-2016 ARM Limited
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <Driver_Common.h>
|
||||
#include "storage_volume_manager.h"
|
||||
#include "cfstore_config.h"
|
||||
#include "cfstore_debug.h"
|
||||
#include "cfstore_svm.h"
|
||||
|
||||
/** @file cfstore_svm.cpp
|
||||
*
|
||||
* This module is provides a C wrapper to the C++ storage-volume-manager.h API,
|
||||
* so it can be called by the C-HAL implementation configuration_store.c
|
||||
*/
|
||||
|
||||
#define CFSTORE_SVM_VOL_01_START_OFFSET 0x80000UL
|
||||
#define CFSTORE_SVM_VOL_01_SIZE 0x80000UL
|
||||
|
||||
#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F;
|
||||
static ARM_DRIVER_STORAGE *cfstore_svm_storage_drv = &ARM_Driver_Storage_MTD_K64F;
|
||||
|
||||
#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */
|
||||
|
||||
/* the storage volume manager instance used to generate virtual mtd descriptors */
|
||||
static StorageVolumeManager volumeManager;
|
||||
|
||||
/* used only for the initialization of the volume-manager. */
|
||||
static void cfstore_svm_volume_manager_initialize_callback(int32_t status)
|
||||
{
|
||||
CFSTORE_FENTRYLOG("%s: operation %d with status %d" , __func__, (int) operation, (int) status);
|
||||
}
|
||||
|
||||
static void cfstore_svm_journal_mtc_callback(int32_t status, ARM_STORAGE_OPERATION operation)
|
||||
{
|
||||
CFSTORE_FENTRYLOG("%s: operation %d with status %d" , __func__, (int) operation, (int) status);
|
||||
}
|
||||
|
||||
int32_t cfstore_svm_init(struct _ARM_DRIVER_STORAGE *storage_mtd)
|
||||
{
|
||||
int32_t ret = ARM_DRIVER_OK;
|
||||
|
||||
CFSTORE_FENTRYLOG("%s:entered\n", __func__);
|
||||
ret = volumeManager.initialize(cfstore_svm_storage_drv, cfstore_svm_volume_manager_initialize_callback);
|
||||
if(ret < ARM_DRIVER_OK) {
|
||||
CFSTORE_ERRLOG("%s:debug: volume-manager::initialize() failed for storage_mtd=%p (ret=%d)", __func__, storage_mtd, (int) ret);
|
||||
return ret;
|
||||
}
|
||||
ret = volumeManager.addVolume_C(CFSTORE_SVM_VOL_01_START_OFFSET, CFSTORE_SVM_VOL_01_SIZE, storage_mtd);
|
||||
if(ret < ARM_DRIVER_OK) {
|
||||
CFSTORE_ERRLOG("%s:debug: volume-manager::addVolume_C() failed for storage_mtd=%p (ret=%d)", __func__, storage_mtd, (int) ret);
|
||||
return ret;
|
||||
}
|
||||
ret = storage_mtd->Initialize(cfstore_svm_journal_mtc_callback);
|
||||
if(ret < ARM_DRIVER_OK) {
|
||||
CFSTORE_ERRLOG("%s:debug: storage_mtd->initialize() failed for storage_mtd=%p (ret=%d)", __func__, storage_mtd, (int) ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* mbed Microcontroller Library
|
||||
* Copyright (c) 2006-2016 ARM Limited
|
||||
*
|
||||
* 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 cfstore_svm.h
|
||||
*
|
||||
* This is the interface file to configuration store storage volume manager.
|
||||
*/
|
||||
|
||||
#ifndef __CFSTORE_SVM_H_
|
||||
#define __CFSTORE_SVM_H_
|
||||
|
||||
#include <Driver_Storage.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
int32_t cfstore_svm_init(struct _ARM_DRIVER_STORAGE *mtd);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*__CFSTORE_SVM_H_ */
|
|
@ -257,8 +257,8 @@ int32_t cfstore_test_startup(void)
|
|||
#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED
|
||||
|
||||
static FlashJournal_t jrnl;
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0);
|
||||
const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0);
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F;
|
||||
const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F;
|
||||
|
||||
ret = FlashJournal_initialize(&jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, NULL);
|
||||
if(ret < JOURNAL_STATUS_OK){
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#endif /* YOTTA_CFG_CFSTORE_UVISOR */
|
||||
|
||||
#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED
|
||||
#include "cfstore_svm.h"
|
||||
#include "flash_journal_strategy_sequential.h"
|
||||
#include "flash_journal.h"
|
||||
#include "Driver_Common.h"
|
||||
|
@ -45,8 +46,10 @@
|
|||
|
||||
#ifdef CFSTORE_DEBUG
|
||||
uint32_t cfstore_optDebug_g = 1;
|
||||
uint32_t cfstore_optLogLevel_g = CFSTORE_LOG_NONE; /*CFSTORE_LOG_NONE|CFSTORE_LOG_ERR|CFSTORE_LOG_DEBUG|CFSTORE_LOG_FENTRY */
|
||||
uint32_t cfstore_optLogTracepoint_g = CFSTORE_TP_NONE; /*CFSTORE_TP_NONE|CFSTORE_TP_CLOSE|CFSTORE_TP_CREATE|CFSTORE_TP_DELETE|CFSTORE_TP_FILE|CFSTORE_TP_FIND|CFSTORE_TP_FLUSH|CFSTORE_TP_INIT|CFSTORE_TP_OPEN|CFSTORE_TP_READ|CFSTORE_TP_WRITE|CFSTORE_TP_VERBOSE1|CFSTORE_TP_VERBOSE2|CFSTORE_TP_VERBOSE3|CFSTORE_TP_FENTRY; */
|
||||
//todo: restore uint32_t cfstore_optLogLevel_g = CFSTORE_LOG_NONE; /*CFSTORE_LOG_NONE|CFSTORE_LOG_ERR|CFSTORE_LOG_DEBUG|CFSTORE_LOG_FENTRY */
|
||||
//uint32_t cfstore_optLogTracepoint_g = CFSTORE_TP_NONE; /*CFSTORE_TP_NONE|CFSTORE_TP_CLOSE|CFSTORE_TP_CREATE|CFSTORE_TP_DELETE|CFSTORE_TP_FILE|CFSTORE_TP_FIND|CFSTORE_TP_FLUSH|CFSTORE_TP_INIT|CFSTORE_TP_OPEN|CFSTORE_TP_READ|CFSTORE_TP_WRITE|CFSTORE_TP_VERBOSE1|CFSTORE_TP_VERBOSE2|CFSTORE_TP_VERBOSE3|CFSTORE_TP_FENTRY; */
|
||||
uint32_t cfstore_optLogLevel_g = CFSTORE_LOG_NONE|CFSTORE_LOG_ERR|CFSTORE_LOG_DEBUG|CFSTORE_LOG_FENTRY;
|
||||
uint32_t cfstore_optLogTracepoint_g = CFSTORE_TP_NONE|CFSTORE_TP_CLOSE|CFSTORE_TP_CREATE|CFSTORE_TP_DELETE|CFSTORE_TP_FILE|CFSTORE_TP_FIND|CFSTORE_TP_FLUSH|CFSTORE_TP_INIT|CFSTORE_TP_OPEN|CFSTORE_TP_READ|CFSTORE_TP_WRITE|CFSTORE_TP_VERBOSE1|CFSTORE_TP_VERBOSE2|CFSTORE_TP_VERBOSE3|CFSTORE_TP_FENTRY;
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -54,10 +57,12 @@ uint32_t cfstore_optLogTracepoint_g = CFSTORE_TP_NONE; /*CFSTORE_TP_NONE|CFSTORE
|
|||
* Externs
|
||||
*/
|
||||
#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0);
|
||||
ARM_DRIVER_STORAGE *cfstore_storage_drv = &ARM_Driver_Storage_(0);
|
||||
extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F;
|
||||
ARM_DRIVER_STORAGE *cfstore_storage_drv = &ARM_Driver_Storage_MTD_K64F;
|
||||
#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */
|
||||
|
||||
struct _ARM_DRIVER_STORAGE cfstore_journal_mtd;
|
||||
|
||||
/*
|
||||
* Defines
|
||||
*
|
||||
|
@ -69,7 +74,10 @@ ARM_DRIVER_STORAGE *cfstore_storage_drv = &ARM_Driver_Storage_(0);
|
|||
* valid sizes of areas should always be greater than the size of the header, and therefore
|
||||
* greater than this value, which is defined as smaller than the header size
|
||||
*
|
||||
* ARM_DRIVER_OK_DONE
|
||||
* CFSTORE_FLASH_NUMSLOTS
|
||||
* number of flash journal slots
|
||||
*
|
||||
* ARM_DRIVER_OK_DONE
|
||||
* value that indicates an operation has been done i.e. a value > 0
|
||||
*/
|
||||
#define CFSTORE_KEY_NAME_CHARS_ACCEPTABLE "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}.-_@"
|
||||
|
@ -79,6 +87,7 @@ ARM_DRIVER_STORAGE *cfstore_storage_drv = &ARM_Driver_Storage_(0);
|
|||
#define CFSTORE_FILE_CREATE_MODE_DEFAULT (ARM_CFSTORE_FMODE)0
|
||||
#define CFSTORE_FLASH_STACK_BUF_SIZE 64
|
||||
#define CFSTORE_FLASH_AREA_SIZE_MIN (sizeof(cfstore_area_header_t) - 1)
|
||||
#define CFSTORE_FLASH_NUMSLOTS 4
|
||||
#define cfstore_fsm_null NULL
|
||||
#define CFSTORE_SENTINEL 0x7fffffff
|
||||
#define CFSTORE_CALLBACK_RET_CODE_DEFAULT 0x1
|
||||
|
@ -169,6 +178,7 @@ typedef enum cfstore_fsm_state_t {
|
|||
cfstore_fsm_state_committing,
|
||||
cfstore_fsm_state_resetting,
|
||||
cfstore_fsm_state_ready, /* ready for next flash journal command to arise */
|
||||
cfstore_fsm_state_formatting, /* flash formatting in progress */
|
||||
cfstore_fsm_state_max
|
||||
} cfstore_fsm_state_t;
|
||||
|
||||
|
@ -180,6 +190,7 @@ typedef enum cfstore_fsm_event_t {
|
|||
cfstore_fsm_event_commit_req,
|
||||
cfstore_fsm_event_commit_done,
|
||||
cfstore_fsm_event_reset_done,
|
||||
cfstore_fsm_event_format_done,
|
||||
cfstore_fsm_event_max,
|
||||
} cfstore_fsm_event_t;
|
||||
|
||||
|
@ -198,6 +209,7 @@ typedef struct cfstore_fsm_t
|
|||
/* strings used for debug trace */
|
||||
static const char* cfstore_flash_opcode_str[] =
|
||||
{
|
||||
"FLASH_JOURNAL_OPCODE_FORMAT",
|
||||
"FLASH_JOURNAL_OPCODE_INITIALIZE",
|
||||
"FLASH_JOURNAL_OPCODE_GET_INFO",
|
||||
"FLASH_JOURNAL_OPCODE_READ_BLOB",
|
||||
|
@ -215,6 +227,7 @@ static const char* cfstore_flash_state_str[] =
|
|||
"committing",
|
||||
"resetting",
|
||||
"ready",
|
||||
"formatting",
|
||||
"unknown"
|
||||
};
|
||||
|
||||
|
@ -226,6 +239,7 @@ static const char* cfstore_flash_event_str[] =
|
|||
"commit_req",
|
||||
"commit_done",
|
||||
"reset_done",
|
||||
"format_done",
|
||||
"unknown"
|
||||
};
|
||||
#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */
|
||||
|
@ -727,7 +741,6 @@ void *cfstore_realloc(void *ptr, ARM_CFSTORE_SIZE size)
|
|||
|
||||
|
||||
#ifdef CFSTORE_TARGET_LIKE_X86_LINUX_NATIVE
|
||||
static inline void cfstore_critical_section_init(CFSTORE_LOCK* lock){ *lock = 0; }
|
||||
static inline void cfstore_critical_section_lock(CFSTORE_LOCK* lock, const char* tag){ (void) tag; __sync_fetch_and_add(lock, 1); }
|
||||
static inline void cfstore_critical_section_unlock(CFSTORE_LOCK* lock, const char* tag){(void) tag; __sync_fetch_and_sub(lock, 1); }
|
||||
|
||||
|
@ -764,7 +777,6 @@ static CFSTORE_INLINE int32_t cfstore_hkvt_refcount_inc(cfstore_area_hkvt_t* hkv
|
|||
* Platform Specific Function Implementations
|
||||
*/
|
||||
|
||||
static inline void cfstore_critical_section_init(CFSTORE_LOCK* lock){ *lock = 0; }
|
||||
static inline void cfstore_critical_section_unlock(CFSTORE_LOCK* lock, const char* tag)
|
||||
{
|
||||
(void) lock;
|
||||
|
@ -1540,6 +1552,9 @@ static void cfstore_flash_journal_callback(int32_t status, FlashJournal_OpCode_t
|
|||
CFSTORE_FENTRYLOG("%s:entered: status=%d, cmd_code=%d (%s)\n", __func__, (int) status, (int) cmd_code, cfstore_flash_opcode_str[cmd_code]);
|
||||
switch(cmd_code)
|
||||
{
|
||||
case FLASH_JOURNAL_OPCODE_FORMAT:
|
||||
ctx->fsm.event = cfstore_fsm_event_format_done;
|
||||
break;
|
||||
case FLASH_JOURNAL_OPCODE_INITIALIZE:
|
||||
ctx->fsm.event = cfstore_fsm_event_init_done;
|
||||
break;
|
||||
|
@ -1595,14 +1610,30 @@ static int32_t cfstore_fsm_stop_on_entry(void* context)
|
|||
static int32_t cfstore_fsm_init_on_entry(void* context)
|
||||
{
|
||||
int32_t ret = ARM_DRIVER_ERROR;
|
||||
const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0);
|
||||
cfstore_ctx_t* ctx = (cfstore_ctx_t*) context;
|
||||
|
||||
CFSTORE_FENTRYLOG("%s:entered\n", __func__);
|
||||
|
||||
ret = FlashJournal_initialize(&ctx->jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, cfstore_flash_journal_callback);
|
||||
ret = cfstore_svm_init(&cfstore_journal_mtd);
|
||||
if(ret < ARM_DRIVER_OK){
|
||||
CFSTORE_DBGLOG("%s:Error: Unable to initialize storage volume manager\n", __func__);
|
||||
cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_formatting, ctx);
|
||||
return ARM_DRIVER_OK;
|
||||
}
|
||||
|
||||
ret = FlashJournal_initialize(&ctx->jrnl, (ARM_DRIVER_STORAGE *) &cfstore_journal_mtd, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, cfstore_flash_journal_callback);
|
||||
CFSTORE_TP(CFSTORE_TP_FSM, "%s:FlashJournal_initialize ret=%d\n", __func__, (int) ret);
|
||||
if(ret < ARM_DRIVER_OK){
|
||||
if(ret == JOURNAL_STATUS_NOT_FORMATTED) {
|
||||
CFSTORE_DBGLOG("%s:Error: flash not formatted\n", __func__);
|
||||
cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_formatting, ctx);
|
||||
return ARM_DRIVER_OK;
|
||||
}
|
||||
if(ret == JOURNAL_STATUS_METADATA_ERROR) {
|
||||
CFSTORE_ERRLOG("%s:Error: flash meta-data (CRC) error detected when initializing flash. Reformatting flash.\n", __func__);
|
||||
cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_formatting, ctx);
|
||||
return ARM_DRIVER_OK;
|
||||
}
|
||||
CFSTORE_ERRLOG("%s:Error: failed to initialize flash journaling layer (ret=%d)\n", __func__, (int) ret);
|
||||
cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_stopped, ctx);
|
||||
}
|
||||
|
@ -1993,18 +2024,64 @@ static int32_t cfstore_fsm_ready_on_commit_req(void* context)
|
|||
/* int32_t cfstore_fsm_ready_on_exit(void* context){ (void) context;} */
|
||||
|
||||
|
||||
/** @brief fsm handler when entering the formatting state
|
||||
*/
|
||||
static int32_t cfstore_fsm_format_on_entry(void* context)
|
||||
{
|
||||
int32_t ret = ARM_DRIVER_ERROR;
|
||||
cfstore_ctx_t* ctx = (cfstore_ctx_t*) context;
|
||||
|
||||
CFSTORE_FENTRYLOG("%s:entered\n", __func__);
|
||||
|
||||
ret = flashJournalStrategySequential_format((ARM_DRIVER_STORAGE *) &cfstore_journal_mtd, CFSTORE_FLASH_NUMSLOTS, cfstore_flash_journal_callback);
|
||||
CFSTORE_TP(CFSTORE_TP_FSM, "%s:flashJournalStrategySequential_format ret=%d\n", __func__, (int) ret);
|
||||
if(ret < ARM_DRIVER_OK){
|
||||
CFSTORE_ERRLOG("%s:Error: failed to format flash (ret=%d)\n", __func__, (int) ret);
|
||||
cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_stopped, ctx);
|
||||
}
|
||||
else if(ret > 0){
|
||||
/* operation completed synchronously*/
|
||||
cfstore_flash_journal_callback(ret, FLASH_JOURNAL_OPCODE_FORMAT);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** @brief fsm handler when in formatting state
|
||||
*/
|
||||
int32_t cfstore_fsm_formatting(void* context)
|
||||
{
|
||||
int32_t ret = ARM_DRIVER_OK;
|
||||
cfstore_ctx_t* ctx = (cfstore_ctx_t*) context;
|
||||
|
||||
CFSTORE_FENTRYLOG("%s:entered\n", __func__);
|
||||
CFSTORE_ASSERT(ctx->fsm.state == cfstore_fsm_state_formatting);
|
||||
CFSTORE_ASSERT(ctx->cmd_code == FLASH_JOURNAL_OPCODE_FORMAT);
|
||||
|
||||
/* only change state if status > 0*/
|
||||
if(ctx->status > 0){
|
||||
ret = cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_initing, ctx);
|
||||
} else if(ctx->status < 0) {
|
||||
CFSTORE_ERRLOG("%s:Error: failed to format flash (ret=%d)\n", __func__, (int) ctx->status);
|
||||
cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_stopped, ctx);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* int32_t cfstore_fsm_format_on_exit(void* context){ (void) context;} */
|
||||
|
||||
|
||||
/* handler functions while in state */
|
||||
static cfstore_fsm_handler cfstore_flash_fsm[cfstore_fsm_state_max][cfstore_fsm_event_max] =
|
||||
{
|
||||
/* state\event: init_done read_done log_done commit_req commit_done reset_done */
|
||||
/* stopped */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null },
|
||||
/* init */ {cfstore_fsm_initing, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null },
|
||||
/* reading */ {cfstore_fsm_null, cfstore_fsm_reading, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null },
|
||||
/* logging */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_logging, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null },
|
||||
/* committing */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_committing, cfstore_fsm_null },
|
||||
/* resetting */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null },
|
||||
/* ready */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_ready_on_commit_req, cfstore_fsm_null, cfstore_fsm_null },
|
||||
/* state\event: init_done read_done log_done commit_req commit_done reset_done format_done, */
|
||||
/* stopped */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null },
|
||||
/* init */ {cfstore_fsm_initing, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null },
|
||||
/* reading */ {cfstore_fsm_null, cfstore_fsm_reading, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null },
|
||||
/* logging */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_logging, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null },
|
||||
/* committing */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_committing, cfstore_fsm_null, cfstore_fsm_null },
|
||||
/* resetting */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null },
|
||||
/* ready */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_ready_on_commit_req, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null },
|
||||
/* formatting */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_formatting },
|
||||
};
|
||||
|
||||
/* handler functions for entering the state*/
|
||||
|
@ -2016,7 +2093,8 @@ cfstore_fsm_handler cfstore_fsm_on_entry[cfstore_fsm_state_max] =
|
|||
cfstore_fsm_log_on_entry,
|
||||
cfstore_fsm_commit_on_entry,
|
||||
cfstore_fsm_null, /* cfstore_fsm_reset_on_entry */
|
||||
cfstore_fsm_null /* cfstore_fsm_ready_on_entry */
|
||||
cfstore_fsm_null, /* cfstore_fsm_ready_on_entry */
|
||||
cfstore_fsm_format_on_entry /* cfstore_fsm_format_on_entry */
|
||||
};
|
||||
|
||||
/* handler functions for exiting state, currently none used */
|
||||
|
@ -2028,7 +2106,8 @@ cfstore_fsm_handler cfstore_fsm_on_exit[cfstore_fsm_state_max] =
|
|||
cfstore_fsm_log_on_exit,
|
||||
cfstore_fsm_commit_on_exit,
|
||||
cfstore_fsm_null, /* cfstore_fsm_reset_on_exit */
|
||||
cfstore_fsm_null /* cfstore_fsm_ready_on_exit */
|
||||
cfstore_fsm_null, /* cfstore_fsm_ready_on_exit */
|
||||
cfstore_fsm_null /* cfstore_fsm_format_on_exit */
|
||||
};
|
||||
|
||||
|
||||
|
@ -2425,8 +2504,8 @@ static bool cfstore_file_is_empty(ARM_CFSTORE_HANDLE hkey)
|
|||
/* @brief See definition in configuration_store.h for description. */
|
||||
ARM_CFSTORE_CAPABILITIES cfstore_get_capabilities(void)
|
||||
{
|
||||
/* getting capabilities doesnt change the sram area so this can happen independently of
|
||||
* an oustanding async operation. its unnecessary to check the fsm state */
|
||||
/* getting capabilities doesn't change the sram area so this can happen independently of
|
||||
* an outstanding async operation. its unnecessary to check the fsm state */
|
||||
return cfstore_caps_g;
|
||||
}
|
||||
|
||||
|
@ -3866,9 +3945,7 @@ static int32_t cfstore_initialise(ARM_CFSTORE_CALLBACK callback, void* client_co
|
|||
ctx->init_ref_count++;
|
||||
/* initially there is no memory allocated for the area */
|
||||
CFSTORE_INIT_LIST_HEAD(&ctx->file_list);
|
||||
/* This is not required here are the lock is statically initialised to 0
|
||||
* cfstore_critical_section_init(&ctx->rw_area0_lock);
|
||||
*/
|
||||
/* ctx->rw_area0_lock initialisation is not required here as the lock is statically initialised to 0 */
|
||||
ctx->area_0_head = NULL;
|
||||
ctx->area_0_tail = NULL;
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2016, ARM Limited, All Rights Reserved
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __FLASH_JOURNAL_CONFIG_H__
|
||||
#define __FLASH_JOURNAL_CONFIG_H__
|
||||
|
||||
#ifndef SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS
|
||||
#define SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS 4
|
||||
#endif
|
||||
|
||||
#endif /* __FLASH_JOURNAL_CONFIG_H__ */
|
|
@ -0,0 +1,220 @@
|
|||
/**********************************************************************
|
||||
*
|
||||
* Filename: flash_journal_crc.c
|
||||
*
|
||||
* Description: Slow and fast implementations of the CRC standards.
|
||||
*
|
||||
* Notes: The parameters for each supported CRC standard are
|
||||
* defined in the header file crc.h. The implementations
|
||||
* here should stand up to further additions to that list.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2000 by Michael Barr. This software is placed into
|
||||
* the public domain and may be used for any purpose. However, this
|
||||
* notice must not be changed or removed and no warranty is either
|
||||
* expressed or implied by its publication or distribution.
|
||||
**********************************************************************/
|
||||
|
||||
#include "flash-journal-strategy-sequential/flash_journal_crc.h"
|
||||
|
||||
#define FALSE 0
|
||||
#define TRUE !FALSE
|
||||
|
||||
#define CRC_NAME "CRC-32"
|
||||
#define POLYNOMIAL 0x04C11DB7
|
||||
#define INITIAL_REMAINDER 0xFFFFFFFF
|
||||
#define FINAL_XOR_VALUE 0xFFFFFFFF
|
||||
#define REFLECT_DATA TRUE
|
||||
#define REFLECT_REMAINDER TRUE
|
||||
#define CHECK_VALUE 0xCBF43926
|
||||
|
||||
|
||||
/*
|
||||
* Derive parameters from the standard-specific parameters in crc.h.
|
||||
*/
|
||||
#define WIDTH (8 * sizeof(flash_journal_crc32_t))
|
||||
/* U postfix required to suppress the following warning with TOOLCHAIN_ARM:
|
||||
* #61-D: integer operation result is out of range
|
||||
*/
|
||||
#define TOPBIT 0x80000000U
|
||||
|
||||
#if (REFLECT_DATA == TRUE)
|
||||
#undef REFLECT_DATA
|
||||
#define REFLECT_DATA(X) ((unsigned char) reflect((X), 8))
|
||||
#else
|
||||
#undef REFLECT_DATA
|
||||
#define REFLECT_DATA(X) (X)
|
||||
#endif
|
||||
|
||||
#if (REFLECT_REMAINDER == TRUE)
|
||||
#undef REFLECT_REMAINDER
|
||||
#define REFLECT_REMAINDER(X) ((flash_journal_crc32_t) reflect((X), WIDTH))
|
||||
#else
|
||||
#undef REFLECT_REMAINDER
|
||||
#define REFLECT_REMAINDER(X) (X)
|
||||
#endif
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Function: reflect()
|
||||
*
|
||||
* Description: Reorder the bits of a binary sequence, by reflecting
|
||||
* them about the middle position.
|
||||
*
|
||||
* Notes: No checking is done that nBits <= 32.
|
||||
*
|
||||
* Returns: The reflection of the original data.
|
||||
*
|
||||
*********************************************************************/
|
||||
static uint32_t
|
||||
reflect(uint32_t data, unsigned char nBits)
|
||||
{
|
||||
uint32_t reflection = 0x00000000;
|
||||
unsigned char bit;
|
||||
|
||||
/*
|
||||
* Reflect the data about the center bit.
|
||||
*/
|
||||
for (bit = 0; bit < nBits; ++bit)
|
||||
{
|
||||
/*
|
||||
* If the LSB bit is set, set the reflection of it.
|
||||
*/
|
||||
if (data & 0x01)
|
||||
{
|
||||
reflection |= (1 << ((nBits - 1) - bit));
|
||||
}
|
||||
|
||||
data = (data >> 1);
|
||||
}
|
||||
|
||||
return (reflection);
|
||||
|
||||
} /* reflect() */
|
||||
|
||||
|
||||
static flash_journal_crc32_t crcTable[256];
|
||||
static flash_journal_crc32_t crcEngineRemainder = INITIAL_REMAINDER;
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Function: flashJournalCrcInit()
|
||||
*
|
||||
* Description: Populate the partial CRC lookup table.
|
||||
*
|
||||
* Notes: This function must be rerun any time the CRC standard
|
||||
* is changed. If desired, it can be run "offline" and
|
||||
* the table results stored in an embedded system's ROM.
|
||||
*
|
||||
* Returns: None defined.
|
||||
*
|
||||
*********************************************************************/
|
||||
void
|
||||
flashJournalCrcInit(void)
|
||||
{
|
||||
flash_journal_crc32_t remainder;
|
||||
int dividend;
|
||||
unsigned char bit;
|
||||
|
||||
|
||||
/*
|
||||
* Compute the remainder of each possible dividend.
|
||||
*/
|
||||
for (dividend = 0; dividend < 256; ++dividend)
|
||||
{
|
||||
/*
|
||||
* Start with the dividend followed by zeros.
|
||||
*/
|
||||
remainder = dividend << (WIDTH - 8);
|
||||
|
||||
/*
|
||||
* Perform modulo-2 division, a bit at a time.
|
||||
*/
|
||||
for (bit = 8; bit > 0; --bit)
|
||||
{
|
||||
/*
|
||||
* Try to divide the current data bit.
|
||||
*/
|
||||
if (remainder & TOPBIT)
|
||||
{
|
||||
remainder = (remainder << 1) ^ POLYNOMIAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
remainder = (remainder << 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the result into the table.
|
||||
*/
|
||||
crcTable[dividend] = remainder;
|
||||
}
|
||||
|
||||
} /* flashJournalCrcInit() */
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Function: flashJournalCrcReset()
|
||||
*
|
||||
* Description: Resets internal state before calling crcCummulative().
|
||||
*
|
||||
* Notes: See the notes to crcCummulative().
|
||||
*
|
||||
* Returns: None defined.
|
||||
*
|
||||
*********************************************************************/
|
||||
void
|
||||
flashJournalCrcReset(void)
|
||||
{
|
||||
static unsigned initCalled = 0;
|
||||
if (!initCalled) {
|
||||
flashJournalCrcInit();
|
||||
initCalled = 1;
|
||||
}
|
||||
|
||||
crcEngineRemainder = INITIAL_REMAINDER;
|
||||
|
||||
} /* flashJournalCrcReset() */
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Function: crcCummulative()
|
||||
*
|
||||
* Description: Compute the CRC of a group of messages.
|
||||
*
|
||||
* Notes:
|
||||
* This function is intended to be used in the following way:
|
||||
* - crcReset() is called first to reset internal state before the first
|
||||
* fragment of a new message is processed with crcCummulative().
|
||||
* - crcCummulative() called successfully appending additional message
|
||||
* fragments to those previously supplied (in order), and returning
|
||||
* the current crc for the message payload so far.
|
||||
*
|
||||
* Returns: The CRC of the message.
|
||||
*
|
||||
*********************************************************************/
|
||||
flash_journal_crc32_t
|
||||
flashJournalCrcCummulative(unsigned char const message[], int nBytes)
|
||||
{
|
||||
unsigned char data;
|
||||
int byte;
|
||||
|
||||
|
||||
/*
|
||||
* Divide the message by the polynomial, a byte at a time.
|
||||
*/
|
||||
for (byte = 0; byte < nBytes; ++byte)
|
||||
{
|
||||
data = REFLECT_DATA(message[byte]) ^ (crcEngineRemainder >> (WIDTH - 8));
|
||||
crcEngineRemainder = crcTable[data] ^ (crcEngineRemainder << 8);
|
||||
}
|
||||
|
||||
/*
|
||||
* The final remainder is the CRC.
|
||||
*/
|
||||
return (REFLECT_REMAINDER(crcEngineRemainder) ^ FINAL_XOR_VALUE);
|
||||
|
||||
} /* crcCummulative() */
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/**********************************************************************
|
||||
*
|
||||
* Filename: flash_journal_crc.h.h
|
||||
*
|
||||
* Description: A header file describing the various CRC standards.
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2000 by Michael Barr. This software is placed into
|
||||
* the public domain and may be used for any purpose. However, this
|
||||
* notice must not be changed or removed and no warranty is either
|
||||
* expressed or implied by its publication or distribution.
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _flash_journal_crc_h
|
||||
#define _flash_journal_crc_h
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef uint32_t flash_journal_crc32_t;
|
||||
|
||||
void flashJournalCrcReset(void);
|
||||
flash_journal_crc32_t flashJournalCrcCummulative(unsigned char const message[], int nBytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _flash_journal_crc_h */
|
|
@ -23,11 +23,22 @@ extern "C" {
|
|||
#endif // __cplusplus
|
||||
|
||||
#include "flash-journal/flash_journal.h"
|
||||
#include "flash-journal-strategy-sequential/config.h"
|
||||
|
||||
static inline uint32_t roundUp_uint32(uint32_t N, uint32_t BOUNDARY) {
|
||||
return ((((N) + (BOUNDARY) - 1) / (BOUNDARY)) * (BOUNDARY));
|
||||
}
|
||||
static inline uint32_t roundDown_uint32(uint32_t N, uint32_t BOUNDARY) {
|
||||
return (((N) / (BOUNDARY)) * (BOUNDARY));
|
||||
}
|
||||
|
||||
#define LCM_OF_ALL_ERASE_UNITS 4096 /* Assume an LCM of erase_units for now. This will be generalized later. */
|
||||
|
||||
static const uint32_t SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER = 0xFFFFFFFFUL;
|
||||
static const uint32_t SEQUENTIAL_FLASH_JOURNAL_VERSION = 1;
|
||||
static const uint32_t SEQUENTIAL_FLASH_JOURNAL_MAGIC = 0xCE02102AUL;
|
||||
static const uint32_t SEQUENTIAL_FLASH_JOURNAL_MAGIC = 0xCE02102AUL;
|
||||
static const uint32_t SEQUENTIAL_FLASH_JOURNAL_VERSION = 1;
|
||||
static const uint32_t SEQUENTIAL_FLASH_JOURNAL_HEADER_MAGIC = 0xCEA00AEEUL;
|
||||
static const uint32_t SEQUENTIAL_FLASH_JOURNAL_HEADER_VERSION = 1;
|
||||
|
||||
|
||||
typedef enum {
|
||||
SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED,
|
||||
|
@ -41,6 +52,20 @@ typedef enum {
|
|||
SEQUENTIAL_JOURNAL_STATE_READING,
|
||||
} SequentialFlashJournalState_t;
|
||||
|
||||
/**
|
||||
* Meta-data placed at the head of a Journal. The actual header would be an
|
||||
* extension of this generic header, and would depend on the implementation
|
||||
* strategy. Initialization algorithms can expect to find this generic header at
|
||||
* the start of every Journal.
|
||||
*/
|
||||
typedef struct _SequentialFlashJournalHeader {
|
||||
FlashJournalHeader_t genericHeader; /** Generic meta-data placed at the head of a Journal; common to all journal types. */
|
||||
uint32_t magic; /** Sequential journal header specific magic code. */
|
||||
uint32_t version; /** Revision number for this sequential journal header. */
|
||||
uint32_t numSlots; /** Maximum number of logged blobs; i.e. maximum number of versions of the journaled payload. */
|
||||
uint32_t sizeofSlot; /** Slot size. Each slot holds a header, blob-payload, and a tail. */
|
||||
} SequentialFlashJournalHeader_t;
|
||||
|
||||
/**
|
||||
* Meta-data placed at the head of a sequential-log entry.
|
||||
*/
|
||||
|
@ -62,9 +87,12 @@ typedef struct _SequentialFlashJournalLogHead {
|
|||
* a partially written log-entry-tail won't be accepted as valid.
|
||||
*/
|
||||
typedef struct _SequentialFlashJournalLogTail {
|
||||
uint64_t sizeofBlob; /**< the size of the payload in this blob. */
|
||||
uint32_t sizeofBlob; /**< the size of the payload in this blob. */
|
||||
uint32_t magic;
|
||||
uint32_t sequenceNumber;
|
||||
uint32_t crc32; /**< This field contains the CRC of the header, body (only including logged data),
|
||||
* and the tail. The 'CRC32' field is assumed to hold 0x0 for the purpose of
|
||||
* computing the CRC */
|
||||
} SequentialFlashJournalLogTail_t;
|
||||
|
||||
#define SEQUENTIAL_JOURNAL_VALID_TAIL(TAIL_PTR) ((TAIL_PTR)->magic == SEQUENTIAL_FLASH_JOURNAL_MAGIC)
|
||||
|
@ -76,7 +104,9 @@ typedef struct _SequentialFlashJournal_t {
|
|||
ARM_DRIVER_STORAGE *mtd; /**< The underlying Memory-Technology-Device. */
|
||||
ARM_STORAGE_CAPABILITIES mtdCapabilities; /**< the return from mtd->GetCapabilities(); held for quick reference. */
|
||||
uint64_t mtdStartOffset; /**< the start of the address range maintained by the underlying MTD. */
|
||||
uint32_t sequentialSkip; /**< size of the log stride. */
|
||||
uint32_t firstSlotOffset; /** Offset from the start of the journal header to the actual logged journal. */
|
||||
uint32_t numSlots; /** Maximum number of logged blobs; i.e. maximum number of versions of the journaled payload. */
|
||||
uint32_t sizeofSlot; /**< size of the log stride. */
|
||||
uint32_t nextSequenceNumber; /**< the next valid sequence number to be used when logging the next blob. */
|
||||
uint32_t currentBlobIndex; /**< index of the most recently written blob. */
|
||||
SequentialFlashJournalState_t state; /**< state of the journal. SEQUENTIAL_JOURNAL_STATE_INITIALIZED being the default. */
|
||||
|
@ -90,12 +120,9 @@ typedef struct _SequentialFlashJournal_t {
|
|||
/** state relevant to initialization. */
|
||||
struct {
|
||||
uint64_t currentOffset;
|
||||
union {
|
||||
SequentialFlashJournalLogHead_t head;
|
||||
struct {
|
||||
uint32_t headSequenceNumber;
|
||||
SequentialFlashJournalLogTail_t tail;
|
||||
};
|
||||
struct {
|
||||
uint32_t headSequenceNumber;
|
||||
SequentialFlashJournalLogTail_t tail;
|
||||
};
|
||||
} initScan;
|
||||
|
||||
|
@ -105,11 +132,11 @@ typedef struct _SequentialFlashJournal_t {
|
|||
size_t sizeofBlob;
|
||||
union {
|
||||
struct {
|
||||
uint64_t eraseOffset;
|
||||
uint64_t mtdEraseOffset;
|
||||
};
|
||||
struct {
|
||||
uint64_t offset; /**< the current offset at which data is being written. */
|
||||
uint64_t tailOffset; /**< offset at which the SequentialFlashJournalLogTail_t will be logged for this log-entry. */
|
||||
uint64_t mtdOffset; /**< the current Storage offset at which data will be written. */
|
||||
uint64_t mtdTailOffset; /**< Storage offset at which the SequentialFlashJournalLogTail_t will be logged for this log-entry. */
|
||||
const uint8_t *dataBeingLogged; /**< temporary pointer aimed at the next data to be logged. */
|
||||
size_t amountLeftToLog;
|
||||
union {
|
||||
|
@ -124,10 +151,10 @@ typedef struct _SequentialFlashJournal_t {
|
|||
struct {
|
||||
const uint8_t *blob; /**< the original buffer holding source data. */
|
||||
size_t sizeofBlob;
|
||||
uint64_t offset; /**< the current offset at which data is being written. */
|
||||
uint64_t mtdOffset; /**< the current Storage offset from which data is being read. */
|
||||
uint8_t *dataBeingRead; /**< temporary pointer aimed at the next data to be read-into. */
|
||||
size_t amountLeftToRead;
|
||||
size_t totalDataRead; /**< the total data that has been read off the blob so far. */
|
||||
size_t logicalOffset; /**< the logical offset within the blob at which the next read will occur. */
|
||||
} read;
|
||||
};
|
||||
} SequentialFlashJournal_t;
|
||||
|
@ -139,6 +166,8 @@ typedef struct _SequentialFlashJournal_t {
|
|||
*/
|
||||
typedef char AssertSequentialJournalSizeLessThanOrEqualToGenericJournal[sizeof(SequentialFlashJournal_t)<=sizeof(FlashJournal_t)?1:-1];
|
||||
|
||||
#define SLOT_ADDRESS(JOURNAL, INDEX) ((JOURNAL)->mtdStartOffset + (JOURNAL)->firstSlotOffset + ((INDEX) * (JOURNAL)->sizeofSlot))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
|
|
@ -24,12 +24,118 @@ extern "C" {
|
|||
|
||||
#include "flash-journal/flash_journal.h"
|
||||
|
||||
/**
|
||||
* Create/format a sequential flash journal at a given offset within a storage
|
||||
* device and with a given slot-cardinality.
|
||||
*
|
||||
* This function must be called *once* for each incarnation of a sequential
|
||||
* journal.
|
||||
*
|
||||
* @param[in] mtd
|
||||
* The underlying Storage driver.
|
||||
*
|
||||
* @param[in] numSlots
|
||||
* Number of slots in the sequential journal. Each slot holds a header, blob-payload, and a tail.
|
||||
*
|
||||
* @param[in] callback
|
||||
* Caller-defined callback to be invoked upon command completion
|
||||
* in case the storage device executes operations asynchronously.
|
||||
* Use a NULL pointer when no callback signals are required.
|
||||
*
|
||||
* @note: this is an asynchronous operation, but it can finish
|
||||
* synchronously if the underlying MTD supports that.
|
||||
*
|
||||
* @return
|
||||
* The function executes in the following ways:
|
||||
* - When the operation is asynchronous, the function only starts the
|
||||
* initialization and control returns to the caller with an
|
||||
* JOURNAL_STATUS_OK before the actual completion of the operation (or with
|
||||
* an appropriate error code in case of failure). When the operation is
|
||||
* completed the command callback is invoked with 1 passed in as the
|
||||
* 'status' parameter of the callback. In case of errors, the completion
|
||||
* callback is invoked with an error status.
|
||||
* - When the operation is executed by the journal in a blocking (i.e.
|
||||
* synchronous) manner, control returns to the caller only upon the actual
|
||||
* completion of the operation or the discovery of a failure condition. In
|
||||
* this case, the function returns 1 to signal successful synchronous
|
||||
* completion or an appropriate error code, and no further
|
||||
* invocation of the completion callback should be expected at a later time.
|
||||
*
|
||||
* Here's a code snippet to suggest how this API might be used by callers:
|
||||
* \code
|
||||
* ASSERT(JOURNAL_STATUS_OK == 0); // this is a precondition; it doesn't need to be put in code
|
||||
* int32_t returnValue = flashJournalStrategySequential_format(MTD, numSlots, callbackHandler);
|
||||
* if (returnValue < JOURNAL_STATUS_OK) {
|
||||
* // handle error
|
||||
* } else if (returnValue == JOURNAL_STATUS_OK) {
|
||||
* ASSERT(MTD->GetCapabilities().asynchronous_ops == 1);
|
||||
* // handle early return from asynchronous execution
|
||||
* } else {
|
||||
* ASSERT(returnValue == 1);
|
||||
* // handle synchronous completion
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* +-------------------------------+ ^
|
||||
* | | |
|
||||
* | Journal Header | |
|
||||
* | starts with generic header | |
|
||||
* | followed by specific header | |
|
||||
* | | | multiple of program_unit
|
||||
* +-------------------------------+ | and erase-boundary
|
||||
* +-------------------------------+ |
|
||||
* | | |
|
||||
* | padding to allow alignment | |
|
||||
* | | |
|
||||
* +-------------------------------+ v
|
||||
* +-------------------------------+
|
||||
* | +---------------------------+ | ^
|
||||
* | | slot header | | |
|
||||
* | | aligned with program_unit| | |
|
||||
* | +---------------------------+ | | slot 0
|
||||
* | | | aligned with LCM of all erase boundaries
|
||||
* | | |
|
||||
* | | |
|
||||
* | | |
|
||||
* | BLOB0 | |
|
||||
* | | |
|
||||
* | | |
|
||||
* | +---------------------------+ | |
|
||||
* | | slot tail | | |
|
||||
* | | aligned with program_unit| | |
|
||||
* | +---------------------------+ | |
|
||||
* +-------------------------------+ v
|
||||
* +-------------------------------+
|
||||
* | +---------------------------+ | ^
|
||||
* | | slot header | | |
|
||||
* | | aligned with program_unit| | |
|
||||
* | +---------------------------+ | | slot 1
|
||||
* | | | aligned with LCM of all erase boundaries
|
||||
* | BLOB1 | |
|
||||
* | | |
|
||||
* . . .
|
||||
* . . .
|
||||
*
|
||||
* . . .
|
||||
* . BLOB(N-1) . .
|
||||
* | | |
|
||||
* | +---------------------------+ | | slot 'N - 1'
|
||||
* | | slot tail | | | aligned with LCM of all erase boundaries
|
||||
* | | aligned with program_unit| | |
|
||||
* | +---------------------------+ | |
|
||||
* +-------------------------------+ v
|
||||
*/
|
||||
int32_t flashJournalStrategySequential_format(ARM_DRIVER_STORAGE *mtd,
|
||||
uint32_t numSlots,
|
||||
FlashJournal_Callback_t callback);
|
||||
|
||||
int32_t flashJournalStrategySequential_initialize(FlashJournal_t *journal,
|
||||
ARM_DRIVER_STORAGE *mtd,
|
||||
const FlashJournal_Ops_t *ops,
|
||||
FlashJournal_Callback_t callback);
|
||||
FlashJournal_Status_t flashJournalStrategySequential_getInfo(FlashJournal_t *journal, FlashJournal_Info_t *info);
|
||||
int32_t flashJournalStrategySequential_read(FlashJournal_t *journal, void *blob, size_t n);
|
||||
int32_t flashJournalStrategySequential_readFrom(FlashJournal_t *journal, size_t offset, void *blob, size_t n);
|
||||
int32_t flashJournalStrategySequential_log(FlashJournal_t *journal, const void *blob, size_t n);
|
||||
int32_t flashJournalStrategySequential_commit(FlashJournal_t *journal);
|
||||
int32_t flashJournalStrategySequential_reset(FlashJournal_t *journal);
|
||||
|
@ -38,6 +144,7 @@ static const FlashJournal_Ops_t FLASH_JOURNAL_STRATEGY_SEQUENTIAL = {
|
|||
flashJournalStrategySequential_initialize,
|
||||
flashJournalStrategySequential_getInfo,
|
||||
flashJournalStrategySequential_read,
|
||||
flashJournalStrategySequential_readFrom,
|
||||
flashJournalStrategySequential_log,
|
||||
flashJournalStrategySequential_commit,
|
||||
flashJournalStrategySequential_reset
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "flash-journal-strategy-sequential/flash_journal_crc.h"
|
||||
#include "flash-journal-strategy-sequential/flash_journal_private.h"
|
||||
#include "flash-journal-strategy-sequential/flash_journal_strategy_sequential.h"
|
||||
#include "support_funcs.h"
|
||||
|
@ -23,82 +24,101 @@
|
|||
|
||||
SequentialFlashJournal_t *activeJournal;
|
||||
|
||||
/* forward declarations */
|
||||
void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation);
|
||||
/*
|
||||
* forward declarations of static-inline helper functions.
|
||||
*/
|
||||
static inline int32_t mtdGetTotalCapacity(ARM_DRIVER_STORAGE *mtd, uint64_t *capacityP);
|
||||
static inline int32_t flashJournalStrategySequential_format_sanityChecks(ARM_DRIVER_STORAGE *mtd, uint32_t numSlots);
|
||||
static inline int32_t flashJournalStrategySequential_read_sanityChecks(SequentialFlashJournal_t *journal, const void *blob, size_t sizeofBlob);
|
||||
static inline int32_t flashJournalStrategySequential_log_sanityChecks(SequentialFlashJournal_t *journal, const void *blob, size_t sizeofBlob);
|
||||
static inline int32_t flashJournalStrategySequential_commit_sanityChecks(SequentialFlashJournal_t *journal);
|
||||
|
||||
static inline int32_t mtdGetTotalCapacity(ARM_DRIVER_STORAGE *mtd, uint64_t *capacityP)
|
||||
|
||||
int32_t flashJournalStrategySequential_format(ARM_DRIVER_STORAGE *mtd,
|
||||
uint32_t numSlots,
|
||||
FlashJournal_Callback_t callback)
|
||||
{
|
||||
/* fetch MTD's INFO */
|
||||
int32_t rc;
|
||||
if ((rc = flashJournalStrategySequential_format_sanityChecks(mtd, numSlots)) != JOURNAL_STATUS_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
ARM_STORAGE_INFO mtdInfo;
|
||||
int32_t rc = mtd->GetInfo(&mtdInfo);
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
if (mtd->GetInfo(&mtdInfo) < ARM_DRIVER_OK) {
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
uint64_t mtdAddr;
|
||||
if (mtdGetStartAddr(mtd, &mtdAddr) < JOURNAL_STATUS_OK) {
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
*capacityP = mtdInfo.total_storage;
|
||||
|
||||
return JOURNAL_STATUS_OK;
|
||||
formatInfoSingleton.mtd = mtd;
|
||||
formatInfoSingleton.mtdAddr = mtdAddr;
|
||||
formatInfoSingleton.callback = callback;
|
||||
formatInfoSingleton.mtdProgramUnit = mtdInfo.program_unit;
|
||||
|
||||
if ((rc = setupSequentialJournalHeader(&formatInfoSingleton.header, mtd, mtdInfo.total_storage, numSlots)) != JOURNAL_STATUS_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* initialize MTD */
|
||||
rc = mtd->Initialize(formatHandler);
|
||||
if (rc < ARM_DRIVER_OK) {
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
} else if (rc == ARM_DRIVER_OK) {
|
||||
return JOURNAL_STATUS_OK; /* An asynchronous operation is pending; it will result in a completion callback
|
||||
* where the rest of processing will take place. */
|
||||
}
|
||||
if (rc != 1) {
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR; /* synchronous completion is expected to return 1 */
|
||||
}
|
||||
|
||||
/* progress the rest of the create state-machine */
|
||||
return flashJournalStrategySequential_format_progress(ARM_DRIVER_OK, ARM_STORAGE_OPERATION_INITIALIZE);
|
||||
}
|
||||
|
||||
static inline int32_t flashJournalStrategySequential_read_sanityChecks(SequentialFlashJournal_t *journal, const void *blob, size_t sizeofBlob)
|
||||
/**
|
||||
* Validate a header at the start of the MTD.
|
||||
*
|
||||
* @param [in/out] headerP
|
||||
* Caller-allocated header which gets filled in during validation.
|
||||
|
||||
* @return JOURNAL_STATUS_OK if the header is sane. As a side-effect, the memory
|
||||
* pointed to by 'headerP' is initialized with the header.
|
||||
*/
|
||||
int32_t readAndVerifyJournalHeader(SequentialFlashJournal_t *journal, SequentialFlashJournalHeader_t *headerP)
|
||||
{
|
||||
if ((journal == NULL) || (blob == NULL) || (sizeofBlob == 0)) {
|
||||
if (headerP == NULL) {
|
||||
return JOURNAL_STATUS_PARAMETER;
|
||||
}
|
||||
if ((journal->state == SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED) || (journal->state == SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS)) {
|
||||
return JOURNAL_STATUS_NOT_INITIALIZED;
|
||||
}
|
||||
if (journal->state != SEQUENTIAL_JOURNAL_STATE_INITIALIZED) {
|
||||
return JOURNAL_STATUS_ERROR; /* journal is in an un-expected state. */
|
||||
}
|
||||
// printf("read sanity checks: totalDataRead = %lu, sizeofJournaledBlob = %lu\n", (uint32_t)journal->read.totalDataRead, (uint32_t)journal->info.sizeofJournaledBlob);
|
||||
if ((journal->info.sizeofJournaledBlob == 0) || (journal->read.totalDataRead == journal->info.sizeofJournaledBlob)) {
|
||||
journal->read.totalDataRead = 0;
|
||||
return JOURNAL_STATUS_EMPTY;
|
||||
|
||||
int32_t rc = journal->mtd->ReadData(journal->mtdStartOffset, headerP, sizeof(SequentialFlashJournalHeader_t));
|
||||
if (rc < ARM_DRIVER_OK) {
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
} else if (rc == ARM_DRIVER_OK) {
|
||||
ARM_STORAGE_CAPABILITIES mtdCaps = journal->mtd->GetCapabilities();
|
||||
if (!mtdCaps.asynchronous_ops) {
|
||||
return JOURNAL_STATUS_ERROR; /* asynchronous_ops must be set if MTD returns ARM_DRIVER_OK. */
|
||||
}
|
||||
|
||||
return JOURNAL_STATUS_ERROR; /* TODO: handle init with pending asynchronous activity. */
|
||||
}
|
||||
|
||||
return JOURNAL_STATUS_OK;
|
||||
}
|
||||
|
||||
static inline int32_t flashJournalStrategySequential_log_sanityChecks(SequentialFlashJournal_t *journal, const void *blob, size_t sizeofBlob)
|
||||
{
|
||||
if ((journal == NULL) || (blob == NULL) || (sizeofBlob == 0)) {
|
||||
return JOURNAL_STATUS_PARAMETER;
|
||||
}
|
||||
if ((journal->state == SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED) || (journal->state == SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS)) {
|
||||
return JOURNAL_STATUS_NOT_INITIALIZED;
|
||||
}
|
||||
if ((journal->state != SEQUENTIAL_JOURNAL_STATE_INITIALIZED) && (journal->state != SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY)) {
|
||||
return JOURNAL_STATUS_ERROR; /* journal is in an un-expected state. */
|
||||
}
|
||||
if (journal->state == SEQUENTIAL_JOURNAL_STATE_INITIALIZED) {
|
||||
if (sizeofBlob > journal->info.capacity) {
|
||||
return JOURNAL_STATUS_BOUNDED_CAPACITY; /* adding this log chunk would cause us to exceed capacity (write past the tail). */
|
||||
}
|
||||
} else if (journal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY) {
|
||||
if (journal->log.offset + sizeofBlob > journal->log.tailOffset) {
|
||||
return JOURNAL_STATUS_BOUNDED_CAPACITY; /* adding this log chunk would cause us to exceed capacity (write past the tail). */
|
||||
}
|
||||
if ((headerP->genericHeader.magic != FLASH_JOURNAL_HEADER_MAGIC) ||
|
||||
(headerP->genericHeader.version != FLASH_JOURNAL_HEADER_VERSION) ||
|
||||
(headerP->genericHeader.sizeofHeader != sizeof(SequentialFlashJournalHeader_t)) ||
|
||||
(headerP->magic != SEQUENTIAL_FLASH_JOURNAL_HEADER_MAGIC) ||
|
||||
(headerP->version != SEQUENTIAL_FLASH_JOURNAL_HEADER_VERSION)) {
|
||||
return JOURNAL_STATUS_NOT_FORMATTED;
|
||||
}
|
||||
|
||||
return JOURNAL_STATUS_OK;
|
||||
}
|
||||
|
||||
static inline int32_t flashJournalStrategySequential_commit_sanityChecks(SequentialFlashJournal_t *journal)
|
||||
{
|
||||
if (journal == NULL) {
|
||||
return JOURNAL_STATUS_PARAMETER;
|
||||
}
|
||||
if (journal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY) {
|
||||
if (journal->prevCommand != FLASH_JOURNAL_OPCODE_LOG_BLOB) {
|
||||
return JOURNAL_STATUS_ERROR;
|
||||
}
|
||||
if ((journal->log.offset == ARM_STORAGE_INVALID_OFFSET) ||
|
||||
(journal->log.tailOffset == ARM_STORAGE_INVALID_OFFSET) ||
|
||||
(journal->log.tailOffset < journal->log.offset) ||
|
||||
(journal->log.tail.sizeofBlob == 0) ||
|
||||
(journal->log.tail.sizeofBlob > journal->info.capacity)) {
|
||||
return JOURNAL_STATUS_ERROR; /* journal is in an un-expected state. */
|
||||
}
|
||||
uint32_t expectedCRC = headerP->genericHeader.checksum;
|
||||
headerP->genericHeader.checksum = 0;
|
||||
flashJournalCrcReset();
|
||||
uint32_t computedCRC = flashJournalCrcCummulative((const unsigned char *)&headerP->genericHeader, sizeof(SequentialFlashJournalLogHead_t));
|
||||
if (computedCRC != expectedCRC) {
|
||||
//printf("readAndVerifyJournalHeader: checksum mismatch during header verification: expected = %u, computed = %u\n", (unsigned int) expectedCRC, (unsigned int) computedCRC);
|
||||
return JOURNAL_STATUS_METADATA_ERROR;
|
||||
}
|
||||
|
||||
return JOURNAL_STATUS_OK;
|
||||
|
@ -111,9 +131,30 @@ int32_t flashJournalStrategySequential_initialize(FlashJournal_t *_jou
|
|||
{
|
||||
int32_t rc;
|
||||
|
||||
/* initialize MTD */
|
||||
rc = mtd->Initialize(mtdHandler);
|
||||
if (rc < ARM_DRIVER_OK) {
|
||||
memset(_journal, 0, sizeof(FlashJournal_t));
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
if (rc == ARM_DRIVER_OK) {
|
||||
ARM_STORAGE_CAPABILITIES mtdCaps = mtd->GetCapabilities();
|
||||
if (!mtdCaps.asynchronous_ops) {
|
||||
return JOURNAL_STATUS_ERROR; /* asynchronous_ops must be set if MTD returns ARM_DRIVER_OK. */
|
||||
}
|
||||
|
||||
return JOURNAL_STATUS_ERROR; /* TODO: handle init with pending asynchronous activity. */
|
||||
}
|
||||
|
||||
SequentialFlashJournal_t *journal;
|
||||
activeJournal = journal = (SequentialFlashJournal_t *)_journal;
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED;
|
||||
activeJournal = journal = (SequentialFlashJournal_t *)_journal;
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED;
|
||||
journal->mtd = mtd;
|
||||
|
||||
/* Setup start address within MTD. */
|
||||
if ((rc = mtdGetStartAddr(journal->mtd, &journal->mtdStartOffset)) != JOURNAL_STATUS_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* fetch MTD's total capacity */
|
||||
uint64_t mtdCapacity;
|
||||
|
@ -125,27 +166,27 @@ int32_t flashJournalStrategySequential_initialize(FlashJournal_t *_jou
|
|||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
|
||||
SequentialFlashJournalHeader_t journalHeader;
|
||||
if ((rc = readAndVerifyJournalHeader(journal, &journalHeader)) != JOURNAL_STATUS_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* initialize the journal structure */
|
||||
memcpy(&journal->ops, ops, sizeof(FlashJournal_Ops_t));
|
||||
journal->mtd = mtd;
|
||||
journal->mtdCapabilities = mtd->GetCapabilities(); /* fetch MTD's capabilities */
|
||||
journal->sequentialSkip = mtdCapacity / SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS;
|
||||
journal->info.capacity = journal->sequentialSkip - (sizeof(SequentialFlashJournalLogHead_t) + sizeof(SequentialFlashJournalLogTail_t)); /* effective capacity */
|
||||
|
||||
journal->firstSlotOffset = journalHeader.genericHeader.journalOffset;
|
||||
journal->numSlots = journalHeader.numSlots;
|
||||
journal->sizeofSlot = journalHeader.sizeofSlot;
|
||||
|
||||
/* effective capacity */
|
||||
journal->info.capacity = journal->sizeofSlot
|
||||
- roundUp_uint32(sizeof(SequentialFlashJournalLogHead_t), mtdInfo.program_unit)
|
||||
- roundUp_uint32(sizeof(SequentialFlashJournalLogTail_t), mtdInfo.program_unit);
|
||||
journal->info.program_unit = mtdInfo.program_unit;
|
||||
journal->callback = callback;
|
||||
journal->prevCommand = FLASH_JOURNAL_OPCODE_INITIALIZE;
|
||||
|
||||
/* initialize MTD */
|
||||
ARM_STORAGE_CAPABILITIES mtdCaps = mtd->GetCapabilities();
|
||||
rc = mtd->Initialize(mtdHandler);
|
||||
if (rc < ARM_DRIVER_OK) {
|
||||
memset(journal, 0, sizeof(FlashJournal_t));
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
if ((mtdCaps.asynchronous_ops) && (rc == ARM_DRIVER_OK)) {
|
||||
return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */
|
||||
}
|
||||
|
||||
if ((rc = discoverLatestLoggedBlob(journal)) != JOURNAL_STATUS_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
@ -167,6 +208,10 @@ int32_t flashJournalStrategySequential_read(FlashJournal_t *_journal, void *blob
|
|||
SequentialFlashJournal_t *journal;
|
||||
activeJournal = journal = (SequentialFlashJournal_t *)_journal;
|
||||
|
||||
if (journal->prevCommand != FLASH_JOURNAL_OPCODE_READ_BLOB) {
|
||||
journal->read.logicalOffset = 0;
|
||||
}
|
||||
|
||||
int32_t rc;
|
||||
if ((rc = flashJournalStrategySequential_read_sanityChecks(journal, blob, sizeofBlob)) != JOURNAL_STATUS_OK) {
|
||||
return rc;
|
||||
|
@ -175,18 +220,53 @@ int32_t flashJournalStrategySequential_read(FlashJournal_t *_journal, void *blob
|
|||
journal->read.blob = blob;
|
||||
journal->read.sizeofBlob = sizeofBlob;
|
||||
|
||||
if ((journal->prevCommand != FLASH_JOURNAL_OPCODE_READ_BLOB) || (journal->read.totalDataRead == 0)) {
|
||||
journal->read.offset = journal->mtdStartOffset
|
||||
+ (journal->currentBlobIndex * journal->sequentialSkip)
|
||||
+ sizeof(SequentialFlashJournalLogHead_t);
|
||||
journal->read.totalDataRead = 0;
|
||||
if (journal->read.logicalOffset == 0) {
|
||||
{ /* Establish the sanity of this slot before proceeding with the read. */
|
||||
uint32_t headSequenceNumber;
|
||||
SequentialFlashJournalLogTail_t tail;
|
||||
if (slotIsSane(journal,
|
||||
SLOT_ADDRESS(journal, journal->currentBlobIndex),
|
||||
&headSequenceNumber,
|
||||
&tail) != 1) {
|
||||
/* TODO: rollback to an older slot. */
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
journal->read.mtdOffset = SLOT_ADDRESS(journal, journal->currentBlobIndex) + sizeof(SequentialFlashJournalLogHead_t);
|
||||
} else {
|
||||
/* journal->read.offset is already set from the previous read execution */
|
||||
// printf("flashJournalStrategySequential_read: continuing read of %lu from offset %lu\n", sizeofBlob, (uint32_t)journal->read.offset);
|
||||
}
|
||||
journal->read.dataBeingRead = blob;
|
||||
journal->read.amountLeftToRead = ((journal->info.sizeofJournaledBlob - journal->read.totalDataRead) < sizeofBlob) ?
|
||||
(journal->info.sizeofJournaledBlob - journal->read.totalDataRead) : sizeofBlob;
|
||||
journal->read.amountLeftToRead = ((journal->info.sizeofJournaledBlob - journal->read.logicalOffset) < sizeofBlob) ?
|
||||
(journal->info.sizeofJournaledBlob - journal->read.logicalOffset) : sizeofBlob;
|
||||
// printf("amount left to read %u\n", journal->read.amountLeftToRead);
|
||||
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_READING;
|
||||
journal->prevCommand = FLASH_JOURNAL_OPCODE_READ_BLOB;
|
||||
return flashJournalStrategySequential_read_progress();
|
||||
}
|
||||
|
||||
int32_t flashJournalStrategySequential_readFrom(FlashJournal_t *_journal, size_t offset, void *blob, size_t sizeofBlob)
|
||||
{
|
||||
SequentialFlashJournal_t *journal;
|
||||
activeJournal = journal = (SequentialFlashJournal_t *)_journal;
|
||||
|
||||
journal->read.logicalOffset = offset;
|
||||
int32_t rc;
|
||||
if ((rc = flashJournalStrategySequential_read_sanityChecks(journal, blob, sizeofBlob)) != JOURNAL_STATUS_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
journal->read.blob = blob;
|
||||
journal->read.sizeofBlob = sizeofBlob;
|
||||
|
||||
journal->read.mtdOffset = SLOT_ADDRESS(journal, journal->currentBlobIndex) + sizeof(SequentialFlashJournalLogHead_t) + offset;
|
||||
|
||||
journal->read.dataBeingRead = blob;
|
||||
journal->read.amountLeftToRead = ((journal->info.sizeofJournaledBlob - journal->read.logicalOffset) < sizeofBlob) ?
|
||||
(journal->info.sizeofJournaledBlob - journal->read.logicalOffset) : sizeofBlob;
|
||||
// printf("amount left to read %u\n", journal->read.amountLeftToRead);
|
||||
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_READING;
|
||||
|
@ -208,24 +288,27 @@ int32_t flashJournalStrategySequential_log(FlashJournal_t *_journal, const void
|
|||
journal->log.sizeofBlob = size;
|
||||
|
||||
if (journal->prevCommand != FLASH_JOURNAL_OPCODE_LOG_BLOB) {
|
||||
/*
|
||||
* This is the first log in the sequence. We have to begin by identifying a new slot and erasing it.
|
||||
*/
|
||||
|
||||
/* choose the next slot */
|
||||
uint32_t logBlobIndex = journal->currentBlobIndex + 1;
|
||||
if (logBlobIndex == SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS) {
|
||||
if (logBlobIndex == journal->numSlots) {
|
||||
logBlobIndex = 0;
|
||||
}
|
||||
journal->log.eraseOffset = journal->mtdStartOffset + (logBlobIndex * journal->sequentialSkip);
|
||||
|
||||
/* ensure that the request is at least as large as the minimum program unit */
|
||||
if (size < journal->info.program_unit) {
|
||||
return JOURNAL_STATUS_SMALL_LOG_REQUEST;
|
||||
}
|
||||
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_ERASE; /* start with erasing the log region */
|
||||
journal->prevCommand = FLASH_JOURNAL_OPCODE_LOG_BLOB;
|
||||
/* setup an erase for the slot */
|
||||
journal->log.mtdEraseOffset = SLOT_ADDRESS(journal, logBlobIndex);
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_ERASE; /* start with erasing the log region */
|
||||
journal->prevCommand = FLASH_JOURNAL_OPCODE_LOG_BLOB;
|
||||
} else {
|
||||
/* This is a continuation of an ongoing logging sequence. */
|
||||
journal->log.dataBeingLogged = blob;
|
||||
journal->log.amountLeftToLog = size;
|
||||
}
|
||||
|
||||
/* progress the state machine for log() */
|
||||
return flashJournalStrategySequential_log_progress();
|
||||
}
|
||||
|
||||
|
@ -240,17 +323,21 @@ int32_t flashJournalStrategySequential_commit(FlashJournal_t *_journal)
|
|||
}
|
||||
|
||||
if (journal->prevCommand == FLASH_JOURNAL_OPCODE_LOG_BLOB) {
|
||||
journal->log.offset = journal->log.tailOffset;
|
||||
/* the tail has already been setup during previous calls to log(); we can now include it in the crc32. */
|
||||
journal->log.tail.crc32 = flashJournalCrcCummulative((const unsigned char *)&journal->log.tail, sizeof(SequentialFlashJournalLogTail_t));
|
||||
flashJournalCrcReset();
|
||||
|
||||
journal->log.mtdOffset = journal->log.mtdTailOffset;
|
||||
journal->log.dataBeingLogged = (const uint8_t *)&journal->log.tail;
|
||||
journal->log.amountLeftToLog = sizeof(SequentialFlashJournalLogTail_t);
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_TAIL;
|
||||
} else {
|
||||
uint32_t logBlobIndex = journal->currentBlobIndex + 1;
|
||||
if (logBlobIndex == SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS) {
|
||||
if (logBlobIndex == journal->numSlots) {
|
||||
logBlobIndex = 0;
|
||||
}
|
||||
journal->log.eraseOffset = journal->mtdStartOffset + (logBlobIndex * journal->sequentialSkip);
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_ERASE;
|
||||
journal->log.mtdEraseOffset = SLOT_ADDRESS(journal, logBlobIndex);
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_ERASE;
|
||||
}
|
||||
|
||||
journal->prevCommand = FLASH_JOURNAL_OPCODE_COMMIT;
|
||||
|
@ -267,3 +354,124 @@ int32_t flashJournalStrategySequential_reset(FlashJournal_t *_journal)
|
|||
journal->prevCommand = FLASH_JOURNAL_OPCODE_RESET;
|
||||
return flashJournalStrategySequential_reset_progress();
|
||||
}
|
||||
|
||||
int32_t mtdGetTotalCapacity(ARM_DRIVER_STORAGE *mtd, uint64_t *capacityP)
|
||||
{
|
||||
/* fetch MTD's INFO */
|
||||
ARM_STORAGE_INFO mtdInfo;
|
||||
int32_t rc = mtd->GetInfo(&mtdInfo);
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
*capacityP = mtdInfo.total_storage;
|
||||
|
||||
return JOURNAL_STATUS_OK;
|
||||
}
|
||||
|
||||
int32_t flashJournalStrategySequential_format_sanityChecks(ARM_DRIVER_STORAGE *mtd, uint32_t numSlots)
|
||||
{
|
||||
/*
|
||||
* basic parameter checking
|
||||
*/
|
||||
if ((mtd == NULL) || (numSlots == 0)) {
|
||||
return JOURNAL_STATUS_PARAMETER;
|
||||
}
|
||||
|
||||
ARM_STORAGE_INFO mtdInfo;
|
||||
if (mtd->GetInfo(&mtdInfo) < ARM_DRIVER_OK) {
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
if (mtdInfo.total_storage == 0) {
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
|
||||
uint64_t mtdAddr;
|
||||
if (mtdGetStartAddr(mtd, &mtdAddr) < JOURNAL_STATUS_OK) {
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
if (mtd->GetBlock(mtdAddr, NULL) < ARM_DRIVER_OK) { /* check validity of journal's start address */
|
||||
return JOURNAL_STATUS_PARAMETER;
|
||||
}
|
||||
if (mtd->GetBlock(mtdAddr + mtdInfo.total_storage - 1, NULL) < ARM_DRIVER_OK) { /* check validity of the journal's end address */
|
||||
return JOURNAL_STATUS_PARAMETER;
|
||||
}
|
||||
|
||||
if ((mtdAddr % mtdInfo.program_unit) != 0) { /* ensure that the journal starts at a programmable unit */
|
||||
return JOURNAL_STATUS_PARAMETER;
|
||||
}
|
||||
if ((mtdAddr % LCM_OF_ALL_ERASE_UNITS) != 0) { /* ensure that the journal starts and ends at an erase-boundary */
|
||||
return JOURNAL_STATUS_PARAMETER;
|
||||
}
|
||||
|
||||
return JOURNAL_STATUS_OK;
|
||||
}
|
||||
|
||||
int32_t flashJournalStrategySequential_read_sanityChecks(SequentialFlashJournal_t *journal, const void *blob, size_t sizeofBlob)
|
||||
{
|
||||
if ((journal == NULL) || (blob == NULL) || (sizeofBlob == 0)) {
|
||||
return JOURNAL_STATUS_PARAMETER;
|
||||
}
|
||||
if ((journal->state == SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED) || (journal->state == SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS)) {
|
||||
return JOURNAL_STATUS_NOT_INITIALIZED;
|
||||
}
|
||||
if (journal->state != SEQUENTIAL_JOURNAL_STATE_INITIALIZED) {
|
||||
return JOURNAL_STATUS_ERROR; /* journal is in an un-expected state. */
|
||||
}
|
||||
// printf("read sanity checks: logicalOffset = %lu, sizeofJournaledBlob = %lu\n", (uint32_t)journal->read.logicalOffset, (uint32_t)journal->info.sizeofJournaledBlob);
|
||||
if ((journal->info.sizeofJournaledBlob == 0) || (journal->read.logicalOffset >= journal->info.sizeofJournaledBlob)) {
|
||||
journal->read.logicalOffset = 0;
|
||||
return JOURNAL_STATUS_EMPTY;
|
||||
}
|
||||
|
||||
return JOURNAL_STATUS_OK;
|
||||
}
|
||||
|
||||
int32_t flashJournalStrategySequential_log_sanityChecks(SequentialFlashJournal_t *journal, const void *blob, size_t sizeofBlob)
|
||||
{
|
||||
if ((journal == NULL) || (blob == NULL) || (sizeofBlob == 0)) {
|
||||
return JOURNAL_STATUS_PARAMETER;
|
||||
}
|
||||
if ((journal->state == SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED) || (journal->state == SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS)) {
|
||||
return JOURNAL_STATUS_NOT_INITIALIZED;
|
||||
}
|
||||
if ((journal->state != SEQUENTIAL_JOURNAL_STATE_INITIALIZED) && (journal->state != SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY)) {
|
||||
return JOURNAL_STATUS_ERROR; /* journal is in an un-expected state. */
|
||||
}
|
||||
if (journal->state == SEQUENTIAL_JOURNAL_STATE_INITIALIZED) {
|
||||
if (sizeofBlob > journal->info.capacity) {
|
||||
return JOURNAL_STATUS_BOUNDED_CAPACITY; /* adding this log chunk would cause us to exceed capacity (write past the tail). */
|
||||
}
|
||||
} else if (journal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY) {
|
||||
if (journal->log.mtdOffset + sizeofBlob > journal->log.mtdTailOffset) {
|
||||
return JOURNAL_STATUS_BOUNDED_CAPACITY; /* adding this log chunk would cause us to exceed capacity (write past the tail). */
|
||||
}
|
||||
}
|
||||
|
||||
/* ensure that the request is at least as large as the minimum program unit */
|
||||
if (sizeofBlob < journal->info.program_unit) {
|
||||
return JOURNAL_STATUS_SMALL_LOG_REQUEST;
|
||||
}
|
||||
|
||||
return JOURNAL_STATUS_OK;
|
||||
}
|
||||
|
||||
int32_t flashJournalStrategySequential_commit_sanityChecks(SequentialFlashJournal_t *journal)
|
||||
{
|
||||
if (journal == NULL) {
|
||||
return JOURNAL_STATUS_PARAMETER;
|
||||
}
|
||||
if (journal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY) {
|
||||
if (journal->prevCommand != FLASH_JOURNAL_OPCODE_LOG_BLOB) {
|
||||
return JOURNAL_STATUS_ERROR;
|
||||
}
|
||||
if ((journal->log.mtdOffset == ARM_STORAGE_INVALID_OFFSET) ||
|
||||
(journal->log.mtdTailOffset == ARM_STORAGE_INVALID_OFFSET) ||
|
||||
(journal->log.mtdTailOffset < journal->log.mtdOffset) ||
|
||||
(journal->log.tail.sizeofBlob == 0) ||
|
||||
(journal->log.tail.sizeofBlob > journal->info.capacity)) {
|
||||
return JOURNAL_STATUS_ERROR; /* journal is in an un-expected state. */
|
||||
}
|
||||
}
|
||||
|
||||
return JOURNAL_STATUS_OK;
|
||||
}
|
||||
|
|
|
@ -15,12 +15,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "flash-journal-strategy-sequential/flash_journal_crc.h"
|
||||
#include "support_funcs.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
static inline int32_t mtdGetStartAddr(ARM_DRIVER_STORAGE *mtd, uint64_t *startAddrP)
|
||||
struct FormatInfo_t formatInfoSingleton;
|
||||
|
||||
int32_t mtdGetStartAddr(ARM_DRIVER_STORAGE *mtd, uint64_t *startAddrP)
|
||||
{
|
||||
ARM_STORAGE_BLOCK mtdBlock;
|
||||
if ((mtd->GetNextBlock(NULL, &mtdBlock)) != ARM_DRIVER_OK) {
|
||||
|
@ -34,130 +37,295 @@ static inline int32_t mtdGetStartAddr(ARM_DRIVER_STORAGE *mtd, uint64_t *startAd
|
|||
return JOURNAL_STATUS_OK;
|
||||
}
|
||||
|
||||
int32_t discoverLatestLoggedBlob(SequentialFlashJournal_t *journal)
|
||||
/**
|
||||
* Check the sanity of a given slot
|
||||
* @param journal
|
||||
* @param slotOffset
|
||||
* @param [out] headSequenceNumberP
|
||||
* sequence number of the slot as read from the header.
|
||||
* @param [out] tailP
|
||||
* the tail of the slot
|
||||
* @return 1 if the slot is valid; i.e. if head and tail match, and if CRC32 agrees.
|
||||
*/
|
||||
int32_t slotIsSane(SequentialFlashJournal_t *journal,
|
||||
uint64_t slotOffset,
|
||||
uint32_t *headSequenceNumberP,
|
||||
SequentialFlashJournalLogTail_t *tailP)
|
||||
{
|
||||
int32_t rc;
|
||||
ARM_DRIVER_STORAGE *mtd = journal->mtd;
|
||||
|
||||
SequentialFlashJournalLogHead_t head;
|
||||
/* TODO: add support for asynchronous read */
|
||||
if (((rc = mtd->ReadData(slotOffset, &head, sizeof(SequentialFlashJournalLogHead_t))) < ARM_DRIVER_OK) ||
|
||||
(rc != sizeof(SequentialFlashJournalLogHead_t))) {
|
||||
if ((rc == ARM_DRIVER_OK) && (journal->mtdCapabilities.asynchronous_ops)) {
|
||||
return JOURNAL_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
|
||||
/* compute the CRC32 of the header */
|
||||
flashJournalCrcReset();
|
||||
flashJournalCrcCummulative((const unsigned char *)&head, sizeof(SequentialFlashJournalLogHead_t));
|
||||
|
||||
// printf("head->version: %lu\n", journal->initScan.head.version);
|
||||
// printf("head->magic: %lx\n", journal->initScan.head.magic);
|
||||
// printf("head->sequenceNumber: %lu\n", journal->initScan.head.sequenceNumber);
|
||||
// printf("head->reserved: %lu\n", journal->initScan.head.reserved);
|
||||
|
||||
if (SEQUENTIAL_JOURNAL_VALID_HEAD(&head)) {
|
||||
*headSequenceNumberP = head.sequenceNumber;
|
||||
// printf("found valid header with sequenceNumber %" PRIu32 "\n", *headSequenceNumberP);
|
||||
|
||||
uint64_t tailoffset = slotOffset
|
||||
- ((slotOffset - SLOT_ADDRESS(journal, 0)) % journal->sizeofSlot)
|
||||
+ journal->sizeofSlot
|
||||
- sizeof(SequentialFlashJournalLogTail_t);
|
||||
// printf("hoping to read a tail at offset %lu\n", (uint32_t)tailoffset);
|
||||
|
||||
/* TODO: add support for asynchronous read */
|
||||
if (((rc = mtd->ReadData(tailoffset, tailP, sizeof(SequentialFlashJournalLogTail_t))) < ARM_DRIVER_OK) ||
|
||||
(rc != sizeof(SequentialFlashJournalLogTail_t))) {
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
|
||||
if (SEQUENTIAL_JOURNAL_VALID_TAIL(tailP) && (tailP->sequenceNumber == *headSequenceNumberP)) {
|
||||
// printf("found valid tail\n");
|
||||
|
||||
/* iterate over the body of the slot computing CRC */
|
||||
#define CRC_CHUNK_SIZE 64
|
||||
uint8_t crcBuffer[CRC_CHUNK_SIZE];
|
||||
uint64_t bodyIndex = 0;
|
||||
uint64_t bodyOffset = slotOffset + sizeof(SequentialFlashJournalLogHead_t);
|
||||
while (bodyIndex < tailP->sizeofBlob) {
|
||||
size_t sizeofReadOperation;
|
||||
if ((tailP->sizeofBlob - bodyIndex) > CRC_CHUNK_SIZE) {
|
||||
sizeofReadOperation = CRC_CHUNK_SIZE;
|
||||
} else {
|
||||
sizeofReadOperation = (tailP->sizeofBlob - bodyIndex);
|
||||
}
|
||||
|
||||
/* TODO: add support for asynchronous read */
|
||||
rc = mtd->ReadData(bodyOffset + bodyIndex, crcBuffer, sizeofReadOperation);
|
||||
if (rc != (int32_t)sizeofReadOperation) {
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
|
||||
bodyIndex += sizeofReadOperation;
|
||||
flashJournalCrcCummulative(crcBuffer, sizeofReadOperation);
|
||||
}
|
||||
|
||||
/* compute CRC32 over the tail */
|
||||
/* extract existing CRC32 from the tail. The CRC32 field in the tail needs to contain 0 before CRC32 can be computed over it. */
|
||||
uint32_t expectedCRC32 = tailP->crc32;
|
||||
tailP->crc32 = 0;
|
||||
|
||||
uint32_t crc32 = flashJournalCrcCummulative((const unsigned char *)tailP, sizeof(SequentialFlashJournalLogTail_t));
|
||||
flashJournalCrcReset();
|
||||
// printf("expectedCRC32: 0x%x, computedCRC32: 0x%x\n", expectedCRC32, crc32);
|
||||
if (crc32 == expectedCRC32) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JOURNAL_STATUS_ERROR;
|
||||
}
|
||||
|
||||
int32_t setupSequentialJournalHeader(SequentialFlashJournalHeader_t *headerP, ARM_DRIVER_STORAGE *mtd, uint64_t totalSize, uint32_t numSlots)
|
||||
{
|
||||
ARM_STORAGE_INFO mtdInfo;
|
||||
if (mtd->GetInfo(&mtdInfo) < ARM_DRIVER_OK) {
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
|
||||
headerP->genericHeader.magic = FLASH_JOURNAL_HEADER_MAGIC;
|
||||
headerP->genericHeader.version = FLASH_JOURNAL_HEADER_VERSION;
|
||||
headerP->genericHeader.sizeofHeader = sizeof(SequentialFlashJournalHeader_t);
|
||||
|
||||
/* Determine 'journalOffset'.
|
||||
* Constraint: journal header should start and terminate at an erase-boundary
|
||||
* (so that slot-0 can be erased independently), and also a program-unit boundary.
|
||||
*/
|
||||
headerP->genericHeader.journalOffset = roundUp_uint32(headerP->genericHeader.sizeofHeader, LCM_OF_ALL_ERASE_UNITS);
|
||||
if ((headerP->genericHeader.journalOffset % mtdInfo.program_unit) != 0) {
|
||||
//printf("setupSequentialJournalHeader: journalOffset is not a multiple of MTD's program_unit\r\n");
|
||||
return JOURNAL_STATUS_PARAMETER;
|
||||
}
|
||||
|
||||
headerP->magic = SEQUENTIAL_FLASH_JOURNAL_HEADER_MAGIC;
|
||||
headerP->version = SEQUENTIAL_FLASH_JOURNAL_HEADER_VERSION;
|
||||
headerP->numSlots = numSlots;
|
||||
|
||||
/* Determine 'sizeofSlot'.
|
||||
* Constraint: slot-size should be a multiple of the erase-units of all involved storage blocks.
|
||||
*/
|
||||
uint64_t spaceAvailableForSlots = totalSize - headerP->genericHeader.journalOffset;
|
||||
headerP->sizeofSlot = roundDown_uint32(spaceAvailableForSlots / numSlots, LCM_OF_ALL_ERASE_UNITS);
|
||||
if (headerP->sizeofSlot == 0) {
|
||||
//printf("setupSequentialJournalHeader: not enough space to create %" PRIu32 " slots\r\n", numSlots);
|
||||
return JOURNAL_STATUS_PARAMETER;
|
||||
}
|
||||
|
||||
headerP->genericHeader.totalSize = headerP->genericHeader.journalOffset + (headerP->sizeofSlot * numSlots);
|
||||
//printf("setupSequentialJournalHeader: header size = %" PRIu32 ", journalOffset = %" PRIu32 ", sizeofSlot = %" PRIu32 ", totalSize = %lu\n", headerP->genericHeader.sizeofHeader, headerP->genericHeader.journalOffset, headerP->sizeofSlot, (uint32_t)headerP->genericHeader.totalSize);
|
||||
|
||||
/* compute checksum over the entire header */
|
||||
headerP->genericHeader.checksum = 0;
|
||||
flashJournalCrcReset();
|
||||
headerP->genericHeader.checksum = flashJournalCrcCummulative((const unsigned char *)&headerP->genericHeader, sizeof(SequentialFlashJournalLogHead_t));
|
||||
|
||||
return JOURNAL_STATUS_OK;
|
||||
}
|
||||
|
||||
int32_t discoverLatestLoggedBlob(SequentialFlashJournal_t *journal)
|
||||
{
|
||||
/* reset top level journal metadata prior to scanning headers. */
|
||||
journal->nextSequenceNumber = SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER; /* we are currently unaware of previously written blobs */
|
||||
journal->currentBlobIndex = SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS;
|
||||
journal->currentBlobIndex = journal->numSlots;
|
||||
journal->info.sizeofJournaledBlob = 0;
|
||||
|
||||
/* begin header-scan from the first block of the MTD */
|
||||
ARM_DRIVER_STORAGE *mtd = journal->mtd;
|
||||
if ((rc = mtdGetStartAddr(journal->mtd, &journal->mtdStartOffset)) != JOURNAL_STATUS_OK) {
|
||||
return rc;
|
||||
}
|
||||
journal->initScan.currentOffset = journal->mtdStartOffset;
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS;
|
||||
journal->initScan.currentOffset = SLOT_ADDRESS(journal, 0);
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS;
|
||||
|
||||
// printf("start of init scan\n");
|
||||
// printf("discoverLatestLoggedBlob: start of init scan\n");
|
||||
for (unsigned blobIndex = 0;
|
||||
blobIndex < SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS;
|
||||
blobIndex++, journal->initScan.currentOffset += journal->sequentialSkip) {
|
||||
// printf("blob index %u\n", blobIndex);
|
||||
blobIndex < journal->numSlots;
|
||||
blobIndex++, journal->initScan.currentOffset += journal->sizeofSlot) {
|
||||
// printf("discoverLatestLoggedBlob: blob index %u\n", blobIndex);
|
||||
/* TODO: it is possible that the header structure spans multiple blocks, needing multiple reads. */
|
||||
|
||||
if (((rc = mtd->ReadData(journal->initScan.currentOffset, &journal->initScan.head, sizeof(SequentialFlashJournalLogHead_t))) < ARM_DRIVER_OK) ||
|
||||
(rc != sizeof(SequentialFlashJournalLogHead_t))) {
|
||||
/* TODO: add support for asynchronous scan */
|
||||
if ((rc == ARM_DRIVER_OK) && (journal->mtdCapabilities.asynchronous_ops)) {
|
||||
return JOURNAL_STATUS_UNSUPPORTED;
|
||||
if (slotIsSane(journal,
|
||||
journal->initScan.currentOffset,
|
||||
&journal->initScan.headSequenceNumber,
|
||||
&journal->initScan.tail) == 1) {
|
||||
// printf("found valid blob with sequence number %lu\n", journal->initScan.headSequenceNumber);
|
||||
uint32_t nextSequenceNumber = journal->initScan.headSequenceNumber + 1;
|
||||
if (nextSequenceNumber == SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER) {
|
||||
nextSequenceNumber = 0;
|
||||
}
|
||||
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
// printf("head->version: %lu\n", journal->initScan.head.version);
|
||||
// printf("head->magic: %lx\n", journal->initScan.head.magic);
|
||||
// printf("head->sequenceNumber: %lu\n", journal->initScan.head.sequenceNumber);
|
||||
// printf("head->reserved: %lu\n", journal->initScan.head.reserved);
|
||||
|
||||
if (SEQUENTIAL_JOURNAL_VALID_HEAD(&journal->initScan.head)) {
|
||||
journal->initScan.headSequenceNumber = journal->initScan.head.sequenceNumber;
|
||||
// printf("found valid header with sequenceNumber %lu\n", journal->initScan.headSequenceNumber);
|
||||
|
||||
uint64_t tailoffset = journal->initScan.currentOffset
|
||||
- ((journal->initScan.currentOffset - journal->mtdStartOffset) % journal->sequentialSkip)
|
||||
+ journal->sequentialSkip
|
||||
- sizeof(SequentialFlashJournalLogTail_t);
|
||||
|
||||
// printf("hoping to read a tail at offset %lu\n", (uint32_t)tailoffset);
|
||||
if (((rc = mtd->ReadData(tailoffset, &journal->initScan.tail, sizeof(SequentialFlashJournalLogTail_t))) < ARM_DRIVER_OK) ||
|
||||
(rc != sizeof(SequentialFlashJournalLogTail_t))) {
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
|
||||
if (SEQUENTIAL_JOURNAL_VALID_TAIL(&journal->initScan.tail) &&
|
||||
(journal->initScan.tail.sequenceNumber == journal->initScan.headSequenceNumber)) {
|
||||
// printf("found valid blob with sequence number %lu\n", journal->initScan.headSequenceNumber);
|
||||
uint32_t nextSequenceNumber = journal->initScan.headSequenceNumber + 1;
|
||||
if (nextSequenceNumber == SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER) {
|
||||
nextSequenceNumber = 0;
|
||||
}
|
||||
|
||||
if ((journal->nextSequenceNumber == SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER) ||
|
||||
/* We take advantage of properties of unsigned arithmetic in the following
|
||||
* expression.
|
||||
*
|
||||
* We want to calculate if (nextSequenceNumber > journal->nextSequenceNumber),
|
||||
* instead we use the expression ((nextSequenceNumber - journal->nextSequenceNumber) > 0)
|
||||
* to take wraparounds into account.
|
||||
*/
|
||||
((int32_t)(nextSequenceNumber - journal->nextSequenceNumber) > 0)) {
|
||||
journal->currentBlobIndex = blobIndex;
|
||||
journal->nextSequenceNumber = nextSequenceNumber;
|
||||
journal->info.sizeofJournaledBlob = journal->initScan.tail.sizeofBlob;
|
||||
// printf("discoverLatestLoggedBlob: index %lu, sizeofBlob: %lu, nextSequenceNumber: %lu\n",
|
||||
// journal->currentBlobIndex, (uint32_t)journal->info.sizeofJournaledBlob, journal->nextSequenceNumber);
|
||||
}
|
||||
/* Have we found the best of the slots seen so far? */
|
||||
if ((journal->nextSequenceNumber == SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER) ||
|
||||
/* We take advantage of properties of unsigned arithmetic in the following
|
||||
* expression.
|
||||
*
|
||||
* We want to calculate if (nextSequenceNumber > journal->nextSequenceNumber),
|
||||
* instead we use the expression ((nextSequenceNumber - journal->nextSequenceNumber) > 0)
|
||||
* to take wraparounds into account.
|
||||
*/
|
||||
((int32_t)(nextSequenceNumber - journal->nextSequenceNumber) > 0)) {
|
||||
journal->currentBlobIndex = blobIndex;
|
||||
journal->nextSequenceNumber = nextSequenceNumber;
|
||||
journal->info.sizeofJournaledBlob = journal->initScan.tail.sizeofBlob;
|
||||
// printf("discoverLatestLoggedBlob: index %lu, sizeofBlob: %lu, nextSequenceNumber: %lu\n",
|
||||
// journal->currentBlobIndex, (uint32_t)journal->info.sizeofJournaledBlob, journal->nextSequenceNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
// printf("finished init scan\n");
|
||||
// printf("discoverLatestLoggedBlob: finished init scan\n");
|
||||
|
||||
/* Handle the case where our scan hasn't yielded any results. */
|
||||
if (journal->nextSequenceNumber == SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER) {
|
||||
// printf("discoverLatestLoggedBlob: initializing to defaults\n");
|
||||
journal->currentBlobIndex = (uint32_t)-1; /* to be incremented to 0 during the first attempt to log(). */
|
||||
journal->nextSequenceNumber = 0;
|
||||
|
||||
/* setup info.current_program_unit */
|
||||
ARM_STORAGE_BLOCK storageBlock;
|
||||
if ((rc = journal->mtd->GetBlock(journal->mtdStartOffset, &storageBlock)) != ARM_DRIVER_OK) {
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED;
|
||||
|
||||
return JOURNAL_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Progress the state machine for the 'format' operation. This method can also be called from an interrupt handler.
|
||||
* @return < JOURNAL_STATUS_OK for error
|
||||
* = JOURNAL_STATUS_OK to signal pending asynchronous activity
|
||||
* > JOURNAL_STATUS_OK for completion
|
||||
*/
|
||||
int32_t flashJournalStrategySequential_format_progress(int32_t status, ARM_STORAGE_OPERATION operationWhichJustFinshed)
|
||||
{
|
||||
int32_t rc;
|
||||
size_t sizeofWrite = roundUp_uint32(formatInfoSingleton.header.genericHeader.sizeofHeader, formatInfoSingleton.mtdProgramUnit);
|
||||
size_t sizeofErase = roundUp_uint32(formatInfoSingleton.header.genericHeader.sizeofHeader, LCM_OF_ALL_ERASE_UNITS);
|
||||
switch (operationWhichJustFinshed) {
|
||||
case ARM_STORAGE_OPERATION_INITIALIZE:
|
||||
if (status != ARM_DRIVER_OK) {
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
|
||||
// printf("erasing %u bytes from offset %u\n", roundUp_uint32(header.genericHeader.sizeofHeader, mtdInfo.program_unit), mtdAddr);
|
||||
rc = (formatInfoSingleton.mtd)->Erase(formatInfoSingleton.mtdAddr, sizeofErase);
|
||||
if (rc < ARM_DRIVER_OK) {
|
||||
if (rc == ARM_STORAGE_ERROR_RUNTIME_OR_INTEGRITY_FAILURE) {
|
||||
return JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE;
|
||||
} else {
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
} else if (rc == ARM_DRIVER_OK) {
|
||||
return JOURNAL_STATUS_OK; /* An asynchronous operation is pending; it will result in a completion callback
|
||||
* where the rest of processing will take place. */
|
||||
}
|
||||
/* handle synchronous completion of programData */
|
||||
status = rc;
|
||||
|
||||
/* intentional fall-through */
|
||||
|
||||
case ARM_STORAGE_OPERATION_ERASE:
|
||||
if (status != (int32_t)sizeofErase) {
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
|
||||
// printf("calling ProgramData at address %u for %u bytes\n",
|
||||
// formatInfoSingleton.mtdAddr, roundUp_uint32(formatInfoSingleton.header.genericHeader.sizeofHeader, formatInfoSingleton.mtdProgramUnit));
|
||||
rc = (formatInfoSingleton.mtd)->ProgramData(formatInfoSingleton.mtdAddr, &(formatInfoSingleton.header), sizeofWrite);
|
||||
if (rc < ARM_DRIVER_OK) {
|
||||
if (rc == ARM_STORAGE_ERROR_RUNTIME_OR_INTEGRITY_FAILURE) {
|
||||
return JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE;
|
||||
} else {
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
} else if (rc == ARM_DRIVER_OK) {
|
||||
return JOURNAL_STATUS_OK; /* An asynchronous operation is pending; it will result in a completion callback
|
||||
* where the rest of processing will take place. */
|
||||
}
|
||||
/* handle synchronous completion of programData */
|
||||
status = rc;
|
||||
|
||||
/* intentional fall-through */
|
||||
|
||||
case ARM_STORAGE_OPERATION_PROGRAM_DATA:
|
||||
if (status != (int32_t)sizeofWrite) {
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
|
||||
return 1; /* acknowledge the completion of create */
|
||||
|
||||
default:
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR; /* we don't expect to be here */
|
||||
}
|
||||
}
|
||||
|
||||
int32_t flashJournalStrategySequential_reset_progress(void)
|
||||
{
|
||||
int32_t rc;
|
||||
SequentialFlashJournal_t *journal = activeJournal;
|
||||
|
||||
if (journal->mtdCapabilities.erase_all) {
|
||||
if ((rc = journal->mtd->EraseAll()) < ARM_DRIVER_OK) {
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */
|
||||
if ((rc = journal->mtd->Erase(SLOT_ADDRESS(journal, 0), journal->numSlots * journal->sizeofSlot)) < ARM_DRIVER_OK) {
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */
|
||||
if (rc == ARM_STORAGE_ERROR_RUNTIME_OR_INTEGRITY_FAILURE) {
|
||||
return JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE;
|
||||
} else {
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
if ((journal->mtdCapabilities.asynchronous_ops) && (rc == ARM_DRIVER_OK)) {
|
||||
return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */
|
||||
}
|
||||
/* else we fall through to handle synchronous completion */
|
||||
} else {
|
||||
if ((rc = journal->mtd->Erase(journal->mtdStartOffset, SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS * journal->sequentialSkip)) < ARM_DRIVER_OK) {
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
if ((journal->mtdCapabilities.asynchronous_ops) && (rc == ARM_DRIVER_OK)) {
|
||||
printf("eturning JOURNAL_STATUS_OK\n");
|
||||
return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */
|
||||
}
|
||||
/* else we fall through to handle synchronous completion */
|
||||
}
|
||||
if ((journal->mtdCapabilities.asynchronous_ops) && (rc == ARM_DRIVER_OK)) {
|
||||
//printf("eturning JOURNAL_STATUS_OK\n");
|
||||
return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */
|
||||
}
|
||||
/* else we fall through to handle synchronous completion */
|
||||
|
||||
journal->nextSequenceNumber = 0;
|
||||
journal->currentBlobIndex = (uint32_t)-1;
|
||||
|
@ -179,11 +347,11 @@ int32_t flashJournalStrategySequential_read_progress(void)
|
|||
ARM_STORAGE_BLOCK storageBlock;
|
||||
|
||||
if ((journal->read.amountLeftToRead) &&
|
||||
((rc = journal->mtd->GetBlock(journal->read.offset, &storageBlock)) != ARM_DRIVER_OK)) {
|
||||
((rc = journal->mtd->GetBlock(journal->read.mtdOffset, &storageBlock)) != ARM_DRIVER_OK)) {
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
uint64_t storageBlockAvailableCapacity = storageBlock.size - (journal->read.offset - storageBlock.addr);
|
||||
uint64_t storageBlockAvailableCapacity = storageBlock.size - (journal->read.mtdOffset - storageBlock.addr);
|
||||
|
||||
while (journal->read.amountLeftToRead) {
|
||||
while (!storageBlockAvailableCapacity) {
|
||||
|
@ -191,7 +359,7 @@ int32_t flashJournalStrategySequential_read_progress(void)
|
|||
journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */
|
||||
return JOURNAL_STATUS_ERROR; /* We ran out of storage blocks. Journal is in an un-expected state. */
|
||||
}
|
||||
journal->read.offset = storageBlock.addr; /* This should not be necessary since we assume
|
||||
journal->read.mtdOffset = storageBlock.addr; /* This should not be necessary since we assume
|
||||
* storage map manages a contiguous address space. */
|
||||
storageBlockAvailableCapacity = storageBlock.size;
|
||||
}
|
||||
|
@ -201,8 +369,8 @@ int32_t flashJournalStrategySequential_read_progress(void)
|
|||
journal->read.amountLeftToRead : storageBlockAvailableCapacity;
|
||||
|
||||
/* perform the IO */
|
||||
//printf("reading %lu bytes at offset %lu\n", xfer, (uint32_t)journal->read.offset);
|
||||
rc = journal->mtd->ReadData(journal->read.offset, journal->read.dataBeingRead, xfer);
|
||||
//printf("reading %lu bytes at offset %lu\n", xfer, (uint32_t)journal->read.mtdOffset);
|
||||
rc = journal->mtd->ReadData(journal->read.mtdOffset, journal->read.dataBeingRead, xfer);
|
||||
if (rc < ARM_DRIVER_OK) {
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
|
@ -211,10 +379,10 @@ int32_t flashJournalStrategySequential_read_progress(void)
|
|||
return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */
|
||||
} else {
|
||||
/* synchronous completion. 'rc' contains the actual number of bytes transferred. */
|
||||
journal->read.offset += rc;
|
||||
journal->read.mtdOffset += rc;
|
||||
journal->read.amountLeftToRead -= rc;
|
||||
journal->read.dataBeingRead += rc;
|
||||
journal->read.totalDataRead += rc;
|
||||
journal->read.logicalOffset += rc;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,6 +390,12 @@ int32_t flashJournalStrategySequential_read_progress(void)
|
|||
return (journal->read.dataBeingRead - journal->read.blob);
|
||||
}
|
||||
|
||||
/**
|
||||
* Progress the state machine for the 'log' operation. This method can also be called from an interrupt handler.
|
||||
* @return < JOURNAL_STATUS_OK for error
|
||||
* = JOURNAL_STATUS_OK to signal pending asynchronous activity
|
||||
* > JOURNAL_STATUS_OK for completion
|
||||
*/
|
||||
int32_t flashJournalStrategySequential_log_progress(void)
|
||||
{
|
||||
SequentialFlashJournal_t *journal = activeJournal;
|
||||
|
@ -234,7 +408,7 @@ int32_t flashJournalStrategySequential_log_progress(void)
|
|||
}
|
||||
|
||||
uint32_t blobIndexBeingLogged = journal->currentBlobIndex + 1;
|
||||
if (blobIndexBeingLogged == SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS) {
|
||||
if (blobIndexBeingLogged == journal->numSlots) {
|
||||
blobIndexBeingLogged = 0;
|
||||
}
|
||||
|
||||
|
@ -242,22 +416,24 @@ int32_t flashJournalStrategySequential_log_progress(void)
|
|||
int32_t rc;
|
||||
|
||||
if (journal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_ERASE) {
|
||||
uint64_t amountLeftToErase = journal->mtdStartOffset
|
||||
+ (blobIndexBeingLogged + 1) * journal->sequentialSkip
|
||||
- journal->log.eraseOffset;
|
||||
uint64_t amountLeftToErase = SLOT_ADDRESS(journal, blobIndexBeingLogged + 1) - journal->log.mtdEraseOffset;
|
||||
// printf("journal state: erasing; offset %lu [size %lu]\n",
|
||||
// (uint32_t)journal->log.eraseOffset, (uint32_t)amountLeftToErase);
|
||||
while (amountLeftToErase) {
|
||||
if ((rc = journal->mtd->Erase(journal->log.eraseOffset, amountLeftToErase)) < ARM_DRIVER_OK) {
|
||||
if ((rc = journal->mtd->Erase(journal->log.mtdEraseOffset, amountLeftToErase)) < ARM_DRIVER_OK) {
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */
|
||||
return JOURNAL_STATUS_ERROR; /* We ran out of storage blocks. Journal is in an un-expected state. */
|
||||
if (rc == ARM_STORAGE_ERROR_RUNTIME_OR_INTEGRITY_FAILURE) {
|
||||
return JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE;
|
||||
} else {
|
||||
return JOURNAL_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
if ((journal->mtdCapabilities.asynchronous_ops) && (rc == ARM_DRIVER_OK)) {
|
||||
return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */
|
||||
} else {
|
||||
/* synchronous completion. */
|
||||
journal->log.eraseOffset += rc;
|
||||
amountLeftToErase -= rc;
|
||||
journal->log.mtdEraseOffset += rc;
|
||||
amountLeftToErase -= rc;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -273,11 +449,11 @@ int32_t flashJournalStrategySequential_log_progress(void)
|
|||
}
|
||||
|
||||
/* check for alignment of next log offset with program_unit */
|
||||
if ((rc = journal->mtd->GetBlock(journal->log.offset, &storageBlock)) != ARM_DRIVER_OK) {
|
||||
if ((rc = journal->mtd->GetBlock(journal->log.mtdOffset, &storageBlock)) != ARM_DRIVER_OK) {
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */
|
||||
return JOURNAL_STATUS_STORAGE_API_ERROR;
|
||||
}
|
||||
if ((journal->log.offset - storageBlock.addr) % journal->info.program_unit) {
|
||||
if ((journal->log.mtdOffset - storageBlock.addr) % journal->info.program_unit) {
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */
|
||||
return JOURNAL_STATUS_ERROR; /* Program offset doesn't align with info.program_unit. This would result in an IO error if attempted. */
|
||||
}
|
||||
|
@ -286,17 +462,21 @@ int32_t flashJournalStrategySequential_log_progress(void)
|
|||
xfer -= xfer % journal->info.program_unit; /* align transfer-size with program_unit. */
|
||||
|
||||
/* perform the IO */
|
||||
// printf("programming %lu bytes at offset %lu\n", xfer, (uint32_t)journal->log.offset);
|
||||
rc = journal->mtd->ProgramData(journal->log.offset, journal->log.dataBeingLogged, xfer);
|
||||
// printf("programming %lu bytes at offset %lu\n", xfer, (uint32_t)journal->log.mtdOffset);
|
||||
rc = journal->mtd->ProgramData(journal->log.mtdOffset, journal->log.dataBeingLogged, xfer);
|
||||
if (rc < ARM_DRIVER_OK) {
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
if (rc == ARM_STORAGE_ERROR_RUNTIME_OR_INTEGRITY_FAILURE) {
|
||||
return JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE;
|
||||
} else {
|
||||
return JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
}
|
||||
if ((journal->mtdCapabilities.asynchronous_ops) && (rc == ARM_DRIVER_OK)) {
|
||||
return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */
|
||||
} else {
|
||||
/* synchronous completion. 'rc' contains the actual number of bytes transferred. */
|
||||
journal->log.offset += rc;
|
||||
journal->log.mtdOffset += rc;
|
||||
journal->log.amountLeftToLog -= rc;
|
||||
journal->log.dataBeingLogged += rc;
|
||||
if (journal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY) {
|
||||
|
@ -312,7 +492,7 @@ int32_t flashJournalStrategySequential_log_progress(void)
|
|||
switch (journal->state) {
|
||||
case SEQUENTIAL_JOURNAL_STATE_LOGGING_ERASE:
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_HEAD;
|
||||
journal->log.offset = journal->mtdStartOffset + blobIndexBeingLogged * journal->sequentialSkip;
|
||||
journal->log.mtdOffset = SLOT_ADDRESS(journal, blobIndexBeingLogged);
|
||||
journal->log.head.version = SEQUENTIAL_FLASH_JOURNAL_VERSION;
|
||||
journal->log.head.magic = SEQUENTIAL_FLASH_JOURNAL_MAGIC;
|
||||
journal->log.head.sequenceNumber = journal->nextSequenceNumber;
|
||||
|
@ -322,23 +502,32 @@ int32_t flashJournalStrategySequential_log_progress(void)
|
|||
// printf("newstate: program HEAD; amount to log %u\n", journal->log.amountLeftToLog);
|
||||
break;
|
||||
|
||||
case SEQUENTIAL_JOURNAL_STATE_LOGGING_HEAD: /* switch to writing the body */
|
||||
case SEQUENTIAL_JOURNAL_STATE_LOGGING_HEAD: /* we've finished writing the head */
|
||||
/* compute CRC32 on the header */
|
||||
flashJournalCrcReset();
|
||||
flashJournalCrcCummulative((const unsigned char *)&journal->log.head, sizeof(SequentialFlashJournalLogHead_t));
|
||||
|
||||
/* switch to writing the body */
|
||||
|
||||
/* Prepare for the tail to be written out at a later time.
|
||||
* This will only be done once Commit() is called. */
|
||||
journal->log.tailOffset = journal->mtdStartOffset
|
||||
+ (blobIndexBeingLogged + 1) * journal->sequentialSkip
|
||||
- sizeof(SequentialFlashJournalLogTail_t);
|
||||
journal->log.mtdTailOffset = SLOT_ADDRESS(journal, blobIndexBeingLogged + 1) - sizeof(SequentialFlashJournalLogTail_t);
|
||||
|
||||
journal->log.tail.magic = SEQUENTIAL_FLASH_JOURNAL_MAGIC;
|
||||
journal->log.tail.sequenceNumber = journal->nextSequenceNumber;
|
||||
journal->log.tail.sizeofBlob = 0; /* we'll update this as we complete our writes. */
|
||||
journal->log.tail.crc32 = 0;
|
||||
|
||||
if (journal->prevCommand == FLASH_JOURNAL_OPCODE_COMMIT) {
|
||||
/* This branch is taken only when commit() is called without any preceding log() operations. */
|
||||
journal->log.tail.crc32 = flashJournalCrcCummulative((const unsigned char *)&journal->log.tail, sizeof(SequentialFlashJournalLogTail_t));
|
||||
flashJournalCrcReset();
|
||||
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_TAIL;
|
||||
journal->log.dataBeingLogged = (const uint8_t *)&journal->log.tail;
|
||||
journal->log.amountLeftToLog = sizeof(SequentialFlashJournalLogTail_t);
|
||||
journal->log.offset = journal->log.tailOffset;
|
||||
// printf("newstate: program TAIL at offset %lu\r\n", (uint32_t)journal->log.offset);
|
||||
journal->log.mtdOffset = journal->log.mtdTailOffset;
|
||||
// printf("newstate: program TAIL at offset %lu\r\n", (uint32_t)journal->log.mtdOffset);
|
||||
} else {
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY;
|
||||
journal->log.dataBeingLogged = journal->log.blob;
|
||||
|
@ -352,15 +541,19 @@ int32_t flashJournalStrategySequential_log_progress(void)
|
|||
if (journal->log.dataBeingLogged == journal->log.blob) {
|
||||
return JOURNAL_STATUS_SMALL_LOG_REQUEST;
|
||||
} else {
|
||||
return (journal->log.dataBeingLogged - journal->log.blob);
|
||||
uint32_t amountOfDataLogged = (journal->log.dataBeingLogged - journal->log.blob);
|
||||
flashJournalCrcCummulative(journal->log.blob, amountOfDataLogged); /* compute CRC32 on logged data */
|
||||
return amountOfDataLogged;
|
||||
}
|
||||
|
||||
case SEQUENTIAL_JOURNAL_STATE_LOGGING_TAIL:
|
||||
// printf("crc32 of slot: 0x%x\n", journal->log.tail.crc32);
|
||||
|
||||
journal->info.sizeofJournaledBlob = journal->log.tail.sizeofBlob;
|
||||
journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state to allow further operations */
|
||||
|
||||
++journal->currentBlobIndex;
|
||||
if (journal->currentBlobIndex == SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS) {
|
||||
if (journal->currentBlobIndex == journal->numSlots) {
|
||||
journal->currentBlobIndex = 0;
|
||||
}
|
||||
// printf("currentBlobIndex: %lu\n", journal->currentBlobIndex);
|
||||
|
@ -381,24 +574,48 @@ int32_t flashJournalStrategySequential_log_progress(void)
|
|||
}
|
||||
}
|
||||
|
||||
void formatHandler(int32_t status, ARM_STORAGE_OPERATION operation)
|
||||
{
|
||||
if (status < ARM_DRIVER_OK) {
|
||||
if (formatInfoSingleton.callback) {
|
||||
formatInfoSingleton.callback(status, FLASH_JOURNAL_OPCODE_FORMAT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t rc = flashJournalStrategySequential_format_progress(status, operation);
|
||||
if (rc != JOURNAL_STATUS_OK) {
|
||||
if (formatInfoSingleton.callback) {
|
||||
formatInfoSingleton.callback(rc, FLASH_JOURNAL_OPCODE_FORMAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation)
|
||||
{
|
||||
int32_t rc;
|
||||
|
||||
if (status < ARM_DRIVER_OK) {
|
||||
printf("mtdHandler: received error status %" PRId32 "\n", status);
|
||||
/* Map integrity failures reported by the Storage driver appropriately. */
|
||||
if (status == ARM_STORAGE_ERROR_RUNTIME_OR_INTEGRITY_FAILURE) {
|
||||
status = JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE;
|
||||
} else {
|
||||
status = JOURNAL_STATUS_STORAGE_IO_ERROR;
|
||||
}
|
||||
|
||||
// printf("journal mtdHandler: received error status %ld\n", status);
|
||||
switch (activeJournal->state) {
|
||||
case SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED:
|
||||
case SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS:
|
||||
if (activeJournal->callback) {
|
||||
activeJournal->callback(JOURNAL_STATUS_STORAGE_IO_ERROR, FLASH_JOURNAL_OPCODE_INITIALIZE);
|
||||
activeJournal->callback(status, FLASH_JOURNAL_OPCODE_INITIALIZE);
|
||||
}
|
||||
break;
|
||||
|
||||
case SEQUENTIAL_JOURNAL_STATE_RESETING:
|
||||
activeJournal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */
|
||||
if (activeJournal->callback) {
|
||||
activeJournal->callback(JOURNAL_STATUS_STORAGE_IO_ERROR, FLASH_JOURNAL_OPCODE_RESET);
|
||||
activeJournal->callback(status, FLASH_JOURNAL_OPCODE_RESET);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -411,7 +628,7 @@ void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation)
|
|||
activeJournal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED;
|
||||
|
||||
if (activeJournal->callback) {
|
||||
activeJournal->callback(JOURNAL_STATUS_STORAGE_IO_ERROR, FLASH_JOURNAL_OPCODE_LOG_BLOB);
|
||||
activeJournal->callback(status, FLASH_JOURNAL_OPCODE_LOG_BLOB);
|
||||
}
|
||||
break;
|
||||
case SEQUENTIAL_JOURNAL_STATE_READING:
|
||||
|
@ -419,7 +636,7 @@ void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation)
|
|||
activeJournal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED;
|
||||
|
||||
if (activeJournal->callback) {
|
||||
activeJournal->callback(JOURNAL_STATUS_STORAGE_IO_ERROR, FLASH_JOURNAL_OPCODE_READ_BLOB);
|
||||
activeJournal->callback(status, FLASH_JOURNAL_OPCODE_READ_BLOB);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -455,7 +672,7 @@ void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation)
|
|||
return;
|
||||
}
|
||||
|
||||
activeJournal->log.eraseOffset += status;
|
||||
activeJournal->log.mtdEraseOffset += status;
|
||||
|
||||
if ((rc = flashJournalStrategySequential_log_progress()) != JOURNAL_STATUS_OK) {
|
||||
activeJournal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */
|
||||
|
@ -476,9 +693,9 @@ void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation)
|
|||
break;
|
||||
|
||||
case ARM_STORAGE_OPERATION_PROGRAM_DATA:
|
||||
// printf("PROGRAM_DATA: received status of %ld\n", status);
|
||||
// printf("journal mtdHandler: PROGRAM_DATA: received status of %ld\n", status);
|
||||
rc = status;
|
||||
activeJournal->log.offset += rc;
|
||||
activeJournal->log.mtdOffset += rc;
|
||||
activeJournal->log.amountLeftToLog -= rc;
|
||||
activeJournal->log.dataBeingLogged += rc;
|
||||
if (activeJournal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY) {
|
||||
|
@ -504,7 +721,7 @@ void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation)
|
|||
break;
|
||||
|
||||
default:
|
||||
printf("mtdHandler: unknown operation %u\n", operation);
|
||||
//printf("mtdHandler: unknown operation %u\n", operation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,12 +21,58 @@
|
|||
#include "flash-journal-strategy-sequential/flash_journal_private.h"
|
||||
#include "flash-journal-strategy-sequential/flash_journal_strategy_sequential.h"
|
||||
|
||||
/* The following singleton captures the state of the format machine. Format is
|
||||
* handled differently because it executes even before a journal exists (or a
|
||||
* Journal_t can be initialized. */
|
||||
extern struct FormatInfo_t {
|
||||
ARM_DRIVER_STORAGE *mtd;
|
||||
SequentialFlashJournalHeader_t header;
|
||||
FlashJournal_Callback_t callback;
|
||||
uint64_t mtdAddr;
|
||||
uint32_t mtdProgramUnit;
|
||||
} formatInfoSingleton;
|
||||
|
||||
extern SequentialFlashJournal_t *activeJournal;
|
||||
|
||||
/**
|
||||
* Check the sanity of a given slot
|
||||
* @param journal
|
||||
* @param slotOffset
|
||||
* @param [out] headSequenceNumberP
|
||||
* sequence number of the slot as read from the header.
|
||||
* @param [out] tailP
|
||||
* the tail of the slot
|
||||
* @return 1 if the slot is valid; i.e. if head and tail match, and if CRC32 agrees.
|
||||
*/
|
||||
int32_t slotIsSane(SequentialFlashJournal_t *journal,
|
||||
uint64_t slotOffset,
|
||||
uint32_t *headSequenceNumberP,
|
||||
SequentialFlashJournalLogTail_t *tailP);
|
||||
|
||||
int32_t mtdGetStartAddr(ARM_DRIVER_STORAGE *mtd, uint64_t *startAddrP);
|
||||
int32_t setupSequentialJournalHeader(SequentialFlashJournalHeader_t *headerP, ARM_DRIVER_STORAGE *mtd, uint64_t totalSize, uint32_t numSlots);
|
||||
int32_t discoverLatestLoggedBlob(SequentialFlashJournal_t *journal);
|
||||
|
||||
/**
|
||||
* Progress the state machine for the 'format' operation. This method can also be called from an interrupt handler.
|
||||
* @return < JOURNAL_STATUS_OK for error
|
||||
* = JOURNAL_STATUS_OK to signal pending asynchronous activity
|
||||
* > JOURNAL_STATUS_OK for completion
|
||||
*/
|
||||
int32_t flashJournalStrategySequential_format_progress(int32_t status, ARM_STORAGE_OPERATION operationWhichJustFinshed);
|
||||
|
||||
/**
|
||||
* Progress the state machine for the 'log' operation. This method can also be called from an interrupt handler.
|
||||
* @return < JOURNAL_STATUS_OK for error
|
||||
* = JOURNAL_STATUS_OK to signal pending asynchronous activity
|
||||
* > JOURNAL_STATUS_OK for completion
|
||||
*/
|
||||
int32_t flashJournalStrategySequential_log_progress(void);
|
||||
|
||||
int32_t flashJournalStrategySequential_reset_progress(void);
|
||||
int32_t flashJournalStrategySequential_read_progress(void);
|
||||
int32_t flashJournalStrategySequential_log_progress(void);
|
||||
|
||||
void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation);
|
||||
void formatHandler(int32_t status, ARM_STORAGE_OPERATION operation);
|
||||
|
||||
#endif /*__FLASH_JOURNAL_SEQUENTIAL_STRATEGY_SUPPORT_FUNCTIONS_H__*/
|
||||
|
|
|
@ -23,7 +23,6 @@ extern "C" {
|
|||
#endif // __cplusplus
|
||||
|
||||
#include "storage_abstraction/Driver_Storage.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* General return codes. All Flash-Journal APIs return an int32_t to allow for
|
||||
|
@ -44,6 +43,9 @@ typedef enum _FlashJournal_Status
|
|||
JOURNAL_STATUS_NOT_INITIALIZED = -9, ///< journal not initialized
|
||||
JOURNAL_STATUS_EMPTY = -10, ///< There is no further data to read
|
||||
JOURNAL_STATUS_SMALL_LOG_REQUEST = -11, ///< log request is smaller than the program_unit of the underlying MTD block.
|
||||
JOURNAL_STATUS_NOT_FORMATTED = -12, ///< need to call xxx_format() before using the journal.
|
||||
JOURNAL_STATUS_METADATA_ERROR = -13, ///< sanity checks for the journal metadata failed.
|
||||
JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE = -14, ///< validation or run-time errors arising from the badkend media.
|
||||
} FlashJournal_Status_t;
|
||||
|
||||
/**
|
||||
|
@ -51,6 +53,7 @@ typedef enum _FlashJournal_Status
|
|||
* completing commands. Refer to \ref ARM_Flash_Callback_t.
|
||||
*/
|
||||
typedef enum _FlashJournal_OpCode {
|
||||
FLASH_JOURNAL_OPCODE_FORMAT,
|
||||
FLASH_JOURNAL_OPCODE_INITIALIZE,
|
||||
FLASH_JOURNAL_OPCODE_GET_INFO,
|
||||
FLASH_JOURNAL_OPCODE_READ_BLOB,
|
||||
|
@ -76,6 +79,29 @@ typedef struct _FlashJournal_Info {
|
|||
///< the requested amount).
|
||||
} FlashJournal_Info_t;
|
||||
|
||||
|
||||
static const uint32_t FLASH_JOURNAL_HEADER_MAGIC = 0xA00AEE1DUL;
|
||||
static const uint32_t FLASH_JOURNAL_HEADER_VERSION = 1;
|
||||
|
||||
/**
|
||||
* Meta-data placed at the head of a Journal. The actual header would be an
|
||||
* extension of this generic header, and would depend on the implementation
|
||||
* strategy. Initialization algorithms can expect to find this generic header at
|
||||
* the start of every Journal.
|
||||
*/
|
||||
typedef struct _FlashJournalHeader {
|
||||
uint32_t magic; /** Journal-header specific magic code */
|
||||
uint32_t version; /** Revision number for this generic journal header. */
|
||||
uint64_t totalSize; /** Total space (in bytes) occupied by the journal, including the header.
|
||||
* Both 'mtdOffset' and 'mtdOffset + totalSize' should
|
||||
* lie on erase boundaries. */
|
||||
uint32_t sizeofHeader; /** The size of the journal header; this is expected to be larger than this generic header. */
|
||||
uint32_t journalOffset; /** Offset from the start of the journal header to the actual logged journal. */
|
||||
uint32_t checksum; /** CRC32 over the entire flash-journal-header, including the implementation
|
||||
* specific extension (i.e. over 'sizeofHeader' bytes). The value of the
|
||||
* field is taken to be 0 for the purpose of computing the checksum. */
|
||||
} FlashJournalHeader_t;
|
||||
|
||||
/**
|
||||
* This is the type of the command completion callback handler for the
|
||||
* asynchronous flash-journal APIs: initialize(), read(), log(), commit() and
|
||||
|
@ -95,8 +121,7 @@ typedef struct _FlashJournal_Info {
|
|||
typedef void (*FlashJournal_Callback_t)(int32_t status, FlashJournal_OpCode_t cmd_code);
|
||||
|
||||
/* forward declarations. */
|
||||
typedef struct _FlashJournal_t FlashJournal_t;
|
||||
typedef struct _FlashJournal_Ops_t FlashJournal_Ops_t;
|
||||
struct FlashJournal_t;
|
||||
|
||||
/**
|
||||
* @ref FlashJournal_t is an abstraction implemented by a table of generic
|
||||
|
@ -111,44 +136,53 @@ typedef struct _FlashJournal_Ops_t FlashJournal_Ops_t;
|
|||
* strategy-specific metadata. The value of this MAX_SIZE may need to be
|
||||
* increased if some future journal-strategy needs more metadata.
|
||||
*/
|
||||
#define FLASH_JOURNAL_HANDLE_MAX_SIZE 140
|
||||
#define FLASH_JOURNAL_HANDLE_MAX_SIZE 160
|
||||
|
||||
/**
|
||||
* This is the set of operations offered by the flash-journal abstraction. A set
|
||||
* of implementations for these operations defines a logging strategy.
|
||||
*/
|
||||
typedef struct _FlashJournal_Ops_t {
|
||||
|
||||
typedef struct FlashJournal_Ops_t {
|
||||
/**
|
||||
* \brief Initialize the flash journal. Refer to @ref FlashJournal_initialize.
|
||||
*/
|
||||
int32_t (*initialize)(FlashJournal_t *journal, ARM_DRIVER_STORAGE *mtd, const FlashJournal_Ops_t *ops, FlashJournal_Callback_t callback);
|
||||
int32_t (*initialize)(struct FlashJournal_t *journal,
|
||||
ARM_DRIVER_STORAGE *mtd,
|
||||
const struct FlashJournal_Ops_t *ops,
|
||||
FlashJournal_Callback_t callback);
|
||||
|
||||
/**
|
||||
* \brief fetch journal metadata. Refer to @ref FlashJournal_getInfo.
|
||||
*/
|
||||
FlashJournal_Status_t (*getInfo) (FlashJournal_t *journal, FlashJournal_Info_t *info);
|
||||
FlashJournal_Status_t (*getInfo) (struct FlashJournal_t *journal, FlashJournal_Info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Read from the most recently logged blob. Refer to @ref FlashJournal_read.
|
||||
*/
|
||||
int32_t (*read) (FlashJournal_t *journal, void *buffer, size_t size);
|
||||
int32_t (*read) (struct FlashJournal_t *journal, void *buffer, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Read from the most recently logged blob from a particular offset. Refer to @ref FlashJournal_readFrom.
|
||||
*/
|
||||
int32_t (*readFrom) (struct FlashJournal_t *journal, size_t offset, void *buffer, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Start logging a new blob or append to the one currently being logged. Refer to @ref FlashJournal_log.
|
||||
*/
|
||||
int32_t (*log) (FlashJournal_t *journal, const void *blob, size_t size);
|
||||
int32_t (*log) (struct FlashJournal_t *journal, const void *blob, size_t size);
|
||||
|
||||
/**
|
||||
* @brief commit a blob accumulated through a non-empty sequence of
|
||||
* previously successful log() operations. Refer to @ref FlashJournal_commit.
|
||||
*/
|
||||
int32_t (*commit) (FlashJournal_t *journal);
|
||||
int32_t (*commit) (struct FlashJournal_t *journal);
|
||||
|
||||
/**
|
||||
* @brief Reset the journal. This has the effect of erasing all valid blobs.
|
||||
* Refer to @ref FlashJournal_reset.
|
||||
*/
|
||||
int32_t (*reset) (FlashJournal_t *journal);
|
||||
int32_t (*reset) (struct FlashJournal_t *journal);
|
||||
} FlashJournal_Ops_t;
|
||||
|
||||
/**
|
||||
|
@ -167,7 +201,7 @@ typedef struct _FlashJournal_Ops_t {
|
|||
* @note: there is a risk of overallocation in case an implementation doesn't
|
||||
* need FLASH_JOURNAL_HANDLE_MAX_SIZE bytes, but the impact should be small.
|
||||
*/
|
||||
typedef struct _FlashJournal_t {
|
||||
typedef struct FlashJournal_t {
|
||||
FlashJournal_Ops_t ops;
|
||||
|
||||
union {
|
||||
|
@ -224,7 +258,7 @@ typedef struct _FlashJournal_t {
|
|||
* JOURNAL_STATUS_OK before the actual completion of the operation (or
|
||||
* with an appropriate error code in case of failure). When the
|
||||
* operation is completed the command callback is invoked with
|
||||
* JOURNAL_STATUS_OK passed in as the 'status' parameter of the
|
||||
* 1 passed in as the 'status' parameter of the
|
||||
* callback. In case of errors, the completion callback is invoked with
|
||||
* an error status.
|
||||
* - When the operation is executed by the journal in a blocking (i.e.
|
||||
|
@ -234,6 +268,11 @@ typedef struct _FlashJournal_t {
|
|||
* completion or an appropriate error code, and no further
|
||||
* invocation of the completion callback should be expected at a later time.
|
||||
*
|
||||
* @note The user must call an appropriate xxx_format() to format underlying
|
||||
* storage before initializing it for use. If Initialize() is called on
|
||||
* unformatted storage, an error value of JOURNAL_STATUS_NOT_FORMATTED will be
|
||||
* returned.
|
||||
*
|
||||
* Here's a code snippet to suggest how this API might be used by callers:
|
||||
* \code
|
||||
* ASSERT(JOURNAL_STATUS_OK == 0); // this is a precondition; it doesn't need to be put in code
|
||||
|
@ -377,6 +416,88 @@ static inline int32_t FlashJournal_read(FlashJournal_t *journal, void *blob, siz
|
|||
return journal->ops.read(journal, blob, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read from the most recently logged blob at a given offset. A front-end
|
||||
* for @ref FlashJournal_Ops_t::readFrom().
|
||||
*
|
||||
* @details Read off a chunk of the logged blob from a given offset. The journal
|
||||
* maintains a read-pointer internally to allow reads to continue where the
|
||||
* previous one left off. This call effectively sets the read-counter before
|
||||
* fetching data. Subsequent reads continue sequentially from where the
|
||||
* readFrom() left off.
|
||||
*
|
||||
* @note: If the given offset stands at (or is beyond) the end of the previously
|
||||
* logged blob, readFrom() returns the error JOURNAL_STATUS_EMPTY (or passes
|
||||
* that value as the status of a completion callback) and resets the read-
|
||||
* pointer to allow re-reading the blob from the start.
|
||||
*
|
||||
* @param [in] journal
|
||||
* A previously initialized journal.
|
||||
*
|
||||
* @param [in] offset
|
||||
* The logical offset (within the blob) at which to read data from.
|
||||
*
|
||||
* @param [out] buffer
|
||||
* The destination of the read operation. The memory is owned
|
||||
* by the caller and should remain valid for the lifetime
|
||||
* of this operation.
|
||||
*
|
||||
* @param [in] size
|
||||
* The maximum amount of data which can be read in this
|
||||
* operation. The memory pointed to by 'buffer' should be as
|
||||
* large as this amount.
|
||||
*
|
||||
* @return
|
||||
* The function executes in the following ways:
|
||||
* - When the operation is asynchronous--i.e. when the underlying MTD's
|
||||
* ARM_STOR_CAPABILITIES::asynchronous_ops is set to 1--and the operation
|
||||
* executed by the journal in a non-blocking (i.e. asynchronous) manner,
|
||||
* control returns to the caller with JOURNAL_STATUS_OK before the actual
|
||||
* completion of the operation (or with an appropriate error code in case of
|
||||
* failure). When the operation completes, the command callback is
|
||||
* invoked with the number of successfully transferred bytes passed in as
|
||||
* the 'status' parameter of the callback. If any error is encountered
|
||||
* after the launch of an asynchronous operation, the completion callback
|
||||
* is invoked with an error status.
|
||||
* - When the operation is executed by the journal in a blocking (i.e.
|
||||
* synchronous) manner, control returns to the caller only upon the
|
||||
* actual completion of the operation, or the discovery of a failure
|
||||
* condition. In synchronous mode, the function returns the number
|
||||
* of data items read or an appropriate error code.
|
||||
*
|
||||
* @note If the underlying MTD's ARM_STORAGE_CAPABILITIES::asynchronous_ops
|
||||
* is set then this operation may execute asynchronously. In the case of
|
||||
* asynchronous operation, the invocation returns early (with
|
||||
* JOURNAL_STATUS_OK) and results in a completion callback later.
|
||||
*
|
||||
* @note If the underlying MTD's ARM_STORAGE_CAPABILITIES::asynchronous_ops
|
||||
* is set, the journal is not required to operate asynchronously. A Read
|
||||
* operation can be finished synchronously in spite of
|
||||
* ARM_STORAGE_CAPABILITIES::asynchronous_ops being set, returning the
|
||||
* number of data items read to indicate successful completion, or an
|
||||
* appropriate error code. In this case no further invocation of a
|
||||
* completion callback should be expected at a later time.
|
||||
*
|
||||
* Here's a code snippet to suggest how this API might be used by callers:
|
||||
* \code
|
||||
* ASSERT(JOURNAL_STATUS_OK == 0); // this is a precondition; it doesn't need to be put in code
|
||||
* int32_t returnValue = FlashJournal_readFrom(&journal, offset, buffer, size);
|
||||
* if (returnValue < JOURNAL_STATUS_OK) {
|
||||
* // handle error
|
||||
* } else if (returnValue == JOURNAL_STATUS_OK) {
|
||||
* ASSERT(MTD->GetCapabilities().asynchronous_ops == 1);
|
||||
* // handle early return from asynchronous execution
|
||||
* } else {
|
||||
* ASSERT(returnValue == size);
|
||||
* // handle synchronous completion
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
static inline int32_t FlashJournal_readFrom(struct FlashJournal_t *journal, size_t offset, void *blob, size_t n)
|
||||
{
|
||||
return journal->ops.readFrom(journal, offset, blob, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start logging a new blob or append to the one currently being logged.
|
||||
* A front-end for @ref FlashJournal_Ops_t::log().
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2016, ARM Limited, All Rights Reserved
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 "storage-volume-manager/storage_volume_manager.h"
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
/* redefine tr_debug() to a printf() equivalent to emit trace */
|
||||
#define tr_debug(...)
|
||||
|
||||
|
||||
void StorageVolume::setup(uint64_t _addr, uint64_t _size, StorageVolumeManager *_volumeManager)
|
||||
{
|
||||
volumeOffset = _addr;
|
||||
volumeSize = _size;
|
||||
volumeManager = _volumeManager;
|
||||
allocated = true;
|
||||
}
|
||||
|
||||
ARM_DRIVER_VERSION StorageVolume::GetVersion(void)
|
||||
{
|
||||
ARM_DRIVER_VERSION bad_ver = {0, 0};
|
||||
|
||||
if (!allocated) {
|
||||
return bad_ver;
|
||||
}
|
||||
return volumeManager->getStorage()->GetVersion();
|
||||
}
|
||||
|
||||
ARM_STORAGE_CAPABILITIES StorageVolume::GetCapabilities(void)
|
||||
{
|
||||
ARM_STORAGE_CAPABILITIES bad_cap;
|
||||
|
||||
if (!allocated) {
|
||||
memset(&bad_cap, 0, sizeof(ARM_STORAGE_CAPABILITIES));
|
||||
return bad_cap;
|
||||
}
|
||||
return volumeManager->getStorage()->GetCapabilities();
|
||||
}
|
||||
|
||||
int32_t StorageVolume::Initialize(ARM_Storage_Callback_t _callback)
|
||||
{
|
||||
if (!allocated) {
|
||||
return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED;
|
||||
}
|
||||
|
||||
callback = _callback; /* nothing else to do since we've already initialized the storage */
|
||||
return 1; /* synchronous completion. */
|
||||
}
|
||||
|
||||
int32_t StorageVolume::Uninitialize(void)
|
||||
{
|
||||
if (!allocated) {
|
||||
return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED;
|
||||
}
|
||||
|
||||
return 1; /* synchronous completion. */
|
||||
}
|
||||
|
||||
int32_t StorageVolume::PowerControl(ARM_POWER_STATE state)
|
||||
{
|
||||
tr_debug("called powerControl(%u)", state);
|
||||
if (!allocated) {
|
||||
return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED;
|
||||
}
|
||||
if (volumeManager->activeVolume != NULL) {
|
||||
return ARM_DRIVER_ERROR_BUSY;
|
||||
}
|
||||
|
||||
volumeManager->activeVolume = this;
|
||||
int32_t rc = volumeManager->getStorage()->PowerControl(state);
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
volumeManager->activeVolume = NULL; /* we're certain that there is no more pending asynch. activity */
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int32_t StorageVolume::ReadData(uint64_t addr, void *data, uint32_t size)
|
||||
{
|
||||
tr_debug("called ReadData(%" PRIu32 ", %" PRIu32 ")", (uint32_t)addr, size);
|
||||
if (!allocated) {
|
||||
return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED;
|
||||
}
|
||||
if (volumeManager->activeVolume != NULL) {
|
||||
return ARM_DRIVER_ERROR_BUSY;
|
||||
}
|
||||
if ((size > volumeSize) || ((addr + size) > volumeSize)) {
|
||||
return ARM_DRIVER_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
volumeManager->activeVolume = this;
|
||||
int32_t rc = volumeManager->getStorage()->ReadData(volumeOffset + addr, data, size);
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
volumeManager->activeVolume = NULL; /* we're certain that there is no more pending asynch. activity */
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int32_t StorageVolume::ProgramData(uint64_t addr, const void *data, uint32_t size)
|
||||
{
|
||||
tr_debug("called ProgramData(%" PRIu32 ", %" PRIu32 ")", (uint32_t)addr, size);
|
||||
if (!allocated) {
|
||||
return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED;
|
||||
}
|
||||
if (volumeManager->activeVolume != NULL) {
|
||||
return ARM_DRIVER_ERROR_BUSY;
|
||||
}
|
||||
if ((size > volumeSize) || ((addr + size) > volumeSize)) {
|
||||
return ARM_DRIVER_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
volumeManager->activeVolume = this;
|
||||
int32_t rc = volumeManager->getStorage()->ProgramData(volumeOffset + addr, data, size);
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
volumeManager->activeVolume = NULL; /* we're certain that there is no more pending asynch. activity */
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int32_t StorageVolume::Erase(uint64_t addr, uint32_t size)
|
||||
{
|
||||
tr_debug("called erase(%" PRIu32 ", %" PRIu32 ")", (uint32_t)addr, size);
|
||||
if (!allocated) {
|
||||
return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED;
|
||||
}
|
||||
if (volumeManager->activeVolume != NULL) {
|
||||
return ARM_DRIVER_ERROR_BUSY;
|
||||
}
|
||||
if ((size > volumeSize) || ((addr + size) > volumeSize)) {
|
||||
return ARM_DRIVER_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
volumeManager->activeVolume = this;
|
||||
int32_t rc = volumeManager->getStorage()->Erase(volumeOffset + addr, size);
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
volumeManager->activeVolume = NULL; /* we're certain that there is no more pending asynch. activity */
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int32_t StorageVolume::EraseAll(void)
|
||||
{
|
||||
tr_debug("called eraseAll");
|
||||
if (!allocated) {
|
||||
return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED;
|
||||
}
|
||||
if (volumeManager->activeVolume != NULL) {
|
||||
return ARM_DRIVER_ERROR_BUSY;
|
||||
}
|
||||
|
||||
int32_t rc;
|
||||
|
||||
/* Allow EraseAll() only if the volume spans the entire storage. */
|
||||
{
|
||||
ARM_STORAGE_INFO info;
|
||||
rc = volumeManager->getStorage()->GetInfo(&info);
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
return ARM_DRIVER_ERROR;
|
||||
}
|
||||
|
||||
if ((volumeOffset != 0) || (volumeSize != info.total_storage)) {
|
||||
return ARM_DRIVER_ERROR_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
volumeManager->activeVolume = this;
|
||||
rc = volumeManager->getStorage()->EraseAll();
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
volumeManager->activeVolume = NULL; /* we're certain that there is no more pending asynch. activity */
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
ARM_STORAGE_STATUS StorageVolume::GetStatus(void)
|
||||
{
|
||||
const uint32_t busy = ((volumeManager->activeVolume != NULL) ? (uint32_t)1 : (uint32_t)0);
|
||||
ARM_STORAGE_STATUS status = {0, 0};
|
||||
status.busy = busy;
|
||||
return status;
|
||||
}
|
||||
|
||||
int32_t StorageVolume::GetInfo(ARM_STORAGE_INFO *infoP)
|
||||
{
|
||||
int32_t rc;
|
||||
rc = volumeManager->getStorage()->GetInfo(infoP);
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
return ARM_DRIVER_ERROR;
|
||||
}
|
||||
|
||||
infoP->total_storage = volumeSize;
|
||||
return ARM_DRIVER_OK;
|
||||
}
|
||||
|
||||
uint32_t StorageVolume::ResolveAddress(uint64_t addr) {
|
||||
return (uint32_t)(volumeOffset + addr);
|
||||
}
|
||||
|
||||
int32_t StorageVolume::GetNextBlock(const ARM_STORAGE_BLOCK* prevP, ARM_STORAGE_BLOCK *nextP)
|
||||
{
|
||||
int32_t rc;
|
||||
ARM_STORAGE_BLOCK tmpBlock;
|
||||
do {
|
||||
/* iterate forward */
|
||||
rc = volumeManager->getStorage()->GetNextBlock(prevP, &tmpBlock);
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Stop iteration if we have progressed past the boundary of this volume. */
|
||||
if (tmpBlock.addr >= (volumeOffset + volumeSize)) {
|
||||
return ARM_DRIVER_ERROR;
|
||||
}
|
||||
} while (!this->overlapsWithBlock(&tmpBlock));
|
||||
|
||||
if (nextP) {
|
||||
memcpy(nextP, &tmpBlock, sizeof(ARM_STORAGE_BLOCK));
|
||||
transformBlockToVolume(nextP);
|
||||
}
|
||||
return ARM_DRIVER_OK;
|
||||
}
|
||||
|
||||
int32_t StorageVolume::GetBlock(uint64_t addr, ARM_STORAGE_BLOCK *blockP)
|
||||
{
|
||||
ARM_STORAGE_BLOCK tmpBlock;
|
||||
int32_t rc = volumeManager->getStorage()->GetBlock(ResolveAddress(addr), &tmpBlock);
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
return rc;
|
||||
}
|
||||
if (!this->overlapsWithBlock(&tmpBlock)) {
|
||||
return ARM_DRIVER_ERROR;
|
||||
}
|
||||
|
||||
if (blockP) {
|
||||
memcpy(blockP, &tmpBlock, sizeof(ARM_STORAGE_BLOCK));
|
||||
transformBlockToVolume(blockP);
|
||||
}
|
||||
return ARM_DRIVER_OK;
|
||||
}
|
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2016, ARM Limited, All Rights Reserved
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 "storage-volume-manager/storage_volume_manager.h"
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
/* redefine these macros to a printf() equivalent to emit trace */
|
||||
#define tr_debug(...)
|
||||
#define tr_error(...)
|
||||
|
||||
/*
|
||||
* The following variable records the volume-manager instance which last setup
|
||||
* StorageVolumeManager::storageCallback() as a callback for a storage driver.
|
||||
* We use this value in the callback handler to de-multiplex the callback into a
|
||||
* volume-manager.
|
||||
*/
|
||||
static StorageVolumeManager *activeVolumeManager;
|
||||
|
||||
InitializeCallback_t initializeCallback;
|
||||
|
||||
#define STORAGE_API_DEFINITIONS_FOR_VOLUME(N) \
|
||||
extern "C" ARM_DRIVER_VERSION GetVersion_ ## N(void) { \
|
||||
return activeVolumeManager->volumes[(N)].GetVersion(); \
|
||||
} \
|
||||
ARM_STORAGE_CAPABILITIES GetCapabilities_ ## N(void) { \
|
||||
return activeVolumeManager->volumes[(N)].GetCapabilities(); \
|
||||
} \
|
||||
int32_t Initialize_ ## N(ARM_Storage_Callback_t callback) { \
|
||||
return activeVolumeManager->volumes[(N)].Initialize(callback); \
|
||||
} \
|
||||
int32_t Uninitialize_ ## N(void) { \
|
||||
return activeVolumeManager->volumes[(N)].Uninitialize(); \
|
||||
} \
|
||||
int32_t PowerControl_ ## N(ARM_POWER_STATE state) { \
|
||||
return activeVolumeManager->volumes[(N)].PowerControl(state); \
|
||||
} \
|
||||
int32_t ReadData_ ## N(uint64_t addr, void *data, uint32_t size) { \
|
||||
return activeVolumeManager->volumes[(N)].ReadData(addr, data, size); \
|
||||
} \
|
||||
int32_t ProgramData_ ## N(uint64_t addr, const void *data, uint32_t size) { \
|
||||
return activeVolumeManager->volumes[(N)].ProgramData(addr, data, size); \
|
||||
} \
|
||||
int32_t Erase_ ## N(uint64_t addr, uint32_t size) { \
|
||||
return activeVolumeManager->volumes[(N)].Erase(addr, size); \
|
||||
} \
|
||||
int32_t EraseAll_ ## N(void) { \
|
||||
return activeVolumeManager->volumes[(N)].EraseAll(); \
|
||||
} \
|
||||
ARM_STORAGE_STATUS GetStatus_ ## N(void) { \
|
||||
return activeVolumeManager->volumes[(N)].GetStatus(); \
|
||||
} \
|
||||
int32_t GetInfo_ ## N(ARM_STORAGE_INFO *infoP) { \
|
||||
return activeVolumeManager->volumes[(N)].GetInfo(infoP); \
|
||||
} \
|
||||
uint32_t ResolveAddress_ ## N(uint64_t addr) { \
|
||||
return activeVolumeManager->volumes[(N)].ResolveAddress(addr); \
|
||||
} \
|
||||
int32_t GetNextBlock_ ## N(const ARM_STORAGE_BLOCK* prevP, ARM_STORAGE_BLOCK *nextP) { \
|
||||
return activeVolumeManager->volumes[(N)].GetNextBlock(prevP, nextP); \
|
||||
} \
|
||||
int32_t GetBlock_ ## N(uint64_t addr, ARM_STORAGE_BLOCK *blockP) { \
|
||||
return activeVolumeManager->volumes[(N)].GetBlock(addr, blockP); \
|
||||
} \
|
||||
ARM_DRIVER_STORAGE VIRTUAL_MTD_ ## N = { \
|
||||
.GetVersion = GetVersion_ ## N, \
|
||||
.GetCapabilities = GetCapabilities_ ## N, \
|
||||
.Initialize = Initialize_ ## N, \
|
||||
.Uninitialize = Uninitialize_ ## N, \
|
||||
.PowerControl = PowerControl_ ## N, \
|
||||
.ReadData = ReadData_ ## N, \
|
||||
.ProgramData = ProgramData_ ## N, \
|
||||
.Erase = Erase_ ## N, \
|
||||
.EraseAll = EraseAll_ ## N, \
|
||||
.GetStatus = GetStatus_ ## N, \
|
||||
.GetInfo = GetInfo_ ## N, \
|
||||
.ResolveAddress = ResolveAddress_ ## N, \
|
||||
.GetNextBlock = GetNextBlock_ ## N, \
|
||||
.GetBlock = GetBlock_ ## N, \
|
||||
};
|
||||
|
||||
#define STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_1 STORAGE_API_DEFINITIONS_FOR_VOLUME(0)
|
||||
#define STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_2 STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_1 STORAGE_API_DEFINITIONS_FOR_VOLUME(1)
|
||||
#define STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_3 STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_2 STORAGE_API_DEFINITIONS_FOR_VOLUME(2)
|
||||
#define STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_4 STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_3 STORAGE_API_DEFINITIONS_FOR_VOLUME(3)
|
||||
/* ... add more of the above if ever needed */
|
||||
|
||||
#define STORAGE_API_DEFINITIONS_FOR_VOLUMES(N) EXPAND(CONCATENATE(STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_, N))
|
||||
|
||||
STORAGE_API_DEFINITIONS_FOR_VOLUMES(MAX_VOLUMES);
|
||||
|
||||
int32_t StorageVolumeManager::initialize(ARM_DRIVER_STORAGE *mtd, InitializeCallback_t callback)
|
||||
{
|
||||
activeVolume = NULL;
|
||||
initializeCallback = callback;
|
||||
|
||||
storage = mtd;
|
||||
storageCapabilities = mtd->GetCapabilities();
|
||||
|
||||
int32_t rc = mtd->GetInfo(&storageInfo);
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
tr_error("StorageVolumeManager::initialize: call to GetInfo() failed with %" PRId32, rc);
|
||||
return ARM_DRIVER_ERROR;
|
||||
}
|
||||
|
||||
rc = mtd->Initialize(storageCallback);
|
||||
if (rc < ARM_DRIVER_OK) {
|
||||
tr_error("Initialize() failed with error %" PRId32, rc);
|
||||
return ARM_DRIVER_ERROR;
|
||||
}
|
||||
|
||||
activeVolumeManager = this;
|
||||
if (rc == ARM_DRIVER_OK) {
|
||||
/* there is pending asynchronous activity which will result in a callback later */
|
||||
return ARM_DRIVER_OK;
|
||||
}
|
||||
|
||||
/* Clear previously allocated volumes */
|
||||
for (size_t i = 0; i < MAX_VOLUMES; i++) {
|
||||
if (volumes[i].isAllocated()) {
|
||||
volumes[i].deallocate();
|
||||
}
|
||||
}
|
||||
|
||||
/* synchronous completion */
|
||||
initialized = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t StorageVolumeManager::addVolume(uint64_t addr, uint64_t size, StorageVolume **volumePP)
|
||||
{
|
||||
tr_debug("StorageVolumeManager_addVolume: addr = %" PRIu32 ", size = %" PRIu32, (uint32_t)addr, (uint32_t)size);
|
||||
|
||||
*volumePP = NULL;
|
||||
|
||||
/*
|
||||
* sanity checks for arguments
|
||||
*/
|
||||
ARM_STORAGE_INFO info;
|
||||
int32_t rc;
|
||||
rc = storage->GetInfo(&info);
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
tr_error("StorageVolumeManager_addVolume: storage->GetInfo() failed with %" PRId32, rc);
|
||||
return ARM_DRIVER_ERROR;
|
||||
}
|
||||
if (size > info.total_storage) {
|
||||
tr_error("StorageVolumeManager_addVolume: 'size' parameter too large: %" PRIu32, (uint32_t)size);
|
||||
return ARM_DRIVER_ERROR;
|
||||
}
|
||||
ARM_STORAGE_BLOCK firstBlock;
|
||||
rc = storage->GetNextBlock(NULL, &firstBlock);
|
||||
if (rc != ARM_DRIVER_OK) {
|
||||
tr_error("StorageVolumeManager_addVolume: storage->GetNextBlock() failed with %" PRId32, rc);
|
||||
return ARM_DRIVER_ERROR;
|
||||
}
|
||||
if ((addr < firstBlock.addr) || ((addr + size) > (firstBlock.addr + info.total_storage))) {
|
||||
tr_error("StorageVolumeManager_addVolume: given range [%" PRIu32 ", %" PRIu32 ") isn't entirely contained within available storage range [%" PRIu32 ", %" PRIu32 ")",
|
||||
(uint32_t)addr, (uint32_t)(addr + size), (uint32_t)firstBlock.addr, (uint32_t)(firstBlock.addr + info.total_storage));
|
||||
return ARM_DRIVER_ERROR;
|
||||
}
|
||||
|
||||
/* Find an unused volume. */
|
||||
uint32_t index = findIndexOfUnusedVolume();
|
||||
if (index == MAX_VOLUMES) {
|
||||
return STORAGE_VOLUME_MANAGER_STATUS_ERROR_EXHASTED_VOLUMES;
|
||||
}
|
||||
|
||||
/* setup volume */
|
||||
StorageVolume *volumeP = &volumes[index];
|
||||
volumeP->setup(addr, size, this);
|
||||
*volumePP = volumeP;
|
||||
tr_debug("StorageVolumeManager_addVolume: setup volume at index %" PRIu32, index);
|
||||
|
||||
return ARM_DRIVER_OK;
|
||||
}
|
||||
|
||||
int32_t StorageVolumeManager::addVolume_C(uint64_t addr, uint64_t size, _ARM_DRIVER_STORAGE *mtd)
|
||||
{
|
||||
int32_t rc;
|
||||
StorageVolume *volumeP;
|
||||
if ((rc = addVolume(addr, size, &volumeP)) < ARM_DRIVER_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* locate index of the allocated volume */
|
||||
size_t index;
|
||||
for (index = 0; index < MAX_VOLUMES; index++) {
|
||||
if (volumes[index].isAllocated() && (&volumes[index] == volumeP)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index == MAX_VOLUMES) {
|
||||
return STORAGE_VOLUME_MANAGER_STATUS_ERROR_EXHASTED_VOLUMES;
|
||||
}
|
||||
|
||||
if (index == 0) {
|
||||
*mtd = VIRTUAL_MTD_0;
|
||||
} else if (index == 1) {
|
||||
*mtd = VIRTUAL_MTD_1;
|
||||
} else if (index == 2) {
|
||||
*mtd = VIRTUAL_MTD_2;
|
||||
} else if (index == 3) {
|
||||
*mtd = VIRTUAL_MTD_3;
|
||||
} else {
|
||||
return ARM_DRIVER_ERROR;
|
||||
}
|
||||
|
||||
return ARM_DRIVER_OK;
|
||||
}
|
||||
|
||||
int32_t StorageVolumeManager::lookupVolume(uint64_t addr, StorageVolume **volumePP)
|
||||
{
|
||||
/*
|
||||
* Traverse the volumes in reverse order of creation; this allows newly created volumes to supersede the older ones.
|
||||
*/
|
||||
for (size_t index = MAX_VOLUMES - 1; index > 0; --index) {
|
||||
StorageVolume *volume = &volumes[index];
|
||||
if ((addr >= volume->getVolumeOffset()) && (addr < (volume->getVolumeOffset() + volume->getVolumeSize()))) {
|
||||
*volumePP = volume;
|
||||
return ARM_DRIVER_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ARM_DRIVER_ERROR;
|
||||
}
|
||||
|
||||
void StorageVolumeManager::storageCallback(int32_t status, ARM_STORAGE_OPERATION operation)
|
||||
{
|
||||
tr_debug("StorageVolumeManager_callback: operation = %u", operation);
|
||||
StorageVolumeManager *volumeManager = activeVolumeManager;
|
||||
|
||||
switch (operation) {
|
||||
case ARM_STORAGE_OPERATION_INITIALIZE:
|
||||
volumeManager->initialized = true;
|
||||
if (initializeCallback != NULL) {
|
||||
initializeCallback(status);
|
||||
}
|
||||
break;
|
||||
|
||||
case ARM_STORAGE_OPERATION_POWER_CONTROL:
|
||||
case ARM_STORAGE_OPERATION_READ_DATA:
|
||||
case ARM_STORAGE_OPERATION_PROGRAM_DATA:
|
||||
case ARM_STORAGE_OPERATION_ERASE:
|
||||
case ARM_STORAGE_OPERATION_ERASE_ALL:
|
||||
if (volumeManager->activeVolume != NULL) {
|
||||
/* Reset activeVolume and invoke callback. We reset activeVolume before the
|
||||
* callback because the callback may attempt to launch another asynchronous
|
||||
* operation, which requires 'activeVolume' to be NULL. */
|
||||
StorageVolume *callbackVolume = volumeManager->activeVolume; /* remember the volume which will receive the callback. */
|
||||
volumeManager->activeVolume = NULL;
|
||||
|
||||
if (callbackVolume->isAllocated() && callbackVolume->getCallback()) {
|
||||
(callbackVolume->getCallback())(status, operation);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
tr_error("StorageVolumeManager_callback: unknown operation %u", operation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t StorageVolumeManager::findIndexOfUnusedVolume(void) const {
|
||||
size_t index;
|
||||
for (index = 0; index < MAX_VOLUMES; index++) {
|
||||
if (!volumes[index].isAllocated()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2016, ARM Limited, All Rights Reserved
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __STORAGE_VOLUME_MANAGER_H__
|
||||
#define __STORAGE_VOLUME_MANAGER_H__
|
||||
|
||||
#ifndef __cplusplus
|
||||
#error "This abstraction requires a C++ toolchain"
|
||||
#endif // __cplusplus
|
||||
|
||||
#include "storage_abstraction/Driver_Storage.h"
|
||||
|
||||
#if !defined(YOTTA_CFG_STORAGE_VOLUME_MANAGER_MAX_VOLUMES)
|
||||
#define MAX_VOLUMES 4
|
||||
#else
|
||||
#define MAX_VOLUMES YOTTA_CFG_STORAGE_VOLUME_MANAGER_MAX_VOLUMES
|
||||
#endif
|
||||
/**<
|
||||
* A static assert to ensure that the size of SequentialJournal is smaller than
|
||||
* FlashJournal_t. The caller will only allocate a FlashJournal_t and expect the
|
||||
* Sequential Strategy to reuse that space for a SequentialFlashJournal_t.
|
||||
*/
|
||||
#ifndef TOOLCHAIN_IAR
|
||||
typedef char AssertStorageVolumeManagerMaxVolumesIsSane[(((MAX_VOLUMES) > 0) && ((MAX_VOLUMES) <= 8)) ? 0:-1];
|
||||
#endif
|
||||
|
||||
#define CONCATENATE(A, B) A ## B
|
||||
#define EXPAND(X) X /* this adds a level of indirection needed to allow macro-expansion following a token-paste operation (see use of CONCATENATE() below). */
|
||||
|
||||
#define STORAGE_API_EXTERN_C_DECLARATIONS_FOR_VOLUME(N) \
|
||||
extern "C" ARM_DRIVER_VERSION GetVersion_ ## N(void); \
|
||||
extern "C" ARM_STORAGE_CAPABILITIES GetCapabilities_ ## N(void); \
|
||||
extern "C" int32_t Initialize_ ## N(ARM_Storage_Callback_t callback); \
|
||||
extern "C" int32_t Uninitialize_ ## N(void); \
|
||||
extern "C" int32_t PowerControl_ ## N(ARM_POWER_STATE state); \
|
||||
extern "C" int32_t ReadData_ ## N(uint64_t addr, void *data, uint32_t size); \
|
||||
extern "C" int32_t ProgramData_ ## N(uint64_t addr, const void *data, uint32_t size); \
|
||||
extern "C" int32_t Erase_ ## N(uint64_t addr, uint32_t size); \
|
||||
extern "C" int32_t EraseAll_ ## N(void); \
|
||||
extern "C" ARM_STORAGE_STATUS GetStatus_ ## N(void); \
|
||||
extern "C" int32_t GetInfo_ ## N(ARM_STORAGE_INFO *infoP); \
|
||||
extern "C" uint32_t ResolveAddress_ ## N(uint64_t addr); \
|
||||
extern "C" int32_t GetNextBlock_ ## N(const ARM_STORAGE_BLOCK* prevP, ARM_STORAGE_BLOCK *nextP); \
|
||||
extern "C" int32_t GetBlock_ ## N(uint64_t addr, ARM_STORAGE_BLOCK *blockP);
|
||||
|
||||
#define STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_1 STORAGE_API_EXTERN_C_DECLARATIONS_FOR_VOLUME(0)
|
||||
#define STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_2 STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_1 STORAGE_API_EXTERN_C_DECLARATIONS_FOR_VOLUME(1)
|
||||
#define STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_3 STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_2 STORAGE_API_EXTERN_C_DECLARATIONS_FOR_VOLUME(2)
|
||||
#define STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_4 STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_3 STORAGE_API_EXTERN_C_DECLARATIONS_FOR_VOLUME(3)
|
||||
/* ... add more of the above if ever needed */
|
||||
|
||||
#define STORAGE_API_EXTERN_C_DECLARATIONS_LIST(N) EXPAND(CONCATENATE(STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_, N))
|
||||
|
||||
STORAGE_API_EXTERN_C_DECLARATIONS_LIST(MAX_VOLUMES);
|
||||
|
||||
/**
|
||||
* Error return codes specific to the Storage volume manager. These extend the
|
||||
* common error codes from ARM_DRIVER_STORAGE. All Volume-manager APIs return an
|
||||
* int32_t to allow for both error and success status returns. This enumeration
|
||||
* contains all possible error status values.
|
||||
*/
|
||||
typedef enum _StorageVolumeManager_Status
|
||||
{
|
||||
STORAGE_VOLUME_MANAGER_STATUS_ERROR_EXHASTED_VOLUMES = -7, ///< exhausted the supply of available volumes
|
||||
STORAGE_VOLUME_MANAGER_STATUS_ERROR_NOT_ERASABLE = -8, ///< Part (or all) of the range provided to Erase() isn't erasable.
|
||||
STORAGE_VOLUME_MANAGER_STATUS_ERROR_NOT_PROGRAMMABLE = -9, ///< Part (or all) of the range provided to ProgramData() isn't programmable.
|
||||
STORAGE_VOLUME_MANAGER_STATUS_ERROR_PROTECTED = -10, ///< Part (or all) of the range to Erase() or ProgramData() is protected.
|
||||
STORAGE_VOLUME_MANAGER_STATUS_ERROR_NOT_INITIALIZED = -11, ///< underlying storage not initialized
|
||||
STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED = -12, ///< attempt to operate on an unallocated volume
|
||||
} StorageVolumeManager_Status_t;
|
||||
|
||||
typedef void (*InitializeCallback_t)(int32_t status);
|
||||
class StorageVolumeManager; /* forward declaration */
|
||||
|
||||
class StorageVolume {
|
||||
public:
|
||||
StorageVolume() : allocated(false) { /* empty */ }
|
||||
|
||||
public:
|
||||
void setup(uint64_t addr, uint64_t size, StorageVolumeManager *volumeManager);
|
||||
|
||||
/*
|
||||
* Mimic the API of ARM_DRIVER_STORAGE
|
||||
*/
|
||||
public:
|
||||
ARM_DRIVER_VERSION GetVersion(void);
|
||||
ARM_STORAGE_CAPABILITIES GetCapabilities(void);
|
||||
int32_t Initialize(ARM_Storage_Callback_t callback);
|
||||
int32_t Uninitialize(void);
|
||||
int32_t PowerControl(ARM_POWER_STATE state);
|
||||
int32_t ReadData(uint64_t addr, void *data, uint32_t size);
|
||||
int32_t ProgramData(uint64_t addr, const void *data, uint32_t size);
|
||||
int32_t Erase(uint64_t addr, uint32_t size);
|
||||
int32_t EraseAll(void);
|
||||
ARM_STORAGE_STATUS GetStatus(void);
|
||||
int32_t GetInfo(ARM_STORAGE_INFO *infoP);
|
||||
uint32_t ResolveAddress(uint64_t addr);
|
||||
int32_t GetNextBlock(const ARM_STORAGE_BLOCK* prevP, ARM_STORAGE_BLOCK *nextP);
|
||||
int32_t GetBlock(uint64_t addr, ARM_STORAGE_BLOCK *blockP);
|
||||
|
||||
public:
|
||||
bool isAllocated(void) const {
|
||||
return allocated;
|
||||
}
|
||||
|
||||
void deallocate(void) {
|
||||
allocated = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Accessor methods.
|
||||
*/
|
||||
|
||||
uint64_t getVolumeOffset(void) const {
|
||||
return volumeOffset;
|
||||
}
|
||||
uint64_t getVolumeSize(void) const {
|
||||
return volumeSize;
|
||||
}
|
||||
const ARM_Storage_Callback_t &getCallback(void) const {
|
||||
return callback;
|
||||
}
|
||||
|
||||
private:
|
||||
bool overlapsWithBlock(const ARM_STORAGE_BLOCK* blockP) const {
|
||||
return (((blockP->addr + blockP->size) <= volumeOffset) || ((volumeOffset + volumeSize) <= blockP->addr)) ? false : true;
|
||||
}
|
||||
|
||||
void transformBlockToVolume(ARM_STORAGE_BLOCK *blockP) const {
|
||||
if (blockP->addr < volumeOffset) {
|
||||
blockP->addr = volumeOffset;
|
||||
}
|
||||
if ((blockP->addr + blockP->size) > (volumeOffset + volumeSize)) {
|
||||
blockP->size = (volumeOffset + volumeSize) - blockP->addr;
|
||||
}
|
||||
|
||||
blockP->addr -= volumeOffset;
|
||||
}
|
||||
|
||||
private:
|
||||
bool allocated;
|
||||
uint64_t volumeOffset;
|
||||
uint64_t volumeSize;
|
||||
ARM_Storage_Callback_t callback;
|
||||
StorageVolumeManager *volumeManager;
|
||||
};
|
||||
|
||||
class StorageVolumeManager {
|
||||
public:
|
||||
StorageVolumeManager() { /* empty */ }
|
||||
~StorageVolumeManager() { /* empty */ }
|
||||
|
||||
/**
|
||||
* Initialize the storage MTD and prepare it for operation within the context of the volume manager.
|
||||
*
|
||||
* @param[in] storage
|
||||
* The underlying MTD.
|
||||
* @param[in] callback
|
||||
* A callback to be invoked upon completion of initialization.
|
||||
*
|
||||
* @return If asynchronous activity is launched, an invocation returns
|
||||
* ARM_DRIVER_OK, and the caller can expect to receive a
|
||||
* callback in the future with a status value of ARM_DRIVER_OK or an error-
|
||||
* code. In the case of synchronous execution, control returns after
|
||||
* completion with a value of 1. Return values less than
|
||||
* ARM_DRIVER_OK (0) signify errors.
|
||||
*/
|
||||
int32_t initialize(ARM_DRIVER_STORAGE *mtd, InitializeCallback_t callback);
|
||||
|
||||
int32_t addVolume(uint64_t addr, uint64_t size, StorageVolume **volumePP);
|
||||
int32_t addVolume_C(uint64_t addr, uint64_t size, _ARM_DRIVER_STORAGE *mtd);
|
||||
int32_t lookupVolume(uint64_t addr, StorageVolume **volumePP);
|
||||
|
||||
/*
|
||||
* Accessor methods.
|
||||
*/
|
||||
|
||||
bool isInitialized() const {
|
||||
return initialized;
|
||||
}
|
||||
ARM_DRIVER_STORAGE *getStorage(void) const {
|
||||
return storage;
|
||||
}
|
||||
const ARM_STORAGE_INFO &getStorageInfo(void) const {
|
||||
return storageInfo;
|
||||
}
|
||||
const ARM_STORAGE_CAPABILITIES &getStorageCapabilities(void) const {
|
||||
return storageCapabilities;
|
||||
}
|
||||
StorageVolume *volumeAtIndex(size_t index) {
|
||||
return &volumes[index];
|
||||
}
|
||||
|
||||
public:
|
||||
static void storageCallback(int32_t status, ARM_STORAGE_OPERATION operation);
|
||||
|
||||
private:
|
||||
friend int32_t StorageVolume::PowerControl(ARM_POWER_STATE state);
|
||||
friend int32_t StorageVolume::ReadData(uint64_t addr, void *data, uint32_t size);
|
||||
friend int32_t StorageVolume::ProgramData(uint64_t addr, const void *data, uint32_t size);
|
||||
friend int32_t StorageVolume::Erase(uint64_t addr, uint32_t size);
|
||||
friend int32_t StorageVolume::EraseAll(void);
|
||||
friend ARM_STORAGE_STATUS StorageVolume::GetStatus(void);
|
||||
StorageVolume *activeVolume; /* This state-variable is set to point to a volume
|
||||
* while there is pending activity. It tracks
|
||||
* the volume which is at the source of the
|
||||
* activity. Once the activity finishes, this
|
||||
* variable is reset. Having this variable set
|
||||
* might be used as an indication that the
|
||||
* underlying storage is busy. */
|
||||
|
||||
#define FRIEND_DECLARATIONS_FOR_VOLUME(N) \
|
||||
friend ARM_DRIVER_VERSION GetVersion_ ## N(void); \
|
||||
friend ARM_STORAGE_CAPABILITIES GetCapabilities_ ## N(void); \
|
||||
friend int32_t Initialize_ ## N(ARM_Storage_Callback_t callback); \
|
||||
friend int32_t Uninitialize_ ## N(void); \
|
||||
friend int32_t PowerControl_ ## N(ARM_POWER_STATE state); \
|
||||
friend int32_t ReadData_ ## N(uint64_t addr, void *data, uint32_t size); \
|
||||
friend int32_t ProgramData_ ## N(uint64_t addr, const void *data, uint32_t size); \
|
||||
friend int32_t Erase_ ## N(uint64_t addr, uint32_t size); \
|
||||
friend int32_t EraseAll_ ## N(void); \
|
||||
friend ARM_STORAGE_STATUS GetStatus_ ## N(void); \
|
||||
friend int32_t GetInfo_ ## N(ARM_STORAGE_INFO *infoP); \
|
||||
friend uint32_t ResolveAddress_ ## N(uint64_t addr); \
|
||||
friend int32_t GetNextBlock_ ## N(const ARM_STORAGE_BLOCK* prevP, ARM_STORAGE_BLOCK *nextP); \
|
||||
friend int32_t GetBlock_ ## N(uint64_t addr, ARM_STORAGE_BLOCK *blockP);
|
||||
|
||||
#define FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_1 FRIEND_DECLARATIONS_FOR_VOLUME(0)
|
||||
#define FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_2 FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_1 FRIEND_DECLARATIONS_FOR_VOLUME(1)
|
||||
#define FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_3 FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_2 FRIEND_DECLARATIONS_FOR_VOLUME(2)
|
||||
#define FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_4 FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_3 FRIEND_DECLARATIONS_FOR_VOLUME(3)
|
||||
/* ... add more of the above if ever needed */
|
||||
|
||||
#define FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES(N) EXPAND(CONCATENATE(FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_, N))
|
||||
|
||||
//todo: remove FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES(MAX_VOLUMES);
|
||||
FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES(MAX_VOLUMES)
|
||||
|
||||
private:
|
||||
size_t findIndexOfUnusedVolume(void) const;
|
||||
|
||||
private:
|
||||
bool initialized;
|
||||
ARM_DRIVER_STORAGE *storage;
|
||||
ARM_STORAGE_INFO storageInfo;
|
||||
ARM_STORAGE_CAPABILITIES storageCapabilities;
|
||||
StorageVolume volumes[MAX_VOLUMES];
|
||||
};
|
||||
|
||||
#endif /* __STORAGE_VOLUME_MANAGER_H__ */
|
|
@ -15,9 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if DEVICE_STORAGE
|
||||
|
||||
#include "Driver_Storage.h"
|
||||
#include "storage_abstraction/Driver_Storage.h"
|
||||
#include "cmsis_nvic.h"
|
||||
#include "MK64F12.h"
|
||||
|
||||
|
@ -33,6 +31,10 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
/* Redefine this macro to a printf equivalent to print trace */
|
||||
#define tr_debug(...)
|
||||
|
||||
|
||||
#ifdef USING_KSDK2
|
||||
/*!
|
||||
* @name Misc utility defines
|
||||
|
@ -142,6 +144,76 @@ extern volatile uint32_t *const kFCCOBx;
|
|||
#define SIZEOF_DOUBLE_PHRASE (16)
|
||||
#endif /* #ifdef USING_KSDK2 */
|
||||
|
||||
/* While the K64F flash controller is capable of launching operations asynchronously and
|
||||
* allowing program execution to continue while an erase/program is active, it
|
||||
* doesn't allow simultaneous read accesses while and erase/program is active on
|
||||
* the same block of flash.
|
||||
*
|
||||
* Read/fetch accesses can originate arbitrarily as a result of program
|
||||
* execution. This means that code which operates on flash should not reside in
|
||||
* flash; or at least it should not reside in the same bank of flash as it is
|
||||
* operating upon. The only way to ensure that application code and flash driver
|
||||
* are residing on separate banks of flash is to reserve bank-0 (or BLOCK0) for
|
||||
* the application and bank-1 (BLOCK1) for the driver--this also happens to be
|
||||
* the default setting.
|
||||
*
|
||||
* But it is quite likely that this default will be over-ridden by the use of
|
||||
* config options depending upon the actual application. If we don't have a
|
||||
* clean separation between the application and the space managed by this
|
||||
* driver, then we need to enforce the following:
|
||||
*
|
||||
* - Force synchronous mode of execution in the storage_driver.
|
||||
* - Disable interrupts during erase/program operations.
|
||||
* - Ensure all code and data structures used in the storage driver execute
|
||||
* out of RAM. Refer to __RAMFUNC (below) which allows for this.
|
||||
*
|
||||
* It is difficult to determine the application's span of internal-flash at
|
||||
* compile time. Therefore we assume that STORAGE_START_ADDR is the
|
||||
* boundary between application and this driver. When this boundary is set to
|
||||
* lie at BLOCK1_START_ADDR, there is no possibility of read-while-write run-
|
||||
* time errors.
|
||||
*
|
||||
* In the following, caps.asynchronous_ops is defined to be 1 if and only if
|
||||
* asynchronous operation mode is requested and there doesn't exist the
|
||||
* possibility of concurrent reads.
|
||||
*/
|
||||
|
||||
#if (defined(STORAGE_START_ADDR) && (STORAGE_START_ADDR != BLOCK1_START_ADDR))
|
||||
#define EXISTS_POSSIBILITY_OF_CONCURRENT_READ 1
|
||||
#else
|
||||
#define EXISTS_POSSIBILITY_OF_CONCURRENT_READ 0
|
||||
#endif
|
||||
|
||||
/* Define '__RAMFUNC' as an attribute to mark a function as residing in RAM.
|
||||
* Use of __RAMFUNC puts a function in the .data section--i.e. the
|
||||
* initialized data section. This will be copied into RAM automatically by the
|
||||
* startup sequence. */
|
||||
#ifndef __RAMFUNC
|
||||
#if defined(__GNUC__) || defined(__clang__) // GCC and llvm/clang
|
||||
#define __RAMFUNC __attribute__ ((section (".data#"), noinline)) /* The '#' following ".data" needs a bit of
|
||||
* explanation. Without it, we are liable to get the following warning 'Warning: ignoring
|
||||
* changed section attributes for .data'. This is because __attribute__((section(".data")))
|
||||
* generates the following assembly:
|
||||
*
|
||||
* .section .data,"ax",%progbits
|
||||
*
|
||||
* But .data doesn't need the 'x' (execute) attribute bit. To remove the warning, we specify
|
||||
* the attribute with a '#' at the tail, which emits:
|
||||
*
|
||||
* .section .data#,"ax",%progbits
|
||||
*
|
||||
* Note that '#' (in the above) acts like a comment-start, and masks the additional
|
||||
* attributes which don't apply to '.data'.
|
||||
*/
|
||||
#elif defined (__CC_ARM)
|
||||
#define __RAMFUNC __attribute__ ((section(".ramfunc"), noinline))
|
||||
#elif defined ( __ICCARM__ )
|
||||
#define __RAMFUNC __ramfunc
|
||||
#else // unknown compiler
|
||||
#error "This compiler is not yet supported. If you can contribute support for defining a function to be RAM resident, please provide a definition for __RAMFUNC"
|
||||
#endif
|
||||
#endif /* #ifndef __RAMFUNC */
|
||||
|
||||
/*
|
||||
* forward declarations
|
||||
*/
|
||||
|
@ -169,16 +241,16 @@ struct mtd_k64f_data {
|
|||
static const ARM_STORAGE_BLOCK blockTable[] = {
|
||||
{
|
||||
/**< This is the start address of the flash block. */
|
||||
#ifdef DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_START_ADDR
|
||||
.addr = DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_START_ADDR,
|
||||
#ifdef STORAGE_START_ADDR
|
||||
.addr = STORAGE_START_ADDR,
|
||||
#else
|
||||
.addr = BLOCK1_START_ADDR,
|
||||
#endif
|
||||
|
||||
/**< This is the size of the flash block, in units of bytes.
|
||||
* Together with addr, it describes a range [addr, addr+size). */
|
||||
#ifdef DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_SIZE
|
||||
.size = DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_SIZE,
|
||||
#ifdef STORAGE_SIZE
|
||||
.size = STORAGE_SIZE,
|
||||
#else
|
||||
.size = BLOCK1_SIZE,
|
||||
#endif
|
||||
|
@ -200,7 +272,8 @@ static const ARM_DRIVER_VERSION version = {
|
|||
};
|
||||
|
||||
|
||||
#if (!defined(DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS) || DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS)
|
||||
#if ((!defined(STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS) || STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS) && \
|
||||
!EXISTS_POSSIBILITY_OF_CONCURRENT_READ)
|
||||
#define ASYNC_OPS 1
|
||||
#else
|
||||
#define ASYNC_OPS 0
|
||||
|
@ -219,8 +292,8 @@ static const ARM_STORAGE_CAPABILITIES caps = {
|
|||
.asynchronous_ops = ASYNC_OPS,
|
||||
|
||||
/* Enable chip-erase functionality if we own all of block-1. */
|
||||
#if ((!defined (DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_START_ADDR) || (DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_START_ADDR == BLOCK1_START_ADDR)) && \
|
||||
(!defined (DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_SIZE) || (DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_SIZE == BLOCK1_SIZE)))
|
||||
#if ((!defined (STORAGE_START_ADDR) || (STORAGE_START_ADDR == BLOCK1_START_ADDR)) && \
|
||||
(!defined (STORAGE_SIZE) || (STORAGE_SIZE == BLOCK1_SIZE)))
|
||||
.erase_all = 1, /**< Supports EraseChip operation. */
|
||||
#else
|
||||
.erase_all = 0, /**< Supports EraseChip operation. */
|
||||
|
@ -228,8 +301,8 @@ static const ARM_STORAGE_CAPABILITIES caps = {
|
|||
};
|
||||
|
||||
static const ARM_STORAGE_INFO info = {
|
||||
#ifdef DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_SIZE
|
||||
.total_storage = DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_SIZE, /**< Total available storage, in units of octets. */
|
||||
#ifdef STORAGE_SIZE
|
||||
.total_storage = STORAGE_SIZE, /**< Total available storage, in units of octets. */
|
||||
#else
|
||||
.total_storage = BLOCK1_SIZE, /**< Total available storage, in units of octets. By default, BLOCK0 is reserved to hold program code. */
|
||||
#endif
|
||||
|
@ -410,6 +483,10 @@ static inline void launchCommand(void)
|
|||
|
||||
#else /* #if !ASYNC_OPS */
|
||||
|
||||
#if EXISTS_POSSIBILITY_OF_CONCURRENT_READ
|
||||
/* This function needs to execute from RAM to avoid read-while-write errors. */
|
||||
__RAMFUNC
|
||||
#endif
|
||||
static void launchCommandAndWaitForCompletion()
|
||||
{
|
||||
// It contains the inlined equivalent of the following code snippet:
|
||||
|
@ -418,10 +495,17 @@ static void launchCommandAndWaitForCompletion()
|
|||
// /* Spin waiting for the command execution to complete. */
|
||||
// }
|
||||
|
||||
#ifdef USING_KSDK2
|
||||
FTFx->FSTAT = FTFx_FSTAT_CCIF_MASK; /* launchcommand() */
|
||||
while ((FTFx->FSTAT & FTFx_FSTAT_CCIF_MASK) == 0) {
|
||||
/* Spin waiting for the command execution to complete. */
|
||||
}
|
||||
#else
|
||||
BW_FTFE_FSTAT_CCIF(FTFE, 1); /* launchCommand() */
|
||||
while (BR_FTFE_FSTAT_CCIF(FTFE) == 0) {
|
||||
/* Spin waiting for the command execution to complete. */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif /* #if !ASYNC_OPS */
|
||||
|
||||
|
@ -562,6 +646,7 @@ static inline void setupNextProgramData(struct mtd_k64f_data *context)
|
|||
if ((context->amountLeftToOperate == PROGRAM_PHRASE_SIZEOF_INLINE_DATA) ||
|
||||
((context->currentOperatingStorageAddress % SIZEOF_DOUBLE_PHRASE) == PROGRAM_PHRASE_SIZEOF_INLINE_DATA)) {
|
||||
setup8ByteWrite(context->currentOperatingStorageAddress, context->currentOperatingData);
|
||||
tr_debug("setupNextProgramData: W8, [%lu]", (uint32_t)context->currentOperatingStorageAddress);
|
||||
|
||||
context->amountLeftToOperate -= PROGRAM_PHRASE_SIZEOF_INLINE_DATA;
|
||||
context->currentOperatingStorageAddress += PROGRAM_PHRASE_SIZEOF_INLINE_DATA;
|
||||
|
@ -569,6 +654,7 @@ static inline void setupNextProgramData(struct mtd_k64f_data *context)
|
|||
} else {
|
||||
size_t amount = sizeofLargestProgramSection(context->currentOperatingStorageAddress, context->amountLeftToOperate);
|
||||
setupProgramSection(context->currentOperatingStorageAddress, context->currentOperatingData, amount);
|
||||
tr_debug("setupNextProgramData: W%u, [%lu]", amount, (uint32_t)context->currentOperatingStorageAddress);
|
||||
|
||||
context->amountLeftToOperate -= amount;
|
||||
context->currentOperatingStorageAddress += amount;
|
||||
|
@ -607,6 +693,8 @@ static inline void setupNextErase(struct mtd_k64f_data *context)
|
|||
|
||||
static int32_t executeCommand(struct mtd_k64f_data *context)
|
||||
{
|
||||
tr_debug("executeCommand: top");
|
||||
|
||||
#if ASYNC_OPS
|
||||
/* Asynchronous operation */
|
||||
(void)context; /* avoid compiler warning about un-used variables */
|
||||
|
@ -624,12 +712,21 @@ static int32_t executeCommand(struct mtd_k64f_data *context)
|
|||
|
||||
enableCommandCompletionInterrupt();
|
||||
|
||||
tr_debug("executeCommand: async. return");
|
||||
return ARM_DRIVER_OK; /* signal asynchronous completion. An interrupt will signal completion later. */
|
||||
#else /* #if ASYNC_OPS */
|
||||
/* Synchronous operation. */
|
||||
/* Synchronous operation. This is the common case. */
|
||||
|
||||
while (1) {
|
||||
tr_debug("executeCommand: synchronous iteration");
|
||||
|
||||
#if EXISTS_POSSIBILITY_OF_CONCURRENT_READ
|
||||
__disable_irq();
|
||||
#endif
|
||||
launchCommandAndWaitForCompletion();
|
||||
#if EXISTS_POSSIBILITY_OF_CONCURRENT_READ
|
||||
__enable_irq();
|
||||
#endif
|
||||
|
||||
/* Execution may result in failure. Check for errors */
|
||||
if (failedWithAccessError() || failedWithProtectionError()) {
|
||||
|
@ -664,6 +761,7 @@ static int32_t executeCommand(struct mtd_k64f_data *context)
|
|||
break;
|
||||
} else {
|
||||
/* erase can be skipped since this sector is already erased. */
|
||||
tr_debug("fast forward erase");
|
||||
progressEraseContextByEraseUnit(context);
|
||||
}
|
||||
}
|
||||
|
@ -691,6 +789,7 @@ static inline void launchCommandFromIRQ(const struct mtd_k64f_data *context)
|
|||
if (failedWithAccessError() || failedWithProtectionError()) {
|
||||
clearErrorStatusBits();
|
||||
if (context->commandCompletionCallback) {
|
||||
tr_debug("irq: invoking callback with error");
|
||||
context->commandCompletionCallback(ARM_DRIVER_ERROR_PARAMETER, context->currentCommand);
|
||||
}
|
||||
return;
|
||||
|
@ -726,6 +825,7 @@ static void ftfe_ccie_irq_handler(void)
|
|||
case ARM_STORAGE_OPERATION_PROGRAM_DATA:
|
||||
if (context->amountLeftToOperate == 0) {
|
||||
if (context->commandCompletionCallback) {
|
||||
tr_debug("irq: [PROGRAM] invoking callback");
|
||||
context->commandCompletionCallback(context->sizeofCurrentOperation, ARM_STORAGE_OPERATION_PROGRAM_DATA);
|
||||
}
|
||||
return;
|
||||
|
@ -745,11 +845,13 @@ static void ftfe_ccie_irq_handler(void)
|
|||
break;
|
||||
} else {
|
||||
/* erase can be skipped since this sector is already erased. */
|
||||
tr_debug("fast forward erase");
|
||||
progressEraseContextByEraseUnit(context);
|
||||
}
|
||||
}
|
||||
if (context->amountLeftToOperate == 0) {
|
||||
if (context->commandCompletionCallback) {
|
||||
tr_debug("irq: [ERASE] invoking callback");
|
||||
context->commandCompletionCallback(context->sizeofCurrentOperation, ARM_STORAGE_OPERATION_ERASE);
|
||||
}
|
||||
return;
|
||||
|
@ -761,6 +863,7 @@ static void ftfe_ccie_irq_handler(void)
|
|||
|
||||
default:
|
||||
if (context->commandCompletionCallback) {
|
||||
tr_debug("irq: [default] invoking callback");
|
||||
context->commandCompletionCallback(ARM_DRIVER_OK, context->currentCommand);
|
||||
}
|
||||
break;
|
||||
|
@ -827,6 +930,8 @@ static ARM_STORAGE_CAPABILITIES getCapabilities(void)
|
|||
|
||||
static int32_t initialize(ARM_Storage_Callback_t callback)
|
||||
{
|
||||
tr_debug("called initialize(%p)", callback);
|
||||
|
||||
struct mtd_k64f_data *context = &mtd_k64f_data;
|
||||
memset(context, 0, sizeof(mtd_k64f_data));
|
||||
context->currentCommand = ARM_STORAGE_OPERATION_INITIALIZE;
|
||||
|
@ -860,6 +965,8 @@ static int32_t initialize(ARM_Storage_Callback_t callback)
|
|||
}
|
||||
|
||||
static int32_t uninitialize(void) {
|
||||
tr_debug("called uninitialize");
|
||||
|
||||
struct mtd_k64f_data *context = &mtd_k64f_data;
|
||||
context->currentCommand = ARM_STORAGE_OPERATION_UNINITIALIZE;
|
||||
|
||||
|
@ -883,6 +990,8 @@ static int32_t uninitialize(void) {
|
|||
|
||||
static int32_t powerControl(ARM_POWER_STATE state)
|
||||
{
|
||||
tr_debug("called powerControl(%u)", state);
|
||||
|
||||
struct mtd_k64f_data *context = &mtd_k64f_data;
|
||||
context->currentCommand = ARM_STORAGE_OPERATION_POWER_CONTROL;
|
||||
|
||||
|
@ -892,6 +1001,8 @@ static int32_t powerControl(ARM_POWER_STATE state)
|
|||
|
||||
static int32_t readData(uint64_t addr, void *data, uint32_t size)
|
||||
{
|
||||
tr_debug("called ReadData(%lu, %lu)", (uint32_t)addr, size);
|
||||
|
||||
struct mtd_k64f_data *context = &mtd_k64f_data;
|
||||
context->currentCommand = ARM_STORAGE_OPERATION_READ_DATA;
|
||||
|
||||
|
@ -914,6 +1025,8 @@ static int32_t readData(uint64_t addr, void *data, uint32_t size)
|
|||
|
||||
static int32_t programData(uint64_t addr, const void *data, uint32_t size)
|
||||
{
|
||||
tr_debug("called ProgramData(%lu, %lu)", (uint32_t)addr, size);
|
||||
|
||||
struct mtd_k64f_data *context = &mtd_k64f_data;
|
||||
if (!context->initialized) {
|
||||
return (int32_t)ARM_DRIVER_ERROR; /* illegal */
|
||||
|
@ -955,6 +1068,8 @@ static int32_t programData(uint64_t addr, const void *data, uint32_t size)
|
|||
|
||||
static int32_t erase(uint64_t addr, uint32_t size)
|
||||
{
|
||||
tr_debug("called erase(%lu, %lu)", (uint32_t)addr, size);
|
||||
|
||||
struct mtd_k64f_data *context = &mtd_k64f_data;
|
||||
|
||||
if (!context->initialized) {
|
||||
|
@ -995,6 +1110,8 @@ static int32_t erase(uint64_t addr, uint32_t size)
|
|||
|
||||
static int32_t eraseAll(void)
|
||||
{
|
||||
tr_debug("called eraseAll");
|
||||
|
||||
struct mtd_k64f_data *context = &mtd_k64f_data;
|
||||
|
||||
if (!context->initialized) {
|
||||
|
@ -1105,7 +1222,7 @@ int32_t getBlock(uint64_t addr, ARM_STORAGE_BLOCK *blockP)
|
|||
return ARM_DRIVER_ERROR;
|
||||
}
|
||||
|
||||
ARM_DRIVER_STORAGE ARM_Driver_Storage_(0) = {
|
||||
ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F = {
|
||||
.GetVersion = getVersion,
|
||||
.GetCapabilities = getCapabilities,
|
||||
.Initialize = initialize,
|
||||
|
@ -1121,5 +1238,3 @@ ARM_DRIVER_STORAGE ARM_Driver_Storage_(0) = {
|
|||
.GetNextBlock = nextBlock,
|
||||
.GetBlock = getBlock
|
||||
};
|
||||
|
||||
#endif /* #if DEVICE_STORAGE */
|
||||
|
|
Loading…
Reference in New Issue