logging: move backends to subdirectory

This is mainly to reduce clutter in `subsys/logging`, but also to make
backend management slightly easier.

Signed-off-by: Christopher Friedt <cfriedt@fb.com>
This commit is contained in:
Christopher Friedt 2022-09-24 13:39:42 -04:00 committed by Christopher Friedt
commit f5587552bb
29 changed files with 584 additions and 545 deletions

View file

@ -0,0 +1,66 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_ADSP
log_backend_adsp.c
)
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_ADSP_HDA
log_backend_adsp_hda.c
)
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_ADSP_MTRACE
log_backend_adsp_mtrace.c
)
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_EFI_CONSOLE
log_backend_efi_console.c
)
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_FS
log_backend_fs.c
)
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_NATIVE_POSIX
log_backend_native_posix.c
)
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_NET
log_backend_net.c
)
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_RTT
log_backend_rtt.c
)
zephyr_include_directories_ifdef(
CONFIG_LOG_BACKEND_SPINEL
${ZEPHYR_BASE}/subsys/net/lib/openthread/platform/
)
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_SPINEL
log_backend_spinel.c
)
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_SWO
log_backend_swo.c
)
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_UART
log_backend_uart.c
)
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_XTENSA_SIM
log_backend_xtensa_sim.c
)

View file

@ -0,0 +1,19 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
menu "Backends"
rsource "Kconfig.adsp"
rsource "Kconfig.adsp_hda"
rsource "Kconfig.adsp_mtrace"
rsource "Kconfig.efi_console"
rsource "Kconfig.fs"
rsource "Kconfig.native_posix"
rsource "Kconfig.net"
rsource "Kconfig.rtt"
rsource "Kconfig.spinel"
rsource "Kconfig.swo"
rsource "Kconfig.uart"
rsource "Kconfig.xtensa_sim"
endmenu

View file

@ -0,0 +1,18 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config LOG_BACKEND_ADSP
bool "Intel ADSP buffer backend"
depends on SOC_FAMILY_INTEL_ADSP
select LOG_OUTPUT
help
Enable backend for the host trace protocol of the Intel ADSP
family of audio processors
if LOG_BACKEND_ADSP
backend = ADSP
backend-str = adsp
source "subsys/logging/Kconfig.template.log_format_config"
endif # LOG_BACKEND_ADSP

View file

@ -0,0 +1,52 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config LOG_BACKEND_ADSP_HDA
bool "Intel ADSP HDA backend"
depends on SOC_FAMILY_INTEL_ADSP && DMA && DMA_INTEL_ADSP_HDA_HOST_OUT
select LOG_OUTPUT
help
Provide a logging backend which writes to a buffer and
periodically flushes to hardware using ringbuffer like
semantics provided by DMA_INTEL_ADSP_HDA.
if LOG_BACKEND_ADSP_HDA
backend = ADSP_HDA
backend-str = adsp_hda
source "subsys/logging/Kconfig.template.log_format_config"
config LOG_BACKEND_ADSP_HDA_SIZE
int "Size of ring buffer"
range 128 8192
default 4096
help
Sets the ring buffer size cAVS HDA uses for logging. Effectively
determines how many log messages may be written to in a period of time.
The period of time is decided by how often to inform the hardware of
writes to the buffer.
config LOG_BACKEND_ADSP_HDA_FLUSH_TIME
int "Time in milliseconds to periodically flush writes to hardware"
range 1 10000
default 500
help
The Intel ADSP HDA backend periodically writes out its buffer contents
to hardware by informing the DMA hardware the contents of the ring
buffer.
config LOG_BACKEND_ADSP_HDA_CAVSTOOL
bool "Log backend is to be used with cavstool"
help
Use cavstool understood IPC messages to inform setup and logging of
HDA messages.
config LOG_BACKEND_ADSP_HDA_PADDING
bool "Log backend should pad the buffer with \0 characters when flushing"
help
HDA requires transfers be 128 byte aligned such that a partial message may
never make it across unless padded with \0 characters to the next 128 byte
aligned address. This may or may not work depending on the log format
being used but should certainly work with text based logging.
endif # LOG_BACKEND_ADSP_HDA

View file

@ -0,0 +1,18 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config LOG_BACKEND_ADSP_MTRACE
bool "Intel ADSP mtrace backend"
depends on SOC_FAMILY_INTEL_ADSP
select LOG_OUTPUT
help
Provide a logging backend which writes to SRAM window
using the SOF Linux driver mtrace buffer layout.
if LOG_BACKEND_ADSP_MTRACE
backend = ADSP_MTRACE
backend-str = adsp_mtrace
source "subsys/logging/Kconfig.template.log_format_config"
endif # LOG_BACKEND_ADSP_MTRACE

View file

@ -0,0 +1,18 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config LOG_BACKEND_EFI_CONSOLE
bool "EFI_CONSOLE backend"
depends on X86_EFI_CONSOLE
select LOG_OUTPUT
default y if !UART_CONSOLE
help
When enabled backend is using EFI CONSOLE to output logs.
if LOG_BACKEND_EFI_CONSOLE
backend = EFI_CON
backend-str = efi_console
source "subsys/logging/Kconfig.template.log_format_config"
endif # LOG_BACKEND_EFI_CONSOLE

View file

@ -0,0 +1,54 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config LOG_BACKEND_FS
bool "File system backend"
depends on FILE_SYSTEM
select LOG_OUTPUT
help
When enabled, backend is using the configured file system to output logs.
As the file system must be mounted for the logging to work, it must be
either configured for auto-mount or manually mounted by the application.
Log messages are discarded as long as the file system is not mounted.
if LOG_BACKEND_FS
backend = FS
backend-str = fs
source "subsys/logging/Kconfig.template.log_format_config"
config LOG_BACKEND_FS_OVERWRITE
bool "Old log files overwrite"
default y
help
When enabled backend overwrites oldest log files.
In other case, when memory is full, new messages are dropped.
config LOG_BACKEND_FS_FILE_PREFIX
string "Log file name prefix"
default "log."
help
User defined name of log files saved in the file system.
The prefix is followed by the number of log file.
config LOG_BACKEND_FS_DIR
string "Log directory"
default "/lfs1"
help
Directory to which log files will be written.
config LOG_BACKEND_FS_FILE_SIZE
int "User defined log file size"
default 4096
range 128 1073741824
help
Max log file size (in bytes).
config LOG_BACKEND_FS_FILES_LIMIT
int "Max number of files containing logs"
default 10
help
Limit of number of files with logs. It is also limited by
size of file system partition.
endif # LOG_BACKEND_FS

View file

@ -0,0 +1,18 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config LOG_BACKEND_NATIVE_POSIX
bool "Native backend"
depends on ARCH_POSIX
default y if !SERIAL
select LOG_OUTPUT
help
Enable backend in native_posix
if LOG_BACKEND_NATIVE_POSIX
backend = NATIVE_POSIX
backend-str = native_posix
source "subsys/logging/Kconfig.template.log_format_config"
endif # LOG_BACKEND_NATIVE_POSIX

View file

@ -0,0 +1,66 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
# Immediate mode cannot be used with network backend as it would cause the sent
# rsyslog message to be malformed.
config LOG_BACKEND_NET
bool "Networking backend"
depends on NETWORKING && NET_UDP && !LOG_MODE_IMMEDIATE
select NET_CONTEXT_NET_PKT_POOL
select LOG_OUTPUT
help
Send syslog messages to network server.
See RFC 5424 (syslog protocol) and RFC 5426 (syslog over UDP)
specifications for details.
if LOG_BACKEND_NET
config LOG_BACKEND_NET_SERVER
string "Syslog server IP address"
help
This can be either IPv4 or IPv6 address.
Server listen UDP port number can be configured here too.
Following syntax is supported:
192.0.2.1:514
192.0.2.42
[2001:db8::1]:514
[2001:db8::2]
2001:db::42
config LOG_BACKEND_NET_MAX_BUF
int "How many network buffers to allocate for sending messages"
range 3 256
default 3
help
Each syslog message should fit into a network packet that will be
sent to server. This number tells how many syslog messages can be
in transit to the server.
config LOG_BACKEND_NET_MAX_BUF_SIZE
int "Max syslog message size"
range 64 1180
default 1180 if NET_IPV6
default 480 if NET_IPV4
default 256
help
As each syslog message needs to fit to UDP packet, set this value
so that messages are not truncated.
The RFC 5426 recommends that for IPv4 the size is 480 octets and for
IPv6 the size is 1180 octets. As each buffer will use RAM, the value
should be selected so that typical messages will fit the buffer.
config LOG_BACKEND_NET_AUTOSTART
bool "Automatically start networking backend"
default y if NET_CONFIG_NEED_IPV4 || NET_CONFIG_NEED_IPV6
help
When enabled automatically start the networking backend on
application start. If no routes to the logging server are available
on application startup, this must be set to n and the backend must be
started by the application later on. Otherwise the logging
thread might block.
backend = NET
backend-str = net
source "subsys/logging/Kconfig.template.log_format_config"
endif # LOG_BACKEND_NET

View file

