mirror of https://github.com/ARMmbed/mbed-os.git
Flash storage for NRF52
Add fstorage and fds from Nordic SDK for SoftDevice compatible flash storage. Mbed HAL flash API mapped to use fstorage API to ensure write and erase doesn't conflict with BLE operation.pull/6547/head
parent
2e7b5ba27b
commit
3b9ec4932c
|
@ -3406,7 +3406,7 @@
|
||||||
// <e> FDS_ENABLED - fds - Flash data storage module
|
// <e> FDS_ENABLED - fds - Flash data storage module
|
||||||
//==========================================================
|
//==========================================================
|
||||||
#ifndef FDS_ENABLED
|
#ifndef FDS_ENABLED
|
||||||
#define FDS_ENABLED 0
|
#define FDS_ENABLED 1
|
||||||
#endif
|
#endif
|
||||||
// <h> Pages - Virtual page settings
|
// <h> Pages - Virtual page settings
|
||||||
|
|
||||||
|
@ -3450,7 +3450,7 @@
|
||||||
// <2=> NRF_FSTORAGE_SD
|
// <2=> NRF_FSTORAGE_SD
|
||||||
|
|
||||||
#ifndef FDS_BACKEND
|
#ifndef FDS_BACKEND
|
||||||
#define FDS_BACKEND 2
|
#define FDS_BACKEND 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// </h>
|
// </h>
|
||||||
|
@ -3885,7 +3885,7 @@
|
||||||
// <e> NRF_FSTORAGE_ENABLED - nrf_fstorage - Flash abstraction library
|
// <e> NRF_FSTORAGE_ENABLED - nrf_fstorage - Flash abstraction library
|
||||||
//==========================================================
|
//==========================================================
|
||||||
#ifndef NRF_FSTORAGE_ENABLED
|
#ifndef NRF_FSTORAGE_ENABLED
|
||||||
#define NRF_FSTORAGE_ENABLED 0
|
#define NRF_FSTORAGE_ENABLED 1
|
||||||
#endif
|
#endif
|
||||||
// <h> nrf_fstorage_sd - Implementation using the SoftDevice.
|
// <h> nrf_fstorage_sd - Implementation using the SoftDevice.
|
||||||
|
|
||||||
|
|
|
@ -3406,7 +3406,7 @@
|
||||||
// <e> FDS_ENABLED - fds - Flash data storage module
|
// <e> FDS_ENABLED - fds - Flash data storage module
|
||||||
//==========================================================
|
//==========================================================
|
||||||
#ifndef FDS_ENABLED
|
#ifndef FDS_ENABLED
|
||||||
#define FDS_ENABLED 0
|
#define FDS_ENABLED 1
|
||||||
#endif
|
#endif
|
||||||
// <h> Pages - Virtual page settings
|
// <h> Pages - Virtual page settings
|
||||||
|
|
||||||
|
@ -3450,7 +3450,7 @@
|
||||||
// <2=> NRF_FSTORAGE_SD
|
// <2=> NRF_FSTORAGE_SD
|
||||||
|
|
||||||
#ifndef FDS_BACKEND
|
#ifndef FDS_BACKEND
|
||||||
#define FDS_BACKEND 2
|
#define FDS_BACKEND 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// </h>
|
// </h>
|
||||||
|
@ -3885,7 +3885,7 @@
|
||||||
// <e> NRF_FSTORAGE_ENABLED - nrf_fstorage - Flash abstraction library
|
// <e> NRF_FSTORAGE_ENABLED - nrf_fstorage - Flash abstraction library
|
||||||
//==========================================================
|
//==========================================================
|
||||||
#ifndef NRF_FSTORAGE_ENABLED
|
#ifndef NRF_FSTORAGE_ENABLED
|
||||||
#define NRF_FSTORAGE_ENABLED 0
|
#define NRF_FSTORAGE_ENABLED 1
|
||||||
#endif
|
#endif
|
||||||
// <h> nrf_fstorage_sd - Implementation using the SoftDevice.
|
// <h> nrf_fstorage_sd - Implementation using the SoftDevice.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# Folder origin
|
# Folder origin
|
||||||
|
|
||||||
components/softdevice
|
components/softdevice
|
||||||
|
components/libraries/fstorage/nrf_fstorage_sd.*
|
||||||
|
|
||||||
# Modifications
|
# Modifications
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,616 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2016 - 2017, Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||||
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||||
|
* such product, must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or other
|
||||||
|
* materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* 4. This software, with or without modification, must only be used with a
|
||||||
|
* Nordic Semiconductor ASA integrated circuit.
|
||||||
|
*
|
||||||
|
* 5. Any software provided in binary form under this license must not be reverse
|
||||||
|
* engineered, decompiled, modified and/or disassembled.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||||
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sdk_common.h"
|
||||||
|
|
||||||
|
#if NRF_MODULE_ENABLED(NRF_FSTORAGE)
|
||||||
|
|
||||||
|
#include "nrf_fstorage_sd.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "nordic_common.h"
|
||||||
|
#include "nrf_soc.h"
|
||||||
|
#include "nrf_sdh.h"
|
||||||
|
#include "nrf_sdh_soc.h"
|
||||||
|
#include "nrf_atomic.h"
|
||||||
|
#include "nrf_atfifo.h"
|
||||||
|
#include "app_util_platform.h"
|
||||||
|
|
||||||
|
|
||||||
|
#if (NRF_FSTORAGE_SD_MAX_WRITE_SIZE % 4)
|
||||||
|
#error NRF_FSTORAGE_SD_MAX_WRITE_SIZE must be a multiple of the word size.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief fstorage operation codes. */
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
NRF_FSTORAGE_OP_WRITE, //!< Write bytes to flash.
|
||||||
|
NRF_FSTORAGE_OP_ERASE //!< Erase flash pages.
|
||||||
|
} nrf_fstorage_sd_opcode_t;
|
||||||
|
|
||||||
|
ANON_UNIONS_ENABLE
|
||||||
|
/**@brief fstorage operation queue element. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
nrf_fstorage_t const * p_fs; //!< The fstorage instance that requested the operation.
|
||||||
|
nrf_fstorage_sd_opcode_t op_code; //!< Requested operation.
|
||||||
|
void * p_param; //!< User-defined parameter passed to the event handler.
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
void const * p_src; //!< Data to be written to flash.
|
||||||
|
uint32_t dest; //!< Destination of the data in flash.
|
||||||
|
uint32_t len; //!< Length of the data to be written (in bytes).
|
||||||
|
uint32_t offset; //!< Write offset.
|
||||||
|
} write;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint32_t page; //!< Physical page number.
|
||||||
|
uint32_t progress; //!< Number of pages erased.
|
||||||
|
uint32_t pages_to_erase; //!< Total number of pages to erase.
|
||||||
|
} erase;
|
||||||
|
};
|
||||||
|
} nrf_fstorage_sd_op_t;
|
||||||
|
ANON_UNIONS_DISABLE
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
NRF_FSTORAGE_STATE_IDLE, //!< No operations requested to the SoftDevice.
|
||||||
|
NRF_FSTORAGE_STATE_OP_PENDING, //!< A non-fstorage operation is pending.
|
||||||
|
NRF_FSTORAGE_STATE_OP_EXECUTING, //!< An fstorage operation is executing.
|
||||||
|
} nrf_fstorage_sd_state_t;
|
||||||
|
|
||||||
|
/**@brief Internal state. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
nrf_atomic_flag_t initialized; //!< fstorage is initalized.
|
||||||
|
nrf_atomic_flag_t queue_running; //!< The queue is running.
|
||||||
|
/** Prevent API calls from entering queue_process(). */
|
||||||
|
nrf_fstorage_sd_state_t state; //!< Internal fstorage state.
|
||||||
|
uint32_t retries; //!< Number of times an operation has been retried on timeout.
|
||||||
|
bool sd_enabled; //!< The SoftDevice is enabled.
|
||||||
|
bool paused; //!< A SoftDevice state change is impending.
|
||||||
|
/** Do not load a new operation when the last one completes. */
|
||||||
|
} nrf_fstorage_sd_work_t;
|
||||||
|
|
||||||
|
|
||||||
|
void nrf_fstorage_sys_evt_handler(uint32_t, void *);
|
||||||
|
bool nrf_fstorage_sdh_req_handler(nrf_sdh_req_evt_t, void *);
|
||||||
|
void nrf_fstorage_sdh_state_handler(nrf_sdh_state_evt_t, void *);
|
||||||
|
|
||||||
|
|
||||||
|
/* Flash device information. */
|
||||||
|
static nrf_fstorage_info_t m_flash_info =
|
||||||
|
{
|
||||||
|
#if defined(NRF51)
|
||||||
|
.erase_unit = 1024,
|
||||||
|
#elif defined(NRF52_SERIES)
|
||||||
|
.erase_unit = 4096,
|
||||||
|
#endif
|
||||||
|
.program_unit = 4,
|
||||||
|
.rmap = true,
|
||||||
|
.wmap = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Queue of fstorage operations. */
|
||||||
|
NRF_ATFIFO_DEF(m_fifo, nrf_fstorage_sd_op_t, NRF_FSTORAGE_SD_QUEUE_SIZE);
|
||||||
|
|
||||||
|
/* Define a nrf_sdh_soc event observer to receive SoftDevice system events. */
|
||||||
|
NRF_SDH_SOC_OBSERVER(m_sys_obs, 0, nrf_fstorage_sys_evt_handler, NULL);
|
||||||
|
|
||||||
|
/* nrf_sdh request observer. */
|
||||||
|
NRF_SDH_REQUEST_OBSERVER(m_req_obs, 0) =
|
||||||
|
{
|
||||||
|
.handler = nrf_fstorage_sdh_req_handler,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* nrf_sdh state observer. */
|
||||||
|
NRF_SDH_STATE_OBSERVER(m_state_obs, 0) =
|
||||||
|
{
|
||||||
|
.handler = nrf_fstorage_sdh_state_handler,
|
||||||
|
};
|
||||||
|
|
||||||
|
static nrf_fstorage_sd_work_t m_flags; /* Internal status. */
|
||||||
|
static nrf_fstorage_sd_op_t * m_p_cur_op; /* The current operation being executed. */
|
||||||
|
static nrf_atfifo_item_get_t m_iget_ctx; /* Context for nrf_atfifo_item_get() and nrf_atfifo_item_free(). */
|
||||||
|
|
||||||
|
|
||||||
|
/* Send events to the application. */
|
||||||
|
static void event_send(nrf_fstorage_sd_op_t const * p_op, ret_code_t result)
|
||||||
|
{
|
||||||
|
if (p_op->p_fs->evt_handler == NULL)
|
||||||
|
{
|
||||||
|
/* Nothing to do. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrf_fstorage_evt_t evt =
|
||||||
|
{
|
||||||
|
.result = result,
|
||||||
|
.p_param = p_op->p_param,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (p_op->op_code)
|
||||||
|
{
|
||||||
|
case NRF_FSTORAGE_OP_WRITE:
|
||||||
|
evt.id = NRF_FSTORAGE_EVT_WRITE_RESULT;
|
||||||
|
evt.addr = p_op->write.dest;
|
||||||
|
evt.len = p_op->write.len;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NRF_FSTORAGE_OP_ERASE:
|
||||||
|
evt.id = NRF_FSTORAGE_EVT_ERASE_RESULT;
|
||||||
|
evt.addr = (p_op->erase.page * m_flash_info.erase_unit);
|
||||||
|
evt.len = p_op->erase.pages_to_erase;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* Should not happen. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_op->p_fs->evt_handler(&evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Write to flash. */
|
||||||
|
static uint32_t write_execute(nrf_fstorage_sd_op_t const * p_op)
|
||||||
|
{
|
||||||
|
uint32_t chunk_len;
|
||||||
|
|
||||||
|
chunk_len = MIN(p_op->write.len - p_op->write.offset, NRF_FSTORAGE_SD_MAX_WRITE_SIZE);
|
||||||
|
chunk_len = MAX(1, chunk_len / m_flash_info.program_unit);
|
||||||
|
|
||||||
|
/* Cast to p_src to uint32_t to perform arithmetic. */
|
||||||
|
uint32_t * p_dest = (uint32_t*)(p_op->write.dest + p_op->write.offset);
|
||||||
|
uint32_t const * p_src = (uint32_t*)((uint32_t)p_op->write.p_src + p_op->write.offset);
|
||||||
|
|
||||||
|
return sd_flash_write(p_dest, p_src, chunk_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Erase flash page(s). */
|
||||||
|
static uint32_t erase_execute(nrf_fstorage_sd_op_t const * p_op)
|
||||||
|
{
|
||||||
|
return sd_flash_page_erase(p_op->erase.page + p_op->erase.progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Free the current queue element. */
|
||||||
|
static void queue_free(void)
|
||||||
|
{
|
||||||
|
(void) nrf_atfifo_item_free(m_fifo, &m_iget_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Load a new operation from the queue. */
|
||||||
|
static bool queue_load_next(void)
|
||||||
|
{
|
||||||
|
m_p_cur_op = nrf_atfifo_item_get(m_fifo, &m_iget_ctx);
|
||||||
|
|
||||||
|
return (m_p_cur_op != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Execute an operation in the queue. */
|
||||||
|
static void queue_process(void)
|
||||||
|
{
|
||||||
|
if (m_flags.state == NRF_FSTORAGE_STATE_IDLE)
|
||||||
|
{
|
||||||
|
if (!queue_load_next())
|
||||||
|
{
|
||||||
|
/* No more operations, nothing to do. */
|
||||||
|
m_flags.queue_running = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_flags.state = NRF_FSTORAGE_STATE_OP_EXECUTING;
|
||||||
|
|
||||||
|
uint32_t rc;
|
||||||
|
switch (m_p_cur_op->op_code)
|
||||||
|
{
|
||||||
|
case NRF_FSTORAGE_OP_WRITE:
|
||||||
|
rc = write_execute(m_p_cur_op);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NRF_FSTORAGE_OP_ERASE:
|
||||||
|
rc = erase_execute(m_p_cur_op);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
rc = NRF_ERROR_INTERNAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (rc)
|
||||||
|
{
|
||||||
|
case NRF_SUCCESS:
|
||||||
|
{
|
||||||
|
/* The operation was accepted by the SoftDevice.
|
||||||
|
* If the SoftDevice is enabled, wait for a system event. Otherwise,
|
||||||
|
* the SoftDevice call is synchronous and will not send an event so we simulate it. */
|
||||||
|
if (!m_flags.sd_enabled)
|
||||||
|
{
|
||||||
|
nrf_fstorage_sys_evt_handler(NRF_EVT_FLASH_OPERATION_SUCCESS, NULL);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NRF_ERROR_BUSY:
|
||||||
|
{
|
||||||
|
/* The SoftDevice is executing a flash operation that was not requested by fstorage.
|
||||||
|
* Stop processing the queue until a system event is received. */
|
||||||
|
m_flags.state = NRF_FSTORAGE_STATE_OP_PENDING;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
/* An error has occurred. We cannot proceed further with this operation.
|
||||||
|
* Reset the internal state so we can accept other operations. */
|
||||||
|
event_send(m_p_cur_op, NRF_ERROR_INTERNAL);
|
||||||
|
m_flags.state = NRF_FSTORAGE_STATE_IDLE;
|
||||||
|
m_flags.queue_running = false;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Start processing the queue if it is not running and fstorage is not paused. */
|
||||||
|
static void queue_start(void)
|
||||||
|
{
|
||||||
|
nrf_atomic_flag_t queue_running = nrf_atomic_u32_fetch_store(&m_flags.queue_running, true);
|
||||||
|
|
||||||
|
if ( (!queue_running)
|
||||||
|
&& (!m_flags.paused))
|
||||||
|
{
|
||||||
|
queue_process();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Flash operation success callback. Keeps track of the progress of an operation. */
|
||||||
|
static bool on_operation_success(nrf_fstorage_sd_op_t * const p_op)
|
||||||
|
{
|
||||||
|
/* Reset the retry counter on success. */
|
||||||
|
m_flags.retries = 0;
|
||||||
|
|
||||||
|
switch (p_op->op_code)
|
||||||
|
{
|
||||||
|
case NRF_FSTORAGE_OP_WRITE:
|
||||||
|
{
|
||||||
|
/* Update the offset only if the operation is successful
|
||||||
|
* so that it can be retried in case it times out. */
|
||||||
|
uint32_t const chunk_len = MIN(p_op->write.len - p_op->write.offset,
|
||||||
|
NRF_FSTORAGE_SD_MAX_WRITE_SIZE);
|
||||||
|
|
||||||
|
p_op->write.offset += chunk_len;
|
||||||
|
|
||||||
|
if (p_op->write.offset == p_op->write.len)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NRF_FSTORAGE_OP_ERASE:
|
||||||
|
{
|
||||||
|
p_op->erase.progress++;
|
||||||
|
|
||||||
|
if (p_op->erase.progress == p_op->erase.pages_to_erase)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* Should not happen. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Flash operation failure callback. */
|
||||||
|
static bool on_operation_failure(nrf_fstorage_sd_op_t const * p_op)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_op);
|
||||||
|
|
||||||
|
m_flags.retries++;
|
||||||
|
|
||||||
|
if (m_flags.retries > NRF_FSTORAGE_SD_MAX_RETRIES)
|
||||||
|
{
|
||||||
|
/* Maximum amount of retries reached. Give up. */
|
||||||
|
m_flags.retries = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ret_code_t init(nrf_fstorage_t * p_fs, void * p_param)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_param);
|
||||||
|
|
||||||
|
if (!nrf_atomic_flag_set_fetch(&m_flags.initialized))
|
||||||
|
{
|
||||||
|
p_fs->p_flash_info = &m_flash_info;
|
||||||
|
|
||||||
|
#if NRF_SDH_ENABLED
|
||||||
|
m_flags.sd_enabled = nrf_sdh_is_enabled();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
(void) NRF_ATFIFO_INIT(m_fifo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ret_code_t uninit(nrf_fstorage_t * p_fs, void * p_param)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_fs);
|
||||||
|
UNUSED_PARAMETER(p_param);
|
||||||
|
|
||||||
|
/* The state is re-initialized upon init().
|
||||||
|
* The common uninitialization code is run by the caller. */
|
||||||
|
|
||||||
|
memset(&m_flags, 0x00, sizeof(m_flags));
|
||||||
|
|
||||||
|
(void) nrf_atfifo_clear(m_fifo);
|
||||||
|
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ret_code_t write(nrf_fstorage_t const * p_fs,
|
||||||
|
uint32_t dest,
|
||||||
|
void const * p_src,
|
||||||
|
uint32_t len,
|
||||||
|
void * p_param)
|
||||||
|
{
|
||||||
|
nrf_fstorage_sd_op_t * p_op;
|
||||||
|
nrf_atfifo_item_put_t iput_ctx;
|
||||||
|
|
||||||
|
/* Get a free queue element. */
|
||||||
|
p_op = nrf_atfifo_item_alloc(m_fifo, &iput_ctx);
|
||||||
|
|
||||||
|
if (p_op == NULL)
|
||||||
|
{
|
||||||
|
return NRF_ERROR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the operation. */
|
||||||
|
memset(p_op, 0x00, sizeof(nrf_fstorage_sd_op_t));
|
||||||
|
|
||||||
|
p_op->op_code = NRF_FSTORAGE_OP_WRITE;
|
||||||
|
p_op->p_fs = p_fs;
|
||||||
|
p_op->p_param = p_param;
|
||||||
|
p_op->write.dest = dest;
|
||||||
|
p_op->write.p_src = p_src;
|
||||||
|
p_op->write.len = len;
|
||||||
|
|
||||||
|
/* Put the operation on the queue. */
|
||||||
|
(void) nrf_atfifo_item_put(m_fifo, &iput_ctx);
|
||||||
|
|
||||||
|
queue_start();
|
||||||
|
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ret_code_t read(nrf_fstorage_t const * p_fs, uint32_t src, void * p_dest, uint32_t len)
|
||||||
|
{
|
||||||
|
memcpy(p_dest, (uint32_t*)src, len);
|
||||||
|
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ret_code_t erase(nrf_fstorage_t const * p_fs,
|
||||||
|
uint32_t page_addr,
|
||||||
|
uint32_t len,
|
||||||
|
void * p_param)
|
||||||
|
{
|
||||||
|
nrf_fstorage_sd_op_t * p_op;
|
||||||
|
nrf_atfifo_item_put_t iput_ctx;
|
||||||
|
|
||||||
|
/* Get a free queue element. */
|
||||||
|
p_op = nrf_atfifo_item_alloc(m_fifo, &iput_ctx);
|
||||||
|
|
||||||
|
if (p_op == NULL)
|
||||||
|
{
|
||||||
|
return NRF_ERROR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the operation. */
|
||||||
|
memset(p_op, 0x00, sizeof(nrf_fstorage_sd_op_t));
|
||||||
|
|
||||||
|
p_op->op_code = NRF_FSTORAGE_OP_ERASE;
|
||||||
|
p_op->p_fs = p_fs;
|
||||||
|
p_op->p_param = p_param;
|
||||||
|
p_op->erase.page = (page_addr / m_flash_info.erase_unit);
|
||||||
|
p_op->erase.pages_to_erase = len;
|
||||||
|
|
||||||
|
/* Put the operation on the queue. */
|
||||||
|
(void) nrf_atfifo_item_put(m_fifo, &iput_ctx);
|
||||||
|
|
||||||
|
queue_start();
|
||||||
|
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t const * rmap(nrf_fstorage_t const * p_fs, uint32_t addr)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_fs);
|
||||||
|
|
||||||
|
return (uint8_t*)addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t * wmap(nrf_fstorage_t const * p_fs, uint32_t addr)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_fs);
|
||||||
|
UNUSED_PARAMETER(addr);
|
||||||
|
|
||||||
|
/* Not supported. */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool is_busy(nrf_fstorage_t const * p_fs)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_fs);
|
||||||
|
|
||||||
|
return (m_flags.state != NRF_FSTORAGE_STATE_IDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void nrf_fstorage_sys_evt_handler(uint32_t sys_evt, void * p_context)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_context);
|
||||||
|
|
||||||
|
switch (m_flags.state)
|
||||||
|
{
|
||||||
|
case NRF_FSTORAGE_STATE_OP_EXECUTING:
|
||||||
|
{
|
||||||
|
/* Handle the result of a flash operation initiated by this module. */
|
||||||
|
bool operation_finished = false;
|
||||||
|
|
||||||
|
switch (sys_evt)
|
||||||
|
{
|
||||||
|
case NRF_EVT_FLASH_OPERATION_SUCCESS:
|
||||||
|
operation_finished = on_operation_success(m_p_cur_op);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NRF_EVT_FLASH_OPERATION_ERROR:
|
||||||
|
operation_finished = on_operation_failure(m_p_cur_op);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation_finished)
|
||||||
|
{
|
||||||
|
/* The operation has finished. Change state to NRF_FSTORAGE_STATE_IDLE
|
||||||
|
* so that queue_process() will fetch a new operation from the queue. */
|
||||||
|
m_flags.state = NRF_FSTORAGE_STATE_IDLE;
|
||||||
|
|
||||||
|
event_send(m_p_cur_op, (sys_evt == NRF_EVT_FLASH_OPERATION_SUCCESS) ?
|
||||||
|
NRF_SUCCESS : NRF_ERROR_TIMEOUT);
|
||||||
|
|
||||||
|
/* Free the queue element after sending out the event to prevent API calls made
|
||||||
|
* in the event context to queue elements indefinitely, without this function
|
||||||
|
* ever returning in case the SoftDevice calls are synchronous. */
|
||||||
|
queue_free();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NRF_FSTORAGE_STATE_OP_PENDING:
|
||||||
|
/* The SoftDevice has completed a flash operation that was not requested by fstorage.
|
||||||
|
* It should be possible to request an operation now.
|
||||||
|
* Process the queue at the end of this function. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* If idle, return. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_flags.paused)
|
||||||
|
{
|
||||||
|
queue_process();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* A flash operation has completed. Let the SoftDevice to change state. */
|
||||||
|
(void) nrf_sdh_request_continue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool nrf_fstorage_sdh_req_handler(nrf_sdh_req_evt_t req, void * p_context)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(req);
|
||||||
|
UNUSED_PARAMETER(p_context);
|
||||||
|
|
||||||
|
m_flags.paused = true;
|
||||||
|
|
||||||
|
/* If there are any operations ongoing, pause the SoftDevice state change. */
|
||||||
|
return (m_flags.state == NRF_FSTORAGE_STATE_IDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void nrf_fstorage_sdh_state_handler(nrf_sdh_state_evt_t state, void * p_context)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_context);
|
||||||
|
|
||||||
|
if ( (state == NRF_SDH_EVT_STATE_ENABLED)
|
||||||
|
|| (state == NRF_SDH_EVT_STATE_DISABLED))
|
||||||
|
{
|
||||||
|
m_flags.paused = false;
|
||||||
|
m_flags.sd_enabled = (state == NRF_SDH_EVT_STATE_ENABLED);
|
||||||
|
|
||||||
|
/* Execute any operations still in the queue. */
|
||||||
|
queue_process();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Exported API implementation. */
|
||||||
|
nrf_fstorage_api_t nrf_fstorage_sd =
|
||||||
|
{
|
||||||
|
.init = init,
|
||||||
|
.uninit = uninit,
|
||||||
|
.read = read,
|
||||||
|
.write = write,
|
||||||
|
.erase = erase,
|
||||||
|
.rmap = rmap,
|
||||||
|
.wmap = wmap,
|
||||||
|
.is_busy = is_busy
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // NRF_FSTORAGE_ENABLED
|
|
@ -0,0 +1,75 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2016 - 2017, Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||||
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||||
|
* such product, must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or other
|
||||||
|
* materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* 4. This software, with or without modification, must only be used with a
|
||||||
|
* Nordic Semiconductor ASA integrated circuit.
|
||||||
|
*
|
||||||
|
* 5. Any software provided in binary form under this license must not be reverse
|
||||||
|
* engineered, decompiled, modified and/or disassembled.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||||
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @defgroup nrf_fstorage_sd SoftDevice implementation
|
||||||
|
* @ingroup nrf_fstorage
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @brief API implementation of fstorage that uses the SoftDevice.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NRF_FSTORAGE_SD_H__
|
||||||
|
#define NRF_FSTORAGE_SD_H__
|
||||||
|
|
||||||
|
#include "nrf_fstorage.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief API implementation that uses the SoftDevice.
|
||||||
|
*
|
||||||
|
* @details An fstorage instance with this API implementation can be initialized by providing
|
||||||
|
* this structure as a parameter to @ref nrf_fstorage_init.
|
||||||
|
* The structure is defined in @c nrf_fstorage_sd.c.
|
||||||
|
*/
|
||||||
|
extern nrf_fstorage_api_t nrf_fstorage_sd;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // NRF_FSTORAGE_SD_H__
|
||||||
|
/** @} */
|
|
@ -0,0 +1,215 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2016 - 2017, Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||||
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||||
|
* such product, must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or other
|
||||||
|
* materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* 4. This software, with or without modification, must only be used with a
|
||||||
|
* Nordic Semiconductor ASA integrated circuit.
|
||||||
|
*
|
||||||
|
* 5. Any software provided in binary form under this license must not be reverse
|
||||||
|
* engineered, decompiled, modified and/or disassembled.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||||
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "sdk_common.h"
|
||||||
|
|
||||||
|
#if NRF_MODULE_ENABLED(NRF_FSTORAGE)
|
||||||
|
|
||||||
|
#include "nrf_fstorage_nvmc.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "nrf_nvmc.h"
|
||||||
|
#include "nrf_atomic.h"
|
||||||
|
|
||||||
|
|
||||||
|
static nrf_fstorage_info_t m_flash_info =
|
||||||
|
{
|
||||||
|
#if defined(NRF51)
|
||||||
|
.erase_unit = 1024,
|
||||||
|
#elif defined(NRF52_SERIES)
|
||||||
|
.erase_unit = 4096,
|
||||||
|
#endif
|
||||||
|
.program_unit = 4,
|
||||||
|
.rmap = true,
|
||||||
|
.wmap = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* An operation initiated by fstorage is ongoing. */
|
||||||
|
static nrf_atomic_flag_t m_flash_operation_ongoing;
|
||||||
|
|
||||||
|
|
||||||
|
/* Send event to the event handler. */
|
||||||
|
static void event_send(nrf_fstorage_t const * p_fs,
|
||||||
|
nrf_fstorage_evt_id_t evt_id,
|
||||||
|
uint32_t addr,
|
||||||
|
uint32_t len,
|
||||||
|
void * p_param)
|
||||||
|
{
|
||||||
|
if (p_fs->evt_handler == NULL)
|
||||||
|
{
|
||||||
|
/* Nothing to do. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrf_fstorage_evt_t evt =
|
||||||
|
{
|
||||||
|
.result = NRF_SUCCESS,
|
||||||
|
.id = evt_id,
|
||||||
|
.addr = addr,
|
||||||
|
.len = len,
|
||||||
|
.p_param = p_param,
|
||||||
|
};
|
||||||
|
|
||||||
|
p_fs->evt_handler(&evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ret_code_t init(nrf_fstorage_t * p_fs, void * p_param)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_param);
|
||||||
|
|
||||||
|
p_fs->p_flash_info = &m_flash_info;
|
||||||
|
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ret_code_t uninit(nrf_fstorage_t * p_fs, void * p_param)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_fs);
|
||||||
|
UNUSED_PARAMETER(p_param);
|
||||||
|
|
||||||
|
(void) nrf_atomic_flag_clear(&m_flash_operation_ongoing);
|
||||||
|
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ret_code_t read(nrf_fstorage_t const * p_fs, uint32_t src, void * p_dest, uint32_t len)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_fs);
|
||||||
|
|
||||||
|
memcpy(p_dest, (uint32_t*)src, len);
|
||||||
|
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ret_code_t write(nrf_fstorage_t const * p_fs,
|
||||||
|
uint32_t dest,
|
||||||
|
void const * p_src,
|
||||||
|
uint32_t len,
|
||||||
|
void * p_param)
|
||||||
|
{
|
||||||
|
if (nrf_atomic_flag_set_fetch(&m_flash_operation_ongoing))
|
||||||
|
{
|
||||||
|
return NRF_ERROR_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrf_nvmc_write_words(dest, (uint32_t*)p_src, (len / m_flash_info.program_unit));
|
||||||
|
|
||||||
|
/* Clear the flag before sending the event, to allow API calls in the event context. */
|
||||||
|
(void) nrf_atomic_flag_clear(&m_flash_operation_ongoing);
|
||||||
|
|
||||||
|
event_send(p_fs, NRF_FSTORAGE_EVT_WRITE_RESULT, dest, len, p_param);
|
||||||
|
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ret_code_t erase(nrf_fstorage_t const * p_fs,
|
||||||
|
uint32_t page_addr,
|
||||||
|
uint32_t len,
|
||||||
|
void * p_param)
|
||||||
|
{
|
||||||
|
uint32_t progress = 0;
|
||||||
|
|
||||||
|
if (nrf_atomic_flag_set_fetch(&m_flash_operation_ongoing))
|
||||||
|
{
|
||||||
|
return NRF_ERROR_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (progress != len)
|
||||||
|
{
|
||||||
|
nrf_nvmc_page_erase(page_addr + (progress * m_flash_info.erase_unit));
|
||||||
|
progress++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the flag before sending the event, to allow API calls in the event context. */
|
||||||
|
(void) nrf_atomic_flag_clear(&m_flash_operation_ongoing);
|
||||||
|
|
||||||
|
event_send(p_fs, NRF_FSTORAGE_EVT_ERASE_RESULT, page_addr, len, p_param);
|
||||||
|
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t const * rmap(nrf_fstorage_t const * p_fs, uint32_t addr)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_fs);
|
||||||
|
|
||||||
|
return (uint8_t*)addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t * wmap(nrf_fstorage_t const * p_fs, uint32_t addr)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_fs);
|
||||||
|
UNUSED_PARAMETER(addr);
|
||||||
|
|
||||||
|
/* Not supported. */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool is_busy(nrf_fstorage_t const * p_fs)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_fs);
|
||||||
|
|
||||||
|
return m_flash_operation_ongoing;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* The exported API. */
|
||||||
|
nrf_fstorage_api_t nrf_fstorage_nvmc =
|
||||||
|
{
|
||||||
|
.init = init,
|
||||||
|
.uninit = uninit,
|
||||||
|
.read = read,
|
||||||
|
.write = write,
|
||||||
|
.erase = erase,
|
||||||
|
.rmap = rmap,
|
||||||
|
.wmap = wmap,
|
||||||
|
.is_busy = is_busy
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // NRF_FSTORAGE_ENABLED
|
|
@ -0,0 +1,75 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2016 - 2017, Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||||
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||||
|
* such product, must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or other
|
||||||
|
* materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* 4. This software, with or without modification, must only be used with a
|
||||||
|
* Nordic Semiconductor ASA integrated circuit.
|
||||||
|
*
|
||||||
|
* 5. Any software provided in binary form under this license must not be reverse
|
||||||
|
* engineered, decompiled, modified and/or disassembled.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||||
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @defgroup nrf_fstorage_nvmc NVMC implementation
|
||||||
|
* @ingroup nrf_fstorage
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @brief API implementation of fstorage that uses the non-volatile memory controller (NVMC).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NRF_FSTORAGE_NVMC_H__
|
||||||
|
#define NRF_FSTORAGE_NVMC_H__
|
||||||
|
|
||||||
|
#include "nrf_fstorage.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief API implementation that uses the non-volatile memory controller.
|
||||||
|
*
|
||||||
|
* @details An fstorage instance with this API implementation can be initialized by providing
|
||||||
|
* this structure as a parameter to @ref nrf_fstorage_init.
|
||||||
|
* The structure is defined in @c nrf_fstorage_nvmc.c.
|
||||||
|
*/
|
||||||
|
extern nrf_fstorage_api_t nrf_fstorage_nvmc;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // NRF_FSTORAGE_NVMC_H__
|
||||||
|
/** @} */
|
|
@ -3,6 +3,10 @@
|
||||||
"macros": [
|
"macros": [
|
||||||
"SOFTDEVICE_PRESENT=1",
|
"SOFTDEVICE_PRESENT=1",
|
||||||
"BLE_STACK_SUPPORT_REQD=1",
|
"BLE_STACK_SUPPORT_REQD=1",
|
||||||
|
"NRF_SDH_ENABLED=1",
|
||||||
|
"NRF_SDH_SOC_ENABLED=1",
|
||||||
|
"NRF_SDH_DISPATCH_MODEL=2",
|
||||||
|
"FDS_BACKEND=2",
|
||||||
"S132=1"
|
"S132=1"
|
||||||
],
|
],
|
||||||
"target_overrides": {
|
"target_overrides": {
|
||||||
|
|
|
@ -5,6 +5,10 @@
|
||||||
"BLE_STACK_SUPPORT_REQD=1",
|
"BLE_STACK_SUPPORT_REQD=1",
|
||||||
"NRF_DFU_SETTINGS_VERSION=1",
|
"NRF_DFU_SETTINGS_VERSION=1",
|
||||||
"NRF_SD_BLE_API_VERSION=5",
|
"NRF_SD_BLE_API_VERSION=5",
|
||||||
|
"NRF_SDH_ENABLED=1",
|
||||||
|
"NRF_SDH_SOC_ENABLED=1",
|
||||||
|
"NRF_SDH_DISPATCH_MODEL=2",
|
||||||
|
"FDS_BACKEND=2",
|
||||||
"S140=1"
|
"S140=1"
|
||||||
],
|
],
|
||||||
"target_overrides": {
|
"target_overrides": {
|
||||||
|
|
|
@ -7,7 +7,6 @@ components/libraries
|
||||||
Only essential folders have been copied over.
|
Only essential folders have been copied over.
|
||||||
|
|
||||||
Removed:
|
Removed:
|
||||||
* atomic_fifo/
|
|
||||||
* block_dev/
|
* block_dev/
|
||||||
* bootloader/
|
* bootloader/
|
||||||
* bsp/
|
* bsp/
|
||||||
|
@ -24,9 +23,7 @@ Removed:
|
||||||
* experimental_ringbuf/
|
* experimental_ringbuf/
|
||||||
* experimental_stack_guard/
|
* experimental_stack_guard/
|
||||||
* experimental_task_manager/
|
* experimental_task_manager/
|
||||||
* fds/
|
|
||||||
* fifo/
|
* fifo/
|
||||||
* fstorage/
|
|
||||||
* gfx/
|
* gfx/
|
||||||
* gpiote/
|
* gpiote/
|
||||||
* hardfault/
|
* hardfault/
|
||||||
|
@ -52,4 +49,3 @@ Removed:
|
||||||
* twi_mngr/
|
* twi_mngr/
|
||||||
* uart/
|
* uart/
|
||||||
* usbd/
|
* usbd/
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011 - 2017, Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||||
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||||
|
* such product, must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or other
|
||||||
|
* materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* 4. This software, with or without modification, must only be used with a
|
||||||
|
* Nordic Semiconductor ASA integrated circuit.
|
||||||
|
*
|
||||||
|
* 5. Any software provided in binary form under this license must not be reverse
|
||||||
|
* engineered, decompiled, modified and/or disassembled.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||||
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "app_util.h"
|
||||||
|
#include "nrf_atfifo.h"
|
||||||
|
#include "nrf_atfifo_internal.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Unions testing */
|
||||||
|
STATIC_ASSERT(sizeof(nrf_atfifo_postag_t) == sizeof(uint32_t));
|
||||||
|
|
||||||
|
|
||||||
|
ret_code_t nrf_atfifo_init(nrf_atfifo_t * const p_fifo, void * p_buf, uint16_t buf_size, uint16_t item_size)
|
||||||
|
{
|
||||||
|
if (NULL == p_buf)
|
||||||
|
{
|
||||||
|
return NRF_ERROR_NULL;
|
||||||
|
}
|
||||||
|
if (0 != (buf_size % item_size))
|
||||||
|
{
|
||||||
|
return NRF_ERROR_INVALID_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_fifo->p_buf = p_buf;
|
||||||
|
p_fifo->tail.tag = 0;
|
||||||
|
p_fifo->head.tag = 0;
|
||||||
|
p_fifo->buf_size = buf_size;
|
||||||
|
p_fifo->item_size = item_size;
|
||||||
|
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ret_code_t nrf_atfifo_clear(nrf_atfifo_t * const p_fifo)
|
||||||
|
{
|
||||||
|
bool released = nrf_atfifo_space_clear(p_fifo);
|
||||||
|
return released ? NRF_SUCCESS : NRF_ERROR_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ret_code_t nrf_atfifo_alloc_put(nrf_atfifo_t * const p_fifo, void const * p_var, size_t size, bool * const p_visible)
|
||||||
|
{
|
||||||
|
nrf_atfifo_item_put_t context;
|
||||||
|
bool visible;
|
||||||
|
void * p_data = nrf_atfifo_item_alloc(p_fifo, &context);
|
||||||
|
if (NULL == p_data)
|
||||||
|
{
|
||||||
|
return NRF_ERROR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(p_data, p_var, size);
|
||||||
|
|
||||||
|
visible = nrf_atfifo_item_put(p_fifo, &context);
|
||||||
|
if (NULL != p_visible)
|
||||||
|
{
|
||||||
|
*p_visible = visible;
|
||||||
|
}
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void * nrf_atfifo_item_alloc(nrf_atfifo_t * const p_fifo, nrf_atfifo_item_put_t * p_context)
|
||||||
|
{
|
||||||
|
if (nrf_atfifo_wspace_req(p_fifo, &(p_context->last_tail)))
|
||||||
|
{
|
||||||
|
return ((uint8_t*)(p_fifo->p_buf)) + p_context->last_tail.pos.wr;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool nrf_atfifo_item_put(nrf_atfifo_t * const p_fifo, nrf_atfifo_item_put_t * p_context)
|
||||||
|
{
|
||||||
|
if ((p_context->last_tail.pos.wr) == (p_context->last_tail.pos.rd))
|
||||||
|
{
|
||||||
|
nrf_atfifo_wspace_close(p_fifo);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ret_code_t nrf_atfifo_get_free(nrf_atfifo_t * const p_fifo, void * const p_var, size_t size, bool * p_released)
|
||||||
|
{
|
||||||
|
nrf_atfifo_item_get_t context;
|
||||||
|
bool released;
|
||||||
|
void const * p_s = nrf_atfifo_item_get(p_fifo, &context);
|
||||||
|
if (NULL == p_s)
|
||||||
|
{
|
||||||
|
return NRF_ERROR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(p_var, p_s, size);
|
||||||
|
|
||||||
|
released = nrf_atfifo_item_free(p_fifo, &context);
|
||||||
|
if (NULL != p_released)
|
||||||
|
{
|
||||||
|
*p_released = released;
|
||||||
|
}
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void * nrf_atfifo_item_get(nrf_atfifo_t * const p_fifo, nrf_atfifo_item_get_t * p_context)
|
||||||
|
{
|
||||||
|
if (nrf_atfifo_rspace_req(p_fifo, &(p_context->last_head)))
|
||||||
|
{
|
||||||
|
return ((uint8_t*)(p_fifo->p_buf)) + p_context->last_head.pos.rd;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool nrf_atfifo_item_free(nrf_atfifo_t * const p_fifo, nrf_atfifo_item_get_t * p_context)
|
||||||
|
{
|
||||||
|
if ((p_context->last_head.pos.wr) == (p_context->last_head.pos.rd))
|
||||||
|
{
|
||||||
|
nrf_atfifo_rspace_close(p_fifo);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,407 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011 - 2017, Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||||
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||||
|
* such product, must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or other
|
||||||
|
* materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* 4. This software, with or without modification, must only be used with a
|
||||||
|
* Nordic Semiconductor ASA integrated circuit.
|
||||||
|
*
|
||||||
|
* 5. Any software provided in binary form under this license must not be reverse
|
||||||
|
* engineered, decompiled, modified and/or disassembled.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||||
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef NRF_ATFIFO_H__
|
||||||
|
#define NRF_ATFIFO_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "nordic_common.h"
|
||||||
|
#include "nrf_assert.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup nrf_atfifo Atomic FIFO
|
||||||
|
* @ingroup app_common
|
||||||
|
*
|
||||||
|
* @brief @tagAPI52 FIFO implementation that allows for making atomic transactions without
|
||||||
|
* locking interrupts.
|
||||||
|
*
|
||||||
|
* @details There are two types of functions to prepare the FIFO writing:
|
||||||
|
* - Single function for simple access:
|
||||||
|
* @code
|
||||||
|
* if (NRF_SUCCESS != nrf_atfifo_simple_put(my_fifo, &data, NULL))
|
||||||
|
* {
|
||||||
|
* // Error handling
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* - Function pair to limit data copying:
|
||||||
|
* @code
|
||||||
|
* struct point3d
|
||||||
|
* {
|
||||||
|
* int x, y, z;
|
||||||
|
* }point3d_t;
|
||||||
|
* nrf_atfifo_context_t context;
|
||||||
|
* point3d_t * point;
|
||||||
|
*
|
||||||
|
* if (NULL != (point = nrf_atfifo_item_alloc(my_fifo, &context)))
|
||||||
|
* {
|
||||||
|
* point->x = a;
|
||||||
|
* point->y = b;
|
||||||
|
* point->z = c;
|
||||||
|
* if (nrf_atfifo_item_put(my_fifo, &context))
|
||||||
|
* {
|
||||||
|
* // Send information to the rest of the system
|
||||||
|
* // that there is new data in the FIFO available for reading.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* {
|
||||||
|
* // Error handling
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @endcode
|
||||||
|
* @note
|
||||||
|
* This atomic FIFO implementation requires that the operation that is
|
||||||
|
* opened last is finished (committed/flushed) first.
|
||||||
|
* This is typical for operations performed from the interrupt runtime
|
||||||
|
* when the other operation is performed from the main thread.
|
||||||
|
*
|
||||||
|
* This implementation does not support typical multithreading operating system
|
||||||
|
* access where operations can be started and finished in totally unrelated order.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read and write position structure.
|
||||||
|
*
|
||||||
|
* A structure that holds the read and write position used by the FIFO head and tail.
|
||||||
|
*/
|
||||||
|
typedef struct nrf_atfifo_postag_pos_s
|
||||||
|
{
|
||||||
|
uint16_t wr; //!< First free space to write the data
|
||||||
|
uint16_t rd; //!< A place after the last data to read
|
||||||
|
}nrf_atfifo_postag_pos_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief End data index tag.
|
||||||
|
*
|
||||||
|
* A tag used to mark the end of data.
|
||||||
|
* To properly realize atomic data committing, the whole variable has to be
|
||||||
|
* accessed atomically.
|
||||||
|
*/
|
||||||
|
typedef union nrf_atfifo_postag_u
|
||||||
|
{
|
||||||
|
uint32_t tag; //!< Whole tag, used for atomic, 32-bit access
|
||||||
|
nrf_atfifo_postag_pos_t pos; //!< Structure that holds reading and writing position separately
|
||||||
|
}nrf_atfifo_postag_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The FIFO instance.
|
||||||
|
*
|
||||||
|
* The instance of atomic FIFO.
|
||||||
|
* Used with all FIFO functions.
|
||||||
|
*/
|
||||||
|
typedef struct nrf_atfifo_s
|
||||||
|
{
|
||||||
|
void * p_buf; //!< Pointer to the data buffer
|
||||||
|
nrf_atfifo_postag_t tail; //!< Read and write tail position tag
|
||||||
|
nrf_atfifo_postag_t head; //!< Read and write head position tag
|
||||||
|
uint16_t buf_size; //!< FIFO size in number of bytes (has to be divisible by @c item_size)
|
||||||
|
uint16_t item_size; //!< Size of a single FIFO item
|
||||||
|
}nrf_atfifo_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief FIFO write operation item context.
|
||||||
|
*
|
||||||
|
* Context structure used to mark an allocated space in FIFO that is ready for put.
|
||||||
|
* All the data required to properly put allocated and written data.
|
||||||
|
*/
|
||||||
|
typedef struct nrf_atfifo_item_put_s
|
||||||
|
{
|
||||||
|
nrf_atfifo_postag_t last_tail; //!< Tail tag value that was here when opening the FIFO to write
|
||||||
|
}nrf_atfifo_item_put_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief FIFO read operation item context.
|
||||||
|
*
|
||||||
|
* Context structure used to mark an opened get operation to properly free an item after reading.
|
||||||
|
*/
|
||||||
|
typedef struct nrf_atfifo_rcontext_s
|
||||||
|
{
|
||||||
|
nrf_atfifo_postag_t last_head; //!< Head tag value that was here when opening the FIFO to read
|
||||||
|
}nrf_atfifo_item_get_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup nrf_atfifo_instmacros FIFO instance macros
|
||||||
|
*
|
||||||
|
* A group of macros helpful for FIFO instance creation and initialization.
|
||||||
|
* They may be used to create and initialize instances for most use cases.
|
||||||
|
*
|
||||||
|
* FIFO may also be created and initialized directly using
|
||||||
|
* @ref nrf_atfifo_init function.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @brief Macro for generating the name for a data buffer.
|
||||||
|
*
|
||||||
|
* The name of the data buffer that would be created by
|
||||||
|
* @ref NRF_ATFIFO_DEF macro.
|
||||||
|
*
|
||||||
|
* @param[in] fifo_id Identifier of the FIFO object.
|
||||||
|
*
|
||||||
|
* @return Name of the buffer variable.
|
||||||
|
*
|
||||||
|
* @note This is auxiliary internal macro and in normal usage
|
||||||
|
* it should not be called.
|
||||||
|
*/
|
||||||
|
#define NRF_ATFIFO_BUF_NAME(fifo_id) CONCAT_2(fifo_id, _data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Macro for generating the name for a FIFO instance.
|
||||||
|
*
|
||||||
|
* The name of the instance variable that will be created by the
|
||||||
|
* @ref NRF_ATFIFO_DEF macro.
|
||||||
|
*
|
||||||
|
* @param[in] fifo_id Identifier of the FIFO object.
|
||||||
|
*
|
||||||
|
* @return Name of the instance variable.
|
||||||
|
*
|
||||||
|
* @note This is auxiliary internal macro and in normal usage
|
||||||
|
* it should not be called.
|
||||||
|
*/
|
||||||
|
#define NRF_ATFIFO_INST_NAME(fifo_id) CONCAT_2(fifo_id, _inst)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Macro for creating an instance.
|
||||||
|
*
|
||||||
|
* Creates the FIFO object variable itself.
|
||||||
|
*
|
||||||
|
* Usage example:
|
||||||
|
* @code
|
||||||
|
* NRF_ATFIFO_DEF(my_fifo, uint16_t, 12);
|
||||||
|
* NRF_ATFIFO_INIT(my_fifo);
|
||||||
|
*
|
||||||
|
* uint16_t some_val = 45;
|
||||||
|
* nrf_atfifo_item_put(my_fifo, &some_val, sizeof(some_val), NULL);
|
||||||
|
* nrf_atfifo_item_get(my_fifo, &some_val, sizeof(some_val), NULL);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @param[in] fifo_id Identifier of a FIFO object.
|
||||||
|
* This identifier will be a pointer to the instance.
|
||||||
|
* It makes it possible to use this directly for the functions
|
||||||
|
* that operate on the FIFO.
|
||||||
|
* Because it is a static const object, it should be optimized by the compiler.
|
||||||
|
* @param[in] storage_type Type of data that will be stored in the FIFO.
|
||||||
|
* @param[in] item_cnt Capacity of the created FIFO in maximum number of items that may be stored.
|
||||||
|
* The phisical size of the buffer will be 1 element bigger.
|
||||||
|
*/
|
||||||
|
#define NRF_ATFIFO_DEF(fifo_id, storage_type, item_cnt) \
|
||||||
|
static storage_type NRF_ATFIFO_BUF_NAME(fifo_id)[(item_cnt)+1]; \
|
||||||
|
static nrf_atfifo_t NRF_ATFIFO_INST_NAME(fifo_id); \
|
||||||
|
static nrf_atfifo_t * const fifo_id = &NRF_ATFIFO_INST_NAME(fifo_id)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Macro for initializing the FIFO that was previously declared by the macro.
|
||||||
|
*
|
||||||
|
* Use this macro to simplify FIFO initialization.
|
||||||
|
*
|
||||||
|
* @note
|
||||||
|
* This macro can be only used on a FIFO object defined by @ref NRF_ATFIFO_DEF macro.
|
||||||
|
*
|
||||||
|
* @param[in] fifo_id Identifier of the FIFO object.
|
||||||
|
*
|
||||||
|
* @return Value from the @ref nrf_atfifo_init function.
|
||||||
|
*/
|
||||||
|
#define NRF_ATFIFO_INIT(fifo_id) \
|
||||||
|
nrf_atfifo_init( \
|
||||||
|
fifo_id, \
|
||||||
|
NRF_ATFIFO_BUF_NAME(fifo_id), \
|
||||||
|
sizeof(NRF_ATFIFO_BUF_NAME(fifo_id)), \
|
||||||
|
sizeof(NRF_ATFIFO_BUF_NAME(fifo_id)[0]) \
|
||||||
|
)
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function for initializing the FIFO.
|
||||||
|
*
|
||||||
|
* Preparing the FIFO instance to work.
|
||||||
|
*
|
||||||
|
* @param[out] p_fifo FIFO object to initialize.
|
||||||
|
* @param[in,out] p_buf FIFO buffer for storing data.
|
||||||
|
* @param[in] buf_size Total buffer size (has to be divisible by @c item_size).
|
||||||
|
* @param[in] item_size Size of a single item held inside the FIFO.
|
||||||
|
*
|
||||||
|
* @retval NRF_SUCCESS If initialization was successful.
|
||||||
|
* @retval NRF_ERROR_NULL If a NULL pointer is provided as the buffer.
|
||||||
|
* @retval NRF_ERROR_INVALID_LENGTH If size of the buffer provided is not divisible by @c item_size.
|
||||||
|
*
|
||||||
|
* @note
|
||||||
|
* Buffer size must be able to hold one element more than the designed FIFO capacity.
|
||||||
|
* This one, empty element is used for overflow checking.
|
||||||
|
*/
|
||||||
|
ret_code_t nrf_atfifo_init(nrf_atfifo_t * const p_fifo, void * p_buf, uint16_t buf_size, uint16_t item_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function for clearing the FIFO.
|
||||||
|
*
|
||||||
|
* Function for clearing the FIFO.
|
||||||
|
*
|
||||||
|
* If this function is called during an opened and uncommitted write operation,
|
||||||
|
* the FIFO is cleared up to the currently ongoing commit.
|
||||||
|
* There is no possibility to cancel an ongoing commit.
|
||||||
|
*
|
||||||
|
* If this function is called during an opened and unflushed read operation,
|
||||||
|
* the read position in the head is set, but copying it into the write head position
|
||||||
|
* is left to read closing operation.
|
||||||
|
*
|
||||||
|
* This way, there is no more data to read, but the memory is released
|
||||||
|
* in the moment when it is safe.
|
||||||
|
*
|
||||||
|
* @param[in,out] p_fifo FIFO object.
|
||||||
|
*
|
||||||
|
* @retval NRF_SUCCESS FIFO totally cleared.
|
||||||
|
* @retval NRF_ERROR_BUSY Function called in the middle of writing or reading operation.
|
||||||
|
* If it is called in the middle of writing operation,
|
||||||
|
* FIFO was cleared up to the already started and uncommitted write.
|
||||||
|
* If it is called in the middle of reading operation,
|
||||||
|
* write head was only moved. It will be copied into read tail when the reading operation
|
||||||
|
* is flushed.
|
||||||
|
*/
|
||||||
|
ret_code_t nrf_atfifo_clear(nrf_atfifo_t * const p_fifo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function for atomically putting data into the FIFO.
|
||||||
|
*
|
||||||
|
* It uses memcpy function inside and in most situations, it is more suitable to
|
||||||
|
* use @ref nrf_atfifo_item_alloc, write the data, and @ref nrf_atfifo_item_put to store a new value
|
||||||
|
* in a FIFO.
|
||||||
|
*
|
||||||
|
* @param[in,out] p_fifo FIFO object.
|
||||||
|
* @param[in] p_var Variable to copy.
|
||||||
|
* @param[in] size Size of the variable to copy.
|
||||||
|
* Can be smaller or equal to the FIFO item size.
|
||||||
|
* @param[out] p_visible See value returned by @ref nrf_atfifo_item_put.
|
||||||
|
* It may be NULL if the caller does not require the current operation status.
|
||||||
|
*
|
||||||
|
* @retval NRF_SUCCESS If an element has been successfully added to the FIFO.
|
||||||
|
* @retval NRF_ERROR_NO_MEM If the FIFO is full.
|
||||||
|
*
|
||||||
|
* @note
|
||||||
|
* To avoid data copying, you can use the @ref nrf_atfifo_item_alloc and @ref nrf_atfifo_item_put
|
||||||
|
* functions pair.
|
||||||
|
*/
|
||||||
|
ret_code_t nrf_atfifo_alloc_put(nrf_atfifo_t * const p_fifo, void const * const p_var, size_t size, bool * const p_visible);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function for opening the FIFO for writing.
|
||||||
|
*
|
||||||
|
* Function called to start the FIFO write operation and access the given FIFO buffer directly.
|
||||||
|
*
|
||||||
|
* @param[in,out] p_fifo FIFO object.
|
||||||
|
* @param[out] p_context Operation context, required by @ref nrf_atfifo_item_put.
|
||||||
|
*
|
||||||
|
* @return Pointer to the space where variable data can be stored.
|
||||||
|
* NULL if there is no space in the buffer.
|
||||||
|
*/
|
||||||
|
void * nrf_atfifo_item_alloc(nrf_atfifo_t * const p_fifo, nrf_atfifo_item_put_t * p_context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function for closing the writing operation.
|
||||||
|
*
|
||||||
|
* Puts a previously allocated context into FIFO.
|
||||||
|
* This function must be called to commit an opened write operation.
|
||||||
|
* It sets all the buffers and marks the data, so that it is visible to read.
|
||||||
|
*
|
||||||
|
* @param[in,out] p_fifo FIFO object.
|
||||||
|
* @param[in] p_context Operation context, filled by the @ref nrf_atfifo_item_alloc function.
|
||||||
|
*
|
||||||
|
* @retval true Data is currently ready and will be visible to read.
|
||||||
|
* @retval false The internal commit was marked, but the writing operation interrupted another writing operation.
|
||||||
|
* The data will be available to read when the interrupted operation is committed.
|
||||||
|
*/
|
||||||
|
bool nrf_atfifo_item_put(nrf_atfifo_t * const p_fifo, nrf_atfifo_item_put_t * p_context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function for getting a single value from the FIFO.
|
||||||
|
*
|
||||||
|
* This function gets the value from the top of the FIFO.
|
||||||
|
* The value is removed from the FIFO memory.
|
||||||
|
*
|
||||||
|
* @param[in,out] p_fifo FIFO object.
|
||||||
|
* @param[out] p_var Pointer to the variable to store the data.
|
||||||
|
* @param[in] size Size of the data to be loaded.
|
||||||
|
* @param[out] p_released See the values returned by @ref nrf_atfifo_item_free.
|
||||||
|
*
|
||||||
|
* @retval NRF_SUCCESS Element was successfully copied from the FIFO memory.
|
||||||
|
* @retval NRF_ERROR_NOT_FOUND No data in the FIFO.
|
||||||
|
*/
|
||||||
|
ret_code_t nrf_atfifo_get_free(nrf_atfifo_t * const p_fifo, void * const p_var, size_t size, bool * p_released);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function for opening the FIFO for reading.
|
||||||
|
*
|
||||||
|
* Function called to start the FIFO read operation and access the given FIFO buffer directly.
|
||||||
|
*
|
||||||
|
* @param[in,out] p_fifo FIFO object.
|
||||||
|
* @param[out] p_context The operation context, required by @ref nrf_atfifo_item_free
|
||||||
|
*
|
||||||
|
* @return Pointer to data buffer or NULL if there is no data in the FIFO.
|
||||||
|
*/
|
||||||
|
void * nrf_atfifo_item_get(nrf_atfifo_t * const p_fifo, nrf_atfifo_item_get_t * p_context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function for closing the reading operation.
|
||||||
|
*
|
||||||
|
* Function used to finish the reading operation.
|
||||||
|
* If this reading operation does not interrupt another reading operation, the head write buffer is moved.
|
||||||
|
* If this reading operation is placed in the middle of another reading, only the new read pointer is written.
|
||||||
|
*
|
||||||
|
* @param[in,out] p_fifo FIFO object.
|
||||||
|
* @param[in] p_context Context of the reading operation to be closed.
|
||||||
|
*
|
||||||
|
* @retval true This operation is not generated in the middle of another read operation and the write head will be updated to the read head (space is released).
|
||||||
|
* @retval false This operation was performed in the middle of another read operation and the write buffer head was not moved (no space is released).
|
||||||
|
*/
|
||||||
|
bool nrf_atfifo_item_free(nrf_atfifo_t * const p_fifo, nrf_atfifo_item_get_t * p_context);
|
||||||
|
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* NRF_ATFIFO_H__ */
|
|
@ -0,0 +1,577 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011 - 2017, Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||||
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||||
|
* such product, must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or other
|
||||||
|
* materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* 4. This software, with or without modification, must only be used with a
|
||||||
|
* Nordic Semiconductor ASA integrated circuit.
|
||||||
|
*
|
||||||
|
* 5. Any software provided in binary form under this license must not be reverse
|
||||||
|
* engineered, decompiled, modified and/or disassembled.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||||
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Atomic FIFO internal file
|
||||||
|
*
|
||||||
|
* This file should be included only by nrf_atfifo internally.
|
||||||
|
* Needs nrf_atfifo.h included first.
|
||||||
|
*/
|
||||||
|
#ifndef NRF_ATFIFO_H__
|
||||||
|
#error This is internal file. Do not include this file in your program.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NRF_ATFIFO_INTERNAL_H__
|
||||||
|
#define NRF_ATFIFO_INTERNAL_H__
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "nrf.h"
|
||||||
|
#include "app_util.h"
|
||||||
|
#include "nordic_common.h"
|
||||||
|
|
||||||
|
#if ((__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U)) == 0
|
||||||
|
#error Unsupported core version
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure that rd and wr pos in a tag are aligned like expected
|
||||||
|
* Changing this would require changes inside assembly code!
|
||||||
|
*/
|
||||||
|
STATIC_ASSERT(offsetof(nrf_atfifo_postag_pos_t, wr) == 0);
|
||||||
|
STATIC_ASSERT(offsetof(nrf_atfifo_postag_pos_t, rd) == 2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Atomically reserve space for a new write.
|
||||||
|
*
|
||||||
|
* @param[in,out] p_fifo FIFO object.
|
||||||
|
* @param[out] old_tail Tail position tag before new space is reserved.
|
||||||
|
*
|
||||||
|
* @retval true Space available.
|
||||||
|
* @retval false Memory full.
|
||||||
|
*
|
||||||
|
* @sa nrf_atfifo_wspace_close
|
||||||
|
*/
|
||||||
|
static bool nrf_atfifo_wspace_req(nrf_atfifo_t * const p_fifo, nrf_atfifo_postag_t * const p_old_tail);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Atomically mark all written data available.
|
||||||
|
*
|
||||||
|
* This function marks all data available for reading.
|
||||||
|
* This marking is done by copying tail.pos.wr into tail.pos.rd.
|
||||||
|
*
|
||||||
|
* It must be called only when closing the first write.
|
||||||
|
* It cannot be called if any write access was interrupted.
|
||||||
|
* See the code below:
|
||||||
|
* @code
|
||||||
|
* if (old_tail.pos.wr == old_tail.pos.rd)
|
||||||
|
* {
|
||||||
|
* nrf_atfifo_wspace_close(my_fifo);
|
||||||
|
* return true;
|
||||||
|
* }
|
||||||
|
* return false;
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @param[in,out] p_fifo FIFO object.
|
||||||
|
*
|
||||||
|
* @sa nrf_atfifo_wspace_req
|
||||||
|
*/
|
||||||
|
static void nrf_atfifo_wspace_close(nrf_atfifo_t * const p_fifo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Atomically get a part of a buffer to read data.
|
||||||
|
*
|
||||||
|
* @param[in,out] p_fifo FIFO object.
|
||||||
|
* @param[out] old_head Head position tag before the data buffer is read.
|
||||||
|
*
|
||||||
|
* @retval true Data available for reading.
|
||||||
|
* @retval false No data in the buffer.
|
||||||
|
*
|
||||||
|
* @sa nrf_atfifo_rspace_close
|
||||||
|
*/
|
||||||
|
static bool nrf_atfifo_rspace_req(nrf_atfifo_t * const p_fifo, nrf_atfifo_postag_t * const p_old_head);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Atomically release all read data.
|
||||||
|
*
|
||||||
|
* This function marks all data that was read as free space,
|
||||||
|
* which is available for writing.
|
||||||
|
* This marking is done by copying head.pos.rd into head.pos.wr.
|
||||||
|
*
|
||||||
|
* It must be called only when closing the first read.
|
||||||
|
* It cannot be called when the current read access interrupted any other read access.
|
||||||
|
* See code below:
|
||||||
|
* @code
|
||||||
|
* if (old_head.pos.wr == old_head.pos.rd)
|
||||||
|
* {
|
||||||
|
* nrf_atfifo_rspace_close(my_fifo);
|
||||||
|
* return true;
|
||||||
|
* }
|
||||||
|
* return false;
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @param[in,out] p_fifo FIFO object.
|
||||||
|
*
|
||||||
|
* @sa nrf_atfifo_rspace_req
|
||||||
|
*/
|
||||||
|
static void nrf_atfifo_rspace_close(nrf_atfifo_t * const p_fifo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Safely clear the FIFO, internal function.
|
||||||
|
*
|
||||||
|
* This function realizes the functionality required by @ref nrf_atfifo_clear.
|
||||||
|
*
|
||||||
|
* @param[in,out] p_fifo FIFO object.
|
||||||
|
*
|
||||||
|
* @retval true All the data was released.
|
||||||
|
* @retval false All the data available for releasing was released, but there is some pending transfer.
|
||||||
|
*/
|
||||||
|
static bool nrf_atfifo_space_clear(nrf_atfifo_t * const p_fifo);
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------------
|
||||||
|
* Implementation starts here
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined ( __CC_ARM )
|
||||||
|
|
||||||
|
|
||||||
|
__ASM bool nrf_atfifo_wspace_req(nrf_atfifo_t * const p_fifo, nrf_atfifo_postag_t * const p_old_tail)
|
||||||
|
{
|
||||||
|
/* Registry usage:
|
||||||
|
* R0 - p_fifo
|
||||||
|
* R1 - p_old_tail
|
||||||
|
* R2 - internal variable old_tail (saved by caller)
|
||||||
|
* R3 - internal variable new_tail (saved by caller)
|
||||||
|
* R4 - internal temporary register (saved by this function)
|
||||||
|
* R5 - not used stored to keep the stack aligned to 8 bytes
|
||||||
|
* Returned value:
|
||||||
|
* R0 (bool - 32 bits)
|
||||||
|
*/
|
||||||
|
push {r4, r5}
|
||||||
|
nrf_atfifo_wspace_req_repeat
|
||||||
|
/* Load tail tag and set memory monitor !!! R2 - old tail !!! */
|
||||||
|
ldrex r2, [r0, #__cpp(offsetof(nrf_atfifo_t, tail))]
|
||||||
|
/* Extract write position !!! R3 !!! */
|
||||||
|
uxth r3, r2
|
||||||
|
/* Increment address with overload support !!! R4 used temporary !!! */
|
||||||
|
ldrh r4, [r0, #__cpp(offsetof(nrf_atfifo_t, item_size))]
|
||||||
|
add r3, r4
|
||||||
|
ldrh r4, [r0, #__cpp(offsetof(nrf_atfifo_t, buf_size))]
|
||||||
|
cmp r3, r4
|
||||||
|
it hs
|
||||||
|
subhs r3, r3, r4
|
||||||
|
|
||||||
|
/* Check if FIFO would overload after making this increment !!! R4 used temporary !!! */
|
||||||
|
ldrh r4, [r0, #__cpp(offsetof(nrf_atfifo_t, head) + offsetof(nrf_atfifo_postag_pos_t, wr))]
|
||||||
|
cmp r3, r4
|
||||||
|
ittt eq
|
||||||
|
clrexeq
|
||||||
|
moveq r0, #__cpp(false)
|
||||||
|
beq nrf_atfifo_wspace_req_exit
|
||||||
|
|
||||||
|
/* Pack everything back !!! R3 - new tail !!! */
|
||||||
|
/* Copy lower byte from new_tail, and higher byte is a value from the top of old_tail */
|
||||||
|
pkhbt r3, r3, r2
|
||||||
|
|
||||||
|
/* Store new value clearing memory monitor !!! R4 used temporary !!! */
|
||||||
|
strex r4, r3, [r0, #__cpp(offsetof(nrf_atfifo_t, tail))]
|
||||||
|
cmp r4, #0
|
||||||
|
bne nrf_atfifo_wspace_req_repeat
|
||||||
|
|
||||||
|
/* Return true */
|
||||||
|
mov r0, #__cpp(true)
|
||||||
|
nrf_atfifo_wspace_req_exit
|
||||||
|
/* Save old tail */
|
||||||
|
str r2, [r1]
|
||||||
|
pop {r4, r5}
|
||||||
|
bx lr
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__ASM void nrf_atfifo_wspace_close(nrf_atfifo_t * const p_fifo)
|
||||||
|
{
|
||||||
|
/* Registry usage:
|
||||||
|
* R0 - p_fifo
|
||||||
|
* R1 - internal temporary register
|
||||||
|
* R2 - new_tail
|
||||||
|
*/
|
||||||
|
nrf_atfifo_wspace_close_repeat
|
||||||
|
ldrex r2, [r0, #__cpp(offsetof(nrf_atfifo_t, tail))]
|
||||||
|
/* Copy from lower byte to higher */
|
||||||
|
pkhbt r2, r2, r2, lsl #16
|
||||||
|
|
||||||
|
strex r1, r2, [r0, #__cpp(offsetof(nrf_atfifo_t, tail))]
|
||||||
|
cmp r1, #0
|
||||||
|
bne nrf_atfifo_wspace_close_repeat
|
||||||
|
bx lr
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__ASM bool nrf_atfifo_rspace_req(nrf_atfifo_t * const p_fifo, nrf_atfifo_postag_t * const p_old_head)
|
||||||
|
{
|
||||||
|
/* Registry usage:
|
||||||
|
* R0 - p_fifo
|
||||||
|
* R1 - p_old_head
|
||||||
|
* R2 - internal variable old_head (saved by caller)
|
||||||
|
* R3 - internal variable new_head (saved by caller)
|
||||||
|
* R4 - internal temporary register (saved by this function)
|
||||||
|
* R5 - not used stored to keep the stack aligned to 8 bytes
|
||||||
|
* Returned value:
|
||||||
|
* R0 (bool - 32 bits)
|
||||||
|
*/
|
||||||
|
push {r4, r5}
|
||||||
|
nrf_atfifo_rspace_req_repeat
|
||||||
|
/* Load tail tag and set memory monitor !!! R2 - old tail !!! */
|
||||||
|
ldrex r2, [r0, #__cpp(offsetof(nrf_atfifo_t, head))]
|
||||||
|
/* Extract read position !!! R3 !!! */
|
||||||
|
uxth r3, r2, ror #16
|
||||||
|
|
||||||
|
/* Check if we have any data !!! R4 used temporary !!! */
|
||||||
|
ldrh r4, [r0, #__cpp(offsetof(nrf_atfifo_t, tail) + offsetof(nrf_atfifo_postag_pos_t, rd))]
|
||||||
|
cmp r3, r4
|
||||||
|
ittt eq
|
||||||
|
clrexeq
|
||||||
|
moveq r0, #__cpp(false)
|
||||||
|
beq nrf_atfifo_rspace_req_exit
|
||||||
|
|
||||||
|
/* Increment address with overload support !!! R4 used temporary !!! */
|
||||||
|
ldrh r4, [r0, #__cpp(offsetof(nrf_atfifo_t, item_size))]
|
||||||
|
add r3, r4
|
||||||
|
ldrh r4, [r0, #__cpp(offsetof(nrf_atfifo_t, buf_size))]
|
||||||
|
cmp r3, r4
|
||||||
|
it hs
|
||||||
|
subhs r3, r3, r4
|
||||||
|
|
||||||
|
/* Pack everything back !!! R3 - new tail !!! */
|
||||||
|
/* Copy lower byte from old_head, and higher byte is a value from write_pos */
|
||||||
|
pkhbt r3, r2, r3, lsl #16
|
||||||
|
|
||||||
|
/* Store new value clearing memory monitor !!! R4 used temporary !!! */
|
||||||
|
strex r4, r3, [r0, #__cpp(offsetof(nrf_atfifo_t, head))]
|
||||||
|
cmp r4, #0
|
||||||
|
bne nrf_atfifo_rspace_req_repeat
|
||||||
|
|
||||||
|
/* Return true */
|
||||||
|
mov r0, #__cpp(true)
|
||||||
|
nrf_atfifo_rspace_req_exit
|
||||||
|
/* Save old head */
|
||||||
|
str r2, [r1]
|
||||||
|
pop {r4, r5}
|
||||||
|
bx lr
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__ASM void nrf_atfifo_rspace_close(nrf_atfifo_t * const p_fifo)
|
||||||
|
{
|
||||||
|
/* Registry usage:
|
||||||
|
* R0 - p_fifo
|
||||||
|
* R1 - internal temporary register
|
||||||
|
* R2 - new_tail
|
||||||
|
*/
|
||||||
|
nrf_atfifo_rspace_close_repeat
|
||||||
|
ldrex r2, [r0, #__cpp(offsetof(nrf_atfifo_t, head))]
|
||||||
|
/* Copy from higher byte to lower */
|
||||||
|
pkhtb r2, r2, r2, asr #16
|
||||||
|
|
||||||
|
strex r1, r2, [r0, #__cpp(offsetof(nrf_atfifo_t, head))]
|
||||||
|
cmp r1, #0
|
||||||
|
bne nrf_atfifo_rspace_close_repeat
|
||||||
|
bx lr
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__ASM bool nrf_atfifo_space_clear(nrf_atfifo_t * const p_fifo)
|
||||||
|
{
|
||||||
|
/* Registry usage:
|
||||||
|
* R0 - p_fifo as input, bool output after
|
||||||
|
* R1 - tail, rd pointer, new_head
|
||||||
|
* R2 - head_old, destroyed when creating new_head
|
||||||
|
* R3 - p_fifo - copy
|
||||||
|
*/
|
||||||
|
mov r3, r0
|
||||||
|
nrf_atfifo_space_clear_repeat
|
||||||
|
/* Load old head in !!! R2 register !!! and read pointer of tail in !!! R1 register !!! */
|
||||||
|
ldrex r2, [r3, #__cpp(offsetof(nrf_atfifo_t, head))]
|
||||||
|
ldrh r1, [r3, #__cpp(offsetof(nrf_atfifo_t, tail) + offsetof(nrf_atfifo_postag_pos_t, rd))]
|
||||||
|
cmp r2, r2, ror #16
|
||||||
|
/* Return false as default */
|
||||||
|
mov r0, #__cpp(false)
|
||||||
|
/* Create new head in !!! R1 register !!! Data in !!! R2 register broken !!! */
|
||||||
|
itett ne
|
||||||
|
uxthne r2, r2
|
||||||
|
orreq r1, r1, r1, lsl #16
|
||||||
|
orrne r1, r2, r1, lsl #16
|
||||||
|
|
||||||
|
/* Skip header test */
|
||||||
|
bne nrf_atfifo_space_clear_head_test_skip
|
||||||
|
|
||||||
|
/* Load whole tail and test it !!! R2 used !!! */
|
||||||
|
ldr r2, [r3, #__cpp(offsetof(nrf_atfifo_t, tail))]
|
||||||
|
cmp r2, r2, ror #16
|
||||||
|
/* Return true if equal */
|
||||||
|
it eq
|
||||||
|
moveq r0, #__cpp(true)
|
||||||
|
|
||||||
|
nrf_atfifo_space_clear_head_test_skip
|
||||||
|
/* Store and test if success !!! R2 used temporary !!! */
|
||||||
|
strex r2, r1, [r3, #__cpp(offsetof(nrf_atfifo_t, head))]
|
||||||
|
cmp r2, #0
|
||||||
|
bne nrf_atfifo_space_clear_repeat
|
||||||
|
bx lr
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined ( __ICCARM__ ) || defined ( __GNUC__ )
|
||||||
|
|
||||||
|
bool nrf_atfifo_wspace_req(nrf_atfifo_t * const p_fifo, nrf_atfifo_postag_t * const p_old_tail)
|
||||||
|
{
|
||||||
|
volatile bool ret;
|
||||||
|
volatile uint32_t old_tail;
|
||||||
|
uint32_t new_tail;
|
||||||
|
uint32_t temp;
|
||||||
|
|
||||||
|
__ASM volatile(
|
||||||
|
/* For more comments see Keil version above */
|
||||||
|
"1: \n"
|
||||||
|
" ldrex %[old_tail], [%[p_fifo], %[offset_tail]] \n"
|
||||||
|
" uxth %[new_tail], %[old_tail] \n"
|
||||||
|
" \n"
|
||||||
|
" ldrh %[temp], [%[p_fifo], %[offset_item_size]] \n"
|
||||||
|
" add %[new_tail], %[temp] \n"
|
||||||
|
" ldrh %[temp], [%[p_fifo], %[offset_buf_size]] \n"
|
||||||
|
" cmp %[new_tail], %[temp] \n"
|
||||||
|
" it hs \n"
|
||||||
|
" subhs %[new_tail], %[new_tail], %[temp] \n"
|
||||||
|
" \n"
|
||||||
|
" ldrh %[temp], [%[p_fifo], %[offset_head_wr]] \n"
|
||||||
|
" cmp %[new_tail], %[temp] \n"
|
||||||
|
" ittt eq \n"
|
||||||
|
" clrexeq \n"
|
||||||
|
" moveq %[ret], %[false_val] \n"
|
||||||
|
" beq.n 2f \n"
|
||||||
|
" \n"
|
||||||
|
" pkhbt %[new_tail], %[new_tail], %[old_tail] \n"
|
||||||
|
" \n"
|
||||||
|
" strex %[temp], %[new_tail], [%[p_fifo], %[offset_tail]] \n"
|
||||||
|
" cmp %[temp], #0 \n"
|
||||||
|
" bne.n 1b \n"
|
||||||
|
" \n"
|
||||||
|
" mov %[ret], %[true_val] \n"
|
||||||
|
"2: \n"
|
||||||
|
: /* Output operands */
|
||||||
|
[ret] "=r"(ret),
|
||||||
|
[temp] "=&r"(temp),
|
||||||
|
[old_tail]"=&r"(old_tail),
|
||||||
|
[new_tail]"=&r"(new_tail)
|
||||||
|
: /* Input operands */
|
||||||
|
[p_fifo] "r"(p_fifo),
|
||||||
|
[offset_tail] "J"(offsetof(nrf_atfifo_t, tail)),
|
||||||
|
[offset_head_wr] "J"(offsetof(nrf_atfifo_t, head) + offsetof(nrf_atfifo_postag_pos_t, wr)),
|
||||||
|
[offset_item_size]"J"(offsetof(nrf_atfifo_t, item_size)),
|
||||||
|
[offset_buf_size] "J"(offsetof(nrf_atfifo_t, buf_size)),
|
||||||
|
[true_val] "I"(true),
|
||||||
|
[false_val] "I"(false)
|
||||||
|
: /* Clobbers */
|
||||||
|
"cc");
|
||||||
|
|
||||||
|
p_old_tail->tag = old_tail;
|
||||||
|
UNUSED_VARIABLE(new_tail);
|
||||||
|
UNUSED_VARIABLE(temp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void nrf_atfifo_wspace_close(nrf_atfifo_t * const p_fifo)
|
||||||
|
{
|
||||||
|
uint32_t temp;
|
||||||
|
uint32_t new_tail;
|
||||||
|
|
||||||
|
__ASM volatile(
|
||||||
|
/* For more comments see Keil version above */
|
||||||
|
"1: \n"
|
||||||
|
" ldrex %[new_tail], [%[p_fifo], %[offset_tail]] \n"
|
||||||
|
" pkhbt %[new_tail],%[new_tail], %[new_tail], lsl #16 \n"
|
||||||
|
" \n"
|
||||||
|
" strex %[temp], %[new_tail], [%[p_fifo], %[offset_tail]] \n"
|
||||||
|
" cmp %[temp], #0 \n"
|
||||||
|
" bne.n 1b \n"
|
||||||
|
: /* Output operands */
|
||||||
|
[temp] "=&r"(temp),
|
||||||
|
[new_tail] "=&r"(new_tail)
|
||||||
|
: /* Input operands */
|
||||||
|
[p_fifo] "r"(p_fifo),
|
||||||
|
[offset_tail] "J"(offsetof(nrf_atfifo_t, tail))
|
||||||
|
: /* Clobbers */
|
||||||
|
"cc");
|
||||||
|
|
||||||
|
UNUSED_VARIABLE(temp);
|
||||||
|
UNUSED_VARIABLE(new_tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool nrf_atfifo_rspace_req(nrf_atfifo_t * const p_fifo, nrf_atfifo_postag_t * const p_old_head)
|
||||||
|
{
|
||||||
|
volatile bool ret;
|
||||||
|
volatile uint32_t old_head;
|
||||||
|
uint32_t new_head;
|
||||||
|
uint32_t temp;
|
||||||
|
|
||||||
|
__ASM volatile(
|
||||||
|
/* For more comments see Keil version above */
|
||||||
|
"1: \n"
|
||||||
|
" ldrex %[old_head], [%[p_fifo], %[offset_head]] \n"
|
||||||
|
" uxth %[new_head], %[old_head], ror #16 \n"
|
||||||
|
" \n"
|
||||||
|
" ldrh %[temp], [%[p_fifo], %[offset_tail_rd]] \n"
|
||||||
|
" cmp %[new_head], %[temp] \n"
|
||||||
|
" ittt eq \n"
|
||||||
|
" clrexeq \n"
|
||||||
|
" moveq %[ret], %[false_val] \n"
|
||||||
|
" beq.n 2f \n"
|
||||||
|
" \n"
|
||||||
|
" ldrh %[temp], [%[p_fifo], %[offset_item_size]] \n"
|
||||||
|
" add %[new_head], %[temp] \n"
|
||||||
|
" ldrh %[temp], [%[p_fifo], %[offset_buf_size]] \n"
|
||||||
|
" cmp %[new_head], %[temp] \n"
|
||||||
|
" it hs \n"
|
||||||
|
" subhs %[new_head], %[new_head], %[temp] \n"
|
||||||
|
" \n"
|
||||||
|
" pkhbt %[new_head], %[old_head], %[new_head], lsl #16 \n"
|
||||||
|
" \n"
|
||||||
|
" strex %[temp], %[new_head], [%[p_fifo], %[offset_head]] \n"
|
||||||
|
" cmp %[temp], #0 \n"
|
||||||
|
" bne.n 1b \n"
|
||||||
|
" \n"
|
||||||
|
" mov %[ret], %[true_val] \n"
|
||||||
|
"2: \n"
|
||||||
|
: /* Output operands */
|
||||||
|
[ret] "=r"(ret),
|
||||||
|
[temp] "=&r"(temp),
|
||||||
|
[old_head]"=&r"(old_head),
|
||||||
|
[new_head]"=&r"(new_head)
|
||||||
|
: /* Input operands */
|
||||||
|
[p_fifo] "r"(p_fifo),
|
||||||
|
[offset_head] "J"(offsetof(nrf_atfifo_t, head)),
|
||||||
|
[offset_tail_rd] "J"(offsetof(nrf_atfifo_t, tail) + offsetof(nrf_atfifo_postag_pos_t, rd)),
|
||||||
|
[offset_item_size]"J"(offsetof(nrf_atfifo_t, item_size)),
|
||||||
|
[offset_buf_size] "J"(offsetof(nrf_atfifo_t, buf_size)),
|
||||||
|
[true_val] "I"(true),
|
||||||
|
[false_val] "I"(false)
|
||||||
|
: /* Clobbers */
|
||||||
|
"cc");
|
||||||
|
|
||||||
|
p_old_head->tag = old_head;
|
||||||
|
UNUSED_VARIABLE(new_head);
|
||||||
|
UNUSED_VARIABLE(temp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void nrf_atfifo_rspace_close(nrf_atfifo_t * const p_fifo)
|
||||||
|
{
|
||||||
|
uint32_t temp;
|
||||||
|
uint32_t new_head;
|
||||||
|
|
||||||
|
__ASM volatile(
|
||||||
|
/* For more comments see Keil version above */
|
||||||
|
"1: \n"
|
||||||
|
" ldrex %[new_head], [%[p_fifo], %[offset_head]] \n"
|
||||||
|
" pkhtb %[new_head],%[new_head], %[new_head], asr #16 \n"
|
||||||
|
" \n"
|
||||||
|
" strex %[temp], %[new_head], [%[p_fifo], %[offset_head]] \n"
|
||||||
|
" cmp %[temp], #0 \n"
|
||||||
|
" bne.n 1b \n"
|
||||||
|
: /* Output operands */
|
||||||
|
[temp] "=&r"(temp),
|
||||||
|
[new_head] "=&r"(new_head)
|
||||||
|
: /* Input operands */
|
||||||
|
[p_fifo] "r"(p_fifo),
|
||||||
|
[offset_head] "J"(offsetof(nrf_atfifo_t, head))
|
||||||
|
: /* Clobbers */
|
||||||
|
"cc");
|
||||||
|
|
||||||
|
UNUSED_VARIABLE(temp);
|
||||||
|
UNUSED_VARIABLE(new_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool nrf_atfifo_space_clear(nrf_atfifo_t * const p_fifo)
|
||||||
|
{
|
||||||
|
volatile bool ret;
|
||||||
|
uint32_t old_head; /* This variable is left broken after assembly code finishes */
|
||||||
|
uint32_t new_head;
|
||||||
|
|
||||||
|
__ASM volatile(
|
||||||
|
"1: \n"
|
||||||
|
" ldrex %[old_head], [%[p_fifo], %[offset_head]] \n"
|
||||||
|
" ldrh %[new_head], [%[p_fifo], %[offset_tail_rd]] \n"
|
||||||
|
" cmp %[old_head], %[old_head], ror #16 \n"
|
||||||
|
" \n"
|
||||||
|
" mov %[ret], %[false_val] \n"
|
||||||
|
" \n"
|
||||||
|
" itett ne \n"
|
||||||
|
" uxthne %[old_head], %[old_head] \n"
|
||||||
|
" orreq %[new_head], %[new_head], %[new_head], lsl #16 \n"
|
||||||
|
" orrne %[new_head], %[old_head], %[new_head], lsl #16 \n"
|
||||||
|
" \n"
|
||||||
|
" bne.n 2f \n"
|
||||||
|
" \n"
|
||||||
|
" ldr %[old_head], [%[p_fifo], %[offset_tail]] \n"
|
||||||
|
" cmp %[old_head], %[old_head], ror #16 \n"
|
||||||
|
" it eq \n"
|
||||||
|
" moveq %[ret], %[true_val] \n"
|
||||||
|
" \n"
|
||||||
|
"2: \n"
|
||||||
|
" strex %[old_head], %[new_head], [%[p_fifo], %[offset_head]] \n"
|
||||||
|
" cmp %[old_head], #0 \n"
|
||||||
|
" bne.n 1b \n"
|
||||||
|
: /* Output operands */
|
||||||
|
[ret] "=&r"(ret),
|
||||||
|
[old_head] "=&r"(old_head),
|
||||||
|
[new_head] "=&r"(new_head)
|
||||||
|
: /* Input operands */
|
||||||
|
[p_fifo] "r"(p_fifo),
|
||||||
|
[offset_head] "J"(offsetof(nrf_atfifo_t, head)),
|
||||||
|
[offset_tail] "J"(offsetof(nrf_atfifo_t, tail)),
|
||||||
|
[offset_tail_rd] "J"(offsetof(nrf_atfifo_t, tail) + offsetof(nrf_atfifo_postag_pos_t, rd)),
|
||||||
|
[true_val] "I"(true),
|
||||||
|
[false_val] "I"(false)
|
||||||
|
: /* Clobbers */
|
||||||
|
"cc");
|
||||||
|
|
||||||
|
UNUSED_VARIABLE(old_head);
|
||||||
|
UNUSED_VARIABLE(new_head);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error Unsupported compiler
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* NRF_ATFIFO_INTERNAL_H__ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,700 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015 - 2017, Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||||
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||||
|
* such product, must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or other
|
||||||
|
* materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* 4. This software, with or without modification, must only be used with a
|
||||||
|
* Nordic Semiconductor ASA integrated circuit.
|
||||||
|
*
|
||||||
|
* 5. Any software provided in binary form under this license must not be reverse
|
||||||
|
* engineered, decompiled, modified and/or disassembled.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||||
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef FDS_H__
|
||||||
|
#define FDS_H__
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup fds Flash Data Storage
|
||||||
|
* @ingroup app_common
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @brief Flash Data Storage (FDS).
|
||||||
|
*
|
||||||
|
* @details Flash Data Storage is a minimalistic, record-oriented file system for the on-chip
|
||||||
|
* flash. Files are stored as a collection of records of variable length. FDS supports
|
||||||
|
* synchronous read operations and asynchronous write operations (write, update,
|
||||||
|
* and delete). FDS can be used from multiple threads.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "sdk_errors.h"
|
||||||
|
#include "app_util_platform.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Invalid file ID.
|
||||||
|
*
|
||||||
|
* This value must not be used as a file ID by the application.
|
||||||
|
*/
|
||||||
|
#define FDS_FILE_ID_INVALID (0xFFFF)
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Record key for deleted records.
|
||||||
|
*
|
||||||
|
* This key is used to flag a record as "dirty", which means that it should be removed during
|
||||||
|
* the next garbage collection. This value must not be used as a record key by the application.
|
||||||
|
*/
|
||||||
|
#define FDS_RECORD_KEY_DIRTY (0x0000)
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief FDS return values.
|
||||||
|
*/
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
FDS_SUCCESS = NRF_SUCCESS, //!< The operation completed successfully.
|
||||||
|
FDS_ERR_OPERATION_TIMEOUT, //!< Error. The operation timed out.
|
||||||
|
FDS_ERR_NOT_INITIALIZED, //!< Error. The module has not been initialized.
|
||||||
|
FDS_ERR_UNALIGNED_ADDR, //!< Error. The input data is not aligned to a word boundary.
|
||||||
|
FDS_ERR_INVALID_ARG, //!< Error. The parameter contains invalid data.
|
||||||
|
FDS_ERR_NULL_ARG, //!< Error. The parameter is NULL.
|
||||||
|
FDS_ERR_NO_OPEN_RECORDS, //!< Error. The record is not open, so it cannot be closed.
|
||||||
|
FDS_ERR_NO_SPACE_IN_FLASH, //!< Error. There is no space in flash memory.
|
||||||
|
FDS_ERR_NO_SPACE_IN_QUEUES, //!< Error. There is no space in the internal queues.
|
||||||
|
FDS_ERR_RECORD_TOO_LARGE, //!< Error. The record exceeds the maximum allowed size.
|
||||||
|
FDS_ERR_NOT_FOUND, //!< Error. The record was not found.
|
||||||
|
FDS_ERR_NO_PAGES, //!< Error. No flash pages are available.
|
||||||
|
FDS_ERR_USER_LIMIT_REACHED, //!< Error. The maximum number of users has been reached.
|
||||||
|
FDS_ERR_CRC_CHECK_FAILED, //!< Error. The CRC check failed.
|
||||||
|
FDS_ERR_BUSY, //!< Error. The underlying flash subsystem was busy.
|
||||||
|
FDS_ERR_INTERNAL, //!< Error. An internal error occurred.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief The record metadata as stored in flash.
|
||||||
|
* @warning Do not edit or reorder the fields in this structure.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t record_key; //!< The record key.
|
||||||
|
uint16_t length_words; //!< The length of the record data (in 4-byte words).
|
||||||
|
uint16_t file_id; //!< The ID of the file that the record belongs to.
|
||||||
|
uint16_t crc16; //!< CRC16-CCITT check value.
|
||||||
|
/* The CRC is calculated over the entire record as stored in flash,
|
||||||
|
* including the record metadata except the CRC field itself.
|
||||||
|
*/
|
||||||
|
uint32_t record_id; //!< The unique record ID (32 bits).
|
||||||
|
} fds_header_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief The record descriptor structure that is used to manipulate records.
|
||||||
|
*
|
||||||
|
* This structure is used by the FDS module. You must provide the descriptor to the module when
|
||||||
|
* you manipulate existing records. However, you should never modify it or use any of its fields.
|
||||||
|
*
|
||||||
|
* @note Never reuse the same descriptor for different records.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t record_id; //!< The unique record ID.
|
||||||
|
uint32_t const * p_record; //!< The last known location of the record in flash.
|
||||||
|
uint16_t gc_run_count; //!< Number of times garbage collection has been run.
|
||||||
|
bool record_is_open; //!< Whether the record is currently open.
|
||||||
|
} fds_record_desc_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Structure that can be used to read the contents of a record stored in flash.
|
||||||
|
*
|
||||||
|
* This structure does not reflect the physical layout of a record in flash, but it points
|
||||||
|
* to the locations where the record header (metadata) and the record data are stored.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
fds_header_t const * p_header; //!< Location of the record header in flash.
|
||||||
|
void const * p_data; //!< Location of the record data in flash.
|
||||||
|
} fds_flash_record_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief A record to be written to flash. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t file_id; //!< The ID of the file that the record belongs to.
|
||||||
|
uint16_t key; //!< The record key.
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
void const * p_data;
|
||||||
|
uint32_t length_words;
|
||||||
|
} data;
|
||||||
|
} fds_record_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief A token to a reserved space in flash, created by @ref fds_reserve.
|
||||||
|
*
|
||||||
|
* This token can be used to write the record in the reserved space (@ref fds_record_write_reserved)
|
||||||
|
* or to cancel the reservation (@ref fds_reserve_cancel).
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t page; //!< The logical ID of the page where space was reserved.
|
||||||
|
uint16_t length_words; //!< The amount of space reserved (in 4-byte words).
|
||||||
|
} fds_reserve_token_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief A token to keep information about the progress of @ref fds_record_find,
|
||||||
|
* @ref fds_record_find_by_key, and @ref fds_record_find_in_file.
|
||||||
|
*
|
||||||
|
* @note Always zero-initialize the token before using it for the first time.
|
||||||
|
* @note Never reuse the same token to search for different records.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t const * p_addr;
|
||||||
|
uint16_t page;
|
||||||
|
} fds_find_token_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief FDS event IDs.
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
FDS_EVT_INIT, //!< Event for @ref fds_init.
|
||||||
|
FDS_EVT_WRITE, //!< Event for @ref fds_record_write and @ref fds_record_write_reserved.
|
||||||
|
FDS_EVT_UPDATE, //!< Event for @ref fds_record_update.
|
||||||
|
FDS_EVT_DEL_RECORD, //!< Event for @ref fds_record_delete.
|
||||||
|
FDS_EVT_DEL_FILE, //!< Event for @ref fds_file_delete.
|
||||||
|
FDS_EVT_GC //!< Event for @ref fds_gc.
|
||||||
|
} fds_evt_id_t;
|
||||||
|
|
||||||
|
|
||||||
|
ANON_UNIONS_ENABLE
|
||||||
|
|
||||||
|
/**@brief An FDS event. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
fds_evt_id_t id; //!< The event ID. See @ref fds_evt_id_t.
|
||||||
|
ret_code_t result; //!< The result of the operation related to this event.
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint32_t record_id;
|
||||||
|
uint16_t file_id;
|
||||||
|
uint16_t record_key;
|
||||||
|
bool is_record_updated;
|
||||||
|
} write; //!< Information for @ref FDS_EVT_WRITE and @ref FDS_EVT_UPDATE events.
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint32_t record_id;
|
||||||
|
uint16_t file_id;
|
||||||
|
uint16_t record_key;
|
||||||
|
} del; //!< Information for @ref FDS_EVT_DEL_RECORD and @ref FDS_EVT_DEL_FILE events.
|
||||||
|
};
|
||||||
|
} fds_evt_t;
|
||||||
|
|
||||||
|
ANON_UNIONS_DISABLE
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief File system statistics. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t pages_available; //!< The number of pages available.
|
||||||
|
uint16_t open_records; //!< The number of open records.
|
||||||
|
uint16_t valid_records; //!< The number of valid records.
|
||||||
|
uint16_t dirty_records; //!< The number of deleted ("dirty") records.
|
||||||
|
uint16_t words_reserved; //!< The number of words reserved by @ref fds_reserve().
|
||||||
|
|
||||||
|
/**@brief The number of words written to flash, including those reserved for future writes. */
|
||||||
|
uint16_t words_used;
|
||||||
|
|
||||||
|
/**@brief The largest number of free contiguous words in the file system.
|
||||||
|
*
|
||||||
|
* This number indicates the largest record that can be stored by FDS.
|
||||||
|
* It takes into account all reservations for future writes.
|
||||||
|
*/
|
||||||
|
uint16_t largest_contig;
|
||||||
|
|
||||||
|
/**@brief The largest number of words that can be reclaimed by garbage collection.
|
||||||
|
*
|
||||||
|
* The actual amount of space freed by garbage collection might be less than this value if
|
||||||
|
* records are open while garbage collection is run.
|
||||||
|
*/
|
||||||
|
uint16_t freeable_words;
|
||||||
|
|
||||||
|
/**@brief Filesystem corruption has been detected.
|
||||||
|
*
|
||||||
|
* One or more corrupted records were detected. FDS will heal the filesystem automatically
|
||||||
|
* next time garbage collection is run, but some data may be lost.
|
||||||
|
*
|
||||||
|
* @note: This flag is unrelated to CRC failures.
|
||||||
|
*/
|
||||||
|
bool corruption;
|
||||||
|
} fds_stat_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief FDS event handler function prototype.
|
||||||
|
*
|
||||||
|
* @param p_evt The event.
|
||||||
|
*/
|
||||||
|
typedef void (*fds_cb_t)(fds_evt_t const * p_evt);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for registering an FDS event handler.
|
||||||
|
*
|
||||||
|
* The maximum amount of handlers that can be registered can be configured by changing the value
|
||||||
|
* of @ref FDS_MAX_USERS in fds_config.h.
|
||||||
|
*
|
||||||
|
* @param[in] cb The event handler function.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If the event handler was registered successfully.
|
||||||
|
* @retval FDS_ERR_USER_LIMIT_REACHED If the maximum number of registered callbacks is reached.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_register(fds_cb_t cb);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for initializing the module.
|
||||||
|
*
|
||||||
|
* This function initializes the module and installs the file system (unless it is installed
|
||||||
|
* already).
|
||||||
|
*
|
||||||
|
* This function is asynchronous. Completion is reported through an event. Make sure to call
|
||||||
|
* @ref fds_register before calling @ref fds_init so that you receive the completion event.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If the operation was queued successfully.
|
||||||
|
* @retval FDS_ERR_NO_PAGES If there is no space available in flash memory to install the
|
||||||
|
* file system.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_init(void);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for writing a record to flash.
|
||||||
|
*
|
||||||
|
* There are no restrictions on the file ID and the record key, except that the record key must be
|
||||||
|
* different from @ref FDS_RECORD_KEY_DIRTY and the file ID must be different from
|
||||||
|
* @ref FDS_FILE_ID_INVALID. In particular, no restrictions are made regarding the uniqueness of
|
||||||
|
* the file ID or the record key. All records with the same file ID are grouped into one file.
|
||||||
|
* If no file with the specified ID exists, it is created. There can be multiple records with the
|
||||||
|
* same record key in a file.
|
||||||
|
*
|
||||||
|
* Some modules need exclusive use of certain file IDs and record keys.
|
||||||
|
* See @ref lib_fds_functionality_keys for details.
|
||||||
|
*
|
||||||
|
* Record data can consist of multiple chunks. The data must be aligned to a 4 byte boundary, and
|
||||||
|
* because it is not buffered internally, it must be kept in memory until the callback for the
|
||||||
|
* operation has been received. The length of the data must not exceed @ref FDS_VIRTUAL_PAGE_SIZE
|
||||||
|
* words minus 14 bytes.
|
||||||
|
*
|
||||||
|
* This function is asynchronous. Completion is reported through an event that is sent to
|
||||||
|
* the registered event handler function.
|
||||||
|
*
|
||||||
|
* @param[out] p_desc The descriptor of the record that was written. Pass NULL if you do not
|
||||||
|
* need the descriptor.
|
||||||
|
* @param[in] p_record The record to be written to flash.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If the operation was queued successfully.
|
||||||
|
* @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If @p p_record is NULL.
|
||||||
|
* @retval FDS_ERR_INVALID_ARG If the file ID or the record key is invalid.
|
||||||
|
* @retval FDS_ERR_UNALIGNED_ADDR If the record data is not aligned to a 4 byte boundary.
|
||||||
|
* @retval FDS_ERR_RECORD_TOO_LARGE If the record data exceeds the maximum length.
|
||||||
|
* @retval FDS_ERR_NO_SPACE_IN_QUEUES If the operation queue is full or there are more record
|
||||||
|
* chunks than can be buffered.
|
||||||
|
* @retval FDS_ERR_NO_SPACE_IN_FLASH If there is not enough free space in flash to store the
|
||||||
|
* record.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_record_write(fds_record_desc_t * p_desc,
|
||||||
|
fds_record_t const * p_record);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for reserving space in flash.
|
||||||
|
*
|
||||||
|
* This function can be used to reserve space in flash memory. To write a record into the reserved
|
||||||
|
* space, use @ref fds_record_write_reserved. Alternatively, use @ref fds_reserve_cancel to cancel
|
||||||
|
* a reservation.
|
||||||
|
*
|
||||||
|
* Note that this function does not write any data to flash.
|
||||||
|
*
|
||||||
|
* @param[out] p_token A token that can be used to write a record in the reserved space or
|
||||||
|
* cancel the reservation.
|
||||||
|
* @param[in] length_words The length of the record data (in 4-byte words).
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If the flash space was reserved successfully.
|
||||||
|
* @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If @p p_token is NULL instead of a valid token address.
|
||||||
|
* @retval FDS_ERR_RECORD_TOO_LARGE If the record length exceeds the maximum length.
|
||||||
|
* @retval FDS_ERR_NO_SPACE_IN_FLASH If there is not enough free space in flash to store the
|
||||||
|
* record.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_reserve(fds_reserve_token_t * p_token, uint16_t length_words);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for canceling an @ref fds_reserve operation.
|
||||||
|
*
|
||||||
|
* @param[in] p_token The token that identifies the reservation, produced by @ref fds_reserve.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If the reservation was canceled.
|
||||||
|
* @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If @p p_token is NULL instead of a valid token address.
|
||||||
|
* @retval FDS_ERR_INVALID_ARG If @p p_token contains invalid data.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_reserve_cancel(fds_reserve_token_t * p_token);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for writing a record to a space in flash that was reserved using
|
||||||
|
* @ref fds_reserve.
|
||||||
|
*
|
||||||
|
* There are no restrictions on the file ID and the record key, except that the record key must be
|
||||||
|
* different from @ref FDS_RECORD_KEY_DIRTY and the file ID must be different from
|
||||||
|
* @ref FDS_FILE_ID_INVALID. In particular, no restrictions are made regarding the uniqueness of
|
||||||
|
* the file ID or the record key. All records with the same file ID are grouped into one file.
|
||||||
|
* If no file with the specified ID exists, it is created. There can be multiple records with the
|
||||||
|
* same record key in a file.
|
||||||
|
*
|
||||||
|
* Record data can consist of multiple chunks. The data must be aligned to a 4 byte boundary, and
|
||||||
|
* because it is not buffered internally, it must be kept in memory until the callback for the
|
||||||
|
* operation has been received. The length of the data must not exceed @ref FDS_VIRTUAL_PAGE_SIZE
|
||||||
|
* words minus 14 bytes.
|
||||||
|
*
|
||||||
|
* This function is asynchronous. Completion is reported through an event that is sent to the
|
||||||
|
* registered event handler function.
|
||||||
|
*
|
||||||
|
* @note
|
||||||
|
* This function behaves similarly to @ref fds_record_write, with the exception that it never
|
||||||
|
* fails with the error @ref FDS_ERR_NO_SPACE_IN_FLASH.
|
||||||
|
*
|
||||||
|
* @param[out] p_desc The descriptor of the record that was written. Pass NULL if you do not
|
||||||
|
* need the descriptor.
|
||||||
|
* @param[in] p_record The record to be written to flash.
|
||||||
|
* @param[in] p_token The token that identifies the space reserved in flash.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If the operation was queued successfully.
|
||||||
|
* @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If @p p_token is NULL instead of a valid token address.
|
||||||
|
* @retval FDS_ERR_INVALID_ARG If the file ID or the record key is invalid.
|
||||||
|
* @retval FDS_ERR_UNALIGNED_ADDR If the record data is not aligned to a 4 byte boundary.
|
||||||
|
* @retval FDS_ERR_RECORD_TOO_LARGE If the record data exceeds the maximum length.
|
||||||
|
* @retval FDS_ERR_NO_SPACE_IN_QUEUES If the operation queue is full or there are more record
|
||||||
|
* chunks than can be buffered.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_record_write_reserved(fds_record_desc_t * p_desc,
|
||||||
|
fds_record_t const * p_record,
|
||||||
|
fds_reserve_token_t const * p_token);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for deleting a record.
|
||||||
|
*
|
||||||
|
* Deleted records cannot be located using @ref fds_record_find, @ref fds_record_find_by_key, or
|
||||||
|
* @ref fds_record_find_in_file. Additionally, they can no longer be opened using
|
||||||
|
* @ref fds_record_open.
|
||||||
|
*
|
||||||
|
* Note that deleting a record does not free the space it occupies in flash memory.
|
||||||
|
* To reclaim flash space used by deleted records, call @ref fds_gc to run garbage collection.
|
||||||
|
*
|
||||||
|
* This function is asynchronous. Completion is reported through an event that is sent to the
|
||||||
|
* registered event handler function.
|
||||||
|
*
|
||||||
|
* @param[in] p_desc The descriptor of the record that should be deleted.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If the operation was queued successfully.
|
||||||
|
* @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If the specified record descriptor @p p_desc is NULL.
|
||||||
|
* @retval FDS_ERR_NO_SPACE_IN_QUEUES If the operation queue is full.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_record_delete(fds_record_desc_t * p_desc);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for deleting all records in a file.
|
||||||
|
*
|
||||||
|
* This function deletes a file, including all its records. Deleted records cannot be located
|
||||||
|
* using @ref fds_record_find, @ref fds_record_find_by_key, or @ref fds_record_find_in_file.
|
||||||
|
* Additionally, they can no longer be opened using @ref fds_record_open.
|
||||||
|
*
|
||||||
|
* Note that deleting records does not free the space they occupy in flash memory.
|
||||||
|
* To reclaim flash space used by deleted records, call @ref fds_gc to run garbage collection.
|
||||||
|
*
|
||||||
|
* This function is asynchronous. Completion is reported through an event that is sent to the
|
||||||
|
* registered event handler function.
|
||||||
|
*
|
||||||
|
* @param[in] file_id The ID of the file to be deleted.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If the operation was queued successfully.
|
||||||
|
* @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized.
|
||||||
|
* @retval FDS_ERR_INVALID_ARG If the specified @p file_id is invalid.
|
||||||
|
* @retval FDS_ERR_NO_SPACE_IN_QUEUES If the operation queue is full.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_file_delete(uint16_t file_id);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for updating a record.
|
||||||
|
*
|
||||||
|
* Updating a record first writes a new record (@p p_record) to flash and then deletes the
|
||||||
|
* old record (identified by @p p_desc).
|
||||||
|
*
|
||||||
|
* There are no restrictions on the file ID and the record key, except that the record key must be
|
||||||
|
* different from @ref FDS_RECORD_KEY_DIRTY and the file ID must be different from
|
||||||
|
* @ref FDS_FILE_ID_INVALID. In particular, no restrictions are made regarding the uniqueness of
|
||||||
|
* the file ID or the record key. All records with the same file ID are grouped into one file.
|
||||||
|
* If no file with the specified ID exists, it is created. There can be multiple records with the
|
||||||
|
* same record key in a file.
|
||||||
|
*
|
||||||
|
* Record data can consist of multiple chunks. The data must be aligned to a 4 byte boundary, and
|
||||||
|
* because it is not buffered internally, it must be kept in memory until the callback for the
|
||||||
|
* operation has been received. The length of the data must not exceed @ref FDS_VIRTUAL_PAGE_SIZE
|
||||||
|
* words minus 14 bytes.
|
||||||
|
*
|
||||||
|
* This function is asynchronous. Completion is reported through an event that is sent to the
|
||||||
|
* registered event handler function.
|
||||||
|
*
|
||||||
|
* @param[in, out] p_desc The descriptor of the record to update. When the function
|
||||||
|
* returns with FDS_SUCCESS, this parameter contains the
|
||||||
|
* descriptor of the newly written record.
|
||||||
|
* @param[in] p_record The updated record to be written to flash.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If the operation was queued successfully.
|
||||||
|
* @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized.
|
||||||
|
* @retval FDS_ERR_INVALID_ARG If the file ID or the record key is invalid.
|
||||||
|
* @retval FDS_ERR_UNALIGNED_ADDR If the record data is not aligned to a 4 byte boundary.
|
||||||
|
* @retval FDS_ERR_RECORD_TOO_LARGE If the record data exceeds the maximum length.
|
||||||
|
* @retval FDS_ERR_NO_SPACE_IN_QUEUES If the operation queue is full or there are more record
|
||||||
|
* chunks than can be buffered.
|
||||||
|
* @retval FDS_ERR_NO_SPACE_IN_FLASH If there is not enough free space in flash to store the
|
||||||
|
* updated record.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_record_update(fds_record_desc_t * p_desc,
|
||||||
|
fds_record_t const * p_record);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for iterating through all records in flash.
|
||||||
|
*
|
||||||
|
* To search for the next record, call the function again and supply the same @ref fds_find_token_t
|
||||||
|
* structure to resume searching from the last record that was found.
|
||||||
|
*
|
||||||
|
* Note that the order with which records are iterated is not defined.
|
||||||
|
*
|
||||||
|
* @param[out] p_desc The descriptor of the record that was found.
|
||||||
|
* @param[out] p_token A token containing information about the progress of the operation.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If a record was found.
|
||||||
|
* @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If @p p_desc or @p p_token is NULL.
|
||||||
|
* @retval FDS_ERR_NOT_FOUND If no matching record was found.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_record_iterate(fds_record_desc_t * p_desc,
|
||||||
|
fds_find_token_t * p_token);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for searching for records with a given record key in a file.
|
||||||
|
*
|
||||||
|
* This function finds the first record in a file that has the given record key. To search for the
|
||||||
|
* next record with the same key in the file, call the function again and supply the same
|
||||||
|
* @ref fds_find_token_t structure to resume searching from the last record that was found.
|
||||||
|
*
|
||||||
|
* @param[in] file_id The file ID.
|
||||||
|
* @param[in] record_key The record key.
|
||||||
|
* @param[out] p_desc The descriptor of the record that was found.
|
||||||
|
* @param[out] p_token A token containing information about the progress of the operation.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If a record was found.
|
||||||
|
* @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If @p p_desc or @p p_token is NULL.
|
||||||
|
* @retval FDS_ERR_NOT_FOUND If no matching record was found.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_record_find(uint16_t file_id,
|
||||||
|
uint16_t record_key,
|
||||||
|
fds_record_desc_t * p_desc,
|
||||||
|
fds_find_token_t * p_token);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for searching for records with a given record key.
|
||||||
|
*
|
||||||
|
* This function finds the first record with a given record key, independent of the file it
|
||||||
|
* belongs to. To search for the next record with the same key, call the function again and supply
|
||||||
|
* the same @ref fds_find_token_t structure to resume searching from the last record that was found.
|
||||||
|
*
|
||||||
|
* @param[in] record_key The record key.
|
||||||
|
* @param[out] p_desc The descriptor of the record that was found.
|
||||||
|
* @param[out] p_token A token containing information about the progress of the operation.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If a record was found.
|
||||||
|
* @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If @p p_desc or @p p_token is NULL.
|
||||||
|
* @retval FDS_ERR_NOT_FOUND If no record with the given key was found.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_record_find_by_key(uint16_t record_key,
|
||||||
|
fds_record_desc_t * p_desc,
|
||||||
|
fds_find_token_t * p_token);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for searching for any record in a file.
|
||||||
|
*
|
||||||
|
* This function finds the first record in a file, independent of its record key.
|
||||||
|
* To search for the next record in the same file, call the function again and supply the same
|
||||||
|
* @ref fds_find_token_t structure to resume searching from the last record that was found.
|
||||||
|
*
|
||||||
|
* @param[in] file_id The file ID.
|
||||||
|
* @param[out] p_desc The descriptor of the record that was found.
|
||||||
|
* @param[out] p_token A token containing information about the progress of the operation.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If a record was found.
|
||||||
|
* @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If @p p_desc or @p p_token is NULL.
|
||||||
|
* @retval FDS_ERR_NOT_FOUND If no matching record was found.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_record_find_in_file(uint16_t file_id,
|
||||||
|
fds_record_desc_t * p_desc,
|
||||||
|
fds_find_token_t * p_token);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for opening a record for reading.
|
||||||
|
*
|
||||||
|
* This function opens a record that is stored in flash, so that it can be read. The function
|
||||||
|
* initializes an @ref fds_flash_record_t structure, which can be used to access the record data as
|
||||||
|
* well as its associated metadata. The pointers provided in the @ref fds_flash_record_t structure
|
||||||
|
* are pointers to flash memory.
|
||||||
|
*
|
||||||
|
* Opening a record with @ref fds_record_open prevents garbage collection to run on the virtual
|
||||||
|
* flash page in which record is stored, so that the contents of the memory pointed by fields in
|
||||||
|
* @ref fds_flash_record_t are guaranteed to remain unmodified as long as the record is kept open.
|
||||||
|
*
|
||||||
|
* When you are done reading a record, call @ref fds_record_close to close it. Garbage collection
|
||||||
|
* can then reclaim space on the virtual page where the record is stored. Note that you must
|
||||||
|
* provide the same descriptor for @ref fds_record_close as you did for this function.
|
||||||
|
*
|
||||||
|
* @param[in] p_desc The descriptor of the record to open.
|
||||||
|
* @param[out] p_flash_record The record, as stored in flash.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If the record was opened successfully.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If @p p_desc or @p p_flash_record is NULL.
|
||||||
|
* @retval FDS_ERR_NOT_FOUND If the record was not found. It might have been deleted, or
|
||||||
|
* it might not have been written yet.
|
||||||
|
* @retval FDS_ERR_CRC_CHECK_FAILED If the CRC check for the record failed.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_record_open(fds_record_desc_t * p_desc,
|
||||||
|
fds_flash_record_t * p_flash_record);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for closing a record.
|
||||||
|
*
|
||||||
|
* Closing a record allows garbage collection to run on the virtual page in which the record is
|
||||||
|
* stored (if no other records remain open on that page). The descriptor passed as an argument
|
||||||
|
* must be the same as the one used to open the record using @ref fds_record_open.
|
||||||
|
*
|
||||||
|
* Note that closing a record does not invalidate its descriptor. You can still supply the
|
||||||
|
* descriptor to all functions that accept a record descriptor as a parameter.
|
||||||
|
*
|
||||||
|
* @param[in] p_desc The descriptor of the record to close.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If the record was closed successfully.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If @p p_desc is NULL.
|
||||||
|
* @retval FDS_ERR_NO_OPEN_RECORDS If the record is not open.
|
||||||
|
* @retval FDS_ERR_NOT_FOUND If the record could not be found.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_record_close(fds_record_desc_t * p_desc);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for running garbage collection.
|
||||||
|
*
|
||||||
|
* Garbage collection reclaims the flash space that is occupied by records that have been deleted,
|
||||||
|
* or that failed to be completely written due to, for example, a power loss.
|
||||||
|
*
|
||||||
|
* This function is asynchronous. Completion is reported through an event that is sent to the
|
||||||
|
* registered event handler function.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If the operation was queued successfully.
|
||||||
|
* @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized.
|
||||||
|
* @retval FDS_ERR_NO_SPACE_IN_QUEUES If the operation queue is full.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_gc(void);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for obtaining a descriptor from a record ID.
|
||||||
|
*
|
||||||
|
* This function can be used to reconstruct a descriptor from a record ID, like the one that is
|
||||||
|
* passed to the callback function.
|
||||||
|
*
|
||||||
|
* @note
|
||||||
|
* This function does not check whether a record with the given record ID exists.
|
||||||
|
* If a non-existing record ID is supplied, the resulting descriptor is invalid and will cause
|
||||||
|
* other functions to fail when it is supplied as parameter.
|
||||||
|
*
|
||||||
|
* @param[out] p_desc The descriptor of the record with the given record ID.
|
||||||
|
* @param[in] record_id The record ID for which a descriptor should be returned.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If a descriptor was returned.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If @p p_desc is NULL.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_descriptor_from_rec_id(fds_record_desc_t * p_desc,
|
||||||
|
uint32_t record_id);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for obtaining a record ID from a record descriptor.
|
||||||
|
*
|
||||||
|
* This function can be used to extract a record ID from a descriptor. For example, you could use
|
||||||
|
* it in the callback function to compare the record ID of an event to the record IDs of the
|
||||||
|
* records for which you have a descriptor.
|
||||||
|
*
|
||||||
|
* @warning
|
||||||
|
* This function does not check whether the record descriptor is valid. If the descriptor is not
|
||||||
|
* initialized or has been tampered with, the resulting record ID might be invalid.
|
||||||
|
*
|
||||||
|
* @param[in] p_desc The descriptor from which the record ID should be extracted.
|
||||||
|
* @param[out] p_record_id The record ID that is contained in the given descriptor.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If a record ID was returned.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If @p p_desc or @p p_record_id is NULL.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_record_id_from_desc(fds_record_desc_t const * p_desc,
|
||||||
|
uint32_t * p_record_id);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for retrieving file system statistics.
|
||||||
|
*
|
||||||
|
* This function retrieves file system statistics, such as the number of open records, the space
|
||||||
|
* that can be reclaimed by garbage collection, and others.
|
||||||
|
*
|
||||||
|
* @param[out] p_stat File system statistics.
|
||||||
|
*
|
||||||
|
* @retval FDS_SUCCESS If the statistics were returned successfully.
|
||||||
|
* @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized.
|
||||||
|
* @retval FDS_ERR_NULL_ARG If @p p_stat is NULL.
|
||||||
|
*/
|
||||||
|
ret_code_t fds_stat(fds_stat_t * p_stat);
|
||||||
|
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // FDS_H__
|
|
@ -0,0 +1,327 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015 - 2017, Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||||
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||||
|
* such product, must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or other
|
||||||
|
* materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* 4. This software, with or without modification, must only be used with a
|
||||||
|
* Nordic Semiconductor ASA integrated circuit.
|
||||||
|
*
|
||||||
|
* 5. Any software provided in binary form under this license must not be reverse
|
||||||
|
* engineered, decompiled, modified and/or disassembled.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||||
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef FDS_INTERNAL_DEFS_H__
|
||||||
|
#define FDS_INTERNAL_DEFS_H__
|
||||||
|
#include "sdk_config.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#if defined (FDS_THREADS)
|
||||||
|
#include "nrf_soc.h"
|
||||||
|
#include "app_util_platform.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FDS_PAGE_TAG_SIZE (2) // Page tag size, in 4-byte words.
|
||||||
|
#define FDS_PAGE_TAG_WORD_0 (0) // Offset of the first word in the page tag from the page address.
|
||||||
|
#define FDS_PAGE_TAG_WORD_1 (1) // Offset of the second word in the page tag from the page address.
|
||||||
|
|
||||||
|
// Page tag constants
|
||||||
|
#define FDS_PAGE_TAG_MAGIC (0xDEADC0DE)
|
||||||
|
#define FDS_PAGE_TAG_SWAP (0xF11E01FF)
|
||||||
|
#define FDS_PAGE_TAG_DATA (0xF11E01FE)
|
||||||
|
|
||||||
|
#define FDS_ERASED_WORD (0xFFFFFFFF)
|
||||||
|
|
||||||
|
#define FDS_OFFSET_TL (0) // Offset of TL from the record base address, in 4-byte words.
|
||||||
|
#define FDS_OFFSET_IC (1) // Offset of IC from the record base address, in 4-byte words.
|
||||||
|
#define FDS_OFFSET_ID (2) // Offset of ID from the record base address, in 4-byte words.
|
||||||
|
#define FDS_OFFSET_DATA (3) // Offset of the data from the record base address, in 4-byte words.
|
||||||
|
|
||||||
|
#define FDS_HEADER_SIZE_TL (1) // Size of the TL part of the header, in 4-byte words.
|
||||||
|
#define FDS_HEADER_SIZE_IC (1) // Size of the IC part of the header, in 4-byte words.
|
||||||
|
#define FDS_HEADER_SIZE_ID (1) // Size of the record ID in the header, in 4-byte words.
|
||||||
|
#define FDS_HEADER_SIZE (3) // Size of the whole header, in 4-byte words.
|
||||||
|
|
||||||
|
#define FDS_OP_EXECUTING (NRF_SUCCESS)
|
||||||
|
#define FDS_OP_COMPLETED (0x1D1D)
|
||||||
|
|
||||||
|
#define NRF_FSTORAGE_NVMC 1
|
||||||
|
#define NRF_FSTORAGE_SD 2
|
||||||
|
|
||||||
|
// The size of a physical page, in 4-byte words.
|
||||||
|
#if defined(NRF51)
|
||||||
|
#define FDS_PHY_PAGE_SIZE (256)
|
||||||
|
#elif defined(NRF52_SERIES)
|
||||||
|
#define FDS_PHY_PAGE_SIZE (1024)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// The number of physical pages to be used. This value is configured indirectly.
|
||||||
|
#define FDS_PHY_PAGES ((FDS_VIRTUAL_PAGES * FDS_VIRTUAL_PAGE_SIZE) / FDS_PHY_PAGE_SIZE)
|
||||||
|
|
||||||
|
// The size of a virtual page, in number of physical pages.
|
||||||
|
#define FDS_PHY_PAGES_IN_VPAGE (FDS_VIRTUAL_PAGE_SIZE / FDS_PHY_PAGE_SIZE)
|
||||||
|
|
||||||
|
// The number of pages available to store data; which is the total minus one (the swap).
|
||||||
|
#define FDS_DATA_PAGES (FDS_VIRTUAL_PAGES - 1)
|
||||||
|
|
||||||
|
// Just a shorter name for the size, in words, of a virtual page.
|
||||||
|
#define FDS_PAGE_SIZE (FDS_VIRTUAL_PAGE_SIZE)
|
||||||
|
|
||||||
|
|
||||||
|
#if (FDS_VIRTUAL_PAGE_SIZE % FDS_PHY_PAGE_SIZE != 0)
|
||||||
|
#error "FDS_VIRTUAL_PAGE_SIZE must be a multiple of the size of a physical page."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (FDS_VIRTUAL_PAGES < 2)
|
||||||
|
#error "FDS requires at least two virtual pages."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Page types.
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
FDS_PAGE_DATA, // Page is ready for storage.
|
||||||
|
FDS_PAGE_SWAP, // Page is reserved for garbage collection.
|
||||||
|
FDS_PAGE_ERASED, // Page is erased.
|
||||||
|
FDS_PAGE_UNDEFINED, // Undefined page type.
|
||||||
|
} fds_page_type_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
FDS_HEADER_VALID, // Valid header.
|
||||||
|
FDS_HEADER_DIRTY, // Header is incomplete, or record has been deleted.
|
||||||
|
FDS_HEADER_CORRUPT // Header contains corrupt information, not related to CRC.
|
||||||
|
} fds_header_status_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
fds_page_type_t page_type; // The page type.
|
||||||
|
uint32_t const * p_addr; // The address of the page.
|
||||||
|
uint16_t write_offset; // The page write offset, in 4-byte words.
|
||||||
|
uint16_t words_reserved; // The amount of words reserved.
|
||||||
|
uint32_t volatile records_open; // The number of open records.
|
||||||
|
bool can_gc; // Indicates that there are some records that have been deleted.
|
||||||
|
} fds_page_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t const * p_addr;
|
||||||
|
uint16_t write_offset;
|
||||||
|
} fds_swap_page_t;
|
||||||
|
|
||||||
|
|
||||||
|
// FDS op-codes.
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
FDS_OP_NONE,
|
||||||
|
FDS_OP_INIT, // Initialize the module.
|
||||||
|
FDS_OP_WRITE, // Write a record to flash.
|
||||||
|
FDS_OP_UPDATE, // Update a record.
|
||||||
|
FDS_OP_DEL_RECORD, // Delete a record.
|
||||||
|
FDS_OP_DEL_FILE, // Delete a file.
|
||||||
|
FDS_OP_GC // Run garbage collection.
|
||||||
|
} fds_op_code_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
FDS_OP_INIT_TAG_SWAP,
|
||||||
|
FDS_OP_INIT_TAG_DATA,
|
||||||
|
FDS_OP_INIT_ERASE_SWAP,
|
||||||
|
FDS_OP_INIT_PROMOTE_SWAP,
|
||||||
|
} fds_init_step_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
FDS_OP_WRITE_HEADER_BEGIN, // Write the record key and length.
|
||||||
|
FDS_OP_WRITE_HEADER_FINALIZE, // Write the file ID and CRC.
|
||||||
|
FDS_OP_WRITE_RECORD_ID, // Write the record ID.
|
||||||
|
FDS_OP_WRITE_DATA, // Write the record data.
|
||||||
|
FDS_OP_WRITE_FIND_RECORD,
|
||||||
|
FDS_OP_WRITE_FLAG_DIRTY, // Flag a record as dirty (as part of an update operation).
|
||||||
|
FDS_OP_WRITE_DONE,
|
||||||
|
} fds_write_step_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
FDS_OP_DEL_RECORD_FLAG_DIRTY, // Flag a record as dirty.
|
||||||
|
FDS_OP_DEL_FILE_FLAG_DIRTY, // Flag multiple records as dirty.
|
||||||
|
FDS_OP_DEL_DONE,
|
||||||
|
} fds_delete_step_t;
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(__CC_ARM)
|
||||||
|
#pragma push
|
||||||
|
#pragma anon_unions
|
||||||
|
#elif defined(__ICCARM__)
|
||||||
|
#pragma language=extended
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
// anonymous unions are enabled by default
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
fds_op_code_t op_code; // The opcode for the operation.
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
fds_init_step_t step; // The current step the operation is at.
|
||||||
|
} init;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
fds_header_t header;
|
||||||
|
void const * p_data;
|
||||||
|
uint16_t page; // The page the flash space for this command was reserved.
|
||||||
|
fds_write_step_t step; // The current step the operation is at.
|
||||||
|
uint32_t record_to_delete; // The record to delete in case this is an update.
|
||||||
|
} write;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
fds_delete_step_t step;
|
||||||
|
uint16_t file_id;
|
||||||
|
uint16_t record_key;
|
||||||
|
uint32_t record_to_delete;
|
||||||
|
} del;
|
||||||
|
};
|
||||||
|
} fds_op_t;
|
||||||
|
|
||||||
|
#if defined(__CC_ARM)
|
||||||
|
#pragma pop
|
||||||
|
#elif defined(__ICCARM__)
|
||||||
|
// leave anonymous unions enabled
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
// anonymous unions are enabled by default
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PAGE_ERASED = 0x1, // One or more erased pages found.
|
||||||
|
PAGE_DATA = 0x2, // One or more data pages found.
|
||||||
|
PAGE_SWAP_CLEAN = 0x4, // A clean (empty) swap page was found.
|
||||||
|
PAGE_SWAP_DIRTY = 0x8, // A dirty (non-empty) swap page was found.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
// No erased pages or FDS pages found.
|
||||||
|
// This is a fatal error.
|
||||||
|
NO_PAGES,
|
||||||
|
|
||||||
|
// The filesystem can not be garbage collected.
|
||||||
|
// This is a fatal error.
|
||||||
|
NO_SWAP = (PAGE_DATA),
|
||||||
|
|
||||||
|
// Perform a fresh installation.
|
||||||
|
FRESH_INSTALL = (PAGE_ERASED),
|
||||||
|
|
||||||
|
// Tag an erased page as swap.
|
||||||
|
TAG_SWAP = (PAGE_ERASED | PAGE_DATA),
|
||||||
|
|
||||||
|
// Tag all erased pages as data.
|
||||||
|
TAG_DATA = (PAGE_ERASED | PAGE_SWAP_CLEAN),
|
||||||
|
|
||||||
|
// Tag all remaining erased pages as data.
|
||||||
|
TAG_DATA_INST = (PAGE_ERASED | PAGE_DATA | PAGE_SWAP_CLEAN),
|
||||||
|
|
||||||
|
// The swap is dirty, likely because the device powered off during GC.
|
||||||
|
// Because there is also an erased page, assume that that page has been garbage collected.
|
||||||
|
// Hence, tag the swap as data (promote), an erased page as swap and remaining pages as data.
|
||||||
|
PROMOTE_SWAP = (PAGE_ERASED | PAGE_SWAP_DIRTY),
|
||||||
|
|
||||||
|
// Tag the swap as data (promote), an erased page as swap and remaining pages as data.
|
||||||
|
PROMOTE_SWAP_INST = (PAGE_ERASED | PAGE_DATA | PAGE_SWAP_DIRTY),
|
||||||
|
|
||||||
|
// The swap is dirty (written) and there are no erased pages. It is likely that the device
|
||||||
|
// powered off during GC. It is safe to discard (erase) the swap, since data that was
|
||||||
|
// swapped out still lies in one of the valid pages.
|
||||||
|
DISCARD_SWAP = (PAGE_DATA | PAGE_SWAP_DIRTY),
|
||||||
|
|
||||||
|
// Do nothing.
|
||||||
|
ALREADY_INSTALLED = (PAGE_DATA | PAGE_SWAP_CLEAN),
|
||||||
|
|
||||||
|
} fds_init_opts_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GC_BEGIN, // Begin GC.
|
||||||
|
GC_NEXT_PAGE, // GC a page.
|
||||||
|
GC_FIND_NEXT_RECORD, // Find a valid record to copy.
|
||||||
|
GC_COPY_RECORD, // Copy a valid record to swap.
|
||||||
|
GC_ERASE_PAGE, // Erase the page being garbage collected.
|
||||||
|
GC_DISCARD_SWAP, // Erase (discard) the swap page.
|
||||||
|
GC_PROMOTE_SWAP, // Tag the swap as valid.
|
||||||
|
GC_TAG_NEW_SWAP // Tag a freshly erased (GCed) page as swap.
|
||||||
|
} fds_gc_state_t;
|
||||||
|
|
||||||
|
|
||||||
|
// Holds garbage collection status and related data.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
fds_gc_state_t state; // The current GC step.
|
||||||
|
uint16_t cur_page; // The current page being garbage collected.
|
||||||
|
uint32_t const * p_record_src; // The current record being copied to swap.
|
||||||
|
uint16_t run_count; // Total number of times GC was run.
|
||||||
|
bool do_gc_page[FDS_DATA_PAGES]; // Controls which pages to garbage collect.
|
||||||
|
bool resume; // Whether or not GC should be resumed.
|
||||||
|
} fds_gc_data_t;
|
||||||
|
|
||||||
|
|
||||||
|
// Macros to enable and disable application interrupts.
|
||||||
|
#if defined (FDS_THREADS)
|
||||||
|
|
||||||
|
#define CRITICAL_SECTION_ENTER() CRITICAL_REGION_ENTER()
|
||||||
|
#define CRITICAL_SECTION_EXIT() CRITICAL_REGION_EXIT()
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define CRITICAL_SECTION_ENTER()
|
||||||
|
#define CRITICAL_SECTION_EXIT()
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // FDS_INTERNAL_DEFS_H__
|
|
@ -0,0 +1,257 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2016 - 2017, Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||||
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||||
|
* such product, must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or other
|
||||||
|
* materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* 4. This software, with or without modification, must only be used with a
|
||||||
|
* Nordic Semiconductor ASA integrated circuit.
|
||||||
|
*
|
||||||
|
* 5. Any software provided in binary form under this license must not be reverse
|
||||||
|
* engineered, decompiled, modified and/or disassembled.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||||
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "sdk_config.h"
|
||||||
|
|
||||||
|
#if NRF_FSTORAGE_ENABLED
|
||||||
|
|
||||||
|
#include "nrf_fstorage.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "sdk_errors.h"
|
||||||
|
#include "nrf_section.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Create the section "fs_data". */
|
||||||
|
NRF_SECTION_DEF(fs_data, nrf_fstorage_t);
|
||||||
|
|
||||||
|
static bool addr_within_bounds(nrf_fstorage_t const * p_fs, uint32_t addr, uint32_t len);
|
||||||
|
static bool addr_is_aligned32(uint32_t addr);
|
||||||
|
|
||||||
|
|
||||||
|
ret_code_t nrf_fstorage_init(nrf_fstorage_t * p_fs,
|
||||||
|
nrf_fstorage_api_t * p_api,
|
||||||
|
void * p_param)
|
||||||
|
{
|
||||||
|
if ((p_fs == NULL) || (p_api == NULL))
|
||||||
|
{
|
||||||
|
return NRF_ERROR_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_fs->p_api = p_api;
|
||||||
|
|
||||||
|
return (p_fs->p_api)->init(p_fs, p_param);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ret_code_t nrf_fstorage_uninit(nrf_fstorage_t * p_fs,
|
||||||
|
void * p_param)
|
||||||
|
{
|
||||||
|
ret_code_t rc;
|
||||||
|
|
||||||
|
if (p_fs == NULL)
|
||||||
|
{
|
||||||
|
return NRF_ERROR_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_fs->p_api == NULL)
|
||||||
|
{
|
||||||
|
return NRF_ERROR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = (p_fs->p_api)->uninit(p_fs, p_param);
|
||||||
|
|
||||||
|
/* Uninitialize the API. */
|
||||||
|
p_fs->p_api = NULL;
|
||||||
|
p_fs->p_flash_info = NULL;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ret_code_t nrf_fstorage_read(nrf_fstorage_t const * p_fs,
|
||||||
|
uint32_t src,
|
||||||
|
void * p_dest,
|
||||||
|
uint32_t len)
|
||||||
|
{
|
||||||
|
if ((p_fs == NULL) || (p_dest == NULL))
|
||||||
|
{
|
||||||
|
return NRF_ERROR_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_fs->p_api == NULL)
|
||||||
|
{
|
||||||
|
return NRF_ERROR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
{
|
||||||
|
return NRF_ERROR_INVALID_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Source addres must be word-aligned. */
|
||||||
|
if ( !addr_is_aligned32(src)
|
||||||
|
|| !addr_within_bounds(p_fs, src, len))
|
||||||
|
{
|
||||||
|
return NRF_ERROR_INVALID_ADDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (p_fs->p_api)->read(p_fs, src, p_dest, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ret_code_t nrf_fstorage_write(nrf_fstorage_t const * p_fs,
|
||||||
|
uint32_t dest,
|
||||||
|
void const * p_src,
|
||||||
|
uint32_t len,
|
||||||
|
void * p_context)
|
||||||
|
{
|
||||||
|
if ((p_fs == NULL) || (p_src == NULL))
|
||||||
|
{
|
||||||
|
return NRF_ERROR_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_fs->p_api == NULL)
|
||||||
|
{
|
||||||
|
return NRF_ERROR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Length must be a multiple of the program unit. */
|
||||||
|
if ((len == 0) || ((len % p_fs->p_flash_info->program_unit) != 0))
|
||||||
|
{
|
||||||
|
return NRF_ERROR_INVALID_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Source and destination addresses must be word-aligned. */
|
||||||
|
if ( !addr_is_aligned32(dest)
|
||||||
|
|| !addr_is_aligned32((uint32_t)p_src)
|
||||||
|
|| !addr_within_bounds(p_fs, dest, len))
|
||||||
|
{
|
||||||
|
return NRF_ERROR_INVALID_ADDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (p_fs->p_api)->write(p_fs, dest, p_src, len, p_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ret_code_t nrf_fstorage_erase(nrf_fstorage_t const * p_fs,
|
||||||
|
uint32_t page_addr,
|
||||||
|
uint32_t len,
|
||||||
|
void * p_context)
|
||||||
|
{
|
||||||
|
if (p_fs == NULL)
|
||||||
|
{
|
||||||
|
return NRF_ERROR_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_fs->p_api == NULL)
|
||||||
|
{
|
||||||
|
return NRF_ERROR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
{
|
||||||
|
return NRF_ERROR_INVALID_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Address must be aligned to a page boundary. */
|
||||||
|
if ( ((page_addr & (p_fs->p_flash_info->erase_unit - 1)) != 0)
|
||||||
|
|| !addr_within_bounds(p_fs, page_addr, (len * p_fs->p_flash_info->erase_unit)))
|
||||||
|
{
|
||||||
|
return NRF_ERROR_INVALID_ADDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (p_fs->p_api)->erase(p_fs, page_addr, len, p_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t const * nrf_fstorage_rmap(nrf_fstorage_t const * p_fs, uint32_t addr)
|
||||||
|
{
|
||||||
|
if (p_fs == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (p_fs->p_api)->rmap(p_fs, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t * nrf_fstorage_wmap(nrf_fstorage_t const * p_fs, uint32_t addr)
|
||||||
|
{
|
||||||
|
if (p_fs == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (p_fs->p_api)->wmap(p_fs, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool nrf_fstorage_is_busy(nrf_fstorage_t const * p_fs)
|
||||||
|
{
|
||||||
|
/* If a NULL instance is provided, return true if any instance is busy.
|
||||||
|
* Uninitialized instances are considered not busy. */
|
||||||
|
if ((p_fs == NULL) || (p_fs->p_api == NULL))
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < NRF_FSTORAGE_INSTANCE_CNT; i++)
|
||||||
|
{
|
||||||
|
p_fs = NRF_FSTORAGE_INSTANCE_GET(i); /* cannot be NULL. */
|
||||||
|
if (p_fs->p_api != NULL)
|
||||||
|
{
|
||||||
|
/* p_api->is_busy() cannot be NULL. */
|
||||||
|
if (p_fs->p_api->is_busy(p_fs))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p_fs->p_api->is_busy(p_fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool addr_within_bounds(nrf_fstorage_t const * p_fs,
|
||||||
|
uint32_t addr,
|
||||||
|
uint32_t len)
|
||||||
|
{
|
||||||
|
return ((addr >= p_fs->start_addr) &&
|
||||||
|
(addr + len - 1 <= p_fs->end_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool addr_is_aligned32(uint32_t addr)
|
||||||
|
{
|
||||||
|
return !(addr & 0x03);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // NRF_FSTORAGE_ENABLED
|
|
@ -0,0 +1,340 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2016 - 2017, Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||||
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||||
|
* such product, must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or other
|
||||||
|
* materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* 4. This software, with or without modification, must only be used with a
|
||||||
|
* Nordic Semiconductor ASA integrated circuit.
|
||||||
|
*
|
||||||
|
* 5. Any software provided in binary form under this license must not be reverse
|
||||||
|
* engineered, decompiled, modified and/or disassembled.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||||
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef NRF_FSTORAGE_H__
|
||||||
|
#define NRF_FSTORAGE_H__
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @defgroup nrf_fstorage Flash storage (fstorage)
|
||||||
|
* @ingroup app_common
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @brief Flash abstraction library that provides basic read, write, and erase operations.
|
||||||
|
*
|
||||||
|
* @details The fstorage library can be implemented in different ways. Two implementations are provided:
|
||||||
|
* - The @ref nrf_fstorage_sd implements flash access through the SoftDevice.
|
||||||
|
* - The @ref nrf_fstorage_nvmc implements flash access through the non-volatile memory controller.
|
||||||
|
*
|
||||||
|
* You can select the implementation that should be used independently for each instance of fstorage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "sdk_errors.h"
|
||||||
|
#include "nrf_section.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Macro for defining an fstorage instance.
|
||||||
|
*
|
||||||
|
* Users of fstorage must define an instance variable by using this macro.
|
||||||
|
* Each instance is tied to an API implementation and contains information such
|
||||||
|
* as the program and erase units for the target flash peripheral.
|
||||||
|
* Instance variables are placed in the "fs_data" section of the binary.
|
||||||
|
*
|
||||||
|
* @param[in] inst A definition of an @ref nrf_fstorage_t variable.
|
||||||
|
*/
|
||||||
|
#define NRF_FSTORAGE_DEF(inst) NRF_SECTION_ITEM_REGISTER(fs_data, inst)
|
||||||
|
|
||||||
|
/**@brief Macro for retrieving an fstorage instance. */
|
||||||
|
#define NRF_FSTORAGE_INSTANCE_GET(i) NRF_SECTION_ITEM_GET(fs_data, nrf_fstorage_t, (i))
|
||||||
|
|
||||||
|
/**@brief Macro for retrieving the total number of fstorage instances. */
|
||||||
|
#define NRF_FSTORAGE_INSTANCE_CNT NRF_SECTION_ITEM_COUNT(fs_data, nrf_fstorage_t)
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Event IDs. */
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
NRF_FSTORAGE_EVT_READ_RESULT,
|
||||||
|
NRF_FSTORAGE_EVT_WRITE_RESULT, //!< Event for @ref nrf_fstorage_write.
|
||||||
|
NRF_FSTORAGE_EVT_ERASE_RESULT //!< Event for @ref nrf_fstorage_erase.
|
||||||
|
} nrf_fstorage_evt_id_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief An fstorage event. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
nrf_fstorage_evt_id_t id; //!< The event ID.
|
||||||
|
ret_code_t result; //!< Result of the operation.
|
||||||
|
uint32_t addr; //!< Address at which the operation was performed.
|
||||||
|
uint32_t len; //!< Length of the operation.
|
||||||
|
void * p_param; //!< User-defined parameter passed to the event handler.
|
||||||
|
} nrf_fstorage_evt_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Event handler function prototype.
|
||||||
|
*
|
||||||
|
* @param[in] p_evt The event.
|
||||||
|
*/
|
||||||
|
typedef void (*nrf_fstorage_evt_handler_t)(nrf_fstorage_evt_t * p_evt);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Information about the implementation and the flash peripheral. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t erase_unit; //!< Size of a flash page (in bytes). A flash page is the smallest unit that can be erased.
|
||||||
|
uint32_t program_unit; //!< Size of the smallest programmable unit (in bytes).
|
||||||
|
bool rmap; //!< The device address space is memory mapped to the MCU address space.
|
||||||
|
bool wmap; //!< The device address space is memory mapped to a writable MCU address space.
|
||||||
|
} const nrf_fstorage_info_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* Necessary forward declaration. */
|
||||||
|
struct nrf_fstorage_api_s;
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief An fstorage instance.
|
||||||
|
*
|
||||||
|
* @details Use the @ref NRF_FSTORAGE_DEF macro to define an fstorage instance.
|
||||||
|
*
|
||||||
|
* An instance is tied to an API implementation and contains information about the flash device,
|
||||||
|
* such as the program and erase units as well and implementation-specific functionality.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/**@brief The API implementation used by this instance. */
|
||||||
|
struct nrf_fstorage_api_s const * p_api;
|
||||||
|
|
||||||
|
/**@brief Information about the implementation functionality and the flash peripheral. */
|
||||||
|
nrf_fstorage_info_t * p_flash_info;
|
||||||
|
|
||||||
|
/**@brief The event handler function.
|
||||||
|
*
|
||||||
|
* If set to NULL, no events will be sent.
|
||||||
|
*/
|
||||||
|
nrf_fstorage_evt_handler_t evt_handler;
|
||||||
|
|
||||||
|
/**@brief The beginning of the flash space on which this fstorage instance should operate.
|
||||||
|
* All flash operations must be within the address specified in
|
||||||
|
* this field and @ref end_addr.
|
||||||
|
*
|
||||||
|
* This field must be set manually.
|
||||||
|
*/
|
||||||
|
uint32_t start_addr;
|
||||||
|
|
||||||
|
/**@brief The last address (exclusive) of flash on which this fstorage instance should operate.
|
||||||
|
* All flash operations must be within the address specified in
|
||||||
|
* this field and @ref start_addr.
|
||||||
|
*
|
||||||
|
* This field must be set manually.
|
||||||
|
*/
|
||||||
|
uint32_t end_addr;
|
||||||
|
} nrf_fstorage_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Functions provided by the API implementation. */
|
||||||
|
typedef struct nrf_fstorage_api_s
|
||||||
|
{
|
||||||
|
/**@brief Initialize the flash peripheral. */
|
||||||
|
ret_code_t (*init)(nrf_fstorage_t * p_fs, void * p_param);
|
||||||
|
/**@brief Uninitialize the flash peripheral. */
|
||||||
|
ret_code_t (*uninit)(nrf_fstorage_t * p_fs, void * p_param);
|
||||||
|
/**@brief Read data from flash. */
|
||||||
|
ret_code_t (*read)(nrf_fstorage_t const * p_fs, uint32_t src, void * p_dest, uint32_t len);
|
||||||
|
/**@brief Write bytes to flash. */
|
||||||
|
ret_code_t (*write)(nrf_fstorage_t const * p_fs, uint32_t dest, void const * p_src, uint32_t len, void * p_param);
|
||||||
|
/**@brief Erase flash pages. */
|
||||||
|
ret_code_t (*erase)(nrf_fstorage_t const * p_fs, uint32_t addr, uint32_t len, void * p_param);
|
||||||
|
/**@brief Map a device address to a readable address within the MCU address space. */
|
||||||
|
uint8_t const * (*rmap)(nrf_fstorage_t const * p_fs, uint32_t addr);
|
||||||
|
/**@brief Map a device address to a writable address within the MCU address space. */
|
||||||
|
uint8_t * (*wmap)(nrf_fstorage_t const * p_fs, uint32_t addr);
|
||||||
|
/**@brief Check if there are any pending flash operations. */
|
||||||
|
bool (*is_busy)(nrf_fstorage_t const * p_fs);
|
||||||
|
} const nrf_fstorage_api_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for initializing fstorage.
|
||||||
|
*
|
||||||
|
* @param[in] p_fs The fstorage instance to initialize.
|
||||||
|
* @param[in] p_api The API implementation to use.
|
||||||
|
* @param[in] p_param An optional parameter to pass to the implementation-specific API call.
|
||||||
|
*
|
||||||
|
* @retval NRF_SUCCESS If initialization was successful.
|
||||||
|
* @retval NRF_ERROR_NULL If @p p_fs or @p p_api field in @p p_fs is NULL.
|
||||||
|
* @retval NRF_ERROR_INTERNAL If another error occurred.
|
||||||
|
*/
|
||||||
|
ret_code_t nrf_fstorage_init(nrf_fstorage_t * p_fs,
|
||||||
|
nrf_fstorage_api_t * p_api,
|
||||||
|
void * p_param);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for uninitializing an fstorage instance.
|
||||||
|
*
|
||||||
|
* @param[in] p_fs The fstorage instance to uninitialize.
|
||||||
|
* @param[in] p_param An optional parameter to pass to the implementation-specific API call.
|
||||||
|
*
|
||||||
|
* @retval NRF_SUCCESS If uninitialization was successful.
|
||||||
|
* @retval NRF_ERROR_NULL If @p p_fs is NULL.
|
||||||
|
* @retval NRF_ERROR_INVALID_STATE If the module is not initialized.
|
||||||
|
* @retval NRF_ERROR_INTERNAL If another error occurred.
|
||||||
|
*/
|
||||||
|
ret_code_t nrf_fstorage_uninit(nrf_fstorage_t * p_fs, void * p_param);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for reading data from flash.
|
||||||
|
*
|
||||||
|
* Copy @p len bytes from @p addr to @p p_dest.
|
||||||
|
*
|
||||||
|
* @param[in] p_fs The fstorage instance.
|
||||||
|
* @param[in] addr Address in flash where to read from.
|
||||||
|
* @param[in] p_dest Buffer where the data should be copied.
|
||||||
|
* @param[in] len Length of the data to be copied (in bytes).
|
||||||
|
*
|
||||||
|
* @retval NRF_SUCCESS If the operation was successful.
|
||||||
|
* @retval NRF_ERROR_NULL If @p p_fs or @p p_dest is NULL.
|
||||||
|
* @retval NRF_ERROR_INVALID_STATE If the module is not initialized.
|
||||||
|
* @retval NRF_ERROR_INVALID_LENGTH If @p len is zero or otherwise invalid.
|
||||||
|
* @retval NRF_ERROR_INVALID_ADDR If the address @p addr is outside the flash memory
|
||||||
|
* boundaries specified in @p p_fs, or if it is unaligned.
|
||||||
|
*/
|
||||||
|
ret_code_t nrf_fstorage_read(nrf_fstorage_t const * p_fs,
|
||||||
|
uint32_t addr,
|
||||||
|
void * p_dest,
|
||||||
|
uint32_t len);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for writing data to flash.
|
||||||
|
*
|
||||||
|
* Write @p len bytes from @p p_src to @p dest.
|
||||||
|
*
|
||||||
|
* When using @ref nrf_fstorage_sd, the data is written by several calls to @ref sd_flash_write if
|
||||||
|
* the length of the data exceeds @ref NRF_FSTORAGE_SD_MAX_WRITE_SIZE bytes.
|
||||||
|
* Only one event is sent upon completion.
|
||||||
|
*
|
||||||
|
* @note The data to be written to flash must be kept in memory until the operation has
|
||||||
|
* terminated and an event is received.
|
||||||
|
*
|
||||||
|
* @param[in] p_fs The fstorage instance.
|
||||||
|
* @param[in] dest Address in flash memory where to write the data.
|
||||||
|
* @param[in] p_src Data to be written.
|
||||||
|
* @param[in] len Length of the data (in bytes).
|
||||||
|
* @param[in] p_param User-defined parameter passed to the event handler (may be NULL).
|
||||||
|
*
|
||||||
|
* @retval NRF_SUCCESS If the operation was accepted.
|
||||||
|
* @retval NRF_ERROR_NULL If @p p_fs or @p p_src is NULL.
|
||||||
|
* @retval NRF_ERROR_INVALID_STATE If the module is not initialized.
|
||||||
|
* @retval NRF_ERROR_INVALID_LENGTH If @p len is zero or not a multiple of the program unit,
|
||||||
|
* or if it is otherwise invalid.
|
||||||
|
* @retval NRF_ERROR_INVALID_ADDR If the address @p dest is outside the flash memory
|
||||||
|
* boundaries specified in @p p_fs, or if it is unaligned.
|
||||||
|
* @retval NRF_ERROR_NO_MEM If no memory is available to accept the operation.
|
||||||
|
* When using the @ref nrf_fstorage_sd, this error
|
||||||
|
* indicates that the internal queue of operations is full.
|
||||||
|
*/
|
||||||
|
ret_code_t nrf_fstorage_write(nrf_fstorage_t const * p_fs,
|
||||||
|
uint32_t dest,
|
||||||
|
void const * p_src,
|
||||||
|
uint32_t len,
|
||||||
|
void * p_param);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for erasing flash pages.
|
||||||
|
*
|
||||||
|
* @details This function erases @p len pages starting from the page at address @p page_addr.
|
||||||
|
* The erase operation must be initiated on a page boundary.
|
||||||
|
*
|
||||||
|
* @param[in] p_fs The fstorage instance.
|
||||||
|
* @param[in] page_addr Address of the page to erase.
|
||||||
|
* @param[in] len Number of pages to erase.
|
||||||
|
* @param[in] p_param User-defined parameter passed to the event handler (may be NULL).
|
||||||
|
*
|
||||||
|
* @retval NRF_SUCCESS If the operation was accepted.
|
||||||
|
* @retval NRF_ERROR_NULL If @p p_fs is NULL.
|
||||||
|
* @retval NRF_ERROR_INVALID_STATE If the module is not initialized.
|
||||||
|
* @retval NRF_ERROR_INVALID_LENGTH If @p len is zero.
|
||||||
|
* @retval NRF_ERROR_INVALID_ADDR If the address @p page_addr is outside the flash memory
|
||||||
|
* boundaries specified in @p p_fs, or if it is unaligned.
|
||||||
|
* @retval NRF_ERROR_NO_MEM If no memory is available to accept the operation.
|
||||||
|
* When using the @ref nrf_fstorage_sd, this error
|
||||||
|
* indicates that the internal queue of operations is full.
|
||||||
|
*/
|
||||||
|
ret_code_t nrf_fstorage_erase(nrf_fstorage_t const * p_fs,
|
||||||
|
uint32_t page_addr,
|
||||||
|
uint32_t len,
|
||||||
|
void * p_param);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Map a flash address to a pointer in the MCU address space that can be dereferenced.
|
||||||
|
*
|
||||||
|
* @param p_fs The fstorage instance.
|
||||||
|
* @param addr The address to map.
|
||||||
|
*
|
||||||
|
* @retval A pointer to the specified address,
|
||||||
|
* or @c NULL if the address cannot be mapped or if @p p_fs is @c NULL.
|
||||||
|
*/
|
||||||
|
uint8_t const * nrf_fstorage_rmap(nrf_fstorage_t const * p_fs, uint32_t addr);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Map a flash address to a pointer in the MCU address space that can be written to.
|
||||||
|
*
|
||||||
|
* @param p_fs The fstorage instance.
|
||||||
|
* @param addr The address to map.
|
||||||
|
*
|
||||||
|
* @retval A pointer to the specified address,
|
||||||
|
* or @c NULL if the address cannot be mapped or if @p p_fs is @c NULL.
|
||||||
|
*/
|
||||||
|
uint8_t * nrf_fstorage_wmap(nrf_fstorage_t const * p_fs, uint32_t addr);
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Function for querying the status of fstorage.
|
||||||
|
*
|
||||||
|
* @details An uninitialized instance of fstorage is treated as not busy.
|
||||||
|
*
|
||||||
|
* @param[in] p_fs The fstorage instance. Pass NULL to query all instances.
|
||||||
|
*
|
||||||
|
* @returns If @p p_fs is @c NULL, this function returns true if any fstorage instance is busy or false otherwise.
|
||||||
|
* @returns If @p p_fs is not @c NULL, this function returns true if the fstorage instance is busy or false otherwise.
|
||||||
|
*/
|
||||||
|
bool nrf_fstorage_is_busy(nrf_fstorage_t const * p_fs);
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // NRF_FSTORAGE_H__
|
|
@ -38,82 +38,172 @@
|
||||||
|
|
||||||
#if DEVICE_FLASH
|
#if DEVICE_FLASH
|
||||||
|
|
||||||
#include "flash_api.h"
|
#include "hal/flash_api.h"
|
||||||
#include "nrf_nvmc.h"
|
#include "hal/lp_ticker_api.h"
|
||||||
#include "nrf_soc.h"
|
|
||||||
|
#include "nrf_fstorage.h"
|
||||||
|
|
||||||
#if defined(SOFTDEVICE_PRESENT)
|
#if defined(SOFTDEVICE_PRESENT)
|
||||||
#include "nrf_sdm.h"
|
#include "nrf_fstorage_sd.h"
|
||||||
|
#else
|
||||||
|
#include "nrf_fstorage_nvmc.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define PAGE_ERASE_TIMEOUT_US (200 * 1000) // Max. value from datasheet: 89.7 ms
|
||||||
|
#define WORD_SIZE_IN_BYTES 4
|
||||||
|
|
||||||
|
NRF_FSTORAGE_DEF(static nrf_fstorage_t nordic_fstorage) = { 0 };
|
||||||
|
|
||||||
int32_t flash_init(flash_t *obj)
|
int32_t flash_init(flash_t *obj)
|
||||||
{
|
{
|
||||||
(void)(obj);
|
(void)(obj);
|
||||||
|
|
||||||
|
ret_code_t result = NRF_SUCCESS;
|
||||||
|
|
||||||
|
/* Only initialize once. */
|
||||||
|
static bool do_init = true;
|
||||||
|
|
||||||
|
if (do_init) {
|
||||||
|
do_init = false;
|
||||||
|
|
||||||
|
/* Set instance to cover the whole flash. */
|
||||||
|
nordic_fstorage.p_flash_info = NULL;
|
||||||
|
nordic_fstorage.evt_handler = NULL;
|
||||||
|
nordic_fstorage.start_addr = 0;
|
||||||
|
nordic_fstorage.end_addr = NRF_FICR->CODESIZE * NRF_FICR->CODEPAGESIZE;
|
||||||
|
|
||||||
|
/* Initialize either SoftDevice API or NVMC API.
|
||||||
|
* SoftDevice API should work both when the SoftDevice is enabled or disabled.
|
||||||
|
* NVMC API is used when the SoftDevice is not present.
|
||||||
|
*/
|
||||||
#if defined(SOFTDEVICE_PRESENT)
|
#if defined(SOFTDEVICE_PRESENT)
|
||||||
uint8_t sd_enabled;
|
result = nrf_fstorage_init(&nordic_fstorage, &nrf_fstorage_sd, NULL);
|
||||||
if ((sd_softdevice_is_enabled(&sd_enabled) == NRF_SUCCESS) && sd_enabled == 1) {
|
#else
|
||||||
return -1;
|
result = nrf_fstorage_init(&nordic_fstorage, &nrf_fstorage_nvmc, NULL);
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
|
||||||
|
/* Initialize low power ticker for timeouts. */
|
||||||
|
lp_ticker_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert Nordic SDK error code to mbed HAL. */
|
||||||
|
return (result == NRF_SUCCESS) ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t flash_free(flash_t *obj)
|
int32_t flash_free(flash_t *obj)
|
||||||
{
|
{
|
||||||
(void)(obj);
|
(void)(obj);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t flash_erase_sector(flash_t *obj, uint32_t address)
|
int32_t flash_erase_sector(flash_t *obj, uint32_t address)
|
||||||
{
|
{
|
||||||
(void)(obj);
|
(void)(obj);
|
||||||
#if defined(SOFTDEVICE_PRESENT)
|
|
||||||
uint8_t sd_enabled;
|
ret_code_t result = NRF_ERROR_NO_MEM;
|
||||||
if ((sd_softdevice_is_enabled(&sd_enabled) == NRF_SUCCESS) && sd_enabled == 1) {
|
|
||||||
return -1;
|
/* Setup stop watch for timeout. */
|
||||||
|
uint32_t start_us = lp_ticker_read();
|
||||||
|
uint32_t now_us = start_us;
|
||||||
|
|
||||||
|
/* Retry if flash is busy until timeout is reached. */
|
||||||
|
while (((now_us - start_us) < PAGE_ERASE_TIMEOUT_US) &&
|
||||||
|
(result == NRF_ERROR_NO_MEM)) {
|
||||||
|
|
||||||
|
/* Map mbed HAL call to Nordic fstorage call. */
|
||||||
|
result = nrf_fstorage_erase(&nordic_fstorage, address, 1, NULL);
|
||||||
|
|
||||||
|
/* Read timeout timer. */
|
||||||
|
now_us = lp_ticker_read();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
nrf_nvmc_page_erase(address);
|
/* Convert Nordic SDK error code to mbed HAL. */
|
||||||
return 0;
|
return (result == NRF_SUCCESS) ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t flash_program_page(flash_t *obj, uint32_t address, const uint8_t *data, uint32_t size)
|
int32_t flash_program_page(flash_t *obj, uint32_t address, const uint8_t *data, uint32_t size)
|
||||||
{
|
{
|
||||||
#if defined(SOFTDEVICE_PRESENT)
|
(void)(obj);
|
||||||
uint8_t sd_enabled;
|
|
||||||
if ((sd_softdevice_is_enabled(&sd_enabled) == NRF_SUCCESS) && sd_enabled == 1) {
|
/* Check that data pointer is not NULL. */
|
||||||
return -1;
|
ret_code_t result = NRF_ERROR_NULL;
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
|
||||||
|
/* nrf_fstorage_write only accepts word aligned input buffers.
|
||||||
|
* Cast data pointer to word pointer and read word into temporary word variable
|
||||||
|
* which should be word aligned. This should be safe on Cortex-m4 which is able
|
||||||
|
* to load unaligned data.
|
||||||
|
*/
|
||||||
|
const uint32_t *data_word = (const uint32_t *) data;
|
||||||
|
|
||||||
|
/* Loop through input buffer 4 bytes at a time. */
|
||||||
|
for (size_t index = 0; index < (size / WORD_SIZE_IN_BYTES); index++) {
|
||||||
|
|
||||||
|
/* Load 4 bytes into temporary variable. */
|
||||||
|
uint32_t word = data_word[index];
|
||||||
|
|
||||||
|
/* Setup stop watch for timeout. */
|
||||||
|
uint32_t start_us = lp_ticker_read();
|
||||||
|
uint32_t now_us = start_us;
|
||||||
|
|
||||||
|
/* Retry if flash is busy until timeout is reached. */
|
||||||
|
do {
|
||||||
|
/* Write one word at a time. */
|
||||||
|
result = nrf_fstorage_write(&nordic_fstorage,
|
||||||
|
address + (WORD_SIZE_IN_BYTES * index),
|
||||||
|
&word,
|
||||||
|
WORD_SIZE_IN_BYTES,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* Read timeout timer. */
|
||||||
|
now_us = lp_ticker_read();
|
||||||
|
|
||||||
|
/* Loop if queue is full or until time runs out. */
|
||||||
|
} while (((now_us - start_us) < PAGE_ERASE_TIMEOUT_US) &&
|
||||||
|
(result == NRF_ERROR_NO_MEM));
|
||||||
|
|
||||||
|
/* Break loop if write command wasn't accepted. */
|
||||||
|
if (result != NRF_SUCCESS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
/* We will use *_words function to speed up flashing code. Word means 32bit -> 4B
|
/* Convert Nordic SDK error code to mbed HAL. */
|
||||||
* or sizeof(uint32_t).
|
return (result == NRF_SUCCESS) ? 0 : -1;
|
||||||
*/
|
|
||||||
nrf_nvmc_write_words(address, (const uint32_t *) data, (size / sizeof(uint32_t)));
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t flash_get_size(const flash_t *obj)
|
uint32_t flash_get_size(const flash_t *obj)
|
||||||
{
|
{
|
||||||
(void)(obj);
|
(void)(obj);
|
||||||
/* Just count flash size. */
|
|
||||||
return NRF_FICR->CODESIZE * NRF_FICR->CODEPAGESIZE;
|
return nordic_fstorage.end_addr - nordic_fstorage.start_addr;
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t flash_get_sector_size(const flash_t *obj, uint32_t address)
|
uint32_t flash_get_sector_size(const flash_t *obj, uint32_t address)
|
||||||
{
|
{
|
||||||
(void)(obj);
|
(void)(obj);
|
||||||
/* Test if passed address is in flash space. */
|
|
||||||
if (address < flash_get_size(obj)) {
|
/* Check if passed address is in flash area. Note end_addr is outside valid range. */
|
||||||
return NRF_FICR->CODEPAGESIZE;
|
if ((address >= nordic_fstorage.start_addr) && (address < nordic_fstorage.end_addr)) {
|
||||||
|
return nordic_fstorage.p_flash_info->erase_unit;
|
||||||
}
|
}
|
||||||
/* Something goes wrong, return invalid size error code. */
|
|
||||||
|
/* Return invalid size if request is outisde flash area. */
|
||||||
return MBED_FLASH_INVALID_SIZE;
|
return MBED_FLASH_INVALID_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t flash_get_page_size(const flash_t *obj)
|
uint32_t flash_get_page_size(const flash_t *obj)
|
||||||
{
|
{
|
||||||
(void)(obj);
|
(void)(obj);
|
||||||
return NRF_FICR->CODEPAGESIZE;
|
|
||||||
|
/* Return minimum writeable page size. */
|
||||||
|
return WORD_SIZE_IN_BYTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t flash_get_start_address(const flash_t *obj)
|
uint32_t flash_get_start_address(const flash_t *obj)
|
||||||
|
@ -122,5 +212,3 @@ uint32_t flash_get_start_address(const flash_t *obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** @}*/
|
|
||||||
|
|
|
@ -3507,6 +3507,7 @@
|
||||||
"device_has": [
|
"device_has": [
|
||||||
"ITM",
|
"ITM",
|
||||||
"TRNG",
|
"TRNG",
|
||||||
|
"FLASH",
|
||||||
"STCLK_OFF_DURING_SLEEP"
|
"STCLK_OFF_DURING_SLEEP"
|
||||||
],
|
],
|
||||||
"extra_labels": [
|
"extra_labels": [
|
||||||
|
@ -3636,6 +3637,7 @@
|
||||||
],
|
],
|
||||||
"device_has": [
|
"device_has": [
|
||||||
"ITM",
|
"ITM",
|
||||||
|
"FLASH",
|
||||||
"STCLK_OFF_DURING_SLEEP"
|
"STCLK_OFF_DURING_SLEEP"
|
||||||
],
|
],
|
||||||
"extra_labels": [
|
"extra_labels": [
|
||||||
|
@ -3669,7 +3671,7 @@
|
||||||
"supported_form_factors": ["ARDUINO"],
|
"supported_form_factors": ["ARDUINO"],
|
||||||
"inherits": ["MCU_NRF52840"],
|
"inherits": ["MCU_NRF52840"],
|
||||||
"macros_add": ["BOARD_PCA10056", "CONFIG_GPIO_AS_PINRESET", "SWI_DISABLE0", "NRF52_ERRATA_20"],
|
"macros_add": ["BOARD_PCA10056", "CONFIG_GPIO_AS_PINRESET", "SWI_DISABLE0", "NRF52_ERRATA_20"],
|
||||||
"device_has_add": ["FLASH", "ANALOGIN", "I2C", "I2C_ASYNCH", "INTERRUPTIN", "LOWPOWERTIMER", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_ASYNCH", "SERIAL_FC", "SLEEP", "SPI", "SPISLAVE", "TRNG"],
|
"device_has_add": ["ANALOGIN", "I2C", "I2C_ASYNCH", "INTERRUPTIN", "LOWPOWERTIMER", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_ASYNCH", "SERIAL_FC", "SLEEP", "SPI", "SPISLAVE", "TRNG"],
|
||||||
"release_versions": ["2", "5"],
|
"release_versions": ["2", "5"],
|
||||||
"device_name": "nRF52840_xxAA",
|
"device_name": "nRF52840_xxAA",
|
||||||
"bootloader_supported": true
|
"bootloader_supported": true
|
||||||
|
|
Loading…
Reference in New Issue