drivers: console: uart_mux: Initial UART muxing support

Create support for muxed UARTs which are attached to a real
UART and which use GSM 07.10 muxing protocol to create virtual
channels that can be run on top of the real UART.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2020-04-01 12:47:53 +03:00
commit 420b195b5e
7 changed files with 1049 additions and 0 deletions

View file

@ -9,4 +9,5 @@ zephyr_sources_if_kconfig(uart_mcumgr.c)
zephyr_sources_if_kconfig(uart_pipe.c)
zephyr_sources_if_kconfig(xtensa_sim_console.c)
zephyr_sources_if_kconfig(native_posix_console.c)
zephyr_sources_if_kconfig(uart_mux.c)
zephyr_sources_if_kconfig(gsm_mux.c)

View file

@ -297,4 +297,76 @@ source "subsys/logging/Kconfig.template.log_config"
source "drivers/console/Kconfig.gsm_mux"
config UART_MUX
bool "Enable UART muxing (GSM 07.10) support [EXPERIMENTAL]"
depends on SERIAL_SUPPORT_INTERRUPT && GSM_MUX
select UART_INTERRUPT_DRIVEN
help
Enable this option to create UART muxer that run over a physical
UART. The GSM 07.10 muxing protocol is used to separate the data
between these muxed UARTs.
config UART_MUX_INIT_PRIORITY
int "Init priority"
default 95
depends on UART_MUX
help
Device driver initialization priority. UART mux has to be
initialized after the UART driver it uses.
config UART_MUX_DEVICE_NAME
string "UART mux device name template"
default "GSM"
depends on UART_MUX
help
Device name template for the UART mux Devices. First device would
have name $(UART_MUX_DEVICE_NAME)_0, etc. User will access muxed
UART using this name.
config UART_MUX_RINGBUF_SIZE
int "UART mux ring buffer size"
default 256
depends on UART_MUX
help
UART mux ring buffer size when passing data from RX ISR to worker
thread that will do the unmuxing.
config UART_MUX_TEMP_BUF_SIZE
int "Size of the temp buffer when reading data from real UART"
default 32
depends on UART_MUX
help
Size of the temporary RX buffer in receiving ISR.
config UART_MUX_DEVICE_COUNT
int "Number of UART mux devices (DLCIs)"
default GSM_MUX_DLCI_MAX
depends on UART_MUX
range 1 64
help
Number of instances of UART muxes. Default value is set by
maximum number of DLCIs (Data Link Connection Identifiers)
configured in the system.
config UART_MUX_REAL_DEVICE_COUNT
int "Number of real UART devices"
default 1
depends on UART_MUX
help
Tells how many real UART devices there are. Typically there is
only one UART and the muxed UARTs are run on top of that. If you have
two modems for example, then you would need to increase this to two.
module = UART_MUX
module-str = UART mux
source "subsys/logging/Kconfig.template.log_config"
config UART_MUX_VERBOSE_DEBUG
bool "Print hexdump of sent and received packets"
depends on UART_MUX_LOG_LEVEL_DBG
help
As there might be lot of debug output printed, only enable
this if really needed.
endif

807
drivers/console/uart_mux.c Normal file
View file

