/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is property of Nordic Semiconductor ASA.
 * Terms and conditions of usage are described in detail in NORDIC
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 *
 * Licensees are granted free, non-transferable use of the information. NO
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 * the file.
 *
 */

#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "nordic_common.h"
#include "nrf_error.h"
#include "nrf_assert.h"
#include "nrf.h"
#include "nrf_soc.h"
#include "app_util.h"
#include "pstorage.h"


#define INVALID_OPCODE              0x00                       /**< Invalid op code identifier. */
#define SOC_MAX_WRITE_SIZE          1024                       /**< Maximum write size allowed for a single call to \ref sd_flash_write as specified in the SoC API. */

/**
 * @defgroup api_param_check API Parameters check macros.
 *
 * @details Macros that verify parameters passed to the module in the APIs. These macros
 *          could be mapped to nothing in final versions of code to save execution and size.
 *
 * @{
 */
/**
 * @brief Checks if API parameters are null. Mostly used for pointer parameters.
 */
#define NULL_PARAM_CHECK(PARAM)                                                                   \
        if ((PARAM) == NULL)                                                                      \
        {                                                                                         \
            return NRF_ERROR_NULL;                                                                \
        }

/**@brief Verifies the module identifier supplied by the application is within permissible
 *        range.
 */
#define MODULE_ID_RANGE_CHECK(ID)                                                                 \
        if ((PSTORAGE_MAX_APPLICATIONS <= ((ID)->module_id)) ||                                   \
            (m_app_table[(ID)->module_id].cb == NULL))                                            \
        {                                                                                         \
            return NRF_ERROR_INVALID_PARAM;                                                       \
        }


/**@brief Verifies the block identifier supplied by the application is within the permissible
 *        range.
 */
#define BLOCK_ID_RANGE_CHECK(ID)                                                                  \
        if ((m_app_table[(ID)->module_id].base_id + (m_app_table[(ID)->module_id].block_count *   \
            MODULE_BLOCK_SIZE(ID))) <= ((ID)->block_id))                                          \
        {                                                                                         \
            return NRF_ERROR_INVALID_PARAM;                                                       \
        }


/**@brief Verifies the block size requested by the application can be supported by the module. */
#define BLOCK_SIZE_CHECK(X)                                                                       \
        if (((X) > PSTORAGE_MAX_BLOCK_SIZE) || ((X) < PSTORAGE_MIN_BLOCK_SIZE))                   \
        {                                                                                         \
            return NRF_ERROR_INVALID_PARAM;                                                       \
        }

/**@brief Verifies block size requested by Application in registration API */
#define BLOCK_COUNT_CHECK(COUNT, SIZE)                                                            \
        if (((COUNT) == 0) || ((m_next_page_addr + ((COUNT) *(SIZE)) > PSTORAGE_DATA_END_ADDR)))  \
        {                                                                                         \
            return NRF_ERROR_INVALID_PARAM;                                                       \
        }

/**@brief Verifies size parameter provided by application in API. */
#define SIZE_CHECK(ID, SIZE)                                                                      \
        if(((SIZE) == 0) || ((SIZE) > MODULE_BLOCK_SIZE(ID)))                                     \
        {                                                                                         \
            return NRF_ERROR_INVALID_PARAM;                                                       \
        }

/**@brief Verifies offset parameter provided by application in API. */
#define OFFSET_CHECK(ID, OFFSET, SIZE)                                                              \
        if(((SIZE) + (OFFSET)) > MODULE_BLOCK_SIZE(ID))                                           \
        {                                                                                         \
            return NRF_ERROR_INVALID_PARAM;                                                       \
        }

#ifdef PSTORAGE_RAW_MODE_ENABLE

/**@brief Verifies the module identifier supplied by the application is registered for raw mode.
 */