@ -0,0 +1,108 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config LOG_BACKEND_RTT
bool "Segger J-Link RTT backend"
depends on USE_SEGGER_RTT
default y if !SHELL_BACKEND_RTT
select SEGGER_RTT_CUSTOM_LOCKING
select LOG_OUTPUT
help
When enabled, backend will use RTT for logging. This backend works on a per
message basis. Only a whole message (terminated with a carriage return: '\r')
is transferred to up-buffer at once depending on available space and
selected mode.
In panic mode backend always blocks and waits until there is space
in up-buffer for a message and message is transferred to host.
if LOG_BACKEND_RTT
choice LOG_BACKEND_RTT_MODE
prompt "Logger behavior"
default LOG_BACKEND_RTT_MODE_BLOCK
config LOG_BACKEND_RTT_MODE_DROP
bool "Drop messages that do not fit in up-buffer."
help
If there is not enough space in up-buffer for a message, drop it.
Number of dropped messages will be logged.
Increase up-buffer size helps to reduce dropping of messages.
config LOG_BACKEND_RTT_MODE_BLOCK
bool "Block until message is transferred to host."
help
Waits until there is enough space in the up-buffer for a message.
config LOG_BACKEND_RTT_MODE_OVERWRITE
bool "Overwrite messages if up-buffer full"
help
If there is not enough space in up-buffer for a message overwrite
oldest one.
endchoice
backend = RTT
backend-str = rtt
source "subsys/logging/Kconfig.template.log_format_config"
config LOG_BACKEND_RTT_MESSAGE_SIZE
int "Size of internal buffer for storing messages."
range 32 256
default 128
depends on LOG_BACKEND_RTT_MODE_DROP
help
This option defines maximum message size transferable to up-buffer.
if LOG_BACKEND_RTT_MODE_BLOCK
config LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE
int "Size of the output buffer"
default 16
help
Buffer is used by log_output module for preparing output data (e.g.
string formatting).
config LOG_BACKEND_RTT_RETRY_CNT
int "Number of retries"
default 4
help
Number of TX retries before dropping the data and assuming that
RTT session is inactive.
config LOG_BACKEND_RTT_RETRY_DELAY_MS
int "Delay between TX retries in milliseconds"
default 5
help
Sleep period between TX retry attempts. During RTT session, host pulls
data periodically. Period starts from 1-2 milliseconds and can be
increased if traffic on RTT increases (also from host to device). In
case of heavy traffic data can be lost and it may be necessary to
increase delay or number of retries.
endif # LOG_BACKEND_RTT_MODE_BLOCK
config LOG_BACKEND_RTT_BUFFER
int "Buffer number used for logger output."
range 0 SEGGER_RTT_MAX_NUM_UP_BUFFERS
default 0
help
Select index of up-buffer used for logger output, by default it uses
terminal up-buffer and its settings.
config LOG_BACKEND_RTT_BUFFER_SIZE
int "Size of reserved up-buffer for logger output."
default 1024
depends on LOG_BACKEND_RTT_BUFFER > 0
help
Specify reserved size of up-buffer used for logger output.
# Enable processing of printk calls using log if terminal buffer is used.
# Same buffer is used by RTT console. If printk would go through RTT console
# that will lead to corruption of RTT data which is not protected against being
# interrupted by an interrupt.
config LOG_BACKEND_RTT_FORCE_PRINTK
bool
default y if LOG_BACKEND_RTT_BUFFER = 0 && RTT_CONSOLE
select LOG_PRINTK
endif # LOG_BACKEND_RTT

View file

@ -0,0 +1,27 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config LOG_BACKEND_SPINEL
bool "OpenThread dedicated Spinel protocol backend"
depends on !LOG_BACKEND_UART
depends on NET_L2_OPENTHREAD
select LOG_OUTPUT
help
When enabled, backend will use OpenThread dedicated SPINEL protocol for logging.
This protocol is byte oriented and wraps given messages into serial frames.
Backend should be enabled only to OpenThread purposes and when UART backend is disabled
or works on another UART device to avoid interference.
if LOG_BACKEND_SPINEL
config LOG_BACKEND_SPINEL_BUFFER_SIZE
int "Size of reserved up-buffer for logger output."
default 64
help
Specify reserved size of up-buffer used for logger output.
backend = SPINEL
backend-str = spinel
source "subsys/logging/Kconfig.template.log_format_config"
endif # LOG_BACKEND_SPINEL

View file

@ -0,0 +1,31 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config LOG_BACKEND_SWO
bool "Serial Wire Output (SWO) backend"
depends on HAS_SWO
select LOG_OUTPUT
help
When enabled, backend will use SWO for logging.
if LOG_BACKEND_SWO
config LOG_BACKEND_SWO_FREQ_HZ
int "Set SWO output frequency"
default 0
help
Set SWO output frequency. Value 0 will select maximum frequency
supported by the given MCU. Not all debug probes support high
frequency SWO operation. In this case the frequency has to be set
manually.
SWO value defined by this option will be configured at boot. Most SWO
viewer programs will configure SWO frequency when attached to the
debug probe. Such configuration will persist only until the device
reset. To ensure flawless operation the frequency configured here and
by the SWO viewer program has to match.
backend = SWO
backend-str = swo
source "subsys/logging/Kconfig.template.log_format_config"
endif # LOG_BACKEND_SWO

View file

@ -0,0 +1,51 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config LOG_BACKEND_UART
bool "UART backend"
depends on UART_CONSOLE
default y if !SHELL_BACKEND_SERIAL
select LOG_OUTPUT
help
When enabled backend is using UART to output logs.
if LOG_BACKEND_UART
config LOG_BACKEND_UART_ASYNC
bool "Use UART Asynchronous API"
depends on UART_ASYNC_API
depends on !LOG_BACKEND_UART_OUTPUT_DICTIONARY_HEX
config LOG_BACKEND_UART_BUFFER_SIZE
int "Number of bytes to buffer in RAM before flushing"
default 32 if LOG_BACKEND_UART_ASYNC
default 1
help
Sets the number of bytes which can be buffered in RAM before log_output_flush
is automatically called on the backend.
backend = UART
backend-str = uart
source "subsys/logging/Kconfig.template.log_format_config"
if LOG_BACKEND_UART_OUTPUT_DICTIONARY
choice
prompt "Dictionary mode output format"
default LOG_BACKEND_UART_OUTPUT_DICTIONARY_BIN
config LOG_BACKEND_UART_OUTPUT_DICTIONARY_BIN
bool "Dictionary (binary)"
help
Dictionary-based logging output in binary.
config LOG_BACKEND_UART_OUTPUT_DICTIONARY_HEX
bool "Dictionary (hexadecimal)"
help
Dictionary-based logging output in hexadecimal. Supported only for UART backend.
endchoice
endif # LOG_BACKEND_UART_OUTPUT_DICTIONARY
endif # LOG_BACKEND_UART

View file

@ -0,0 +1,26 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config LOG_BACKEND_XTENSA_SIM
bool "Xtensa simulator backend"
depends on SOC_XTENSA_SAMPLE_CONTROLLER || SOC_FAMILY_INTEL_ADSP
default y if SOC_XTENSA_SAMPLE_CONTROLLER
select LOG_OUTPUT
help
Enable backend in xtensa simulator
config LOG_BACKEND_XTENSA_OUTPUT_BUFFER_SIZE
int "Size of the output buffer"
default 16
depends on LOG_BACKEND_XTENSA_SIM
help
Buffer is used by log_output module for preparing output data (e.g.
string formatting).
if LOG_BACKEND_XTENSA_SIM
backend = XTENSA_SIM
backend-str = xtensa_sim
source "subsys/logging/Kconfig.template.log_format_config"
endif # LOG_BACKEND_XTENSA_SIM

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log_backend.h>
#include <zephyr/logging/log_core.h>
#include <zephyr/logging/log_output.h>
#include <zephyr/logging/log_backend_std.h>
/*
* A lock is needed as log_process() and log_panic() have no internal locks
* to prevent concurrency. Meaning if log_process is called after
* log_panic was called previously, log_process may happen from another
* CPU and calling context than the log processing thread running in
* the background. On an SMP system this is a race.
*
* This caused a race on the output trace such that the logging output
* was garbled and useless.
*/
static struct k_spinlock lock;
static uint32_t log_format_current = CONFIG_LOG_BACKEND_ADSP_OUTPUT_DEFAULT;
void intel_adsp_trace_out(int8_t *str, size_t len);
static int char_out(uint8_t *data, size_t length, void *ctx)
{
intel_adsp_trace_out(data, length);
return length;
}
/**
* 80 bytes seems to catch most sensibly sized log message lines
* in one go letting the trace out call output whole complete
* lines. This avoids the overhead of a spin lock in the trace_out
* more often as well as avoiding entwined characters from printk if
* LOG_PRINTK=n.
*/
#define LOG_BUF_SIZE 80
static uint8_t log_buf[LOG_BUF_SIZE];
LOG_OUTPUT_DEFINE(log_output_adsp, char_out, log_buf, sizeof(log_buf));
static uint32_t format_flags(void)
{
uint32_t flags = LOG_OUTPUT_FLAG_LEVEL | LOG_OUTPUT_FLAG_TIMESTAMP;
if (IS_ENABLED(CONFIG_LOG_BACKEND_FORMAT_TIMESTAMP)) {
flags |= LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP;
}
return flags;
}
static void panic(struct log_backend const *const backend)
{
k_spinlock_key_t key = k_spin_lock(&lock);
log_backend_std_panic(&log_output_adsp);
k_spin_unlock(&lock, key);
}
static inline void dropped(const struct log_backend *const backend,
uint32_t cnt)
{
log_output_dropped_process(&log_output_adsp, cnt);
}
static void process(const struct log_backend *const backend,
union log_msg_generic *msg)
{
log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
k_spinlock_key_t key = k_spin_lock(&lock);
log_output_func(&log_output_adsp, &msg->log, format_flags());
k_spin_unlock(&lock, key);
}
static int format_set(const struct log_backend *const backend, uint32_t log_type)
{
log_format_current = log_type;
return 0;
}
const struct log_backend_api log_backend_adsp_api = {
.process = process,
.dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
.panic = panic,
.format_set = format_set,
};
LOG_BACKEND_DEFINE(log_backend_adsp, log_backend_adsp_api, true);

View file

