171 lines
3.4 KiB
C
171 lines
3.4 KiB
C
/** @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 <logging/log.h>
|
|
LOG_MODULE_REGISTER(modem_iface_uart, CONFIG_MODEM_LOG_LEVEL);
|
|
|
|
#include <kernel.h>
|
|
#include <drivers/uart.h>
|
|
|
|
#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;
|
|
}
|