Nordic nrf51 sdk sources. Mirrored from https://github.com/ARMmbed/nrf51-sdk.

Dependents:   nRF51822 nRF51822

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers fds.c Source File

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 }