@ -0,0 +1,376 @@
/*
* Copyright (c) 2022 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/arch/xtensa/cache.h>
#include <zephyr/logging/log_backend.h>
#include <zephyr/logging/log_core.h>
#include <zephyr/logging/log_output.h>
#include <zephyr/logging/log_output_dict.h>
#include <zephyr/logging/log_backend_std.h>
#include <zephyr/logging/log_backend_adsp_hda.h>
#include <zephyr/drivers/dma.h>
#include <zephyr/kernel.h>
static uint32_t log_format_current = CONFIG_LOG_BACKEND_ADSP_HDA_OUTPUT_DEFAULT;
static const struct device *const hda_log_dev =
DEVICE_DT_GET(DT_NODELABEL(hda_host_in));
static uint32_t hda_log_chan;
/*
* HDA requires 128 byte aligned data and 128 byte aligned transfers.
*/
#define ALIGNMENT DMA_BUF_ALIGNMENT(DT_NODELABEL(hda_host_in))
static __aligned(ALIGNMENT) uint8_t hda_log_buf[CONFIG_LOG_BACKEND_ADSP_HDA_SIZE];
static volatile uint32_t hda_log_buffered;
static struct k_timer hda_log_timer;
static adsp_hda_log_hook_t hook;
static uint32_t write_idx;
static struct k_spinlock hda_log_lock;
/* atomic bit flags for state */
#define HDA_LOG_DMA_READY 0
#define HDA_LOG_PANIC_MODE 1
static atomic_t hda_log_flags;
/**
* Flush the buffered log to the HDA stream
*
* If @kconfig{CONFIG_LOG_BACKEND_ADSP_HDA_PADDING} is enabled
* the buffer is extended with '\0' characters to align the buffer
* to 128 bytes.
*/
static uint32_t hda_log_flush(void)
{
if (hda_log_buffered == 0) {
return 0;
}
uint32_t nearest128 = hda_log_buffered & ~((128) - 1);
#ifdef CONFIG_LOG_BACKEND_ADSP_HDA_PADDING
if (nearest128 != hda_log_buffered) {
uint32_t next128 = nearest128 + 128;
uint32_t padding = next128 - hda_log_buffered;
for (int i = 0; i < padding; i++) {
hda_log_buf[write_idx] = '\0';
hda_log_buffered++;
write_idx++;
}
nearest128 = hda_log_buffered & ~((128) - 1);
}
__ASSERT(hda_log_buffered == nearest128,
"Expected flush length to be 128 byte aligned");
#endif
#if !(IS_ENABLED(CONFIG_KERNEL_COHERENCE))
z_xtensa_cache_flush(hda_log_buf, CONFIG_LOG_BACKEND_ADSP_HDA_SIZE);
#endif
dma_reload(hda_log_dev, hda_log_chan, 0, 0, nearest128);
hda_log_buffered = hda_log_buffered - nearest128;
return nearest128;
}
static int hda_log_out(uint8_t *data, size_t length, void *ctx)
{
int ret;
bool do_log_flush;
struct dma_status status;
k_spinlock_key_t key = k_spin_lock(&hda_log_lock);
/* Defaults when DMA not yet initialized */
uint32_t dma_free = sizeof(hda_log_buf);
uint32_t write_pos = 0;
if (atomic_test_bit(&hda_log_flags, HDA_LOG_DMA_READY)) {
ret = dma_get_status(hda_log_dev, hda_log_chan, &status);
if (ret != 0) {
ret = length;
goto out;
}
/* The hardware tells us what space we have available, and where to
* start writing. If the buffer is full we have no space.
*/
if (status.free <= 0) {
ret = length;
goto out;
}
dma_free = status.free;
write_pos = status.write_position;
}
/* Account for buffered writes since last dma_reload
*
* No underflow should be possible here, status.free is the apparent
* free space in the buffer from the DMA's read/write positions.
* When dma_reload is called status.free may be reduced by
* the nearest 128 divisible value of hda_log_buffered,
* where hda_log_buffered is then subtracted by the same amount.
* After which status.free should only increase in value.
*
* Assert this trueth though, just in case.
*/
__ASSERT_NO_MSG(dma_free > hda_log_buffered);
uint32_t available = dma_free - hda_log_buffered;
/* If there isn't enough space for the message there's an overflow */
if (available < length) {
ret = 0;
goto out;
}
/* Copy over the message to the buffer */
write_idx = write_pos + hda_log_buffered;
if (write_idx > sizeof(hda_log_buf)) {
write_idx -= sizeof(hda_log_buf);
}
size_t copy_len = (write_idx + length) < sizeof(hda_log_buf) ? length
: sizeof(hda_log_buf) - write_idx;
memcpy(&hda_log_buf[write_idx], data, copy_len);
write_idx += copy_len;
/* There may be a wrapped copy */
size_t wrap_copy_len = length - copy_len;
if (wrap_copy_len != 0) {
memcpy(&hda_log_buf[0], &data[copy_len], wrap_copy_len);
write_idx = wrap_copy_len;
}
ret = length;
hda_log_buffered += length;
uint32_t written = 0;
out:
/* If DMA_READY changes from unset to set during this call, that is
* perfectly acceptable. The default values for write_pos and dma_free
* are the correct values if that occurs.
*/
do_log_flush = ((hda_log_buffered > sizeof(hda_log_buf)/2) ||
atomic_test_bit(&hda_log_flags, HDA_LOG_PANIC_MODE))
&& atomic_test_bit(&hda_log_flags, HDA_LOG_DMA_READY);
/* Flush if there's a hook set AND dma is ready */
if (do_log_flush && hook != NULL) {
written = hda_log_flush();
}
k_spin_unlock(&hda_log_lock, key);
/* The hook may have log calls and needs to be done outside of the spin
* lock to avoid recursion on the spin lock (deadlocks) in cases of
* direct logging.
*/
if (hook != NULL && written > 0) {
hook(written);
}
return ret;
}
/**
* 128 bytes is the smallest transferrable size for HDA so use that
* and encompass almost all log lines in the formatter before flushing
* and memcpy'ing to the HDA buffer.
*/
#define LOG_BUF_SIZE 128
static uint8_t log_buf[LOG_BUF_SIZE];
LOG_OUTPUT_DEFINE(log_output_adsp_hda, hda_log_out, log_buf, LOG_BUF_SIZE);
static void hda_log_periodic(struct k_timer *tm)
{
ARG_UNUSED(tm);
uint32_t written = 0;
k_spinlock_key_t key = k_spin_lock(&hda_log_lock);
if (hook != NULL) {
written = hda_log_flush();
}
/* The hook may have log calls and needs to be done outside of the spin
* lock to avoid recursion on the spin lock (deadlocks) in cases of
* direct logging.
*/
if (hook != NULL && written > 0) {
hook(written);
}
k_spin_unlock(&hda_log_lock, key);
}
static inline void dropped(const struct log_backend *const backend,
uint32_t cnt)
{
ARG_UNUSED(backend);
if (IS_ENABLED(CONFIG_LOG_DICTIONARY_SUPPORT)) {
log_dict_output_dropped_process(&log_output_adsp_hda, cnt);
} else {
log_output_dropped_process(&log_output_adsp_hda, cnt);
}
}
static void panic(struct log_backend const *const backend)
{
ARG_UNUSED(backend);
/* will immediately flush all future writes once set */
atomic_set_bit(&hda_log_flags, HDA_LOG_PANIC_MODE);
/* flushes the log queue */
log_backend_std_panic(&log_output_adsp_hda);
}
static int format_set(const struct log_backend *const backend, uint32_t log_type)
{
ARG_UNUSED(backend);
log_format_current = log_type;
return 0;
}
static volatile uint32_t counter;
static void process(const struct log_backend *const backend,
union log_msg_generic *msg)
{
ARG_UNUSED(backend);
uint32_t flags = log_backend_std_get_flags();
log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
log_output_func(&log_output_adsp_hda, &msg->log, flags);
}
/**
* Lazily initialized, while the DMA may not be setup we continue
* to buffer log messages untilt he buffer is full.
*/
static void init(const struct log_backend *const backend)
{
ARG_UNUSED(backend);
hda_log_buffered = 0;
}
const struct log_backend_api log_backend_adsp_hda_api = {
.process = process,
.dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
.panic = panic,
.format_set = format_set,
.init = init,
};
LOG_BACKEND_DEFINE(log_backend_adsp_hda, log_backend_adsp_hda_api, true);
void adsp_hda_log_init(adsp_hda_log_hook_t fn, uint32_t channel)
{
hook = fn;
int res;
__ASSERT(device_is_ready(hda_log_dev), "DMA device is not ready");
hda_log_chan = dma_request_channel(hda_log_dev, &channel);
__ASSERT(hda_log_chan >= 0, "No valid DMA channel");
__ASSERT(hda_log_chan == channel, "Not requested channel");
hda_log_buffered = 0;
/* configure channel */
struct dma_block_config hda_log_dma_blk_cfg = {
.block_size = CONFIG_LOG_BACKEND_ADSP_HDA_SIZE,
.source_address = (uint32_t)(uintptr_t)&hda_log_buf,
};
struct dma_config hda_log_dma_cfg = {
.channel_direction = MEMORY_TO_HOST,
.block_count = 1,
.head_block = &hda_log_dma_blk_cfg,
.source_data_size = 4,
};
res = dma_config(hda_log_dev, hda_log_chan, &hda_log_dma_cfg);
__ASSERT(res == 0, "DMA config failed");
res = dma_start(hda_log_dev, hda_log_chan);
__ASSERT(res == 0, "DMA start failed");
atomic_set_bit(&hda_log_flags, HDA_LOG_DMA_READY);
k_timer_init(&hda_log_timer, hda_log_periodic, NULL);
k_timer_start(&hda_log_timer,
K_MSEC(CONFIG_LOG_BACKEND_ADSP_HDA_FLUSH_TIME),
K_MSEC(CONFIG_LOG_BACKEND_ADSP_HDA_FLUSH_TIME));
}
#ifdef CONFIG_LOG_BACKEND_ADSP_HDA_CAVSTOOL
#include <intel_adsp_ipc.h>
#include <cavstool.h>
#define CHANNEL 6
#define IPC_TIMEOUT K_MSEC(1500)
static inline void hda_ipc_msg(const struct device *dev, uint32_t data,
uint32_t ext, k_timeout_t timeout)
{
__ASSERT(intel_adsp_ipc_send_message_sync(dev, data, ext, timeout),
"Unexpected ipc send message failure, try increasing IPC_TIMEOUT");
}
void adsp_hda_log_cavstool_hook(uint32_t written)
{
/* We *must* send this, but we may be in a timer ISR, so we are
* forced into a retry loop without timeouts and such.
*/
bool done = false;
/* Send IPC message notifying log data has been written */
do {
done = intel_adsp_ipc_send_message(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_PRINT,
(written << 8) | CHANNEL);
} while (!done);
/* Wait for confirmation log data has been received */
do {
done = intel_adsp_ipc_is_complete(INTEL_ADSP_IPC_HOST_DEV);
} while (!done);
}
int adsp_hda_log_cavstool_init(const struct device *dev)
{
ARG_UNUSED(dev);
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_RESET, CHANNEL, IPC_TIMEOUT);
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_CONFIG,
CHANNEL | (CONFIG_LOG_BACKEND_ADSP_HDA_SIZE << 8), IPC_TIMEOUT);
adsp_hda_log_init(adsp_hda_log_cavstool_hook, CHANNEL);
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_START, CHANNEL, IPC_TIMEOUT);
return 0;
}
SYS_INIT(adsp_hda_log_cavstool_init, POST_KERNEL, 99);
#endif /* CONFIG_LOG_BACKEND_ADSP_HDA_CAVSTOOL */

View file

