/*
 * 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_H__
#define __FLASH_JOURNAL_H__

#ifdef __cplusplus
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
 * both error and success status returns. This enumeration contains all
 * possible error status values.
 */
typedef enum _FlashJournal_Status
{
    JOURNAL_STATUS_OK                =   0,
    JOURNAL_STATUS_ERROR             =  -1, ///< Unspecified error
    JOURNAL_STATUS_BUSY              =  -2, ///< Underlying storage is currently unavailable
    JOURNAL_STATUS_TIMEOUT           =  -3, ///< Timeout occurred
    JOURNAL_STATUS_UNSUPPORTED       =  -4, ///< Operation not supported
    JOURNAL_STATUS_PARAMETER         =  -5, ///< Parameter error
    JOURNAL_STATUS_BOUNDED_CAPACITY  =  -6, ///< Attempt to write larger than available capacity
    JOURNAL_STATUS_STORAGE_API_ERROR =  -7, ///< Failure from some Storage API
    JOURNAL_STATUS_STORAGE_IO_ERROR  =  -8, ///< Failure from underlying storage during an IO operation.
    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.
} FlashJournal_Status_t;

/**
 * Command opcodes for flash. Completion callbacks use these codes to refer to
 * completing commands. Refer to \ref ARM_Flash_Callback_t.
 */
typedef enum _FlashJournal_OpCode {
    FLASH_JOURNAL_OPCODE_INITIALIZE,
    FLASH_JOURNAL_OPCODE_GET_INFO,
    FLASH_JOURNAL_OPCODE_READ_BLOB,
    FLASH_JOURNAL_OPCODE_LOG_BLOB,
    FLASH_JOURNAL_OPCODE_COMMIT,
    FLASH_JOURNAL_OPCODE_RESET,
} FlashJournal_OpCode_t;

/**
 * @brief Flash Journal information. This contains journal-metadata, and is the
 *     return value from calling GetInfo() on the journal driver.
 */
typedef struct _FlashJournal_Info {
    uint64_t capacity;             ///< Maximum capacity (in octets) of the flash journal--i.e. the largest 'blob' which can be contained as payload.
    uint64_t sizeofJournaledBlob;  ///< size (in octets) of the most recently logged blob.
    uint32_t program_unit;         ///< Minimum programming size (in units of octets) for
                                   ///<   the current storage block--the one which will be used
                                   ///<   for the next log() operation. This value may change as we
                                   ///<   cycle through the blocks of the underlying MTD.
                                   ///<   Callers of FlashJournal_log() should refer to this field
                                   ///<   upon receiving the error JOURNAL_STATUS_SMALL_LOG_REQUEST
                                   ///<   (of when the actual amount of data logged is smaller than
                                   ///<   the requested amount).
} FlashJournal_Info_t;

/**
 * This is the type of the command completion callback handler for the
 * asynchronous flash-journal APIs: initialize(), read(), log(), commit() and
 * reset() (which is nearly all APIs).
 *
 * @param status
 *          A code to indicate the status of the completed operation. For data
 *          transfer operations, the status field is overloaded in case of
 *          success to return the amount of data successfully transferred; this
 *          can be done safely because error codes are negative values.
 *
 * @param cmd_code
 *          The command op-code of type FlashJournal_OpCode_t. This value isn't
 *          essential for the callback, but it is expected that this information
 *          could be a quick and useful filter.
 */
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;

/**
 * @ref FlashJournal_t is an abstraction implemented by a table of generic
 * operations (i.e. strategy) together with an opaque, strategy-specific
 * data. Taken together, the FlashJournal_t is an opaque handle containing
 * such top-level metadata.
 *
 * Algorithms depending on the FlashJournal can be generic (i.e. independent of
 * the strategy) in their use of the Flash-Journal APIs. For the sake of being
 * able to allocate a FlashJournal_t for use in such generic algorithms, the
 * FlashJournal_t contains a MAX_SIZE to accommodate the largest of the
 * 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

/**
 * 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 {
    /**
     * \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);

    /**
     * \brief fetch journal metadata. Refer to @ref FlashJournal_getInfo.
     */
    FlashJournal_Status_t (*getInfo)   (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);

    /**
     * @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);

    /**
     * @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);

    /**
     * @brief Reset the journal. This has the effect of erasing all valid blobs.
     *     Refer to @ref FlashJournal_reset.
     */
    int32_t               (*reset)     (FlashJournal_t *journal);
} FlashJournal_Ops_t;

