R1 code for micro:bit based train controller code, requires second micro:bit running rx code to operate - see https://meanderingpi.wordpress.com/ for more information

Fork of nrf51-sdk by Lancaster University

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers fstorage.c Source File

fstorage.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 "fstorage.h "
00034 #include <stdio.h>
00035 #include <string.h>
00036 #include <stdbool.h>
00037 #include "fstorage_config.h"
00038 #include "nrf_error.h"
00039 #include "nrf_soc.h"
00040 
00041 
00042 #define FS_FLAG_INIT                (1 << 0)    /**< fstorage has been initialized. */
00043 #define FS_FLAG_PROCESSING          (1 << 1)    /**< fstorage is executing queued flash operations. */
00044 #define FS_FLAG_FLASH_REQ_PENDING   (1 << 2)    /**< fstorage is waiting for a flash operation initiated by another module to complete. */
00045 
00046 
00047 /**@brief Macro invocation that registers section fs_data.
00048  *
00049  * @details Required for compilation.
00050  */
00051 NRF_SECTION_VARS_REGISTER_SECTION(fs_data);
00052 
00053 
00054 /**@brief Macro invocation that declares symbols used to find the beginning and end of the section fs_data.
00055  *
00056  * @details Required for compilation.
00057  */
00058 NRF_SECTION_VARS_REGISTER_SYMBOLS(fs_config_t, fs_data);
00059 
00060 
00061 /**@defgroup Section vars helper macros.
00062  *
00063  * @details Macros used to manipulate registered section variables.
00064  */
00065  /**@brief Get section variable with fstorage configuration by index. */
00066 #define FS_SECTION_VARS_GET(i)          NRF_SECTION_VARS_GET(i, fs_config_t, fs_data)
00067  /**@brief Get the number of registered section variables. */
00068 #define FS_SECTION_VARS_COUNT           NRF_SECTION_VARS_COUNT(fs_config_t, fs_data)
00069  /**@brief Get the start address of the registered section variables. */
00070 #define FS_SECTION_VARS_START_ADDR      NRF_SECTION_VARS_START_ADDR(fs_data)
00071  /**@brief Get the end address of the registered section variables. */
00072 #define FS_SECTION_VARS_END_ADDR        NRF_SECTION_VARS_END_ADDR(fs_data)
00073 
00074 /** @} */
00075 
00076 
00077 /**@brief The command queue element.
00078  *
00079  * @details Encapsulate details of a command requested to this module.
00080  */
00081 typedef struct
00082 {
00083     fs_config_t const * p_config;           /**< The configuration of the user who requested the operation. */
00084     uint8_t             op_code;            /**< Operation code. */
00085     uint32_t const *    p_src;              /**< Pointer to the data to be written to flash. The data must be kept in memory until the operation has finished. */
00086     uint32_t const *    p_addr;             /**< Destination of the data in flash. */
00087     fs_length_t         length_words;       /**< Length of the operation */
00088     fs_length_t         offset;             /**< Offset of the operation if operation is done in chunks */
00089 } fs_cmd_t;
00090 
00091 
00092 /**@brief Structure that defines the command queue
00093  *
00094  * @details This queue holds flash operations requested to the module. 
00095  *          The data to be written must be kept in memory until the write operation is completed,
00096  *          i.e., a callback indicating completion is received by the application.
00097  */
00098 typedef struct
00099 {
00100     uint8_t     rp;                         /**< The current element being processed. */
00101     uint8_t     count;                      /**< Number of elements in the queue. */
00102     fs_cmd_t    cmd[FS_CMD_QUEUE_SIZE];     /**< Array to maintain flash access operation details. */
00103 } fs_cmd_queue_t;
00104 
00105 
00106 static uint8_t          m_flags;            /**< FStorage status flags. */
00107 static fs_cmd_queue_t   m_cmd_queue;        /**< Flash operation request queue. */
00108 static uint16_t         m_retry_count = 0;  /**< Number of times a single flash operation was retried. */
00109 
00110 
00111 // Function prototypes
00112 static ret_code_t queue_process(void);
00113 static ret_code_t queue_process_impl(void);
00114 static void app_notify(uint32_t result, fs_cmd_t const * p_cmd);
00115 
00116 
00117 /**@brief Macro to check that the configuration is non-NULL and within
00118 *         valid section variable memory bounds.
00119  *
00120  * @param[in]   config    Configuration to check.
00121  */
00122 #define FS_CHECK_CONFIG(config) \
00123     ((FS_SECTION_VARS_START_ADDR < config) && (config < FS_SECTION_VARS_END_ADDR))
00124 
00125 
00126 /**@brief Function to check that the configuration is non-NULL and within
00127 *         valid section variable memory bounds.
00128  *
00129  * @param[in]   config    Configuration to check.
00130  */
00131 static bool check_config(fs_config_t const * const config)
00132 {
00133     if (config == NULL)
00134     {
00135         return false;
00136     }
00137 
00138     if ((FS_SECTION_VARS_START_ADDR <= (uint32_t)config) && ((uint32_t)config < FS_SECTION_VARS_END_ADDR))
00139     {
00140         return true;
00141     }
00142     else
00143     {
00144         return false;
00145     }
00146 }
00147 
00148 
00149 /**@brief Function to initialize the queue. */
00150 static void queue_init(void)
00151 {
00152     memset(&m_cmd_queue, 0, sizeof(fs_cmd_queue_t));
00153 }
00154 
00155 
00156 /**@brief Function to reset a queue item to its default values.
00157  *
00158  * @param   index   Index of the queue element.
00159  */
00160 static void cmd_reset(uint32_t index)
00161 {
00162     memset(&m_cmd_queue.cmd[index], 0, sizeof(fs_cmd_t));
00163 }
00164 
00165 
00166 /**@brief Function to enqueue flash access command
00167  *
00168  * @param[in]   config      Registered configuration.
00169  * @param[in]   op_code     Operation code.
00170  * @param[in]   address     Destination of the data.
00171  * @param[in]   p_src       Source of data or NULL if n/a.
00172  * @param[in]   length      Length of the data, in 4 byte words.
00173  *
00174  * @retval NRF_SUCCESS      Success. Command enqueued.
00175  * @retval NRF_ERROR_NO_MEM Error. Queue is full.
00176  * @retval Any error returned by the SoftDevice flash API.
00177  */
00178 static ret_code_t cmd_enqueue(fs_config_t      const * p_config,
00179                               uint8_t                  op_code,
00180                               uint32_t         const * p_addr,
00181                               uint32_t         const * p_src,
00182                               fs_length_t              length_words)
00183 {
00184     fs_cmd_t * p_cmd;
00185     uint8_t    write_pos;
00186 
00187     if (m_cmd_queue.count == FS_CMD_QUEUE_SIZE - 1)
00188     {
00189         return NRF_ERROR_NO_MEM;
00190     }
00191 
00192     write_pos = (m_cmd_queue.rp + m_cmd_queue.count) % FS_CMD_QUEUE_SIZE;
00193 
00194     p_cmd = &m_cmd_queue.cmd[write_pos];
00195 
00196     p_cmd->p_config     = p_config;
00197     p_cmd->op_code      = op_code;
00198     p_cmd->p_src        = p_src;
00199     p_cmd->p_addr       = p_addr;
00200     p_cmd->length_words = length_words;
00201 
00202     m_cmd_queue.count++;
00203 
00204     return queue_process();
00205 }
00206 
00207 
00208 /**@brief Function to consume queue item and notify the return value of the operation.
00209  *
00210  * @details This function will report the result and remove the command from the queue after
00211  *          notification.
00212  */
00213 static void cmd_consume(uint32_t result, const fs_cmd_t * p_cmd)
00214 {
00215     // Consume the current item on the queue.
00216     uint8_t rp = m_cmd_queue.rp;
00217 
00218     m_cmd_queue.count--;
00219     if (m_cmd_queue.count == 0)
00220     {
00221         // There are no elements left. Stop processing the queue.
00222         m_flags &= ~FS_FLAG_PROCESSING;
00223     }
00224 
00225     if (++(m_cmd_queue.rp) == FS_CMD_QUEUE_SIZE)
00226     {
00227         m_cmd_queue.rp = 0;
00228     }
00229 
00230     // Notify upon successful operation.
00231     app_notify(result, p_cmd);
00232 
00233     // Reset the queue element.
00234     cmd_reset(rp);
00235 }
00236 
00237 
00238 /**@brief Function to store data to flash.
00239  *
00240  * @param[in]   p_cmd   The queue element associated with the operation.
00241  *
00242  * @retval NRF_SUCCESS  Success. The request was sent to the SoftDevice.
00243  * @retval Any error returned by the SoftDevice flash API.
00244  */
00245 static __INLINE uint32_t store_execute(fs_cmd_t const * const p_cmd)
00246 {
00247     // Write in chunks if write-size is larger than FS_MAX_WRITE_SIZE.
00248     fs_length_t const length = ((p_cmd->length_words - p_cmd->offset) < FS_MAX_WRITE_SIZE_WORDS) ?
00249         (p_cmd->length_words - p_cmd->offset) : FS_MAX_WRITE_SIZE_WORDS;
00250 
00251     return sd_flash_write((uint32_t*)p_cmd->p_addr + p_cmd->offset /* destination */,
00252                           (uint32_t*)p_cmd->p_src + p_cmd->offset  /* source */,
00253                           length);
00254 }
00255 
00256 
00257 /**@brief Function to erase a page.
00258  *
00259  * @param[in]   p_cmd   The queue element associated with the operation.
00260  *
00261  * @retval NRF_SUCCESS  Success. The request was sent to the SoftDevice.
00262  * @retval Any error returned by the SoftDevice flash API.
00263  */
00264 static __INLINE uint32_t erase_execute(fs_cmd_t const * const p_cmd)
00265 {
00266     // Erase the page.
00267     return sd_flash_page_erase((uint32_t)(p_cmd->p_addr + p_cmd->offset) / FS_PAGE_SIZE);
00268 }
00269 
00270 
00271 /**@brief Function to process the current element in the queue and return the result.
00272  *
00273  * @retval NRF_SUCCESS          Success.
00274  * @retval NRF_ERROR_FORBIDDEN  Error. Undefined command.
00275  * @retval Any error returned by the SoftDevice flash API.
00276  */
00277 static uint32_t queue_process_impl(void)
00278 {
00279     uint32_t ret;
00280     
00281     fs_cmd_t const * const p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp];
00282 
00283     switch (p_cmd->op_code)
00284     {
00285         case FS_OP_STORE:
00286             ret = store_execute(p_cmd);
00287             break;
00288 
00289         case FS_OP_ERASE:
00290             ret = erase_execute(p_cmd);
00291             break;
00292 
00293         case FS_OP_NONE:
00294             ret = NRF_SUCCESS;
00295             break;
00296 
00297         default:
00298             ret = NRF_ERROR_FORBIDDEN;
00299             break;
00300     }
00301 
00302     return ret;
00303 }
00304 
00305 
00306 /**@brief Starts processing the queue if there are no pending flash operations
00307  *        for which we are awaiting a callback.
00308  */
00309 static ret_code_t queue_process(void)
00310 {
00311     ret_code_t ret = NRF_SUCCESS;
00312 
00313     /** If the queue is not being processed, and there are still
00314      *  some elements in it, then start processing. */
00315     if ( !(m_flags & FS_FLAG_PROCESSING) &&
00316           (m_cmd_queue.count > 0))
00317     {
00318         m_flags |= FS_FLAG_PROCESSING;
00319 
00320         ret = queue_process_impl();
00321 
00322         /** There is ongoing flash-operation which was not
00323          *  initiated by fstorage. */
00324         if (ret == NRF_ERROR_BUSY)
00325         {
00326             // Wait for a system callback.
00327             m_flags |= FS_FLAG_FLASH_REQ_PENDING;
00328 
00329             // Stop processing the queue.
00330             m_flags &= ~FS_FLAG_PROCESSING;
00331 
00332             ret = NRF_SUCCESS;
00333         }
00334         else if (ret != NRF_SUCCESS)
00335         {
00336             // Another error has occurred.
00337             app_notify(ret, &m_cmd_queue.cmd[m_cmd_queue.rp]);
00338         }
00339     }
00340 
00341     // If we are already processing the queue, return immediately.
00342     return ret;
00343 }
00344 
00345 
00346 /**@brief Flash operation success callback handler.
00347  *
00348  * @details     This function updates read/write pointers.
00349  *              This function resets retry count.
00350  */
00351 static __INLINE void on_operation_success(void)
00352 {
00353     fs_cmd_t * const p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp];
00354 
00355     m_retry_count = 0;
00356 
00357     switch (p_cmd->op_code)
00358     {
00359         case FS_OP_STORE:
00360             // Update the offset on successful write.
00361             p_cmd->offset += FS_MAX_WRITE_SIZE_WORDS;
00362             break;
00363 
00364         case FS_OP_ERASE:
00365             // Update the offset to correspond to the page that has been erased.
00366             p_cmd->offset += FS_PAGE_SIZE_WORDS;
00367             break;
00368     }
00369 
00370     // If offset is equal to or larger than length, then the operation has finished.
00371     if (p_cmd->offset >= p_cmd->length_words)
00372     {
00373         cmd_consume(NRF_SUCCESS, p_cmd);
00374     }
00375 
00376     queue_process();
00377 }
00378 
00379 
00380 /**@brief Flash operation failure callback handler.
00381  *
00382  * @details Function to keep track of retries and notify failures.
00383  */
00384 static __INLINE void on_operation_failure(uint32_t sys_evt)
00385 {
00386     const fs_cmd_t * p_cmd;
00387     
00388     if (++m_retry_count > FS_CMD_MAX_RETRIES)
00389     {
00390         p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp];
00391         cmd_consume(NRF_ERROR_TIMEOUT, p_cmd);
00392     }
00393 
00394     queue_process();
00395 }
00396 
00397 
00398 /**@brief Function to notify users.
00399  *
00400  * @param[in]   result      Result of the flash operation.
00401  * @param[in]   p_cmd       The command associated with the callback.
00402  */
00403 static void app_notify(uint32_t result, fs_cmd_t const * const p_cmd)
00404 {
00405     p_cmd->p_config->cb(p_cmd->op_code, result, p_cmd->p_addr, p_cmd->length_words);
00406 }
00407 
00408 
00409 ret_code_t fs_init(void)
00410 {
00411     uint16_t   lowest_index = 0;
00412     uint16_t   lowest_order = 0xFFFF;
00413     uint32_t * current_end  = (uint32_t*)FS_PAGE_END_ADDR;
00414     uint32_t   num_left     = FS_SECTION_VARS_COUNT;
00415 
00416     queue_init();
00417 
00418     /** Assign pages to registered users, beginning with the ones with the lowest
00419      *  order, which will be assigned pages with the lowest memory address. */
00420     do
00421     {
00422         fs_config_t * p_config;
00423         for (uint16_t i = 0; i < FS_SECTION_VARS_COUNT; i++)
00424         {
00425             p_config = FS_SECTION_VARS_GET(i);
00426 
00427             // Skip the ones which have the end-address already set.
00428             if (p_config->p_end_addr != NULL)
00429                 continue;
00430 
00431             if (p_config->page_order < lowest_order)
00432             {
00433                 lowest_order = p_config->page_order;
00434                 lowest_index = i;
00435             }
00436         }
00437 
00438         p_config = FS_SECTION_VARS_GET(lowest_index);
00439 
00440         p_config->p_end_addr   = current_end;
00441         p_config->p_start_addr = p_config->p_end_addr - (p_config->num_pages * FS_PAGE_SIZE_WORDS);
00442 
00443         current_end  = p_config->p_start_addr;
00444         lowest_order = 0xFFFF;
00445 
00446     } while ( --num_left > 0 );
00447 
00448     m_flags |= FS_FLAG_INIT;
00449 
00450     return NRF_SUCCESS;
00451 }
00452 
00453 
00454 ret_code_t fs_store(fs_config_t const *       p_config,
00455                     uint32_t    const *       p_addr,
00456                     uint32_t    const * const p_data,
00457                     fs_length_t               length_words)
00458 {
00459     if ((m_flags & FS_FLAG_INIT) == 0)
00460     {
00461         return NRF_ERROR_INVALID_STATE;
00462     }
00463 
00464     if (!check_config(p_config))
00465     {
00466         return NRF_ERROR_FORBIDDEN;
00467     }
00468 
00469     if (!is_word_aligned(p_addr))
00470     {
00471         return NRF_ERROR_INVALID_ADDR;
00472     }
00473 
00474     // Check that the erase operation is on pages owned by this user (configuration).
00475     if ((p_addr < p_config->p_start_addr) || ((p_addr + length_words) > p_config->p_end_addr))
00476     {
00477         return NRF_ERROR_INVALID_ADDR;
00478     }
00479 
00480     return cmd_enqueue(p_config, FS_OP_STORE, p_addr, p_data, length_words);
00481 }
00482 
00483 
00484 ret_code_t fs_erase(fs_config_t const *       p_config,
00485                     uint32_t          * const p_addr,
00486                     fs_length_t const         length_words)
00487 {
00488     if ((m_flags & FS_FLAG_INIT) == 0)
00489     {
00490         return NRF_ERROR_INVALID_STATE;
00491     }
00492 
00493     if (!check_config(p_config))
00494     {
00495         return NRF_ERROR_FORBIDDEN;
00496     }
00497 
00498     /** Check that the address is aligned on a page boundary and the length to erase
00499      *  is a multiple of the page size. */
00500     if (((uint32_t)p_addr & (FS_PAGE_SIZE - 1)) ||
00501         (length_words     & (FS_PAGE_SIZE_WORDS - 1)))
00502     {
00503         return NRF_ERROR_INVALID_ADDR;
00504     }
00505 
00506     // Check that the erase operation is on pages owned by this user (configuration).
00507     if ((p_addr < p_config->p_start_addr) || ((p_addr + length_words) > p_config->p_end_addr))
00508     {
00509         return NRF_ERROR_INVALID_ADDR;
00510     }
00511 
00512     return cmd_enqueue(p_config, FS_OP_ERASE, p_addr, NULL, length_words);
00513 }
00514 
00515 
00516 /**@brief Function to handle system events from the SoftDevice.
00517  *
00518  * @details     This function should be dispatched system events if any of the modules used by
00519  *              the application rely on FStorage. Examples include @ref Peer Manager and
00520  *              @ref Flash Data Storage.
00521  *
00522  * @param[in]   sys_evt     System Event received.
00523  */
00524 void fs_sys_event_handler(uint32_t sys_evt)
00525 {
00526     if (m_flags & FS_FLAG_PROCESSING)
00527     {
00528         /** A flash operation was initiated by this module.
00529          *  Handle its result. */
00530         switch (sys_evt)
00531         {
00532             case NRF_EVT_FLASH_OPERATION_SUCCESS:
00533                 on_operation_success();
00534                 break;
00535 
00536             case NRF_EVT_FLASH_OPERATION_ERROR:
00537                 on_operation_failure(sys_evt);
00538                 break;
00539         }
00540     }
00541     else if ((m_flags & FS_FLAG_FLASH_REQ_PENDING))
00542     {
00543         /** A flash operation was initiated outside this module.
00544          *  We have now receveid a callback which indicates it has
00545          *  finished. Clear the FS_FLAG_FLASH_REQ_PENDING flag. */
00546          m_flags &= ~FS_FLAG_FLASH_REQ_PENDING;
00547 
00548          // Resume processing the queue, if necessary.
00549          queue_process();
00550     }
00551 }
00552 
00553 
00554 // Just for testing out section vars (across many compilers).
00555 void fs_debug_print()
00556 {
00557     printf("fs start address: 0x%08lx\r\n", (unsigned long)FS_SECTION_VARS_START_ADDR);
00558     printf("fs end address: 0x%08lx\r\n",   (unsigned long)FS_SECTION_VARS_END_ADDR);
00559     printf("Num items: 0x%08lx\r\n",        (unsigned long)FS_SECTION_VARS_COUNT);
00560     printf("===== ITEMS %lu =====\r\n",     (unsigned long)FS_SECTION_VARS_COUNT);
00561 
00562     for(int i = 0; i < FS_SECTION_VARS_COUNT; i++)
00563     {
00564         fs_config_t* config = FS_SECTION_VARS_GET(i);
00565         printf( "Address: 0x%08lx, CB: 0x%08lx\r\n",
00566                 (unsigned long)config, (unsigned long)config->cb );
00567     }
00568     printf("\r\n");
00569 }