drivers: net: ppp: async uart support

Implemented an option to use asyncronous uart api
(CONFIG_NET_PPP_ASYNC_UART).

Signed-off-by: Jani Hirsimäki <jani.hirsimaki@nordicsemi.no>
This commit is contained in:
Jani Hirsimäki 2022-01-07 15:14:30 +02:00 committed by Maureen Helm
commit b204c26e94
2 changed files with 212 additions and 5 deletions

View file

@ -18,6 +18,11 @@ menuconfig NET_PPP
if NET_PPP
config NET_PPP_ASYNC_UART
bool "Asynchronous UART API is used"
depends on UART_ASYNC_API
depends on !GSM_MUX
config NET_PPP_UART_NAME
string "UART device name the PPP is connected to"
depends on !MODEM_GSM_PPP
@ -89,6 +94,32 @@ config PPP_NET_IF_NO_AUTO_START
This option allows user to disable autostarting of the PPP interface
immediately after initialization.
if NET_PPP_ASYNC_UART
config NET_PPP_ASYNC_UART_TX_BUF_LEN
int "Buffer length from where the write to async UART is done"
default 2048
help
This options sets the size of the UART TX buffer where data
is being written from to UART.
config NET_PPP_ASYNC_UART_RX_RECOVERY_TIMEOUT
int "UART RX recovery timeout in milliseconds"
default 200
help
The time that UART RX is in disabled state in case
when we cannot receive more data from UART.
config NET_PPP_ASYNC_UART_RX_ENABLE_TIMEOUT
int "A timeout for uart_rx_enable()"
default 100
config NET_PPP_ASYNC_UART_TX_TIMEOUT
int "A timeout for uart_tx()"
default 100
endif # NET_PPP_ASYNC_UART
module = NET_PPP
module-dep = LOG
module-str = Log level for ppp driver

View file

