BA / Mbed OS BaBoRo1
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers nvstore.cpp Source File

nvstore.cpp

00001 /*
00002  * Copyright (c) 2018 ARM Limited. All rights reserved.
00003  * SPDX-License-Identifier: Apache-2.0
00004  * Licensed under the Apache License, Version 2.0 (the License); you may
00005  * not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  * http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
00012  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 // ----------------------------------------------------------- Includes -----------------------------------------------------------
00018 
00019 #include "nvstore.h"
00020 
00021 #if NVSTORE_ENABLED
00022 
00023 #include "FlashIAP.h"
00024 #include "mbed_critical.h"
00025 #include "mbed_assert.h"
00026 #include "Thread.h"
00027 #include "mbed_wait_api.h"
00028 #include <algorithm>
00029 #include <string.h>
00030 #include <stdio.h>
00031 
00032 // --------------------------------------------------------- Definitions ----------------------------------------------------------
00033 
00034 static const uint16_t delete_item_flag = 0x8000;
00035 static const uint16_t set_once_flag    = 0x4000;
00036 static const uint16_t header_flag_mask = 0xF000;
00037 
00038 static const uint16_t master_record_key = 0xFFE;
00039 static const uint16_t no_key            = 0xFFF;
00040 static const uint16_t last_reserved_key = master_record_key;
00041 
00042 typedef struct
00043 {
00044     uint16_t key_and_flags;
00045     uint16_t size;
00046     uint32_t crc;
00047 } nvstore_record_header_t;
00048 
00049 static const uint32_t offs_by_key_area_mask     = 0x80000000UL;
00050 static const uint32_t offs_by_key_set_once_mask = 0x40000000UL;
00051 static const uint32_t offs_by_key_flag_mask     = 0xC0000000UL;
00052 static const unsigned int offs_by_key_area_bit_pos     = 31;
00053 static const unsigned int offs_by_key_set_once_bit_pos = 30;
00054 
00055 typedef struct {
00056     uint16_t version;
00057     uint16_t reserved1;
00058     uint32_t reserved2;
00059 } master_record_data_t;
00060 
00061 static const uint32_t min_area_size = 4096;
00062 
00063 static const int num_write_retries = 16;
00064 
00065 static const uint8_t blank_flash_val = 0xFF;
00066 
00067 // See whether any of these defines are given (by config files)
00068 // If so, this means that that area configuration is given by the user
00069 #if defined(NVSTORE_AREA_1_ADDRESS) || defined(NVSTORE_AREA_1_SIZE) ||\
00070     defined(NVSTORE_AREA_2_ADDRESS) || defined(NVSTORE_AREA_2_SIZE)
00071 
00072 // Require all area configuration parameters if any one of them is present
00073 #if !defined(NVSTORE_AREA_1_ADDRESS) || !defined(NVSTORE_AREA_1_SIZE) ||\
00074     !defined(NVSTORE_AREA_2_ADDRESS) || !defined(NVSTORE_AREA_2_SIZE)
00075 #error Incomplete NVStore area configuration
00076 #endif
00077 #if (NVSTORE_AREA_1_SIZE == 0) || (NVSTORE_AREA_2_SIZE == 0)
00078 #error NVStore area size cannot be 0
00079 #endif
00080 
00081 NVStore::nvstore_area_data_t NVStore::initial_area_params[] = {{NVSTORE_AREA_1_ADDRESS, NVSTORE_AREA_1_SIZE},
00082                                                                {NVSTORE_AREA_2_ADDRESS, NVSTORE_AREA_2_SIZE}};
00083 #else
00084 NVStore::nvstore_area_data_t NVStore::initial_area_params[] = {{0, 0},
00085                                                                {0, 0}};
00086 #endif
00087 
00088 typedef enum {
00089     NVSTORE_AREA_STATE_NONE = 0,
00090     NVSTORE_AREA_STATE_EMPTY,
00091     NVSTORE_AREA_STATE_VALID,
00092 } area_state_e;
00093 
00094 static const uint32_t initial_crc = 0xFFFFFFFF;
00095 
00096 
00097 // -------------------------------------------------- Local Functions Declaration ----------------------------------------------------
00098 
00099 // -------------------------------------------------- Functions Implementation ----------------------------------------------------
00100 
00101 // Align a value to a specified size.
00102 // Parameters :
00103 // val           - [IN]   Value.
00104 // size          - [IN]   Size.
00105 // Return        : Aligned value.
00106 static inline uint32_t align_up(uint32_t val, uint32_t size)
00107 {
00108     return (((val - 1) / size) + 1) * size;
00109 }
00110 
00111 // CRC32 calculation. Supports "rolling" calculation (using the initial value).
00112 // Parameters :
00113 // init_crc      - [IN]   Initial CRC.
00114 // data_size      - [IN]   Buffer's data size.
00115 // data_buf      - [IN]   Data buffer.
00116 // Return        : CRC.
00117 static uint32_t crc32(uint32_t init_crc, uint32_t data_size, uint8_t *data_buf)
00118 {
00119     uint32_t i, j;
00120     uint32_t crc, mask;
00121 
00122     crc = init_crc;
00123     for (i = 0; i < data_size; i++) {
00124         crc = crc ^ (uint32_t) (data_buf[i]);
00125         for (j = 0; j < 8; j++) {
00126           mask = -(crc & 1);
00127           crc = (crc >> 1) ^ (0xEDB88320 & mask);
00128         }
00129     }
00130     return crc;
00131 }
00132 
00133 NVStore::NVStore() : _init_done(0), _init_attempts(0), _active_area(0), _max_keys(NVSTORE_MAX_KEYS),
00134       _active_area_version(0), _free_space_offset(0), _size(0), _mutex(0), _offset_by_key(0), _flash(0),
00135       _min_prog_size(0), _page_buf(0)
00136 {
00137     memcpy(_flash_area_params, 0, sizeof(_flash_area_params));
00138 }
00139 
00140 NVStore::~NVStore()
00141 {
00142     if (_init_done) {
00143         deinit();
00144     }
00145 }
00146 
00147 uint16_t NVStore::get_max_keys() const
00148 {
00149     return _max_keys;
00150 }
00151 
00152 uint16_t NVStore::get_max_possible_keys()
00153 {
00154     if (!_init_done) {
00155         init();
00156     }
00157 
00158     size_t max_possible_keys = _size / align_up(sizeof(nvstore_record_header_t) * 2, _min_prog_size) - 1;
00159 
00160     return (uint16_t)std::min(max_possible_keys, (size_t) last_reserved_key);
00161 }
00162 
00163 void NVStore::set_max_keys(uint16_t num_keys)
00164 {
00165     MBED_ASSERT(num_keys < get_max_possible_keys());
00166     _max_keys = num_keys;
00167     // User is allowed to change number of keys. As this affects init, need to deinitialize now.
00168     // Don't call init right away - it is lazily called by get/set functions if needed.
00169     deinit();
00170 }
00171 
00172 int NVStore::flash_read_area(uint8_t area, uint32_t offset, uint32_t size, void *buf)
00173 {
00174     return _flash->read(buf, _flash_area_params[area].address + offset, size);
00175 }
00176 
00177 int NVStore::flash_write_area(uint8_t area, uint32_t offset, uint32_t size, const void *buf)
00178 {
00179     int ret;
00180     // On some boards, write action can fail due to HW limitations (like critical drivers
00181     // that disable all other actions). Just retry a few times until success.
00182     for (int i = 0; i < num_write_retries; i++) {
00183         ret = _flash->program(buf, _flash_area_params[area].address + offset, size);
00184         if (!ret) {
00185             return ret;
00186         }
00187         wait_ms(1);
00188     }
00189     return ret;
00190 }
00191 
00192 int NVStore::flash_erase_area(uint8_t area)
00193 {
00194     int ret;
00195     // On some boards, write action can fail due to HW limitations (like critical drivers
00196     // that disable all other actions). Just retry a few times until success.
00197     for (int i = 0; i < num_write_retries; i++) {
00198         ret = _flash->erase(_flash_area_params[area].address, _flash_area_params[area].size);
00199         if (!ret) {
00200             return ret;
00201         }
00202         wait_ms(1);
00203     }
00204     return ret;
00205 }
00206 
00207 void NVStore::calc_validate_area_params()
00208 {
00209     int num_sectors = 0;
00210 
00211     size_t flash_addr = _flash->get_flash_start();
00212     size_t flash_size = _flash->get_flash_size();
00213     size_t sector_size;
00214     int max_sectors = flash_size / _flash->get_sector_size(flash_addr) + 1;
00215     size_t *sector_map = new size_t[max_sectors];
00216 
00217     int area = 0;
00218     size_t left_size = flash_size;
00219 
00220     memcpy(_flash_area_params, initial_area_params, sizeof(_flash_area_params));
00221     int user_config = (_flash_area_params[0].size != 0);
00222     int in_area = 0;
00223     size_t area_size = 0;
00224 
00225     while (left_size) {
00226         sector_size = _flash->get_sector_size(flash_addr);
00227         sector_map[num_sectors++] = flash_addr;
00228 
00229         if (user_config) {
00230             // User configuration - here we validate it
00231             // Check that address is on a sector boundary, that size covers complete sector sizes,
00232             // and that areas don't overlap.
00233             if (_flash_area_params[area].address == flash_addr) {
00234                 in_area = 1;
00235             }
00236             if (in_area) {
00237                 area_size += sector_size;
00238                 if (area_size == _flash_area_params[area].size) {
00239                     area++;
00240                     if (area == NVSTORE_NUM_AREAS) {
00241                         break;
00242                     }
00243                     in_area = 0;
00244                     area_size = 0;
00245                 }
00246             }
00247         }
00248 
00249         flash_addr += sector_size;
00250         left_size -= sector_size;
00251     }
00252     sector_map[num_sectors] = flash_addr;
00253 
00254     if (user_config) {
00255         // Valid areas were counted. Assert if not the expected number.
00256         MBED_ASSERT(area == NVSTORE_NUM_AREAS);
00257     } else {
00258         // Not user configuration - calculate area parameters.
00259         // Take last two sectors by default. If their sizes aren't big enough, take
00260         // a few consecutive ones.
00261         area = 1;
00262         _flash_area_params[area].size = 0;
00263         int i;
00264         for (i = num_sectors - 1; i >= 0; i--) {
00265             sector_size = sector_map[i+1] - sector_map[i];
00266             _flash_area_params[area].size += sector_size;
00267             if (_flash_area_params[area].size >= min_area_size) {
00268                 _flash_area_params[area].address = sector_map[i];
00269                 area--;
00270                 if (area < 0) {
00271                     break;
00272                 }
00273                 _flash_area_params[area].size = 0;
00274             }
00275         }
00276     }
00277 
00278     delete[] sector_map;
00279 }
00280 
00281 
00282 int NVStore::calc_empty_space(uint8_t area, uint32_t &offset)
00283 {
00284     uint32_t buf[32];
00285     uint8_t *chbuf;
00286     uint32_t i, j;
00287     int ret;
00288 
00289     offset = _size;
00290     for (i = 0; i < _size / sizeof(buf); i++) {
00291         offset -= sizeof(buf);
00292         ret = flash_read_area(area, offset, sizeof(buf), buf);
00293         if (ret) {
00294             return ret;
00295         }
00296         chbuf = (uint8_t *) buf;
00297         for (j = sizeof(buf); j > 0; j--) {
00298             if (chbuf[j - 1] != blank_flash_val) {
00299                 offset += j;
00300                 return 0;
00301             }
00302         }
00303     }
00304     return 0;
00305 }
00306 
00307 int NVStore::read_record(uint8_t area, uint32_t offset, uint16_t buf_size, void *buf,
00308                          uint16_t &actual_size, int validate_only, int &valid,
00309                          uint16_t &key, uint16_t &flags, uint32_t &next_offset)
00310 {
00311     uint8_t int_buf[128];
00312     void *buf_ptr;
00313     uint16_t data_size, chunk_size;
00314     int os_ret;
00315     nvstore_record_header_t header;
00316     uint32_t crc = initial_crc;
00317 
00318     valid = 1;
00319 
00320     os_ret = flash_read_area(area, offset, sizeof(header), &header);
00321     if (os_ret) {
00322         return NVSTORE_READ_ERROR;
00323     }
00324 
00325     crc = crc32(crc, sizeof(header) - sizeof(header.crc), (uint8_t *) &header);
00326 
00327     actual_size = 0;
00328     key   = header.key_and_flags & ~header_flag_mask;
00329     flags = header.key_and_flags & header_flag_mask;
00330 
00331     if ((key >= _max_keys) && (key != master_record_key)) {
00332         valid = 0;
00333         return NVSTORE_SUCCESS;
00334     }
00335 
00336     data_size = header.size;
00337     offset += sizeof(header);
00338 
00339     // In case of validate only enabled, we use our internal buffer for data reading,
00340     // instead of the user one. This allows us to use a smaller buffer, on which CRC
00341     // is continuously calculated.
00342     if (validate_only) {
00343         buf_ptr = int_buf;
00344         buf_size = sizeof(int_buf);
00345     } else {
00346         if (data_size > buf_size) {
00347             offset += data_size;
00348             actual_size = data_size;
00349             next_offset = align_up(offset, _min_prog_size);
00350             return NVSTORE_BUFF_TOO_SMALL;
00351         }
00352         buf_ptr = buf;
00353     }
00354 
00355     while (data_size) {
00356         chunk_size = std::min(data_size, buf_size);
00357         os_ret = flash_read_area(area, offset, chunk_size, buf_ptr);
00358         if (os_ret) {
00359             return NVSTORE_READ_ERROR;
00360         }
00361         crc = crc32(crc, chunk_size, (uint8_t *) buf_ptr);
00362         data_size -= chunk_size;
00363         offset += chunk_size;
00364     }
00365 
00366     if (header.crc != crc) {
00367         valid = 0;
00368         return NVSTORE_SUCCESS;
00369     }
00370 
00371     actual_size = header.size;
00372     next_offset = align_up(offset, _min_prog_size);
00373 
00374     return NVSTORE_SUCCESS;
00375 }
00376 
00377 int NVStore::write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t flags,
00378                           uint32_t data_size, const void *data_buf, uint32_t &next_offset)
00379 {
00380     nvstore_record_header_t header;
00381     uint32_t crc = initial_crc;
00382     int os_ret;
00383     uint8_t *prog_buf;
00384 
00385     header.key_and_flags = key | flags;
00386     header.size = data_size;
00387     header.crc = 0; // Satisfy compiler
00388     crc = crc32(crc, sizeof(header) - sizeof(header.crc), (uint8_t *) &header);
00389     if (data_size) {
00390         crc = crc32(crc, data_size, (uint8_t *) data_buf);
00391     }
00392     header.crc = crc;
00393 
00394     // In case page size is greater than header size, we can't write header and data
00395     // separately. Instead, we need to copy header and start of data to our page buffer
00396     // and write them together. Otherwise, simply write header and data separately.
00397     uint32_t prog_size = sizeof(header);
00398     uint32_t copy_size = 0;
00399     if (_min_prog_size > sizeof(header)) {
00400         prog_buf = _page_buf;
00401         memcpy(prog_buf, &header, sizeof(header));
00402         if (data_size) {
00403             memcpy(prog_buf, &header, sizeof(header));
00404             copy_size = std::min(data_size, _min_prog_size - sizeof(header));
00405             memcpy(prog_buf + sizeof(header), data_buf, copy_size);
00406             data_size -= copy_size;
00407             prog_size += copy_size;
00408         }
00409     } else {
00410         prog_buf = (uint8_t *) &header;
00411     }
00412 
00413     os_ret = flash_write_area(area, offset, prog_size, prog_buf);
00414     if (os_ret) {
00415         return NVSTORE_WRITE_ERROR;
00416     }
00417     offset += prog_size;
00418 
00419     if (data_size) {
00420         prog_buf = (uint8_t *) data_buf + copy_size;
00421         os_ret = flash_write_area(area, offset, data_size, prog_buf);
00422         if (os_ret) {
00423             return NVSTORE_WRITE_ERROR;
00424         }
00425         offset += data_size;
00426     }
00427 
00428     next_offset = align_up(offset, _min_prog_size);
00429     return NVSTORE_SUCCESS;
00430 }
00431 
00432 int NVStore::write_master_record(uint8_t area, uint16_t version, uint32_t &next_offset)
00433 {
00434     master_record_data_t master_rec;
00435 
00436     master_rec.version = version;
00437     master_rec.reserved1 = 0;
00438     master_rec.reserved2 = 0;
00439     return write_record(area, 0, master_record_key, 0, sizeof(master_rec),
00440                         &master_rec, next_offset);
00441 }
00442 
00443 int NVStore::copy_record(uint8_t from_area, uint32_t from_offset, uint32_t to_offset,
00444                          uint32_t &next_offset)
00445 {
00446     uint8_t local_buf[128];
00447     uint16_t record_size, chunk_size, prog_buf_size;
00448     int os_ret;
00449     nvstore_record_header_t *header;
00450     uint8_t *read_buf, *prog_buf;
00451 
00452     // This function assumes that the source record is valid, so no need to recalculate CRC.
00453 
00454     if (_min_prog_size > sizeof(nvstore_record_header_t)) {
00455         prog_buf = _page_buf;
00456         prog_buf_size = _min_prog_size;
00457     } else {
00458         prog_buf = local_buf;
00459         prog_buf_size = sizeof(local_buf);
00460     }
00461     read_buf = prog_buf;
00462 
00463     os_ret = flash_read_area(from_area, from_offset, sizeof(nvstore_record_header_t), read_buf);
00464     if (os_ret) {
00465         return NVSTORE_READ_ERROR;
00466     }
00467 
00468     header = (nvstore_record_header_t *) read_buf;
00469     record_size = sizeof(nvstore_record_header_t) + header->size;
00470 
00471     // No need to copy records whose flags indicate deletion
00472     if (header->key_and_flags & delete_item_flag) {
00473         next_offset = align_up(to_offset, _min_prog_size);
00474         return NVSTORE_SUCCESS;
00475     }
00476 
00477     // no need to align record size here, as it won't change the outcome of this condition
00478     if (to_offset + record_size >= _size) {
00479         return NVSTORE_FLASH_AREA_TOO_SMALL;
00480     }
00481 
00482     uint16_t start_size = sizeof(nvstore_record_header_t);
00483     from_offset += start_size;
00484     read_buf += start_size;
00485     record_size -= start_size;
00486 
00487     do {
00488         chunk_size = std::min(record_size, (uint16_t)(prog_buf_size - start_size));
00489         if (chunk_size) {
00490             os_ret = flash_read_area(from_area, from_offset, chunk_size, read_buf);
00491             if (os_ret) {
00492                 return NVSTORE_READ_ERROR;
00493             }
00494         }
00495         os_ret = flash_write_area(1 - from_area, to_offset, chunk_size + start_size, prog_buf);
00496         if (os_ret) {
00497             return NVSTORE_WRITE_ERROR;
00498         }
00499 
00500         read_buf = prog_buf;
00501         record_size -= chunk_size;
00502         from_offset += chunk_size;
00503         to_offset += chunk_size + start_size;
00504         start_size = 0;
00505     } while (record_size);
00506 
00507     next_offset = align_up(to_offset, _min_prog_size);
00508     return NVSTORE_SUCCESS;
00509 }
00510 
00511 int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint16_t buf_size, const void *buf)
00512 {
00513     uint32_t curr_offset, new_area_offset, next_offset;
00514     int ret;
00515     uint8_t curr_area;
00516 
00517     new_area_offset = align_up(sizeof(nvstore_record_header_t) + sizeof(master_record_data_t), _min_prog_size);
00518 
00519     // If GC is triggered by a set item request, we need to first write that item in the new location,
00520     // otherwise we may either write it twice (if already included), or lose it in case we decide
00521     // to skip it at garbage collection phase (and the system crashes).
00522     if ((key != no_key) && !(flags & delete_item_flag)) {
00523         ret = write_record(1 - _active_area, new_area_offset, key, 0, buf_size, buf, next_offset);
00524         if (ret != NVSTORE_SUCCESS) {
00525             return ret;
00526         }
00527         _offset_by_key[key] = new_area_offset | (1 - _active_area) << offs_by_key_area_bit_pos |
00528                                 (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos);
00529         new_area_offset = next_offset;
00530     }
00531 
00532     // Now iterate on all types, and copy the ones who have valid offsets (meaning that they exist)
00533     // to the other area.
00534     for (key = 0; key < _max_keys; key++) {
00535         curr_offset = _offset_by_key[key];
00536         uint16_t save_flags = curr_offset & offs_by_key_area_mask;
00537         curr_area = (uint8_t)(curr_offset >> offs_by_key_area_bit_pos) & 1;
00538         curr_offset &= ~offs_by_key_flag_mask;
00539         if ((!curr_offset) || (curr_area != _active_area)) {
00540             continue;
00541         }
00542         ret = copy_record(curr_area, curr_offset, new_area_offset, next_offset);
00543         if (ret != NVSTORE_SUCCESS) {
00544             return ret;
00545         }
00546         _offset_by_key[key] = new_area_offset | (1 - curr_area) << offs_by_key_area_bit_pos | save_flags;
00547         new_area_offset = next_offset;
00548     }
00549 
00550     // Now write master record, with version incremented by 1.
00551     _active_area_version++;
00552     ret = write_master_record(1 - _active_area, _active_area_version, next_offset);
00553     if (ret != NVSTORE_SUCCESS) {
00554         return ret;
00555     }
00556 
00557     _free_space_offset = new_area_offset;
00558 
00559     // Only now we can switch to the new active area
00560     _active_area = 1 - _active_area;
00561 
00562     // The older area doesn't concern us now. Erase it now.
00563     if (flash_erase_area(1 - _active_area)) {
00564         return NVSTORE_WRITE_ERROR;
00565     }
00566 
00567     return ret;
00568 }
00569 
00570 
00571 int NVStore::do_get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual_size,
00572                     int validate_only)
00573 {
00574     int ret = NVSTORE_SUCCESS;
00575     int valid;
00576     uint32_t record_offset, next_offset;
00577     uint16_t read_type, flags;
00578     uint8_t area;
00579 
00580     if (!_init_done) {
00581         ret = init();
00582         if (ret != NVSTORE_SUCCESS) {
00583             return ret;
00584         }
00585     }
00586 
00587     if (key >= _max_keys) {
00588         return NVSTORE_BAD_VALUE;
00589     }
00590 
00591     if (!buf) {
00592         buf_size = 0;
00593         // This is only required in order to satisfy static code analysis tools, fearing
00594         // that a null buff is dereferenced inside read_record function. However, this won't happen
00595         // when buf_size is 0, so just have buf point to a dummy location.
00596         buf = &flags;
00597     }
00598 
00599     _mutex->lock();
00600     record_offset = _offset_by_key[key];
00601 
00602     if (!record_offset) {
00603         _mutex->unlock();
00604         return NVSTORE_NOT_FOUND;
00605     }
00606 
00607     area = (uint8_t)(record_offset >> offs_by_key_area_bit_pos) & 1;
00608     record_offset &= ~offs_by_key_flag_mask;
00609 
00610     ret = read_record(area, record_offset, buf_size, buf,
00611                       actual_size, validate_only, valid,
00612                       read_type, flags, next_offset);
00613     if ((ret == NVSTORE_SUCCESS) && !valid) {
00614         ret = NVSTORE_DATA_CORRUPT;
00615     }
00616 
00617     _mutex->unlock();
00618     return ret;
00619 }
00620 
00621 int NVStore::get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual_size)
00622 {
00623     return do_get(key, buf_size, buf, actual_size, 0);
00624 }
00625 
00626 int NVStore::get_item_size(uint16_t key, uint16_t &actual_size)
00627 {
00628     return do_get(key, 0, NULL, actual_size, 1);
00629 }
00630 
00631 int NVStore::do_set(uint16_t &key, uint16_t buf_size, const void *buf, uint16_t flags)
00632 {
00633     int ret = NVSTORE_SUCCESS;
00634     uint32_t record_offset, record_size, new_free_space;
00635     uint32_t next_offset;
00636 
00637     if (!_init_done) {
00638         ret = init();
00639         if (ret != NVSTORE_SUCCESS) {
00640             return ret;
00641         }
00642     }
00643 
00644     if ((key != no_key) && (key >= _max_keys)) {
00645         return NVSTORE_BAD_VALUE;
00646     }
00647 
00648     if ((key == no_key) && (flags & delete_item_flag)) {
00649         return NVSTORE_BAD_VALUE;
00650     }
00651 
00652     if (!buf) {
00653         buf_size = 0;
00654     }
00655 
00656     if ((flags & delete_item_flag) && !_offset_by_key[key]) {
00657         return NVSTORE_NOT_FOUND;
00658     }
00659 
00660     if ((key != no_key) && (_offset_by_key[key] & offs_by_key_set_once_mask)) {
00661         return NVSTORE_ALREADY_EXISTS;
00662     }
00663 
00664     record_size = align_up(sizeof(nvstore_record_header_t) + buf_size, _min_prog_size);
00665 
00666     _mutex->lock();
00667 
00668     if (key == no_key) {
00669         for (key = NVSTORE_NUM_PREDEFINED_KEYS; key < _max_keys; key++) {
00670             if (!_offset_by_key[key]) {
00671                 break;
00672             }
00673         }
00674         if (key == _max_keys) {
00675             return NVSTORE_NO_FREE_KEY;
00676         }
00677     }
00678 
00679     new_free_space = core_util_atomic_incr_u32(&_free_space_offset, record_size);
00680     record_offset = new_free_space - record_size;
00681 
00682     // If we cross the area limit, we need to invoke GC.
00683     if (new_free_space >= _size) {
00684         ret = garbage_collection(key, flags, buf_size, buf);
00685         _mutex->unlock();
00686         return ret;
00687     }
00688 
00689     // Now write the record
00690     ret = write_record(_active_area, record_offset, key, flags, buf_size, buf, next_offset);
00691     if (ret != NVSTORE_SUCCESS) {
00692         _mutex->unlock();
00693         return ret;
00694     }
00695 
00696     // Update _offset_by_key. High bit indicates area.
00697     if (flags & delete_item_flag) {
00698         _offset_by_key[key] = 0;
00699     } else {
00700         _offset_by_key[key] = record_offset | (_active_area << offs_by_key_area_bit_pos) |
00701                               (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos);
00702     }
00703 
00704     _mutex->unlock();
00705 
00706     return NVSTORE_SUCCESS;
00707 }
00708 
00709 int NVStore::set(uint16_t key, uint16_t buf_size, const void *buf)
00710 {
00711     return do_set(key, buf_size, buf, 0);
00712 }
00713 
00714 int NVStore::set_once(uint16_t key, uint16_t buf_size, const void *buf)
00715 {
00716     return do_set(key, buf_size, buf, set_once_flag);
00717 }
00718 
00719 int NVStore::set_alloc_key(uint16_t &key, uint16_t buf_size, const void *buf)
00720 {
00721     key = no_key;
00722     return do_set(key, buf_size, buf, 0);
00723 }
00724 
00725 int NVStore::remove(uint16_t key)
00726 {
00727     return do_set(key, 0, NULL, delete_item_flag);
00728 }
00729 
00730 int NVStore::init()
00731 {
00732     area_state_e area_state[NVSTORE_NUM_AREAS];
00733     uint32_t free_space_offset_of_area[NVSTORE_NUM_AREAS];
00734     uint32_t init_attempts_val;
00735     uint32_t next_offset;
00736     int os_ret;
00737     int ret = NVSTORE_SUCCESS;
00738     int valid;
00739     uint16_t key;
00740     uint16_t flags;
00741     uint16_t versions[NVSTORE_NUM_AREAS];
00742     uint16_t actual_size;
00743 
00744     if (_init_done) {
00745         return NVSTORE_SUCCESS;
00746     }
00747 
00748     // This handles the case that init function is called by more than one thread concurrently.
00749     // Only the one who gets the value of 1 in _init_attempts_val will proceed, while others will
00750     // wait until init is finished.
00751     init_attempts_val = core_util_atomic_incr_u32(&_init_attempts, 1);
00752     if (init_attempts_val != 1) {
00753         while (!_init_done) {
00754             wait_ms(1);
00755         }
00756         return NVSTORE_SUCCESS;
00757     }
00758 
00759     _offset_by_key = new uint32_t[_max_keys];
00760     MBED_ASSERT(_offset_by_key);
00761 
00762     for (key = 0; key < _max_keys; key++) {
00763         _offset_by_key[key] = 0;
00764     }
00765 
00766     _mutex = new PlatformMutex;
00767     MBED_ASSERT(_mutex);
00768 
00769     _size = (uint32_t) -1;
00770     _flash = new mbed::FlashIAP;
00771     MBED_ASSERT(_flash);
00772     _flash->init();
00773 
00774     _min_prog_size = std::max(_flash->get_page_size(), (uint32_t)sizeof(nvstore_record_header_t));
00775     if (_min_prog_size > sizeof(nvstore_record_header_t)) {
00776         _page_buf = new uint8_t[_min_prog_size];
00777         MBED_ASSERT(_page_buf);
00778     }
00779 
00780     calc_validate_area_params();
00781 
00782     for (uint8_t area = 0; area < NVSTORE_NUM_AREAS; area++) {
00783         area_state[area] = NVSTORE_AREA_STATE_NONE;
00784         free_space_offset_of_area[area] =  0;
00785         versions[area] = 0;
00786 
00787         _size = std::min(_size, _flash_area_params[area].size);
00788 
00789         // Find start of empty space at the end of the area. This serves for both
00790         // knowing whether the area is empty and for the record traversal at the end.
00791         os_ret = calc_empty_space(area, free_space_offset_of_area[area]);
00792         MBED_ASSERT(!os_ret);
00793 
00794         if (!free_space_offset_of_area[area]) {
00795             area_state[area] = NVSTORE_AREA_STATE_EMPTY;
00796             continue;
00797         }
00798 
00799         // Check validity of master record
00800         master_record_data_t master_rec;
00801         ret = read_record(area, 0, sizeof(master_rec), &master_rec,
00802                           actual_size, 0, valid,
00803                           key, flags, next_offset);
00804         MBED_ASSERT((ret == NVSTORE_SUCCESS) || (ret == NVSTORE_BUFF_TOO_SMALL));
00805         if (ret == NVSTORE_BUFF_TOO_SMALL) {
00806             // Buf too small error means that we have a corrupt master record -
00807             // treat it as such
00808             valid = 0;
00809         }
00810 
00811         // We have a non valid master record, in a non-empty area. Just erase the area.
00812         if ((!valid) || (key != master_record_key)) {
00813             os_ret = flash_erase_area(area);
00814             MBED_ASSERT(!os_ret);
00815             area_state[area] = NVSTORE_AREA_STATE_EMPTY;
00816             continue;
00817         }
00818         versions[area] = master_rec.version;
00819 
00820         // Place _free_space_offset after the master record (for the traversal,
00821         // which takes place after this loop).
00822         _free_space_offset = next_offset;
00823         area_state[area] = NVSTORE_AREA_STATE_VALID;
00824 
00825         // Unless both areas are valid (a case handled later), getting here means
00826         // that we found our active area.
00827         _active_area = area;
00828         _active_area_version = versions[area];
00829     }
00830 
00831     // In case we have two empty areas, arbitrarily assign 0 to the active one.
00832     if ((area_state[0] == NVSTORE_AREA_STATE_EMPTY) && (area_state[1] == NVSTORE_AREA_STATE_EMPTY)) {
00833         _active_area = 0;
00834         ret = write_master_record(_active_area, 1, _free_space_offset);
00835         MBED_ASSERT(ret == NVSTORE_SUCCESS);
00836         _init_done = 1;
00837         return NVSTORE_SUCCESS;
00838     }
00839 
00840     // In case we have two valid areas, choose the one having the higher version (or 0
00841     // in case of wrap around). Erase the other one.
00842     if ((area_state[0] == NVSTORE_AREA_STATE_VALID) && (area_state[1] == NVSTORE_AREA_STATE_VALID)) {
00843         if ((versions[0] > versions[1]) || (!versions[0])) {
00844             _active_area = 0;
00845         } else {
00846             _active_area = 1;
00847         }
00848         _active_area_version = versions[_active_area];
00849         os_ret = flash_erase_area(1 - _active_area);
00850         MBED_ASSERT(!os_ret);
00851     }
00852 
00853     // Traverse area until reaching the empty space at the end or until reaching a faulty record
00854     while (_free_space_offset < free_space_offset_of_area[_active_area]) {
00855         ret = read_record(_active_area, _free_space_offset, 0, NULL,
00856                           actual_size, 1, valid,
00857                           key, flags, next_offset);
00858         MBED_ASSERT(ret == NVSTORE_SUCCESS);
00859 
00860         // In case we have a faulty record, this probably means that the system crashed when written.
00861         // Perform a garbage collection, to make the the other area valid.
00862         if (!valid) {
00863             ret = garbage_collection(no_key, 0, 0, NULL);
00864             break;
00865         }
00866         if (flags & delete_item_flag) {
00867             _offset_by_key[key] = 0;
00868         } else {
00869             _offset_by_key[key] = _free_space_offset | (_active_area << offs_by_key_area_bit_pos) |
00870                                   (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos);
00871         }
00872         _free_space_offset = next_offset;
00873     }
00874 
00875     _init_done = 1;
00876     return NVSTORE_SUCCESS;
00877 }
00878 
00879 int NVStore::deinit()
00880 {
00881     if (_init_done) {
00882         _flash->deinit();
00883         delete _flash;
00884         delete _mutex;
00885         delete[] _offset_by_key;
00886         if (_page_buf) {
00887             delete[] _page_buf;
00888             _page_buf = 0;
00889         }
00890     }
00891 
00892     _init_attempts = 0;
00893     _init_done = 0;
00894 
00895     return NVSTORE_SUCCESS;
00896 }
00897 
00898 int NVStore::reset()
00899 {
00900     uint8_t area;
00901     int os_ret;
00902 
00903     if (!_init_done) {
00904         init();
00905     }
00906 
00907     // Erase both areas, and reinitialize the module. This is totally not thread safe,
00908     // as init doesn't take the case of re-initialization into account. It's OK, as this function
00909     // should only be called in pre-production cases.
00910     for (area = 0; area < NVSTORE_NUM_AREAS; area++) {
00911         os_ret = flash_erase_area(area);
00912         if (os_ret) {
00913             return NVSTORE_WRITE_ERROR;
00914         }
00915     }
00916 
00917     deinit();
00918     return init();
00919 }
00920 
00921 int NVStore::get_area_params(uint8_t area, uint32_t &address, size_t &size)
00922 {
00923     if (area >= NVSTORE_NUM_AREAS) {
00924         return NVSTORE_BAD_VALUE;
00925     }
00926 
00927     if (!_init_done) {
00928         init();
00929     }
00930 
00931     address = _flash_area_params[area].address;
00932     size = _flash_area_params[area].size;
00933 
00934     return NVSTORE_SUCCESS;
00935 }
00936 
00937 size_t NVStore::size()
00938 {
00939     if (!_init_done) {
00940         init();
00941     }
00942 
00943     return _size;
00944 }
00945 
00946 #endif // NVSTORE_ENABLED