Knight KE / Mbed OS Game_Master
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;
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         new_area_offset = next_offset;
00539     }
00540 
00541     // Now iterate on all types, and copy the ones who have valid offsets (meaning that they exist)
00542     // to the other area.
00543     for (key = 0; key < _max_keys; key++) {
00544         curr_offset = _offset_by_key[key];
00545         uint16_t save_flags = curr_offset & offs_by_key_flag_mask & ~offs_by_key_area_mask;
00546         curr_area = (uint8_t)(curr_offset >> offs_by_key_area_bit_pos) & 1;
00547         curr_offset &= ~offs_by_key_flag_mask;
00548         if ((!curr_offset) || (curr_area != _active_area)) {
00549             continue;
00550         }
00551         ret = copy_record(curr_area, curr_offset, new_area_offset, next_offset);
00552         if (ret != NVSTORE_SUCCESS) {
00553             return ret;
00554         }
00555         _offset_by_key[key] = new_area_offset | (1 - curr_area) << offs_by_key_area_bit_pos | save_flags;
00556         new_area_offset = next_offset;
00557     }
00558 
00559     // Now write master record, with version incremented by 1.
00560     _active_area_version++;
00561     ret = write_master_record(1 - _active_area, _active_area_version, next_offset);
00562     if (ret != NVSTORE_SUCCESS) {
00563         return ret;
00564     }
00565 
00566     _free_space_offset = new_area_offset;
00567 
00568     // Only now we can switch to the new active area
00569     _active_area = 1 - _active_area;
00570 
00571     // The older area doesn't concern us now. Erase it now.
00572     if (flash_erase_area(1 - _active_area)) {
00573         return NVSTORE_WRITE_ERROR;
00574     }
00575 
00576     return ret;
00577 }
00578 
00579 
00580 int NVStore::do_get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual_size,
00581                     int validate_only)
00582 {
00583     int ret = NVSTORE_SUCCESS;
00584     int valid;
00585     uint32_t record_offset, next_offset;
00586     uint16_t read_type, flags;
00587     uint8_t area, owner;
00588 
00589     if (!_init_done) {
00590         ret = init();
00591         if (ret != NVSTORE_SUCCESS) {
00592             return ret;
00593         }
00594     }
00595 
00596     if (key >= _max_keys) {
00597         return NVSTORE_BAD_VALUE;
00598     }
00599 
00600     if (!buf) {
00601         buf_size = 0;
00602         // This is only required in order to satisfy static code analysis tools, fearing
00603         // that a null buff is dereferenced inside read_record function. However, this won't happen
00604         // when buf_size is 0, so just have buf point to a dummy location.
00605         buf = &flags;
00606     }
00607 
00608     _mutex->lock();
00609     
00610     record_offset = _offset_by_key[key];
00611     area = (uint8_t)(record_offset >> offs_by_key_area_bit_pos) & 1;
00612     record_offset &= offs_by_key_offset_mask;
00613 
00614     if (!record_offset) {
00615         _mutex->unlock();
00616         return NVSTORE_NOT_FOUND;
00617     }
00618 
00619     ret = read_record(area, record_offset, buf_size, buf,
00620                       actual_size, validate_only, valid,
00621                       read_type, flags, owner, next_offset);
00622     if ((ret == NVSTORE_SUCCESS) && !valid) {
00623         ret = NVSTORE_DATA_CORRUPT;
00624     }
00625 
00626     _mutex->unlock();
00627     return ret;
00628 }
00629 
00630 int NVStore::get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual_size)
00631 {
00632     return do_get(key, buf_size, buf, actual_size, 0);
00633 }
00634 
00635 int NVStore::get_item_size(uint16_t key, uint16_t &actual_size)
00636 {
00637     return do_get(key, 0, NULL, actual_size, 1);
00638 }
00639 
00640 int NVStore::do_set(uint16_t key, uint16_t buf_size, const void *buf, uint16_t flags)
00641 {
00642     int ret = NVSTORE_SUCCESS;
00643     uint32_t record_offset, record_size, new_free_space;
00644     uint32_t next_offset;
00645     uint8_t owner;
00646 
00647     if (!_init_done) {
00648         ret = init();
00649         if (ret != NVSTORE_SUCCESS) {
00650             return ret;
00651         }
00652     }
00653 
00654     if (key >= _max_keys) {
00655         return NVSTORE_BAD_VALUE;
00656     }
00657 
00658     if (buf_size >= max_data_size) {
00659         return NVSTORE_BAD_VALUE;
00660     }
00661 
00662     if (!buf) {
00663         buf_size = 0;
00664     }
00665 
00666     if ((flags & delete_item_flag) && !(_offset_by_key[key] & offs_by_key_offset_mask)) {
00667         return NVSTORE_NOT_FOUND;
00668     }
00669 
00670     if (_offset_by_key[key] & offs_by_key_set_once_mask) {
00671         return NVSTORE_ALREADY_EXISTS;
00672     }
00673 
00674     record_size = align_up(sizeof(nvstore_record_header_t) + buf_size, _min_prog_size);
00675 
00676     _mutex->lock();
00677 
00678     owner = (_offset_by_key[key] & offs_by_key_owner_mask) >> offs_by_key_owner_bit_pos;
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, owner, 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, owner, buf_size, buf, next_offset);
00691     if (ret != NVSTORE_SUCCESS) {
00692         _mutex->unlock();
00693         return ret;
00694     }
00695 
00696     // Update _offset_by_key
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                               (owner << offs_by_key_owner_bit_pos);
00703     }
00704 
00705     _mutex->unlock();
00706 
00707     return NVSTORE_SUCCESS;
00708 }
00709 
00710 int NVStore::set(uint16_t key, uint16_t buf_size, const void *buf)
00711 {
00712     return do_set(key, buf_size, buf, 0);
00713 }
00714 
00715 int NVStore::set_once(uint16_t key, uint16_t buf_size, const void *buf)
00716 {
00717     return do_set(key, buf_size, buf, set_once_flag);
00718 }
00719 
00720 int NVStore::allocate_key(uint16_t &key, uint8_t owner)
00721 {
00722     int ret = NVSTORE_SUCCESS;
00723 
00724     if ((owner == NVSTORE_UNSPECIFIED_OWNER) || (owner >= NVSTORE_MAX_OWNERS)) {
00725         return NVSTORE_BAD_VALUE;
00726     }
00727 
00728     if (!_init_done) {
00729         ret = init();
00730         if (ret != NVSTORE_SUCCESS) {
00731             return ret;
00732         }
00733     }
00734 
00735     _mutex->lock();
00736 
00737     for (key = NVSTORE_NUM_PREDEFINED_KEYS; key < _max_keys; key++) {
00738         if (!_offset_by_key[key]) {
00739             break;
00740         }
00741     }
00742     if (key == _max_keys) {
00743         ret = NVSTORE_NO_FREE_KEY;
00744     } else {
00745         _offset_by_key[key] |= offs_by_key_allocated_mask | (owner << offs_by_key_owner_bit_pos);
00746     }
00747     _mutex->unlock();
00748     return ret;
00749 }
00750 
00751 int NVStore::free_all_keys_by_owner(uint8_t owner)
00752 {
00753     int ret = NVSTORE_SUCCESS;
00754 
00755     if ((owner == NVSTORE_UNSPECIFIED_OWNER) || (owner >= NVSTORE_MAX_OWNERS)) {
00756         return NVSTORE_BAD_VALUE;
00757     }
00758 
00759     if (!_init_done) {
00760         ret = init();
00761         if (ret != NVSTORE_SUCCESS) {
00762             return ret;
00763         }
00764     }
00765 
00766     _mutex->lock();
00767 
00768     for (uint16_t key = 0; key < _max_keys; key++) {
00769         uint8_t curr_owner = (_offset_by_key[key] & offs_by_key_owner_mask) >> offs_by_key_owner_bit_pos;
00770         if (curr_owner != owner) {
00771             continue;
00772         }
00773         ret = remove(key);
00774         if (ret) {
00775             break;
00776         }
00777     }
00778 
00779     _mutex->unlock();
00780     return ret;
00781 }
00782 
00783 int NVStore::remove(uint16_t key)
00784 {
00785     return do_set(key, 0, NULL, delete_item_flag);
00786 }
00787 
00788 int NVStore::init()
00789 {
00790     area_state_e area_state[NVSTORE_NUM_AREAS];
00791     uint32_t free_space_offset_of_area[NVSTORE_NUM_AREAS];
00792     uint32_t init_attempts_val;
00793     uint32_t next_offset;
00794     int os_ret;
00795     int ret = NVSTORE_SUCCESS;
00796     int valid;
00797     uint16_t key;
00798     uint16_t flags;
00799     uint16_t versions[NVSTORE_NUM_AREAS];
00800     uint16_t actual_size;
00801     uint8_t owner;
00802 
00803     if (_init_done) {
00804         return NVSTORE_SUCCESS;
00805     }
00806 
00807     // This handles the case that init function is called by more than one thread concurrently.
00808     // Only the one who gets the value of 1 in _init_attempts_val will proceed, while others will
00809     // wait until init is finished.
00810     init_attempts_val = core_util_atomic_incr_u32(&_init_attempts, 1);
00811     if (init_attempts_val != 1) {
00812         while (!_init_done) {
00813             wait_ms(1);
00814         }
00815         return NVSTORE_SUCCESS;
00816     }
00817 
00818     _offset_by_key = new uint32_t[_max_keys];
00819     MBED_ASSERT(_offset_by_key);
00820 
00821     for (key = 0; key < _max_keys; key++) {
00822         _offset_by_key[key] = 0;
00823     }
00824 
00825     _mutex = new PlatformMutex;
00826     MBED_ASSERT(_mutex);
00827 
00828     _size = (uint32_t) -1;
00829     _flash = new mbed::FlashIAP;
00830     MBED_ASSERT(_flash);
00831     _flash->init();
00832 
00833     _min_prog_size = std::max(_flash->get_page_size(), (uint32_t)sizeof(nvstore_record_header_t));
00834     if (_min_prog_size > sizeof(nvstore_record_header_t)) {
00835         _page_buf = new uint8_t[_min_prog_size];
00836         MBED_ASSERT(_page_buf);
00837     }
00838 
00839     calc_validate_area_params();
00840 
00841     for (uint8_t area = 0; area < NVSTORE_NUM_AREAS; area++) {
00842         area_state[area] = NVSTORE_AREA_STATE_NONE;
00843         free_space_offset_of_area[area] =  0;
00844         versions[area] = 0;
00845 
00846         _size = std::min(_size, _flash_area_params[area].size);
00847 
00848         // Find start of empty space at the end of the area. This serves for both
00849         // knowing whether the area is empty and for the record traversal at the end.
00850         os_ret = calc_empty_space(area, free_space_offset_of_area[area]);
00851         MBED_ASSERT(!os_ret);
00852 
00853         if (!free_space_offset_of_area[area]) {
00854             area_state[area] = NVSTORE_AREA_STATE_EMPTY;
00855             continue;
00856         }
00857 
00858         // Check validity of master record
00859         master_record_data_t master_rec;
00860         ret = read_record(area, 0, sizeof(master_rec), &master_rec,
00861                           actual_size, 0, valid,
00862                           key, flags, owner, next_offset);
00863         MBED_ASSERT((ret == NVSTORE_SUCCESS) || (ret == NVSTORE_BUFF_TOO_SMALL));
00864         if (ret == NVSTORE_BUFF_TOO_SMALL) {
00865             // Buf too small error means that we have a corrupt master record -
00866             // treat it as such
00867             valid = 0;
00868         }
00869 
00870         // We have a non valid master record, in a non-empty area. Just erase the area.
00871         if ((!valid) || (key != master_record_key)) {
00872             os_ret = flash_erase_area(area);
00873             MBED_ASSERT(!os_ret);
00874             area_state[area] = NVSTORE_AREA_STATE_EMPTY;
00875             continue;
00876         }
00877         versions[area] = master_rec.version;
00878 
00879         // Place _free_space_offset after the master record (for the traversal,
00880         // which takes place after this loop).
00881         _free_space_offset = next_offset;
00882         area_state[area] = NVSTORE_AREA_STATE_VALID;
00883 
00884         // Unless both areas are valid (a case handled later), getting here means
00885         // that we found our active area.
00886         _active_area = area;
00887         _active_area_version = versions[area];
00888     }
00889 
00890     // In case we have two empty areas, arbitrarily assign 0 to the active one.
00891     if ((area_state[0] == NVSTORE_AREA_STATE_EMPTY) && (area_state[1] == NVSTORE_AREA_STATE_EMPTY)) {
00892         _active_area = 0;
00893         ret = write_master_record(_active_area, 1, _free_space_offset);
00894         MBED_ASSERT(ret == NVSTORE_SUCCESS);
00895         _init_done = 1;
00896         return NVSTORE_SUCCESS;
00897     }
00898 
00899     // In case we have two valid areas, choose the one having the higher version (or 0
00900     // in case of wrap around). Erase the other one.
00901     if ((area_state[0] == NVSTORE_AREA_STATE_VALID) && (area_state[1] == NVSTORE_AREA_STATE_VALID)) {
00902         if ((versions[0] > versions[1]) || (!versions[0])) {
00903             _active_area = 0;
00904         } else {
00905             _active_area = 1;
00906         }
00907         _active_area_version = versions[_active_area];
00908         os_ret = flash_erase_area(1 - _active_area);
00909         MBED_ASSERT(!os_ret);
00910     }
00911 
00912     // Traverse area until reaching the empty space at the end or until reaching a faulty record
00913     while (_free_space_offset < free_space_offset_of_area[_active_area]) {
00914         ret = read_record(_active_area, _free_space_offset, 0, NULL,
00915                           actual_size, 1, valid,
00916                           key, flags, owner, next_offset);
00917         MBED_ASSERT(ret == NVSTORE_SUCCESS);
00918 
00919         // In case we have a faulty record, this probably means that the system crashed when written.
00920         // Perform a garbage collection, to make the the other area valid.
00921         if (!valid) {
00922             ret = garbage_collection(no_key, 0, 0, 0, NULL);
00923             break;
00924         }
00925         if (flags & delete_item_flag) {
00926             _offset_by_key[key] = 0;
00927         } else {
00928             _offset_by_key[key] = _free_space_offset | (_active_area << offs_by_key_area_bit_pos) |
00929                                   (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos) |
00930                                   (owner << offs_by_key_owner_bit_pos);
00931         }
00932         _free_space_offset = next_offset;
00933     }
00934 
00935     _init_done = 1;
00936     return NVSTORE_SUCCESS;
00937 }
00938 
00939 int NVStore::deinit()
00940 {
00941     if (_init_done) {
00942         _flash->deinit();
00943         delete _flash;
00944         delete _mutex;
00945         delete[] _offset_by_key;
00946         if (_page_buf) {
00947             delete[] _page_buf;
00948             _page_buf = 0;
00949         }
00950     }
00951 
00952     _init_attempts = 0;
00953     _init_done = 0;
00954 
00955     return NVSTORE_SUCCESS;
00956 }
00957 
00958 int NVStore::reset()
00959 {
00960     uint8_t area;
00961     int os_ret;
00962 
00963     if (!_init_done) {
00964         init();
00965     }
00966 
00967     // Erase both areas, and reinitialize the module. This is totally not thread safe,
00968     // as init doesn't take the case of re-initialization into account. It's OK, as this function
00969     // should only be called in pre-production cases.
00970     for (area = 0; area < NVSTORE_NUM_AREAS; area++) {
00971         os_ret = flash_erase_area(area);
00972         if (os_ret) {
00973             return NVSTORE_WRITE_ERROR;
00974         }
00975     }
00976 
00977     deinit();
00978     return init();
00979 }
00980 
00981 int NVStore::get_area_params(uint8_t area, uint32_t &address, size_t &size)
00982 {
00983     if (area >= NVSTORE_NUM_AREAS) {
00984         return NVSTORE_BAD_VALUE;
00985     }
00986 
00987     if (!_init_done) {
00988         init();
00989     }
00990 
00991     address = _flash_area_params[area].address;
00992     size = _flash_area_params[area].size;
00993 
00994     return NVSTORE_SUCCESS;
00995 }
00996 
00997 size_t NVStore::size()
00998 {
00999     if (!_init_done) {
01000         init();
01001     }
01002 
01003     return _size;
01004 }
01005 
01006 #endif // NVSTORE_ENABLED