@ -37,6 +37,7 @@ LOG_MODULE_REGISTER(net_ppp, LOG_LEVEL);
#include "../../subsys/net/ip/net_private.h"
#define UART_BUF_LEN CONFIG_NET_PPP_UART_BUF_LEN
#define UART_TX_BUF_LEN CONFIG_NET_PPP_ASYNC_UART_TX_BUF_LEN
enum ppp_driver_state {
STATE_HDLC_FRAME_START,
@ -61,9 +62,17 @@ struct ppp_driver_context {
/* ppp data is read into this buf */
uint8_t buf[UART_BUF_LEN];
#if defined(CONFIG_NET_PPP_ASYNC_UART)
/* with async we use 2 rx buffers */
uint8_t buf2[UART_BUF_LEN];
struct k_work_delayable uart_recovery_work;
/* ppp buf use when sending data */
uint8_t send_buf[UART_TX_BUF_LEN];
#else
/* ppp buf use when sending data */
uint8_t send_buf[UART_BUF_LEN];
#endif
uint8_t mac_addr[6];
struct net_linkaddr ll_addr;
@ -95,6 +104,155 @@ struct ppp_driver_context {
static struct ppp_driver_context ppp_driver_context_data;
#if defined(CONFIG_NET_PPP_ASYNC_UART)
static bool rx_retry_pending;
static bool uart_recovery_pending;
static uint8_t *next_buf;
static K_SEM_DEFINE(uarte_tx_finished, 0, 1);
static void uart_callback(const struct device *dev,
struct uart_event *evt,
void *user_data)
{
struct ppp_driver_context *context = user_data;
uint8_t *p;
int err, ret, len, space_left;
switch (evt->type) {
case UART_TX_DONE:
LOG_DBG("UART_TX_DONE: sent %d bytes", evt->data.tx.len);
k_sem_give(&uarte_tx_finished);
break;
case UART_TX_ABORTED:
LOG_DBG("Tx aborted");
k_sem_give(&uarte_tx_finished);
break;
case UART_RX_RDY:
len = evt->data.rx.len;
p = evt->data.rx.buf + evt->data.rx.offset;
LOG_DBG("Received data %d bytes", len);
ret = ring_buf_put(&context->rx_ringbuf, p, len);
if (ret < evt->data.rx.len) {
LOG_WRN("Rx buffer doesn't have enough space. "
"Bytes pending: %d, written only: %d. "
"Disabling RX for now.",
evt->data.rx.len, ret);
/* No possibility to set flow ctrl ON towards PC,
* thus workrounding this lack in async API by turning
* rx off for now and re-enabling that later.
*/
if (!rx_retry_pending) {
uart_rx_disable(dev);
rx_retry_pending = true;
}
}
space_left = ring_buf_space_get(&context->rx_ringbuf);
if (!rx_retry_pending && space_left < (sizeof(context->rx_buf) / 8)) {
/* Not much room left in buffer after a write to ring buffer.
* We submit a work, but enable flow ctrl also
* in this case to avoid packet losses.
*/
uart_rx_disable(dev);
rx_retry_pending = true;
LOG_WRN("%d written to RX buf, but after that only %d space left. "
"Disabling RX for now.",
ret, space_left);
}
k_work_submit_to_queue(&context->cb_workq, &context->cb_work);
break;
case UART_RX_BUF_REQUEST:
{
LOG_DBG("UART_RX_BUF_REQUEST: buf %p", next_buf);
if (next_buf) {
err = uart_rx_buf_rsp(dev, next_buf, sizeof(context->buf));
if (err) {
LOG_ERR("uart_rx_buf_rsp() err: %d", err);
}
}
break;
}
case UART_RX_BUF_RELEASED:
next_buf = evt->data.rx_buf.buf;
LOG_DBG("UART_RX_BUF_RELEASED: buf %p", next_buf);
break;
case UART_RX_DISABLED:
LOG_DBG("UART_RX_DISABLED - re-enabling in a while");
if (rx_retry_pending && !uart_recovery_pending) {
k_work_schedule(&context->uart_recovery_work,
K_MSEC(CONFIG_NET_PPP_ASYNC_UART_RX_RECOVERY_TIMEOUT));
rx_retry_pending = false;
uart_recovery_pending = true;
}
break;
case UART_RX_STOPPED:
LOG_DBG("UART_RX_STOPPED: stop reason %d", evt->data.rx_stop.reason);
if (evt->data.rx_stop.reason != 0) {
rx_retry_pending = true;
}
break;
}
}
static int ppp_async_uart_rx_enable(struct ppp_driver_context *context)
{
int err;
next_buf = context->buf2;
err = uart_callback_set(context->dev, uart_callback, (void *)context);
if (err) {
LOG_ERR("Failed to set uart callback, err %d", err);
}
err = uart_rx_enable(context->dev, context->buf, sizeof(context->buf),
CONFIG_NET_PPP_ASYNC_UART_RX_ENABLE_TIMEOUT);
if (err) {
LOG_ERR("uart_rx_enable() failed, err %d", err);
} else {
LOG_DBG("RX enabled");
}
rx_retry_pending = false;
return err;
}
static void uart_recovery(struct k_work *work)
{
struct ppp_driver_context *ppp =
CONTAINER_OF(work, struct ppp_driver_context, uart_recovery_work);
int ret;
ret = ring_buf_space_get(&ppp->rx_ringbuf);
if (ret >= (sizeof(ppp->rx_buf) / 2)) {
ret = ppp_async_uart_rx_enable(ppp);
if (ret) {
LOG_ERR("ppp_async_uart_rx_enable() failed, err %d", ret);
} else {
LOG_WRN("UART RX recovered");
}
uart_recovery_pending = false;
} else {
LOG_ERR("Rx buffer still doesn't have enough room %d to be re-enabled", ret);
k_work_schedule(&ppp->uart_recovery_work,
K_MSEC(CONFIG_NET_PPP_ASYNC_UART_RX_RECOVERY_TIMEOUT));
}
}
#endif
static int ppp_save_byte(struct ppp_driver_context *ppp, uint8_t byte)
{
int ret;
@ -206,6 +364,18 @@ static int ppp_send_flush(struct ppp_driver_context *ppp, int off)
*/
if (IS_ENABLED(CONFIG_GSM_MUX)) {
(void)uart_fifo_fill(ppp->dev, buf, off);
} else if (IS_ENABLED(CONFIG_NET_PPP_ASYNC_UART)) {
#if defined(CONFIG_NET_PPP_ASYNC_UART)
int ret;
k_sem_take(&uarte_tx_finished, K_FOREVER);
ret = uart_tx(ppp->dev, buf, off, CONFIG_NET_PPP_ASYNC_UART_TX_TIMEOUT);
if (ret) {
LOG_ERR("uart_tx() failed, err %d", ret);
k_sem_give(&uarte_tx_finished);
}
#endif
} else {
while (off--) {
uart_poll_out(ppp->dev, *buf++);
@ -350,7 +520,7 @@ static int ppp_input_byte(struct ppp_driver_context *ppp, uint8_t byte)
break;
default:
LOG_DBG("[%p] Invalid state %d", ppp, ppp->state);
LOG_ERR("[%p] Invalid state %d", ppp, ppp->state);
break;
}
@ -720,8 +890,10 @@ static int ppp_driver_init(const struct device *dev)
K_KERNEL_STACK_SIZEOF(ppp_workq),
K_PRIO_COOP(PPP_WORKQ_PRIORITY), NULL);
k_thread_name_set(&ppp->cb_workq.thread, "ppp_workq");
#if defined(CONFIG_NET_PPP_ASYNC_UART)
k_work_init_delayable(&ppp->uart_recovery_work, uart_recovery);
#endif
#endif
ppp->pkt = NULL;
ppp_change_state(ppp, STATE_HDLC_FRAME_START);
#if defined(CONFIG_PPP_CLIENT_CLIENTSERVER)
@ -800,7 +972,7 @@ static struct net_stats_ppp *ppp_get_stats(const struct device *dev)
}
#endif
#if !defined(CONFIG_NET_TEST)
#if !defined(CONFIG_NET_TEST) && !defined(CONFIG_NET_PPP_ASYNC_UART)
static void ppp_uart_flush(const struct device *dev)
{
uint8_t c;
@ -833,7 +1005,7 @@ static void ppp_uart_isr(const struct device *uart, void *user_data)
k_work_submit_to_queue(&context->cb_workq, &context->cb_work);
}
}
#endif /* !CONFIG_NET_TEST */
#endif /* !CONFIG_NET_TEST && !CONFIG_NET_PPP_ASYNC_UART */
static int ppp_start(const struct device *dev)
{
@ -880,13 +1052,17 @@ static int ppp_start(const struct device *dev)
LOG_ERR("Cannot find dev %s", dev_name);
return -ENODEV;
}
#if defined(CONFIG_NET_PPP_ASYNC_UART)
k_sem_give(&uarte_tx_finished);
ppp_async_uart_rx_enable(context);
#else
uart_irq_rx_disable(context->dev);
uart_irq_tx_disable(context->dev);
ppp_uart_flush(context->dev);
uart_irq_callback_user_data_set(context->dev, ppp_uart_isr,
context);
uart_irq_rx_enable(context->dev);
#endif
}
#endif /* !CONFIG_NET_TEST */