123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- /**
- * Copyright (c) 2016 - 2020, 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.p_src = p_op->write.p_src;
- 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)
- {
- uint32_t rc;
- 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;
- 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. */
- event_send(m_p_cur_op, NRF_ERROR_INTERNAL);
- /* Reset the internal state so we can accept other operations. */
- m_flags.state = NRF_FSTORAGE_STATE_IDLE;
- m_flags.queue_running = false;
- /* Free the current queue element. */
- queue_free();
- } break;
- }
- }
- /* Start processing the queue if it is not running and fstorage is not paused. */
- static void queue_start(void)
- {
- if ( (!nrf_atomic_flag_set_fetch(&m_flags.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);
- p_fs->p_flash_info = &m_flash_info;
- if (!nrf_atomic_flag_set_fetch(&m_flags.initialized))
- {
- #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);
- if ( (sys_evt != NRF_EVT_FLASH_OPERATION_SUCCESS)
- && (sys_evt != NRF_EVT_FLASH_OPERATION_ERROR))
- {
- /* Ignore any non-flash events. */
- return;
- }
- switch (m_flags.state)
- {
- case NRF_FSTORAGE_STATE_IDLE:
- /* Ignore flash events if no flash operation was requested. */
- return;
- 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;
- 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;
- }
- if (!m_flags.paused)
- {
- queue_process();
- }
- else
- {
- /* A flash operation has completed. Let the SoftDevice 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
|