@ -0,0 +1,229 @@
/*
* Copyright (c) 2019,2022 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log_backend_adsp_mtrace.h>
#include <zephyr/logging/log_backend.h>
#include <zephyr/logging/log_core.h>
#include <zephyr/logging/log_output.h>
#include <zephyr/logging/log_backend_std.h>
#include <zephyr/kernel.h>
#include <soc.h>
#include <adsp_memory.h>
#include <adsp_debug_window.h>
/*
* A lock is needed as log_process() and log_panic() have no internal locks
* to prevent concurrency. Meaning if log_process is called after
* log_panic was called previously, log_process may happen from another
* CPU and calling context than the log processing thread running in
* the background. On an SMP system this is a race.
*
* This caused a race on the output trace such that the logging output
* was garbled and useless.
*/
static struct k_spinlock mtrace_lock;
static uint32_t log_format_current = CONFIG_LOG_BACKEND_ADSP_MTRACE_OUTPUT_DEFAULT;
static adsp_mtrace_log_hook_t mtrace_hook;
static bool mtrace_active;
static bool mtrace_panic_mode;
/*
* SRAM window for debug info is organized to equal size slots.
* The descriptors on first page describe the layout of slots.
* One type of debug info slot is ADSP_DBG_WIN_SLOT_DEBUG_LOG.
* These slots (if defined) can be used for mtrace output.
*
* The log buffer slots have the following layout:
*
* u32 host_read_ptr;
* u32 dsp_write_ptr;
* u8 buffer[];
*
* The two pointers are offsets within the buffer. Buffer is empty
* when the two pointers are equal, and full when host read pointer
* is one ahead of the DSP writer pointer.
*/
#define MTRACE_LOG_BUF_SIZE (ADSP_DW_SLOT_SIZE - 2 * sizeof(uint32_t))
#define MTRACE_LOGGING_SLOT_TYPE(n) (ADSP_DW_SLOT_DEBUG_LOG | ((n) & ADSP_DW_SLOT_CORE_MASK))
#define MTRACE_CORE 0
struct adsp_debug_slot {
uint32_t host_ptr;
uint32_t dsp_ptr;
uint8_t data[ADSP_DW_SLOT_SIZE - sizeof(uint32_t) * 2];
} __packed;
static void mtrace_init(void)
{
if (ADSP_DW->descs[0].type == MTRACE_LOGGING_SLOT_TYPE(MTRACE_CORE)) {
return;
}
ADSP_DW->descs[0].type = MTRACE_LOGGING_SLOT_TYPE(MTRACE_CORE);
}
static size_t mtrace_out(int8_t *str, size_t len, size_t *space_left)
{
struct adsp_debug_slot *slot = (struct adsp_debug_slot *)(ADSP_DW->slots[0]);
uint8_t *data = slot->data;
uint32_t r = slot->host_ptr;
uint32_t w = slot->dsp_ptr;
size_t avail, out;
if (w > r) {
avail = MTRACE_LOG_BUF_SIZE - w + r - 1;
} else if (w == r) {
avail = MTRACE_LOG_BUF_SIZE - 1;
} else {
avail = r - w - 1;
}
if (len == 0) {
out = 0;
goto out;
}
/* data that does not fit is dropped */
out = MIN(avail, len);
if (w + out >= MTRACE_LOG_BUF_SIZE) {
size_t tail = MTRACE_LOG_BUF_SIZE - w;
size_t head = out - tail;
memcpy(data + w, str, tail);
memcpy(data, str + tail, head);
w = head;
} else {
memcpy(data + w, str, out);
w += out;
}
slot->dsp_ptr = w;
out:
if (space_left) {
*space_left = avail > len ? avail - len : 0;
}
return out;
}
static int char_out(uint8_t *data, size_t length, void *ctx)
{
size_t space_left = 0;
size_t out;
/*
* we handle the data even if mtrace notifier is not
* active. this ensures we can capture early boot messages.
*/
out = mtrace_out(data, length, &space_left);
if (mtrace_active && mtrace_hook) {
/* if we are in panic mode, need to flush out asap */
if (unlikely(mtrace_panic_mode))
space_left = 0;
mtrace_hook(out, space_left);
}
return length;
}
/**
* 80 bytes seems to catch most sensibly sized log message lines
* in one go letting the trace out call output whole complete
* lines. This avoids the overhead of a spin lock in the trace_out
* more often as well as avoiding entwined characters from printk if
* LOG_PRINTK=n.
*/
#define LOG_BUF_SIZE 80
static uint8_t log_buf[LOG_BUF_SIZE];
LOG_OUTPUT_DEFINE(log_output_adsp_mtrace, char_out, log_buf, sizeof(log_buf));
static uint32_t format_flags(void)
{
uint32_t flags = LOG_OUTPUT_FLAG_LEVEL | LOG_OUTPUT_FLAG_TIMESTAMP;
if (IS_ENABLED(CONFIG_LOG_BACKEND_FORMAT_TIMESTAMP)) {
flags |= LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP;
}
return flags;
}
static void panic(struct log_backend const *const backend)
{
mtrace_panic_mode = true;
}
static void dropped(const struct log_backend *const backend,
uint32_t cnt)
{
log_output_dropped_process(&log_output_adsp_mtrace, cnt);
}
static void process(const struct log_backend *const backend,
union log_msg_generic *msg)
{
log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
k_spinlock_key_t key = k_spin_lock(&mtrace_lock);
log_output_func(&log_output_adsp_mtrace, &msg->log, format_flags());
k_spin_unlock(&mtrace_lock, key);
}
static int format_set(const struct log_backend *const backend, uint32_t log_type)
{
log_format_current = log_type;
return 0;
}
/**
* Lazily initialized, while the DMA may not be setup we continue
* to buffer log messages untilt he buffer is full.
*/
static void init(const struct log_backend *const backend)
{
ARG_UNUSED(backend);
mtrace_init();
}
const struct log_backend_api log_backend_adsp_mtrace_api = {
.process = process,
.dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
.panic = panic,
.format_set = format_set,
.init = init,
};
LOG_BACKEND_DEFINE(log_backend_adsp_mtrace, log_backend_adsp_mtrace_api, true);
void adsp_mtrace_log_init(adsp_mtrace_log_hook_t hook)
{
mtrace_init();
mtrace_hook = hook;
mtrace_active = true;
}
const struct log_backend *log_backend_adsp_mtrace_get(void)
{
return &log_backend_adsp_mtrace;
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2022 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
#include <zephyr/logging/log_backend.h>
#include <zephyr/logging/log_backend_std.h>
LOG_MODULE_REGISTER(log_efi);
static volatile bool in_panic;
static uint32_t log_format_current = CONFIG_LOG_BACKEND_EFI_CON_OUTPUT_DEFAULT;
static struct k_spinlock lock;
#define LOG_BUF_SIZE 128
static uint8_t efi_output_buf[LOG_BUF_SIZE + 1];
extern int efi_console_putchar(int c);
static int char_out(uint8_t *data, size_t length, void *ctx)
{
ARG_UNUSED(ctx);
size_t cnt = 0;
uint8_t *ptr = data;
while (cnt < length) {
efi_console_putchar(*ptr);
cnt++;
ptr++;
}
return length;
}
LOG_OUTPUT_DEFINE(log_output_efi, char_out, efi_output_buf, sizeof(efi_output_buf));
static void process(const struct log_backend *const backend,
union log_msg_generic *msg)
{
uint32_t flags = log_backend_std_get_flags();
log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
k_spinlock_key_t key = k_spin_lock(&lock);
log_output_func(&log_output_efi, &msg->log, flags);
k_spin_unlock(&lock, key);
}
static int format_set(const struct log_backend *const backend, uint32_t log_type)
{
log_format_current = log_type;
return 0;
}
static void log_backend_efi_init(struct log_backend const *const backend)
{
ARG_UNUSED(backend);
}
static void panic(struct log_backend const *const backend)
{
in_panic = true;
log_backend_std_panic(&log_output_efi);
}
static void dropped(const struct log_backend *const backend, uint32_t cnt)
{
ARG_UNUSED(backend);
log_backend_std_dropped(&log_output_efi, cnt);
}
const struct log_backend_api log_backend_efi_api = {
.process = process,
.panic = panic,
.init = log_backend_efi_init,
.dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
.format_set = format_set,
};
LOG_BACKEND_DEFINE(log_backend_efi, log_backend_efi_api, true);

View file

@ -0,0 +1,505 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <zephyr/logging/log_backend.h>
#include <zephyr/logging/log_output_dict.h>
#include <zephyr/logging/log_backend_std.h>
#include <assert.h>
#include <zephyr/fs/fs.h>
#define MAX_PATH_LEN 256
#define MAX_FLASH_WRITE_SIZE 256
#define LOG_PREFIX_LEN (sizeof(CONFIG_LOG_BACKEND_FS_FILE_PREFIX) - 1)
#define MAX_FILE_NUMERAL 9999
#define FILE_NUMERAL_LEN 4
enum backend_fs_state {
BACKEND_FS_NOT_INITIALIZED = 0,
BACKEND_FS_CORRUPTED,
BACKEND_FS_OK
};
static struct fs_file_t file;
static enum backend_fs_state backend_state = BACKEND_FS_NOT_INITIALIZED;
static int file_ctr, newest, oldest;
static int allocate_new_file(struct fs_file_t *file);
static int del_oldest_log(void);
static int get_log_file_id(struct fs_dirent *ent);
#ifndef CONFIG_LOG_BACKEND_FS_TESTSUITE
static uint32_t log_format_current = CONFIG_LOG_BACKEND_FS_OUTPUT_DEFAULT;
#endif
static int check_log_volumen_available(void)
{
int index = 0;
char const *name;
int rc = 0;
while (rc == 0) {
rc = fs_readmount(&index, &name);
if (rc == 0) {
if (strncmp(CONFIG_LOG_BACKEND_FS_DIR,
name,
strlen(name))
== 0) {
return 0;
}
}
}
return -ENOENT;
}
static int create_log_dir(const char *path)
{
const char *next;
const char *last = path + (strlen(path) - 1);
char w_path[MAX_PATH_LEN];
int rc, len;
struct fs_dir_t dir;
fs_dir_t_init(&dir);
/* the fist directory name is the mount point*/
/* the firs path's letter might be meaningless `/`, let's skip it */
next = strchr(path + 1, '/');
if (!next) {
return 0;
}
while (true) {
next++;
if (next > last) {
return 0;
}
next = strchr(next, '/');
if (!next) {
next = last;
len = last - path + 1;
} else {
len = next - path;
}
memcpy(w_path, path, len);
w_path[len] = 0;
rc = fs_opendir(&dir, w_path);
if (rc) {
/* assume directory doesn't exist */
rc = fs_mkdir(w_path);
if (rc) {
break;
}
}
rc = fs_closedir(&dir);
if (rc) {
break;
}
}
return rc;
}
static int check_log_file_exist(int num)
{
struct fs_dir_t dir;
struct fs_dirent ent;
int rc;
fs_dir_t_init(&dir);
rc = fs_opendir(&dir, CONFIG_LOG_BACKEND_FS_DIR);
if (rc) {
return -EIO;
}
while (true) {
rc = fs_readdir(&dir, &ent);
if (rc < 0) {
rc = -EIO;
goto close_dir;
}
if (ent.name[0] == 0) {
break;
}
rc = get_log_file_id(&ent);
if (rc == num) {
rc = 1;
goto close_dir;
}
}
rc = 0;
close_dir:
(void) fs_closedir(&dir);
return rc;
}
int write_log_to_file(uint8_t *data, size_t length, void *ctx)
{
int rc;
struct fs_file_t *f = &file;
if (backend_state == BACKEND_FS_NOT_INITIALIZED) {
if (check_log_volumen_available()) {
return length;
}
rc = create_log_dir(CONFIG_LOG_BACKEND_FS_DIR);
if (!rc) {
rc = allocate_new_file(&file);
}
backend_state = (rc ? BACKEND_FS_CORRUPTED : BACKEND_FS_OK);
}
if (backend_state == BACKEND_FS_OK) {
/* Check if new data overwrites max file size.
* If so, create new log file.
*/
int size = fs_tell(f);
if (size < 0) {
backend_state = BACKEND_FS_CORRUPTED;
return length;
} else if ((size + length) > CONFIG_LOG_BACKEND_FS_FILE_SIZE) {
rc = allocate_new_file(f);
if (rc < 0) {
goto on_error;
}
}
rc = fs_write(f, data, length);
if (rc >= 0) {
if (IS_ENABLED(CONFIG_LOG_BACKEND_FS_OVERWRITE) &&
(rc != length)) {
del_oldest_log();
return 0;
}
/* If overwrite is disabled, full memory
* cause the log record abandonment.
*/
length = rc;
} else {
rc = check_log_file_exist(newest);
if (rc == 0) {
/* file was lost somehow
* try to get a new one
*/
file_ctr--;
rc = allocate_new_file(f);
if (rc < 0) {
goto on_error;
}
} else if (rc < 0) {
/* fs is corrupted*/
goto on_error;
}
length = 0;
}
rc = fs_sync(f);
if (rc < 0) {
/* Something is wrong */
goto on_error;
}
}
return length;
on_error:
backend_state = BACKEND_FS_CORRUPTED;
return length;
}
static int get_log_file_id(struct fs_dirent *ent)
{
size_t len;
int num;
if (ent->type != FS_DIR_ENTRY_FILE) {
return -1;
}
len = strlen(ent->name);
if (len != LOG_PREFIX_LEN + FILE_NUMERAL_LEN) {
return -1;
}
if (memcmp(ent->name, CONFIG_LOG_BACKEND_FS_FILE_PREFIX, LOG_PREFIX_LEN) != 0) {
return -1;
}
num = atoi(ent->name + LOG_PREFIX_LEN);
if (num <= MAX_FILE_NUMERAL && num >= 0) {
return num;
}
return -1;
}
static int allocate_new_file(struct fs_file_t *file)
{
/* In case of no log file or current file fills up
* create new log file.
*/
int rc;
struct fs_statvfs stat;
int curr_file_num;
struct fs_dirent ent;
char fname[MAX_PATH_LEN];
off_t file_size;
assert(file);
if (backend_state == BACKEND_FS_NOT_INITIALIZED) {
/* Search for the last used log number. */
struct fs_dir_t dir;
int file_num = 0;
fs_dir_t_init(&dir);
curr_file_num = 0;
int max = 0, min = MAX_FILE_NUMERAL;
rc = fs_opendir(&dir, CONFIG_LOG_BACKEND_FS_DIR);
while (rc >= 0) {
rc = fs_readdir(&dir, &ent);
if ((rc < 0) || (ent.name[0] == 0)) {
break;
}
file_num = get_log_file_id(&ent);
if (file_num >= 0) {
if (file_num > max) {
max = file_num;
}
if (file_num < min) {
min = file_num;
}
++file_ctr;
}
}
oldest = min;
if ((file_ctr > 1) &&
((max - min) >
2 * CONFIG_LOG_BACKEND_FS_FILES_LIMIT)) {
/* oldest log is in the range around the min */
newest = min;
oldest = max;
(void)fs_closedir(&dir);
rc = fs_opendir(&dir, CONFIG_LOG_BACKEND_FS_DIR);
while (rc == 0) {
rc = fs_readdir(&dir, &ent);
if ((rc < 0) || (ent.name[0] == 0)) {
break;
}
file_num = get_log_file_id(&ent);
if (file_num < min + CONFIG_LOG_BACKEND_FS_FILES_LIMIT) {
if (newest < file_num) {
newest = file_num;
}
}
if (file_num > max - CONFIG_LOG_BACKEND_FS_FILES_LIMIT) {
if (oldest > file_num) {
oldest = file_num;
}
}
}
} else {
newest = max;
oldest = min;
}
(void)fs_closedir(&dir);
if (rc < 0) {
goto out;
}
curr_file_num = newest;
/* Is there space left in the newest file? */
snprintf(fname, sizeof(fname), "%s/%s%04d", CONFIG_LOG_BACKEND_FS_DIR,
CONFIG_LOG_BACKEND_FS_FILE_PREFIX, curr_file_num);
rc = fs_open(file, fname, FS_O_CREATE | FS_O_WRITE | FS_O_APPEND);
if (rc < 0) {
goto out;
}
file_size = fs_tell(file);
if (file_size < CONFIG_LOG_BACKEND_FS_FILE_SIZE) {
/* There is space left to log to the latest file, no need to create
* a new one or delete old ones at this point.
*/
if (file_ctr == 0) {
++file_ctr;
}
backend_state = BACKEND_FS_OK;
goto out;
} else {
fs_close(file);
if (file_ctr >= 1) {
curr_file_num++;
if (curr_file_num > MAX_FILE_NUMERAL) {
curr_file_num = 0;
}
}
backend_state = BACKEND_FS_OK;
}
} else {
fs_close(file);
curr_file_num = newest;
curr_file_num++;
if (curr_file_num > MAX_FILE_NUMERAL) {
curr_file_num = 0;
}
}
rc = fs_statvfs(CONFIG_LOG_BACKEND_FS_DIR, &stat);
/* Check if there is enough space to write file or max files number
* is not exceeded.
*/
while ((file_ctr >= CONFIG_LOG_BACKEND_FS_FILES_LIMIT) ||
((stat.f_bfree * stat.f_frsize) <=
CONFIG_LOG_BACKEND_FS_FILE_SIZE)) {
if (IS_ENABLED(CONFIG_LOG_BACKEND_FS_OVERWRITE)) {
rc = del_oldest_log();
if (rc < 0) {
goto out;
}
rc = fs_statvfs(CONFIG_LOG_BACKEND_FS_DIR,
&stat);
if (rc < 0) {
goto out;
}
} else {
return -ENOSPC;
}
}
snprintf(fname, sizeof(fname), "%s/%s%04d",
CONFIG_LOG_BACKEND_FS_DIR,
CONFIG_LOG_BACKEND_FS_FILE_PREFIX, curr_file_num);
rc = fs_open(file, fname, FS_O_CREATE | FS_O_WRITE);
if (rc < 0) {
goto out;
}
++file_ctr;
newest = curr_file_num;
out:
return rc;
}
static int del_oldest_log(void)
{
int rc;
static char dellname[MAX_PATH_LEN];
while (true) {
snprintf(dellname, sizeof(dellname), "%s/%s%04d",
CONFIG_LOG_BACKEND_FS_DIR,
CONFIG_LOG_BACKEND_FS_FILE_PREFIX, oldest);
rc = fs_unlink(dellname);
if ((rc == 0) || (rc == -ENOENT)) {
oldest++;
if (oldest > MAX_FILE_NUMERAL) {
oldest = 0;
}
if (rc == 0) {
--file_ctr;
break;
}
} else {
break;
}
}
return rc;
}
BUILD_ASSERT(!IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE),
"Immediate logging is not supported by LOG FS backend.");
#ifndef CONFIG_LOG_BACKEND_FS_TESTSUITE
static uint8_t __aligned(4) buf[MAX_FLASH_WRITE_SIZE];
LOG_OUTPUT_DEFINE(log_output, write_log_to_file, buf, MAX_FLASH_WRITE_SIZE);
static void log_backend_fs_init(const struct log_backend *const backend)
{
}
static void panic(struct log_backend const *const backend)
{
/* In case of panic deinitialize backend. It is better to keep
* current data rather than log new and risk of failure.
*/
log_backend_deactivate(backend);
}
static void dropped(const struct log_backend *const backend, uint32_t cnt)
{
ARG_UNUSED(backend);
if (IS_ENABLED(CONFIG_LOG_BACKEND_FS_OUTPUT_DICTIONARY)) {
log_dict_output_dropped_process(&log_output, cnt);
} else {
log_backend_std_dropped(&log_output, cnt);
}
}
static void process(const struct log_backend *const backend,
union log_msg_generic *msg)
{
uint32_t flags = log_backend_std_get_flags();
log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
log_output_func(&log_output, &msg->log, flags);
}
static int format_set(const struct log_backend *const backend, uint32_t log_type)
{
log_format_current = log_type;
return 0;
}
static const struct log_backend_api log_backend_fs_api = {
.process = process,
.panic = panic,
.init = log_backend_fs_init,
.dropped = dropped,
.format_set = format_set,
};
LOG_BACKEND_DEFINE(log_backend_fs, log_backend_fs_api, true);
#endif

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stddef.h>
#include <zephyr/logging/log_backend.h>
#include <zephyr/logging/log_backend_std.h>
#include <zephyr/logging/log_core.h>
#include <zephyr/logging/log_output.h>
#include <zephyr/irq.h>
#include <zephyr/arch/posix/posix_trace.h>
#define _STDOUT_BUF_SIZE 256
static char stdout_buff[_STDOUT_BUF_SIZE];
static int n_pend; /* Number of pending characters in buffer */
static uint32_t log_format_current = CONFIG_LOG_BACKEND_NATIVE_POSIX_OUTPUT_DEFAULT;
static void preprint_char(int c)
{
int printnow = 0;
if (c == '\r') {
/* Discard carriage returns */
return;
}
if (c != '\n') {
stdout_buff[n_pend++] = c;
stdout_buff[n_pend] = 0;
} else {
printnow = 1;
}
if (n_pend >= _STDOUT_BUF_SIZE - 1) {
printnow = 1;
}
if (printnow) {
posix_print_trace("%s\n", stdout_buff);
n_pend = 0;
stdout_buff[0] = 0;
}
}
static uint8_t buf[_STDOUT_BUF_SIZE];
static int char_out(uint8_t *data, size_t length, void *ctx)
{
for (size_t i = 0; i < length; i++) {
preprint_char(data[i]);
}
return length;
}
LOG_OUTPUT_DEFINE(log_output_posix, char_out, buf, sizeof(buf));
static void panic(struct log_backend const *const backend)
{
log_output_flush(&log_output_posix);
}
static void dropped(const struct log_backend *const backend, uint32_t cnt)
{
ARG_UNUSED(backend);
log_output_dropped_process(&log_output_posix, cnt);
}
static void process(const struct log_backend *const backend,
union log_msg_generic *msg)
{
uint32_t flags = log_backend_std_get_flags();
log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
log_output_func(&log_output_posix, &msg->log, flags);
}
static int format_set(const struct log_backend *const backend, uint32_t log_type)
{
log_format_current = log_type;
return 0;
}
const struct log_backend_api log_backend_native_posix_api = {
.process = process,
.panic = panic,
.dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
.format_set = format_set,
};
LOG_BACKEND_DEFINE(log_backend_native_posix,
log_backend_native_posix_api,
true);

