diff --git a/features/nvstore/TESTS/nvstore/functionality/main.cpp b/features/nvstore/TESTS/nvstore/functionality/main.cpp index 742206294f..b0f0633950 100644 --- a/features/nvstore/TESTS/nvstore/functionality/main.cpp +++ b/features/nvstore/TESTS/nvstore/functionality/main.cpp @@ -133,9 +133,17 @@ static void nvstore_basic_functionality_test() result = nvstore.set(19, 10, &(nvstore_testing_buf_set[3])); TEST_ASSERT_EQUAL(NVSTORE_ALREADY_EXISTS, result); - result = nvstore.set_alloc_key(key, 17, &(nvstore_testing_buf_set[3])); + result = nvstore.allocate_key(key, 3); TEST_ASSERT_EQUAL(NVSTORE_NUM_PREDEFINED_KEYS, key); TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result); + result = nvstore.set(NVSTORE_NUM_PREDEFINED_KEYS, 17, &(nvstore_testing_buf_set[3])); + TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result); + + result = nvstore.allocate_key(key, 3); + TEST_ASSERT_EQUAL(NVSTORE_NUM_PREDEFINED_KEYS + 1, key); + TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result); + result = nvstore.set(NVSTORE_NUM_PREDEFINED_KEYS + 1, 17, &(nvstore_testing_buf_set[3])); + TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result); // Make sure set items are also gotten OK after reset result = nvstore.deinit(); @@ -168,6 +176,20 @@ static void nvstore_basic_functionality_test() TEST_ASSERT_EQUAL(17, actual_len_bytes); TEST_ASSERT_EQUAL_UINT8_ARRAY(&nvstore_testing_buf_set[3], nvstore_testing_buf_get, 17); + result = nvstore.get(NVSTORE_NUM_PREDEFINED_KEYS + 1, 64, nvstore_testing_buf_get, actual_len_bytes); + TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result); + TEST_ASSERT_EQUAL(17, actual_len_bytes); + TEST_ASSERT_EQUAL_UINT8_ARRAY(&nvstore_testing_buf_set[3], nvstore_testing_buf_get, 17); + + result = nvstore.free_all_keys_by_owner(3); + TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result); + + result = nvstore.get(NVSTORE_NUM_PREDEFINED_KEYS, 64, nvstore_testing_buf_get, actual_len_bytes); + TEST_ASSERT_EQUAL(NVSTORE_NOT_FOUND, result); + + result = nvstore.get(NVSTORE_NUM_PREDEFINED_KEYS + 1, 64, nvstore_testing_buf_get, actual_len_bytes); + TEST_ASSERT_EQUAL(NVSTORE_NOT_FOUND, result); + result = nvstore.get(10, 65, nvstore_testing_buf_get, actual_len_bytes); TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result); TEST_ASSERT_EQUAL(64, actual_len_bytes); @@ -346,6 +368,12 @@ static void nvstore_basic_functionality_test() TEST_ASSERT_EQUAL(53, actual_len_bytes); TEST_ASSERT_EQUAL_UINT8_ARRAY(&(nvstore_testing_buf_set[10]), nvstore_testing_buf_get, 53); + result = nvstore.get(NVSTORE_NUM_PREDEFINED_KEYS, 64, nvstore_testing_buf_get, actual_len_bytes); + TEST_ASSERT_EQUAL(NVSTORE_NOT_FOUND, result); + + result = nvstore.get(NVSTORE_NUM_PREDEFINED_KEYS + 1, 64, nvstore_testing_buf_get, actual_len_bytes); + TEST_ASSERT_EQUAL(NVSTORE_NOT_FOUND, result); + delete[] nvstore_testing_buf_set; delete[] nvstore_testing_buf_get; } diff --git a/features/nvstore/source/nvstore.cpp b/features/nvstore/source/nvstore.cpp index 7f81ae386c..ec4557b197 100644 --- a/features/nvstore/source/nvstore.cpp +++ b/features/nvstore/source/nvstore.cpp @@ -42,15 +42,24 @@ static const uint16_t last_reserved_key = master_record_key; typedef struct { uint16_t key_and_flags; - uint16_t size; + uint16_t size_and_owner; uint32_t crc; } nvstore_record_header_t; -static const uint32_t offs_by_key_area_mask = 0x80000000UL; -static const uint32_t offs_by_key_set_once_mask = 0x40000000UL; -static const uint32_t offs_by_key_flag_mask = 0xC0000000UL; -static const unsigned int offs_by_key_area_bit_pos = 31; -static const unsigned int offs_by_key_set_once_bit_pos = 30; +static const uint32_t offs_by_key_area_mask = 0x00000001UL; +static const uint32_t offs_by_key_set_once_mask = 0x00000002UL; +static const uint32_t offs_by_key_allocated_mask = 0x00000004UL; +static const uint32_t offs_by_key_flag_mask = 0x00000007UL; +static const uint32_t offs_by_key_offset_mask = 0x0FFFFFF8UL; +static const uint32_t offs_by_key_owner_mask = 0xF0000000UL; + +static const unsigned int offs_by_key_area_bit_pos = 0; +static const unsigned int offs_by_key_set_once_bit_pos = 1; +static const unsigned int offs_by_key_owner_bit_pos = 28; + +static const uint16_t size_mask = 0x0FFF; +static const uint16_t owner_mask = 0xF000; +static const unsigned int owner_bit_pos = 12; typedef struct { uint16_t version; @@ -59,6 +68,7 @@ typedef struct { } master_record_data_t; static const uint32_t min_area_size = 4096; +static const uint32_t max_data_size = 4096; static const int num_write_retries = 16; @@ -134,7 +144,6 @@ NVStore::NVStore() : _init_done(0), _init_attempts(0), _active_area(0), _max_key _active_area_version(0), _free_space_offset(0), _size(0), _mutex(0), _offset_by_key(0), _flash(0), _min_prog_size(0), _page_buf(0) { - memset(_flash_area_params, 0, sizeof(_flash_area_params)); } NVStore::~NVStore() @@ -306,7 +315,7 @@ int NVStore::calc_empty_space(uint8_t area, uint32_t &offset) int NVStore::read_record(uint8_t area, uint32_t offset, uint16_t buf_size, void *buf, uint16_t &actual_size, int validate_only, int &valid, - uint16_t &key, uint16_t &flags, uint32_t &next_offset) + uint16_t &key, uint16_t &flags, uint8_t &owner, uint32_t &next_offset) { uint8_t int_buf[128]; void *buf_ptr; @@ -327,13 +336,14 @@ int NVStore::read_record(uint8_t area, uint32_t offset, uint16_t buf_size, void actual_size = 0; key = header.key_and_flags & ~header_flag_mask; flags = header.key_and_flags & header_flag_mask; + owner = (header.size_and_owner & owner_mask) >> owner_bit_pos; if ((key >= _max_keys) && (key != master_record_key)) { valid = 0; return NVSTORE_SUCCESS; } - data_size = header.size; + data_size = header.size_and_owner & size_mask; offset += sizeof(header); // In case of validate only enabled, we use our internal buffer for data reading, @@ -368,13 +378,13 @@ int NVStore::read_record(uint8_t area, uint32_t offset, uint16_t buf_size, void return NVSTORE_SUCCESS; } - actual_size = header.size; + actual_size = header.size_and_owner & size_mask; next_offset = align_up(offset, _min_prog_size); return NVSTORE_SUCCESS; } -int NVStore::write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t flags, +int NVStore::write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t flags, uint8_t owner, uint32_t data_size, const void *data_buf, uint32_t &next_offset) { nvstore_record_header_t header; @@ -383,7 +393,7 @@ int NVStore::write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t uint8_t *prog_buf; header.key_and_flags = key | flags; - header.size = data_size; + header.size_and_owner = data_size | (owner << owner_bit_pos); header.crc = 0; // Satisfy compiler crc = crc32(crc, sizeof(header) - sizeof(header.crc), (uint8_t *) &header); if (data_size) { @@ -436,7 +446,7 @@ int NVStore::write_master_record(uint8_t area, uint16_t version, uint32_t &next_ master_rec.version = version; master_rec.reserved1 = 0; master_rec.reserved2 = 0; - return write_record(area, 0, master_record_key, 0, sizeof(master_rec), + return write_record(area, 0, master_record_key, 0, 0, sizeof(master_rec), &master_rec, next_offset); } @@ -466,7 +476,7 @@ int NVStore::copy_record(uint8_t from_area, uint32_t from_offset, uint32_t to_of } header = (nvstore_record_header_t *) read_buf; - record_size = sizeof(nvstore_record_header_t) + header->size; + record_size = sizeof(nvstore_record_header_t) + (header->size_and_owner & size_mask); // No need to copy records whose flags indicate deletion if (header->key_and_flags & delete_item_flag) { @@ -508,7 +518,7 @@ int NVStore::copy_record(uint8_t from_area, uint32_t from_offset, uint32_t to_of return NVSTORE_SUCCESS; } -int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint16_t buf_size, const void *buf) +int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint8_t owner, uint16_t buf_size, const void *buf) { uint32_t curr_offset, new_area_offset, next_offset; int ret; @@ -520,7 +530,7 @@ int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint16_t buf_size, // otherwise we may either write it twice (if already included), or lose it in case we decide // to skip it at garbage collection phase (and the system crashes). if ((key != no_key) && !(flags & delete_item_flag)) { - ret = write_record(1 - _active_area, new_area_offset, key, 0, buf_size, buf, next_offset); + ret = write_record(1 - _active_area, new_area_offset, key, 0, owner, buf_size, buf, next_offset); if (ret != NVSTORE_SUCCESS) { return ret; } @@ -533,7 +543,7 @@ int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint16_t buf_size, // to the other area. for (key = 0; key < _max_keys; key++) { curr_offset = _offset_by_key[key]; - uint16_t save_flags = curr_offset & offs_by_key_area_mask; + uint16_t save_flags = curr_offset & offs_by_key_flag_mask & ~offs_by_key_area_mask; curr_area = (uint8_t)(curr_offset >> offs_by_key_area_bit_pos) & 1; curr_offset &= ~offs_by_key_flag_mask; if ((!curr_offset) || (curr_area != _active_area)) { @@ -575,7 +585,7 @@ int NVStore::do_get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual int valid; uint32_t record_offset, next_offset; uint16_t read_type, flags; - uint8_t area; + uint8_t area, owner; if (!_init_done) { ret = init(); @@ -597,19 +607,19 @@ int NVStore::do_get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual } _mutex->lock(); + record_offset = _offset_by_key[key]; + area = (uint8_t)(record_offset >> offs_by_key_area_bit_pos) & 1; + record_offset &= offs_by_key_offset_mask; if (!record_offset) { _mutex->unlock(); return NVSTORE_NOT_FOUND; } - area = (uint8_t)(record_offset >> offs_by_key_area_bit_pos) & 1; - record_offset &= ~offs_by_key_flag_mask; - ret = read_record(area, record_offset, buf_size, buf, actual_size, validate_only, valid, - read_type, flags, next_offset); + read_type, flags, owner, next_offset); if ((ret == NVSTORE_SUCCESS) && !valid) { ret = NVSTORE_DATA_CORRUPT; } @@ -628,11 +638,12 @@ int NVStore::get_item_size(uint16_t key, uint16_t &actual_size) return do_get(key, 0, NULL, actual_size, 1); } -int NVStore::do_set(uint16_t &key, uint16_t buf_size, const void *buf, uint16_t flags) +int NVStore::do_set(uint16_t key, uint16_t buf_size, const void *buf, uint16_t flags) { int ret = NVSTORE_SUCCESS; uint32_t record_offset, record_size, new_free_space; uint32_t next_offset; + uint8_t owner; if (!_init_done) { ret = init(); @@ -641,11 +652,11 @@ int NVStore::do_set(uint16_t &key, uint16_t buf_size, const void *buf, uint16_t } } - if ((key != no_key) && (key >= _max_keys)) { + if (key >= _max_keys) { return NVSTORE_BAD_VALUE; } - if ((key == no_key) && (flags & delete_item_flag)) { + if (buf_size >= max_data_size) { return NVSTORE_BAD_VALUE; } @@ -653,11 +664,11 @@ int NVStore::do_set(uint16_t &key, uint16_t buf_size, const void *buf, uint16_t buf_size = 0; } - if ((flags & delete_item_flag) && !_offset_by_key[key]) { + if ((flags & delete_item_flag) && !(_offset_by_key[key] & offs_by_key_offset_mask)) { return NVSTORE_NOT_FOUND; } - if ((key != no_key) && (_offset_by_key[key] & offs_by_key_set_once_mask)) { + if (_offset_by_key[key] & offs_by_key_set_once_mask) { return NVSTORE_ALREADY_EXISTS; } @@ -665,40 +676,31 @@ int NVStore::do_set(uint16_t &key, uint16_t buf_size, const void *buf, uint16_t _mutex->lock(); - if (key == no_key) { - for (key = NVSTORE_NUM_PREDEFINED_KEYS; key < _max_keys; key++) { - if (!_offset_by_key[key]) { - break; - } - } - if (key == _max_keys) { - return NVSTORE_NO_FREE_KEY; - } - } - + owner = (_offset_by_key[key] & offs_by_key_owner_mask) >> offs_by_key_owner_bit_pos; new_free_space = core_util_atomic_incr_u32(&_free_space_offset, record_size); record_offset = new_free_space - record_size; // If we cross the area limit, we need to invoke GC. if (new_free_space >= _size) { - ret = garbage_collection(key, flags, buf_size, buf); + ret = garbage_collection(key, flags, owner, buf_size, buf); _mutex->unlock(); return ret; } // Now write the record - ret = write_record(_active_area, record_offset, key, flags, buf_size, buf, next_offset); + ret = write_record(_active_area, record_offset, key, flags, owner, buf_size, buf, next_offset); if (ret != NVSTORE_SUCCESS) { _mutex->unlock(); return ret; } - // Update _offset_by_key. High bit indicates area. + // Update _offset_by_key if (flags & delete_item_flag) { _offset_by_key[key] = 0; } else { _offset_by_key[key] = record_offset | (_active_area << offs_by_key_area_bit_pos) | - (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos); + (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos) | + (owner << offs_by_key_owner_bit_pos); } _mutex->unlock(); @@ -716,10 +718,67 @@ int NVStore::set_once(uint16_t key, uint16_t buf_size, const void *buf) return do_set(key, buf_size, buf, set_once_flag); } -int NVStore::set_alloc_key(uint16_t &key, uint16_t buf_size, const void *buf) +int NVStore::allocate_key(uint16_t &key, uint8_t owner) { - key = no_key; - return do_set(key, buf_size, buf, 0); + int ret = NVSTORE_SUCCESS; + + if ((owner == NVSTORE_UNSPECIFIED_OWNER) || (owner >= NVSTORE_MAX_OWNERS)) { + return NVSTORE_BAD_VALUE; + } + + if (!_init_done) { + ret = init(); + if (ret != NVSTORE_SUCCESS) { + return ret; + } + } + + _mutex->lock(); + + for (key = NVSTORE_NUM_PREDEFINED_KEYS; key < _max_keys; key++) { + if (!_offset_by_key[key]) { + break; + } + } + if (key == _max_keys) { + ret = NVSTORE_NO_FREE_KEY; + } else { + _offset_by_key[key] |= offs_by_key_allocated_mask | (owner << offs_by_key_owner_bit_pos); + } + _mutex->unlock(); + return ret; +} + +int NVStore::free_all_keys_by_owner(uint8_t owner) +{ + int ret = NVSTORE_SUCCESS; + + if ((owner == NVSTORE_UNSPECIFIED_OWNER) || (owner >= NVSTORE_MAX_OWNERS)) { + return NVSTORE_BAD_VALUE; + } + + if (!_init_done) { + ret = init(); + if (ret != NVSTORE_SUCCESS) { + return ret; + } + } + + _mutex->lock(); + + for (uint16_t key = 0; key < _max_keys; key++) { + uint8_t curr_owner = (_offset_by_key[key] & offs_by_key_owner_mask) >> offs_by_key_owner_bit_pos; + if (curr_owner != owner) { + continue; + } + ret = remove(key); + if (ret) { + break; + } + } + + _mutex->unlock(); + return ret; } int NVStore::remove(uint16_t key) @@ -740,6 +799,7 @@ int NVStore::init() uint16_t flags; uint16_t versions[NVSTORE_NUM_AREAS]; uint16_t actual_size; + uint8_t owner; if (_init_done) { return NVSTORE_SUCCESS; @@ -800,7 +860,7 @@ int NVStore::init() master_record_data_t master_rec; ret = read_record(area, 0, sizeof(master_rec), &master_rec, actual_size, 0, valid, - key, flags, next_offset); + key, flags, owner, next_offset); MBED_ASSERT((ret == NVSTORE_SUCCESS) || (ret == NVSTORE_BUFF_TOO_SMALL)); if (ret == NVSTORE_BUFF_TOO_SMALL) { // Buf too small error means that we have a corrupt master record - @@ -854,20 +914,21 @@ int NVStore::init() while (_free_space_offset < free_space_offset_of_area[_active_area]) { ret = read_record(_active_area, _free_space_offset, 0, NULL, actual_size, 1, valid, - key, flags, next_offset); + key, flags, owner, next_offset); MBED_ASSERT(ret == NVSTORE_SUCCESS); // In case we have a faulty record, this probably means that the system crashed when written. // Perform a garbage collection, to make the the other area valid. if (!valid) { - ret = garbage_collection(no_key, 0, 0, NULL); + ret = garbage_collection(no_key, 0, 0, 0, NULL); break; } if (flags & delete_item_flag) { _offset_by_key[key] = 0; } else { _offset_by_key[key] = _free_space_offset | (_active_area << offs_by_key_area_bit_pos) | - (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos); + (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos) | + (owner << offs_by_key_owner_bit_pos); } _free_space_offset = next_offset; } diff --git a/features/nvstore/source/nvstore.h b/features/nvstore/source/nvstore.h index 91c675e057..1355283b12 100644 --- a/features/nvstore/source/nvstore.h +++ b/features/nvstore/source/nvstore.h @@ -53,6 +53,12 @@ typedef enum { NVSTORE_NUM_PREDEFINED_KEYS } nvstore_predefined_keys_e; +typedef enum { + NVSTORE_UNSPECIFIED_OWNER = 0, + // All owners (by features) should be specified here. + NVSTORE_MAX_OWNERS = 16 +} nvstore_owner_e; + #ifndef NVSTORE_MAX_KEYS #define NVSTORE_MAX_KEYS ((uint16_t)NVSTORE_NUM_PREDEFINED_KEYS) #endif @@ -159,22 +165,28 @@ public: int set(uint16_t key, uint16_t buf_size, const void *buf); /** - * @brief Programs one item of data on Flash, allocating a key. + * @brief Allocate a free key (to be used later in a set operation). * * @param[out] key Returned key of stored item. - * @param[in] buf_size Item size in bytes. - * @param[in] buf Buffer containing data. + * @param[in] owner Owner of allocated key. + * + * @returns NVSTORE_SUCCESS Key was successfully allocated. + * NVSTORE_NO_FREE_KEY Couldn't allocate a key for this call. + * + */ + int allocate_key(uint16_t &key, uint8_t owner = NVSTORE_UNSPECIFIED_OWNER); + + /** + * @brief Free all allocated keys that belong to a specific owner. + * + * @param[in] owner Owner. * * @returns NVSTORE_SUCCESS Value was successfully written on Flash. * NVSTORE_WRITE_ERROR Physical error writing data. * NVSTORE_BAD_VALUE Bad value in any of the parameters. - * NVSTORE_FLASH_AREA_TOO_SMALL - * Not enough space in Flash area. - * NVSTORE_ALREADY_EXISTS Item set with write once API already exists. - * NVSTORE_NO_FREE_KEY Couldn't allocate a key for this call. * */ - int set_alloc_key(uint16_t &key, uint16_t buf_size, const void *buf); + int free_all_keys_by_owner(uint8_t owner); /** * @brief Programs one item of data on Flash, given key, allowing no consequent sets to this key. @@ -344,13 +356,14 @@ private: * @param[out] valid Is the record valid. * @param[out] key Record key. * @param[out] flags Record flags. + * @param[out] owner Owner. * @param[out] next_offset Offset of next record. * * @returns 0 for success, nonzero for failure. */ int read_record(uint8_t area, uint32_t offset, uint16_t buf_size, void *buf, uint16_t &actual_size, int validate_only, int &valid, - uint16_t &key, uint16_t &flags, uint32_t &next_offset); + uint16_t &key, uint16_t &flags, uint8_t &owner, uint32_t &next_offset); /** * @brief Write an NVStore record from a given location. @@ -359,13 +372,14 @@ private: * @param[in] offset Offset of record in area. * @param[in] key Record key. * @param[in] flags Record flags. + * @param[in] owner Owner. * @param[in] data_size Data size (bytes). * @param[in] data_buf Data buffer. * @param[out] next_offset Offset of next record. * * @returns 0 for success, nonzero for failure. */ - int write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t flags, + int write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t flags, uint8_t owner, uint32_t data_size, const void *data_buf, uint32_t &next_offset); /** @@ -398,12 +412,13 @@ private: * * @param[in] key Record key. * @param[in] flags Record flags. + * @param[in] owner Owner. * @param[in] buf_size Data size (bytes). * @param[in] buf Data buffer. * * @returns 0 for success, nonzero for failure. */ - int garbage_collection(uint16_t key, uint16_t flags, uint16_t buf_size, const void *buf); + int garbage_collection(uint16_t key, uint16_t flags, uint8_t owner, uint16_t buf_size, const void *buf); /** * @brief Actual logics of get API (covers also get size API). @@ -422,14 +437,14 @@ private: /** * @brief Actual logics of set API (covers also set_once and remove APIs). * - * @param[out] key key (both input and output). + * @param[in] key key. * @param[in] buf_size Buffer size (bytes). * @param[in] buf Input Buffer. * @param[in] flags Record flags. * * @returns 0 for success, nonzero for failure. */ - int do_set(uint16_t &key, uint16_t buf_size, const void *buf, uint16_t flags); + int do_set(uint16_t key, uint16_t buf_size, const void *buf, uint16_t flags); }; /** @}*/