@ -0,0 +1,807 @@
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(uart_mux, CONFIG_UART_MUX_LOG_LEVEL);
#include <sys/__assert.h>
#include <kernel.h>
#include <syscall_handler.h>
#include <device.h>
#include <drivers/uart.h>
#include <drivers/console/uart_mux.h>
#include <sys/ring_buffer.h>
#include <sys/util.h>
#include <sys/atomic.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
/* 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 */
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 */
u8_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); \
static struct uart_mux uart_mux_##x __used \
__attribute__((__section__(".uart_mux.data"))) = { \
.rx_ringbuf = &uart_rx_ringbuf_##x, \
};
UTIL_LISTIFY(CONFIG_UART_MUX_REAL_DEVICE_COUNT, DEFINE_UART_MUX, _)
extern struct uart_mux __uart_mux_start[];
extern struct uart_mux __uart_mux_end[];
/* 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 {
};
#define DEV_DATA(dev) \
((struct uart_mux_dev_data *)(dev)->driver_data)
struct uart_mux_dev_data {
sys_snode_t node;
/* Configuration data */
struct uart_mux_config cfg;
/* This UART mux device */
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->cb_user_data);
}
static void uart_mux_rx_work(struct k_work *work)
{
struct uart_mux *uart_mux;
u8_t *data;
size_t len;
int ret;
uart_mux = CONTAINER_OF(work, struct uart_mux, rx_work);
/* 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.
*/
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;
}
if (IS_ENABLED(CONFIG_UART_MUX_VERBOSE_DEBUG)) {
char tmp[sizeof("RECV muxed ") + 10];
snprintk(tmp, sizeof(tmp), "RECV muxed %s",
uart_mux->uart->config->name);
LOG_HEXDUMP_DBG(data, len, log_strdup(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);
}
}
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);
u8_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 %d bytes from ringbuffer send to uart %p", 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->config->name);
LOG_HEXDUMP_DBG(data, len, log_strdup(tmp));
}
(void)gsm_dlci_send(dev_data->dlci, data, len);
ring_buf_get_finish(dev_data->tx_ringbuf, len);
}
static int uart_mux_init(struct device *dev)
{
struct uart_mux_dev_data *dev_data = DEV_DATA(dev);
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->config->name, dev, dev_data, dev->config->config_info);
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(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(real_uart->uart) &&
uart_irq_rx_ready(real_uart->uart)) {
rx = uart_fifo_read(real_uart->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 %d bytes",
rx - wrote);
}
k_work_submit(&real_uart->rx_work);
}
}
static void uart_mux_flush_isr(struct device *dev)
{
u8_t c;
while (uart_fifo_read(dev, &c, 1) > 0) {
continue;
}
}
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->config->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(struct device *mux, struct device *uart,
struct uart_mux **mux_uart)
{
bool found = false;
struct uart_mux *real_uart;
for (real_uart = __uart_mux_start; real_uart != __uart_mux_end;
real_uart++) {
if (real_uart->uart == uart) {
found = true;
break;
}
}
if (found == false) {
for (real_uart = __uart_mux_start; real_uart != __uart_mux_end;
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->config->name, 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(struct device *mux_uart, 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->config->name, uart->config->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(struct device *dev, unsigned char *p_char)
{
ARG_UNUSED(dev);
ARG_UNUSED(p_char);
return -ENOTSUP;
}
static void uart_mux_poll_out(struct device *dev, unsigned char out_char)
{
struct uart_mux_dev_data *dev_data = DEV_DATA(dev);
if (dev_data->dev == NULL) {
return;
}
(void)gsm_dlci_send(dev_data->dlci, &out_char, 1);
}
static int uart_mux_err_check(struct device *dev)
{
ARG_UNUSED(dev);
return -ENOTSUP;
}
static int uart_mux_configure(struct device *dev,
const struct uart_config *cfg)
{
ARG_UNUSED(dev);
ARG_UNUSED(cfg);
return -ENOTSUP;
}
static int uart_mux_config_get(struct device *dev, struct uart_config *cfg)
{
ARG_UNUSED(dev);
ARG_UNUSED(cfg);
return -ENOTSUP;
}
static int uart_mux_fifo_fill(struct device *dev, const u8_t *tx_data, int len)
{
struct uart_mux_dev_data *dev_data;
size_t wrote;
if (dev == NULL) {
return -EINVAL;
}
dev_data = DEV_DATA(dev);
if (dev_data->dev == NULL) {
return -ENOENT;
}
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 %d bytes", len - wrote);
}
k_work_submit(&dev_data->tx_work);
return wrote;
}
static int uart_mux_fifo_read(struct device *dev, u8_t *rx_data, const int size)
{
struct uart_mux_dev_data *dev_data;
u32_t len;
if (dev == NULL) {
return -EINVAL;
}
dev_data = DEV_DATA(dev);
if (dev_data->dev == NULL) {
return -ENOENT;
}
LOG_DBG("%s size %d rx_ringbuf space %u",
dev->config->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(struct device *dev)
{
struct uart_mux_dev_data *dev_data = DEV_DATA(dev);
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(&dev_data->cb_work);
}
}
static void uart_mux_irq_tx_disable(struct device *dev)
{
struct uart_mux_dev_data *dev_data = DEV_DATA(dev);
if (dev_data == NULL || dev_data->dev == NULL) {
return;
}
dev_data->tx_enabled = false;
}
static int uart_mux_irq_tx_ready(struct device *dev)
{
struct uart_mux_dev_data *dev_data = DEV_DATA(dev);
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(struct device *dev)
{
struct uart_mux_dev_data *dev_data = DEV_DATA(dev);
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(&dev_data->cb_work);
}
}
static void uart_mux_irq_rx_disable(struct device *dev)
{
struct uart_mux_dev_data *dev_data = DEV_DATA(dev);
if (dev_data == NULL || dev_data->dev == NULL) {
return;
}
dev_data->rx_enabled = false;
}
static int uart_mux_irq_tx_complete(struct device *dev)
{
ARG_UNUSED(dev);
return -ENOTSUP;
}
static int uart_mux_irq_rx_ready(struct device *dev)
{
struct uart_mux_dev_data *dev_data = DEV_DATA(dev);
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(struct device *dev)
{
ARG_UNUSED(dev);
}
static void uart_mux_irq_err_disable(struct device *dev)
{
ARG_UNUSED(dev);
}
static int uart_mux_irq_is_pending(struct device *dev)
{
struct uart_mux_dev_data *dev_data = DEV_DATA(dev);
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(struct device *dev)
{
ARG_UNUSED(dev);
return 1;
}
static void uart_mux_irq_callback_set(struct device *dev,
uart_irq_callback_user_data_t cb,
void *user_data)
{
struct uart_mux_dev_data *dev_data = DEV_DATA(dev);
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,
};
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;
}
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(struct device *uart, const u8_t *buf, size_t size)
{
struct uart_mux_dev_data *dev_data = DEV_DATA(uart);
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->config->name);
LOG_HEXDUMP_DBG(buf, size, log_strdup(tmp));
}
k_mutex_lock(&dev_data->real_uart->lock, K_FOREVER);
do {
uart_poll_out(dev_data->real_uart->uart, *buf++);
} while (--size);
k_mutex_unlock(&dev_data->real_uart->lock);
return 0;
}
int uart_mux_recv(struct device *mux, struct gsm_dlci *dlci, u8_t *data,
size_t len)
{
struct uart_mux_dev_data *dev_data = DEV_DATA(mux);
size_t wrote = 0;
LOG_DBG("%s: dlci %p data %p len %zd", mux->config->name, 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->config->name);
LOG_HEXDUMP_DBG(data, len, log_strdup(tmp));
}
wrote = ring_buf_put(dev_data->rx_ringbuf, data, len);
if (wrote < len) {
LOG_ERR("Ring buffer full, drop %d bytes", len - wrote);
}
dev_data->rx_ready = true;
if (dev_data->cb && dev_data->rx_enabled) {
k_work_submit(&dev_data->cb_work);
}
return wrote;
}
#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_AND_API_INIT(uart_mux_##x, \
CONFIG_UART_MUX_DEVICE_NAME "_" #x, \
&uart_mux_init, \
&uart_mux_dev_data_##x, \
&uart_mux_config_##x, \
POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
&uart_mux_driver_api);
UTIL_LISTIFY(CONFIG_UART_MUX_DEVICE_COUNT, DEFINE_UART_MUX_CFG_DATA, _)
UTIL_LISTIFY(CONFIG_UART_MUX_DEVICE_COUNT, DEFINE_UART_MUX_DEV_DATA, _)
UTIL_LISTIFY(CONFIG_UART_MUX_DEVICE_COUNT, DEFINE_UART_MUX_DEVICE, _)

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <device.h>
#include <drivers/console/uart_mux.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Send data to real UART (the data should be muxed already)
*
* @param uart Muxed uart
* @param buf Data to send
* @param size Data length
*
* @return 0 if data was sent, <0 if error
*/
int uart_mux_send(struct device *uart, const u8_t *buf, size_t size);
/**
* @brief Receive unmuxed data.
*
* @param mux UART mux device structure.
* @param uart Real UART device structure.
* @param dlci_address DLCI id for the virtual muxing channel
* @param timeout Amount of time to wait for the channel creation.
*
* @retval >=0 No errors, number of bytes received
* @retval <0 Error
*/
int uart_mux_recv(struct device *mux, struct gsm_dlci *dlci,
u8_t *data, size_t len);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,117 @@
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Public APIs for UART MUX drivers
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_UART_MUX_H_
#define ZEPHYR_INCLUDE_DRIVERS_UART_MUX_H_
/**
* @brief UART Mux Interface
* @defgroup uart_mux_interface UART Mux Interface
* @ingroup io_interfaces
* @{
*/
#include <device.h>
#include <drivers/uart.h>
#ifdef __cplusplus
extern "C" {
#endif
struct gsm_dlci;
/**
* @typedef uart_mux_attach_cb_t
*
* @brief Define the user callback function which is called when
* the UART mux is attached properly.
*
* @param mux UART mux device
* @param dlci_address DLCI id for the virtual muxing channel
* @param connected True if DLCI is connected, false otherwise.
* @param user_data Arbitrary user data.
*/
typedef void (*uart_mux_attach_cb_t)(struct device *mux, int dlci_address,
bool connected, void *user_data);
/** @brief UART mux driver API structure. */
__subsystem struct uart_mux_driver_api {
/**
* The uart_driver_api must be placed in first position in this
* struct so that we are compatible with uart API. Note that currently
* not all of the UART API functions are implemented.
*/
struct uart_driver_api uart_api;
/**
* Attach the mux to this UART. The API will call the callback after
* the DLCI is created or not.
*/
int (*attach)(struct device *mux, struct device *uart,
int dlci_address, uart_mux_attach_cb_t cb,
void *user_data);
};
/**
* @brief Attach physical/real UART to UART muxing device.
*
* @param mux UART mux device structure.
* @param uart Real UART device structure.
* @param dlci_address DLCI id for the virtual muxing channel
* @param cb Callback is called when the DLCI is ready and connected
* @param user_data Caller supplied optional data
*
* @retval 0 No errors, the attachment was successful
* @retval <0 Error
*/
static inline int uart_mux_attach(struct device *mux, struct device *uart,
int dlci_address, uart_mux_attach_cb_t cb,
void *user_data)
{
const struct uart_mux_driver_api *api =
(const struct uart_mux_driver_api *)mux->driver_api;
return api->attach(mux, uart, dlci_address, cb, user_data);
}
/**
* @brief Get UART related to a specific DLCI channel
*
* @param dlci_address DLCI address, value >0 and <63
*
* @return UART device if found, NULL otherwise
*/
__syscall struct device *uart_mux_find(int dlci_address);
/**
* @brief Allocate muxing UART device.
*
* @details This will return next available uart mux driver that will mux the
* data when read or written. This device corresponds to one DLCI channel.
* User must first call this to allocate the DLCI and then call the attach
* function to fully enable the muxing.
*
* @retval device New UART device that will automatically mux data sent to it.
* @retval NULL if error
*/
struct device *uart_mux_alloc(void);
#ifdef __cplusplus
}
#endif
#include <syscalls/uart_mux.h>
/**
* @}
*/
#endif /* ZEPHYR_INCLUDE_DRIVERS_UART_MUX_H_ */

View file

@ -165,6 +165,16 @@
__net_l2_data_end = .;
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
#if defined(CONFIG_UART_MUX)
SECTION_DATA_PROLOGUE(uart_mux,,SUBALIGN(4))
{
__uart_mux_start = .;
*(".uart_mux.*")
KEEP(*(SORT_BY_NAME(".uart_mux.*")))
__uart_mux_end = .;
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
#endif
#if defined(CONFIG_USB_DEVICE_STACK)
SECTION_DATA_PROLOGUE(usb_descriptor,,SUBALIGN(1))
{

View file

@ -895,6 +895,7 @@ class SizeCalculator:
"ccm_data",
"usb_descriptor",
"usb_data", "usb_bos_desc",
"uart_mux",
'log_backends_sections',
'log_dynamic_sections',
'log_const_sections',