#define MODULE_RAW_ID_RANGE_CHECK(ID)                                                             \
        if ((PSTORAGE_MAX_APPLICATIONS+1 != ((ID)->module_id)) ||                                 \
            (m_raw_app_table.cb == NULL))                                                         \
        {                                                                                         \
            return NRF_ERROR_INVALID_PARAM;                                                       \
        }

#endif // PSTORAGE_RAW_MODE_ENABLE

/**@} */

 /**@brief    Verify module's initialization status.
 *
 * @details   Verify module's initialization status. Returns NRF_ERROR_INVALID_STATE in case a
 *            module API is called without initializing the module.
 */
#define VERIFY_MODULE_INITIALIZED()                                                               \
        do                                                                                        \
        {                                                                                         \
            if (!m_module_initialized)                                                            \
            {                                                                                     \
                 return NRF_ERROR_INVALID_STATE;                                                  \
            }                                                                                     \
        } while(0)

/**@brief Macro to fetch the block size registered for the module. */
#define MODULE_BLOCK_SIZE(ID) (m_app_table[(ID)->module_id].block_size)

/**@} */

/**
 * @brief Application registration information.
 *
 * @details Define application specific information that application needs to maintain to be able
 *          to process requests from each one of them.
 */
typedef struct ps_module_table
{
    pstorage_ntf_cb_t      cb;             /**< Callback registered with the module to be notified of result of flash access.  */
    pstorage_block_t       base_id;        /**< Base block id assigned to the module */
    uint32_t               block_size;     /**< Size of block for the module */
    uint16_t               block_count;    /**< Number of block requested by application */
    uint16_t               no_of_pages;    /**< Variable to remember how many pages have been allocated for this module. This information is used for clearing of block, so that application does not need to have knowledge of number of pages its using. */
}pstorage_module_table_t;

#ifdef PSTORAGE_RAW_MODE_ENABLE
/**
 * @brief Application registration information.
 *
 * @details Define application specific information that application registered for raw mode.
 */
typedef struct ps_raw_module_table
{
    pstorage_ntf_cb_t      cb;             /**< Callback registered with the module to be notified of result of flash access.  */
    uint16_t               no_of_pages;    /**< Variable to remember how many pages have been allocated for this module. This information is used for clearing of block, so that application does not need to have knowledge of number of pages its using. */
}pstorage_raw_module_table_t;
#endif // PSTORAGE_RAW_MODE_ENABLE


//
// Utility functions needed for interfacing with flash through SoC APIs.
// SoC APIs are non blocking and provide the result of flash access through an event.
// Plus, only one flash access operation is permitted at a time. Current design of most SDK modules
// assumes a blocking interface for the flash. In order to ensure no APIs of bond manager are
// altered this interface, queues any flash access if need be for bond manager and notifies the
// bond manager in case of any failure. In case there are no failure, bond manager can assume the
// operation succeeded. It should also be noted that in order to avoid copy of data, and achieve
// optimizations in flash access, only the latest changes written to flash. Therefore, if the
// contents of pointed by the source address changes, the changed content will be written to flash.
// Queued access operations will be scheduled in FIFO order.
//
//

/**
 * @brief Defines command queue element.
 *
 * @details Defines command queue element. Each element encapsulates needed information to process
 *          a flash access command.
 */
typedef struct cmd_element
{
    uint8_t              op_code;          /**< Identifies flash access operation being queued. Element is free is op-code is INVALID_OPCODE */
    uint32_t             size;             /**< Identifies size in bytes requested for the operation. */
    pstorage_handle_t    storage_addr;     /**< Address/Identifier for persistent memory. */
    uint8_t              * p_data_addr;    /**< Address/Identifier for data memory. This is assumed to be resident memory. */
    uint32_t             offset;           /**< Offset requested by the application for access operation. */
    uint32_t             round_val;        /**< Round value for multiple round operations. For erase operations, the round value will contain current round counter which is identical to number of pages erased. For store operations, the round value contains current round of operation * SOC_MAX_WRITE_SIZE to ensure each store to the SoC Flash API is within the SoC limit. */
}cmd_queue_element_t;


