Glimworm Beacons / nRF51822

Fork of nRF51822 by Nordic Semiconductor

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers pstorage.c Source File

pstorage.c

00001 /*
00002  * Copyright (c) Nordic Semiconductor ASA
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without modification,
00006  * are permitted provided that the following conditions are met:
00007  *
00008  *   1. Redistributions of source code must retain the above copyright notice, this
00009  *   list of conditions and the following disclaimer.
00010  *
00011  *   2. Redistributions in binary form must reproduce the above copyright notice, this
00012  *   list of conditions and the following disclaimer in the documentation and/or
00013  *   other materials provided with the distribution.
00014  *
00015  *   3. Neither the name of Nordic Semiconductor ASA nor the names of other
00016  *   contributors to this software may be used to endorse or promote products
00017  *   derived from this software without specific prior written permission.
00018  *
00019  *
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00021  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00022  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00023  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
00024  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00025  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00026  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
00027  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00028  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00029  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030  *
00031  */
00032 
00033 #include "pstorage.h "
00034 #include <stdlib.h>
00035 #include <stdint.h>
00036 #include <string.h>
00037 #include "nordic_common.h"
00038 #include "nrf_error.h"
00039 #include "nrf_assert.h"
00040 // #include "nrf.h"
00041 #include "nrf_soc.h"
00042 #include "app_util.h"
00043 
00044 #define INVALID_OPCODE     0x00                            /**< Invalid op code identifier. */
00045 #define SOC_MAX_WRITE_SIZE 1024                            /**< Maximum write size allowed for a single call to \ref sd_flash_write as specified in the SoC API. */
00046 #define RAW_MODE_APP_ID    (PSTORAGE_MAX_APPLICATIONS + 1) /**< Application id for raw mode. */
00047 
00048 /**
00049  * @defgroup api_param_check API Parameters check macros.
00050  *
00051  * @details Macros that verify parameters passed to the module in the APIs. These macros
00052  *          could be mapped to nothing in final versions of code to save execution and size.
00053  *
00054  * @{
00055  */
00056 
00057 /**
00058  * @brief Check if the input pointer is NULL, if it is returns NRF_ERROR_NULL.
00059  */
00060 #define NULL_PARAM_CHECK(PARAM)                                                                   \
00061         if ((PARAM) == NULL)                                                                      \
00062         {                                                                                         \
00063             return NRF_ERROR_NULL;                                                                \
00064         }
00065 
00066 /**
00067  * @brief Verifies the module identifier supplied by the application is within permissible
00068  *        range.
00069  */
00070 #define MODULE_ID_RANGE_CHECK(ID)                                                                 \
00071         if ((((ID)->module_id) >= PSTORAGE_MAX_APPLICATIONS) ||                                   \
00072             (m_app_table[(ID)->module_id].cb == NULL))                                            \
00073         {                                                                                         \
00074             return NRF_ERROR_INVALID_PARAM;                                                       \
00075         }
00076 
00077 /**
00078  * @brief Verifies the block identifier supplied by the application is within the permissible
00079  *        range.
00080  */
00081 #define BLOCK_ID_RANGE_CHECK(ID)                                                                  \
00082         if (((ID)->block_id) >= (m_app_table[(ID)->module_id].base_id +                           \
00083             (m_app_table[(ID)->module_id].block_count * MODULE_BLOCK_SIZE(ID))))                  \
00084         {                                                                                         \
00085             return NRF_ERROR_INVALID_PARAM;                                                       \
00086         }
00087 
00088 /**
00089  * @brief Verifies the block size requested by the application can be supported by the module.
00090  */
00091 #define BLOCK_SIZE_CHECK(X)                                                                       \
00092         if (((X) > PSTORAGE_MAX_BLOCK_SIZE) || ((X) < PSTORAGE_MIN_BLOCK_SIZE))                   \
00093         {                                                                                         \
00094             return NRF_ERROR_INVALID_PARAM;                                                       \
00095         }
00096 
00097 /**
00098  * @brief Verifies block size requested by Application in registration API.
00099  */
00100 #define BLOCK_COUNT_CHECK(COUNT, SIZE)                                                            \
00101         if (((COUNT) == 0) ||                                                                     \
00102             ((m_next_page_addr + ((COUNT) *(SIZE)) > PSTORAGE_SWAP_ADDR)) ||                      \
00103             ((((COUNT) * (SIZE)) > PSTORAGE_FLASH_PAGE_SIZE) && (PSTORAGE_FLASH_PAGE_SIZE % (SIZE))))  \
00104         {                                                                                         \
00105             return NRF_ERROR_INVALID_PARAM;                                                       \
00106         }
00107 
00108 /**
00109  * @brief Verifies size parameter provided by application in API.
00110  */
00111 #define SIZE_CHECK(ID, SIZE)                                                                      \
00112         if(((SIZE) == 0) || ((SIZE) > MODULE_BLOCK_SIZE(ID)))                                     \
00113         {                                                                                         \
00114             return NRF_ERROR_INVALID_PARAM;                                                       \
00115         }
00116 
00117 /**
00118  * @brief Verifies offset parameter provided by application in API.
00119  */
00120 #define OFFSET_CHECK(ID, OFFSET, SIZE)                                                            \
00121         if(((SIZE) + (OFFSET)) > MODULE_BLOCK_SIZE(ID))                                           \
00122         {                                                                                         \
00123             return NRF_ERROR_INVALID_PARAM;                                                       \
00124         }
00125 
00126 #ifdef PSTORAGE_RAW_MODE_ENABLE
00127 
00128 /**
00129  * @brief Verifies the module identifier supplied by the application is registered for raw mode.
00130  */
00131 #define MODULE_RAW_ID_RANGE_CHECK(ID)                                                             \
00132         if ((PSTORAGE_MAX_APPLICATIONS+1 != ((ID)->module_id)) ||                                 \
00133             (m_raw_app_table.cb == NULL))                                                         \
00134         {                                                                                         \
00135             return NRF_ERROR_INVALID_PARAM;                                                       \
00136         }
00137 
00138 #endif // PSTORAGE_RAW_MODE_ENABLE
00139 
00140 /**@} */
00141 
00142 
00143 /**@brief    Verify module's initialization status.
00144  *
00145  * @details   Verify module's initialization status. Returns NRF_ERROR_INVALID_STATE in case a
00146  *            module API is called without initializing the module.
00147  */
00148 #define VERIFY_MODULE_INITIALIZED()                                                               \
00149         do                                                                                        \
00150         {                                                                                         \
00151             if (!m_module_initialized)                                                            \
00152             {                                                                                     \
00153                  return NRF_ERROR_INVALID_STATE;                                                  \
00154             }                                                                                     \
00155         } while(0)
00156 
00157 /**@brief Macro to fetch the block size registered for the module. */
00158 #define MODULE_BLOCK_SIZE(ID) (m_app_table[(ID)->module_id].block_size)
00159 
00160 
00161 /** @brief States for the Update/Clear swap backup state machine. */
00162 typedef enum
00163 {
00164     STATE_INIT,                /**< State for indicating that swap can be used when using update/clear API. */
00165     STATE_DATA_TO_SWAP_WRITE,  /**< State for doing backup of data page into the swap page when using update/clear API. */
00166     STATE_DATA_ERASE,          /**< State for erasing data page when using update/clear API. */
00167     STATE_HEAD_RESTORE,        /**< State for restoring head (beginning) of backed up data from swap to data page when using update/clear API. */
00168     STATE_TAIL_RESTORE,        /**< State for restoring tail (end) of backed up data from swap to data page when using update/clear API. */
00169     STATE_NEW_BODY_WRITE,      /**< State for writing body (middle) data to the data page when using update/clear API. */
00170     STATE_SWAP_ERASE,          /**< State for erasing the swap page when using the update/clear API. */
00171     STATE_COMPLETE,            /**< State for indicating that update/clear sequence is completed internal in the module when using the update/clear API. */
00172     STATE_SWAP_DIRTY           /**< State for initializing the swap region on module initialization. */
00173 } swap_backup_state_t;
00174 
00175 
00176 /**
00177  * @brief Application registration information.
00178  *
00179  * @details Define application specific information that application needs to maintain to be able
00180  *          to process requests from each one of them.
00181  */
00182 typedef struct
00183 {
00184     pstorage_ntf_cb_t    cb;             /**< Callback registered with the module to be notified of result of flash access.  */
00185     pstorage_block_t     base_id;        /**< Base block id assigned to the module. */
00186     pstorage_size_t      block_size;     /**< Size of block for the module. */
00187     pstorage_size_t      block_count;    /**< Number of block requested by application. */
00188     pstorage_size_t      num_of_pages;   /**< Variable to remember how many pages have been allocated for this module. This information is used for clearing of block, so that application does not need to have knowledge of number of pages its using. */
00189 } pstorage_module_table_t;
00190 
00191 
00192 #ifdef PSTORAGE_RAW_MODE_ENABLE
00193 /**
00194  * @brief Application registration information.
00195  *
00196  * @details Define application specific information that application registered for raw mode.
00197  */
00198 typedef struct
00199 {
00200     pstorage_ntf_cb_t      cb;             /**< Callback registered with the module to be notified of result of flash access.  */
00201     uint16_t               num_of_pages;   /**< Variable to remember how many pages have been allocated for this module. This information is used for clearing of block, so that application does not need to have knowledge of number of pages its using. */
00202 } pstorage_raw_module_table_t;
00203 #endif // PSTORAGE_RAW_MODE_ENABLE
00204 
00205 
00206 /**
00207  * @brief Defines command queue element.
00208  *
00209  * @details Defines command queue element. Each element encapsulates needed information to process
00210  *          a flash access command.
00211  */
00212 typedef struct
00213 {
00214     uint8_t              op_code;       /**< Identifies flash access operation being queued. Element is free if op-code is INVALID_OPCODE. */
00215     pstorage_size_t      size;          /**< Identifies size in bytes requested for the operation. */
00216     pstorage_size_t      offset;        /**< Offset requested by the application for access operation. */
00217     pstorage_handle_t    storage_addr;  /**< Address/Identifier for persistent memory. */
00218     uint8_t *            p_data_addr;   /**< Address/Identifier for data memory. This is assumed to be resident memory. */
00219 } cmd_queue_element_t;
00220 
00221 
00222 /**
00223  * @brief Defines command queue, an element is free if op_code field is not invalid.
00224  *
00225  * @details Defines commands enqueued for flash access. At any point of time, this queue has one or
00226  *          more flash access operation pending if the count field is not zero. When the queue is
00227  *          not empty, the rp (read pointer) field points to the flash access command in progress
00228  *          or to requested next. The queue implements a simple first in first out algorithm.
00229  *          Data addresses are assumed to be resident.
00230  */
00231 typedef struct
00232 {
00233     uint8_t              rp;                           /**< Read pointer, pointing to flash access that is ongoing or to be requested next. */
00234     uint8_t              count;                        /**< Number of elements in the queue.  */
00235     bool                 flash_access;                 /**< Flag to ensure an flash event received is for an request issued by the module. */
00236     cmd_queue_element_t  cmd[PSTORAGE_CMD_QUEUE_SIZE]; /**< Array to maintain flash access operation details. */
00237 } cmd_queue_t;
00238 
00239 
00240 static cmd_queue_t         m_cmd_queue;                  /**< Flash operation request queue. */
00241 static pstorage_size_t     m_next_app_instance;          /**< Points to the application module instance that can be allocated next. */
00242 static uint32_t            m_next_page_addr;             /**< Points to the flash address that can be allocated to a module next, this is needed as blocks of a module can span across flash pages. */
00243 static pstorage_size_t     m_round_val;                  /**< Round value for multiple round operations. For erase operations, the round value will contain current round counter which is identical to number of pages erased. For store operations, the round value contains current round of operation * SOC_MAX_WRITE_SIZE to ensure each store to the SoC Flash API is within the SoC limit. */
00244 static bool                m_module_initialized = false; /**< Flag for checking if module has been initialized. */
00245 static swap_backup_state_t m_swap_state;                 /**< Swap page state. */
00246 
00247 
00248 static pstorage_module_table_t m_app_table[PSTORAGE_MAX_APPLICATIONS]; /**< Registered application information table. */
00249 
00250 #ifdef PSTORAGE_RAW_MODE_ENABLE
00251 static pstorage_raw_module_table_t m_raw_app_table;                    /**< Registered application information table for raw mode. */
00252 #endif // PSTORAGE_RAW_MODE_ENABLE
00253 
00254 
00255 /**
00256  * @brief Routine called to actually issue the flash access request to the SoftDevice.
00257  *
00258  * @retval    NRF_SUCCESS    on success, else an error code indicating reason for failure.
00259  */
00260 static uint32_t cmd_process(void);
00261 
00262 
00263 /**
00264  * @brief Routine to notify application of any errors.
00265  *
00266  * @param[in] result Result of event being notified.
00267  */
00268 static void app_notify(uint32_t result);
00269 
00270 
00271 /**
00272  * @defgroup utility_functions Utility internal functions.
00273  * @{
00274  * @details Utility functions needed for interfacing with flash through SoC APIs.
00275  * SoC APIs are non blocking and provide the result of flash access through an event.
00276  *
00277  * @note Only one flash access operation is permitted at a time by SoC. Hence a queue is
00278  * maintained by this module.
00279  */
00280 
00281 
00282 /**
00283  * @brief Initializes command queue element.
00284  *
00285  * @param[in] index Element index being initialized.
00286  */
00287 static void cmd_queue_element_init(uint32_t index)
00288 {
00289     // Internal function and checks on range of index can be avoided.
00290     m_cmd_queue.cmd[index].op_code                = INVALID_OPCODE;
00291     m_cmd_queue.cmd[index].size                   = 0;
00292     m_cmd_queue.cmd[index].storage_addr.module_id = PSTORAGE_MAX_APPLICATIONS;
00293     m_cmd_queue.cmd[index].storage_addr.block_id  = 0;
00294     m_cmd_queue.cmd[index].p_data_addr            = NULL;
00295     m_cmd_queue.cmd[index].offset                 = 0;
00296 }
00297 
00298 
00299 /**
00300  * @brief Initializes command queue.
00301  */
00302 static void cmd_queue_init(void)
00303 {
00304     uint32_t cmd_index;
00305 
00306     m_round_val              = 0;
00307     m_swap_state             = STATE_INIT;
00308     m_cmd_queue.rp           = 0;
00309     m_cmd_queue.count        = 0;
00310     m_cmd_queue.flash_access = false;
00311 
00312     for (cmd_index = 0; cmd_index < PSTORAGE_CMD_QUEUE_SIZE; cmd_index++)
00313     {
00314         cmd_queue_element_init(cmd_index);
00315     }
00316 }
00317 
00318 
00319 /**
00320  * @brief Routine to enqueue a flash access operation.
00321  *
00322  * @param[in] opcode         Identifies operation requested to be enqueued.
00323  * @param[in] p_storage_addr Identiifes module and flash address on which operation is requested.
00324  * @param[in] p_data_addr    Identifies data address for flash access.
00325  * @param[in] size           Size in bytes of data requested for the access operation.
00326  * @param[in] offset         Offset within the flash memory block at which operation is requested.
00327  *
00328  * @retval    NRF_SUCCESS    on success, else an error code indicating reason for failure.
00329  *
00330  * @note All paramater check should be performed before requesting in an enqueue.
00331  */
00332 static uint32_t cmd_queue_enqueue(uint8_t             opcode,
00333                                   pstorage_handle_t * p_storage_addr,
00334                                   uint8_t           * p_data_addr,
00335                                   pstorage_size_t     size,
00336                                   pstorage_size_t     offset)
00337 {
00338     uint32_t retval;
00339     uint8_t  write_index = 0;
00340 
00341     if (m_cmd_queue.count != PSTORAGE_CMD_QUEUE_SIZE)
00342     {
00343         // Enqueue the command if it is queue is not full.
00344         write_index = m_cmd_queue.rp + m_cmd_queue.count;
00345 
00346         if (write_index >= PSTORAGE_CMD_QUEUE_SIZE)
00347         {
00348             write_index -= PSTORAGE_CMD_QUEUE_SIZE;
00349         }
00350 
00351         m_cmd_queue.cmd[write_index].op_code      = opcode;
00352         m_cmd_queue.cmd[write_index].p_data_addr  = p_data_addr;
00353         m_cmd_queue.cmd[write_index].storage_addr = (*p_storage_addr);
00354         m_cmd_queue.cmd[write_index].size         = size;
00355         m_cmd_queue.cmd[write_index].offset       = offset;
00356         retval                                    = NRF_SUCCESS;
00357         if (m_cmd_queue.flash_access == false)
00358         {
00359             retval = cmd_process();
00360             if (retval == NRF_ERROR_BUSY)
00361             {
00362                 // In case of busy error code, it is possible to attempt to access flash.
00363                 retval = NRF_SUCCESS;
00364             }
00365         }
00366         m_cmd_queue.count++;
00367     }
00368     else
00369     {
00370         retval = NRF_ERROR_NO_MEM;
00371     }
00372 
00373     return retval;
00374 }
00375 
00376 
00377 /**
00378  * @brief Dequeues a command element.
00379  *
00380  * @retval    NRF_SUCCESS    on success, else an error code indicating reason for failure.
00381  */
00382 static uint32_t cmd_queue_dequeue(void)
00383 {
00384     uint32_t retval;
00385     retval = NRF_SUCCESS;
00386 
00387     // If any flash operation is enqueued, schedule.
00388     if (m_cmd_queue.count > 0)
00389     {
00390         retval = cmd_process();
00391         if (retval != NRF_SUCCESS)
00392         {
00393             // Flash could be accessed by modules other than Bond Manager, hence a busy error is
00394             // acceptable, but any other error needs to be indicated to the bond manager.
00395             if (retval == NRF_ERROR_BUSY)
00396             {
00397                 // In case of busy error code, it is possible to attempt to access flash.
00398                 retval = NRF_SUCCESS;
00399             }
00400         }
00401     }
00402     else
00403     {
00404         // No flash access request pending.
00405     }
00406 
00407     return retval;
00408 }
00409 
00410 
00411 /**
00412  * @brief Routine to notify application of any errors.
00413  *
00414  * @param[in] result Result of event being notified.
00415  */
00416 static void app_notify(uint32_t result)
00417 {
00418     pstorage_ntf_cb_t ntf_cb;
00419     uint8_t           op_code = m_cmd_queue.cmd[m_cmd_queue.rp].op_code;
00420 
00421 #ifdef PSTORAGE_RAW_MODE_ENABLE
00422     if (m_cmd_queue.cmd[m_cmd_queue.rp].storage_addr.module_id == RAW_MODE_APP_ID)
00423     {
00424         ntf_cb = m_raw_app_table.cb;
00425     }
00426     else
00427 #endif // PSTORAGE_RAW_MODE_ENABLE
00428     {
00429         ntf_cb = m_app_table[m_cmd_queue.cmd[m_cmd_queue.rp].storage_addr.module_id].cb;
00430     }
00431 
00432     // Indicate result to client.
00433     // For PSTORAGE_CLEAR_OP_CODE no size is returned as the size field is used only internally
00434     // for clients registering multiple pages.
00435     ntf_cb(&m_cmd_queue.cmd[m_cmd_queue.rp].storage_addr,
00436            op_code,
00437            result,
00438            m_cmd_queue.cmd[m_cmd_queue.rp].p_data_addr,
00439            m_cmd_queue.cmd[m_cmd_queue.rp].size);
00440 }
00441 
00442 
00443 /**
00444  * @brief Handles Flash Access Result Events declared in pstorage_platform.h.
00445  *
00446  * @param[in] sys_evt System event to be handled.
00447  */
00448 void pstorage_sys_event_handler(uint32_t sys_evt)
00449 {
00450     uint32_t retval = NRF_SUCCESS;
00451 
00452     // Its possible the flash access was not initiated by bond manager, hence
00453     // event is processed only if the event triggered was for an operation requested by the
00454     // bond manager.
00455     if (m_cmd_queue.flash_access == true)
00456     {
00457         cmd_queue_element_t * p_cmd;
00458 
00459         m_cmd_queue.flash_access = false;
00460 
00461         if (m_swap_state == STATE_SWAP_DIRTY)
00462         {
00463             if (sys_evt == NRF_EVT_FLASH_OPERATION_SUCCESS)
00464             {
00465                 m_swap_state = STATE_INIT;
00466             }
00467             else
00468             {
00469                 // If clearing the swap fails, set the application back to un-initialized, to give
00470                 // the application a chance for a retry.
00471                 m_module_initialized = false;
00472             }
00473 
00474             // Schedule any queued flash access operations.
00475             retval = cmd_queue_dequeue();
00476             if (retval != NRF_SUCCESS)
00477             {
00478                 app_notify(retval);
00479             }
00480             return;
00481         }
00482 
00483         switch (sys_evt)
00484         {
00485             case NRF_EVT_FLASH_OPERATION_SUCCESS:
00486             {
00487                 p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp];
00488                 m_round_val++;
00489 
00490                 const bool store_finished =
00491                     ((p_cmd->op_code == PSTORAGE_STORE_OP_CODE) &&
00492                      ((m_round_val * SOC_MAX_WRITE_SIZE) >= p_cmd->size));
00493 
00494                 const bool update_finished =
00495                     ((p_cmd->op_code == PSTORAGE_UPDATE_OP_CODE) &&
00496                      (m_swap_state == STATE_COMPLETE));
00497 
00498                 const bool clear_block_finished =
00499                     ((p_cmd->op_code == PSTORAGE_CLEAR_OP_CODE) &&
00500                      (m_swap_state == STATE_COMPLETE));
00501 
00502                 const bool clear_all_finished =
00503                     ((p_cmd->op_code == PSTORAGE_CLEAR_OP_CODE) &&
00504                      ((m_round_val * SOC_MAX_WRITE_SIZE) >= p_cmd->size) &&
00505                      (m_swap_state == STATE_INIT));
00506 
00507                 if (update_finished ||
00508                     clear_block_finished ||
00509                     clear_all_finished ||
00510                     store_finished)
00511                 {
00512                     m_swap_state = STATE_INIT;
00513 
00514                     app_notify(retval);
00515 
00516                     // Initialize/free the element as it is now processed.
00517                     cmd_queue_element_init(m_cmd_queue.rp);
00518                     m_round_val = 0;
00519                     m_cmd_queue.count--;
00520                     m_cmd_queue.rp++;
00521 
00522                     if (m_cmd_queue.rp >= PSTORAGE_CMD_QUEUE_SIZE)
00523                     {
00524                         m_cmd_queue.rp -= PSTORAGE_CMD_QUEUE_SIZE;
00525                     }
00526                 }
00527                 // Schedule any queued flash access operations.
00528                 retval = cmd_queue_dequeue();
00529 
00530                 if (retval != NRF_SUCCESS)
00531                 {
00532                     app_notify(retval);
00533                 }
00534             }
00535             break;
00536 
00537             case NRF_EVT_FLASH_OPERATION_ERROR:
00538                 app_notify(NRF_ERROR_TIMEOUT);
00539                 break;
00540 
00541             default:
00542                 // No implementation needed.
00543                 break;
00544 
00545         }
00546     }
00547 }
00548 
00549 
00550 /** @brief Function for handling flash accesses when using swap.
00551  *
00552  * __________________________________________________________
00553  * |                       Page                             |
00554  * |________________________________________________________|
00555  * | head | affected body (to be updated or cleared) | tail |
00556  * |______|__________________________________________|______|
00557  *
00558  * @param[in] p_cmd          Queue element being processed.
00559  * @param[in] page_number    The affected page number.
00560  * @param[in] head_word_size Size of the head in number of words.
00561  * @param[in] tail_word_size Size of the tail in number of words.
00562  *
00563  * @retval    NRF_SUCCESS    on success, else an error code indicating reason for failure.
00564  */
00565 static uint32_t swap_state_process(cmd_queue_element_t * p_cmd,
00566                                    uint32_t              page_number,
00567                                    uint32_t              head_word_size,
00568                                    uint32_t              tail_word_size)
00569 {
00570     uint32_t retval = NRF_ERROR_INTERNAL;
00571 
00572     // Adjust entry point to state machine if needed. When we update has no head or tail its
00573     // no need for using the swap.
00574     if (m_swap_state == STATE_INIT)
00575     {
00576         if ((head_word_size == 0) && (tail_word_size == 0))
00577         {
00578             // Only skip swap usage if the new data fills a whole flash page.
00579             m_swap_state = STATE_DATA_ERASE;
00580         }
00581         else
00582         {
00583             // Else start backing up application data to swap.
00584             m_swap_state = STATE_DATA_TO_SWAP_WRITE;
00585         }
00586     }
00587 
00588     switch (m_swap_state)
00589     {
00590         case STATE_DATA_TO_SWAP_WRITE:
00591             // Backup previous content into swap page.
00592             retval = sd_flash_write((uint32_t *)(PSTORAGE_SWAP_ADDR),
00593                                     (uint32_t *)(page_number * PSTORAGE_FLASH_PAGE_SIZE),
00594                                     PSTORAGE_FLASH_PAGE_SIZE / sizeof(uint32_t));
00595             if (retval == NRF_SUCCESS)
00596             {
00597                 m_swap_state = STATE_DATA_ERASE;
00598             }
00599             break;
00600 
00601         case STATE_DATA_ERASE:
00602             // Clear the application data page.
00603             retval = sd_flash_page_erase(page_number);
00604             if (retval == NRF_SUCCESS)
00605             {
00606                 if (head_word_size == 0)
00607                 {
00608                     if (tail_word_size == 0)
00609                     {
00610                         if (p_cmd->op_code == PSTORAGE_CLEAR_OP_CODE)
00611                         {
00612                             m_swap_state = STATE_COMPLETE;
00613                         }
00614                         else
00615                         {
00616                             m_swap_state = STATE_NEW_BODY_WRITE;
00617                         }
00618                     }
00619                     else
00620                     {
00621                         m_swap_state = STATE_TAIL_RESTORE;
00622                     }
00623                 }
00624                 else
00625                 {
00626                     m_swap_state = STATE_HEAD_RESTORE;
00627                 }
00628             }
00629             break;
00630 
00631         case STATE_HEAD_RESTORE:
00632             // Restore head from swap to application data page.
00633             retval = sd_flash_write((uint32_t *)(page_number * PSTORAGE_FLASH_PAGE_SIZE),
00634                                     (uint32_t *)PSTORAGE_SWAP_ADDR,
00635                                     head_word_size);
00636             if (retval == NRF_SUCCESS)
00637             {
00638                 if (tail_word_size == 0)
00639                 {
00640                     if (p_cmd->op_code == PSTORAGE_CLEAR_OP_CODE)
00641                     {
00642                         m_swap_state = STATE_SWAP_ERASE;
00643                     }
00644                     else
00645                     {
00646                         m_swap_state = STATE_NEW_BODY_WRITE;
00647                     }
00648                 }
00649                 else
00650                 {
00651                     m_swap_state = STATE_TAIL_RESTORE;
00652                 }
00653             }
00654             break;
00655 
00656         case STATE_TAIL_RESTORE:
00657             // Restore tail from swap to application data page.
00658             retval = sd_flash_write((uint32_t *)((page_number * PSTORAGE_FLASH_PAGE_SIZE) +
00659                                                  (head_word_size * sizeof(uint32_t)) +
00660                                                  p_cmd->size),
00661                                     (uint32_t *)(PSTORAGE_SWAP_ADDR +
00662                                                  (head_word_size * sizeof(uint32_t)) +
00663                                                  p_cmd->size),
00664                                     tail_word_size);
00665             if (retval == NRF_SUCCESS)
00666             {
00667                 if (p_cmd->op_code == PSTORAGE_CLEAR_OP_CODE)
00668                 {
00669                     m_swap_state = STATE_SWAP_ERASE;
00670                 }
00671                 else
00672                 {
00673                     m_swap_state = STATE_NEW_BODY_WRITE;
00674                 }
00675             }
00676             break;
00677 
00678         case STATE_NEW_BODY_WRITE:
00679             // Write new data (body) to application data page.
00680             retval = sd_flash_write((uint32_t *)((page_number * PSTORAGE_FLASH_PAGE_SIZE) +
00681                                                  (head_word_size * sizeof(uint32_t))),
00682                                     (uint32_t *)p_cmd->p_data_addr,
00683                                     p_cmd->size / sizeof(uint32_t));
00684             if (retval == NRF_SUCCESS)
00685             {
00686                 if ((head_word_size == 0) && (tail_word_size == 0))
00687                 {
00688                     m_swap_state = STATE_COMPLETE;
00689                 }
00690                 else
00691                 {
00692                     m_swap_state = STATE_SWAP_ERASE;
00693                 }
00694             }
00695             break;
00696 
00697         case STATE_SWAP_ERASE:
00698             // Clear the swap page for subsequent use.
00699             retval = sd_flash_page_erase(PSTORAGE_SWAP_ADDR / PSTORAGE_FLASH_PAGE_SIZE);
00700             if (retval == NRF_SUCCESS)
00701             {
00702                 m_swap_state = STATE_COMPLETE;
00703             }
00704             break;
00705 
00706         default:
00707             break;
00708     }
00709 
00710     return retval;
00711 }
00712 
00713 
00714 /**
00715  * @brief Routine called to actually issue the flash access request to the SoftDevice.
00716  *
00717  * @retval    NRF_SUCCESS    on success, else an error code indicating reason for failure.
00718  */
00719 static uint32_t cmd_process(void)
00720 {
00721     uint32_t              retval;
00722     uint32_t              storage_addr;
00723     cmd_queue_element_t * p_cmd;
00724 
00725     retval = NRF_ERROR_FORBIDDEN;
00726 
00727     p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp];
00728 
00729     storage_addr = p_cmd->storage_addr.block_id;
00730 
00731     switch (p_cmd->op_code)
00732     {
00733         case PSTORAGE_STORE_OP_CODE:
00734         {
00735             uint32_t  size;
00736             uint32_t  offset;
00737             uint8_t * p_data_addr = p_cmd->p_data_addr;
00738 
00739             offset        = (m_round_val * SOC_MAX_WRITE_SIZE);
00740             size          = p_cmd->size - offset;
00741             p_data_addr  += offset;
00742             storage_addr += (p_cmd->offset + offset);
00743 
00744             if (size < SOC_MAX_WRITE_SIZE)
00745             {
00746                 retval = sd_flash_write(((uint32_t *)storage_addr),
00747                                         (uint32_t *)p_data_addr,
00748                                         size / sizeof(uint32_t));
00749             }
00750             else
00751             {
00752                 retval = sd_flash_write(((uint32_t *)storage_addr),
00753                                         (uint32_t *)p_data_addr,
00754                                         SOC_MAX_WRITE_SIZE / sizeof(uint32_t));
00755             }
00756         }
00757         break;
00758 
00759         case PSTORAGE_CLEAR_OP_CODE:
00760         {
00761             // Calculate page number before clearing.
00762             uint32_t page_number;
00763 
00764             pstorage_size_t block_size =
00765                 m_app_table[p_cmd->storage_addr.module_id].block_size;
00766 
00767             pstorage_size_t block_count =
00768                 m_app_table[p_cmd->storage_addr.module_id].block_count;
00769 
00770             pstorage_block_t base_address =
00771                 m_app_table[p_cmd->storage_addr.module_id].base_id;
00772 
00773             // If the whole module should be cleared.
00774             if (((base_address == storage_addr) && (block_size * block_count == p_cmd->size)) ||
00775                 (p_cmd->storage_addr.module_id == RAW_MODE_APP_ID))
00776             {
00777                 page_number = ((storage_addr / PSTORAGE_FLASH_PAGE_SIZE) + m_round_val);
00778 
00779                 retval = sd_flash_page_erase(page_number);
00780             }
00781             // If one block is to be erased.
00782             else
00783             {
00784                 page_number = (storage_addr / PSTORAGE_FLASH_PAGE_SIZE);
00785 
00786                 uint32_t head_word_size = (
00787                     storage_addr -
00788                     (page_number * PSTORAGE_FLASH_PAGE_SIZE)
00789                     ) / sizeof(uint32_t);
00790 
00791                 uint32_t tail_word_size = (
00792                     ((page_number + 1) * PSTORAGE_FLASH_PAGE_SIZE) -
00793                     (storage_addr + p_cmd->size)
00794                     ) / sizeof(uint32_t);
00795 
00796                 retval = swap_state_process(p_cmd,
00797                                             page_number,
00798                                             head_word_size,
00799                                             tail_word_size);
00800             }
00801         }
00802         break;
00803 
00804         case PSTORAGE_UPDATE_OP_CODE:
00805         {
00806             uint32_t page_number = (storage_addr / PSTORAGE_FLASH_PAGE_SIZE);
00807 
00808             uint32_t head_word_size = (
00809                 storage_addr + p_cmd->offset -
00810                 (page_number * PSTORAGE_FLASH_PAGE_SIZE)
00811                 ) / sizeof(uint32_t);
00812 
00813             uint32_t tail_word_size = (
00814                 ((page_number + 1) * PSTORAGE_FLASH_PAGE_SIZE) -
00815                 (storage_addr + p_cmd->offset + p_cmd->size)
00816                 ) / sizeof(uint32_t);
00817 
00818             retval = swap_state_process(p_cmd, page_number, head_word_size, tail_word_size);
00819         }
00820         break;
00821 
00822         default:
00823             // Should never reach here.
00824             break;
00825     }
00826 
00827     if (retval == NRF_SUCCESS)
00828     {
00829         m_cmd_queue.flash_access = true;
00830     }
00831 
00832     return retval;
00833 }
00834 /** @} */
00835 
00836 
00837 uint32_t pstorage_init(void)
00838 {
00839     uint32_t retval;
00840 
00841     cmd_queue_init();
00842 
00843     m_next_app_instance = 0;
00844     m_next_page_addr    = PSTORAGE_DATA_START_ADDR;
00845     m_round_val         = 0;
00846 
00847     for (uint32_t index = 0; index < PSTORAGE_MAX_APPLICATIONS; index++)
00848     {
00849         m_app_table[index].cb           = NULL;
00850         m_app_table[index].block_size   = 0;
00851         m_app_table[index].num_of_pages = 0;
00852         m_app_table[index].block_count  = 0;
00853     }
00854 
00855 #ifdef PSTORAGE_RAW_MODE_ENABLE
00856     m_raw_app_table.cb           = NULL;
00857     m_raw_app_table.num_of_pages = 0;
00858     m_module_initialized         = true;
00859     m_swap_state                 = STATE_INIT;
00860 
00861     retval = NRF_SUCCESS;
00862 #else
00863     m_swap_state = STATE_SWAP_DIRTY;
00864 
00865     // Erase swap region in case it is dirty.
00866     retval = sd_flash_page_erase(PSTORAGE_SWAP_ADDR / PSTORAGE_FLASH_PAGE_SIZE);
00867     if (retval == NRF_SUCCESS)
00868     {
00869         m_cmd_queue.flash_access = true;
00870         m_module_initialized     = true;
00871     }
00872 #endif //PSTORAGE_RAW_MODE_ENABLE
00873 
00874     return retval;
00875 }
00876 
00877 
00878 uint32_t pstorage_register(pstorage_module_param_t * p_module_param,
00879                            pstorage_handle_t       * p_block_id)
00880 {
00881     uint16_t page_count;
00882     uint32_t total_size;
00883 
00884     VERIFY_MODULE_INITIALIZED();
00885     NULL_PARAM_CHECK(p_module_param);
00886     NULL_PARAM_CHECK(p_block_id);
00887     NULL_PARAM_CHECK(p_module_param->cb);
00888     BLOCK_SIZE_CHECK(p_module_param->block_size);
00889     BLOCK_COUNT_CHECK(p_module_param->block_count, p_module_param->block_size);
00890 
00891     // Block size should be a multiple of word size.
00892     if (!((p_module_param->block_size % sizeof(uint32_t)) == 0))
00893     {
00894         return NRF_ERROR_INVALID_PARAM;
00895     }
00896 
00897     if (m_next_app_instance == PSTORAGE_MAX_APPLICATIONS)
00898     {
00899         return NRF_ERROR_NO_MEM;
00900     }
00901 
00902     p_block_id->module_id = m_next_app_instance;
00903     p_block_id->block_id  = m_next_page_addr;
00904 
00905     m_app_table[m_next_app_instance].base_id     = p_block_id->block_id;
00906     m_app_table[m_next_app_instance].cb          = p_module_param->cb;
00907     m_app_table[m_next_app_instance].block_size  = p_module_param->block_size;
00908     m_app_table[m_next_app_instance].block_count = p_module_param->block_count;
00909 
00910     // Calculate number of flash pages allocated for the device.
00911     page_count = 0;
00912     total_size = p_module_param->block_size * p_module_param->block_count;
00913     do
00914     {
00915         page_count++;
00916         if (total_size > PSTORAGE_FLASH_PAGE_SIZE)
00917         {
00918             total_size -= PSTORAGE_FLASH_PAGE_SIZE;
00919         }
00920         else
00921         {
00922             total_size = 0;
00923         }
00924         m_next_page_addr += PSTORAGE_FLASH_PAGE_SIZE;
00925     }
00926     while (total_size >= PSTORAGE_FLASH_PAGE_SIZE);
00927 
00928     m_app_table[m_next_app_instance].num_of_pages = page_count;
00929     m_next_app_instance++;
00930 
00931     return NRF_SUCCESS;
00932 }
00933 
00934 
00935 uint32_t pstorage_block_identifier_get(pstorage_handle_t * p_base_id,
00936                                        pstorage_size_t     block_num,
00937                                        pstorage_handle_t * p_block_id)
00938 {
00939     pstorage_handle_t temp_id;
00940 
00941     VERIFY_MODULE_INITIALIZED();
00942     NULL_PARAM_CHECK(p_base_id);
00943     NULL_PARAM_CHECK(p_block_id);
00944     MODULE_ID_RANGE_CHECK(p_base_id);
00945 
00946     temp_id           = (*p_base_id);
00947     temp_id.block_id += (block_num * MODULE_BLOCK_SIZE(p_base_id));
00948 
00949     BLOCK_ID_RANGE_CHECK(&temp_id);
00950 
00951     (*p_block_id) = temp_id;
00952 
00953     return NRF_SUCCESS;
00954 }
00955 
00956 
00957 uint32_t pstorage_store(pstorage_handle_t * p_dest,
00958                         uint8_t           * p_src,
00959                         pstorage_size_t     size,
00960                         pstorage_size_t     offset)
00961 {
00962     VERIFY_MODULE_INITIALIZED();
00963     NULL_PARAM_CHECK(p_src);
00964     NULL_PARAM_CHECK(p_dest);
00965     MODULE_ID_RANGE_CHECK(p_dest);
00966     BLOCK_ID_RANGE_CHECK(p_dest);
00967     SIZE_CHECK(p_dest, size);
00968     OFFSET_CHECK(p_dest, offset,size);
00969 
00970     // Verify word alignment.
00971     if ((!is_word_aligned(p_src)) || (!is_word_aligned((void *)(uint32_t)offset)))
00972     {
00973         return NRF_ERROR_INVALID_ADDR;
00974     }
00975 
00976     if ((!is_word_aligned((uint32_t *)p_dest->block_id)))
00977     {
00978         return NRF_ERROR_INVALID_ADDR;
00979     }
00980 
00981     return cmd_queue_enqueue(PSTORAGE_STORE_OP_CODE, p_dest, p_src, size, offset);
00982 }
00983 
00984 
00985 uint32_t pstorage_update(pstorage_handle_t * p_dest,
00986                          uint8_t           * p_src,
00987                          pstorage_size_t     size,
00988                          pstorage_size_t     offset)
00989 {
00990     VERIFY_MODULE_INITIALIZED();
00991     NULL_PARAM_CHECK(p_src);
00992     NULL_PARAM_CHECK(p_dest);
00993     MODULE_ID_RANGE_CHECK(p_dest);
00994     BLOCK_ID_RANGE_CHECK(p_dest);
00995     SIZE_CHECK(p_dest, size);
00996     OFFSET_CHECK(p_dest, offset, size);
00997 
00998     // Verify word alignment.
00999     if ((!is_word_aligned(p_src)) || (!is_word_aligned((void *)(uint32_t)offset)))
01000     {
01001         return NRF_ERROR_INVALID_ADDR;
01002     }
01003 
01004     if ((!is_word_aligned((uint32_t *)p_dest->block_id)))
01005     {
01006         return NRF_ERROR_INVALID_ADDR;
01007     }
01008 
01009     return cmd_queue_enqueue(PSTORAGE_UPDATE_OP_CODE, p_dest, p_src, size, offset);
01010 }
01011 
01012 
01013 uint32_t pstorage_load(uint8_t           * p_dest,
01014                        pstorage_handle_t * p_src,
01015                        pstorage_size_t     size,
01016                        pstorage_size_t     offset)
01017 {
01018     VERIFY_MODULE_INITIALIZED();
01019     NULL_PARAM_CHECK(p_src);
01020     NULL_PARAM_CHECK(p_dest);
01021     MODULE_ID_RANGE_CHECK(p_src);
01022     BLOCK_ID_RANGE_CHECK(p_src);
01023     SIZE_CHECK(p_src, size);
01024     OFFSET_CHECK(p_src, offset, size);
01025 
01026     // Verify word alignment.
01027     if ((!is_word_aligned(p_dest)) || (!is_word_aligned((void *)(uint32_t)offset)))
01028     {
01029         return NRF_ERROR_INVALID_ADDR;
01030     }
01031 
01032     if ((!is_word_aligned((uint32_t *)p_src->block_id)))
01033     {
01034         return NRF_ERROR_INVALID_ADDR;
01035     }
01036 
01037     memcpy(p_dest, (((uint8_t *)p_src->block_id) + offset), size);
01038 
01039     m_app_table[p_src->module_id].cb(p_src, PSTORAGE_LOAD_OP_CODE, NRF_SUCCESS, p_dest, size);
01040 
01041     return NRF_SUCCESS;
01042 }
01043 
01044 
01045 uint32_t pstorage_clear(pstorage_handle_t * p_dest, pstorage_size_t size)
01046 {
01047     uint32_t retval;
01048 
01049     VERIFY_MODULE_INITIALIZED();
01050     NULL_PARAM_CHECK(p_dest);
01051     MODULE_ID_RANGE_CHECK(p_dest);
01052     BLOCK_ID_RANGE_CHECK(p_dest);
01053 
01054     if ((!is_word_aligned((uint32_t *)p_dest->block_id)))
01055     {
01056         return NRF_ERROR_INVALID_ADDR;
01057     }
01058 
01059     if (
01060         !(
01061             ((p_dest->block_id - m_app_table[p_dest->module_id].base_id) %
01062              m_app_table[p_dest->module_id].block_size) == 0
01063             )
01064         )
01065     {
01066         return NRF_ERROR_INVALID_PARAM;
01067     }
01068 
01069     retval = cmd_queue_enqueue(PSTORAGE_CLEAR_OP_CODE, p_dest, NULL, size, 0);
01070 
01071     return retval;
01072 }
01073 
01074 
01075 uint32_t pstorage_access_status_get(uint32_t * p_count)
01076 {
01077     VERIFY_MODULE_INITIALIZED();
01078     NULL_PARAM_CHECK(p_count);
01079 
01080     (*p_count) = m_cmd_queue.count;
01081 
01082     return NRF_SUCCESS;
01083 }
01084 
01085 #ifdef PSTORAGE_RAW_MODE_ENABLE
01086 
01087 
01088 uint32_t pstorage_raw_register(pstorage_module_param_t * p_module_param,
01089                                pstorage_handle_t       * p_block_id)
01090 {
01091     VERIFY_MODULE_INITIALIZED();
01092     NULL_PARAM_CHECK(p_module_param);
01093     NULL_PARAM_CHECK(p_block_id);
01094     NULL_PARAM_CHECK(p_module_param->cb);
01095 
01096     if (m_raw_app_table.cb != NULL)
01097     {
01098         return NRF_ERROR_NO_MEM;
01099     }
01100 
01101     p_block_id->module_id = RAW_MODE_APP_ID;
01102     m_raw_app_table.cb    = p_module_param->cb;
01103 
01104     return NRF_SUCCESS;
01105 }
01106 
01107 
01108 uint32_t pstorage_raw_store(pstorage_handle_t * p_dest,
01109                             uint8_t           * p_src,
01110                             pstorage_size_t     size,
01111                             pstorage_size_t     offset)
01112 {
01113     VERIFY_MODULE_INITIALIZED();
01114     NULL_PARAM_CHECK(p_src);
01115     NULL_PARAM_CHECK(p_dest);
01116     MODULE_RAW_ID_RANGE_CHECK(p_dest);
01117 
01118     // Verify word alignment.
01119     if ((!is_word_aligned(p_src)) || (!is_word_aligned((void *)(uint32_t)offset)))
01120     {
01121         return NRF_ERROR_INVALID_ADDR;
01122     }
01123 
01124     return cmd_queue_enqueue(PSTORAGE_STORE_OP_CODE, p_dest, p_src, size, offset);
01125 }
01126 
01127 
01128 uint32_t pstorage_raw_clear(pstorage_handle_t * p_dest, pstorage_size_t size)
01129 {
01130     uint32_t retval;
01131 
01132     VERIFY_MODULE_INITIALIZED();
01133     NULL_PARAM_CHECK(p_dest);
01134     MODULE_RAW_ID_RANGE_CHECK(p_dest);
01135 
01136     retval = cmd_queue_enqueue(PSTORAGE_CLEAR_OP_CODE, p_dest, NULL, size, 0);
01137 
01138     return retval;
01139 }
01140 
01141 #endif // PSTORAGE_RAW_MODE_ENABLE