takashi kadono / Mbed OS Nucleo_446

Dependencies:   ssd1331

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