d56a05f7a7
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 <mike@foundries.io>
153 lines
3.1 KiB
C
153 lines
3.1 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)
|
|
{
|
|
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;
|
|
}
|