Nordic stack and drivers for the mbed BLE API

Dependents:   idd_hw5_bleFanProto

Fork of nRF51822 by Nordic Semiconductor

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers pstorage.cpp Source File

pstorage.cpp

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