/** @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) { uint8_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, uint8_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 uint8_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_dev(struct modem_iface *iface, const char *dev_name) { /* get UART device */ iface->dev = device_get_binding(dev_name); if (!iface->dev) { return -ENODEV; } 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; } int modem_iface_uart_init(struct modem_iface *iface, struct modem_iface_uart_data *data, const char *dev_name) { int ret; if (!iface || !data) { return -EINVAL; } 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); /* get UART device */ ret = modem_iface_uart_init_dev(iface, dev_name); if (ret < 0) { iface->iface_data = NULL; iface->read = NULL; iface->write = NULL; return ret; } return 0; }