mirror of https://github.com/ARMmbed/mbed-os.git
230 lines
8.3 KiB
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;
|
|
}
|
|
}
|
|
}
|