test
Fork of nrf51-sdk by
Embed:
(wiki syntax)
Show/hide line numbers
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 15:51:26 by 1.7.2