/**
 * @brief Defines command queue, an element is free is op_code field is not invalid.
 *
 * @details Defines commands enqueued for flash access. At any point of time, this queue has one or
 *          more flash access operation pending if the count field is not zero. When the queue is
 *          not empty, the rp (read pointer) field points to the flash access in progress or to
 *          requested next. The queue implements a simple first in first out algorithm.
 *          Data addresses are assumed to be resident.
 */
typedef struct queue
{
    uint8_t              rp;                     /**< Read pointer, pointing to flash access that is ongoing or to be requested next. */
    uint8_t              count;                  /**< Number of elements in the queue.  */
    bool                 flash_access;           /**< Flag to ensure an flash event received is for an request issued by the module. */
    cmd_queue_element_t  cmd[CMD_QUEUE_SIZE];    /**< Array where flash access operation details are maintained */

}cmd_queue_t;

static cmd_queue_t             m_cmd_queue;                               /**< Flash operation request queue. */
static pstorage_module_table_t m_app_table[PSTORAGE_MAX_APPLICATIONS];    /**< Registered application information table. */

#ifdef PSTORAGE_RAW_MODE_ENABLE
static pstorage_raw_module_table_t m_raw_app_table; /**< Registered application information table for raw mode. */
#endif // PSTORAGE_RAW_MODE_ENABLE

static uint32_t m_next_app_instance;                                      /**< Points to the application module instance that can be allocated next */
static uint32_t m_next_page_addr;                                         /**< Points to the flash address that can be allocated to a module next, this is needed as blocks of a module can span across flash pages. */
static bool     m_module_initialized = false;                             /**< Flag for checking if module has been initialized. */

static uint32_t process_cmd(void);
static void app_notify (uint32_t reason);

//Initializes command queue element.
static void cmd_queue_init_element(uint32_t index)
{
    // Internal function and checks on range of index can be avoided
    m_cmd_queue.cmd[index].op_code                = INVALID_OPCODE;
    m_cmd_queue.cmd[index].size                   = 0;
    m_cmd_queue.cmd[index].storage_addr.module_id = PSTORAGE_MAX_APPLICATIONS;
    m_cmd_queue.cmd[index].storage_addr.block_id  = 0;
    m_cmd_queue.cmd[index].p_data_addr            = NULL;
    m_cmd_queue.cmd[index].offset                 = 0;
    m_cmd_queue.cmd[index].round_val              = 0;
}


// Initializes command queue.
static void cmd_queue_init (void)
{
    uint32_t cmd_index;

    m_cmd_queue.rp = 0;
    m_cmd_queue.count = 0;
    m_cmd_queue.flash_access = false;

    for(cmd_index = 0; cmd_index < CMD_QUEUE_SIZE; cmd_index++)
    {
        cmd_queue_init_element(cmd_index);
    }
}