View file

@ -0,0 +1,238 @@
/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(log_backend_net, CONFIG_LOG_DEFAULT_LEVEL);
#include <zephyr/logging/log_backend.h>
#include <zephyr/logging/log_core.h>
#include <zephyr/logging/log_output.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/net_context.h>
/* Set this to 1 if you want to see what is being sent to server */
#define DEBUG_PRINTING 0
#if DEBUG_PRINTING
#define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__)
#else
#define DBG(fmt, ...)
#endif
#if defined(CONFIG_NET_IPV6) || CONFIG_NET_HOSTNAME_ENABLE
#define MAX_HOSTNAME_LEN NET_IPV6_ADDR_LEN
#else
#define MAX_HOSTNAME_LEN NET_IPV4_ADDR_LEN
#endif
static char dev_hostname[MAX_HOSTNAME_LEN + 1];
static uint8_t output_buf[CONFIG_LOG_BACKEND_NET_MAX_BUF_SIZE];
static bool net_init_done;
struct sockaddr server_addr;
static bool panic_mode;
static uint32_t log_format_current = CONFIG_LOG_BACKEND_NET_OUTPUT_DEFAULT;
const struct log_backend *log_backend_net_get(void);
NET_PKT_SLAB_DEFINE(syslog_tx_pkts, CONFIG_LOG_BACKEND_NET_MAX_BUF);
NET_PKT_DATA_POOL_DEFINE(syslog_tx_bufs,
ROUND_UP(CONFIG_LOG_BACKEND_NET_MAX_BUF_SIZE /
CONFIG_NET_BUF_DATA_SIZE, 1) *
CONFIG_LOG_BACKEND_NET_MAX_BUF);
static struct k_mem_slab *get_tx_slab(void)
{
return &syslog_tx_pkts;
}
struct net_buf_pool *get_data_pool(void)
{
return &syslog_tx_bufs;
}
static int line_out(uint8_t *data, size_t length, void *output_ctx)
{
struct net_context *ctx = (struct net_context *)output_ctx;
int ret = -ENOMEM;
if (ctx == NULL) {
return length;
}
ret = net_context_send(ctx, data, length, NULL, K_NO_WAIT, NULL);
if (ret < 0) {
goto fail;
}
DBG(data);
fail:
return length;
}
LOG_OUTPUT_DEFINE(log_output_net, line_out, output_buf, sizeof(output_buf));
static int do_net_init(void)
{
struct sockaddr *local_addr = NULL;
struct sockaddr_in6 local_addr6 = {0};
struct sockaddr_in local_addr4 = {0};
socklen_t server_addr_len;
struct net_context *ctx;
int ret;
if (IS_ENABLED(CONFIG_NET_IPV4) && server_addr.sa_family == AF_INET) {
local_addr = (struct sockaddr *)&local_addr4;
server_addr_len = sizeof(struct sockaddr_in);
local_addr4.sin_port = 0U;
}
if (IS_ENABLED(CONFIG_NET_IPV6) && server_addr.sa_family == AF_INET6) {
local_addr = (struct sockaddr *)&local_addr6;
server_addr_len = sizeof(struct sockaddr_in6);
local_addr6.sin6_port = 0U;
}
if (local_addr == NULL) {
DBG("Server address unknown\n");
return -EINVAL;
}
local_addr->sa_family = server_addr.sa_family;
ret = net_context_get(server_addr.sa_family, SOCK_DGRAM, IPPROTO_UDP,
&ctx);
if (ret < 0) {
DBG("Cannot get context (%d)\n", ret);
return ret;
}
if (IS_ENABLED(CONFIG_NET_HOSTNAME_ENABLE)) {
(void)strncpy(dev_hostname, net_hostname_get(), MAX_HOSTNAME_LEN);
} else if (IS_ENABLED(CONFIG_NET_IPV6) &&
server_addr.sa_family == AF_INET6) {
const struct in6_addr *src;
src = net_if_ipv6_select_src_addr(
NULL, &net_sin6(&server_addr)->sin6_addr);
if (src) {
net_addr_ntop(AF_INET6, src, dev_hostname,
MAX_HOSTNAME_LEN);
net_ipaddr_copy(&local_addr6.sin6_addr, src);
} else {
goto unknown;
}
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
server_addr.sa_family == AF_INET) {
const struct in_addr *src;
src = net_if_ipv4_select_src_addr(
NULL, &net_sin(&server_addr)->sin_addr);
if (src) {
net_addr_ntop(AF_INET, src, dev_hostname,
MAX_HOSTNAME_LEN);
net_ipaddr_copy(&local_addr4.sin_addr, src);
} else {
goto unknown;
}
} else {
unknown:
DBG("Cannot setup local context\n");
return -EINVAL;
}
ret = net_context_bind(ctx, local_addr, server_addr_len);
if (ret < 0) {
DBG("Cannot bind context (%d)\n", ret);
return ret;
}
(void)net_context_connect(ctx, &server_addr, server_addr_len,
NULL, K_NO_WAIT, NULL);
/* We do not care about return value for this UDP connect call that
* basically does nothing. Calling the connect is only useful so that
* we can see the syslog connection in net-shell.
*/
net_context_setup_pools(ctx, get_tx_slab, get_data_pool);
log_output_ctx_set(&log_output_net, ctx);
log_output_hostname_set(&log_output_net, dev_hostname);
return 0;
}
static void process(const struct log_backend *const backend,
union log_msg_generic *msg)
{
uint32_t flags = LOG_OUTPUT_FLAG_FORMAT_SYSLOG | LOG_OUTPUT_FLAG_TIMESTAMP;
if (panic_mode) {
return;
}
if (!net_init_done && do_net_init() == 0) {
net_init_done = true;
}
log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
log_output_func(&log_output_net, &msg->log, flags);
}
static int format_set(const struct log_backend *const backend, uint32_t log_type)
{
log_format_current = log_type;
return 0;
}
static void init_net(struct log_backend const *const backend)
{
ARG_UNUSED(backend);
int ret;
net_sin(&server_addr)->sin_port = htons(514);
ret = net_ipaddr_parse(CONFIG_LOG_BACKEND_NET_SERVER,
sizeof(CONFIG_LOG_BACKEND_NET_SERVER) - 1,
&server_addr);
if (ret == 0) {
LOG_ERR("Cannot configure syslog server address");
return;
}
log_backend_deactivate(log_backend_net_get());
}
static void panic(struct log_backend const *const backend)
{
panic_mode = true;
}
const struct log_backend_api log_backend_net_api = {
.panic = panic,
.init = init_net,
.process = process,
.format_set = format_set,
};
/* Note that the backend can be activated only after we have networking
* subsystem ready so we must not start it immediately.
*/
LOG_BACKEND_DEFINE(log_backend_net, log_backend_net_api,
IS_ENABLED(CONFIG_LOG_BACKEND_NET_AUTOSTART));
const struct log_backend *log_backend_net_get(void)
{
return &log_backend_net;
}

