Denislam Valeev / Mbed OS Nucleo_rtos_basic
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 }
00138 
00139 NVStore::~NVStore()
00140 {
00141     if (_init_done) {
00142         deinit();
00143     }
00144 }
00145 
00146 uint16_t NVStore::get_max_keys() const
00147 {
00148     return _max_keys;
00149 }
00150 
00151 uint16_t NVStore::get_max_possible_keys()
00152 {
00153     if (!_init_done) {
00154         init();
00155     }
00156 
00157     size_t max_possible_keys = _size / align_up(sizeof(nvstore_record_header_t) * 2, _min_prog_size) - 1;
00158 
00159     return (uint16_t)std::min(max_possible_keys, (size_t) last_reserved_key);
00160 }
00161 
00162 void NVStore::set_max_keys(uint16_t num_keys)
00163 {
00164     MBED_ASSERT(num_keys < get_max_possible_keys());
00165     _max_keys = num_keys;
00166     // User is allowed to change number of keys. As this affects init, need to deinitialize now.
00167     // Don't call init right away - it is lazily called by get/set functions if needed.
00168     deinit();
00169 }
00170 
00171 int NVStore::flash_read_area(uint8_t area, uint32_t offset, uint32_t size, void *buf)
00172 {
00173     return _flash->read(buf, _flash_area_params[area].address + offset, size);
00174 }
00175 
00176 int NVStore::flash_write_area(uint8_t area, uint32_t offset, uint32_t size, const void *buf)
00177 {
00178     int ret;
00179     // On some boards, write action can fail due to HW limitations (like critical drivers
00180     // that disable all other actions). Just retry a few times until success.
00181     for (int i = 0; i < num_write_retries; i++) {
00182         ret = _flash->program(buf, _flash_area_params[area].address + offset, size);
00183         if (!ret) {
00184             return ret;
00185         }
00186         wait_ms(1);
00187     }
00188     return ret;
00189 }
00190 
00191 int NVStore::flash_erase_area(uint8_t area)
00192 {
00193     int ret;
00194     // On some boards, write action can fail due to HW limitations (like critical drivers
00195     // that disable all other actions). Just retry a few times until success.
00196     for (int i = 0; i < num_write_retries; i++) {
00197         ret = _flash->erase(_flash_area_params[area].address, _flash_area_params[area].size);
00198         if (!ret) {
00199             return ret;
00200         }
00201         wait_ms(1);
00202     }
00203     return ret;
00204 }
00205 
00206 void NVStore::calc_validate_area_params()
00207 {
00208     int num_sectors = 0;
00209 
00210     size_t flash_addr = _flash->get_flash_start();
00211     size_t flash_size = _flash->get_flash_size();
00212     size_t sector_size;
00213     int max_sectors = flash_size / _flash->get_sector_size(flash_addr) + 1;
00214     size_t *sector_map = new size_t[max_sectors];
00215 
00216     int area = 0;
00217     size_t left_size = flash_size;
00218 
00219     memcpy(_flash_area_params, initial_area_params, sizeof(_flash_area_params));
00220     int user_config = (_flash_area_params[0].size != 0);
00221     int in_area = 0;
00222     size_t area_size = 0;
00223 
00224     while (left_size) {
00225         sector_size = _flash->get_sector_size(flash_addr);
00226         sector_map[num_sectors++] = flash_addr;
00227 
00228         if (user_config) {
00229             // User configuration - here we validate it
00230             // Check that address is on a sector boundary, that size covers complete sector sizes,
00231             // and that areas don't overlap.
00232             if (_flash_area_params[area].address == flash_addr) {
00233                 in_area = 1;
00234             }
00235             if (in_area) {
00236                 area_size += sector_size;
00237                 if (area_size == _flash_area_params[area].size) {
00238                     area++;
00239                     if (area == NVSTORE_NUM_AREAS) {
00240                         break;
00241                     }
00242                     in_area = 0;
00243                     area_size = 0;
00244                 }
00245             }
00246         }
00247 
00248         flash_addr += sector_size;
00249         left_size -= sector_size;
00250     }
00251     sector_map[num_sectors] = flash_addr;
00252 
00253     if (user_config) {
00254         // Valid areas were counted. Assert if not the expected number.
00255         MBED_ASSERT(area == NVSTORE_NUM_AREAS);
00256     } else {
00257         // Not user configuration - calculate area parameters.
00258         // Take last two sectors by default. If their sizes aren't big enough, take
00259         // a few consecutive ones.
00260         area = 1;
00261         _flash_area_params[area].size = 0;
00262         int i;
00263         for (i = num_sectors - 1; i >= 0; i--) {
00264             sector_size = sector_map[i+1] - sector_map[i];
00265             _flash_area_params[area].size += sector_size;
00266             if (_flash_area_params[area].size >= min_area_size) {
00267                 _flash_area_params[area].address = sector_map[i];
00268                 area--;
00269                 if (area < 0) {
00270                     break;
00271                 }
00272                 _flash_area_params[area].size = 0;
00273             }
00274         }
00275     }
00276 
00277     delete[] sector_map;
00278 }
00279 
00280 
00281 int NVStore::calc_empty_space(uint8_t area, uint32_t &offset)
00282 {
00283     uint32_t buf[32];
00284     uint8_t *chbuf;
00285     uint32_t i, j;
00286     int ret;
00287 
00288     offset = _size;
00289     for (i = 0; i < _size / sizeof(buf); i++) {
00290         offset -= sizeof(buf);
00291         ret = flash_read_area(area, offset, sizeof(buf), buf);
00292         if (ret) {
00293             return ret;
00294         }
00295         chbuf = (uint8_t *) buf;
00296         for (j = sizeof(buf); j > 0; j--) {
00297             if (chbuf[j - 1] != blank_flash_val) {
00298                 offset += j;
00299                 return 0;
00300             }
00301         }
00302     }
00303     return 0;
00304 }
00305 
00306 int NVStore::read_record(uint8_t area, uint32_t offset, uint16_t buf_size, void *buf,
00307                          uint16_t &actual_size, int validate_only, int &valid,
00308                          uint16_t &key, uint16_t &flags, uint32_t &next_offset)
00309 {
00310     uint8_t int_buf[128];
00311     void *buf_ptr;
00312     uint16_t data_size, chunk_size;
00313     int os_ret;
00314     nvstore_record_header_t header;
00315     uint32_t crc = initial_crc;
00316 
00317     valid = 1;
00318 
00319     os_ret = flash_read_area(area, offset, sizeof(header), &header);
00320     if (os_ret) {
00321         return NVSTORE_READ_ERROR;
00322     }
00323 
00324     crc = crc32(crc, sizeof(header) - sizeof(header.crc), (uint8_t *) &header);
00325 
00326     actual_size = 0;
00327     key   = header.key_and_flags & ~header_flag_mask;
00328     flags = header.key_and_flags & header_flag_mask;
00329 
00330     if ((key >= _max_keys) && (key != master_record_key)) {
00331         valid = 0;
00332         return NVSTORE_SUCCESS;
00333     }
00334 
00335     data_size = header.size;
00336     offset += sizeof(header);
00337 
00338     // In case of validate only enabled, we use our internal buffer for data reading,
00339     // instead of the user one. This allows us to use a smaller buffer, on which CRC
00340     // is continuously calculated.
00341     if (validate_only) {
00342         buf_ptr = int_buf;
00343         buf_size = sizeof(int_buf);
00344     } else {
00345         if (data_size > buf_size) {
00346             offset += data_size;
00347             actual_size = data_size;
00348             next_offset = align_up(offset, _min_prog_size);
00349             return NVSTORE_BUFF_TOO_SMALL;
00350         }
00351         buf_ptr = buf;
00352     }
00353 
00354     while (data_size) {
00355         chunk_size = std::min(data_size, buf_size);
00356         os_ret = flash_read_area(area, offset, chunk_size, buf_ptr);
00357         if (os_ret) {
00358             return NVSTORE_READ_ERROR;
00359         }
00360         crc = crc32(crc, chunk_size, (uint8_t *) buf_ptr);
00361         data_size -= chunk_size;
00362         offset += chunk_size;
00363     }
00364 
00365     if (header.crc != crc) {
00366         valid = 0;
00367         return NVSTORE_SUCCESS;
00368     }
00369 
00370     actual_size = header.size;
00371     next_offset = align_up(offset, _min_prog_size);
00372 
00373     return NVSTORE_SUCCESS;
00374 }
00375 
00376 int NVStore::write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t flags,
00377                           uint32_t data_size, const void *data_buf, uint32_t &next_offset)
00378 {
00379     nvstore_record_header_t header;
00380     uint32_t crc = initial_crc;
00381     int os_ret;
00382     uint8_t *prog_buf;
00383 
00384     header.key_and_flags = key | flags;
00385     header.size = data_size;
00386     header.crc = 0; // Satisfy compiler
00387     crc = crc32(crc, sizeof(header) - sizeof(header.crc), (uint8_t *) &header);
00388     if (data_size) {
00389         crc = crc32(crc, data_size, (uint8_t *) data_buf);
00390     }
00391     header.crc = crc;
00392 
00393     // In case page size is greater than header size, we can't write header and data
00394     // separately. Instead, we need to copy header and start of data to our page buffer
00395     // and write them together. Otherwise, simply write header and data separately.
00396     uint32_t prog_size = sizeof(header);
00397     uint32_t copy_size = 0;
00398     if (_min_prog_size > sizeof(header)) {
00399         prog_buf = _page_buf;
00400         memcpy(prog_buf, &header, sizeof(header));
00401         if (data_size) {
00402             memcpy(prog_buf, &header, sizeof(header));
00403             copy_size = std::min(data_size, _min_prog_size - sizeof(header));
00404             memcpy(prog_buf + sizeof(header), data_buf, copy_size);
00405             data_size -= copy_size;
00406             prog_size += copy_size;
00407         }
00408     } else {
00409         prog_buf = (uint8_t *) &header;
00410     }
00411 
00412     os_ret = flash_write_area(area, offset, prog_size, prog_buf);
00413     if (os_ret) {
00414         return NVSTORE_WRITE_ERROR;
00415     }
00416     offset += prog_size;
00417 
00418     if (data_size) {
00419         prog_buf = (uint8_t *) data_buf + copy_size;
00420         os_ret = flash_write_area(area, offset, data_size, prog_buf);
00421         if (os_ret) {
00422             return NVSTORE_WRITE_ERROR;
00423         }
00424         offset += data_size;
00425     }
00426 
00427     next_offset = align_up(offset, _min_prog_size);
00428     return NVSTORE_SUCCESS;
00429 }
00430 
00431 int NVStore::write_master_record(uint8_t area, uint16_t version, uint32_t &next_offset)
00432 {
00433     master_record_data_t master_rec;
00434 
00435     master_rec.version = version;
00436     master_rec.reserved1 = 0;
00437     master_rec.reserved2 = 0;
00438     return write_record(area, 0, master_record_key, 0, sizeof(master_rec),
00439                         &master_rec, next_offset);
00440 }
00441 
00442 int NVStore::copy_record(uint8_t from_area, uint32_t from_offset, uint32_t to_offset,
00443                          uint32_t &next_offset)
00444 {
00445     uint8_t local_buf[128];
00446     uint16_t record_size, chunk_size, prog_buf_size;
00447     int os_ret;
00448     nvstore_record_header_t *header;
00449     uint8_t *read_buf, *prog_buf;
00450 
00451     // This function assumes that the source record is valid, so no need to recalculate CRC.
00452 
00453     if (_min_prog_size > sizeof(nvstore_record_header_t)) {
00454         prog_buf = _page_buf;
00455         prog_buf_size = _min_prog_size;
00456     } else {
00457         prog_buf = local_buf;
00458         prog_buf_size = sizeof(local_buf);
00459     }
00460     read_buf = prog_buf;
00461 
00462     os_ret = flash_read_area(from_area, from_offset, sizeof(nvstore_record_header_t), read_buf);
00463     if (os_ret) {
00464         return NVSTORE_READ_ERROR;
00465     }
00466 
00467     header = (nvstore_record_header_t *) read_buf;
00468     record_size = sizeof(nvstore_record_header_t) + header->size;
00469 
00470     // No need to copy records whose flags indicate deletion
00471     if (header->key_and_flags & delete_item_flag) {
00472         next_offset = align_up(to_offset, _min_prog_size);
00473         return NVSTORE_SUCCESS;
00474     }
00475 
00476     // no need to align record size here, as it won't change the outcome of this condition
00477     if (to_offset + record_size >= _size) {
00478         return NVSTORE_FLASH_AREA_TOO_SMALL;
00479     }
00480 
00481     uint16_t start_size = sizeof(nvstore_record_header_t);
00482     from_offset += start_size;
00483     read_buf += start_size;
00484     record_size -= start_size;
00485 
00486     do {
00487         chunk_size = std::min(record_size, (uint16_t)(prog_buf_size - start_size));
00488         if (chunk_size) {
00489             os_ret = flash_read_area(from_area, from_offset, chunk_size, read_buf);
00490             if (os_ret) {
00491                 return NVSTORE_READ_ERROR;
00492             }
00493         }
00494         os_ret = flash_write_area(1 - from_area, to_offset, chunk_size + start_size, prog_buf);
00495         if (os_ret) {
00496             return NVSTORE_WRITE_ERROR;
00497         }
00498 
00499         read_buf = prog_buf;
00500         record_size -= chunk_size;
00501         from_offset += chunk_size;
00502         to_offset += chunk_size + start_size;
00503         start_size = 0;
00504     } while (record_size);
00505 
00506     next_offset = align_up(to_offset, _min_prog_size);
00507     return NVSTORE_SUCCESS;
00508 }
00509 
00510 int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint16_t buf_size, const void *buf)
00511 {
00512     uint32_t curr_offset, new_area_offset, next_offset;
00513     int ret;
00514     uint8_t curr_area;
00515 
00516     new_area_offset = align_up(sizeof(nvstore_record_header_t) + sizeof(master_record_data_t), _min_prog_size);
00517 
00518     // If GC is triggered by a set item request, we need to first write that item in the new location,
00519     // otherwise we may either write it twice (if already included), or lose it in case we decide
00520     // to skip it at garbage collection phase (and the system crashes).
00521     if ((key != no_key) && !(flags & delete_item_flag)) {
00522         ret = write_record(1 - _active_area, new_area_offset, key, 0, buf_size, buf, next_offset);
00523         if (ret != NVSTORE_SUCCESS) {
00524             return ret;
00525         }
00526         _offset_by_key[key] = new_area_offset | (1 - _active_area) << offs_by_key_area_bit_pos |
00527                                 (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos);
00528         new_area_offset = next_offset;
00529     }
00530 
00531     // Now iterate on all types, and copy the ones who have valid offsets (meaning that they exist)
00532     // to the other area.
00533     for (key = 0; key < _max_keys; key++) {
00534         curr_offset = _offset_by_key[key];
00535         uint16_t save_flags = curr_offset & offs_by_key_area_mask;
00536         curr_area = (uint8_t)(curr_offset >> offs_by_key_area_bit_pos) & 1;
00537         curr_offset &= ~offs_by_key_flag_mask;
00538         if ((!curr_offset) || (curr_area != _active_area)) {
00539             continue;
00540         }
00541         ret = copy_record(curr_area, curr_offset, new_area_offset, next_offset);
00542         if (ret != NVSTORE_SUCCESS) {
00543             return ret;
00544         }
00545         _offset_by_key[key] = new_area_offset | (1 - curr_area) << offs_by_key_area_bit_pos | save_flags;
00546         new_area_offset = next_offset;
00547     }
00548 
00549     // Now write master record, with version incremented by 1.
00550     _active_area_version++;
00551     ret = write_master_record(1 - _active_area, _active_area_version, next_offset);
00552     if (ret != NVSTORE_SUCCESS) {
00553         return ret;
00554     }
00555 
00556     _free_space_offset = new_area_offset;
00557 
00558     // Only now we can switch to the new active area
00559     _active_area = 1 - _active_area;
00560 
00561     // The older area doesn't concern us now. Erase it now.
00562     if (flash_erase_area(1 - _active_area)) {
00563         return NVSTORE_WRITE_ERROR;
00564     }
00565 
00566     return ret;
00567 }
00568 
00569 
00570 int NVStore::do_get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual_size,
00571                     int validate_only)
00572 {
00573     int ret = NVSTORE_SUCCESS;
00574     int valid;
00575     uint32_t record_offset, next_offset;
00576     uint16_t read_type, flags;
00577     uint8_t area;
00578 
00579     if (!_init_done) {
00580         ret = init();
00581         if (ret != NVSTORE_SUCCESS) {
00582             return ret;
00583         }
00584     }
00585 
00586     if (key >= _max_keys) {
00587         return NVSTORE_BAD_VALUE;
00588     }
00589 
00590     if (!buf) {
00591         buf_size = 0;
00592     }
00593 
00594     _mutex->lock();
00595     record_offset = _offset_by_key[key];
00596 
00597     if (!record_offset) {
00598         _mutex->unlock();
00599         return NVSTORE_NOT_FOUND;
00600     }
00601 
00602     area = (uint8_t)(record_offset >> offs_by_key_area_bit_pos) & 1;
00603     record_offset &= ~offs_by_key_flag_mask;
00604 
00605     ret = read_record(area, record_offset, buf_size, buf,
00606                       actual_size, validate_only, valid,
00607                       read_type, flags, next_offset);
00608     if ((ret == NVSTORE_SUCCESS) && !valid) {
00609         ret = NVSTORE_DATA_CORRUPT;
00610     }
00611 
00612     _mutex->unlock();
00613     return ret;
00614 }
00615 
00616 int NVStore::get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual_size)
00617 {
00618     return do_get(key, buf_size, buf, actual_size, 0);
00619 }
00620 
00621 int NVStore::get_item_size(uint16_t key, uint16_t &actual_size)
00622 {
00623     return do_get(key, 0, NULL, actual_size, 1);
00624 }
00625 
00626 int NVStore::do_set(uint16_t key, uint16_t buf_size, const void *buf, uint16_t flags)
00627 {
00628     int ret = NVSTORE_SUCCESS;
00629     uint32_t record_offset, record_size, new_free_space;
00630     uint32_t next_offset;
00631 
00632     if (!_init_done) {
00633         ret = init();
00634         if (ret != NVSTORE_SUCCESS) {
00635             return ret;
00636         }
00637     }
00638 
00639     if (key >= _max_keys) {
00640         return NVSTORE_BAD_VALUE;
00641     }
00642 
00643     if (!buf) {
00644         buf_size = 0;
00645     }
00646 
00647     if ((flags & delete_item_flag) && !_offset_by_key[key]) {
00648         return NVSTORE_NOT_FOUND;
00649     }
00650 
00651     if (_offset_by_key[key] & offs_by_key_set_once_mask) {
00652         return NVSTORE_ALREADY_EXISTS;
00653     }
00654 
00655     record_size = align_up(sizeof(nvstore_record_header_t) + buf_size, _min_prog_size);
00656 
00657     _mutex->lock();
00658 
00659     new_free_space = core_util_atomic_incr_u32(&_free_space_offset, record_size);
00660     record_offset = new_free_space - record_size;
00661 
00662     // If we cross the area limit, we need to invoke GC.
00663     if (new_free_space >= _size) {
00664         ret = garbage_collection(key, flags, buf_size, buf);
00665         _mutex->unlock();
00666         return ret;
00667     }
00668 
00669     // Now write the record
00670     ret = write_record(_active_area, record_offset, key, flags, buf_size, buf, next_offset);
00671     if (ret != NVSTORE_SUCCESS) {
00672         _mutex->unlock();
00673         return ret;
00674     }
00675 
00676     // Update _offset_by_key. High bit indicates area.
00677     if (flags & delete_item_flag) {
00678         _offset_by_key[key] = 0;
00679     } else {
00680         _offset_by_key[key] = record_offset | (_active_area << offs_by_key_area_bit_pos) |
00681                               (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos);
00682     }
00683 
00684     _mutex->unlock();
00685 
00686     return NVSTORE_SUCCESS;
00687 }
00688 
00689 int NVStore::set(uint16_t key, uint16_t buf_size, const void *buf)
00690 {
00691     return do_set(key, buf_size, buf, 0);
00692 }
00693 
00694 int NVStore::set_once(uint16_t key, uint16_t buf_size, const void *buf)
00695 {
00696     return do_set(key, buf_size, buf, set_once_flag);
00697 }
00698 
00699 int NVStore::remove(uint16_t key)
00700 {
00701     return do_set(key, 0, NULL, delete_item_flag);
00702 }
00703 
00704 int NVStore::init()
00705 {
00706     area_state_e area_state[NVSTORE_NUM_AREAS];
00707     uint32_t free_space_offset_of_area[NVSTORE_NUM_AREAS];
00708     uint32_t init_attempts_val;
00709     uint32_t next_offset;
00710     int os_ret;
00711     int ret = NVSTORE_SUCCESS;
00712     int valid;
00713     uint16_t key;
00714     uint16_t flags;
00715     uint16_t versions[NVSTORE_NUM_AREAS];
00716     uint16_t actual_size;
00717 
00718     if (_init_done) {
00719         return NVSTORE_SUCCESS;
00720     }
00721 
00722     // This handles the case that init function is called by more than one thread concurrently.
00723     // Only the one who gets the value of 1 in _init_attempts_val will proceed, while others will
00724     // wait until init is finished.
00725     init_attempts_val = core_util_atomic_incr_u32(&_init_attempts, 1);
00726     if (init_attempts_val != 1) {
00727         while (!_init_done) {
00728             wait_ms(1);
00729         }
00730         return NVSTORE_SUCCESS;
00731     }
00732 
00733     _offset_by_key = new uint32_t[_max_keys];
00734     MBED_ASSERT(_offset_by_key);
00735 
00736     for (key = 0; key < _max_keys; key++) {
00737         _offset_by_key[key] = 0;
00738     }
00739 
00740     _mutex = new PlatformMutex;
00741     MBED_ASSERT(_mutex);
00742 
00743     _size = (uint32_t) -1;
00744     _flash = new mbed::FlashIAP;
00745     MBED_ASSERT(_flash);
00746     _flash->init();
00747 
00748     _min_prog_size = std::max(_flash->get_page_size(), (uint32_t)sizeof(nvstore_record_header_t));
00749     if (_min_prog_size > sizeof(nvstore_record_header_t)) {
00750         _page_buf = new uint8_t[_min_prog_size];
00751         MBED_ASSERT(_page_buf);
00752     }
00753 
00754     calc_validate_area_params();
00755 
00756     for (uint8_t area = 0; area < NVSTORE_NUM_AREAS; area++) {
00757         area_state[area] = NVSTORE_AREA_STATE_NONE;
00758         free_space_offset_of_area[area] =  0;
00759         versions[area] = 0;
00760 
00761         _size = std::min(_size, _flash_area_params[area].size);
00762 
00763         // Find start of empty space at the end of the area. This serves for both
00764         // knowing whether the area is empty and for the record traversal at the end.
00765         os_ret = calc_empty_space(area, free_space_offset_of_area[area]);
00766         MBED_ASSERT(!os_ret);
00767 
00768         if (!free_space_offset_of_area[area]) {
00769             area_state[area] = NVSTORE_AREA_STATE_EMPTY;
00770             continue;
00771         }
00772 
00773         // Check validity of master record
00774         master_record_data_t master_rec;
00775         ret = read_record(area, 0, sizeof(master_rec), &master_rec,
00776                           actual_size, 0, valid,
00777                           key, flags, next_offset);
00778         MBED_ASSERT((ret == NVSTORE_SUCCESS) || (ret == NVSTORE_BUFF_TOO_SMALL));
00779         if (ret == NVSTORE_BUFF_TOO_SMALL) {
00780             // Buf too small error means that we have a corrupt master record -
00781             // treat it as such
00782             valid = 0;
00783         }
00784 
00785         // We have a non valid master record, in a non-empty area. Just erase the area.
00786         if ((!valid) || (key != master_record_key)) {
00787             os_ret = flash_erase_area(area);
00788             MBED_ASSERT(!os_ret);
00789             area_state[area] = NVSTORE_AREA_STATE_EMPTY;
00790             continue;
00791         }
00792         versions[area] = master_rec.version;
00793 
00794         // Place _free_space_offset after the master record (for the traversal,
00795         // which takes place after this loop).
00796         _free_space_offset = next_offset;
00797         area_state[area] = NVSTORE_AREA_STATE_VALID;
00798 
00799         // Unless both areas are valid (a case handled later), getting here means
00800         // that we found our active area.
00801         _active_area = area;
00802         _active_area_version = versions[area];
00803     }
00804 
00805     // In case we have two empty areas, arbitrarily assign 0 to the active one.
00806     if ((area_state[0] == NVSTORE_AREA_STATE_EMPTY) && (area_state[1] == NVSTORE_AREA_STATE_EMPTY)) {
00807         _active_area = 0;
00808         ret = write_master_record(_active_area, 1, _free_space_offset);
00809         MBED_ASSERT(ret == NVSTORE_SUCCESS);
00810         _init_done = 1;
00811         return NVSTORE_SUCCESS;
00812     }
00813 
00814     // In case we have two valid areas, choose the one having the higher version (or 0
00815     // in case of wrap around). Erase the other one.
00816     if ((area_state[0] == NVSTORE_AREA_STATE_VALID) && (area_state[1] == NVSTORE_AREA_STATE_VALID)) {
00817         if ((versions[0] > versions[1]) || (!versions[0])) {
00818             _active_area = 0;
00819         } else {
00820             _active_area = 1;
00821         }
00822         _active_area_version = versions[_active_area];
00823         os_ret = flash_erase_area(1 - _active_area);
00824         MBED_ASSERT(!os_ret);
00825     }
00826 
00827     // Traverse area until reaching the empty space at the end or until reaching a faulty record
00828     while (_free_space_offset < free_space_offset_of_area[_active_area]) {
00829         ret = read_record(_active_area, _free_space_offset, 0, NULL,
00830                           actual_size, 1, valid,
00831                           key, flags, next_offset);
00832         MBED_ASSERT(ret == NVSTORE_SUCCESS);
00833 
00834         // In case we have a faulty record, this probably means that the system crashed when written.
00835         // Perform a garbage collection, to make the the other area valid.
00836         if (!valid) {
00837             ret = garbage_collection(no_key, 0, 0, NULL);
00838             break;
00839         }
00840         if (flags & delete_item_flag) {
00841             _offset_by_key[key] = 0;
00842         } else {
00843             _offset_by_key[key] = _free_space_offset | (_active_area << offs_by_key_area_bit_pos) |
00844                                   (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos);
00845         }
00846         _free_space_offset = next_offset;
00847     }
00848 
00849     _init_done = 1;
00850     return NVSTORE_SUCCESS;
00851 }
00852 
00853 int NVStore::deinit()
00854 {
00855     if (_init_done) {
00856         _flash->deinit();
00857         delete _flash;
00858         delete _mutex;
00859         delete[] _offset_by_key;
00860         if (_page_buf) {
00861             delete[] _page_buf;
00862             _page_buf = 0;
00863         }
00864     }
00865 
00866     _init_attempts = 0;
00867     _init_done = 0;
00868 
00869     return NVSTORE_SUCCESS;
00870 }
00871 
00872 int NVStore::reset()
00873 {
00874     uint8_t area;
00875     int os_ret;
00876 
00877     if (!_init_done) {
00878         init();
00879     }
00880 
00881     // Erase both areas, and reinitialize the module. This is totally not thread safe,
00882     // as init doesn't take the case of re-initialization into account. It's OK, as this function
00883     // should only be called in pre-production cases.
00884     for (area = 0; area < NVSTORE_NUM_AREAS; area++) {
00885         os_ret = flash_erase_area(area);
00886         if (os_ret) {
00887             return NVSTORE_WRITE_ERROR;
00888         }
00889     }
00890 
00891     deinit();
00892     return init();
00893 }
00894 
00895 int NVStore::get_area_params(uint8_t area, uint32_t &address, size_t &size)
00896 {
00897     if (area >= NVSTORE_NUM_AREAS) {
00898         return NVSTORE_BAD_VALUE;
00899     }
00900 
00901     if (!_init_done) {
00902         init();
00903     }
00904 
00905     address = _flash_area_params[area].address;
00906     size = _flash_area_params[area].size;
00907 
00908     return NVSTORE_SUCCESS;
00909 }
00910 
00911 size_t NVStore::size()
00912 {
00913     if (!_init_done) {
00914         init();
00915     }
00916 
00917     return _size;
00918 }
00919 
00920 #endif // NVSTORE_ENABLED