// Routine to enqueue a flash access operation.
static uint32_t cmd_queue_enqueue(uint8_t opcode, pstorage_handle_t * p_storage_addr,uint8_t * p_data_addr, uint32_t size, uint32_t offset)
{
    uint32_t retval;
    uint8_t  write_index = 0;


    // If no flash access operation is ongoing, schedule the operation now!
    // Else, needs to be enqueued.
    //    For Store/Write Operation, is something is already enqueued for same source,
    //    destination address, do nothing, wait for the queued write to be executed.
    //
    //    For Erase, any queued write operations in the range of dest address to dest address+size need not
    //    to be executed, hence flush them out as  information is going to be erased in any case.
    retval = NRF_ERROR_NO_MEM;

    if ((m_cmd_queue.flash_access == false) && (m_cmd_queue.count == 0))
    {
        m_cmd_queue.rp = 0;
        m_cmd_queue.cmd[m_cmd_queue.rp].op_code      = opcode;
        m_cmd_queue.cmd[m_cmd_queue.rp].p_data_addr  = p_data_addr;
        m_cmd_queue.cmd[m_cmd_queue.rp].storage_addr = (*p_storage_addr);
        m_cmd_queue.cmd[m_cmd_queue.rp].size         = size;
        m_cmd_queue.cmd[m_cmd_queue.rp].offset       = offset;
        m_cmd_queue.cmd[m_cmd_queue.rp].round_val    = 0;
        m_cmd_queue.count++;
        retval = process_cmd();
        if ((retval == NRF_SUCCESS) || (retval == NRF_ERROR_BUSY))
        {
            // In case of busy error code, it is possible to attempt to access flash
            retval = NRF_SUCCESS;
        }
    }
    else if (CMD_QUEUE_SIZE != m_cmd_queue.count)
    {
        // TODO: Added intelligence to not enqueue write commands with same source and
        // destination, or in case a clear is issued in an block range, remove all requested
        // writes

        // Enqueue the command if it is queue is not full
        write_index = m_cmd_queue.rp + m_cmd_queue.count;

        if (write_index >= CMD_QUEUE_SIZE)
        {
            write_index -= CMD_QUEUE_SIZE;
        }

        m_cmd_queue.cmd[write_index].op_code      = opcode;
        m_cmd_queue.cmd[write_index].p_data_addr  = p_data_addr;
        m_cmd_queue.cmd[write_index].storage_addr = (*p_storage_addr);
        m_cmd_queue.cmd[write_index].size         = size;
        m_cmd_queue.cmd[write_index].offset       = offset;
        m_cmd_queue.cmd[write_index].round_val    = 0;
        m_cmd_queue.count++;

        retval = NRF_SUCCESS;

    }

    return retval;
}


// Dequeues a command element.
static uint32_t cmd_queue_dequeue(void)
{
    uint32_t retval;    

    cmd_queue_element_t * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp];
    // Update count and read pointer to process any queued requests
    if(p_cmd->round_val >= p_cmd->size)
    {
        // Initialize/free the element as it is now processed.
        cmd_queue_init_element(m_cmd_queue.rp);

        m_cmd_queue.count--;
        m_cmd_queue.rp++;
    }

    retval = NRF_SUCCESS;

    // If any flash operation is enqueued, schedule
    if (m_cmd_queue.count)
    {
        retval = process_cmd();

        if (retval != NRF_SUCCESS)
        {
            // Flash could be accessed by modules other than Bond Manager, hence a busy error is
            // acceptable, but any other error needs to be indicated to the bond manager
            if (retval != NRF_ERROR_BUSY)
            {
                app_notify (retval);
            }
            else
            {
                // In case of busy next trigger will be a success or a failure event
            }
        }
    }
    else
    {
        // No flash access request pending
    }

    return retval;
}


// Routine to notify application of any errors.
static void app_notify (uint32_t result)
{
    pstorage_ntf_cb_t  ntf_cb;
    uint8_t            op_code = m_cmd_queue.cmd[m_cmd_queue.rp].op_code;
    
#ifdef PSTORAGE_RAW_MODE_ENABLE
    if(m_cmd_queue.cmd[m_cmd_queue.rp].storage_addr.module_id == (PSTORAGE_MAX_APPLICATIONS + 1))
    {
        ntf_cb = m_raw_app_table.cb;
    }
    else
#endif // PSTORAGE_RAW_MODE_ENABLE
    {
        ntf_cb = m_app_table[m_cmd_queue.cmd[m_cmd_queue.rp].storage_addr.module_id].cb;
    }

    // Indicate result to client.
    // For PSTORAGE_CLEAR_OP_CODE no size is returned as the size field is used only internally 
    // for clients registering multiple pages.
    ntf_cb(&m_cmd_queue.cmd[m_cmd_queue.rp].storage_addr,
           op_code,
           result,
           m_cmd_queue.cmd[m_cmd_queue.rp].p_data_addr,
           op_code == PSTORAGE_CLEAR_OP_CODE ? 0 : m_cmd_queue.cmd[m_cmd_queue.rp].size);
}