View file

@ -0,0 +1,311 @@
/*
* Copyright (c) 2018 omSquare s.r.o.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log_backend.h>
#include <zephyr/logging/log_core.h>
#include <zephyr/logging/log_output.h>
#include <zephyr/logging/log_backend_std.h>
#include <SEGGER_RTT.h>
#ifndef CONFIG_LOG_BACKEND_RTT_BUFFER_SIZE
#define CONFIG_LOG_BACKEND_RTT_BUFFER_SIZE 0
#endif
#ifndef CONFIG_LOG_BACKEND_RTT_MESSAGE_SIZE
#define CONFIG_LOG_BACKEND_RTT_MESSAGE_SIZE 0
#endif
#ifndef CONFIG_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE
#define CONFIG_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE 0
#endif
#ifndef CONFIG_LOG_BACKEND_RTT_RETRY_DELAY_MS
/* Long enough to detect host presence */
#define CONFIG_LOG_BACKEND_RTT_RETRY_DELAY_MS 10
#endif
#ifndef CONFIG_LOG_BACKEND_RTT_RETRY_CNT
/* Big enough to detect host presence */
#define CONFIG_LOG_BACKEND_RTT_RETRY_CNT 10
#endif
#define DROP_MAX 99
#define DROP_MSG "messages dropped: \r\n"
#define DROP_MSG_LEN (sizeof(DROP_MSG) - 1)
#define MESSAGE_SIZE CONFIG_LOG_BACKEND_RTT_MESSAGE_SIZE
#define CHAR_BUF_SIZE \
((IS_ENABLED(CONFIG_LOG_BACKEND_RTT_MODE_BLOCK) && \
!IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE)) ? \
CONFIG_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE : 1)
#define RTT_LOCK() \
COND_CODE_0(CONFIG_LOG_BACKEND_RTT_BUFFER, (SEGGER_RTT_LOCK()), ())
#define RTT_UNLOCK() \
COND_CODE_0(CONFIG_LOG_BACKEND_RTT_BUFFER, (SEGGER_RTT_UNLOCK()), ())
#define RTT_BUFFER_SIZE \
COND_CODE_0(CONFIG_LOG_BACKEND_RTT_BUFFER, \
(0), (CONFIG_LOG_BACKEND_RTT_BUFFER_SIZE))
static const char *drop_msg = DROP_MSG;
static uint8_t rtt_buf[RTT_BUFFER_SIZE];
static uint8_t line_buf[MESSAGE_SIZE + DROP_MSG_LEN];
static uint8_t *line_pos;
static uint8_t char_buf[CHAR_BUF_SIZE];
static int drop_cnt;
static int drop_warn;
static bool panic_mode;
static bool host_present;
static int data_out_block_mode(uint8_t *data, size_t length, void *ctx);
static int data_out_drop_mode(uint8_t *data, size_t length, void *ctx);
static int char_out_drop_mode(uint8_t data);
static int line_out_drop_mode(void);
static uint32_t log_format_current = CONFIG_LOG_BACKEND_RTT_OUTPUT_DEFAULT;
static inline bool is_sync_mode(void)
{
return IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) || panic_mode;
}
static inline bool is_panic_mode(void)
{
return panic_mode;
}
static int data_out_drop_mode(uint8_t *data, size_t length, void *ctx)
{
(void) ctx;
uint8_t *pos;
if (is_sync_mode()) {
return data_out_block_mode(data, length, ctx);
}
for (pos = data; pos < data + length; pos++) {
if (char_out_drop_mode(*pos)) {
break;
}
}
return (int) (pos - data);
}
static int char_out_drop_mode(uint8_t data)
{
if (data == '\n') {
if (line_out_drop_mode()) {
return 1;
}
line_pos = line_buf;
return 0;
}
if (line_pos < line_buf + MESSAGE_SIZE - 1) {
*line_pos++ = data;
}
/* not enough space in line buffer, we have to wait for EOL */
return 0;
}
static int line_out_drop_mode(void)
{
/* line cannot be empty */
__ASSERT_NO_MSG(line_pos > line_buf);
/* Handle the case if line contains only '\n' */
if (line_pos - line_buf == 1) {
line_pos++;
}
*(line_pos - 1) = '\r';
*line_pos++ = '\n';
if (drop_cnt > 0 && !drop_warn) {
int cnt = MIN(drop_cnt, DROP_MAX);
__ASSERT_NO_MSG(line_pos - line_buf <= MESSAGE_SIZE);
memmove(line_buf + DROP_MSG_LEN, line_buf, line_pos - line_buf);
(void)memcpy(line_buf, drop_msg, DROP_MSG_LEN);
line_pos += DROP_MSG_LEN;
drop_warn = 1;
if (cnt < 10) {
line_buf[DROP_MSG_LEN - 2] = ' ';
line_buf[DROP_MSG_LEN - 3] = (uint8_t) ('0' + cnt);
line_buf[DROP_MSG_LEN - 4] = ' ';
} else {
line_buf[DROP_MSG_LEN - 2] = (uint8_t) ('0' + cnt % 10);
line_buf[DROP_MSG_LEN - 3] = (uint8_t) ('0' + cnt / 10);
line_buf[DROP_MSG_LEN - 4] = '>';
}
}
int ret;
RTT_LOCK();
ret = SEGGER_RTT_WriteSkipNoLock(CONFIG_LOG_BACKEND_RTT_BUFFER,
line_buf, line_pos - line_buf);
RTT_UNLOCK();
if (ret == 0) {
drop_cnt++;
} else {
drop_cnt = 0;
drop_warn = 0;
}
return 0;
}
static void on_failed_write(int retry_cnt)
{
if (retry_cnt == 0) {
host_present = false;
} else if (is_sync_mode()) {
k_busy_wait(USEC_PER_MSEC *
CONFIG_LOG_BACKEND_RTT_RETRY_DELAY_MS);
} else {
k_msleep(CONFIG_LOG_BACKEND_RTT_RETRY_DELAY_MS);
}
}
static void on_write(int retry_cnt)
{
host_present = true;
if (is_panic_mode()) {
/* In panic mode block on each write until host reads it. This
* way it is ensured that if system resets all messages are read
* by the host. While pending on data being read by the host we
* must also detect situation where host is disconnected.
*/
while (SEGGER_RTT_HasDataUp(CONFIG_LOG_BACKEND_RTT_BUFFER) &&
host_present) {
on_failed_write(retry_cnt--);
}
}
}
static int data_out_block_mode(uint8_t *data, size_t length, void *ctx)
{
int ret = 0;
/* This function is also called in drop mode for synchronous operation
* in that case retry is undesired */
int retry_cnt = IS_ENABLED(CONFIG_LOG_BACKEND_RTT_MODE_BLOCK) ?
CONFIG_LOG_BACKEND_RTT_RETRY_CNT : 1;
do {
if (!is_sync_mode()) {
RTT_LOCK();
ret = SEGGER_RTT_WriteSkipNoLock(CONFIG_LOG_BACKEND_RTT_BUFFER,
data, length);
RTT_UNLOCK();
} else {
ret = SEGGER_RTT_WriteSkipNoLock(CONFIG_LOG_BACKEND_RTT_BUFFER,
data, length);
}
if (ret) {
on_write(retry_cnt);
} else if (host_present) {
retry_cnt--;
on_failed_write(retry_cnt);
} else {
}
} while ((ret == 0) && host_present);
return ((ret == 0) && host_present) ? 0 : length;
}
static int data_out_overwrite_mode(uint8_t *data, size_t length, void *ctx)
{
if (!is_sync_mode()) {
RTT_LOCK();
SEGGER_RTT_WriteWithOverwriteNoLock(CONFIG_LOG_BACKEND_RTT_BUFFER,
data, length);
RTT_UNLOCK();
} else {
SEGGER_RTT_WriteWithOverwriteNoLock(CONFIG_LOG_BACKEND_RTT_BUFFER,
data, length);
}
return length;
}
LOG_OUTPUT_DEFINE(log_output_rtt,
IS_ENABLED(CONFIG_LOG_BACKEND_RTT_MODE_BLOCK) ?
data_out_block_mode :
IS_ENABLED(CONFIG_LOG_BACKEND_RTT_MODE_OVERWRITE) ?
data_out_overwrite_mode : data_out_drop_mode,
char_buf, sizeof(char_buf));
static void log_backend_rtt_cfg(void)
{
SEGGER_RTT_ConfigUpBuffer(CONFIG_LOG_BACKEND_RTT_BUFFER, "Logger",
rtt_buf, sizeof(rtt_buf),
SEGGER_RTT_MODE_NO_BLOCK_SKIP);
}
static void log_backend_rtt_init(struct log_backend const *const backend)
{
if (CONFIG_LOG_BACKEND_RTT_BUFFER > 0) {
log_backend_rtt_cfg();
}
host_present = true;
line_pos = line_buf;
}
static void panic(struct log_backend const *const backend)
{
panic_mode = true;
log_backend_std_panic(&log_output_rtt);
}
static void dropped(const struct log_backend *const backend, uint32_t cnt)
{
ARG_UNUSED(backend);
log_backend_std_dropped(&log_output_rtt, cnt);
}
static void process(const struct log_backend *const backend,
union log_msg_generic *msg)
{
uint32_t flags = log_backend_std_get_flags();
log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
log_output_func(&log_output_rtt, &msg->log, flags);
}
static int format_set(const struct log_backend *const backend, uint32_t log_type)
{
log_format_current = log_type;
return 0;
}
const struct log_backend_api log_backend_rtt_api = {
.process = process,
.panic = panic,
.init = log_backend_rtt_init,
.dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
.format_set = format_set,
};
LOG_BACKEND_DEFINE(log_backend_rtt, log_backend_rtt_api, true);

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log_backend.h>
#include <zephyr/logging/log_backend_std.h>
#include <zephyr/logging/log_output.h>
#include <openthread/platform/logging.h>
#include <utils/uart.h>
#include <platform-zephyr.h>
#ifndef CONFIG_LOG_BACKEND_SPINEL_BUFFER_SIZE
#define CONFIG_LOG_BACKEND_SPINEL_BUFFER_SIZE 0
#endif
static uint8_t char_buf[CONFIG_LOG_BACKEND_SPINEL_BUFFER_SIZE];
static bool panic_mode;
static uint16_t last_log_level;
static uint32_t log_format_current = CONFIG_LOG_BACKEND_SPINEL_OUTPUT_DEFAULT;
static int write(uint8_t *data, size_t length, void *ctx);
LOG_OUTPUT_DEFINE(log_output_spinel, write, char_buf, sizeof(char_buf));
static inline bool is_panic_mode(void)
{
return panic_mode;
}
static void process(const struct log_backend *const backend,
union log_msg_generic *msg)
{
/* prevent adding CRLF, which may crash spinel decoding */
uint32_t flags = LOG_OUTPUT_FLAG_CRLF_NONE | log_backend_std_get_flags();
log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
log_output_func(&log_output_spinel, &msg->log, flags);
}
static int format_set(const struct log_backend *const backend, uint32_t log_type)
{
log_format_current = log_type;
return 0;
}
static void log_backend_spinel_init(struct log_backend const *const backend)
{
memset(char_buf, '\0', sizeof(char_buf));
}
static void panic(struct log_backend const *const backend)
{
ARG_UNUSED(backend);
panic_mode = true;
}
static void dropped(const struct log_backend *const backend, uint32_t cnt)
{
ARG_UNUSED(backend);
log_backend_std_dropped(&log_output_spinel, cnt);
}
static int write(uint8_t *data, size_t length, void *ctx)
{
otLogLevel log_level;
if (is_panic_mode()) {
/* In panic mode otPlatLog implemented for Spinel protocol
* cannot be used, because it cannot be called from interrupt.
* In such situation raw data bytes without encoding are send.
*/
platformUartPanic();
otPlatUartSend(data, length);
} else {
switch (last_log_level) {
case LOG_LEVEL_ERR:
log_level = OT_LOG_LEVEL_CRIT;
break;
case LOG_LEVEL_WRN:
log_level = OT_LOG_LEVEL_WARN;
break;
case LOG_LEVEL_INF:
log_level = OT_LOG_LEVEL_INFO;
break;
case LOG_LEVEL_DBG:
log_level = OT_LOG_LEVEL_DEBG;
break;
default:
log_level = OT_LOG_LEVEL_NONE;
break;
}
otPlatLog(log_level, OT_LOG_REGION_PLATFORM, "%s", data);
}
/* make sure that buffer will be clean in next attempt */
memset(char_buf, '\0', length);
return length;
}
const struct log_backend_api log_backend_spinel_api = {
.process = process,
.panic = panic,
.init = log_backend_spinel_init,
.dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
.format_set = format_set,
};
LOG_BACKEND_DEFINE(log_backend_spinel, log_backend_spinel_api, true);

