From c0064f1de88a04146b55e059ae40dc4ebf1eb0a9 Mon Sep 17 00:00:00 2001 From: Chris Friedt Date: Wed, 8 Nov 2023 17:29:26 +0800 Subject: [PATCH] logging: uart: support multiple instances Extends the log_backend_uart to support logging to multiple UART instances. Signed-off-by: Christopher Friedt --- doc/build/dts/api/api.rst | 3 + dts/bindings/misc/zephyr,log-uart.yaml | 16 +++ subsys/logging/backends/log_backend_uart.c | 115 +++++++++++++++------ 3 files changed, 103 insertions(+), 31 deletions(-) create mode 100644 dts/bindings/misc/zephyr,log-uart.yaml diff --git a/doc/build/dts/api/api.rst b/doc/build/dts/api/api.rst index f452d0f5281..44b87653cc3 100644 --- a/doc/build/dts/api/api.rst +++ b/doc/build/dts/api/api.rst @@ -424,6 +424,9 @@ device. interprocess-communication (IPC) * - zephyr,itcm - Instruction Tightly Coupled Memory node on some Arm SoCs + * - zephyr,log-uart + - Sets the UART device(s) used by the logging subsystem's UART backend. + If defined, the UART log backend would output to the devices listed in this node. * - zephyr,ocm - On-chip memory node on Xilinx Zynq-7000 and ZynqMP SoCs * - zephyr,osdp-uart diff --git a/dts/bindings/misc/zephyr,log-uart.yaml b/dts/bindings/misc/zephyr,log-uart.yaml new file mode 100644 index 00000000000..2e0a065e3b2 --- /dev/null +++ b/dts/bindings/misc/zephyr,log-uart.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2023 Meta +# SPDX-License-Identifier: Apache-2.0 + +description: Log Backend UART + +compatible: "zephyr,log-uart" + +include: [base.yaml] + +properties: + + uarts: + type: phandles + required: true + description: | + UART devices to be used by the UART log backend. diff --git a/subsys/logging/backends/log_backend_uart.c b/subsys/logging/backends/log_backend_uart.c index 5bfcbdd89b1..7c58ea1774e 100644 --- a/subsys/logging/backends/log_backend_uart.c +++ b/subsys/logging/backends/log_backend_uart.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2023 Meta * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,32 +19,43 @@ #include LOG_MODULE_REGISTER(log_uart); +struct lbu_data { + struct k_sem sem; + uint32_t log_format_current; + volatile bool in_panic; + bool use_async; +}; + +struct lbu_cb_ctx { + const struct log_output *output; + const struct device *device; + struct lbu_data *data; +}; + /* 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) { + const struct lbu_cb_ctx *ctx = user_data; + struct lbu_data *data = ctx->data; + + ARG_UNUSED(dev); + switch (evt->type) { case UART_TX_DONE: - k_sem_give(&sem); + k_sem_give(&data->sem); break; default: break; } } -static void dict_char_out_hex(uint8_t *data, size_t length) +static void dict_char_out_hex(const struct device *uart_dev, uint8_t *data, size_t length) { for (size_t i = 0; i < length; i++) { char c; @@ -63,8 +75,10 @@ static void dict_char_out_hex(uint8_t *data, size_t length) static int char_out(uint8_t *data, size_t length, void *ctx) { - ARG_UNUSED(ctx); int err; + const struct lbu_cb_ctx *cb_ctx = ctx; + struct lbu_data *lb_data = cb_ctx->data; + const struct device *uart_dev = cb_ctx->device; if (pm_device_runtime_is_enabled(uart_dev) && !k_is_in_isr()) { if (pm_device_runtime_get(uart_dev) < 0) { @@ -76,11 +90,12 @@ static int char_out(uint8_t *data, size_t length, void *ctx) } if (IS_ENABLED(CONFIG_LOG_BACKEND_UART_OUTPUT_DICTIONARY_HEX)) { - dict_char_out_hex(data, length); + dict_char_out_hex(uart_dev, data, length); goto cleanup; } - if (!IS_ENABLED(CONFIG_LOG_BACKEND_UART_ASYNC) || in_panic || !use_async) { + if (!IS_ENABLED(CONFIG_LOG_BACKEND_UART_ASYNC) || lb_data->in_panic || + !lb_data->use_async) { for (size_t i = 0; i < length; i++) { uart_poll_out(uart_dev, data[i]); } @@ -90,7 +105,7 @@ static int char_out(uint8_t *data, size_t length, void *ctx) err = uart_tx(uart_dev, data, length, SYS_FOREVER_US); __ASSERT_NO_MSG(err == 0); - err = k_sem_take(&sem, K_FOREVER); + err = k_sem_take(&lb_data->sem, K_FOREVER); __ASSERT_NO_MSG(err == 0); (void)err; @@ -103,29 +118,37 @@ cleanup: 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) { + const struct lbu_cb_ctx *ctx = backend->cb->ctx; + struct lbu_data *data = ctx->data; uint32_t flags = log_backend_std_get_flags(); + log_format_func_t log_output_func = log_format_func_t_get(data->log_format_current); - log_format_func_t log_output_func = log_format_func_t_get(log_format_current); - - log_output_func(&log_output_uart, &msg->log, flags); + log_output_func(ctx->output, &msg->log, flags); } static int format_set(const struct log_backend *const backend, uint32_t log_type) { - log_format_current = log_type; + const struct lbu_cb_ctx *ctx = backend->cb->ctx; + struct lbu_data *data = ctx->data; + + data->log_format_current = log_type; + return 0; } static void log_backend_uart_init(struct log_backend const *const backend) { + const struct lbu_cb_ctx *ctx = backend->cb->ctx; + const struct device *uart_dev = ctx->device; + struct lbu_data *data = ctx->data; + __ASSERT_NO_MSG(device_is_ready(uart_dev)); + log_output_ctx_set(ctx->output, (void *)ctx); + 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 @@ -140,20 +163,25 @@ static void log_backend_uart_init(struct log_backend const *const backend) } if (IS_ENABLED(CONFIG_LOG_BACKEND_UART_ASYNC)) { - int err = uart_callback_set(uart_dev, uart_callback, NULL); + int err = uart_callback_set(uart_dev, uart_callback, (void *)ctx); if (err == 0) { - use_async = true; - k_sem_init(&sem, 0, 1); + data->use_async = true; + k_sem_init(&data->sem, 0, 1); } else { LOG_WRN("Failed to initialize asynchronous mode (err:%d). " - "Fallback to polling.", err); + "Fallback to polling.", + err); } } } static void panic(struct log_backend const *const backend) { + const struct lbu_cb_ctx *ctx = backend->cb->ctx; + struct lbu_data *data = ctx->data; + const struct device *uart_dev = ctx->device; + /* Ensure that the UART device is in active mode */ #if defined(CONFIG_PM_DEVICE_RUNTIME) if (pm_device_runtime_is_enabled(uart_dev)) { @@ -172,20 +200,22 @@ static void panic(struct log_backend const *const backend) if ((rc == 0) && (pm_state == PM_DEVICE_STATE_SUSPENDED)) { pm_device_action_run(uart_dev, PM_DEVICE_ACTION_RESUME); } +#else + ARG_UNUSED(uart_dev); #endif /* CONFIG_PM_DEVICE */ - in_panic = true; - log_backend_std_panic(&log_output_uart); + data->in_panic = true; + log_backend_std_panic(ctx->output); } static void dropped(const struct log_backend *const backend, uint32_t cnt) { - ARG_UNUSED(backend); + const struct lbu_cb_ctx *ctx = backend->cb->ctx; if (IS_ENABLED(CONFIG_LOG_BACKEND_UART_OUTPUT_DICTIONARY)) { - log_dict_output_dropped_process(&log_output_uart, cnt); + log_dict_output_dropped_process(ctx->output, cnt); } else { - log_backend_std_dropped(&log_output_uart, cnt); + log_backend_std_dropped(ctx->output, cnt); } } @@ -197,5 +227,28 @@ const struct log_backend_api log_backend_uart_api = { .format_set = format_set, }; -LOG_BACKEND_DEFINE(log_backend_uart, log_backend_uart_api, - IS_ENABLED(CONFIG_LOG_BACKEND_UART_AUTOSTART)); +#define LBU_DEFINE(node_id, idx) \ + static uint8_t lbu_buffer_##idx[CONFIG_LOG_BACKEND_UART_BUFFER_SIZE]; \ + LOG_OUTPUT_DEFINE(lbu_output_##idx, char_out, lbu_buffer_##idx, \ + CONFIG_LOG_BACKEND_UART_BUFFER_SIZE); \ + \ + static struct lbu_data lbu_data_##idx = { \ + .log_format_current = CONFIG_LOG_BACKEND_UART_OUTPUT_DEFAULT, \ + }; \ + \ + static const struct lbu_cb_ctx lbu_cb_ctx_##idx = { \ + .output = &lbu_output_##idx, \ + .device = DEVICE_DT_GET(node_id), \ + .data = &lbu_data_##idx, \ + }; \ + \ + LOG_BACKEND_DEFINE(log_backend_uart##idx, log_backend_uart_api, \ + IS_ENABLED(CONFIG_LOG_BACKEND_UART_AUTOSTART), \ + (void *)&lbu_cb_ctx_##idx); + +#if DT_HAS_CHOSEN(zephyr_log_uart) +#define LBU_PHA_FN(node_id, prop, idx) LBU_DEFINE(DT_PHANDLE_BY_IDX(node_id, prop, idx), idx) +DT_FOREACH_PROP_ELEM_SEP(DT_CHOSEN(zephyr_log_uart), uarts, LBU_PHA_FN, ()); +#else +LBU_DEFINE(DT_CHOSEN(zephyr_console), 0); +#endif