Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of nrf51-sdk by
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 }
Generated on Tue Jul 12 2022 18:07:42 by
