// ---------------------------------------------------------------------------- // 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 #include #include #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; } } }