blob: e8dd83296f31d290c8e22e56e9a8313a4ed9099f [file] [log] [blame]
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/shell/shell_log_backend.h>
#include <zephyr/shell/shell.h>
#include "shell_ops.h"
#include <zephyr/logging/log_ctrl.h>
static bool process_msg_from_buffer(const struct shell *sh);
int z_shell_log_backend_output_func(uint8_t *data, size_t length, void *ctx)
{
z_shell_print_stream(ctx, data, length);
return length;
}
/* Set fifo clean state (in case of deferred mode). */
static void fifo_reset(const struct shell_log_backend *backend)
{
mpsc_pbuf_init(backend->mpsc_buffer, backend->mpsc_buffer_config);
}
void z_shell_log_backend_enable(const struct shell_log_backend *backend,
void *ctx, uint32_t init_log_level)
{
int err = 0;
if (IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE)) {
const struct shell *sh;
sh = (const struct shell *)ctx;
z_flag_sync_mode_set(sh, true);
/* Reenable transport in blocking mode */
err = sh->iface->api->enable(sh->iface, true);
}
if (err == 0) {
fifo_reset(backend);
log_backend_enable(backend->backend, ctx, init_log_level);
log_output_ctx_set(backend->log_output, ctx);
backend->control_block->dropped_cnt = 0;
backend->control_block->state = SHELL_LOG_BACKEND_ENABLED;
}
}
void z_shell_log_backend_disable(const struct shell_log_backend *backend)
{
log_backend_disable(backend->backend);
backend->control_block->state = SHELL_LOG_BACKEND_DISABLED;
}
bool z_shell_log_backend_process(const struct shell_log_backend *backend)
{
const struct shell *sh =
(const struct shell *)backend->backend->cb->ctx;
uint32_t dropped;
bool colors = IS_ENABLED(CONFIG_SHELL_VT100_COLORS) &&
z_flag_use_colors_get(sh);
dropped = atomic_set(&backend->control_block->dropped_cnt, 0);
if (dropped) {
struct shell_vt100_colors col;
if (colors) {
z_shell_vt100_colors_store(sh, &col);
z_shell_vt100_color_set(sh, SHELL_VT100_COLOR_RED);
}
log_output_dropped_process(backend->log_output, dropped);
if (colors) {
z_shell_vt100_colors_restore(sh, &col);
}
}
return process_msg_from_buffer(sh);
}
static void panic(const struct log_backend *const backend)
{
const struct shell *sh = (const struct shell *)backend->cb->ctx;
int err;
if (IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE)) {
return;
}
err = sh->iface->api->enable(sh->iface, true);
if (err == 0) {
sh->log_backend->control_block->state =
SHELL_LOG_BACKEND_PANIC;
z_flag_sync_mode_set(sh, true);
/* Move to the start of next line. */
z_shell_multiline_data_calc(&sh->ctx->vt100_ctx.cons,
sh->ctx->cmd_buff_pos,
sh->ctx->cmd_buff_len);
z_shell_op_cursor_vert_move(sh, -1);
z_shell_op_cursor_horiz_move(sh,
-sh->ctx->vt100_ctx.cons.cur_x);
while (process_msg_from_buffer(sh)) {
/* empty */
}
} else {
z_shell_log_backend_disable(sh->log_backend);
}
}
static void dropped(const struct log_backend *const backend, uint32_t cnt)
{
const struct shell *sh = (const struct shell *)backend->cb->ctx;
const struct shell_log_backend *log_backend = sh->log_backend;
if (IS_ENABLED(CONFIG_SHELL_STATS)) {
atomic_add(&sh->stats->log_lost_cnt, cnt);
}
atomic_add(&log_backend->control_block->dropped_cnt, cnt);
}
static void copy_to_pbuffer(struct mpsc_pbuf_buffer *mpsc_buffer,
union log_msg_generic *msg, uint32_t timeout)
{
size_t wlen;
union mpsc_pbuf_generic *dst;
wlen = log_msg_generic_get_wlen((union mpsc_pbuf_generic *)msg);
dst = mpsc_pbuf_alloc(mpsc_buffer, wlen, K_MSEC(timeout));
if (!dst) {
/* No space to store the log */
return;
}
/* First word contains internal mpsc packet flags and when copying
* those flags must be omitted.
*/
uint8_t *dst_data = (uint8_t *)dst + sizeof(struct mpsc_pbuf_hdr);
uint8_t *src_data = (uint8_t *)msg + sizeof(struct mpsc_pbuf_hdr);
size_t hdr_wlen = ceiling_fraction(sizeof(struct mpsc_pbuf_hdr),
sizeof(uint32_t));
dst->hdr.data = msg->buf.hdr.data;
memcpy(dst_data, src_data, (wlen - hdr_wlen) * sizeof(uint32_t));
mpsc_pbuf_commit(mpsc_buffer, dst);
}
static void process_log_msg(const struct shell *sh,
const struct log_output *log_output,
union log_msg_generic *msg,
bool locked, bool colors)
{
unsigned int key = 0;
uint32_t flags = LOG_OUTPUT_FLAG_LEVEL |
LOG_OUTPUT_FLAG_TIMESTAMP |
LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP;
if (colors) {
flags |= LOG_OUTPUT_FLAG_COLORS;
}
if (locked) {
/*
* If running in the thread context, lock the shell mutex to synchronize with
* messages printed on the shell thread. In the ISR context, using a mutex is
* forbidden so use the IRQ lock to at least synchronize log messages printed
* in different contexts.
*/
if (k_is_in_isr()) {
key = irq_lock();
} else {
k_mutex_lock(&sh->ctx->wr_mtx, K_FOREVER);
}
if (!z_flag_cmd_ctx_get(sh)) {
z_shell_cmd_line_erase(sh);
}
}
log_output_msg_process(log_output, &msg->log, flags);
if (locked) {
if (!z_flag_cmd_ctx_get(sh)) {
z_shell_print_prompt_and_cmd(sh);
}
if (k_is_in_isr()) {
irq_unlock(key);
} else {
k_mutex_unlock(&sh->ctx->wr_mtx);
}
}
}
static bool process_msg_from_buffer(const struct shell *sh)
{
const struct shell_log_backend *log_backend = sh->log_backend;
struct mpsc_pbuf_buffer *mpsc_buffer = log_backend->mpsc_buffer;
const struct log_output *log_output = log_backend->log_output;
union log_msg_generic *msg;
bool colors = IS_ENABLED(CONFIG_SHELL_VT100_COLORS) &&
z_flag_use_colors_get(sh);
msg = (union log_msg_generic *)mpsc_pbuf_claim(mpsc_buffer);
if (!msg) {
return false;
}
process_log_msg(sh, log_output, msg, false, colors);
mpsc_pbuf_free(mpsc_buffer, &msg->buf);
return true;
}
static void process(const struct log_backend *const backend,
union log_msg_generic *msg)
{
const struct shell *sh = (const struct shell *)backend->cb->ctx;
const struct shell_log_backend *log_backend = sh->log_backend;
struct mpsc_pbuf_buffer *mpsc_buffer = log_backend->mpsc_buffer;
const struct log_output *log_output = log_backend->log_output;
bool colors = IS_ENABLED(CONFIG_SHELL_VT100_COLORS) &&
z_flag_use_colors_get(sh);
struct k_poll_signal *signal;
switch (sh->log_backend->control_block->state) {
case SHELL_LOG_BACKEND_ENABLED:
if (IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE)) {
process_log_msg(sh, log_output, msg, true, colors);
} else {
copy_to_pbuffer(mpsc_buffer, msg,
log_backend->timeout);
if (IS_ENABLED(CONFIG_MULTITHREADING)) {
signal =
&sh->ctx->signals[SHELL_SIGNAL_LOG_MSG];
k_poll_signal_raise(signal, 0);
}
}
break;
case SHELL_LOG_BACKEND_PANIC:
z_shell_cmd_line_erase(sh);
process_log_msg(sh, log_output, msg, true, colors);
break;
case SHELL_LOG_BACKEND_DISABLED:
__fallthrough;
default:
break;
}
}
const struct log_backend_api log_backend_shell_api = {
.process = process,
.dropped = dropped,
.panic = panic,
};