diff --git a/Documentation/NuttxPortingGuide.html b/Documentation/NuttxPortingGuide.html
index 7aebea6768b..f685d92abcf 100644
--- a/Documentation/NuttxPortingGuide.html
+++ b/Documentation/NuttxPortingGuide.html
@@ -4443,7 +4443,7 @@ void board_led_off(int led);
void rxint(FAR struct uart_dev_s *dev, bool enable);
bool rxavailable(FAR struct uart_dev_s *dev);
#ifdef CONFIG_SERIAL_IFLOWCONTROL
- bool rxflowcontrol(FAR struct uart_dev_s *dev);
+ bool rxflowcontrol(FAR struct uart_dev_s *dev, unsigned int nbuffered, bool upper);
#endif
void send(FAR struct uart_dev_s *dev, int ch);
void txint(FAR struct uart_dev_s *dev, bool enable);
diff --git a/arch/arm/src/stm32/stm32_serial.c b/arch/arm/src/stm32/stm32_serial.c
index 8a7ed096436..698568bd711 100644
--- a/arch/arm/src/stm32/stm32_serial.c
+++ b/arch/arm/src/stm32/stm32_serial.c
@@ -327,7 +327,8 @@ static void up_rxint(struct uart_dev_s *dev, bool enable);
static bool up_rxavailable(struct uart_dev_s *dev);
#endif
#ifdef CONFIG_SERIAL_IFLOWCONTROL
-static bool up_rxflowcontrol(struct uart_dev_s *dev);
+static bool up_rxflowcontrol(struct uart_dev_s *dev, unsigned int nbuffered,
+ bool upper);
#endif
static void up_send(struct uart_dev_s *dev, int ch);
static void up_txint(struct uart_dev_s *dev, bool enable);
@@ -2134,13 +2135,16 @@ static bool up_rxavailable(struct uart_dev_s *dev)
* Name: up_rxflowcontrol
*
* Description:
- * Called when Rx buffer is full. Return true if the Rx interrupt was
- * disabled.
+ * Called when Rx buffer is full (or exceeds configured watermark levels
+ * if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is defined).
+ * Return true if UART activated RX flow control to block more incoming
+ * data
*
****************************************************************************/
#ifdef CONFIG_SERIAL_IFLOWCONTROL
-static bool up_rxflowcontrol(struct uart_dev_s *dev)
+static bool up_rxflowcontrol(struct uart_dev_s *dev,
+ unsigned int nbuffered, bool upper)
{
struct up_dev_s *priv = (struct up_dev_s*)dev->priv;
uint16_t ie;
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 80956c7988b..e3c7856b5fd 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -523,6 +523,48 @@ config SERIAL_NPOLLWAITERS
Maximum number of threads than can be waiting for POLL events.
Default: 2
+config SERIAL_IFLOWCONTROL
+ bool
+ default n
+
+config SERIAL_OFLOWCONTROL
+ bool
+ default n
+
+config SERIAL_IFLOWCONTROL_WATERMARKS
+ bool "RX flow control watermarks"
+ default n
+ depends on SERIAL_IFLOWCONTROL
+ ---help---
+ Call the "lower half" rxflowcontrol method whenever the number of
+ characters in the serial RX buffer falls above an upper water mark
+ level or below a lower watermark level. The default behavior is to
+ call the rxflowcontrol method only when the RX buffer is full.
+
+if SERIAL_IFLOWCONTROL_WATERMARKS
+
+config SERIAL_IFLOWCONTROL_LOWER_WATERMARK
+ int "RX lower Watermark (percent)"
+ default 10
+ range 0 100
+ ---help---
+ Call the rxflowcontrol method then there are this amount (or or less)
+ data buffered in the serial drivers RX buffer. This is expressed
+ as a percentage of the total size of the RX buffer which may vary
+ from instance-to-instance.
+
+config SERIAL_IFLOWCONTROL_UPPER_WATERMARK
+ int "RX upper Watermark (percent)"
+ default 90
+ range 0 100
+ ---help---
+ Call the rxflowcontrol method then there are this amount (or more)
+ data buffered in the serial drivers RX buffer. This is expressed
+ as a percentage of the total size of the RX buffer which may vary
+ from instance-to-instance.
+
+endif # SERIAL_IFLOWCONTROL_WATERMARKS
+
config SERIAL_TIOCSERGSTRUCT
bool "Support TIOCSERGSTRUCT"
default n
@@ -1843,11 +1885,3 @@ config SCI1_2STOP
1=Two stop bits
endmenu # SCI1 Configuration
-
-config SERIAL_IFLOWCONTROL
- bool
- default n
-
-config SERIAL_OFLOWCONTROL
- bool
- default n
diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c
index 4d40527feb4..9b9b82669e6 100644
--- a/drivers/serial/serial.c
+++ b/drivers/serial/serial.c
@@ -522,6 +522,10 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen
{
FAR struct inode *inode = filep->f_inode;
FAR uart_dev_t *dev = inode->i_private;
+#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS
+ unsigned int nbuffered;
+ unsigned int watermark;
+#endif
irqstate_t flags;
ssize_t recvd = 0;
int16_t tail;
@@ -565,7 +569,7 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen
#endif
/* Check if there is more data to return in the circular buffer.
- * NOTE: Rx interrupt handling logic may aynchronously increment
+ * NOTE: Rx interrupt handling logic may asynchronously increment
* the head index but must not modify the tail index. The tail
* index is only modified in this function. Therefore, no
* special handshaking is required here.
@@ -774,6 +778,30 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen
}
#ifdef CONFIG_SERIAL_IFLOWCONTROL
+#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS
+ /* How many bytes are now buffered */
+
+ if (buf->head >= buf->tail)
+ {
+ nbuffered = buf->head - buf->tail;
+ }
+ else
+ {
+ nbuffered = buf->size - buf->tail + buf->head;
+ }
+
+ /* Is the level now below the watermark level that we need to report? */
+
+ watermark = (CONFIG_SERIAL_IFLOWCONTROL_LOWER_WATERMARK * buf->size) / 100
+ if (nbuffered <= watermark)
+ {
+ /* Let the lower level driver know that the watermark level has been
+ * crossed.
+ */
+
+ (void)uart_rxflowcontrol(dev, nubuffered, false))
+ }
+#else
if (dev->recv.head == dev->recv.tail)
{
/* We might leave Rx interrupt disabled if full recv buffer was read
@@ -782,6 +810,7 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen
uart_enablerxint(dev);
}
+#endif
#endif
uart_givesem(&dev->recv.sem);
diff --git a/drivers/serial/serialirq.c b/drivers/serial/serialirq.c
index 1c76dfe5ecb..203677354d2 100644
--- a/drivers/serial/serialirq.c
+++ b/drivers/serial/serialirq.c
@@ -138,6 +138,9 @@ void uart_xmitchars(FAR uart_dev_t *dev)
void uart_recvchars(FAR uart_dev_t *dev)
{
+#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS
+ unsigned int watermark;
+#endif
unsigned int status;
int nexthead = dev->recv.head + 1;
uint16_t nbytes = 0;
@@ -147,6 +150,12 @@ void uart_recvchars(FAR uart_dev_t *dev)
nexthead = 0;
}
+#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS
+ /* Pre-calcuate the watermark level that we will need to test against. */
+
+ watermark = (CONFIG_SERIAL_IFLOWCONTROL_UPPER_WATERMARK * buf->size) / 100
+#endif
+
/* Loop putting characters into the receive buffer until there are no further
* characters to available.
*/
@@ -157,19 +166,50 @@ void uart_recvchars(FAR uart_dev_t *dev)
char ch;
#ifdef CONFIG_SERIAL_IFLOWCONTROL
- /* Check if RX buffer is full and allow serial low-level driver to pause
- * processing. This allows proper utilization of hardware flow control.
- */
+ unsigned int nbuffered;
- if (is_full)
+ /* How many bytes are buffered */
+
+ if (buf->head >= buf->tail)
{
- if (uart_rxflowcontrol(dev))
+ nbuffered = buf->head - buf->tail;
+ }
+ else
+ {
+ nbuffered = buf->size - buf->tail + buf->head;
+ }
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS
+ /* Is the level now above the watermark level that we need to report? */
+
+ if (nbuffered >= watermark)
+ {
+ /* Let the lower level driver know that the watermark level has been
+ * crossed.
+ */
+
+ if (uart_rxflowcontrol(dev, nubuffered, true))
{
/* Low-level driver activated RX flow control, exit loop now. */
break;
}
}
+#else
+ /* Check if RX buffer is full and allow serial low-level driver to pause
+ * processing. This allows proper utilization of hardware flow control.
+ */
+
+ if (is_full)
+ {
+ if (uart_rxflowcontrol(dev, nbuffered, true))
+ {
+ /* Low-level driver activated RX flow control, exit loop now. */
+
+ break;
+ }
+ }
+#endif
#endif
ch = uart_receive(dev, &status);
diff --git a/include/nuttx/serial/serial.h b/include/nuttx/serial/serial.h
index 2a32e416ba9..34eb1cc6d7e 100644
--- a/include/nuttx/serial/serial.h
+++ b/include/nuttx/serial/serial.h
@@ -79,8 +79,8 @@
#define uart_receive(dev,s) dev->ops->receive(dev,s)
#ifdef CONFIG_SERIAL_IFLOWCONTROL
-#define uart_rxflowcontrol(dev) \
- (dev->ops->rxflowcontrol && dev->ops->rxflowcontrol(dev))
+#define uart_rxflowcontrol(dev,n,u) \
+ (dev->ops->rxflowcontrol && dev->ops->rxflowcontrol(dev,n,u))
#endif
/************************************************************************************
@@ -165,9 +165,12 @@ struct uart_ops_s
CODE bool (*rxavailable)(FAR struct uart_dev_s *dev);
#ifdef CONFIG_SERIAL_IFLOWCONTROL
- /* Return true if UART activated RX flow control to block more incoming data. */
+ /* Return true if UART activated RX flow control to block more incoming
+ * data.
+ */
- CODE bool (*rxflowcontrol)(FAR struct uart_dev_s *dev);
+ CODE bool (*rxflowcontrol)(FAR struct uart_dev_s *dev,
+ unsigned int nbuffered, bool upper);
#endif
/* This method will send one byte on the UART */