/**
 * @brief An opaque handle constituting the Flash Journal.
 *
 * @details This structure is intentionally opaque to avoid exposing data
 *     internal to an implementation strategy; this prevents accesses through any
 *     means other than through the defined API.
 *
 * Having a known size for the handle allows the caller to remain malloc-free.
 *
 * @note: There should be static asserts in the code to verify our assumption
 * that the real FlashJournal handle fits within FLASH_JOURNAL_HANDLE_MAX_SIZE
 * bytes.
 *
 * @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 {
    FlashJournal_Ops_t       ops;

    union {
        ARM_DRIVER_STORAGE  *mtd;
        FlashJournal_Info_t  info;
        void                *pointer;
        uint8_t              octet;
        uint32_t             data[FLASH_JOURNAL_HANDLE_MAX_SIZE / sizeof(uint32_t)];
    } opaque;
} FlashJournal_t;

/**
 * @brief Initialize a flash journal.
 *
 * This is a front-end for @ref FlashJournal_Ops_t::initialize() of the
 * underlying strategy.
 *
 * This function must be called *before* the middle-ware component starts
 * using a journal. As a part of bringing the journal to a ready state, it
 * also discovers the most recently logged blob.
 *
 * Initialize() receives a callback handler to be invoked upon completion of
 * asynchronous operations.
 *
 * @param [out] journal
 *                A caller-supplied buffer large enough to hold an
 *                initialized journal. The internals of the actual journal
 *                are opaque to the caller and depend on the logging
 *                strategy (as defined by the parameter 'ops'). This memory
 *                should be at least as large as 'FLASH_JOURNAL_HANDLE_MAX_SIZE'.
 *                Upon successful return, the journal is setup in an
 *                initialized state.
 *
 * @param  [in] mtd
 *                The underlying Storage_Driver targeted by the journal. MTD
 *                stands for Memory-Technology-Device.
 *
 * @param  [in] ops
 *                This is the set of operations which define the logging strategy.
 *
 * @param  [in] callback
 *                Caller-defined callback to be invoked upon command completion of
 *                initialization; and also for all future invocations of
 *                asynchronous APIs. 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
 *     JOURNAL_STATUS_OK 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 = FlashJournal_initialize(&journal, MTD, &STRATEGY_SEQUENTIAL, 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
 */
static inline int32_t FlashJournal_initialize(FlashJournal_t           *journal,
                                              ARM_DRIVER_STORAGE       *mtd,
                                              const FlashJournal_Ops_t *ops,
                                              FlashJournal_Callback_t   callback)
{
    return ops->initialize(journal, mtd, ops, callback);
}

/**
 * @brief Fetch journal metadata. A front-end for @ref FlashJournal_Ops_t::getInfo().
 *
 * @param [in] journal
 *                A previously initialized journal.
 *
 * @param [out] info
 *                A caller-supplied buffer capable of being filled in with an
 *                FlashJournal_Info_t.
 *
 * @return JOURNAL_STATUS_OK if a FlashJournal_Info_t structure containing
 *         top level metadata about the journal is filled into the supplied
 *         buffer, else an appropriate error value.
 *
 * @note It is the caller's responsibility to ensure that the buffer passed in
 *         is able to be initialized with a FlashJournal_Info_t.
 *
 * @note getInfo()s can still be called during a sequence of
 *     log()s.
 *
 * @note This API returns synchronously--it does not result in an invocation
 *     of a completion callback.
 *
 * 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
 *     FlashJournal_Info_t info;
 *     int32_t returnValue = FlashJournal_getInfo(&journal, &info);
 *     if (returnValue < JOURNAL_STATUS_OK) {
 *         // handle error
 *     } else {
 *         ASSERT(returnValue == JOURNAL_STATUS_OK);
 *         // work with the 'info'.
 *     }
 * \endcode
 */
static inline FlashJournal_Status_t FlashJournal_getInfo(FlashJournal_t *journal, FlashJournal_Info_t *info)
{
    return journal->ops.getInfo(journal, info);
}

