Skip to content

Commit

Permalink
[sg noup] net: ppp: simple flow control for interrupt based UART
Browse files Browse the repository at this point in the history
This is required for the lb_radio_gateway, so that we can create
back-pressure towards Linux if we get too much data.

Issue: SG-21138

(cherry picked from commit 040ad4d8c03b82d0a0f649a2a6b25f644726519b)
  • Loading branch information
andreasmueller authored and rettichschnidi committed May 8, 2024
1 parent c052fb7 commit c23af30
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 0 deletions.
7 changes: 7 additions & 0 deletions drivers/net/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ config NET_PPP_RINGBUF_SIZE
PPP ring buffer size when passing data from RX ISR to worker
thread that will pass the data to IP stack.

config NET_PPP_RINGBUF_MIN_SPACE
int "Minimum space in RX ring buffer before enabling flow-control"
default 50
help
Minimum number of free bytes in RX ring buffer. If remaning space reaches this limit, UART
flow-control is enabled.

config NET_PPP_RX_STACK_SIZE
int "Size of the stack allocated for receiving data from modem"
default 768
Expand Down
40 changes: 40 additions & 0 deletions drivers/net/ppp.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ LOG_MODULE_REGISTER(net_ppp, LOG_LEVEL);
#include <zephyr/net/net_core.h>
#include <zephyr/sys/ring_buffer.h>
#include <zephyr/sys/crc.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/console/uart_mux.h>
#include <zephyr/random/random.h>
Expand All @@ -50,6 +51,8 @@ BUILD_ASSERT(UART_YIELD_INTERVAL_BYTES * 8 * 1000 /
"current UART k_yield() interval & speed leads to blocking time > 5 ms");
#endif

#define RX_RINGBUF_MIN_SPACE CONFIG_NET_PPP_RINGBUF_MIN_SPACE

enum ppp_driver_state {
STATE_HDLC_FRAME_START,
STATE_HDLC_FRAME_ADDRESS,
Expand All @@ -59,6 +62,33 @@ enum ppp_driver_state {
#define PPP_WORKQ_PRIORITY CONFIG_NET_PPP_RX_PRIORITY
#define PPP_WORKQ_STACK_SIZE CONFIG_NET_PPP_RX_STACK_SIZE

#define UART_RTS_NODE DT_ALIAS(ppp_uart_rts)

/*
* Note regarding synchronization: this variable is written both from a thread and an interrupt,
* however since the interrupt only writes the variable when the ring buffer is (mostly) full and
* the thread only when the ring buffer is empty, there is no issue in reality (there might be an
* issue in case of the dongle, which uses USB CDC ACM instead of UART though).
*/
static bool uart_ready_for_data = true;

static void flow_control_set_rts(const struct device *uart, const bool ready_for_data) {
if (uart_ready_for_data == ready_for_data) {
return;
}
uart_ready_for_data = ready_for_data;

#ifdef CONFIG_UART_LINE_CTRL
uart_line_ctrl_set(uart, UART_LINE_CTRL_RTS, ready_for_data ? 1 : 0);
#else /* CONFIG_UART_LINE_CTRL */
if (ready_for_data) {
uart_irq_rx_enable(uart);
} else {
uart_irq_rx_disable(uart);
}
#endif /* CONFIG_UART_LINE_CTRL */
}

K_KERNEL_STACK_DEFINE(ppp_workq, PPP_WORKQ_STACK_SIZE);

struct ppp_driver_context {
Expand Down Expand Up @@ -888,6 +918,11 @@ static int ppp_consume_ringbuf(struct ppp_driver_context *ppp)
LOG_DBG("Cannot flush ring buffer (%d)", ret);
}

if (ring_buf_is_empty(&ppp->rx_ringbuf)) {
LOG_DBG("ringbuf empty, enabling UART");
flow_control_set_rts(ppp->dev, true);
}

return -EAGAIN;
}

Expand Down Expand Up @@ -1029,6 +1064,11 @@ static void ppp_uart_isr(const struct device *uart, void *user_data)
break;
}

if (ring_buf_space_get(&context->rx_ringbuf) < RX_RINGBUF_MIN_SPACE) {
LOG_DBG("ringbuf almost full, disabling UART");
flow_control_set_rts(uart, false);
}

k_work_submit_to_queue(&context->cb_workq, &context->cb_work);
}
}
Expand Down

0 comments on commit c23af30

Please sign in to comment.