zephyr/drivers/console/uart_mux.c
Yong Cong Sin bbe5e1e6eb build: namespace the generated headers with zephyr/
Namespaced the generated headers with `zephyr` to prevent
potential conflict with other headers.

Introduce a temporary Kconfig `LEGACY_GENERATED_INCLUDE_PATH`
that is enabled by default. This allows the developers to
continue the use of the old include paths for the time being
until it is deprecated and eventually removed. The Kconfig will
generate a build-time warning message, similar to the
`CONFIG_TIMER_RANDOM_GENERATOR`.

Updated the includes path of in-tree sources accordingly.

Most of the changes here are scripted, check the PR for more
info.

Signed-off-by: Yong Cong Sin <ycsin@meta.com>
2024-05-28 22:03:55 +02:00

890 lines
21 KiB
C

/*
* 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/internal/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_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.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 <zephyr/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);