View file

@ -0,0 +1,152 @@
/*
* Copyright (c) 2018 Piotr Mienkowski
*
* SPDX-License-Identifier: Apache-2.0
*/
/** @file
* @brief Serial Wire Output (SWO) backend implementation.
*
* SWO/SWV has been developed by ARM. The following code works only on ARM
* architecture.
*
* An SWO viewer program will typically set-up the SWO port including its
* frequency when connected to the debug probe. Such configuration can persist
* only until the MCU reset. The SWO backend initialization function will
* re-configure the SWO port upon boot and set the frequency as specified by
* the LOG_BACKEND_SWO_FREQ_HZ Kconfig option. To ensure flawless operation
* this frequency should much the one set by the SWO viewer program.
*
* The initialization code assumes that SWO core frequency is equal to HCLK
* as defined by the clock-frequency property in the CPU node. This may require
* additional, vendor specific configuration.
*/
#include <zephyr/logging/log_backend.h>
#include <zephyr/logging/log_core.h>
#include <zephyr/logging/log_output.h>
#include <zephyr/logging/log_backend_std.h>
#include <zephyr/drivers/pinctrl.h>
#include <soc.h>
/** The stimulus port from which SWO data is received and displayed */
#define ITM_PORT_LOGGER 0
/* If ITM has pin control properties, apply them for SWO pins */
#if DT_NODE_HAS_PROP(DT_NODELABEL(itm), pinctrl_0)
PINCTRL_DT_DEFINE(DT_NODELABEL(itm));
#endif
/* Set TPIU prescaler for the current debug trace clock frequency. */
#if CONFIG_LOG_BACKEND_SWO_FREQ_HZ == 0
#define SWO_FREQ_DIV 1
#else
/* Set reference frequency which can be custom or cpu frequency. */
#if DT_NODE_HAS_PROP(DT_NODELABEL(itm), swo_ref_frequency)
#define SWO_REF_FREQ DT_PROP(DT_NODELABEL(itm), swo_ref_frequency)
#elif DT_NODE_HAS_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
#define SWO_REF_FREQ DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
#else
#error "Missing DT 'clock-frequency' property on cpu@0 node"
#endif
#define SWO_FREQ_DIV \
((SWO_REF_FREQ + (CONFIG_LOG_BACKEND_SWO_FREQ_HZ / 2)) / \
CONFIG_LOG_BACKEND_SWO_FREQ_HZ)
#if SWO_FREQ_DIV > 0xFFFF
#error CONFIG_LOG_BACKEND_SWO_FREQ_HZ is too low. SWO clock divider is 16-bit. \
Minimum supported SWO clock frequency is \
[Reference Clock Frequency]/2^16.
#endif
#endif
static uint8_t buf[1];
static uint32_t log_format_current = CONFIG_LOG_BACKEND_SWO_OUTPUT_DEFAULT;
static int char_out(uint8_t *data, size_t length, void *ctx)
{
ARG_UNUSED(ctx);
for (size_t i = 0; i < length; i++) {
ITM_SendChar(data[i]);
}
return length;
}
LOG_OUTPUT_DEFINE(log_output_swo, char_out, buf, sizeof(buf));
static void log_backend_swo_process(const struct log_backend *const backend,
union log_msg_generic *msg)
{
uint32_t flags = log_backend_std_get_flags();
log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
log_output_func(&log_output_swo, &msg->log, flags);
}
static int format_set(const struct log_backend *const backend, uint32_t log_type)
{
log_format_current = log_type;
return 0;
}
static void log_backend_swo_init(struct log_backend const *const backend)
{
/* Enable DWT and ITM units */
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
/* Enable access to ITM registers */
ITM->LAR = 0xC5ACCE55;
/* Disable stimulus ports ITM_STIM0-ITM_STIM31 */
ITM->TER = 0x0;
/* Disable ITM */
ITM->TCR = 0x0;
/* Select NRZ (UART) encoding protocol */
TPI->SPPR = 2;
/* Set SWO baud rate prescaler value: SWO_clk = ref_clock/(ACPR + 1) */
TPI->ACPR = SWO_FREQ_DIV - 1;
/* Enable unprivileged access to ITM stimulus ports */
ITM->TPR = 0x0;
/* Configure Debug Watchpoint and Trace */
DWT->CTRL &= (DWT_CTRL_POSTPRESET_Msk | DWT_CTRL_POSTINIT_Msk | DWT_CTRL_CYCCNTENA_Msk);
DWT->CTRL |= (DWT_CTRL_POSTPRESET_Msk | DWT_CTRL_POSTINIT_Msk);
/* Configure Formatter and Flush Control Register */
TPI->FFCR = 0x00000100;
/* Enable ITM, set TraceBusID=1, no local timestamp generation */
ITM->TCR = 0x0001000D;
/* Enable stimulus port used by the logger */
ITM->TER = 1 << ITM_PORT_LOGGER;
/* Initialize pin control settings, if any are defined */
#if DT_NODE_HAS_PROP(DT_NODELABEL(itm), pinctrl_0)
const struct pinctrl_dev_config *pincfg =
PINCTRL_DT_DEV_CONFIG_GET(DT_NODELABEL(itm));
pinctrl_apply_state(pincfg, PINCTRL_STATE_DEFAULT);
#endif
}
static void log_backend_swo_panic(struct log_backend const *const backend)
{
}
static void dropped(const struct log_backend *const backend, uint32_t cnt)
{
ARG_UNUSED(backend);
log_backend_std_dropped(&log_output_swo, cnt);
}
const struct log_backend_api log_backend_swo_api = {
.process = log_backend_swo_process,
.panic = log_backend_swo_panic,
.init = log_backend_swo_init,
.dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
.format_set = format_set,
};
LOG_BACKEND_DEFINE(log_backend_swo, log_backend_swo_api, true);

