mbed-os/features/frameworks/nanostack-libservice/source/nvmHelper/ns_nvm_helper.c

230 lines
8.3 KiB
C

// ----------------------------------------------------------------------------
// Copyright 2016-2017 ARM Ltd.
//
// 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.
// ----------------------------------------------------------------------------
#include <string.h>
#include <ns_types.h>
#include <nsdynmemLIB.h>
#include "ns_list.h"
#include "platform/arm_hal_nvm.h"
#include "ns_nvm_helper.h"
#define TRACE_GROUP "nnvm"
/* NVM operations */
#define NS_NVM_NONE 0x00
#define NS_NVM_INIT 0x01
#define NS_NVM_KEY_CREATE 0x02
#define NS_NVM_KEY_READ 0x03
#define NS_NVM_KEY_WRITE 0x04
#define NS_NVM_FLUSH 0x05
#define NS_NVM_KEY_DELETE 0x06
typedef struct {
ns_nvm_callback *callback;
const char *client_key_name;
void *client_context;
int operation;
uint8_t *buffer;
uint16_t *buffer_len;
void *original_request;
ns_list_link_t link;
} ns_nvm_request_t;
static bool ns_nvm_initialized = false;
static bool ns_nvm_operation_in_progress = false;
static ns_nvm_request_t *ns_nvm_create_request(ns_nvm_callback *callback, void *context, const char *key_name, uint8_t *buf, uint16_t *buf_len, uint8_t operation);
static int ns_nvm_operation_start(ns_nvm_request_t *request);
static int ns_nvm_operation_continue(ns_nvm_request_t *request, bool free_request);
static void ns_nvm_operation_end(ns_nvm_request_t *ns_nvm_request_ptr, int client_retval);
static NS_LIST_DEFINE(ns_nvm_request_list, ns_nvm_request_t, link);
/*
* Callback from platform NVM adaptation
*/
void ns_nvm_callback_func(platform_nvm_status status, void *args)
{
ns_nvm_request_t *ns_nvm_request_ptr = (ns_nvm_request_t*)args;
int client_retval = NS_NVM_OK;
if (status == PLATFORM_NVM_ERROR) {
client_retval = NS_NVM_ERROR;
} else if (status == PLATFORM_NVM_KEY_NOT_FOUND) {
client_retval = NS_NVM_DATA_NOT_FOUND;
}
switch(ns_nvm_request_ptr->operation) {
case NS_NVM_INIT:
ns_nvm_operation_continue(ns_nvm_request_ptr->original_request, true);
ns_dyn_mem_free(ns_nvm_request_ptr);
break;
case NS_NVM_FLUSH:
case NS_NVM_KEY_READ:
ns_nvm_operation_end(ns_nvm_request_ptr, client_retval);
break;
case NS_NVM_KEY_CREATE:
if (status == PLATFORM_NVM_OK) {
ns_nvm_request_ptr->operation = NS_NVM_KEY_WRITE;
platform_nvm_write(ns_nvm_callback_func, ns_nvm_request_ptr->client_key_name, ns_nvm_request_ptr->buffer, ns_nvm_request_ptr->buffer_len, ns_nvm_request_ptr);
} else {
ns_nvm_operation_end(ns_nvm_request_ptr, client_retval);
}
break;
case NS_NVM_KEY_DELETE:
case NS_NVM_KEY_WRITE:
if (status == PLATFORM_NVM_OK) {
// write ok, flush the changes
ns_nvm_request_ptr->operation = NS_NVM_FLUSH;
platform_nvm_flush(ns_nvm_callback_func, ns_nvm_request_ptr);
} else {
// write failed, inform client
ns_nvm_operation_end(ns_nvm_request_ptr, client_retval);
}
break;
}
}
int ns_nvm_key_delete(ns_nvm_callback *callback, const char *key_name, void *context)
{
if (!callback || !key_name) {
return NS_NVM_ERROR;
}
ns_nvm_request_t *ns_nvm_request_ptr = ns_nvm_create_request(callback, context, key_name, NULL, NULL, NS_NVM_KEY_DELETE);
return ns_nvm_operation_start(ns_nvm_request_ptr);
}
int ns_nvm_data_read(ns_nvm_callback *callback, const char *key_name, uint8_t *buf, uint16_t *buf_len, void *context)
{
if (!callback || !key_name || !buf || !buf_len) {
return NS_NVM_ERROR;
}
ns_nvm_request_t *ns_nvm_request_ptr = ns_nvm_create_request(callback, context, key_name, buf, buf_len, NS_NVM_KEY_READ);
return ns_nvm_operation_start(ns_nvm_request_ptr);
}
int ns_nvm_data_write(ns_nvm_callback *callback, const char *key_name, uint8_t *buf, uint16_t *buf_len, void *context)
{
if (!callback || !key_name || !buf || !buf_len) {
return NS_NVM_ERROR;
}
ns_nvm_request_t *ns_nvm_request_ptr = ns_nvm_create_request(callback, context, key_name, buf, buf_len, NS_NVM_KEY_WRITE);
return ns_nvm_operation_start(ns_nvm_request_ptr);
}
static int ns_nvm_operation_start(ns_nvm_request_t *nvm_request)
{
int ret = NS_NVM_OK;
platform_nvm_status pnvm_status;
if (!nvm_request) {
return NS_NVM_MEMORY;
}
if (ns_nvm_initialized == true) {
// NVM already initialized, continue directly
if (!ns_nvm_operation_in_progress) {
ret = ns_nvm_operation_continue(nvm_request, true);
} else {
// add request to list and handle when existing calls has been handled.
ns_list_add_to_end(&ns_nvm_request_list, nvm_request);
}
} else {
ns_nvm_request_t *ns_nvm_request_ptr = ns_nvm_create_request(NULL, NULL, NULL, NULL, NULL, NS_NVM_INIT);
if (!ns_nvm_request_ptr) {
ns_dyn_mem_free(nvm_request);
ns_dyn_mem_free(ns_nvm_request_ptr);
return NS_NVM_MEMORY;
}
ns_nvm_request_ptr->original_request = nvm_request;
pnvm_status = platform_nvm_init(ns_nvm_callback_func, ns_nvm_request_ptr);
if (pnvm_status != PLATFORM_NVM_OK) {
ns_dyn_mem_free(nvm_request);
ns_dyn_mem_free(ns_nvm_request_ptr);
return NS_NVM_ERROR;
}
ns_list_init(&ns_nvm_request_list);
ns_nvm_initialized = true;
ns_nvm_operation_in_progress = true;
}
return ret;
}
static ns_nvm_request_t *ns_nvm_create_request(ns_nvm_callback *callback, void *context, const char *key_name, uint8_t *buf, uint16_t *buf_len, uint8_t operation)
{
ns_nvm_request_t *ns_nvm_request_ptr = ns_dyn_mem_temporary_alloc(sizeof(ns_nvm_request_t));
if (!ns_nvm_request_ptr) {
return NULL;
}
ns_nvm_request_ptr->client_context = context;
ns_nvm_request_ptr->callback = callback;
ns_nvm_request_ptr->client_key_name = key_name;
ns_nvm_request_ptr->operation = operation;
ns_nvm_request_ptr->buffer = buf;
ns_nvm_request_ptr->buffer_len = buf_len;
return ns_nvm_request_ptr;
}
static int ns_nvm_operation_continue(ns_nvm_request_t *request, bool free_request)
{
platform_nvm_status ret = PLATFORM_NVM_OK;
ns_nvm_operation_in_progress = true;
switch(request->operation) {
case NS_NVM_KEY_WRITE:
request->operation = NS_NVM_KEY_CREATE;
ret = platform_nvm_key_create(ns_nvm_callback_func, request->client_key_name, *request->buffer_len, 0, request);
break;
case NS_NVM_KEY_READ:
ret = platform_nvm_read(ns_nvm_callback_func, request->client_key_name, request->buffer, request->buffer_len, request);
break;
case NS_NVM_KEY_DELETE:
ret = platform_nvm_key_delete(ns_nvm_callback_func, request->client_key_name, request);
break;
}
if (ret != PLATFORM_NVM_OK) {
if (free_request == true) {
// free request if requested
ns_dyn_mem_free(request);
}
ns_nvm_operation_in_progress = false;
return NS_NVM_ERROR;
}
return NS_NVM_OK;
}
static void ns_nvm_operation_end(ns_nvm_request_t *ns_nvm_request_ptr, int client_retval)
{
ns_nvm_request_ptr->callback(client_retval, ns_nvm_request_ptr->client_context);
ns_dyn_mem_free(ns_nvm_request_ptr);
ns_nvm_operation_in_progress = false;
ns_list_foreach_safe(ns_nvm_request_t, pending_req, &ns_nvm_request_list) {
// there are pending requests to be processed
ns_list_remove(&ns_nvm_request_list, pending_req);
int ret = ns_nvm_operation_continue(pending_req, false);
if (ret != NS_NVM_OK) {
ns_nvm_operation_end(pending_req, ret);
} else {
break;
}
}
}