zephyr/subsys/logging/log_frontend_dict_uart.c
Krzysztof Chruscinski bb74b4f028 lib: os: cbprintf: Renamed flags used for conversion function
At some point, package copy function was extended and renamed
to cbprintf_package_convert. However, flags used by this
function were not renamed and used contained COPY idiom.
Deprecating flags with COPY and replacing them with flags
with CONVERT idiom to match function which is utilizing them.

Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
2022-08-26 12:52:08 +02:00

355 lines
8.7 KiB
C

/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log_frontend.h>
#include <zephyr/logging/log_internal.h>
#include <zephyr/logging/log_output_dict.h>
#include <zephyr/sys/mpsc_pbuf.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
static uint32_t dbuf[CONFIG_LOG_FRONTEND_DICT_UART_BUFFER_SIZE / sizeof(uint32_t)];
struct log_frontend_uart_pkt_hdr {
MPSC_PBUF_HDR;
uint16_t len: 12;
uint32_t noff: 2;
} __packed;
BUILD_ASSERT(sizeof(struct log_frontend_uart_pkt_hdr) == sizeof(uint16_t));
struct log_frontend_uart_generic_pkt {
struct log_frontend_uart_pkt_hdr hdr;
uint8_t data[0];
} __packed;
struct log_frontend_uart_dropped_pkt {
struct log_frontend_uart_pkt_hdr hdr;
struct log_dict_output_dropped_msg_t data;
} __packed;
struct log_frontend_uart_pkt {
struct log_frontend_uart_pkt_hdr hdr;
struct log_dict_output_normal_msg_hdr_t data_hdr;
uint8_t data[0];
} __packed;
/* Union needed to avoid warning when casting to packed structure. */
union log_frontend_pkt {
struct log_frontend_uart_generic_pkt *generic;
struct log_frontend_uart_dropped_pkt *dropped;
struct log_frontend_uart_pkt *pkt;
const union mpsc_pbuf_generic *ro_pkt;
union mpsc_pbuf_generic *rw_pkt;
};
static uint32_t get_wlen(const union mpsc_pbuf_generic *packet)
{
struct log_frontend_uart_generic_pkt *hdr = (struct log_frontend_uart_generic_pkt *)packet;
return (uint32_t)hdr->hdr.len;
}
static void notify_drop(const struct mpsc_pbuf_buffer *buffer,
const union mpsc_pbuf_generic *packet)
{
}
static const struct mpsc_pbuf_buffer_config config = {
.buf = dbuf,
.size = ARRAY_SIZE(dbuf),
.notify_drop = notify_drop,
.get_wlen = get_wlen,
.flags = 0
};
static const struct device *const dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
static struct mpsc_pbuf_buffer buf;
static atomic_t active_cnt; /* Counts number of buffered messages. */
static atomic_t dropped; /* Counter storing number of dropped messages to be reported. */
static atomic_t adding_drop; /* Indicates that drop report message is being added. */
static volatile bool in_panic; /* Indicates that logging is in panic state. */
static bool dropped_notify; /* Indicate that drop report message should be added. */
static void tx(void);
static atomic_val_t add_drop_msg(void);
static void timeout(struct k_timer *timer)
{
if (dropped != 0) {
dropped_notify = true;
if (add_drop_msg() == 0) {
tx();
}
}
}
K_TIMER_DEFINE(dropped_timer, timeout, NULL);
/* Attempts to get pending message and initiate UART transfer. In panic it polls
* out the message in the blocking mode.
*/
static void tx(void)
{
union log_frontend_pkt generic_pkt;
struct log_frontend_uart_generic_pkt *pkt;
if (!IS_ENABLED(CONFIG_UART_ASYNC_API) && !in_panic) {
uart_irq_tx_enable(dev);
return;
}
generic_pkt.ro_pkt = mpsc_pbuf_claim(&buf);
pkt = generic_pkt.generic;
__ASSERT_NO_MSG(pkt == NULL);
size_t len = sizeof(uint32_t) * pkt->hdr.len - pkt->hdr.noff -
sizeof(struct log_frontend_uart_pkt_hdr);
if (in_panic) {
for (int i = 0; i < len; i++) {
uart_poll_out(dev, pkt->data[i]);
}
atomic_dec(&active_cnt);
} else {
int err = uart_tx(dev, pkt->data, len, SYS_FOREVER_US);
(void)err;
__ASSERT_NO_MSG(err == 0);
}
}
/* Add drop message and reset drop message count. */
static atomic_val_t add_drop_msg(void)
{
union log_frontend_pkt generic_pkt;
struct log_frontend_uart_dropped_pkt *pkt;
size_t len = sizeof(struct log_frontend_uart_dropped_pkt);
size_t wlen = ceiling_fraction(len, sizeof(uint32_t));
if (atomic_cas(&adding_drop, 0, 1) == false) {
return 1;
}
generic_pkt.rw_pkt = mpsc_pbuf_alloc(&buf, wlen, K_NO_WAIT);
pkt = generic_pkt.dropped;
if (pkt == NULL) {
return 1;
}
dropped_notify = false;
pkt->hdr.len = wlen;
pkt->hdr.noff = sizeof(uint32_t) * wlen - len;
pkt->data.type = MSG_DROPPED_MSG;
pkt->data.num_dropped_messages = atomic_set(&dropped, 0);
mpsc_pbuf_commit(&buf, generic_pkt.rw_pkt);
return atomic_inc(&active_cnt);
}
static void uart_callback(const struct device *dev,
struct uart_event *evt,
void *user_data)
{
switch (evt->type) {
case UART_TX_DONE:
{
union log_frontend_pkt generic_pkt;
generic_pkt.generic = CONTAINER_OF(evt->data.tx.buf,
struct log_frontend_uart_generic_pkt, hdr);
mpsc_pbuf_free(&buf, generic_pkt.ro_pkt);
atomic_val_t rem_pkts = atomic_dec(&active_cnt);
if (dropped_notify) {
rem_pkts = add_drop_msg();
}
if (rem_pkts > 1) {
tx();
}
}
break;
case UART_TX_ABORTED:
break;
default:
break;
}
}
static void uart_isr_callback(const struct device *dev, void *user_data)
{
static size_t curr_offset;
static union log_frontend_pkt isr_pkt;
if (uart_irq_update(dev) && uart_irq_tx_ready(dev)) {
if (isr_pkt.ro_pkt == NULL) {
isr_pkt.ro_pkt = mpsc_pbuf_claim(&buf);
__ASSERT_NO_MSG(isr_pkt.ro_pkt != NULL);
curr_offset = 0;
}
if (isr_pkt.ro_pkt != NULL) {
struct log_frontend_uart_generic_pkt *pkt = isr_pkt.generic;
size_t len = sizeof(uint32_t) * pkt->hdr.len - pkt->hdr.noff -
sizeof(struct log_frontend_uart_pkt_hdr);
size_t rem = len - curr_offset;
if (rem > 0) {
uint8_t *d = &pkt->data[curr_offset];
curr_offset += uart_fifo_fill(dev, d, rem);
} else {
mpsc_pbuf_free(&buf, isr_pkt.ro_pkt);
isr_pkt.ro_pkt = NULL;
static struct k_spinlock lock;
k_spinlock_key_t key = k_spin_lock(&lock);
active_cnt--;
if (active_cnt == 0) {
uart_irq_tx_disable(dev);
}
k_spin_unlock(&lock, key);
}
}
}
}
static inline void hdr_fill(struct log_dict_output_normal_msg_hdr_t *hdr,
const void *source,
const struct log_msg_desc desc)
{
hdr->type = MSG_NORMAL;
hdr->domain = desc.domain;
hdr->level = desc.level;
hdr->package_len = desc.package_len;
hdr->data_len = desc.data_len;
hdr->timestamp = z_log_timestamp();
hdr->source = (source != NULL) ?
(IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING) ?
log_dynamic_source_id((void *)source) :
log_const_source_id((void *)source)) :
0U;
}
/* Handle logging message in synchronous manner, in panic mode. */
static void sync_msg(const void *source,
const struct log_msg_desc desc,
uint8_t *package, const void *data)
{
struct log_dict_output_normal_msg_hdr_t hdr;
uint8_t *datas[3] = {(uint8_t *)&hdr, package, (uint8_t *)data};
size_t len[3] = {sizeof(hdr), desc.package_len, desc.data_len};
hdr_fill(&hdr, source, desc);
for (int i = 0; i < ARRAY_SIZE(datas); i++) {
for (int j = 0; j < len[i]; j++) {
uart_poll_out(dev, datas[i][j]);
}
}
}
void log_frontend_msg(const void *source,
const struct log_msg_desc desc,
uint8_t *package, const void *data)
{
uint16_t strl[4];
struct log_msg_desc outdesc = desc;
int plen = cbprintf_package_copy(package, desc.package_len, NULL, 0,
CBPRINTF_PACKAGE_CONVERT_RW_STR,
strl, ARRAY_SIZE(strl));
size_t dlen = desc.data_len;
bool dev_ready = device_is_ready(dev);
size_t total_len = plen + dlen + sizeof(struct log_frontend_uart_pkt);
size_t total_wlen = ceiling_fraction(total_len, sizeof(uint32_t));
if (in_panic) {
sync_msg(source, desc, package, data);
}
union log_frontend_pkt generic_pkt;
struct log_frontend_uart_pkt *pkt;
generic_pkt.rw_pkt = mpsc_pbuf_alloc(&buf, total_wlen, K_NO_WAIT);
pkt = generic_pkt.pkt;
if (pkt == NULL) {
/* Dropping */
atomic_inc(&dropped);
return;
}
pkt->hdr.len = total_wlen;
pkt->hdr.noff = sizeof(uint32_t) * total_wlen - total_len;
outdesc.package_len = plen;
hdr_fill(&pkt->data_hdr, source, outdesc);
plen = cbprintf_package_copy(package, desc.package_len,
pkt->data, plen,
CBPRINTF_PACKAGE_CONVERT_RW_STR,
strl, ARRAY_SIZE(strl));
if (plen < 0) {
/* error */
}
if (dlen != 0) {
memcpy(&pkt->data[plen], data, dlen);
}
mpsc_pbuf_commit(&buf, generic_pkt.rw_pkt);
if (dev_ready && (atomic_inc(&active_cnt) == 0)) {
tx();
}
}
void log_frontend_panic(void)
{
in_panic = true;
/* Flush all data */
while (atomic_get(&active_cnt) > 0) {
tx();
/* empty */
}
}
void log_frontend_init(void)
{
int err;
if (IS_ENABLED(CONFIG_UART_ASYNC_API)) {
err = uart_callback_set(dev, uart_callback, NULL);
} else if (IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN)) {
uart_irq_callback_user_data_set(dev, uart_isr_callback, NULL);
err = 0;
}
__ASSERT(err == 0, "Failed to set callback");
if (err < 0) {
return;
}
mpsc_pbuf_init(&buf, &config);
}
/* Cannot be started in log_frontend_init because it is called before kernel is ready. */
static int log_frontend_uart_start_timer(const struct device *unused)
{
ARG_UNUSED(unused);
k_timeout_t t = K_MSEC(CONFIG_LOG_FRONTEND_DICT_UART_DROPPED_NOTIFY_PERIOD);
k_timer_start(&dropped_timer, t, t);
return 0;
}
SYS_INIT(log_frontend_uart_start_timer, POST_KERNEL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);