Mouse code for the MacroRat
Diff: mbed-dev/targets/TARGET_NORDIC/TARGET_NRF5/sdk/libraries/fstorage/fstorage.c
- Revision:
- 18:6a4db94011d3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-dev/targets/TARGET_NORDIC/TARGET_NRF5/sdk/libraries/fstorage/fstorage.c Sun May 14 23:18:57 2017 +0000 @@ -0,0 +1,521 @@ +/* + * Copyright (c) 2015 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 or object form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 "fstorage.h" +#include "fstorage_config.h" +#include "fstorage_internal_defs.h" + +#include <stdint.h> +#include <string.h> +#include <stdbool.h> +#include "nrf_error.h" +#include "nrf_soc.h" + + +static uint8_t m_flags; // fstorage status flags. +static fs_op_queue_t m_queue; // Queue of requested operations. +static uint8_t m_retry_count; // Number of times the last flash operation was retried. + + +// Sends events to the application. +static void send_event(fs_op_t const * const p_op, fs_ret_t result) +{ + fs_evt_t evt; + memset(&evt, 0x00, sizeof(fs_evt_t)); + + switch (p_op->op_code) + { + case FS_OP_STORE: + evt.id = FS_EVT_STORE; + evt.store.p_data = p_op->store.p_dest; + evt.store.length_words = p_op->store.length_words; + break; + + case FS_OP_ERASE: + evt.id = FS_EVT_ERASE; + evt.erase.first_page = p_op->erase.page - p_op->erase.pages_erased; + evt.erase.last_page = p_op->erase.page; + break; + + default: + // Should not happen. + break; + } + + p_op->p_config->callback(&evt, result); +} + + +// Checks that a configuration is non-NULL and within section variable bounds. +static bool check_config(fs_config_t const * const config) +{ + if ((config != NULL) && + (FS_SECTION_VARS_START_ADDR <= (uint32_t)config) && + (FS_SECTION_VARS_END_ADDR > (uint32_t)config)) + { + return true; + } + + return false; +} + + +// Executes a store operation. +static uint32_t store_execute(fs_op_t const * const p_op) +{ + uint16_t chunk_len; + + if ((p_op->store.length_words - p_op->store.offset) < FS_MAX_WRITE_SIZE_WORDS) + { + chunk_len = p_op->store.length_words - p_op->store.offset; + } + else + { + chunk_len = FS_MAX_WRITE_SIZE_WORDS; + } + + return sd_flash_write((uint32_t*)p_op->store.p_dest + p_op->store.offset, + (uint32_t*)p_op->store.p_src + p_op->store.offset, + chunk_len); +} + + +// Executes an erase operation. +static uint32_t erase_execute(fs_op_t const * const p_op) +{ + return sd_flash_page_erase(p_op->erase.page); +} + + +// Advances the queue, wrapping around if necessary. +// If no elements are left in the queue, clears the FS_FLAG_PROCESSING flag. +static void queue_advance(void) +{ + if (--m_queue.count == 0) + { + m_flags &= ~FS_FLAG_PROCESSING; + } + + if (++m_queue.rp == FS_QUEUE_SIZE) + { + m_queue.rp = 0; + } +} + + +// Processes the current element in the queue. If the queue is empty, does nothing. +static void queue_process(void) +{ + uint32_t ret; + fs_op_t * const p_op = &m_queue.op[m_queue.rp]; + + if (m_queue.count > 0) + { + switch (p_op->op_code) + { + case FS_OP_STORE: + ret = store_execute(p_op); + break; + + case FS_OP_ERASE: + ret = erase_execute(p_op); + break; + + default: + ret = FS_ERR_INTERNAL; + break; + } + + // There is a pending flash operation which was not initiated by this module. + // Stop processing the queue and wait for a system event. + if (ret == NRF_ERROR_BUSY) + { + m_flags &= ~FS_FLAG_PROCESSING; + m_flags |= FS_FLAG_FLASH_REQ_PENDING; + } + else if (ret != NRF_SUCCESS) + { + // An error has occurred. + send_event(p_op, FS_ERR_INTERNAL); + } + else + { + // Operation is executing. + } + } +} + + +// Starts processing the queue if there are no pending flash operations, both inside and +// outside this module. Returns immediately otherwise. +static void queue_start(void) +{ + if (!(m_flags & FS_FLAG_PROCESSING) && + !(m_flags & FS_FLAG_FLASH_REQ_PENDING)) + { + m_flags |= FS_FLAG_PROCESSING; + queue_process(); + } +} + + +// Flash operation success callback handler. Keeps track of the progress of an operation. +// If it has finished, advances the queue and notifies the application. +static void on_operation_success(fs_op_t * const p_op) +{ + m_retry_count = 0; + + switch (p_op->op_code) + { + case FS_OP_STORE: + { + uint16_t chunk_len; + + if ((p_op->store.length_words - p_op->store.offset) < FS_MAX_WRITE_SIZE_WORDS) + { + chunk_len = p_op->store.length_words - p_op->store.offset; + } + else + { + chunk_len = FS_MAX_WRITE_SIZE_WORDS; + } + + p_op->store.offset += chunk_len; + + if (p_op->store.offset == p_op->store.length_words) + { + // The operation has finished. + send_event(p_op, FS_SUCCESS); + queue_advance(); + } + } + break; + + case FS_OP_ERASE: + { + p_op->erase.page++; + p_op->erase.pages_erased++; + + if (p_op->erase.pages_erased == p_op->erase.pages_to_erase) + { + send_event(p_op, FS_SUCCESS); + queue_advance(); + } + } + break; + + default: + // Should not happen. + break; + } +} + + +// Flash operation failure callback handler. If the maximum number of retries has +// been reached, notifies the application and advances the queue. +static void on_operation_failure(fs_op_t const * const p_op) +{ + if (++m_retry_count > FS_OP_MAX_RETRIES) + { + m_retry_count = 0; + + send_event(p_op, FS_ERR_OPERATION_TIMEOUT); + queue_advance(); + } +} + + +// Retrieves a pointer to the next free element in the queue. +// Additionally, increases the number of elements stored in the queue. +static bool queue_get_next_free(fs_op_t ** p_op) +{ + uint32_t idx; + + if (m_queue.count == FS_QUEUE_SIZE) + { + return false; + } + + idx = ((m_queue.rp + m_queue.count) < FS_QUEUE_SIZE) ? + (m_queue.rp + m_queue.count) : 0; + + m_queue.count++; + + // Zero the element so that unassigned fields will be zero. + memset(&m_queue.op[idx], 0x00, sizeof(fs_op_t)); + + *p_op = &m_queue.op[idx]; + + return true; +} + + +fs_ret_t fs_init(void) +{ + uint32_t const users = FS_SECTION_VARS_COUNT; + uint32_t const * p_current_end = FS_PAGE_END_ADDR; + uint32_t index_max = 0x00; + uint32_t index_last = 0xFFFFFFFF; + + if (m_flags & FS_FLAG_INITIALIZED) + { + return FS_SUCCESS; + } + + #if 0 + // Check for configurations with duplicate priority. + for (uint32_t i = 0; i < users; i++) + { + for (uint32_t j = i + 1; j < users; j++) + { + fs_config_t const * const p_config_i = FS_SECTION_VARS_GET(i); + fs_config_t const * const p_config_j = FS_SECTION_VARS_GET(j); + + if (p_config_i->page_order == p_config_j->page_order) + { + // Error. + return FS_ERR_INVALID_CFG; + } + } + } + #endif + + // Assign pages to registered users, beginning with the ones with the highest + // priority, which will be assigned pages with the highest memory address. + + for (uint32_t i = 0; i < users; i++) + { + uint8_t max_priority = 0x00; + + for (uint32_t j = 0; j < users; j++) + { + fs_config_t * const p_config = FS_SECTION_VARS_GET(j); + + // Skip the one assigned during last iteration. + if (j == index_last) + { + continue; + } + + if (p_config->priority >= max_priority) + { + max_priority = p_config->priority; + index_max = j; + } + } + + fs_config_t * const p_config = FS_SECTION_VARS_GET(index_max); + + p_config->p_end_addr = p_current_end; + p_config->p_start_addr = p_current_end - (p_config->num_pages * FS_PAGE_SIZE_WORDS); + + p_current_end = p_config->p_start_addr; + + index_last = index_max; + } + + m_flags |= FS_FLAG_INITIALIZED; + + return FS_SUCCESS; +} + + +fs_ret_t fs_store(fs_config_t const * const p_config, + uint32_t const * const p_dest, + uint32_t const * const p_src, + uint16_t const length_words) +{ + fs_op_t * p_op; + + if (!(m_flags & FS_FLAG_INITIALIZED)) + { + return FS_ERR_NOT_INITIALIZED; + } + + if (!check_config(p_config)) + { + return FS_ERR_INVALID_CFG; + } + + if ((p_src == NULL) || (p_dest == NULL)) + { + return FS_ERR_NULL_ARG; + } + + // Check that both pointers are word aligned. + if (((uint32_t)p_src & 0x03) || + ((uint32_t)p_dest & 0x03)) + { + return FS_ERR_UNALIGNED_ADDR; + } + + // Check that the operation doesn't go outside the client's memory boundaries. + if ((p_config->p_start_addr > p_dest) || + (p_config->p_end_addr < (p_dest + length_words))) + { + return FS_ERR_INVALID_ADDR; + } + + if (length_words == 0) + { + return FS_ERR_INVALID_ARG; + } + + if (!queue_get_next_free(&p_op)) + { + return FS_ERR_QUEUE_FULL; + } + + // Initialize the operation. + p_op->p_config = p_config; + p_op->op_code = FS_OP_STORE; + p_op->store.p_src = p_src; + p_op->store.p_dest = p_dest; + p_op->store.length_words = length_words; + + queue_start(); + + return FS_SUCCESS; +} + + +fs_ret_t fs_erase(fs_config_t const * const p_config, + uint32_t const * const p_page_addr, + uint16_t const num_pages) +{ + fs_op_t * p_op; + + if (!(m_flags & FS_FLAG_INITIALIZED)) + { + return FS_ERR_NOT_INITIALIZED; + } + + if (!check_config(p_config)) + { + return FS_ERR_INVALID_CFG; + } + + if (p_page_addr == NULL) + { + return FS_ERR_NULL_ARG; + } + + // Check that the page is aligned to a page boundary. + if (((uint32_t)p_page_addr % FS_PAGE_SIZE) != 0) + { + return FS_ERR_UNALIGNED_ADDR; + } + + // Check that the operation doesn't go outside the client's memory boundaries. + if ((p_page_addr < p_config->p_start_addr) || + (p_page_addr + (FS_PAGE_SIZE_WORDS * num_pages) > p_config->p_end_addr)) + { + return FS_ERR_INVALID_ADDR; + } + + if (num_pages == 0) + { + return FS_ERR_INVALID_ARG; + } + + if (!queue_get_next_free(&p_op)) + { + return FS_ERR_QUEUE_FULL; + } + + // Initialize the operation. + p_op->p_config = p_config; + p_op->op_code = FS_OP_ERASE; + p_op->erase.page = ((uint32_t)p_page_addr / FS_PAGE_SIZE); + p_op->erase.pages_to_erase = num_pages; + + queue_start(); + + return FS_SUCCESS; +} + + +fs_ret_t fs_queued_op_count_get(uint32_t * const p_op_count) +{ + if (p_op_count == NULL) + { + return FS_ERR_NULL_ARG; + } + + *p_op_count = m_queue.count; + + return FS_SUCCESS; +} + + +void fs_sys_event_handler(uint32_t sys_evt) +{ + fs_op_t * const p_op = &m_queue.op[m_queue.rp]; + + if (m_flags & FS_FLAG_PROCESSING) + { + // A flash operation was initiated by this module. Handle the result. + switch (sys_evt) + { + case NRF_EVT_FLASH_OPERATION_SUCCESS: + on_operation_success(p_op); + break; + + case NRF_EVT_FLASH_OPERATION_ERROR: + on_operation_failure(p_op); + break; + } + } + else if ((m_flags & FS_FLAG_FLASH_REQ_PENDING)) + { + // A flash operation was initiated outside this module. + // A callback which indicates that it has finished was received. + m_flags &= ~FS_FLAG_FLASH_REQ_PENDING; + + // If there are any elements left in the queue, set FS_FLAG_PROCESSING. + if (m_queue.count > 0) + { + m_flags |= FS_FLAG_PROCESSING; + } + } + + // Resume processing the queue, if necessary. + queue_process(); +} +