aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/experimental_log/src/nrf_log_backend_flash.c
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/experimental_log/src/nrf_log_backend_flash.c')
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/experimental_log/src/nrf_log_backend_flash.c739
1 files changed, 739 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/experimental_log/src/nrf_log_backend_flash.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/experimental_log/src/nrf_log_backend_flash.c
new file mode 100644
index 0000000..0efd5d8
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/experimental_log/src/nrf_log_backend_flash.c
@@ -0,0 +1,739 @@
+/**
+ * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form, except as embedded into a Nordic
+ * Semiconductor ASA integrated circuit in a product or a software update for
+ * such product, must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * 4. This software, with or without modification, must only be used with a
+ * Nordic Semiconductor ASA integrated circuit.
+ *
+ * 5. Any software provided in binary form under this license must not be reverse
+ * engineered, decompiled, modified and/or disassembled.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include "sdk_common.h"
+#if NRF_MODULE_ENABLED(NRF_LOG) && NRF_MODULE_ENABLED(NRF_LOG_BACKEND_FLASH)
+#include "nrf_log_backend_flash.h"
+#include "nrf_log_str_formatter.h"
+#include "nrf_fstorage_nvmc.h"
+#include "nrf_log.h"
+#include "nrf_atomic.h"
+#include "nrf_queue.h"
+#include "app_error.h"
+#include <stdbool.h>
+
+#if (NRF_LOG_BACKEND_FLASHLOG_ENABLED == 0) && (NRF_LOG_BACKEND_CRASHLOG_ENABLED == 0)
+#error "No flash backend enabled."
+#endif
+
+/** @brief Maximum logger message payload (arguments or data in hexdump) which can be stored. */
+#define FLASH_LOG_MAX_PAYLOAD_SIZE (NRF_LOG_BACKEND_FLASH_SER_BUFFER_SIZE - sizeof(nrf_log_header_t))
+
+/** @brief Size of serialization buffer in words. */
+#define FLASH_LOG_SER_BUFFER_WORDS (NRF_LOG_BACKEND_FLASH_SER_BUFFER_SIZE/sizeof(uint32_t))
+
+/** @brief Length of logger header. */
+#define LOG_HEADER_LEN (sizeof(nrf_log_header_t))
+
+/** @brief Length of logger header given in 32 bit words. */
+#define LOG_HEADER_LEN_WORDS (LOG_HEADER_LEN/sizeof(uint32_t))
+
+/** @brief Maximum possible length of standard log message. */
+#define STD_LOG_MSG_MAX_LEN (LOG_HEADER_LEN + NRF_LOG_MAX_NUM_OF_ARGS*sizeof(uint32_t))
+
+/* Buffer must be multiple of 4. */
+STATIC_ASSERT((NRF_LOG_BACKEND_FLASH_SER_BUFFER_SIZE % sizeof(uint32_t)) == 0);
+
+/* Buffer must fit standard log message. */
+STATIC_ASSERT(NRF_LOG_BACKEND_FLASH_SER_BUFFER_SIZE >= STD_LOG_MSG_MAX_LEN);
+
+/** @brief Flash page size in bytes. */
+#define CODE_PAGE_SIZE 4096
+
+/** @brief Start address of the area dedicated for flash log. */
+#define FLASH_LOG_START_ADDR (NRF_LOG_BACKEND_FLASH_START_PAGE * CODE_PAGE_SIZE)
+
+/** @brief End address of the area dedicated for flash log. */
+#define FLASH_LOG_END_ADDR (FLASH_LOG_START_ADDR + (NRF_LOG_BACKEND_PAGES * CODE_PAGE_SIZE) - 1)
+
+/** @brief Size of the area dedicated for flash log. */
+#define FLASH_LOG_SIZE (NRF_LOG_BACKEND_PAGES * CODE_PAGE_SIZE)
+
+/** @brief Start address determined in runtime.
+ *
+ * If configuration indicates that flash log should be placed after application.
+ * */
+#if defined ( __CC_ARM )
+#define RUNTIME_START_ADDR \
+ _Pragma("diag_suppress 170") \
+ ((NRF_LOG_BACKEND_FLASH_START_PAGE == 0) ? \
+ (CODE_PAGE_SIZE*CEIL_DIV((uint32_t)CODE_END, CODE_PAGE_SIZE)) : FLASH_LOG_START_ADDR) \
+ _Pragma("diag_default 170")
+#else
+#define RUNTIME_START_ADDR ((NRF_LOG_BACKEND_FLASH_START_PAGE == 0) ? \
+ (CODE_PAGE_SIZE*CEIL_DIV((uint32_t)CODE_END, CODE_PAGE_SIZE)) : FLASH_LOG_START_ADDR)
+#endif
+static void fstorage_evt_handler(nrf_fstorage_evt_t * p_evt);
+
+/** @brief Message queue for run time flash log. */
+#if NRF_LOG_BACKEND_FLASHLOG_ENABLED
+NRF_QUEUE_DEF(nrf_log_entry_t *,
+ m_flashlog_queue,
+ NRF_LOG_BACKEND_FLASHLOG_QUEUE_SIZE,
+ NRF_QUEUE_MODE_NO_OVERFLOW);
+static const nrf_queue_t * mp_flashlog_queue = &m_flashlog_queue;
+#else
+static const nrf_queue_t * mp_flashlog_queue = NULL;
+#endif
+
+
+/** @brief Message FIFO for crash log. */
+#if NRF_LOG_BACKEND_CRASHLOG_ENABLED
+NRF_QUEUE_DEF(nrf_log_entry_t *,
+ m_crashlog_queue,
+ NRF_LOG_BACKEND_CRASHLOG_FIFO_SIZE,
+ NRF_QUEUE_MODE_NO_OVERFLOW);
+static const nrf_queue_t * mp_crashlog_queue = &m_crashlog_queue;
+#else
+static const nrf_queue_t * mp_crashlog_queue = NULL;
+#endif
+
+
+/** @brief Fstorage instance used for flash log. */
+NRF_FSTORAGE_DEF(nrf_fstorage_t m_log_flash_fstorage) =
+{
+ /* Set a handler for fstorage events. */
+ .evt_handler = fstorage_evt_handler,
+ .start_addr = FLASH_LOG_START_ADDR,
+ .end_addr = FLASH_LOG_END_ADDR,
+};
+
+/** @brief Flash log state. */
+typedef enum
+{
+ LOG_BACKEND_FLASH_ACTIVE, /**< Flash backend is active. */
+ LOG_BACKEND_FLASH_INACTIVE, /**< Flash backend is inactive. All incoming requests are skipped. */
+ LOG_BACKEND_FLASH_IN_PANIC, /**< Flash backend is in panic mode. Incoming messages are written to flash in synchronous mode. */
+} log_backend_flash_state_t;
+
+static log_backend_flash_state_t m_state; /**< Flash logger backend state. */
+static nrf_atomic_flag_t m_busy_flag; /**< Flag indicating if module performs flash writing. */
+static uint32_t m_flash_buf[FLASH_LOG_SER_BUFFER_WORDS]; /**< Buffer used for serializing messages. */
+static uint32_t m_curr_addr; /**< Address of free spot in the storage area. */
+static size_t m_curr_len; /**< Length of current message being written. */
+static uint32_t m_dropped; /**< Number of dropped messages. */
+
+/** @brief Log message string injected when entering panic mode. */
+static const char crashlog_str[] = "-----------CRASHLOG------------\r\n";
+
+/** @brief Function saturates input to maximum possible length and rounds up value to be multiple
+ * of word size.
+ *
+ * @param length Length value.
+ *
+ * @return Modified input length.
+ */
+static uint32_t saturate_align_length(uint32_t length)
+{
+ length = (length > FLASH_LOG_MAX_PAYLOAD_SIZE) ? FLASH_LOG_MAX_PAYLOAD_SIZE : length; //saturate
+ length = CEIL_DIV(length, sizeof(uint32_t))*sizeof(uint32_t);
+ return length;
+}
+
+
+/**
+ * @brief Function for copying logger message to the buffer.
+ *
+ * @param[in] p_msg Logger message.
+ * @param[out] p_buf Output buffer where serialized message is placed.
+ * @param[in,out] p_len Buffer size as input, length of prepared data as output.
+ *
+ * @return True if message fits into the buffer, false otherwise
+ */
+static bool msg_to_buf(nrf_log_entry_t * p_msg, uint8_t * p_buf, size_t * p_len)
+{
+ uint32_t data_len;
+ nrf_log_header_t header = {0};
+ uint32_t memobj_offset = HEADER_SIZE*sizeof(uint32_t);
+
+ nrf_memobj_read(p_msg, &header, HEADER_SIZE*sizeof(uint32_t), 0);
+
+ memcpy(p_buf, &header, sizeof(nrf_log_header_t));
+ p_buf += sizeof(nrf_log_header_t);
+
+ switch (header.base.generic.type)
+ {
+ case HEADER_TYPE_STD:
+ {
+ data_len = header.base.std.nargs * sizeof(uint32_t);
+ break;
+ }
+ case HEADER_TYPE_HEXDUMP:
+ {
+ data_len = saturate_align_length(header.base.hexdump.len);
+ break;
+ }
+ default:
+ *p_len = 0;
+ return false;
+ }
+ nrf_memobj_read(p_msg, p_buf, data_len, memobj_offset);
+
+ if (*p_len >= sizeof(nrf_log_header_t) + data_len)
+ {
+ *p_len = sizeof(nrf_log_header_t) + data_len;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/**
+ * @brief Function for getting logger message stored in flash.
+ *
+ * @param[in] p_buf Pointer to the location where message is stored.
+ * @param[out] pp_header Pointer to the log message header.
+ * @param[out] pp_data Pointer to the log message data (arguments or data in case of hexdump).
+ *
+ * @return True if message was successfully fetched, false otherwise.
+ */
+static bool msg_from_buf(uint32_t * p_buf,
+ nrf_log_header_t * * pp_header,
+ uint8_t * * pp_data,
+ uint32_t * p_len)
+{
+ *pp_header = (nrf_log_header_t *)p_buf;
+ *pp_data = (uint8_t *)&p_buf[LOG_HEADER_LEN_WORDS];
+
+ uint32_t data_len;
+
+ switch ((*pp_header)->base.generic.type)
+ {
+ case HEADER_TYPE_STD:
+ {
+ data_len = ((*pp_header)->base.std.nargs)*sizeof(uint32_t);
+ break;
+ }
+ case HEADER_TYPE_HEXDUMP:
+ {
+
+ data_len = saturate_align_length((*pp_header)->base.hexdump.len);
+ break;
+ }
+ default:
+ return false;
+ }
+
+ *p_len = LOG_HEADER_LEN + data_len;
+ return true;
+}
+
+/**
+ * @brief Function for processing log message queue.
+ *
+ * If writing to flash is synchronous then function drains the queue and writes all messages to flash.
+ * If writing to flash is asynchronous then function starts single write operation. In asynchronous mode
+ * function is called when new message is put into the queue from from flash operation callback.
+ *
+ * Function detects the situation that flash module reports attempt to write outside dedicated area.
+ * In that case flash backend stops writing any new messages.
+ *
+ * @param p_queue Queue will log messages
+ * @param fstorage_blocking If true it indicates that flash operations are blocking, event handler is not used.
+ */
+static void log_msg_queue_process(nrf_queue_t const * p_queue, bool fstorage_blocking)
+{
+ nrf_log_entry_t * p_msg;
+ bool busy = false;
+ while (nrf_queue_pop(p_queue, &p_msg) == NRF_SUCCESS)
+ {
+ ret_code_t err_code;
+
+ m_curr_len = sizeof(m_flash_buf);
+ if (!msg_to_buf(p_msg, (uint8_t *)m_flash_buf, &m_curr_len))
+ {
+ continue;
+ }
+
+ err_code = nrf_fstorage_write(&m_log_flash_fstorage, m_curr_addr, m_flash_buf, m_curr_len, p_msg);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ if (fstorage_blocking)
+ {
+ m_curr_addr += m_curr_len;
+
+ nrf_memobj_put(p_msg);
+ }
+ else
+ {
+ busy = true;
+ break;
+ }
+ }
+ else if (!fstorage_blocking && (err_code == NRF_ERROR_NO_MEM))
+ {
+ // fstorage queue got full. Drop entry.
+ nrf_memobj_put(p_msg);
+ m_dropped++;
+ break;
+ }
+ else if (err_code == NRF_ERROR_INVALID_ADDR)
+ {
+ // Trying to write outside the area, flash log is full. Skip any new writes.
+ nrf_memobj_put(p_msg);
+ m_state = LOG_BACKEND_FLASH_INACTIVE;
+ }
+ else
+ {
+ ASSERT(false);
+ }
+ }
+
+ if (!busy)
+ {
+ UNUSED_RETURN_VALUE(nrf_atomic_flag_clear(&m_busy_flag));
+ }
+}
+
+static void queue_element_drop(nrf_queue_t const * p_queue)
+{
+ nrf_log_entry_t * p_msg;
+ if (nrf_queue_pop(p_queue, &p_msg) == NRF_SUCCESS)
+ {
+ m_dropped++;
+ nrf_memobj_put(p_msg);
+ }
+}
+
+static void fstorage_evt_handler(nrf_fstorage_evt_t * p_evt)
+{
+ if (m_state == LOG_BACKEND_FLASH_ACTIVE)
+ {
+ switch (p_evt->id)
+ {
+ case NRF_FSTORAGE_EVT_WRITE_RESULT:
+ {
+ if (p_evt->result == NRF_SUCCESS)
+ {
+ m_curr_addr += m_curr_len;
+ m_curr_len = 0;
+ log_msg_queue_process(mp_flashlog_queue, false);
+ }
+ else
+ {
+ m_dropped++;
+ }
+
+ if (p_evt->p_param)
+ {
+ nrf_memobj_put((nrf_log_entry_t *)p_evt->p_param);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ else if ((m_state == LOG_BACKEND_FLASH_INACTIVE) &&
+ (p_evt->id == NRF_FSTORAGE_EVT_ERASE_RESULT) &&
+ (p_evt->addr == RUNTIME_START_ADDR))
+ {
+ m_state = LOG_BACKEND_FLASH_ACTIVE;
+ }
+}
+
+/**
+ * @brief Function for enqueueing new message.
+ *
+ * If queue is full then the oldest message is freed.
+ *
+ * @param p_queue Queue.
+ * @param p_msg Message.
+ *
+ * @return Number of dropped messages
+ */
+static uint32_t message_enqueue(nrf_queue_t const * p_queue, nrf_log_entry_t * p_msg)
+{
+ uint32_t dropped = 0;
+
+ //flag was set, busy so enqueue message
+ while (nrf_queue_push(p_queue, &p_msg) != NRF_SUCCESS)
+ {
+
+ nrf_log_entry_t * p_old_msg;
+ if (nrf_queue_pop(p_queue, &p_old_msg) == NRF_SUCCESS)
+ {
+ nrf_memobj_put(p_old_msg);
+ dropped++;
+ }
+ }
+
+ return dropped;
+}
+
+
+void nrf_log_backend_flashlog_put(nrf_log_backend_t const * p_backend,
+ nrf_log_entry_t * p_msg)
+{
+ if (m_state == LOG_BACKEND_FLASH_ACTIVE)
+ {
+ nrf_memobj_get(p_msg);
+
+ m_dropped += message_enqueue(mp_flashlog_queue, p_msg);
+
+ if (nrf_atomic_flag_set_fetch(&m_busy_flag) == 0)
+ {
+ log_msg_queue_process(mp_flashlog_queue, false);
+ }
+ }
+}
+
+
+void nrf_log_backend_crashlog_put(nrf_log_backend_t const * p_backend,
+ nrf_log_entry_t * p_msg)
+{
+ if (m_state != LOG_BACKEND_FLASH_INACTIVE)
+ {
+ nrf_memobj_get(p_msg);
+
+ UNUSED_RETURN_VALUE(message_enqueue(mp_crashlog_queue, p_msg));
+ }
+
+ if (m_state == LOG_BACKEND_FLASH_IN_PANIC)
+ {
+ log_msg_queue_process(mp_crashlog_queue, true);
+ }
+}
+
+void nrf_log_backend_flashlog_flush(nrf_log_backend_t const * p_backend)
+{
+ queue_element_drop(mp_flashlog_queue);
+}
+
+void nrf_log_backend_crashlog_flush(nrf_log_backend_t const * p_backend)
+{
+ queue_element_drop(mp_crashlog_queue);
+}
+
+void nrf_log_backend_flashlog_panic_set(nrf_log_backend_t const * p_backend)
+{
+ /* Empty */
+}
+
+/**
+ * @brief Function for injecting log message which will indicate start of crash log.
+ */
+static void crashlog_marker_inject(void)
+{
+ nrf_log_header_t crashlog_marker_hdr = {
+ .base = {
+ .std = {
+ .type = HEADER_TYPE_STD,
+ .severity = NRF_LOG_SEVERITY_INFO_RAW,
+ .nargs = 0,
+ .addr = (uint32_t)crashlog_str & STD_ADDR_MASK
+ }
+ },
+ .module_id = 0,
+ .timestamp = 0,
+ };
+ m_flash_buf[0] = crashlog_marker_hdr.base.raw;
+ m_flash_buf[1] = crashlog_marker_hdr.module_id;
+ m_flash_buf[2] = crashlog_marker_hdr.timestamp;
+ (void)nrf_fstorage_write(&m_log_flash_fstorage, m_curr_addr, m_flash_buf, LOG_HEADER_LEN, NULL);
+ m_curr_addr += LOG_HEADER_LEN;
+}
+
+
+void nrf_log_backend_crashlog_panic_set(nrf_log_backend_t const * p_backend)
+{
+ if (nrf_fstorage_init(&m_log_flash_fstorage, &nrf_fstorage_nvmc, NULL) == NRF_SUCCESS)
+ {
+ m_state = LOG_BACKEND_FLASH_IN_PANIC;
+
+ /* In case of Softdevice MWU may protect access to NVMC. */
+ NVIC_DisableIRQ(MWU_IRQn);
+
+ log_msg_queue_process(mp_flashlog_queue, true);
+
+ crashlog_marker_inject();
+
+ log_msg_queue_process(mp_crashlog_queue, true);
+ }
+ else
+ {
+ m_state = LOG_BACKEND_FLASH_INACTIVE;
+ }
+}
+
+/**
+ * @brief Function for determining first empty location in area dedicated for flash logger backend.
+ */
+static uint32_t empty_addr_get(void)
+{
+ uint32_t token = 0;
+ nrf_log_header_t * p_dummy_header;
+ uint8_t * p_dummy_data;
+
+ while(nrf_log_backend_flash_next_entry_get(&token, &p_dummy_header, &p_dummy_data) == NRF_SUCCESS)
+ {
+
+ }
+
+ return token;
+}
+
+
+ret_code_t nrf_log_backend_flash_init(nrf_fstorage_api_t const * p_fs_api)
+{
+ ret_code_t err_code;
+
+
+ uint32_t start_addr = RUNTIME_START_ADDR;
+ uint32_t end_addr = start_addr + FLASH_LOG_SIZE - 1;
+
+ m_log_flash_fstorage.start_addr = start_addr;
+ m_log_flash_fstorage.end_addr = end_addr;
+
+ err_code = nrf_fstorage_init(&m_log_flash_fstorage, p_fs_api, NULL);
+ if (err_code != NRF_SUCCESS)
+ {
+ return err_code;
+ }
+
+ m_curr_addr = empty_addr_get();
+ m_state = LOG_BACKEND_FLASH_ACTIVE;
+
+ return err_code;
+}
+
+
+ret_code_t nrf_log_backend_flash_next_entry_get(uint32_t * p_token,
+ nrf_log_header_t * * pp_header,
+ uint8_t * * pp_data)
+{
+ uint32_t * p_addr = p_token;
+ uint32_t len;
+
+ *p_addr = (*p_addr == 0) ? RUNTIME_START_ADDR : *p_addr;
+
+ if (nrf_fstorage_rmap(&m_log_flash_fstorage, *p_addr) == NULL)
+ {
+ //Supports only memories which can be mapped for reading.
+ return NRF_ERROR_NOT_SUPPORTED;
+ }
+
+ if (msg_from_buf((uint32_t *)*p_addr, pp_header, pp_data, &len))
+ {
+ *p_addr += len;
+ return NRF_SUCCESS;
+ }
+ else
+ {
+ return NRF_ERROR_NOT_FOUND;
+ }
+}
+
+
+ret_code_t nrf_log_backend_flash_erase(void)
+{
+ ret_code_t err_code;
+
+ m_state = LOG_BACKEND_FLASH_INACTIVE;
+ err_code = nrf_fstorage_erase(&m_log_flash_fstorage, RUNTIME_START_ADDR, NRF_LOG_BACKEND_PAGES, NULL);
+
+ m_curr_addr = RUNTIME_START_ADDR;
+
+ return err_code;
+}
+
+#if NRF_LOG_BACKEND_FLASHLOG_ENABLED
+const nrf_log_backend_api_t nrf_log_backend_flashlog_api = {
+ .put = nrf_log_backend_flashlog_put,
+ .flush = nrf_log_backend_flashlog_flush,
+ .panic_set = nrf_log_backend_flashlog_panic_set,
+};
+#endif
+
+#if NRF_LOG_BACKEND_CRASHLOG_ENABLED
+const nrf_log_backend_api_t nrf_log_backend_crashlog_api = {
+ .put = nrf_log_backend_crashlog_put,
+ .flush = nrf_log_backend_crashlog_flush,
+ .panic_set = nrf_log_backend_crashlog_panic_set,
+};
+#endif
+
+#if NRF_LOG_BACKEND_FLASH_CLI_CMDS
+#include "nrf_cli.h"
+
+static uint8_t m_buffer[64];
+static nrf_cli_t const * mp_cli;
+
+static void cli_tx(void const * p_context, char const * p_buffer, size_t len);
+
+static nrf_fprintf_ctx_t m_fprintf_ctx =
+{
+ .p_io_buffer = (char *)m_buffer,
+ .io_buffer_size = sizeof(m_buffer)-1,
+ .io_buffer_cnt = 0,
+ .auto_flush = true,
+ .p_user_ctx = &mp_cli,
+ .fwrite = cli_tx
+};
+
+
+static void flashlog_clear_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
+{
+ if (nrf_cli_help_requested(p_cli))
+ {
+ nrf_cli_help_print(p_cli, NULL, 0);
+ }
+
+ UNUSED_RETURN_VALUE(nrf_log_backend_flash_erase());
+}
+
+#include "nrf_delay.h"
+static void cli_tx(void const * p_context, char const * p_buffer, size_t len)
+{
+ nrf_cli_t * * pp_cli = (nrf_cli_t * *)p_context;
+ char * p_strbuf = (char *)&p_buffer[len];
+ *p_strbuf = '\0';
+ nrf_cli_fprintf((nrf_cli_t const *)*pp_cli, NRF_CLI_DEFAULT, p_buffer);
+ // nrf_delay_ms(10);
+}
+
+
+static void entry_process(nrf_cli_t const * p_cli, nrf_log_header_t * p_header, uint8_t * p_data)
+{
+ mp_cli = p_cli;
+
+ nrf_log_str_formatter_entry_params_t params =
+ {
+ .timestamp = p_header->timestamp,
+ .module_id = p_header->module_id,
+ .use_colors = 0,
+ };
+
+ switch (p_header->base.generic.type)
+ {
+ case HEADER_TYPE_STD:
+ {
+ params.severity = (nrf_log_severity_t)p_header->base.std.severity;
+ nrf_log_std_entry_process((const char *)((uint32_t)p_header->base.std.addr),
+ (uint32_t *)p_data,
+ p_header->base.std.nargs,
+ &params,
+ &m_fprintf_ctx);
+ break;
+ }
+ case HEADER_TYPE_HEXDUMP:
+ {
+ params.severity = (nrf_log_severity_t)p_header->base.hexdump.severity;
+
+ nrf_log_hexdump_entry_process(p_data,
+ p_header->base.hexdump.len,
+ &params,
+ &m_fprintf_ctx);
+ break;
+ }
+ default:
+ ASSERT(0);
+ }
+
+}
+
+
+static void flashlog_read_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
+{
+ if (nrf_cli_help_requested(p_cli))
+ {
+ nrf_cli_help_print(p_cli, NULL, 0);
+ }
+
+ uint32_t token = 0;
+ uint8_t * p_data = NULL;
+ bool empty = true;
+ nrf_log_header_t * p_header;
+
+ while (1)
+ {
+ if (nrf_log_backend_flash_next_entry_get(&token, &p_header, &p_data) == NRF_SUCCESS)
+ {
+ entry_process(p_cli, p_header, p_data);
+ empty = false;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (empty)
+ {
+ nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Flash log empty\r\n");
+ }
+}
+
+
+static void flashlog_status_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
+{
+ if (nrf_cli_help_requested(p_cli))
+ {
+ nrf_cli_help_print(p_cli, NULL, 0);
+ }
+
+ nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "Flash log status:\r\n");
+ nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "\t\t- Location (address: 0x%08X, length: %d)\r\n",
+ RUNTIME_START_ADDR, FLASH_LOG_SIZE);
+ nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "\t\t- Current usage:%d%% (%d of %d bytes used)\r\n",
+ 100ul * (m_curr_addr - RUNTIME_START_ADDR)/FLASH_LOG_SIZE,
+ m_curr_addr - RUNTIME_START_ADDR,
+ FLASH_LOG_SIZE);
+ nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "\t\t- Dropped logs: %d\r\n", m_dropped);
+
+
+}
+
+
+NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_flashlog_cmd)
+{
+ NRF_CLI_CMD(clear, NULL, "Remove logs", flashlog_clear_cmd),
+ NRF_CLI_CMD(read, NULL, "Read stored logs", flashlog_read_cmd),
+ NRF_CLI_CMD(status, NULL, "Flash log status", flashlog_status_cmd),
+ NRF_CLI_SUBCMD_SET_END
+};
+
+NRF_CLI_CMD_REGISTER(flashlog, &m_flashlog_cmd, "Commands for reading logs stored in non-volatile memory", NULL);
+
+#endif //NRF_LOG_BACKEND_FLASH_CLI_CMDS
+
+#endif //NRF_MODULE_ENABLED(NRF_LOG) && NRF_MODULE_ENABLED(NRF_LOG_BACKEND_FLASH)