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 */