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:
parent
ebade6f48c
commit
f5587552bb
29 changed files with 584 additions and 545 deletions
66
subsys/logging/backends/CMakeLists.txt
Normal file
66
subsys/logging/backends/CMakeLists.txt
Normal 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
|
||||
)
|
19
subsys/logging/backends/Kconfig
Normal file
19
subsys/logging/backends/Kconfig
Normal 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
|
18
subsys/logging/backends/Kconfig.adsp
Normal file
18
subsys/logging/backends/Kconfig.adsp
Normal 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
|
52
subsys/logging/backends/Kconfig.adsp_hda
Normal file
52
subsys/logging/backends/Kconfig.adsp_hda
Normal 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
|
18
subsys/logging/backends/Kconfig.adsp_mtrace
Normal file
18
subsys/logging/backends/Kconfig.adsp_mtrace
Normal 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
|
18
subsys/logging/backends/Kconfig.efi_console
Normal file
18
subsys/logging/backends/Kconfig.efi_console
Normal 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
|
54
subsys/logging/backends/Kconfig.fs
Normal file
54
subsys/logging/backends/Kconfig.fs
Normal 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
|
18
subsys/logging/backends/Kconfig.native_posix
Normal file
18
subsys/logging/backends/Kconfig.native_posix
Normal 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
|
66
subsys/logging/backends/Kconfig.net
Normal file
66
subsys/logging/backends/Kconfig.net
Normal 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
|
108
subsys/logging/backends/Kconfig.rtt
Normal file
108
subsys/logging/backends/Kconfig.rtt
Normal 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
|
27
subsys/logging/backends/Kconfig.spinel
Normal file
27
subsys/logging/backends/Kconfig.spinel
Normal 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
|
31
subsys/logging/backends/Kconfig.swo
Normal file
31
subsys/logging/backends/Kconfig.swo
Normal 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
|
51
subsys/logging/backends/Kconfig.uart
Normal file
51
subsys/logging/backends/Kconfig.uart
Normal 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
|
26
subsys/logging/backends/Kconfig.xtensa_sim
Normal file
26
subsys/logging/backends/Kconfig.xtensa_sim
Normal 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
|
98
subsys/logging/backends/log_backend_adsp.c
Normal file
98
subsys/logging/backends/log_backend_adsp.c
Normal 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);
|
376
subsys/logging/backends/log_backend_adsp_hda.c
Normal file
376
subsys/logging/backends/log_backend_adsp_hda.c
Normal 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 */
|
229
subsys/logging/backends/log_backend_adsp_mtrace.c
Normal file
229
subsys/logging/backends/log_backend_adsp_mtrace.c
Normal 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;
|
||||
}
|
88
subsys/logging/backends/log_backend_efi_console.c
Normal file
88
subsys/logging/backends/log_backend_efi_console.c
Normal 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);
|
505
subsys/logging/backends/log_backend_fs.c
Normal file
505
subsys/logging/backends/log_backend_fs.c
Normal 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
|
98
subsys/logging/backends/log_backend_native_posix.c
Normal file
98
subsys/logging/backends/log_backend_native_posix.c
Normal 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);
|
238
subsys/logging/backends/log_backend_net.c
Normal file
238
subsys/logging/backends/log_backend_net.c
Normal 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;
|
||||
}
|
311
subsys/logging/backends/log_backend_rtt.c
Normal file
311
subsys/logging/backends/log_backend_rtt.c
Normal 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);
|
112
subsys/logging/backends/log_backend_spinel.c
Normal file
112
subsys/logging/backends/log_backend_spinel.c
Normal 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);
|
152
subsys/logging/backends/log_backend_swo.c
Normal file
152
subsys/logging/backends/log_backend_swo.c
Normal 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);
|
164
subsys/logging/backends/log_backend_uart.c
Normal file
164
subsys/logging/backends/log_backend_uart.c
Normal 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);
|
75
subsys/logging/backends/log_backend_xtensa_sim.c
Normal file
75
subsys/logging/backends/log_backend_xtensa_sim.c
Normal 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);
|
Loading…
Add table
Add a link
Reference in a new issue