// Handles Flash Access Result Events.
void pstorage_sys_event_handler (uint32_t sys_evt)
{
    uint32_t retval;

    retval = NRF_SUCCESS;

    // Its possible the flash access was not initiated by bond manager, hence
    // event is processed only if the event triggered was for an operation requested by the
    // bond manager.
    if (m_cmd_queue.flash_access == true)
    {
        cmd_queue_element_t * p_cmd;
        m_cmd_queue.flash_access = false;
        switch (sys_evt)
        {
            case NRF_EVT_FLASH_OPERATION_SUCCESS:
                p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp];
                if ((p_cmd->op_code != PSTORAGE_CLEAR_OP_CODE) || (p_cmd->round_val >= p_cmd->size))
                {
                    app_notify(retval);
                }
                // Schedule any queued flash access operations
                retval = cmd_queue_dequeue ();
                if (retval != NRF_SUCCESS)
                {
                    app_notify(retval);
                }

                break;
            case NRF_EVT_FLASH_OPERATION_ERROR:
                app_notify(NRF_ERROR_TIMEOUT);
                break;
            default:
                // No implementation needed.
                break;
        }
    }
}


// Routine called to actually issue the flash access request to the SoftDevice.
static uint32_t process_cmd(void)
{
    uint32_t             retval;
    uint32_t             storage_addr;
    cmd_queue_element_t * p_cmd;

    retval = NRF_ERROR_FORBIDDEN;

    p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp];

    storage_addr = p_cmd->storage_addr.block_id;

    if (p_cmd->op_code == PSTORAGE_CLEAR_OP_CODE)
    {
        // Calculate page number before copy.
        uint32_t page_number;

        page_number =  ((storage_addr / PSTORAGE_FLASH_PAGE_SIZE) +
                        p_cmd->round_val);

        retval = sd_flash_page_erase(page_number);

        if (NRF_SUCCESS == retval)
        {
            p_cmd->round_val++;
        }
    }
    else if (p_cmd->op_code == PSTORAGE_STORE_OP_CODE)
    {
        uint32_t size;
        uint8_t * p_data_addr = p_cmd->p_data_addr;
    
        p_data_addr += p_cmd->round_val;
        
        storage_addr += (p_cmd->offset + p_cmd->round_val);
        
        size = p_cmd->size - p_cmd->round_val;
        
        if (size < SOC_MAX_WRITE_SIZE)
        {
            retval = sd_flash_write(((uint32_t *)storage_addr),
                                     (uint32_t *)p_data_addr,
                                     size / sizeof(uint32_t));
        }
        else
        {
            retval = sd_flash_write(((uint32_t *)storage_addr),
                                     (uint32_t *)p_data_addr,
                                     SOC_MAX_WRITE_SIZE / sizeof(uint32_t));
        }
        

        if (retval == NRF_SUCCESS)
        {
            p_cmd->round_val += SOC_MAX_WRITE_SIZE;
        }
    }
    else
    {
        // Should never reach here.
    }
    
    if (retval == NRF_SUCCESS)
    {
       m_cmd_queue.flash_access = true;
    }

    return retval;
}


// Module initialization routine to be called once by the application.
uint32_t pstorage_init(void)
{
    cmd_queue_init();
    m_next_app_instance = 0;
    m_next_page_addr = PSTORAGE_DATA_START_ADDR;
    m_module_initialized = true;

    for(unsigned int index = 0; index < PSTORAGE_MAX_APPLICATIONS; index++)
    {
        m_app_table[index].cb = NULL;
        m_app_table[index].block_size = 0;
        m_app_table[index].no_of_pages = 0;
        m_app_table[index].block_count = 0;
    }

#ifdef PSTORAGE_RAW_MODE_ENABLE
    m_raw_app_table.cb = NULL;
    m_raw_app_table.no_of_pages = 0;
#endif //PSTORAGE_RAW_MODE_ENABLE
    return NRF_SUCCESS;
}