View file

@ -0,0 +1,164 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log_backend.h>
#include <zephyr/logging/log_core.h>
#include <zephyr/logging/log_output.h>
#include <zephyr/logging/log_output_dict.h>
#include <zephyr/logging/log_backend_std.h>
#include <zephyr/logging/log.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/__assert.h>
LOG_MODULE_REGISTER(log_uart);
/* Fixed size to avoid auto-added trailing '\0'.
* Used if CONFIG_LOG_BACKEND_UART_OUTPUT_DICTIONARY_HEX.
*/
static const char LOG_HEX_SEP[10] = "##ZLOGV1##";
static const struct device *const uart_dev =
DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
static struct k_sem sem;
static volatile bool in_panic;
static bool use_async;
static uint32_t log_format_current = CONFIG_LOG_BACKEND_UART_OUTPUT_DEFAULT;
static void uart_callback(const struct device *dev,
struct uart_event *evt,
void *user_data)
{
switch (evt->type) {
case UART_TX_DONE:
k_sem_give(&sem);
break;
default:
break;
}
}
static void dict_char_out_hex(uint8_t *data, size_t length)
{
for (size_t i = 0; i < length; i++) {
char c;
uint8_t x;
/* upper 8-bit */
x = data[i] >> 4;
(void)hex2char(x, &c);
uart_poll_out(uart_dev, c);
/* lower 8-bit */
x = data[i] & 0x0FU;
(void)hex2char(x, &c);
uart_poll_out(uart_dev, c);
}
}
static int char_out(uint8_t *data, size_t length, void *ctx)
{
ARG_UNUSED(ctx);
int err;
if (IS_ENABLED(CONFIG_LOG_BACKEND_UART_OUTPUT_DICTIONARY_HEX)) {
dict_char_out_hex(data, length);
return length;
}
if (!IS_ENABLED(CONFIG_LOG_BACKEND_UART_ASYNC) || in_panic || !use_async) {
for (size_t i = 0; i < length; i++) {
uart_poll_out(uart_dev, data[i]);
}
return length;
}
err = uart_tx(uart_dev, data, length, SYS_FOREVER_US);
__ASSERT_NO_MSG(err == 0);
err = k_sem_take(&sem, K_FOREVER);
__ASSERT_NO_MSG(err == 0);
(void)err;
return length;
}
static uint8_t uart_output_buf[CONFIG_LOG_BACKEND_UART_BUFFER_SIZE];
LOG_OUTPUT_DEFINE(log_output_uart, char_out, uart_output_buf, sizeof(uart_output_buf));
static void process(const struct log_backend *const backend,
union log_msg_generic *msg)
{
uint32_t flags = log_backend_std_get_flags();
log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
log_output_func(&log_output_uart, &msg->log, flags);
}
static int format_set(const struct log_backend *const backend, uint32_t log_type)
{
log_format_current = log_type;
return 0;
}
static void log_backend_uart_init(struct log_backend const *const backend)
{
__ASSERT_NO_MSG(device_is_ready(uart_dev));
if (IS_ENABLED(CONFIG_LOG_BACKEND_UART_OUTPUT_DICTIONARY_HEX)) {
/* Print a separator so the output can be fed into
* log parser directly. This is useful when capturing
* from UART directly where there might be other output
* (e.g. bootloader).
*/
for (int i = 0; i < sizeof(LOG_HEX_SEP); i++) {
uart_poll_out(uart_dev, LOG_HEX_SEP[i]);
}
return;
}
if (IS_ENABLED(CONFIG_LOG_BACKEND_UART_ASYNC)) {
int err = uart_callback_set(uart_dev, uart_callback, NULL);
if (err == 0) {
use_async = true;
k_sem_init(&sem, 0, 1);
} else {
LOG_WRN("Failed to initialize asynchronous mode (err:%d). "
"Fallback to polling.", err);
}
}
}
static void panic(struct log_backend const *const backend)
{
in_panic = true;
log_backend_std_panic(&log_output_uart);
}
static void dropped(const struct log_backend *const backend, uint32_t cnt)
{
ARG_UNUSED(backend);
if (IS_ENABLED(CONFIG_LOG_BACKEND_UART_OUTPUT_DICTIONARY)) {
log_dict_output_dropped_process(&log_output_uart, cnt);
} else {
log_backend_std_dropped(&log_output_uart, cnt);
}
}
const struct log_backend_api log_backend_uart_api = {
.process = process,
.panic = panic,
.init = log_backend_uart_init,
.dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
.format_set = format_set,
};
LOG_BACKEND_DEFINE(log_backend_uart, log_backend_uart_api, true);

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2019 Intel Corporation Inc.
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stddef.h>
#include <zephyr/logging/log_backend.h>
#include <zephyr/logging/log_core.h>
#include <zephyr/logging/log_output.h>
#include <zephyr/logging/log_backend_std.h>
#include <xtensa/simcall.h>
#define CHAR_BUF_SIZE (IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? \
1 : CONFIG_LOG_BACKEND_XTENSA_OUTPUT_BUFFER_SIZE)
static uint8_t xtensa_log_buf[CHAR_BUF_SIZE];
static uint32_t log_format_current = CONFIG_LOG_BACKEND_XTENSA_SIM_OUTPUT_DEFAULT;
static int char_out(uint8_t *data, size_t length, void *ctx)
{
register int a2 __asm__ ("a2") = SYS_write;
register int a3 __asm__ ("a3") = 1;
register int a4 __asm__ ("a4") = (int) data;
register int a5 __asm__ ("a5") = length;
__asm__ volatile("simcall"
: "=a"(a2), "=a"(a3)
: "a"(a2), "a"(a3), "a"(a4), "a"(a5));
return length;
}
LOG_OUTPUT_DEFINE(log_output_xsim, char_out,
xtensa_log_buf, sizeof(xtensa_log_buf));
static void process(const struct log_backend *const backend,
union log_msg_generic *msg)
{
uint32_t flags = log_backend_std_get_flags();
log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
log_output_func(&log_output_xsim, &msg->log, flags);
}
static int format_set(const struct log_backend *const backend, uint32_t log_type)
{
log_format_current = log_type;
return 0;
}
static void panic(struct log_backend const *const backend)
{
log_backend_std_panic(&log_output_xsim);
}
static void dropped(const struct log_backend *const backend, uint32_t cnt)
{
ARG_UNUSED(backend);
log_backend_std_dropped(&log_output_xsim, cnt);
}
const struct log_backend_api log_backend_xtensa_sim_api = {
.process = process,
.panic = panic,
.dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
.format_set = format_set,
};
LOG_BACKEND_DEFINE(log_backend_xtensa_sim, log_backend_xtensa_sim_api, true);