/**
 * @brief Read from the most recently logged blob. A front-end for @ref
 * FlashJournal_Ops_t::read().
 *
 * @details Read off a chunk of the logged blob sequentially. The blob may
 *     be larger than the size of the read (or even of available SRAM), so
 *     multiple calls to read() could be necessary before the entire blob is
 *     read off. The journal maintains a read-pointer internally to allow
 *     reads to continue where the previous one left off.
 *
 * @note: Once the entire blob is read, the final read() 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 [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_read(&journal, 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_read(FlashJournal_t *journal, void *blob, size_t n)
{
    return journal->ops.read(journal, 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().
 *
 * @details Extend (or start off) the currently logged blob sequentially.
 *     There could be several calls to log() before the entire blob is
 *     accumulated. A sequence of one or more log() must be terminated by a
 *     commit() before the state of the blob is sealed and made persistent.
 *     The journal maintains a log-pointer internally to allow
 *     log()s to continue where the previous one left off.
 *
 * @param [in] journal
 *               A previously initialized journal.
 *
 * @param [in] blob
 *               The source of the log operation. The memory is owned
 *               by the caller and should remain valid for the lifetime
 *               of this operation.
 *
 * @param [in] size
 *               The amount of data being logged in this operation. The
 *               buffer pointed to by 'blob' should be as large as this
 *               amount.
 *
 * @return [please be sure to read notes (below) regarding other return values]
 *   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
 *     logged, 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 log
 *     operation can be finished synchronously in spite of
 *     ARM_STORAGE_CAPABILITIES::asynchronous_ops being set, returning the
 *     number of data items logged 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.
 *
 * @note If a log operation will exceed available capacity, it fails with the
 *     error JOURNAL_STATUS_BOUNDED_CAPACITY.
 *
 * @note The actual size of data transfer (as reported by the status
 *     parameter of the callback or the return value from log() in case of
 *     synchronous operation) may be smaller than the amount requested. This
 *     could be due to the 'program_unit' of the underlying storage block--
 *     i.e. the minimum programmable size. Refer to @ref
 *     FlashJournal_Info_t::program_unit. It is the caller's responsibility
 *     for resubmitting this left-over data in a subsequent call to log.
 *     When logging an arbitrary amount of data, the last of a sequence of
 *     logs may need to be padded in order to align with the
 *     programming unit.
 *
 * @note If the total size requested to be logged is smaller
 *     than the MTD's program_unit, log() fails with an error value of
 *     JOURNAL_STATUS_SMALL_LOG_REQUEST.
 *
 * @note the data being logged isn't made persistent (or available for read-
 *     backs) until a commit. A sequence of log() operations is expected to end
 *     in a commit(). A new sequence of log()s should be initiated by the caller
 *     only after a commit() has completed. If a sequence of logs() is followed
 *     by an operation other than a commit, that operation will very likely
 *     return an error code. getInfo()s can still be called during a sequence of
 *     log()s.
 *
 * 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_log(&journal, 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
 *
 *         if (returnValue < size) {
 *     #if DEBUG
 *             FlashJournal_Info_t info;
 *             int32_t rc = FlashJournal_getInfo(&journal, &info);
 *             ASSERT(rc == JOURNAL_STATUS_OK);
 *             ASSERT(returnValue == (size - (size % info.program_unit)));
 *     #endif
 *             // move the last (size - returnValue) bytes of the buffer to the
 *             // beginning of the buffer to be used for the successive request.
 *         }
 *     }
 * \endcode
 */
static inline int32_t FlashJournal_log(FlashJournal_t *journal, const void *blob, size_t n)
{
    return journal->ops.log(journal, blob, n);
}

/**
 * @brief Commit a blob accumulated through a (possibly empty) sequence of previously
 *     successful log() operations. A front-end for @ref FlashJournal_Ops_t::commit().
 *
 * @param  [in] journal
 *                A previously initialized journal.
 *
 * @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 1 passed in as the 'status' parameter of the callback to indicate
 *     success. 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 1 to indicate success, 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
 *     commit operation can be finished synchronously in spite of
 *     ARM_STORAGE_CAPABILITIES::asynchronous_ops being set, returning the
 *     total size of the committed blob 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_commit(&journal);
 *     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 {
 *         // handle synchronous completion
 *         ASSERT(returnValue == 1);
 *         ...
 *     }
 * \endcode
 *
 * @note A sequence of log() operations is expected to end in a commit(). A new
 *     sequence of log()s should be initiated by the caller only after a
 *     commit() has completed. If a sequence of logs() is followed
 *     by an operation other than a commit, that operation will very likely
 *     return an error code.
 */
static inline int32_t FlashJournal_commit(FlashJournal_t *journal)
{
    return journal->ops.commit(journal);
}

/**
 * @brief Reset the journal. This has the effect of erasing all valid blobs. A
 *     front-end for @ref FlashJournal_Ops_t::reset().
 *
 * @param [in] journal
 *               A previously initialized journal.
 *
 * @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
 *     JOURNAL_STATUS_OK 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 1 to signal
 *     successful completion, 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
 *     reset operation can be finished synchronously in spite of
 *     ARM_STORAGE_CAPABILITIES::asynchronous_ops being set, returning 1 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_reset(&journal);
 *     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
 */
static inline int32_t FlashJournal_reset(FlashJournal_t *journal)
{
    return journal->ops.reset(journal);
}

#ifdef __cplusplus
}
#endif // __cplusplus

#endif /* __FLASH_JOURNAL_H__ */
