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
fds.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 "fds.h" 00034 #include <stdint.h> 00035 #include <string.h> 00036 #include <stdbool.h> 00037 #include "fds_config.h " 00038 #include "fds_types_internal.h" 00039 #include "fstorage.h " 00040 #include "nrf_error.h" 00041 #include "app_util.h " 00042 00043 00044 /** Our fstorage configuration. 00045 * The other fields will be assigned automatically during compilation. */ 00046 FS_SECTION_VARS_ADD(fs_config_t fs_config) = { .cb = fs_callback, .num_pages = FDS_MAX_PAGES }; 00047 00048 static uint32_t const fds_page_tag_swap[] = {FDS_PAGE_TAG_WORD_0_SWAP, FDS_PAGE_TAG_WORD_1, 00049 FDS_PAGE_TAG_WORD_2, FDS_PAGE_TAG_WORD_3}; 00050 00051 static uint32_t const fds_page_tag_valid[] = {FDS_PAGE_TAG_WORD_0_VALID, FDS_PAGE_TAG_WORD_1, 00052 FDS_PAGE_TAG_WORD_2, FDS_PAGE_TAG_WORD_3}; 00053 00054 static uint32_t const fds_page_tag_gc = FDS_PAGE_TAG_WORD_3_GC; 00055 00056 static fds_tl_t const m_fds_tl_invalid = { .type = FDS_TYPE_ID_INVALID, 00057 .length_words = 0xFFFF }; 00058 00059 /**@brief Internal status flags. */ 00060 static uint8_t volatile m_flags; 00061 00062 static uint8_t m_users; 00063 static fds_cb_t m_cb_table[FDS_MAX_USERS]; 00064 00065 /**@brief The last record ID. Setup page by page_scan() during pages_init(). */ 00066 static fds_record_id_t m_last_rec_id; 00067 00068 /**@brief The internal queues. */ 00069 static fds_cmd_queue_t m_cmd_queue; 00070 static fds_chunk_queue_t m_chunk_queue; 00071 00072 /**@brief Holds the state of pages. Setup by fds_init(). */ 00073 static fds_page_t m_pages[FDS_MAX_PAGES]; 00074 static bool m_swap_page_avail = false; 00075 00076 static fds_gc_data_t m_gc; 00077 static uint16_t m_gc_runs; 00078 00079 static uint8_t volatile m_counter; 00080 00081 00082 static void app_notify(ret_code_t result, 00083 fds_cmd_id_t cmd, 00084 fds_record_id_t record_id, 00085 fds_record_key_t record_key) 00086 { 00087 for (uint8_t user = 0; user < FDS_MAX_USERS; user++) 00088 { 00089 if (m_cb_table[user] != NULL) 00090 { 00091 m_cb_table[user](result, cmd, record_id, record_key); 00092 } 00093 } 00094 } 00095 00096 00097 static void atomic_counter_inc() 00098 { 00099 CRITICAL_SECTION_ENTER(); 00100 m_counter++; 00101 CRITICAL_SECTION_EXIT(); 00102 } 00103 00104 00105 static void atomic_counter_dec() 00106 { 00107 CRITICAL_SECTION_ENTER(); 00108 m_counter--; 00109 CRITICAL_SECTION_EXIT(); 00110 } 00111 00112 00113 static bool atomic_counter_is_zero() 00114 { 00115 bool ret; 00116 CRITICAL_SECTION_ENTER(); 00117 ret = (m_counter == 0); 00118 CRITICAL_SECTION_EXIT(); 00119 return ret; 00120 } 00121 00122 00123 static void flag_set(fds_flags_t flag) 00124 { 00125 CRITICAL_SECTION_ENTER(); 00126 m_flags |= flag; 00127 CRITICAL_SECTION_EXIT(); 00128 } 00129 00130 00131 static void flag_clear(fds_flags_t flag) 00132 { 00133 CRITICAL_SECTION_ENTER(); 00134 m_flags &= ~(flag); 00135 CRITICAL_SECTION_EXIT(); 00136 } 00137 00138 00139 static bool flag_is_set(fds_flags_t flag) 00140 { 00141 bool ret; 00142 CRITICAL_SECTION_ENTER(); 00143 ret = (m_flags & flag); 00144 CRITICAL_SECTION_EXIT(); 00145 return ret; 00146 } 00147 00148 00149 /**@brief Function to check if a header has valid information. */ 00150 static __INLINE bool header_is_valid(fds_header_t const * const p_header) 00151 { 00152 return ((p_header->tl.type != FDS_TYPE_ID_INVALID) && 00153 (p_header->ic.instance != FDS_INSTANCE_ID_INVALID)); 00154 } 00155 00156 00157 static bool address_within_page_bounds(uint32_t const * const p_addr) 00158 { 00159 return (p_addr >= fs_config.p_start_addr) && 00160 (p_addr <= fs_config.p_end_addr) && 00161 (is_word_aligned(p_addr)); 00162 } 00163 00164 00165 /**@brief Internal function to identify the page type. */ 00166 static fds_page_type_t page_identify(uint16_t page_number) 00167 { 00168 uint32_t const * const p_page_addr = m_pages[page_number].start_addr; 00169 00170 uint32_t const word0 = *(p_page_addr); 00171 uint32_t const word1 = *(p_page_addr + 1); 00172 uint32_t const word2 = *(p_page_addr + 2); 00173 uint32_t const word3 = *(p_page_addr + 3); 00174 00175 if (word1 != FDS_PAGE_TAG_WORD_1) 00176 { 00177 return FDS_PAGE_UNDEFINED; 00178 } 00179 00180 if (word2 != FDS_PAGE_TAG_WORD_2) 00181 { 00182 return FDS_PAGE_UNDEFINED; 00183 } 00184 00185 if (word3 == FDS_PAGE_TAG_WORD_3) 00186 { 00187 if (word0 == FDS_PAGE_TAG_WORD_0_SWAP) 00188 { 00189 return FDS_PAGE_SWAP; 00190 } 00191 00192 if (word0 == FDS_PAGE_TAG_WORD_0_VALID) 00193 { 00194 return FDS_PAGE_VALID; 00195 } 00196 } 00197 else if (word3 == FDS_PAGE_TAG_WORD_3_GC) 00198 { 00199 if (word0 == FDS_PAGE_TAG_WORD_0_SWAP || word0 == FDS_PAGE_TAG_WORD_0_VALID) 00200 { 00201 return FDS_PAGE_GC; 00202 } 00203 } 00204 00205 return FDS_PAGE_UNDEFINED; 00206 } 00207 00208 00209 static uint16_t page_by_addr(uint32_t const * const p_addr) 00210 { 00211 if (p_addr == NULL) 00212 { 00213 return 0; 00214 } 00215 00216 // Compute the BYTES offset from the beginning of the first page. 00217 uint32_t const byte_offset = (uint32_t)p_addr - (uint32_t)m_pages[0].start_addr; 00218 00219 // See nrf.h. 00220 #if defined (NRF51) 00221 return byte_offset >> 10; // Divide by page size (1024). 00222 #elif defined (NRF52) 00223 return byte_offset >> 12; // Divide by page size (4096). 00224 #else 00225 #error "Device family must be defined. See nrf.h." 00226 #endif 00227 } 00228 00229 00230 // NOTE: depends on m_pages.write_offset to function. 00231 static bool page_has_space(uint16_t page, fds_length_t length_words) 00232 { 00233 if (page >= FDS_MAX_PAGES) 00234 { 00235 return false; 00236 } 00237 00238 CRITICAL_SECTION_ENTER(); 00239 length_words += m_pages[page].write_offset; 00240 length_words += m_pages[page].words_reserved; 00241 CRITICAL_SECTION_EXIT(); 00242 00243 return (length_words < FS_PAGE_SIZE_WORDS); 00244 } 00245 00246 00247 /**@brief This function scans a page to determine how many words have 00248 * been written to it. This information is used to set the page 00249 * write offset during initialization (mount). Additionally, this 00250 * function will update the last known record ID as it proceeds. 00251 */ 00252 static void page_scan(uint16_t page, uint16_t volatile * words_written) 00253 { 00254 uint32_t const * p_addr = (m_pages[page].start_addr + FDS_PAGE_TAG_SIZE); 00255 00256 *words_written = FDS_PAGE_TAG_SIZE; 00257 00258 // A corrupt TL might cause problems. 00259 while ((p_addr < m_pages[page].start_addr + FS_PAGE_SIZE_WORDS) && 00260 (*p_addr != FDS_ERASED_WORD)) 00261 { 00262 fds_header_t const * const p_header = (fds_header_t*)p_addr; 00263 00264 /** Note: DO NOT check for the validity of the header using 00265 * header_is_valid() here. If an header has an invalid type (0x0000) 00266 * or a missing instance (0xFFFF) then we WANT to skip it. 00267 */ 00268 00269 // Update the last known record id. 00270 if (p_header->id > m_last_rec_id) 00271 { 00272 m_last_rec_id = p_header->id; 00273 } 00274 00275 // Jump to the next record. 00276 p_addr += (FDS_HEADER_SIZE + p_header->tl.length_words); 00277 *words_written += (FDS_HEADER_SIZE + p_header->tl.length_words); 00278 } 00279 } 00280 00281 00282 static bool page_is_empty(uint16_t page) 00283 { 00284 uint32_t const * const p_addr = m_pages[page].start_addr; 00285 00286 for (uint16_t i = 0; i < FS_PAGE_SIZE_WORDS; i++) 00287 { 00288 if (*(p_addr + i) != FDS_ERASED_WORD) 00289 { 00290 return false; 00291 } 00292 } 00293 00294 return true; 00295 } 00296 00297 00298 static ret_code_t page_id_from_virtual_id(uint16_t vpage_id, uint16_t * p_page_id) 00299 { 00300 for (uint16_t i = 0; i < FDS_MAX_PAGES; i++) 00301 { 00302 if (m_pages[i].vpage_id == vpage_id) 00303 { 00304 *p_page_id = i; 00305 return NRF_SUCCESS; 00306 } 00307 } 00308 00309 return NRF_ERROR_NOT_FOUND; 00310 } 00311 00312 00313 static ret_code_t page_from_virtual_id(uint16_t vpage_id, fds_page_t ** p_page) 00314 { 00315 for (uint16_t i = 0; i < FDS_MAX_PAGES; i++) 00316 { 00317 if (m_pages[i].vpage_id == vpage_id) 00318 { 00319 *p_page = &m_pages[i]; 00320 return NRF_SUCCESS; 00321 } 00322 } 00323 00324 return NRF_ERROR_NOT_FOUND; 00325 } 00326 00327 00328 static uint32_t record_id_new() 00329 { 00330 return ++m_last_rec_id; 00331 } 00332 00333 00334 /**@brief Tags a page as swap, i.e., reserved for GC. */ 00335 static ret_code_t page_tag_write_swap(uint16_t page) 00336 { 00337 return fs_store(&fs_config, 00338 m_pages[page].start_addr, 00339 (uint32_t const *)&fds_page_tag_swap, 00340 FDS_PAGE_TAG_SIZE); 00341 } 00342 00343 00344 /**@brief Tags a page as valid, i.e, ready for storage. */ 00345 static ret_code_t page_tag_write_valid(uint16_t page) 00346 { 00347 return fs_store(&fs_config, 00348 m_pages[page].start_addr, 00349 (uint32_t const *)&fds_page_tag_valid, 00350 FDS_PAGE_TAG_SIZE); 00351 } 00352 00353 00354 /**@brief Tags a valid page as being garbage collected. */ 00355 static ret_code_t page_tag_write_gc(uint16_t page) 00356 { 00357 return fs_store(&fs_config, 00358 m_pages[page].start_addr + 3, 00359 (uint32_t const *)&fds_page_tag_gc, 00360 1 /*Words*/); 00361 } 00362 00363 00364 /**@brief Given a page and a record, finds the next valid record. */ 00365 static ret_code_t scan_next_valid(uint16_t page, uint32_t const ** p_record) 00366 { 00367 uint32_t const * p_next_rec = (*p_record); 00368 00369 if (p_next_rec == NULL) 00370 { 00371 // This if the first invocation on this page, start from the beginning. 00372 p_next_rec = m_pages[page].start_addr + FDS_PAGE_TAG_SIZE; 00373 } 00374 else 00375 { 00376 // Jump to the next record. 00377 p_next_rec += (FDS_HEADER_SIZE + ((fds_header_t*)(*p_record))->tl.length_words); 00378 } 00379 00380 // Scan until we find a valid record or until the end of the page. 00381 00382 /** README: We might seek until the write_offset is reached, but it might not 00383 * known at this point. */ 00384 while ((p_next_rec < (m_pages[page].start_addr + FS_PAGE_SIZE_WORDS)) && 00385 (*p_next_rec != FDS_ERASED_WORD)) // Did we jump to an erased word? 00386 { 00387 fds_header_t const * const p_header = (fds_header_t*)p_next_rec; 00388 00389 if (header_is_valid(p_header)) 00390 { 00391 // Bingo! 00392 *p_record = p_next_rec; 00393 return NRF_SUCCESS; 00394 } 00395 else 00396 { 00397 // The item is not valid, jump to the next. 00398 p_next_rec += (FDS_HEADER_SIZE + (p_header->tl.length_words)); 00399 } 00400 } 00401 00402 return NRF_ERROR_NOT_FOUND; 00403 } 00404 00405 00406 static ret_code_t seek_record(fds_record_desc_t * const p_desc) 00407 { 00408 uint32_t const * p_record; 00409 uint16_t page; 00410 bool seek_all_pages = false; 00411 00412 if ((p_desc->ptr_magic == FDS_MAGIC_HWORD) && 00413 (p_desc->gc_magic == m_gc_runs)) 00414 { 00415 // No need to seek the file. 00416 return NRF_SUCCESS; 00417 } 00418 00419 /** The pointer in the descriptor is not initialized, or GC 00420 * has been run since the last time it was retrieved. 00421 * We must seek the record again. */ 00422 00423 // Obtain the physical page ID. 00424 if (page_id_from_virtual_id(p_desc->vpage_id, &page) != NRF_SUCCESS) 00425 { 00426 page = 0; 00427 seek_all_pages = true; 00428 } 00429 00430 do { 00431 // Let's find the address from where we should start seeking the record. 00432 p_record = m_pages[page].start_addr + FDS_PAGE_TAG_SIZE; 00433 00434 /** Seek for a record with matching ID. 00435 * We might get away with seeking to the page write offset, if it is known. */ 00436 00437 while ((p_record < (m_pages[page].start_addr + FS_PAGE_SIZE_WORDS)) && 00438 (*p_record != FDS_ERASED_WORD)) 00439 { 00440 fds_header_t const * const p_header = (fds_header_t*)p_record; 00441 00442 if ((p_header->id != p_desc->record_id) || 00443 (!header_is_valid(p_header))) 00444 { 00445 // ID doesnt't match or the record has been cleared. Jump to the next record. 00446 p_record += FDS_HEADER_SIZE + p_header->tl.length_words; 00447 } 00448 else 00449 { 00450 // Update the pointer in the descriptor. 00451 p_desc->p_rec = p_record; 00452 p_desc->ptr_magic = FDS_MAGIC_HWORD; 00453 p_desc->gc_magic = m_gc_runs; 00454 00455 return NRF_SUCCESS; 00456 } 00457 } 00458 } while (seek_all_pages ? page++ < FDS_MAX_PAGES : 0); 00459 00460 return NRF_ERROR_NOT_FOUND; 00461 } 00462 00463 00464 static ret_code_t find_record(fds_type_id_t const * const p_type, 00465 fds_instance_id_t const * const p_inst, 00466 fds_record_desc_t * const p_desc, 00467 fds_find_token_t * const p_token) 00468 { 00469 if (!flag_is_set(FDS_FLAG_INITIALIZED)) 00470 { 00471 return NRF_ERROR_INVALID_STATE; 00472 } 00473 00474 // Here we distinguish between the first invocation and the and the others. 00475 if ((p_token->magic != FDS_MAGIC_WORD) || 00476 !address_within_page_bounds(p_token->p_addr)) // Is the address is really okay? 00477 { 00478 // Initialize the token. 00479 p_token->magic = FDS_MAGIC_WORD; 00480 p_token->vpage_id = 0; 00481 p_token->p_addr = NULL; 00482 } 00483 else 00484 { 00485 // Look past the last record address. 00486 p_token->p_addr += (FDS_HEADER_SIZE + ((fds_header_t*)p_token->p_addr)->tl.length_words); 00487 } 00488 00489 // Begin (or resume) searching for a record. 00490 for (; p_token->vpage_id < FDS_MAX_PAGES; p_token->vpage_id++) 00491 { 00492 uint16_t page = 0; 00493 00494 // Obtain the physical page ID. 00495 page_id_from_virtual_id(p_token->vpage_id, &page); 00496 00497 if (m_pages[page].page_type != FDS_PAGE_VALID) 00498 { 00499 // Skip this page. 00500 continue; 00501 } 00502 00503 if (p_token->p_addr == NULL) 00504 { 00505 // If it's the first time the function is run, initialize the pointer. 00506 p_token->p_addr = m_pages[page].start_addr + FDS_PAGE_TAG_SIZE; 00507 } 00508 00509 // Seek a valid record on this page, starting from the address stored in the token. 00510 while ((p_token->p_addr < (m_pages[page].start_addr + FS_PAGE_SIZE_WORDS)) && 00511 (*p_token->p_addr != FDS_ERASED_WORD)) // Did we jump to an erased word? 00512 { 00513 fds_header_t const * const p_header = (fds_header_t*)p_token->p_addr; 00514 00515 if (header_is_valid(p_header)) 00516 { 00517 // A valid record was found, check its header for a match. 00518 bool item_match = false; 00519 00520 if (p_type != NULL) 00521 { 00522 if (p_header->tl.type == *p_type) 00523 { 00524 item_match = true; 00525 } 00526 } 00527 00528 if (p_inst != NULL) 00529 { 00530 if (p_header->ic.instance == *p_inst) 00531 { 00532 item_match = (p_type == NULL) ? true : item_match && true; 00533 } 00534 else 00535 { 00536 item_match = false; 00537 } 00538 } 00539 00540 if (item_match) 00541 { 00542 // We found the record! Update the descriptor. 00543 p_desc->vpage_id = m_pages[page].vpage_id; 00544 p_desc->record_id = p_header->id; 00545 00546 p_desc->p_rec = p_token->p_addr; 00547 p_desc->ptr_magic = FDS_MAGIC_HWORD; 00548 p_desc->gc_magic = m_gc_runs; 00549 00550 return NRF_SUCCESS; 00551 } 00552 } 00553 // Jump to the next record. 00554 p_token->p_addr += (FDS_HEADER_SIZE + (p_header->tl.length_words)); 00555 } 00556 00557 /** We have seeked an entire page. Set the address in the token to NULL 00558 * so that it will be set again on the next iteration. */ 00559 p_token->p_addr = NULL; 00560 } 00561 00562 /** If we couldn't find the record, zero the token structure 00563 * so that it can be reused. */ 00564 p_token->magic = 0x00; 00565 00566 return NRF_ERROR_NOT_FOUND; 00567 } 00568 00569 00570 static void gc_init() 00571 { 00572 // Set which pages to GC. 00573 for (uint16_t i = 0; i < FDS_MAX_PAGES; i++) 00574 { 00575 m_gc.do_gc_page[i] = (m_pages[i].page_type == FDS_PAGE_VALID); 00576 } 00577 } 00578 00579 00580 static void gc_reset() 00581 { 00582 m_gc.state = BEGIN; 00583 m_gc.cur_page = 0; 00584 m_gc.p_scan_addr = NULL; 00585 } 00586 00587 00588 static void gc_set_state(fds_gc_state_t new_state) 00589 { 00590 m_gc.state = new_state; 00591 } 00592 00593 00594 static ret_code_t gc_get_next_page(uint16_t * const next_page) 00595 { 00596 for (uint16_t i = 0; i < FDS_MAX_PAGES; i++) 00597 { 00598 if (m_gc.do_gc_page[i]) 00599 { 00600 uint16_t records_open; 00601 00602 CRITICAL_SECTION_ENTER(); 00603 records_open = m_pages[i].records_open; 00604 CRITICAL_SECTION_EXIT(); 00605 00606 // Do not attempt to GC this page anymore. 00607 m_gc.do_gc_page[i] = false; 00608 00609 // Only GC pages with no open records. 00610 if (records_open == 0) 00611 { 00612 *next_page = i; 00613 return NRF_SUCCESS; 00614 } 00615 } 00616 } 00617 00618 return NRF_ERROR_NOT_FOUND; 00619 } 00620 00621 00622 static ret_code_t gc_page() 00623 { 00624 ret_code_t ret; 00625 00626 ret = gc_get_next_page(&m_gc.cur_page); 00627 00628 // No pages left to GC. GC has terminated. Reset GC data. 00629 if (ret != NRF_SUCCESS) 00630 { 00631 gc_reset(); 00632 00633 return COMMAND_COMPLETED; 00634 } 00635 00636 // Prepare to GC the page. 00637 gc_set_state(GC_PAGE); 00638 00639 // Flag the page as being garbage collected. 00640 ret = page_tag_write_gc(m_gc.cur_page); 00641 00642 if (ret != NRF_SUCCESS) 00643 { 00644 return ret; 00645 } 00646 00647 return COMMAND_EXECUTING; 00648 } 00649 00650 00651 static ret_code_t gc_copy_record() 00652 { 00653 ret_code_t fs_ret; 00654 00655 // We have found a record to copy. 00656 fds_record_t const * const p_record = (fds_record_t*)m_gc.p_scan_addr; 00657 00658 gc_set_state(COPY_RECORD); 00659 00660 // Copy the item to swap. 00661 fs_ret = fs_store(&fs_config, 00662 m_pages[m_gc.swap_page].start_addr + m_pages[m_gc.swap_page].write_offset, 00663 (uint32_t*)p_record, 00664 FDS_HEADER_SIZE + p_record->header.tl.length_words); 00665 00666 if (fs_ret != NRF_SUCCESS) 00667 { 00668 // Oops :( 00669 // This is an error. Can we recover? 00670 } 00671 00672 // Remember to update the swap page write offset. 00673 m_pages[m_gc.swap_page].write_offset += (FDS_HEADER_SIZE + p_record->header.tl.length_words); 00674 00675 return COMMAND_EXECUTING; 00676 } 00677 00678 00679 static ret_code_t gc_ready_swap_page() 00680 { 00681 ret_code_t fs_ret; 00682 00683 /** A page has been scanned through. All valid records found were copied to swap. 00684 * The swap page can now be flagged as a valid page. */ 00685 gc_set_state(READY_SWAP); 00686 00687 fs_ret = page_tag_write_valid(m_gc.swap_page); 00688 if (fs_ret != NRF_SUCCESS) 00689 { 00690 return fs_ret; 00691 } 00692 00693 /** Do not update the page type in the internal page structure (m_pages) 00694 * right away. (why?) */ 00695 return COMMAND_EXECUTING; 00696 } 00697 00698 00699 static ret_code_t gc_seek_record() 00700 { 00701 // Let's find a valid record which has not been copied yet. 00702 if (scan_next_valid(m_gc.cur_page, &m_gc.p_scan_addr) == NRF_SUCCESS) 00703 { 00704 /** The record is guaranteed to fit in the destination page, 00705 * so we don't need to check its size. */ 00706 return gc_copy_record(); 00707 } 00708 else 00709 { 00710 /** No more (uncopied) records left on this page. 00711 * The swap page can now be marked as a valid page. */ 00712 return gc_ready_swap_page(); 00713 } 00714 } 00715 00716 00717 static ret_code_t gc_new_swap_page() 00718 { 00719 ret_code_t fs_ret; 00720 uint16_t vpage_id; 00721 00722 gc_set_state(NEW_SWAP); 00723 00724 // Save the swap page virtual page ID. 00725 vpage_id = m_pages[m_gc.swap_page].vpage_id; 00726 00727 /** The swap page has been marked as valid in Flash. We copy the GC'ed page 00728 * write_offset and virtual page ID. */ 00729 m_pages[m_gc.swap_page].page_type = FDS_PAGE_VALID; 00730 m_pages[m_gc.swap_page].vpage_id = m_pages[m_gc.cur_page].vpage_id; 00731 m_pages[m_gc.swap_page].words_reserved = m_pages[m_gc.cur_page].words_reserved; 00732 00733 // The new swap page is now the page we just GC. 00734 m_gc.swap_page = m_gc.cur_page; 00735 00736 // Update the write_offset, words_reserved and vpage_id fields for the new swap page. 00737 m_pages[m_gc.swap_page].page_type = FDS_PAGE_SWAP; 00738 m_pages[m_gc.swap_page].vpage_id = vpage_id; 00739 m_pages[m_gc.swap_page].write_offset = FDS_PAGE_TAG_SIZE; 00740 m_pages[m_gc.swap_page].words_reserved = 0; 00741 00742 /** Finally, erase the new swap page. Remember we still have to flag this 00743 * new page as swap, but we'll wait the callback for this operation to do so. */ 00744 fs_ret = fs_erase(&fs_config, 00745 (uint32_t*)m_pages[m_gc.swap_page].start_addr, 00746 FS_PAGE_SIZE_WORDS); 00747 00748 if (fs_ret != NRF_SUCCESS) 00749 { 00750 return fs_ret; 00751 } 00752 00753 return COMMAND_EXECUTING; 00754 } 00755 00756 00757 static ret_code_t gc_new_swap_page_init() 00758 { 00759 ret_code_t fs_ret; 00760 00761 gc_set_state(INIT_SWAP); 00762 00763 fs_ret = page_tag_write_swap(m_gc.swap_page); 00764 if (fs_ret != NRF_SUCCESS) 00765 { 00766 return fs_ret; 00767 } 00768 00769 return COMMAND_EXECUTING; 00770 } 00771 00772 00773 static ret_code_t gc_execute(uint32_t result) 00774 { 00775 // TODO: Handle resuming GC. 00776 00777 ret_code_t ret; 00778 00779 if (result != NRF_SUCCESS) 00780 { 00781 // An operation failed. Report to the application. 00782 return result; 00783 } 00784 00785 switch (m_gc.state) 00786 { 00787 case BEGIN: 00788 { 00789 // Increment the number of times the GC has been run. 00790 m_gc_runs++; 00791 // Sets up a list of pages to GC. 00792 gc_init(); 00793 // Go ! 00794 ret = gc_page(); 00795 } break; 00796 00797 case GC_PAGE: 00798 /** A page has been successfully flagged as being GC. 00799 * Look for valid records to copy. */ 00800 ret = gc_seek_record(); 00801 break; 00802 00803 case COPY_RECORD: 00804 /** A record has been copied to swap. 00805 * Look for more records to copy. */ 00806 ret = gc_seek_record(); 00807 break; 00808 00809 case READY_SWAP: 00810 /** The swap page has been flagged as 'valid' (ready). 00811 * Let's prepare a new swap page. */ 00812 ret = gc_new_swap_page(); 00813 break; 00814 00815 case NEW_SWAP: 00816 // A new swap page has been prepared. Let's flag it as swap. 00817 ret = gc_new_swap_page_init(); 00818 break; 00819 00820 case INIT_SWAP: 00821 /** The swap was flagged as swap in flash. Let's compress another page. 00822 * Be sure to update the address where to scan from. */ 00823 m_gc.p_scan_addr = NULL; 00824 ret = gc_page(); 00825 break; 00826 00827 default: 00828 // Should really not happen. 00829 ret = NRF_ERROR_INTERNAL; 00830 break; 00831 } 00832 00833 return ret; 00834 } 00835 00836 00837 /**@brief Function for initializing the command queue. */ 00838 static void queues_init(void) 00839 { 00840 memset(&m_cmd_queue, 0, sizeof(fds_cmd_queue_t)); 00841 memset(&m_chunk_queue, 0, sizeof(fds_chunk_queue_t)); 00842 } 00843 00844 00845 void cmd_queue_next(fds_cmd_t ** pp_cmd) 00846 { 00847 if (*pp_cmd != &m_cmd_queue.cmd[FDS_CMD_QUEUE_SIZE - 1]) 00848 { 00849 (*pp_cmd)++; 00850 return; 00851 } 00852 00853 *pp_cmd = &m_cmd_queue.cmd[0]; 00854 } 00855 00856 00857 void chunk_queue_next(fds_record_chunk_t ** pp_chunk) 00858 { 00859 if ((*pp_chunk) != &m_chunk_queue.chunk[FDS_CHUNK_QUEUE_SIZE - 1]) 00860 { 00861 (*pp_chunk)++; 00862 return; 00863 } 00864 00865 *pp_chunk = &m_chunk_queue.chunk[0]; 00866 } 00867 00868 00869 /**@brief Advances one position in the command queue. Returns true if the queue is not empty. */ 00870 static bool cmd_queue_advance(void) 00871 { 00872 // Reset the current element. 00873 memset(&m_cmd_queue.cmd[m_cmd_queue.rp], 0, sizeof(fds_cmd_t)); 00874 00875 CRITICAL_SECTION_ENTER(); 00876 if (m_cmd_queue.count != 0) 00877 { 00878 // Advance in the queue, wrapping around if necessary. 00879 m_cmd_queue.rp = (m_cmd_queue.rp + 1) % FDS_CMD_QUEUE_SIZE; 00880 m_cmd_queue.count--; 00881 } 00882 CRITICAL_SECTION_EXIT(); 00883 00884 return m_cmd_queue.count != 0; 00885 } 00886 00887 00888 /**@brief Returns the current chunk, and advances to the next in the queue. */ 00889 static bool chunk_queue_get_and_advance(fds_record_chunk_t ** pp_chunk) 00890 { 00891 bool chunk_popped = false; 00892 00893 CRITICAL_SECTION_ENTER(); 00894 if (m_chunk_queue.count != 0) 00895 { 00896 // Point to the current chunk and advance the queue. 00897 *pp_chunk = &m_chunk_queue.chunk[m_chunk_queue.rp]; 00898 00899 m_chunk_queue.rp = (m_chunk_queue.rp + 1) % FDS_CHUNK_QUEUE_SIZE; 00900 m_chunk_queue.count--; 00901 00902 chunk_popped = true; 00903 } 00904 CRITICAL_SECTION_EXIT(); 00905 00906 return chunk_popped; 00907 } 00908 00909 00910 static bool chunk_queue_skip(uint8_t num_op) 00911 { 00912 bool chunk_skipped = false; 00913 00914 CRITICAL_SECTION_ENTER(); 00915 if (num_op <= m_chunk_queue.count) 00916 { 00917 m_chunk_queue.count -= num_op; 00918 chunk_skipped = true; 00919 } 00920 CRITICAL_SECTION_EXIT(); 00921 00922 return chunk_skipped; 00923 } 00924 00925 00926 /**@brief Reserves resources on both queues. */ 00927 static ret_code_t queue_reserve(uint8_t num_cmd, 00928 uint8_t num_chunks, 00929 fds_cmd_t ** pp_cmd, 00930 fds_record_chunk_t ** pp_chunk) 00931 { 00932 uint8_t cmd_index; 00933 uint8_t chunk_index; 00934 00935 // This is really just being safe. 00936 if (pp_cmd == NULL || ((pp_chunk == NULL) && (num_chunks != 0))) 00937 { 00938 return NRF_ERROR_NULL; 00939 } 00940 00941 if (num_cmd == 0) 00942 { 00943 return NRF_ERROR_INVALID_DATA; 00944 } 00945 00946 CRITICAL_SECTION_ENTER(); 00947 00948 // Ensure there is enough space in the queues. 00949 if ((m_cmd_queue.count > FDS_CMD_QUEUE_SIZE - num_cmd) || 00950 (m_chunk_queue.count > FDS_CHUNK_QUEUE_SIZE - num_chunks)) 00951 { 00952 CRITICAL_SECTION_EXIT(); 00953 return NRF_ERROR_BUSY; 00954 } 00955 00956 // Find the write position in the commands queue. 00957 cmd_index = m_cmd_queue.count; 00958 cmd_index += m_cmd_queue.rp; 00959 cmd_index = cmd_index % FDS_CMD_QUEUE_SIZE; 00960 00961 *pp_cmd = &m_cmd_queue.cmd[cmd_index]; 00962 m_cmd_queue.count += num_cmd; 00963 00964 /* If no operations are associated with the command, such as is the case 00965 * for initialization and compression, pp_chunk can be NULL. */ 00966 if (num_chunks != 0) 00967 { 00968 chunk_index = m_chunk_queue.count; 00969 chunk_index += m_chunk_queue.rp; 00970 chunk_index = chunk_index % FDS_CHUNK_QUEUE_SIZE; 00971 00972 *pp_chunk = &m_chunk_queue.chunk[chunk_index]; 00973 m_chunk_queue.count += num_chunks; 00974 } 00975 00976 CRITICAL_SECTION_EXIT(); 00977 00978 return NRF_SUCCESS; 00979 } 00980 00981 00982 /**@brief Cancel the reservation on resources on queues. */ 00983 static void queue_reserve_cancel(uint8_t num_cmd, uint8_t num_chunks) 00984 { 00985 CRITICAL_SECTION_ENTER(); 00986 m_cmd_queue.count -= num_cmd; 00987 m_chunk_queue.count -= num_chunks; 00988 CRITICAL_SECTION_EXIT(); 00989 } 00990 00991 00992 static void pages_init(uint16_t * const p_pages_avail, 00993 bool * const p_write_page_tag, 00994 bool * const p_resume_comp) 00995 { 00996 *p_pages_avail = 0; 00997 *p_write_page_tag = false; 00998 *p_resume_comp = false; 00999 01000 /** Scan pages and setup page data. 01001 * This function does NOT perform write operations in flash. */ 01002 for (uint16_t i = 0; i < FDS_MAX_PAGES; i++) 01003 { 01004 // Initialize page data. Note that start_addr must be set BEFORE invoking page_identify(). 01005 m_pages[i].start_addr = fs_config.p_start_addr + (i * FS_PAGE_SIZE_WORDS); 01006 m_pages[i].write_offset = FDS_PAGE_TAG_SIZE; 01007 m_pages[i].vpage_id = i; 01008 m_pages[i].records_open = 0; 01009 m_pages[i].words_reserved = 0; 01010 01011 m_pages[i].page_type = page_identify(i); 01012 01013 switch (m_pages[i].page_type) 01014 { 01015 case FDS_PAGE_UNDEFINED: 01016 { 01017 if (page_is_empty(i)) 01018 { 01019 /* We have found an erased page, which can be initialized. 01020 * This will require a write in flash. */ 01021 m_pages[i].page_type = FDS_PAGE_ERASED; 01022 *p_write_page_tag = true; 01023 } 01024 } break; 01025 01026 case FDS_PAGE_VALID: 01027 { 01028 /** If a page is valid, we update its write offset. 01029 * Additionally, page_scan will update the last known record ID. */ 01030 page_scan(i, &m_pages[i].write_offset); 01031 (*p_pages_avail)++; 01032 } break; 01033 01034 case FDS_PAGE_SWAP: 01035 { 01036 m_gc.swap_page = i; 01037 m_swap_page_avail = true; 01038 } break; 01039 01040 case FDS_PAGE_GC: 01041 { 01042 /** There is an ongoing garbage collection. 01043 * We should resume the operation, which we don't yet. */ 01044 m_gc.cur_page = i; 01045 m_gc.state = GC_PAGE; 01046 *p_resume_comp = true; 01047 } break; 01048 01049 default: 01050 break; 01051 } 01052 } 01053 } 01054 01055 01056 // NOTE: Adds FDS_HEADER_SIZE automatically. 01057 static ret_code_t write_space_reserve(uint16_t length_words, uint16_t * vpage_id) 01058 { 01059 bool space_reserved = false; 01060 uint16_t total_len_words = length_words + FDS_HEADER_SIZE; 01061 01062 if (total_len_words >= FS_PAGE_SIZE_WORDS - FDS_PAGE_TAG_SIZE) 01063 { 01064 return NRF_ERROR_INVALID_LENGTH; 01065 } 01066 01067 for (uint16_t page = 0; page < FDS_MAX_PAGES; page++) 01068 { 01069 if ((m_pages[page].page_type == FDS_PAGE_VALID) && 01070 (page_has_space(page, total_len_words))) 01071 { 01072 space_reserved = true; 01073 *vpage_id = m_pages[page].vpage_id; 01074 01075 CRITICAL_SECTION_ENTER(); 01076 m_pages[page].words_reserved += total_len_words; 01077 CRITICAL_SECTION_EXIT(); 01078 01079 break; 01080 } 01081 } 01082 01083 return space_reserved ? NRF_SUCCESS : NRF_ERROR_NO_MEM; 01084 } 01085 01086 01087 static bool chunk_is_aligned(fds_record_chunk_t const * const p_chunk, uint8_t num_parts) 01088 { 01089 for (uint8_t i = 0; i < num_parts; i++) 01090 { 01091 if (!is_word_aligned(p_chunk[i].p_data)) 01092 { 01093 return false; 01094 } 01095 } 01096 01097 return true; 01098 } 01099 01100 01101 static ret_code_t init_execute(uint32_t result, uint32_t const * p_page_addr) 01102 { 01103 uint16_t cur_page; 01104 bool page_tag_written = false; 01105 01106 if (result != NRF_SUCCESS) 01107 { 01108 // Oops. Error. 01109 return result; 01110 } 01111 01112 // Here we just distinguish between the first invocation and the others. 01113 cur_page = p_page_addr == NULL ? 0 : page_by_addr(p_page_addr) + 1; 01114 01115 if (cur_page == FDS_MAX_PAGES) 01116 { 01117 // We have finished. We'd need to set some flags. 01118 flag_set(FDS_FLAG_INITIALIZED); 01119 flag_clear(FDS_FLAG_INITIALIZING); 01120 01121 return COMMAND_COMPLETED; 01122 } 01123 01124 while (cur_page < FDS_MAX_PAGES && !page_tag_written) 01125 { 01126 if (m_pages[cur_page].page_type == FDS_PAGE_ERASED) 01127 { 01128 page_tag_written = true; 01129 01130 if (m_swap_page_avail) 01131 { 01132 if (page_tag_write_valid(cur_page) != NRF_SUCCESS) 01133 { 01134 // Oops. Error. 01135 } 01136 // Update the page type. 01137 m_pages[cur_page].page_type = FDS_PAGE_VALID; 01138 } 01139 else 01140 { 01141 if (page_tag_write_swap(cur_page) != NRF_SUCCESS) 01142 { 01143 // Oops. Error. 01144 } 01145 // Update the page type. 01146 m_pages[cur_page].page_type = FDS_PAGE_SWAP; 01147 01148 /** Update compression data. We set this information in init_pages 01149 * if it is available, otherwise, we should set it here. */ 01150 m_swap_page_avail = true; 01151 m_gc.swap_page = cur_page; 01152 } 01153 } 01154 01155 cur_page++; 01156 } 01157 01158 if (!page_tag_written) 01159 { 01160 if (m_swap_page_avail) 01161 { 01162 return COMMAND_COMPLETED; 01163 } 01164 else 01165 { 01166 // There is no empty space to use as swap. 01167 // Notify user that no compression is available? 01168 } 01169 } 01170 01171 return COMMAND_EXECUTING; 01172 } 01173 01174 01175 /**@brief Function to execute write and update commands. 01176 * 01177 */ 01178 static ret_code_t store_execute(uint32_t result, fds_cmd_t * const p_cmd) 01179 { 01180 ret_code_t fs_ret; 01181 fds_record_chunk_t * p_chunk = NULL; 01182 fds_page_t * p_page = NULL; 01183 uint32_t * p_write_addr; 01184 01185 // Using virtual page IDs allows other operations to be queued even if GC has been requested. 01186 page_from_virtual_id(p_cmd->vpage_id, &p_page); 01187 01188 if (result != NRF_SUCCESS) 01189 { 01190 // The previous operation has failed, update the page data. 01191 p_page->write_offset += (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA)); 01192 p_page->words_reserved -= (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA)); 01193 01194 return result; 01195 } 01196 01197 // Compute the write address (just syntatic sugar). 01198 p_write_addr = (uint32_t*)(p_page->start_addr + p_page->write_offset); 01199 01200 // Execute the operation. 01201 switch (p_cmd->op_code) 01202 { 01203 case FDS_OP_WRITE_TL: 01204 { 01205 fs_ret = fs_store(&fs_config, 01206 p_write_addr + FDS_WRITE_OFFSET_TL, 01207 (uint32_t*)&p_cmd->record_header.tl, 01208 FDS_HEADER_SIZE_TL /*Words*/); 01209 01210 // Set the next operation to be executed. 01211 p_cmd->op_code = FDS_OP_WRITE_ID; 01212 01213 } break; 01214 01215 case FDS_OP_WRITE_ID: 01216 { 01217 fs_ret = fs_store(&fs_config, 01218 p_write_addr + FDS_WRITE_OFFSET_ID, 01219 (uint32_t*)&p_cmd->record_header.id, 01220 FDS_HEADER_SIZE_ID /*Words*/); 01221 01222 p_cmd->op_code = FDS_OP_WRITE_CHUNK; 01223 01224 } break; 01225 01226 case FDS_OP_WRITE_CHUNK: 01227 { 01228 // Decrement the number of chunks left to write. 01229 p_cmd->num_chunks--; 01230 01231 // Retrieve the chunk to be written. 01232 chunk_queue_get_and_advance(&p_chunk); 01233 01234 fs_ret = fs_store(&fs_config, 01235 p_write_addr + p_cmd->chunk_offset, 01236 p_chunk->p_data, 01237 p_chunk->length_words); 01238 01239 // Accumulate the offset. 01240 p_cmd->chunk_offset += p_chunk->length_words; 01241 01242 if (p_cmd->num_chunks == 0) 01243 { 01244 /** We have written all the record chunks; we'll write 01245 * IC last as a mean to 'validate' the record. */ 01246 p_cmd->op_code = FDS_OP_WRITE_IC; 01247 } 01248 01249 } break; 01250 01251 case FDS_OP_WRITE_IC: 01252 { 01253 fs_ret = fs_store(&fs_config, 01254 p_write_addr + FDS_WRITE_OFFSET_IC, 01255 (uint32_t*)&p_cmd->record_header.ic, 01256 FDS_HEADER_SIZE_IC /*Words*/); 01257 01258 // This is the final operation. 01259 p_cmd->op_code = FDS_OP_DONE; 01260 01261 } break; 01262 01263 case FDS_OP_DONE: 01264 { 01265 // We have successfully written down the IC. The command has completed successfully. 01266 p_page->write_offset += (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA)); 01267 p_page->words_reserved -= (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA)); 01268 01269 return COMMAND_COMPLETED; 01270 01271 }; 01272 01273 default: 01274 fs_ret = NRF_ERROR_INTERNAL; 01275 break; 01276 } 01277 01278 // If fs_store did not succeed, the command has failed. 01279 if (fs_ret != NRF_SUCCESS) 01280 { 01281 /** We're not going to receive a callback from fstorage 01282 * so we update the page data right away. */ 01283 p_page->write_offset += (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA)); 01284 p_page->words_reserved -= (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA)); 01285 01286 // We should propagate the error from fstorage. 01287 return fs_ret; 01288 } 01289 01290 // An operation has successfully been executed. Wait for the callback. 01291 return COMMAND_EXECUTING; 01292 } 01293 01294 01295 static ret_code_t clear_execute(ret_code_t result, fds_cmd_t * const p_cmd) 01296 { 01297 ret_code_t ret; 01298 fds_record_desc_t desc; 01299 01300 // This must persist across calls. 01301 static fds_find_token_t tok; 01302 01303 if (result != NRF_SUCCESS) 01304 { 01305 // A previous operation has failed. Propagate the error. 01306 return result; 01307 } 01308 01309 switch (p_cmd->op_code) 01310 { 01311 case FDS_OP_CLEAR_TL: 01312 { 01313 // We were provided a descriptor for the record. 01314 desc.vpage_id = p_cmd->vpage_id; 01315 desc.record_id = p_cmd->record_header.id; 01316 01317 /** Unfortunately, we always seek the record in this case, 01318 * because we don't buffer an entire record descriptor in the 01319 * fds_cmd_t structure. Keep in mind though, that we will 01320 * seek one page at most. */ 01321 if (seek_record(&desc) != NRF_SUCCESS) 01322 { 01323 // The record never existed, or it is already cleared. 01324 ret = NRF_ERROR_NOT_FOUND; 01325 } 01326 else 01327 { 01328 // Copy the record key, so that it may be returned in the callback. 01329 p_cmd->record_header.tl.type = ((fds_header_t*)desc.p_rec)->tl.type; 01330 p_cmd->record_header.ic.instance = ((fds_header_t*)desc.p_rec)->ic.instance; 01331 01332 ret = fs_store(&fs_config, 01333 desc.p_rec, 01334 (uint32_t*)&m_fds_tl_invalid, 01335 FDS_HEADER_SIZE_TL); 01336 } 01337 01338 p_cmd->op_code = FDS_OP_DONE; 01339 01340 } break; 01341 01342 case FDS_OP_CLEAR_INSTANCE: 01343 { 01344 if (find_record(NULL, &p_cmd->record_header.ic.instance, 01345 &desc, &tok) != NRF_SUCCESS) 01346 { 01347 // No more records to be found. 01348 p_cmd->op_code = FDS_OP_DONE; 01349 01350 // Zero the token, so that we may reuse it. 01351 memset(&tok, 0, sizeof(fds_find_token_t)); 01352 01353 /** We won't receive a callback, since no flash operation 01354 * was initiated. The command has finished. */ 01355 ret = COMMAND_COMPLETED; 01356 } 01357 else 01358 { 01359 ret = fs_store(&fs_config, 01360 desc.p_rec, 01361 (uint32_t*)&m_fds_tl_invalid, 01362 FDS_HEADER_SIZE_TL); 01363 } 01364 } break; 01365 01366 case FDS_OP_DONE: 01367 { 01368 /** The last operation completed successfully. 01369 * The command has finished. Return. */ 01370 ret = COMMAND_COMPLETED; 01371 } break; 01372 01373 default: 01374 ret = NRF_ERROR_INVALID_DATA; 01375 break; 01376 } 01377 01378 // Await for the operation result. 01379 return ret; 01380 } 01381 01382 01383 static ret_code_t cmd_queue_process(void) 01384 { 01385 ret_code_t ret; 01386 fds_cmd_t * const p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; 01387 01388 switch (p_cmd->id) 01389 { 01390 case FDS_CMD_INIT: 01391 ret = init_execute(NRF_SUCCESS, NULL); 01392 break; 01393 01394 case FDS_CMD_WRITE: 01395 case FDS_CMD_UPDATE: 01396 ret = store_execute(NRF_SUCCESS, p_cmd); 01397 break; 01398 01399 case FDS_CMD_CLEAR: 01400 case FDS_CMD_CLEAR_INST: 01401 ret = clear_execute(NRF_SUCCESS, p_cmd); 01402 break; 01403 01404 case FDS_CMD_GC: 01405 ret = gc_execute(NRF_SUCCESS); 01406 break; 01407 01408 default: 01409 ret = NRF_ERROR_FORBIDDEN; 01410 break; 01411 } 01412 01413 if ((ret == COMMAND_EXECUTING) || (ret == COMMAND_COMPLETED)) 01414 { 01415 return NRF_SUCCESS; 01416 } 01417 01418 // This is an error. 01419 return ret; 01420 } 01421 01422 01423 static ret_code_t cmd_queue_process_start(void) 01424 { 01425 bool start_processing = false; 01426 01427 if (!flag_is_set(FDS_FLAG_PROCESSING)) 01428 { 01429 flag_set(FDS_FLAG_PROCESSING); 01430 start_processing = true; 01431 } 01432 01433 if (!start_processing) 01434 { 01435 // We are awaiting a callback, so there is no need to manually start queue processing. 01436 return NRF_SUCCESS; 01437 } 01438 01439 return cmd_queue_process(); 01440 } 01441 01442 01443 static void fs_callback(uint8_t op_code, 01444 uint32_t result, 01445 uint32_t const * p_data, 01446 fs_length_t length) 01447 { 01448 ret_code_t ret; 01449 fds_cmd_t * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; 01450 fds_record_key_t record_key; 01451 01452 switch (p_cmd->id) 01453 { 01454 case FDS_CMD_INIT: 01455 ret = init_execute(result, p_data); 01456 break; 01457 01458 case FDS_CMD_WRITE: 01459 case FDS_CMD_UPDATE: 01460 ret = store_execute(result, p_cmd); 01461 break; 01462 01463 case FDS_CMD_CLEAR: 01464 case FDS_CMD_CLEAR_INST: 01465 ret = clear_execute(result, p_cmd); 01466 break; 01467 01468 case FDS_CMD_GC: 01469 ret = gc_execute(result); 01470 break; 01471 01472 default: 01473 // Should not happen. 01474 ret = NRF_ERROR_INTERNAL; 01475 break; 01476 } 01477 01478 if (ret == COMMAND_EXECUTING /*=NRF_SUCCESS*/) 01479 { 01480 /** The current command is still being processed. 01481 * The command queue does not need to advance. */ 01482 return; 01483 } 01484 01485 // Initialize the fds_record_key_t structure needed for the callback. 01486 record_key.type = p_cmd->record_header.tl.type; 01487 record_key.instance = p_cmd->record_header.ic.instance; 01488 01489 // The command has either completed or an operation (and thus the command) has failed. 01490 if (ret == COMMAND_COMPLETED) 01491 { 01492 // The command has completed successfully. Notify the application. 01493 app_notify(NRF_SUCCESS, p_cmd->id, p_cmd->record_header.id, record_key); 01494 } 01495 else 01496 { 01497 /** An operation has failed. This is fatal for the execution of a command. 01498 * Skip other operations associated with the current command. 01499 * Notify the user of the failure. */ 01500 chunk_queue_skip(p_cmd->num_chunks); 01501 app_notify(ret /*=result*/, p_cmd->id, p_cmd->record_header.id, record_key); 01502 } 01503 01504 // Advance the command queue, and if there is still something in the queue, process it. 01505 if (cmd_queue_advance()) 01506 { 01507 /** Only process the queue if there are no pending commands being queued, since they 01508 * will begin to process the queue on their own. Be sure to clear 01509 * the flag FDS_FLAG_PROCESSING though ! */ 01510 if (atomic_counter_is_zero()) 01511 { 01512 cmd_queue_process(); 01513 } 01514 else 01515 { 01516 flag_clear(FDS_FLAG_PROCESSING); 01517 } 01518 } 01519 else 01520 { 01521 /** No more elements in the queue. Clear the FDS_FLAG_PROCESSING flag, 01522 * so that new commands can start the queue processing. */ 01523 flag_clear(FDS_FLAG_PROCESSING); 01524 } 01525 } 01526 01527 01528 ret_code_t fds_init() 01529 { 01530 ret_code_t fs_ret; 01531 fds_cmd_t * p_cmd; 01532 uint16_t pages_avail; 01533 bool write_page_tag; 01534 bool resume_compression; 01535 01536 fds_record_key_t const dummy_key = {.type = FDS_TYPE_ID_INVALID, 01537 .instance = FDS_INSTANCE_ID_INVALID}; 01538 01539 if (flag_is_set(FDS_FLAG_INITIALIZED)) 01540 { 01541 // Notify immediately. 01542 app_notify(NRF_SUCCESS, FDS_CMD_INIT, 0 /*unused*/, dummy_key /*unused*/); 01543 return NRF_SUCCESS; 01544 } 01545 01546 if (flag_is_set(FDS_FLAG_INITIALIZING)) 01547 { 01548 return NRF_ERROR_INVALID_STATE; 01549 } 01550 01551 fs_ret = fs_init(); 01552 if (fs_ret != NRF_SUCCESS) 01553 { 01554 // fs_init() failed, propagate the error. 01555 return fs_ret; 01556 } 01557 01558 queues_init(); 01559 01560 /** Initialize the last known record to zero. 01561 * Its value will be updated by page_scan() called in pages_init(). */ 01562 m_last_rec_id = 0; 01563 01564 // Initialize the page table containing all info on pages (address, type etc). 01565 pages_init(&pages_avail, &write_page_tag, &resume_compression); 01566 01567 if (pages_avail == 0 && !write_page_tag) 01568 { 01569 return NRF_ERROR_NO_MEM; 01570 } 01571 01572 /** This flag means fds_init() has been called. However, 01573 * the module is NOT yet initialized. */ 01574 flag_set(FDS_FLAG_INITIALIZING); 01575 01576 if (resume_compression) 01577 { 01578 return NRF_SUCCESS; 01579 } 01580 01581 if (write_page_tag) 01582 { 01583 if (queue_reserve(FDS_CMD_QUEUE_SIZE_INIT, 0, &p_cmd, NULL) != NRF_SUCCESS) 01584 { 01585 // Should never happen. 01586 return NRF_ERROR_BUSY; 01587 } 01588 01589 // Initialize the command in the queue. 01590 p_cmd->id = FDS_CMD_INIT; 01591 01592 return cmd_queue_process_start(); 01593 } 01594 else 01595 { 01596 /* No flash operation is necessary for initialization. 01597 * We can notify the application immediately. */ 01598 flag_set (FDS_FLAG_INITIALIZED); 01599 flag_clear(FDS_FLAG_INITIALIZING); 01600 app_notify(NRF_SUCCESS, FDS_CMD_INIT, 0 /*unused*/, dummy_key /*unused*/); 01601 } 01602 01603 return NRF_SUCCESS; 01604 } 01605 01606 01607 ret_code_t fds_open(fds_record_desc_t * const p_desc, 01608 fds_record_t * const p_record) 01609 { 01610 uint16_t page; 01611 01612 if (p_desc == NULL || p_record == NULL) 01613 { 01614 return NRF_ERROR_NULL; 01615 } 01616 01617 if (page_id_from_virtual_id(p_desc->vpage_id, &page) != NRF_SUCCESS) 01618 { 01619 // Should not happen. 01620 return NRF_ERROR_INVALID_DATA; 01621 } 01622 01623 // Seek the record if necessary. 01624 if (seek_record(p_desc) == NRF_SUCCESS) 01625 { 01626 if (header_is_valid((fds_header_t*)p_desc->p_rec)) 01627 { 01628 CRITICAL_SECTION_ENTER(); 01629 m_pages[page].records_open++; 01630 CRITICAL_SECTION_EXIT(); 01631 01632 p_record->header = *((fds_header_t*)p_desc->p_rec); 01633 p_record->p_data = (p_desc->p_rec + FDS_HEADER_SIZE); 01634 01635 return NRF_SUCCESS; 01636 } 01637 } 01638 01639 /** The record could not be found. 01640 * It either never existed or it has been cleared. */ 01641 return NRF_ERROR_NOT_FOUND; 01642 } 01643 01644 01645 ret_code_t fds_close(fds_record_desc_t const * const p_desc) 01646 { 01647 uint16_t page; 01648 01649 if (p_desc == NULL) 01650 { 01651 return NRF_ERROR_NULL; 01652 } 01653 01654 if (page_id_from_virtual_id(p_desc->vpage_id, &page) != NRF_SUCCESS) 01655 { 01656 return NRF_ERROR_INVALID_DATA; 01657 } 01658 01659 CRITICAL_SECTION_ENTER(); 01660 m_pages[page].records_open--; 01661 CRITICAL_SECTION_EXIT(); 01662 01663 return NRF_SUCCESS; 01664 } 01665 01666 01667 static ret_code_t write_enqueue(fds_record_desc_t * const p_desc, 01668 fds_record_key_t key, 01669 uint8_t num_chunks, 01670 fds_record_chunk_t chunks[], 01671 fds_write_token_t const * const p_tok, 01672 bool do_update) 01673 { 01674 ret_code_t ret; 01675 fds_cmd_t * p_cmd; 01676 fds_record_chunk_t * p_chunk = NULL; 01677 uint16_t vpage_id; 01678 uint16_t length_words = 0; 01679 uint8_t cmd_queue_elems; 01680 01681 if (!flag_is_set(FDS_FLAG_INITIALIZED)) 01682 { 01683 return NRF_ERROR_INVALID_STATE; 01684 } 01685 01686 if ((key.type == FDS_TYPE_ID_INVALID) || 01687 (key.instance == FDS_INSTANCE_ID_INVALID)) 01688 { 01689 return NRF_ERROR_INVALID_DATA; 01690 } 01691 01692 if (!chunk_is_aligned(chunks, num_chunks)) 01693 { 01694 return NRF_ERROR_INVALID_ADDR; 01695 } 01696 01697 cmd_queue_elems = do_update ? FDS_CMD_QUEUE_SIZE_UPDATE : FDS_CMD_QUEUE_SIZE_WRITE; 01698 01699 // Reserve space on both queues, and obtain pointers to the first elements reserved. 01700 ret = queue_reserve(cmd_queue_elems, 01701 num_chunks, 01702 &p_cmd, 01703 &p_chunk); 01704 01705 if (ret != NRF_SUCCESS) 01706 { 01707 return ret; 01708 } 01709 01710 // No space was previously reserved for this operation. 01711 if (p_tok == NULL) 01712 { 01713 // Compute the total length of the record. 01714 for (uint8_t i = 0; i < num_chunks; i++) 01715 { 01716 length_words += chunks[i].length_words; 01717 } 01718 01719 /** Find a page where we can write the data. Reserve the space necessary 01720 * to write the metadata as well. */ 01721 ret = write_space_reserve(length_words, &vpage_id); 01722 if (ret != NRF_SUCCESS) 01723 { 01724 // If there is no space available, cancel the queue reservation. 01725 queue_reserve_cancel(cmd_queue_elems, num_chunks); 01726 return ret; 01727 } 01728 } 01729 else 01730 { 01731 length_words = p_tok->length_words; 01732 vpage_id = p_tok->vpage_id; 01733 } 01734 01735 // Initialize the command. 01736 p_cmd->id = do_update ? FDS_CMD_UPDATE : FDS_CMD_WRITE; 01737 p_cmd->op_code = FDS_OP_WRITE_TL; 01738 p_cmd->num_chunks = num_chunks; 01739 p_cmd->chunk_offset = FDS_WRITE_OFFSET_DATA; 01740 p_cmd->vpage_id = vpage_id; 01741 01742 // Fill in the header information. 01743 p_cmd->record_header.id = record_id_new(); 01744 p_cmd->record_header.tl.type = key.type; 01745 p_cmd->record_header.tl.length_words = length_words; 01746 p_cmd->record_header.ic.instance = key.instance; 01747 p_cmd->record_header.ic.checksum = 0; 01748 01749 // Buffer the record chunks in the queue. 01750 for (uint8_t i = 0; i < num_chunks; i++) 01751 { 01752 p_chunk->p_data = chunks[i].p_data; 01753 p_chunk->length_words = chunks[i].length_words; 01754 chunk_queue_next(&p_chunk); 01755 } 01756 01757 if (do_update) 01758 { 01759 // Clear 01760 cmd_queue_next(&p_cmd); 01761 p_cmd->id = FDS_CMD_CLEAR; 01762 p_cmd->op_code = FDS_OP_CLEAR_TL; 01763 01764 p_cmd->vpage_id = p_desc->vpage_id; 01765 p_cmd->record_header.id = p_desc->record_id; 01766 } 01767 01768 // Initialize the record descriptor, if provided. 01769 if (p_desc != NULL) 01770 { 01771 p_desc->vpage_id = vpage_id; 01772 // Don't invoke record_id_new() again. 01773 p_desc->record_id = p_cmd->record_header.id; 01774 } 01775 01776 return cmd_queue_process_start(); 01777 } 01778 01779 01780 ret_code_t fds_reserve(fds_write_token_t * const p_tok, uint16_t length_words) 01781 { 01782 uint16_t vpage_id; 01783 01784 if (!flag_is_set(FDS_FLAG_INITIALIZED)) 01785 { 01786 return NRF_ERROR_INVALID_STATE; 01787 } 01788 01789 if (p_tok == NULL) 01790 { 01791 return NRF_ERROR_NULL; 01792 } 01793 01794 // Reserve space on the page. write_space_reserve() accounts for the header. 01795 if (write_space_reserve(length_words, &vpage_id) == NRF_SUCCESS) 01796 { 01797 p_tok->vpage_id = vpage_id; 01798 p_tok->length_words = length_words; 01799 01800 return NRF_SUCCESS; 01801 } 01802 01803 return NRF_ERROR_NO_MEM; 01804 } 01805 01806 01807 ret_code_t fds_reserve_cancel(fds_write_token_t * const p_tok) 01808 { 01809 fds_page_t * p_page; 01810 01811 if (!flag_is_set(FDS_FLAG_INITIALIZED)) 01812 { 01813 return NRF_ERROR_INVALID_STATE; 01814 } 01815 01816 if (p_tok == NULL) 01817 { 01818 return NRF_ERROR_NULL; 01819 } 01820 01821 if (page_from_virtual_id(p_tok->vpage_id, &p_page) != NRF_SUCCESS) 01822 { 01823 // Could not find the virtual page. This shouldn't happen. 01824 return NRF_ERROR_INVALID_DATA; 01825 } 01826 01827 if ((p_page->words_reserved - p_tok->length_words) < 0) 01828 { 01829 /** We are trying to cancel a reservation for more words than how many are 01830 * currently reserved on the page. This is shouldn't happen. */ 01831 return NRF_ERROR_INVALID_DATA; 01832 } 01833 01834 // Free the space which had been reserved. 01835 p_page->words_reserved -= p_tok->length_words; 01836 01837 // Clean the token. 01838 p_tok->vpage_id = 0; 01839 p_tok->length_words = 0; 01840 01841 return NRF_SUCCESS; 01842 } 01843 01844 01845 ret_code_t fds_write(fds_record_desc_t * const p_desc, 01846 fds_record_key_t key, 01847 uint8_t num_chunks, 01848 fds_record_chunk_t chunks[]) 01849 { 01850 ret_code_t ret; 01851 atomic_counter_inc(); 01852 ret = write_enqueue(p_desc, key, num_chunks, chunks, NULL, false /*not an update*/); 01853 atomic_counter_dec(); 01854 return ret; 01855 } 01856 01857 01858 ret_code_t fds_write_reserved(fds_write_token_t const * const p_tok, 01859 fds_record_desc_t * const p_desc, 01860 fds_record_key_t key, 01861 uint8_t num_chunks, 01862 fds_record_chunk_t chunks[]) 01863 { 01864 ret_code_t ret; 01865 atomic_counter_inc(); 01866 ret = write_enqueue(p_desc, key, num_chunks, chunks, p_tok, false /*not an update*/); 01867 atomic_counter_dec(); 01868 return ret; 01869 } 01870 01871 01872 static ret_code_t clear_enqueue(fds_record_desc_t * const p_desc) 01873 { 01874 ret_code_t ret; 01875 fds_cmd_t * p_cmd; 01876 01877 if (!flag_is_set(FDS_FLAG_INITIALIZED)) 01878 { 01879 return NRF_ERROR_INVALID_STATE; 01880 } 01881 01882 if (p_desc == NULL) 01883 { 01884 return NRF_ERROR_NULL; 01885 } 01886 01887 ret = queue_reserve(FDS_CMD_QUEUE_SIZE_CLEAR, 0, &p_cmd, NULL); 01888 01889 if (ret != NRF_SUCCESS) 01890 { 01891 return ret; 01892 } 01893 01894 // Initialize the command. 01895 p_cmd->id = FDS_CMD_CLEAR; 01896 p_cmd->op_code = FDS_OP_CLEAR_TL; 01897 01898 p_cmd->record_header.id = p_desc->record_id; 01899 p_cmd->vpage_id = p_desc->vpage_id; 01900 01901 return cmd_queue_process_start(); 01902 } 01903 01904 01905 ret_code_t fds_clear(fds_record_desc_t * const p_desc) 01906 { 01907 ret_code_t ret; 01908 atomic_counter_inc(); 01909 ret = clear_enqueue(p_desc); 01910 atomic_counter_dec(); 01911 return ret; 01912 } 01913 01914 01915 static ret_code_t clear_by_instance_enqueue(fds_instance_id_t instance) 01916 { 01917 ret_code_t ret; 01918 fds_cmd_t * p_cmd; 01919 01920 if (!flag_is_set(FDS_FLAG_INITIALIZED)) 01921 { 01922 return NRF_ERROR_INVALID_STATE; 01923 } 01924 01925 ret = queue_reserve(FDS_CMD_QUEUE_SIZE_CLEAR, 0, &p_cmd, NULL); 01926 01927 if (ret != NRF_SUCCESS) 01928 { 01929 return ret; 01930 } 01931 01932 p_cmd->id = FDS_CMD_CLEAR_INST; 01933 p_cmd->op_code = FDS_OP_CLEAR_INSTANCE; 01934 01935 p_cmd->record_header.ic.instance = instance; 01936 01937 return cmd_queue_process_start(); 01938 } 01939 01940 ret_code_t fds_clear_by_instance(fds_instance_id_t instance) 01941 { 01942 ret_code_t ret; 01943 atomic_counter_inc(); 01944 ret = clear_by_instance_enqueue(instance); 01945 atomic_counter_dec(); 01946 return ret; 01947 } 01948 01949 01950 ret_code_t fds_update(fds_record_desc_t * const p_desc, 01951 fds_record_key_t key, 01952 uint8_t num_chunks, 01953 fds_record_chunk_t chunks[]) 01954 { 01955 ret_code_t ret; 01956 atomic_counter_inc(); 01957 ret = write_enqueue(p_desc, key, num_chunks, chunks, NULL, true /*update*/); 01958 atomic_counter_dec(); 01959 return ret; 01960 } 01961 01962 01963 static ret_code_t gc_enqueue() 01964 { 01965 ret_code_t ret; 01966 fds_cmd_t * p_cmd; 01967 01968 if (!flag_is_set(FDS_FLAG_INITIALIZED)) 01969 { 01970 return NRF_ERROR_INVALID_STATE; 01971 } 01972 01973 ret = queue_reserve(FDS_CMD_QUEUE_SIZE_GC, 0, &p_cmd, NULL); 01974 if (ret != NRF_SUCCESS) 01975 { 01976 return ret; 01977 } 01978 01979 p_cmd->id = FDS_CMD_GC; 01980 01981 // Set compression parameters. 01982 m_gc.state = BEGIN; 01983 01984 return cmd_queue_process_start(); 01985 } 01986 01987 01988 ret_code_t fds_gc() 01989 { 01990 ret_code_t ret; 01991 atomic_counter_inc(); 01992 ret = gc_enqueue(); 01993 atomic_counter_dec(); 01994 return ret; 01995 } 01996 01997 01998 ret_code_t fds_find(fds_type_id_t type, 01999 fds_instance_id_t instance, 02000 fds_record_desc_t * const p_desc, 02001 fds_find_token_t * const p_token) 02002 { 02003 if (p_desc == NULL || p_token == NULL) 02004 { 02005 return NRF_ERROR_NULL; 02006 } 02007 02008 return find_record(&type, &instance, p_desc, p_token); 02009 } 02010 02011 02012 ret_code_t fds_find_by_type(fds_type_id_t type, 02013 fds_record_desc_t * const p_desc, 02014 fds_find_token_t * const p_token) 02015 { 02016 if (p_desc == NULL || p_token == NULL) 02017 { 02018 return NRF_ERROR_NULL; 02019 } 02020 02021 return find_record(&type, NULL, p_desc, p_token); 02022 } 02023 02024 02025 ret_code_t fds_find_by_instance(fds_instance_id_t instance, 02026 fds_record_desc_t * const p_desc, 02027 fds_find_token_t * const p_token) 02028 { 02029 if (p_desc == NULL || p_token == NULL) 02030 { 02031 return NRF_ERROR_NULL; 02032 } 02033 02034 return find_record(NULL, &instance, p_desc, p_token); 02035 } 02036 02037 02038 ret_code_t fds_register(fds_cb_t cb) 02039 { 02040 if (m_users == FDS_MAX_USERS) 02041 { 02042 return NRF_ERROR_NO_MEM; 02043 } 02044 02045 m_cb_table[m_users] = cb; 02046 m_users++; 02047 02048 return NRF_SUCCESS; 02049 } 02050 02051 02052 bool fds_descriptor_match(fds_record_desc_t const * const p_desc1, 02053 fds_record_desc_t const * const p_desc2) 02054 { 02055 if ((p_desc1 == NULL) || (p_desc2 == NULL)) 02056 { 02057 return false; 02058 } 02059 02060 return (p_desc1->record_id == p_desc2->record_id); 02061 } 02062 02063 02064 ret_code_t fds_descriptor_from_rec_id(fds_record_desc_t * const p_desc, 02065 fds_record_id_t record_id) 02066 { 02067 if (p_desc == NULL) 02068 { 02069 return NRF_ERROR_NULL; 02070 } 02071 02072 p_desc->record_id = record_id; 02073 p_desc->vpage_id = FDS_VPAGE_ID_UNKNOWN; 02074 02075 return NRF_SUCCESS; 02076 } 02077 02078 ret_code_t fds_record_id_from_desc(fds_record_desc_t const * const p_desc, 02079 fds_record_id_t * const p_record_id) 02080 { 02081 if (p_desc == NULL || p_record_id == NULL) 02082 { 02083 return NRF_ERROR_NULL; 02084 } 02085 02086 *p_record_id = p_desc->record_id; 02087 02088 return NRF_SUCCESS; 02089 } 02090
Generated on Tue Jul 12 2022 18:07:42 by
