mbed-os/features/nanostack/nanostack-hal-mbed-cmsis-rtos/nvm/nvm_ram.c

278 lines
8.7 KiB
C

/*
* Copyright (c) 2016, 2018, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Define flag IGNORE_SIMULATED_NVM_STORAGE to ignore usage of simulated NVM and use
* platform specific NVM instead.
*/
#ifndef IGNORE_SIMULATED_NVM_STORAGE
#include <stdlib.h>
#include <string.h>
#include "ns_types.h"
#include "ns_list.h"
#include "nsdynmemLIB.h"
#include "eventOS_event_timer.h"
#include "platform/arm_hal_nvm.h"
#include "ns_trace.h"
#define TRACE_GROUP "rnvm"
/**
* NVM to RAM/heap, targeted for testing.
* Key/values are stored to ns_list. Otherwise functionality should be similar to
* nanostack configuration-store adaptation.
*/
#define NVM_RAM_STANDARD_MALLOC
#ifdef NVM_RAM_STANDARD_MALLOC
#define ALLOC malloc
#define FREE free
#else
#define ALLOC ns_dyn_mem_alloc
#define FREE ns_dyn_mem_free
#endif
/*
* Data entry stored to NVM
*/
typedef struct {
char *key;
uint8_t *data;
uint32_t data_len;
ns_list_link_t link;
} nvm_data_entry_t;
/*
* Client request to NVM
*/
typedef struct {
nvm_callback *client_cb; // callback provided by client
void *client_context; // context provided by client
void *client_buffer; // buffer provided by client
uint16_t *client_buffer_len; // buffer length provided by client
platform_nvm_status client_status; // status to be returned to client
ns_list_link_t link;
} nvm_client_req_t;
/*
* NVM context
*/
typedef struct {
timeout_t *callback_timer; // timer handle for informing client
bool is_initialized;
} nvm_context_t;
static NS_LIST_DEFINE(nvm_entry_list, nvm_data_entry_t, link);
static NS_LIST_DEFINE(nvm_client_req_list, nvm_client_req_t, link);
static nvm_context_t *nvm_context_ptr = NULL;
static void nvm_ram_timer_start(void *ctx);
static void nvm_ram_free_entry(nvm_data_entry_t *entry);
static platform_nvm_status create_client_request(nvm_callback *callback, void *context, void *buf, uint16_t *buf_len, platform_nvm_status status)
{
nvm_client_req_t *nvm_client_req_ptr;
nvm_client_req_ptr = ALLOC(sizeof(nvm_client_req_t));
if (!nvm_client_req_ptr) {
return PLATFORM_NVM_ERROR;
}
nvm_client_req_ptr->client_cb = callback;
nvm_client_req_ptr->client_context = context;
nvm_client_req_ptr->client_buffer = buf;
nvm_client_req_ptr->client_buffer_len = buf_len;
nvm_client_req_ptr->client_status = status;
ns_list_add_to_end(&nvm_client_req_list, nvm_client_req_ptr);
if (nvm_context_ptr->callback_timer == NULL) {
nvm_ram_timer_start(nvm_client_req_ptr);
}
return PLATFORM_NVM_OK;
}
platform_nvm_status platform_nvm_init(nvm_callback *callback, void *context)
{
if (nvm_context_ptr == NULL) {
nvm_context_ptr = ALLOC(sizeof(nvm_context_t));
if (!nvm_context_ptr) {
return PLATFORM_NVM_ERROR;
}
nvm_context_ptr->callback_timer = NULL;
nvm_context_ptr->is_initialized = true;
ns_list_init(&nvm_entry_list);
ns_list_init(&nvm_client_req_list);
} else {
if (nvm_context_ptr->is_initialized == true) {
return PLATFORM_NVM_ERROR;
}
}
return create_client_request(callback, context, NULL, NULL, PLATFORM_NVM_OK);
}
platform_nvm_status platform_nvm_finalize(nvm_callback *callback, void *context)
{
platform_nvm_status ret;
if (nvm_context_ptr->is_initialized == false) {
return PLATFORM_NVM_ERROR;
}
ret = create_client_request(callback, context, NULL, NULL, PLATFORM_NVM_OK);
if (ret == PLATFORM_NVM_OK) {
nvm_context_ptr->is_initialized = false;
}
return ret;
}
platform_nvm_status platform_nvm_key_create(nvm_callback *callback, const char *key_name, uint16_t value_len, uint32_t flags, void *context)
{
(void)flags;
tr_debug("platform_nvm_key_create() %s len=%d", key_name, (int)value_len);
ns_list_foreach(nvm_data_entry_t, current_entry, &nvm_entry_list) {
if (strcmp(current_entry->key, key_name) == 0) {
// resizing existing key
ns_list_remove(&nvm_entry_list, current_entry);
nvm_ram_free_entry(current_entry);
break;
}
}
nvm_data_entry_t *entry = ALLOC(sizeof(nvm_data_entry_t));
if (!entry) {
return PLATFORM_NVM_ERROR;
}
memset(entry, 0, sizeof(nvm_data_entry_t));
size_t key_len = strlen(key_name) + 1;
entry->key = ALLOC(key_len);
if (!entry->key) {
FREE(entry);
return PLATFORM_NVM_ERROR;
}
memcpy(entry->key, key_name, key_len);
entry->data_len = value_len;
entry->data = ALLOC(value_len);
if (!entry->data) {
FREE(entry->key);
FREE(entry);
return PLATFORM_NVM_ERROR;
}
ns_list_add_to_end(&nvm_entry_list, entry);
return create_client_request(callback, context, NULL, NULL, PLATFORM_NVM_OK);
}
platform_nvm_status platform_nvm_key_delete(nvm_callback *callback, const char *key_name, void *context)
{
platform_nvm_status client_status = PLATFORM_NVM_KEY_NOT_FOUND;
tr_debug("platform_nvm_key_delete() %s", key_name);
ns_list_foreach(nvm_data_entry_t, current_entry, &nvm_entry_list) {
if (strcmp(current_entry->key, key_name) == 0) {
client_status = PLATFORM_NVM_OK;
ns_list_remove(&nvm_entry_list, current_entry);
nvm_ram_free_entry(current_entry);
break;
}
}
return create_client_request(callback, context, NULL, NULL, client_status);
}
platform_nvm_status platform_nvm_write(nvm_callback *callback, const char *key_name, const void *data, uint16_t *data_len, void *context)
{
platform_nvm_status client_status = PLATFORM_NVM_KEY_NOT_FOUND;
tr_debug("platform_nvm_write() %s len=%d", key_name, (int)*data_len);
ns_list_foreach(nvm_data_entry_t, current_entry, &nvm_entry_list) {
if (strcmp(current_entry->key, key_name) == 0) {
if (current_entry->data_len >= *data_len) {
memcpy(current_entry->data, data, *data_len);
} else {
memcpy(current_entry->data, data, current_entry->data_len);
*data_len = current_entry->data_len;
}
client_status = PLATFORM_NVM_OK;
break;
}
}
return create_client_request(callback, context, (void *)data, data_len, client_status);
}
platform_nvm_status platform_nvm_read(nvm_callback *callback, const char *key_name, void *buf, uint16_t *buf_len, void *context)
{
platform_nvm_status client_status = PLATFORM_NVM_KEY_NOT_FOUND;
tr_debug("platform_nvm_read() %s len=%d", key_name, (int)*buf_len);
ns_list_foreach(nvm_data_entry_t, current_entry, &nvm_entry_list) {
if (strcmp(current_entry->key, key_name) == 0) {
if (*buf_len >= current_entry->data_len) {
memcpy(buf, current_entry->data, current_entry->data_len);
*buf_len = current_entry->data_len;
} else {
memcpy(buf, current_entry->data, *buf_len);
}
client_status = PLATFORM_NVM_OK;
break;
}
}
return create_client_request(callback, context, buf, buf_len, client_status);
}
platform_nvm_status platform_nvm_flush(nvm_callback *callback, void *context)
{
tr_debug("platform_nvm_flush()");
return create_client_request(callback, context, NULL, NULL, PLATFORM_NVM_OK);
}
static void nvm_ram_timer_cb(void *args)
{
nvm_client_req_t *nvm_client_req_ptr = (nvm_client_req_t *)args;
nvm_client_req_ptr->client_cb(nvm_client_req_ptr->client_status, nvm_client_req_ptr->client_context);
ns_list_remove(&nvm_client_req_list, nvm_client_req_ptr);
FREE(nvm_client_req_ptr);
nvm_context_ptr->callback_timer = NULL;
if (!ns_list_is_empty(&nvm_client_req_list)) {
// there are more client requests to process
nvm_client_req_ptr = ns_list_get_first(&nvm_client_req_list);
nvm_ram_timer_start(nvm_client_req_ptr);
}
}
static void nvm_ram_timer_start(void *ctx)
{
nvm_context_ptr->callback_timer = eventOS_timeout_ms(nvm_ram_timer_cb, 50, ctx);
}
static void nvm_ram_free_entry(nvm_data_entry_t *entry)
{
FREE(entry->key);
FREE(entry->data);
FREE(entry);
}
#endif /* IGNORE_SIMULATED_NVM_STORAGE */