// Registration routine to request persistent memory of certain sizes based on
// application module requirements.
uint32_t pstorage_register(pstorage_module_param_t * p_module_param,
                           pstorage_handle_t       * p_block_id)
{
    uint16_t page_count;
    uint32_t total_size;

    VERIFY_MODULE_INITIALIZED();
    NULL_PARAM_CHECK(p_module_param);
    NULL_PARAM_CHECK(p_block_id);
    NULL_PARAM_CHECK(p_module_param->cb);
    BLOCK_SIZE_CHECK(p_module_param->block_size);
    BLOCK_COUNT_CHECK(p_module_param->block_count, p_module_param->block_size);

    if (m_next_app_instance == PSTORAGE_MAX_APPLICATIONS)
    {
        return NRF_ERROR_NO_MEM;
    }

    p_block_id->module_id = m_next_app_instance;
    p_block_id->block_id = m_next_page_addr;
    m_app_table[m_next_app_instance].base_id = p_block_id->block_id;
    m_app_table[m_next_app_instance].cb = p_module_param->cb;
    m_app_table[m_next_app_instance].block_size = p_module_param->block_size;
    m_app_table[m_next_app_instance].block_count = p_module_param->block_count;

    // Calculate number of flash pages allocated for the device.
    page_count = 0;
    total_size = p_module_param->block_size * p_module_param->block_count;
    do
    {
        page_count++;
        if (total_size > PSTORAGE_FLASH_PAGE_SIZE)
        {
            total_size -= PSTORAGE_FLASH_PAGE_SIZE;
        }
        else
        {
            total_size = 0;
        }
        m_next_page_addr += PSTORAGE_FLASH_PAGE_SIZE;
    }while(total_size >= PSTORAGE_FLASH_PAGE_SIZE);

    m_app_table[m_next_app_instance].no_of_pages = page_count;
    m_next_app_instance++;

    return NRF_SUCCESS;
}


// API to get the next block identifier.
uint32_t pstorage_block_identifier_get(pstorage_handle_t * p_base_id,
                                       uint32_t            block_num,
                                       pstorage_handle_t * p_block_id)
{
    pstorage_handle_t temp_id;

    VERIFY_MODULE_INITIALIZED();
    NULL_PARAM_CHECK(p_base_id);
    NULL_PARAM_CHECK(p_block_id);
    MODULE_ID_RANGE_CHECK(p_base_id);

    temp_id = (*p_base_id);
    temp_id.block_id += (block_num * MODULE_BLOCK_SIZE(p_base_id));
    BLOCK_ID_RANGE_CHECK(&temp_id);
    (*p_block_id) = temp_id;

    return NRF_SUCCESS;
}


// API to store data persistently.
uint32_t pstorage_store(pstorage_handle_t * p_dest,
                        uint8_t           * p_src,
                        uint32_t            size,
                        uint32_t            offset)
{
    VERIFY_MODULE_INITIALIZED();
    NULL_PARAM_CHECK(p_src);
    NULL_PARAM_CHECK(p_dest);
    MODULE_ID_RANGE_CHECK (p_dest);
    BLOCK_ID_RANGE_CHECK(p_dest);
    SIZE_CHECK(p_dest,size);
    OFFSET_CHECK(p_dest,offset,size);

    // Verify word alignment.
    if ((!is_word_aligned(p_src)) || (!is_word_aligned(p_src+offset)))
    {
        return NRF_ERROR_INVALID_ADDR;
    }

    return cmd_queue_enqueue(PSTORAGE_STORE_OP_CODE, p_dest, p_src, size, offset);
}


