blob: ba882b038eeec8d1e0e6b7ab32380f43c9e2b256 [file] [log] [blame] [edit]
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(uart_mux, CONFIG_UART_MUX_LOG_LEVEL);
#include <zephyr/sys/__assert.h>
#include <zephyr/kernel.h>
#include <zephyr/init.h>
#include <zephyr/syscall_handler.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/console/uart_mux.h>
#include <zephyr/sys/ring_buffer.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/iterable_sections.h>
#include "gsm_mux.h"
#if CONFIG_UART_MUX_DEVICE_COUNT == 0
#error "CONFIG_UART_MUX_DEVICE_COUNT tells number of DLCIs to create " \
"and must be >0"
#endif
#define UART_MUX_WORKQ_PRIORITY CONFIG_UART_MUX_RX_PRIORITY
#define UART_MUX_WORKQ_STACK_SIZE CONFIG_UART_MUX_RX_STACK_SIZE
/* All the RX/TX data is passed via own workqueue. This is done like this
* as the GSM modem uses global workqueue which causes difficulties if we do
* the same here. This workqueue is shared between all the DLCI channels.
*/
K_KERNEL_STACK_DEFINE(uart_mux_stack, UART_MUX_WORKQ_STACK_SIZE);
static struct k_work_q uart_mux_workq;
/* The UART mux contains information about the real UART. It will synchronize
* the access to the real UART and pass data between it and GSM muxing API.
* Usually there is only one instance of these in the system, if we have only
* one UART connected to modem device.
*/
struct uart_mux {
/* The real UART device that is shared between muxed UARTs */
const struct device *uart;
/* GSM mux related to this UART */
struct gsm_mux *mux;
/* Received data is routed from ISR to MUX API via ring buffer */
struct ring_buf *rx_ringbuf;
/* RX worker that passes data from RX ISR to GSM mux API */
struct k_work rx_work;
/* Mutex for accessing the real UART */
struct k_mutex lock;
/* Flag that tells whether this instance is initialized or not */
atomic_t init_done;
/* Temporary buffer when reading data in ISR */
uint8_t rx_buf[CONFIG_UART_MUX_TEMP_BUF_SIZE];
};
#define DEFINE_UART_MUX(x, _) \
RING_BUF_DECLARE(uart_rx_ringbuf_##x, \
CONFIG_UART_MUX_RINGBUF_SIZE); \
STRUCT_SECTION_ITERABLE(uart_mux, uart_mux_##x) = { \
.rx_ringbuf = &uart_rx_ringbuf_##x, \
}
LISTIFY(CONFIG_UART_MUX_REAL_DEVICE_COUNT, DEFINE_UART_MUX, (;), _);
STRUCT_SECTION_START_EXTERN(uart_mux);
STRUCT_SECTION_END_EXTERN(uart_mux);
/* UART Mux Driver Status Codes */
enum uart_mux_status_code {
UART_MUX_UNKNOWN, /* Initial connection status */
UART_MUX_CONFIGURED, /* UART mux configuration done */
UART_MUX_CONNECTED, /* UART mux connected */
UART_MUX_DISCONNECTED, /* UART mux connection lost */
};
struct uart_mux_config {
};
struct uart_mux_dev_data {
sys_snode_t node;
/* Configuration data */
struct uart_mux_config cfg;
/* This UART mux device */
const struct device *dev;
/* The UART device where we are running on top of */
struct uart_mux *real_uart;
/* TX worker that will mux the transmitted data */
struct k_work tx_work;
/* ISR function callback worker */
struct k_work cb_work;
/* ISR function callback */
uart_irq_callback_user_data_t cb;
void *cb_user_data;
/* Attach callback */
uart_mux_attach_cb_t attach_cb;
void *attach_user_data;
/* TX data from application is handled via ring buffer */
struct ring_buf *tx_ringbuf;
/* Received data is routed from RX worker to application via ring
* buffer.
*/
struct ring_buf *rx_ringbuf;
/* Muxing status */
enum uart_mux_status_code status;
/* DLCI (muxing virtual channel) linked to this muxed UART */
struct gsm_dlci *dlci;
/* Status (enabled / disabled) for RX and TX */
bool rx_enabled : 1;
bool tx_enabled : 1;
bool rx_ready : 1;
bool tx_ready : 1;
bool in_use : 1;
};
struct uart_mux_cfg_data {
};
static sys_slist_t uart_mux_data_devlist;
static void uart_mux_cb_work(struct k_work *work)
{
struct uart_mux_dev_data *dev_data =
CONTAINER_OF(work, struct uart_mux_dev_data, cb_work);
dev_data->cb(dev_data->dev, dev_data->cb_user_data);
}
static int uart_mux_consume_ringbuf(struct uart_mux *uart_mux)
{
uint8_t *data;
size_t len;
int ret;
len = ring_buf_get_claim(uart_mux->rx_ringbuf, &data,
CONFIG_UART_MUX_RINGBUF_SIZE);
if (len == 0) {
LOG_DBG("Ringbuf %p is empty!", uart_mux->rx_ringbuf);
return 0;
}
/* We have now received muxed data. Push that through GSM mux API which
* will parse it and call proper functions to get the data to the user.
*/
if (IS_ENABLED(CONFIG_UART_MUX_VERBOSE_DEBUG)) {
char tmp[sizeof("RECV muxed ") + 10];
snprintk(tmp, sizeof(tmp), "RECV muxed %s",
uart_mux->uart->name);
LOG_HEXDUMP_DBG(data, len, tmp);
}
gsm_mux_recv_buf(uart_mux->mux, data, len);
ret = ring_buf_get_finish(uart_mux->rx_ringbuf, len);
if (ret < 0) {
LOG_DBG("Cannot flush ring buffer (%d)", ret);
}
return -EAGAIN;
}
static void uart_mux_rx_work(struct k_work *work)
{
struct uart_mux *uart_mux =
CONTAINER_OF(work, struct uart_mux, rx_work);;
int ret;
do {
ret = uart_mux_consume_ringbuf(uart_mux);
} while (ret == -EAGAIN);
}
static void uart_mux_tx_work(struct k_work *work)
{
struct uart_mux_dev_data *dev_data =
CONTAINER_OF(work, struct uart_mux_dev_data, tx_work);
uint8_t *data;
size_t len;
len = ring_buf_get_claim(dev_data->tx_ringbuf, &data,
CONFIG_UART_MUX_RINGBUF_SIZE);
if (!len) {
LOG_DBG("Ringbuf %p empty!", dev_data->tx_ringbuf);
return;
}
LOG_DBG("Got %ld bytes from ringbuffer send to uart %p", (unsigned long)len,
dev_data->dev);
if (IS_ENABLED(CONFIG_UART_MUX_VERBOSE_DEBUG)) {
char tmp[sizeof("SEND _x") +
sizeof(CONFIG_UART_MUX_DEVICE_NAME)];
snprintk(tmp, sizeof(tmp), "SEND %s",
dev_data->dev->name);
LOG_HEXDUMP_DBG(data, len, tmp);
}
(void)gsm_dlci_send(dev_data->dlci, data, len);
ring_buf_get_finish(dev_data->tx_ringbuf, len);
}
static int uart_mux_init(const struct device *dev)
{
struct uart_mux_dev_data *dev_data = dev->data;
gsm_mux_init();
dev_data->dev = dev;
dev_data->real_uart = NULL; /* will be set when user attach to it */
sys_slist_find_and_remove(&uart_mux_data_devlist, &dev_data->node);
sys_slist_prepend(&uart_mux_data_devlist, &dev_data->node);
k_work_init(&dev_data->tx_work, uart_mux_tx_work);
k_work_init(&dev_data->cb_work, uart_mux_cb_work);
LOG_DBG("Device %s dev %p dev_data %p cfg %p created",
dev->name, dev, dev_data, dev->config);
return 0;
}
/* This IRQ handler is shared between muxing UARTs. After we have received
* data from it in uart_mux_rx_work(), we push the data to GSM mux API which
* will call proper callbacks to pass data to correct recipient.
*/
static void uart_mux_isr(const struct device *uart, void *user_data)
{
struct uart_mux *real_uart = user_data;
int rx = 0;
size_t wrote = 0;
/* Read all data off UART, and send to RX worker for unmuxing */
while (uart_irq_update(uart) &&
uart_irq_rx_ready(uart)) {
rx = uart_fifo_read(uart, real_uart->rx_buf,
sizeof(real_uart->rx_buf));
if (rx <= 0) {
continue;
}
wrote = ring_buf_put(real_uart->rx_ringbuf,
real_uart->rx_buf, rx);
if (wrote < rx) {
LOG_ERR("Ring buffer full, drop %ld bytes", (long)(rx - wrote));
}
k_work_submit_to_queue(&uart_mux_workq, &real_uart->rx_work);
}
}
static void uart_mux_flush_isr(const struct device *dev)
{
uint8_t c;
while (uart_fifo_read(dev, &c, 1) > 0) {
continue;
}
}
void uart_mux_disable(const struct device *dev)
{
struct uart_mux_dev_data *dev_data = dev->data;
const struct device *uart = dev_data->real_uart->uart;
uart_irq_rx_disable(uart);
uart_irq_tx_disable(uart);
uart_mux_flush_isr(uart);
gsm_mux_detach(dev_data->real_uart->mux);
}
void uart_mux_enable(const struct device *dev)
{
struct uart_mux_dev_data *dev_data = dev->data;
struct uart_mux *real_uart = dev_data->real_uart;
LOG_DBG("Claiming uart for uart_mux");
uart_irq_rx_disable(real_uart->uart);
uart_irq_tx_disable(real_uart->uart);
uart_mux_flush_isr(real_uart->uart);
uart_irq_callback_user_data_set(
real_uart->uart, uart_mux_isr,
real_uart);
uart_irq_rx_enable(real_uart->uart);
}
static void dlci_created_cb(struct gsm_dlci *dlci, bool connected,
void *user_data)
{
struct uart_mux_dev_data *dev_data = user_data;
if (connected) {
dev_data->status = UART_MUX_CONNECTED;
} else {
dev_data->status = UART_MUX_DISCONNECTED;
}
LOG_DBG("%s %s", dev_data->dev->name,
dev_data->status == UART_MUX_CONNECTED ? "connected" :
"disconnected");
if (dev_data->attach_cb) {
dev_data->attach_cb(dev_data->dev,
dlci ? gsm_dlci_id(dlci) : -1,
connected,
dev_data->attach_user_data);
}
}
static int init_real_uart(const struct device *mux, const struct device *uart,
struct uart_mux **mux_uart)
{
bool found = false;
struct uart_mux *real_uart;
for (real_uart = TYPE_SECTION_START(uart_mux);
real_uart != TYPE_SECTION_END(uart_mux);
real_uart++) {
if (real_uart->uart == uart) {
found = true;
break;
}
}
if (found == false) {
for (real_uart = TYPE_SECTION_START(uart_mux);
real_uart != TYPE_SECTION_END(uart_mux);
real_uart++) {
if (real_uart->uart == NULL) {
real_uart->uart = uart;
found = true;
break;
}
}
if (found == false) {
return -ENOENT;
}
}
/* Init the real UART only once */
if (atomic_cas(&real_uart->init_done, false, true)) {
real_uart->mux = gsm_mux_create(mux);
LOG_DBG("Initializing UART %s and GSM mux %p",
real_uart->uart->name, (void *)real_uart->mux);
if (!real_uart->mux) {
real_uart->uart = NULL;
atomic_clear(&real_uart->init_done);
return -ENOMEM;
}
k_work_init(&real_uart->rx_work, uart_mux_rx_work);
k_mutex_init(&real_uart->lock);
uart_irq_rx_disable(real_uart->uart);
uart_irq_tx_disable(real_uart->uart);
uart_mux_flush_isr(real_uart->uart);
uart_irq_callback_user_data_set(
real_uart->uart, uart_mux_isr,
real_uart);
uart_irq_rx_enable(real_uart->uart);
}
__ASSERT(real_uart->uart, "Real UART not set");
*mux_uart = real_uart;
return 0;
}
/* This will bind the physical (real) UART to this muxed UART */
static int attach(const struct device *mux_uart, const struct device *uart,
int dlci_address, uart_mux_attach_cb_t cb,
void *user_data)
{
sys_snode_t *sn, *sns;
if (mux_uart == NULL || uart == NULL) {
return -EINVAL;
}
LOG_DBG("Attach DLCI %d (%s) to %s", dlci_address,
mux_uart->name, uart->name);
SYS_SLIST_FOR_EACH_NODE_SAFE(&uart_mux_data_devlist, sn, sns) {
struct uart_mux_dev_data *dev_data =
CONTAINER_OF(sn, struct uart_mux_dev_data, node);
if (dev_data->dev == mux_uart) {
struct uart_mux *real_uart;
int ret;
ret = init_real_uart(mux_uart, uart, &real_uart);
if (ret < 0) {
return ret;
}
dev_data->real_uart = real_uart;
dev_data->tx_ready = true;
dev_data->tx_enabled = true;
dev_data->rx_enabled = true;
dev_data->attach_cb = cb;
dev_data->attach_user_data = user_data;
dev_data->status = UART_MUX_CONFIGURED;
ret = gsm_dlci_create(real_uart->mux,
mux_uart,
dlci_address,
dlci_created_cb,
dev_data,
&dev_data->dlci);
if (ret < 0) {
LOG_DBG("Cannot create DLCI %d (%d)",
dlci_address, ret);
return ret;
}
return 0;
}
}
return -ENOENT;
}
static int uart_mux_poll_in(const struct device *dev, unsigned char *p_char)
{
ARG_UNUSED(dev);
ARG_UNUSED(p_char);
return -ENOTSUP;
}
static void uart_mux_poll_out(const struct device *dev,
unsigned char out_char)
{
struct uart_mux_dev_data *dev_data = dev->data;
if (dev_data->dev == NULL) {
return;
}
(void)gsm_dlci_send(dev_data->dlci, &out_char, 1);
}
static int uart_mux_err_check(const struct device *dev)
{
ARG_UNUSED(dev);
return -ENOTSUP;
}
static int uart_mux_configure(const struct device *dev,
const struct uart_config *cfg)
{
ARG_UNUSED(dev);
ARG_UNUSED(cfg);
return -ENOTSUP;
}
static int uart_mux_config_get(const struct device *dev,
struct uart_config *cfg)
{
ARG_UNUSED(dev);
ARG_UNUSED(cfg);
return -ENOTSUP;
}
static int uart_mux_fifo_fill(const struct device *dev,
const uint8_t *tx_data, int len)
{
struct uart_mux_dev_data *dev_data;
size_t wrote;
if (dev == NULL) {
return -EINVAL;
}
dev_data = dev->data;
if (dev_data->dev == NULL) {
return -ENOENT;
}
/* If we're not in ISR context, do the xfer synchronously. This
* effectively let's applications use this implementation of fifo_fill
* as a multi-byte poll_out which prevents each byte getting wrapped by
* mux headers.
*/
if (!k_is_in_isr() && dev_data->dlci) {
return gsm_dlci_send(dev_data->dlci, tx_data, len);
}
LOG_DBG("dev_data %p len %d tx_ringbuf space %u",
dev_data, len, ring_buf_space_get(dev_data->tx_ringbuf));
if (dev_data->status != UART_MUX_CONNECTED) {
LOG_WRN("UART mux not connected, drop %d bytes", len);
return 0;
}
dev_data->tx_ready = false;
wrote = ring_buf_put(dev_data->tx_ringbuf, tx_data, len);
if (wrote < len) {
LOG_WRN("Ring buffer full, drop %ld bytes", (long)(len - wrote));
}
k_work_submit_to_queue(&uart_mux_workq, &dev_data->tx_work);
return wrote;
}
static int uart_mux_fifo_read(const struct device *dev, uint8_t *rx_data,
const int size)
{
struct uart_mux_dev_data *dev_data;
uint32_t len;
if (dev == NULL) {
return -EINVAL;
}
dev_data = dev->data;
if (dev_data->dev == NULL) {
return -ENOENT;
}
LOG_DBG("%s size %d rx_ringbuf space %u",
dev->name, size,
ring_buf_space_get(dev_data->rx_ringbuf));
len = ring_buf_get(dev_data->rx_ringbuf, rx_data, size);
if (ring_buf_is_empty(dev_data->rx_ringbuf)) {
dev_data->rx_ready = false;
}
return len;
}
static void uart_mux_irq_tx_enable(const struct device *dev)
{
struct uart_mux_dev_data *dev_data = dev->data;
if (dev_data == NULL || dev_data->dev == NULL) {
return;
}
dev_data->tx_enabled = true;
if (dev_data->cb && dev_data->tx_ready) {
k_work_submit_to_queue(&uart_mux_workq, &dev_data->cb_work);
}
}
static void uart_mux_irq_tx_disable(const struct device *dev)
{
struct uart_mux_dev_data *dev_data = dev->data;
if (dev_data == NULL || dev_data->dev == NULL) {
return;
}
dev_data->tx_enabled = false;
}
static int uart_mux_irq_tx_ready(const struct device *dev)
{
struct uart_mux_dev_data *dev_data = dev->data;
if (dev_data == NULL) {
return -EINVAL;
}
if (dev_data->dev == NULL) {
return -ENOENT;
}
return dev_data->tx_ready;
}
static void uart_mux_irq_rx_enable(const struct device *dev)
{
struct uart_mux_dev_data *dev_data = dev->data;
if (dev_data == NULL || dev_data->dev == NULL) {
return;
}
dev_data->rx_enabled = true;
if (dev_data->cb && dev_data->rx_ready) {
k_work_submit_to_queue(&uart_mux_workq, &dev_data->cb_work);
}
}
static void uart_mux_irq_rx_disable(const struct device *dev)
{
struct uart_mux_dev_data *dev_data = dev->data;
if (dev_data == NULL || dev_data->dev == NULL) {
return;
}
dev_data->rx_enabled = false;
}
static int uart_mux_irq_tx_complete(const struct device *dev)
{
ARG_UNUSED(dev);
return -ENOTSUP;
}
static int uart_mux_irq_rx_ready(const struct device *dev)
{
struct uart_mux_dev_data *dev_data = dev->data;
if (dev_data == NULL) {
return -EINVAL;
}
if (dev_data->dev == NULL) {
return -ENOENT;
}
return dev_data->rx_ready;
}
static void uart_mux_irq_err_enable(const struct device *dev)
{
ARG_UNUSED(dev);
}
static void uart_mux_irq_err_disable(const struct device *dev)
{
ARG_UNUSED(dev);
}
static int uart_mux_irq_is_pending(const struct device *dev)
{
struct uart_mux_dev_data *dev_data = dev->data;
if (dev_data == NULL || dev_data->dev == NULL) {
return 0;
}
if (dev_data->tx_ready && dev_data->tx_enabled) {
return 1;
}
if (dev_data->rx_ready && dev_data->rx_enabled) {
return 1;
}
return 0;
}
static int uart_mux_irq_update(const struct device *dev)
{
ARG_UNUSED(dev);
return 1;
}
static void uart_mux_irq_callback_set(const struct device *dev,
uart_irq_callback_user_data_t cb,
void *user_data)
{
struct uart_mux_dev_data *dev_data = dev->data;
if (dev_data == NULL) {
return;
}
dev_data->cb = cb;
dev_data->cb_user_data = user_data;
}
static struct uart_mux_driver_api uart_mux_driver_api = {
.uart_api.poll_in = uart_mux_poll_in,
.uart_api.poll_out = uart_mux_poll_out,
.uart_api.err_check = uart_mux_err_check,
.uart_api.configure = uart_mux_configure,
.uart_api.config_get = uart_mux_config_get,
.uart_api.fifo_fill = uart_mux_fifo_fill,
.uart_api.fifo_read = uart_mux_fifo_read,
.uart_api.irq_tx_enable = uart_mux_irq_tx_enable,
.uart_api.irq_tx_disable = uart_mux_irq_tx_disable,
.uart_api.irq_tx_ready = uart_mux_irq_tx_ready,
.uart_api.irq_rx_enable = uart_mux_irq_rx_enable,
.uart_api.irq_rx_disable = uart_mux_irq_rx_disable,
.uart_api.irq_tx_complete = uart_mux_irq_tx_complete,
.uart_api.irq_rx_ready = uart_mux_irq_rx_ready,
.uart_api.irq_err_enable = uart_mux_irq_err_enable,
.uart_api.irq_err_disable = uart_mux_irq_err_disable,
.uart_api.irq_is_pending = uart_mux_irq_is_pending,
.uart_api.irq_update = uart_mux_irq_update,
.uart_api.irq_callback_set = uart_mux_irq_callback_set,
.attach = attach,
};
const struct device *uart_mux_alloc(void)
{
sys_snode_t *sn, *sns;
SYS_SLIST_FOR_EACH_NODE_SAFE(&uart_mux_data_devlist, sn, sns) {
struct uart_mux_dev_data *dev_data =
CONTAINER_OF(sn, struct uart_mux_dev_data, node);
if (dev_data->in_use) {
continue;
}
dev_data->in_use = true;
return dev_data->dev;
}
return NULL;
}
#ifdef CONFIG_USERSPACE
static inline const struct device *z_vrfy_uart_mux_find(int dlci_address)
{
return z_impl_uart_mux_find(dlci_address);
}
#include <syscalls/uart_mux_find_mrsh.c>
#endif /* CONFIG_USERSPACE */
const struct device *z_impl_uart_mux_find(int dlci_address)
{
sys_snode_t *sn, *sns;
SYS_SLIST_FOR_EACH_NODE_SAFE(&uart_mux_data_devlist, sn, sns) {
struct uart_mux_dev_data *dev_data =
CONTAINER_OF(sn, struct uart_mux_dev_data, node);
if (!dev_data->in_use) {
continue;
}
if (dev_data->dlci == NULL) {
continue;
}
if (gsm_dlci_id(dev_data->dlci) == dlci_address) {
return dev_data->dev;
}
}
return NULL;
}
int uart_mux_send(const struct device *uart, const uint8_t *buf, size_t size)
{
struct uart_mux_dev_data *dev_data = uart->data;
size_t remaining = size;
if (size == 0) {
return 0;
}
if (atomic_get(&dev_data->real_uart->init_done) == false) {
return -ENODEV;
}
if (IS_ENABLED(CONFIG_UART_MUX_VERBOSE_DEBUG)) {
char tmp[sizeof("SEND muxed ") + 10];
snprintk(tmp, sizeof(tmp), "SEND muxed %s",
dev_data->real_uart->uart->name);
LOG_HEXDUMP_DBG(buf, size, tmp);
}
k_mutex_lock(&dev_data->real_uart->lock, K_FOREVER);
do {
uart_poll_out(dev_data->real_uart->uart, *buf++);
} while (--remaining);
k_mutex_unlock(&dev_data->real_uart->lock);
return size;
}
int uart_mux_recv(const struct device *mux, struct gsm_dlci *dlci,
uint8_t *data,
size_t len)
{
struct uart_mux_dev_data *dev_data = mux->data;
size_t wrote = 0;
LOG_DBG("%s: dlci %p data %p len %zd", mux->name, (void *)dlci,
data, len);
if (IS_ENABLED(CONFIG_UART_MUX_VERBOSE_DEBUG)) {
char tmp[sizeof("RECV _x") +
sizeof(CONFIG_UART_MUX_DEVICE_NAME)];
snprintk(tmp, sizeof(tmp), "RECV %s",
dev_data->dev->name);
LOG_HEXDUMP_DBG(data, len, tmp);
}
wrote = ring_buf_put(dev_data->rx_ringbuf, data, len);
if (wrote < len) {
LOG_ERR("Ring buffer full, drop %ld bytes", (long)(len - wrote));
}
dev_data->rx_ready = true;
if (dev_data->cb && dev_data->rx_enabled) {
k_work_submit_to_queue(&uart_mux_workq, &dev_data->cb_work);
}
return wrote;
}
void uart_mux_foreach(uart_mux_cb_t cb, void *user_data)
{
sys_snode_t *sn, *sns;
SYS_SLIST_FOR_EACH_NODE_SAFE(&uart_mux_data_devlist, sn, sns) {
struct uart_mux_dev_data *dev_data =
CONTAINER_OF(sn, struct uart_mux_dev_data, node);
if (!dev_data->in_use) {
continue;
}
cb(dev_data->real_uart->uart, dev_data->dev,
dev_data->dlci ? gsm_dlci_id(dev_data->dlci) : -1,
user_data);
}
}
#define DEFINE_UART_MUX_CFG_DATA(x, _) \
struct uart_mux_cfg_data uart_mux_config_##x = { \
}
#define DEFINE_UART_MUX_DEV_DATA(x, _) \
RING_BUF_DECLARE(tx_ringbuf_##x, CONFIG_UART_MUX_RINGBUF_SIZE); \
RING_BUF_DECLARE(rx_ringbuf_##x, CONFIG_UART_MUX_RINGBUF_SIZE); \
static struct uart_mux_dev_data uart_mux_dev_data_##x = { \
.tx_ringbuf = &tx_ringbuf_##x, \
.rx_ringbuf = &rx_ringbuf_##x, \
}
#define DEFINE_UART_MUX_DEVICE(x, _) \
DEVICE_DEFINE(uart_mux_##x, \
CONFIG_UART_MUX_DEVICE_NAME "_" #x, \
&uart_mux_init, \
NULL, \
&uart_mux_dev_data_##x, \
&uart_mux_config_##x, \
POST_KERNEL, \
CONFIG_CONSOLE_INIT_PRIORITY, \
&uart_mux_driver_api)
LISTIFY(CONFIG_UART_MUX_DEVICE_COUNT, DEFINE_UART_MUX_CFG_DATA, (;), _);
LISTIFY(CONFIG_UART_MUX_DEVICE_COUNT, DEFINE_UART_MUX_DEV_DATA, (;), _);
LISTIFY(CONFIG_UART_MUX_DEVICE_COUNT, DEFINE_UART_MUX_DEVICE, (;), _);
static int init_uart_mux(void)
{
k_work_queue_start(&uart_mux_workq, uart_mux_stack,
K_KERNEL_STACK_SIZEOF(uart_mux_stack),
K_PRIO_COOP(UART_MUX_WORKQ_PRIORITY), NULL);
k_thread_name_set(&uart_mux_workq.thread, "uart_mux_workq");
return 0;
}
SYS_INIT(init_uart_mux, POST_KERNEL, CONFIG_CONSOLE_INIT_PRIORITY);