text: /**
   * 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};
      size_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)
  