// API to load data from persistent memory.
uint32_t pstorage_load(uint8_t           * p_dest,
                       pstorage_handle_t * p_src,
                       uint32_t            size,
                       uint32_t            offset)
{
    VERIFY_MODULE_INITIALIZED();
    NULL_PARAM_CHECK(p_src);
    NULL_PARAM_CHECK(p_dest);
    MODULE_ID_RANGE_CHECK (p_src);
    BLOCK_ID_RANGE_CHECK(p_src);
    SIZE_CHECK(p_src,size);
    OFFSET_CHECK(p_src,offset,size);

    // Verify word alignment.
    if ((!is_word_aligned (p_dest)) || (!is_word_aligned (p_dest + offset)))
    {
        return NRF_ERROR_INVALID_ADDR;
    }

    memcpy (p_dest, (((uint8_t *)p_src->block_id) + offset), size);

    return NRF_SUCCESS;
}


// API to clear data in blocks of persistent memory.
uint32_t pstorage_clear(pstorage_handle_t * p_dest, uint32_t size)
{
    uint32_t retval;
    uint32_t pages;

    VERIFY_MODULE_INITIALIZED();
    NULL_PARAM_CHECK(p_dest);
    MODULE_ID_RANGE_CHECK(p_dest);
    BLOCK_ID_RANGE_CHECK(p_dest);

    pages = m_app_table[p_dest->module_id].no_of_pages;

    retval = cmd_queue_enqueue(PSTORAGE_CLEAR_OP_CODE, p_dest, NULL , pages , 0);

    return retval;
}


#ifdef PSTORAGE_RAW_MODE_ENABLE

// Registration routine to request persistent memory of certain sizes based on 
// application module requirements.
uint32_t pstorage_raw_register(pstorage_module_param_t * p_module_param,
                               pstorage_handle_t       * p_block_id)
{
    VERIFY_MODULE_INITIALIZED();
    NULL_PARAM_CHECK(p_module_param);
    NULL_PARAM_CHECK(p_block_id);
    NULL_PARAM_CHECK(p_module_param->cb);

    if (m_raw_app_table.cb != NULL)
    {
        return NRF_ERROR_NO_MEM;
    }

    p_block_id->module_id = PSTORAGE_MAX_APPLICATIONS + 1;
    m_raw_app_table.cb    = p_module_param->cb;

    return NRF_SUCCESS;
}

// API to store data persistently.
uint32_t pstorage_raw_store(pstorage_handle_t * p_dest,
                            uint8_t           * p_src,
                            uint32_t            size,
                            uint32_t            offset)
{
    VERIFY_MODULE_INITIALIZED();
    NULL_PARAM_CHECK(p_src);
    NULL_PARAM_CHECK(p_dest);
    MODULE_RAW_ID_RANGE_CHECK(p_dest);

    // Verify word alignment.
    if ((!is_word_aligned(p_src)) || (!is_word_aligned(p_src+offset)))
    {
        return NRF_ERROR_INVALID_ADDR;
    }

    return cmd_queue_enqueue(PSTORAGE_STORE_OP_CODE, p_dest, p_src, size, offset);
}

// API to clear data in blocks of persistent memory.
uint32_t pstorage_raw_clear(pstorage_handle_t * p_dest, uint32_t size)
{
    uint32_t retval;
    uint32_t pages;

    VERIFY_MODULE_INITIALIZED();
    NULL_PARAM_CHECK(p_dest);
    MODULE_RAW_ID_RANGE_CHECK(p_dest);

    retval = NRF_SUCCESS;

    pages = CEIL_DIV(size, PSTORAGE_FLASH_PAGE_SIZE);
    
    retval = cmd_queue_enqueue(PSTORAGE_CLEAR_OP_CODE, p_dest, NULL , pages, 0);

    return retval;
}
#endif // PSTORAGE_RAW_MODE_ENABLE
