From d56a05f7a7a8abe606aef1d196ff828170c5da67 Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Wed, 7 Aug 2019 08:02:00 -0700 Subject: [PATCH] drivers: modem: interface: introduce UART interface driver layer The UART-based modem interface layer implements the modem context interface for Zephyr's UART APIs. This driver closely resembles the existing modem receiver, but conforming to the modem interface agreements. Example modem driver setup code looks like this: /* create modem context object */ static struct modem_context mctx; /* create uart interface data object and buffers */ static struct modem_iface_uart_data iface_data; static u8_t iface_isr_buf[MDM_RECV_BUF_SIZE]; static u8_t iface_rb_buf[MDM_MAX_DATA_LENGTH]; iface_data.isr_buf = &iface_isr_buf[0]; iface_data.isr_buf_len = sizeof(iface_isr_buf); iface_data.rx_rb_buf = &iface_rb_buf[0]; iface_data.rx_rb_buf_len = sizeof(iface_rb_buf); ret = modem_iface_uart_init(&mctx.iface, &iface_data, UART_DEV_NAME); Signed-off-by: Michael Scott --- drivers/modem/CMakeLists.txt | 2 + drivers/modem/Kconfig | 11 +++ drivers/modem/modem_iface_uart.c | 152 +++++++++++++++++++++++++++++++ drivers/modem/modem_iface_uart.h | 54 +++++++++++ 4 files changed, 219 insertions(+) create mode 100644 drivers/modem/modem_iface_uart.c create mode 100644 drivers/modem/modem_iface_uart.h diff --git a/drivers/modem/CMakeLists.txt b/drivers/modem/CMakeLists.txt index e2246e0b422..1b587df5b08 100644 --- a/drivers/modem/CMakeLists.txt +++ b/drivers/modem/CMakeLists.txt @@ -8,6 +8,8 @@ zephyr_sources_ifdef(CONFIG_MODEM_CONTEXT modem_pin.c ) +zephyr_sources_ifdef(CONFIG_MODEM_IFACE_UART modem_iface_uart.c) + if(CONFIG_MODEM_UBLOX_SARA_R4) zephyr_library_include_directories(${ZEPHYR_BASE}/subsys/net/ip) zephyr_library_sources(ublox-sara-r4.c) diff --git a/drivers/modem/Kconfig b/drivers/modem/Kconfig index 3a553f8c9c2..05e0548ee47 100644 --- a/drivers/modem/Kconfig +++ b/drivers/modem/Kconfig @@ -64,6 +64,17 @@ config MODEM_CONTEXT_VERBOSE_DEBUG Enabling this setting will turn on VERY heavy debugging from the modem context helper. Do NOT leave on for production. +config MODEM_IFACE_UART + bool "UART-based modem interface" + depends on SERIAL_SUPPORT_INTERRUPT + select UART_INTERRUPT_DRIVEN + select RING_BUFFER + help + To configure this layer for use, create a modem_iface_uart_data + object and pass it's reference to modem_iface_uart_init() + along with the modem_iface reference from your modem_context object + and the UART device name. + endif # MODEM_CONTEXT config MODEM_SHELL diff --git a/drivers/modem/modem_iface_uart.c b/drivers/modem/modem_iface_uart.c new file mode 100644 index 00000000000..15d731f81b4 --- /dev/null +++ b/drivers/modem/modem_iface_uart.c @@ -0,0 +1,152 @@ +/** @file + * @brief interface for modem context + * + * UART-based modem interface implementation for modem context driver. + */ + +/* + * Copyright (c) 2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(modem_iface_uart, CONFIG_MODEM_LOG_LEVEL); + +#include +#include + +#include "modem_context.h" +#include "modem_iface_uart.h" + +/** + * @brief Drains UART. + * + * @note Discards remaining data. + * + * @param *iface: modem interface. + * + * @retval None. + */ +static void modem_iface_uart_flush(struct modem_iface *iface) +{ + u8_t c; + + while (uart_fifo_read(iface->dev, &c, 1) > 0) { + continue; + } +} + +/** + * @brief Modem interface interrupt handler. + * + * @note Fills interfaces ring buffer with received data. + * When ring buffer is full the data is discarded. + * + * @param *uart_dev: uart device. + * + * @retval None. + */ +static void modem_iface_uart_isr(struct device *uart_dev) +{ + struct modem_context *ctx; + struct modem_iface_uart_data *data; + int rx = 0, ret; + + /* lookup the modem context */ + ctx = modem_context_from_iface_dev(uart_dev); + if (!ctx || !ctx->iface.iface_data) { + return; + } + + data = (struct modem_iface_uart_data *)(ctx->iface.iface_data); + /* get all of the data off UART as fast as we can */ + while (uart_irq_update(ctx->iface.dev) && + uart_irq_rx_ready(ctx->iface.dev)) { + rx = uart_fifo_read(ctx->iface.dev, + data->isr_buf, data->isr_buf_len); + if (rx <= 0) { + continue; + } + + ret = ring_buf_put(&data->rx_rb, data->isr_buf, rx); + if (ret != rx) { + LOG_ERR("Rx buffer doesn't have enough space. " + "Bytes pending: %d, written: %d", + rx, ret); + modem_iface_uart_flush(&ctx->iface); + k_sem_give(&data->rx_sem); + break; + } + + k_sem_give(&data->rx_sem); + } +} + +static int modem_iface_uart_read(struct modem_iface *iface, + u8_t *buf, size_t size, size_t *bytes_read) +{ + struct modem_iface_uart_data *data; + + if (!iface || !iface->iface_data) { + return -EINVAL; + } + + if (size == 0) { + *bytes_read = 0; + return 0; + } + + data = (struct modem_iface_uart_data *)(iface->iface_data); + *bytes_read = ring_buf_get(&data->rx_rb, buf, size); + + return 0; +} + +static int modem_iface_uart_write(struct modem_iface *iface, + const u8_t *buf, size_t size) +{ + if (!iface || !iface->iface_data) { + return -EINVAL; + } + + if (size == 0) { + return 0; + } + + do { + uart_poll_out(iface->dev, *buf++); + } while (--size); + + return 0; +} + +int modem_iface_uart_init(struct modem_iface *iface, + struct modem_iface_uart_data *data, + const char *dev_name) +{ + if (!iface || !data) { + return -EINVAL; + } + + /* get UART device */ + iface->dev = device_get_binding(dev_name); + if (!iface->dev) { + return -ENODEV; + } + + iface->iface_data = data; + iface->read = modem_iface_uart_read; + iface->write = modem_iface_uart_write; + + ring_buf_init(&data->rx_rb, data->rx_rb_buf_len, data->rx_rb_buf); + k_sem_init(&data->rx_sem, 0, 1); + + uart_irq_rx_disable(iface->dev); + uart_irq_tx_disable(iface->dev); + modem_iface_uart_flush(iface); + uart_irq_callback_set(iface->dev, modem_iface_uart_isr); + uart_irq_rx_enable(iface->dev); + + return 0; +} diff --git a/drivers/modem/modem_iface_uart.h b/drivers/modem/modem_iface_uart.h new file mode 100644 index 00000000000..b74fe8beeef --- /dev/null +++ b/drivers/modem/modem_iface_uart.h @@ -0,0 +1,54 @@ +/** @file + * @brief Modem interface for UART header file. + * + * Modem interface UART handling for modem context driver. + */ + +/* + * Copyright (c) 2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_IFACE_UART_H_ +#define ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_IFACE_UART_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct modem_iface_uart_data { + /* ISR char buffer */ + char *isr_buf; + size_t isr_buf_len; + + /* ring buffer char buffer */ + char *rx_rb_buf; + size_t rx_rb_buf_len; + + /* ring buffer */ + struct ring_buf rx_rb; + + /* rx semaphore */ + struct k_sem rx_sem; +}; + +/** + * @brief Init modem interface for UART + * + * @param *iface: modem interface to initialize. + * @param *data: modem interface data to use + * + * @retval 0 if ok, < 0 if error. + */ +int modem_iface_uart_init(struct modem_iface *iface, + struct modem_iface_uart_data *data, + const char *dev_name); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_IFACE_UART_H_ */