git: 09ef538713e0 - main - uart: Add primitive noise filtering on RX

From: Justin Hibbits <jhibbits_at_FreeBSD.org>
Date: Wed, 27 Nov 2024 14:17:29 UTC
The branch main has been updated by jhibbits:

URL: https://cgit.FreeBSD.org/src/commit/?id=09ef538713e08a98584dbcf66ff80290cc1b41a2

commit 09ef538713e08a98584dbcf66ff80290cc1b41a2
Author:     Justin Hibbits <jhibbits@FreeBSD.org>
AuthorDate: 2024-11-20 17:08:26 +0000
Commit:     Justin Hibbits <jhibbits@FreeBSD.org>
CommitDate: 2024-11-27 14:17:09 +0000

    uart: Add primitive noise filtering on RX
    
    A long cable attached to the UART can act as an antenna if disconnected
    from the other end.  This can cause noise on the receive side, possibly
    as reflections from the transmit side, leading to an interrupt storm.
    Filter this by adding a threshold of received characters without TX
    ready, above which characters are dropped.  This is disabled by default,
    but has been tested with a threshold of 1000+.  A high threshold is
    recommended to avoid dropping characters during, for instance, a large
    copy/paste from the other end.
    
    Sponsored by:   Juniper Networks, Inc.
---
 sys/dev/uart/uart_dev_ns8250.c | 17 +++++++++++++++++
 sys/dev/uart/uart_dev_ns8250.h |  1 +
 2 files changed, 18 insertions(+)

diff --git a/sys/dev/uart/uart_dev_ns8250.c b/sys/dev/uart/uart_dev_ns8250.c
index 3da2e8b8c758..1df39b499539 100644
--- a/sys/dev/uart/uart_dev_ns8250.c
+++ b/sys/dev/uart/uart_dev_ns8250.c
@@ -77,6 +77,11 @@ static int broken_txfifo = 0;
 SYSCTL_INT(_hw, OID_AUTO, broken_txfifo, CTLFLAG_RWTUN,
 	&broken_txfifo, 0, "UART FIFO has QEMU emulation bug");
 
+static int uart_noise_threshold = 0;
+SYSCTL_INT(_hw, OID_AUTO, uart_noise_threshold, CTLFLAG_RWTUN,
+	&uart_noise_threshold, 0,
+	"Number of UART RX interrupts where TX is not ready, before data is discarded");
+
 /*
  * To use early printf on x86, add the following to your kernel config:
  *
@@ -1012,6 +1017,7 @@ int
 ns8250_bus_receive(struct uart_softc *sc)
 {
 	struct uart_bas *bas;
+	struct ns8250_softc *ns8250 = (struct ns8250_softc *)sc;
 	int xc;
 	uint8_t lsr;
 
@@ -1023,6 +1029,17 @@ ns8250_bus_receive(struct uart_softc *sc)
 			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
 			break;
 		}
+		/* Filter out possible noise on the line.
+		 * Expect that the device should be able to transmit as well as
+		 * receive, so if we receive too many characters before transmit
+		 * is ready, it's probably noise.
+		 */
+		if ((lsr & (LSR_TXRDY | LSR_TEMT)) == 0 &&
+		    uart_noise_threshold > 0) {
+			if (++ns8250->noise_count >= uart_noise_threshold)
+				break;
+		} else
+			ns8250->noise_count = 0;
 		xc = uart_getreg(bas, REG_DATA);
 		if (lsr & LSR_FE)
 			xc |= UART_STAT_FRAMERR;
diff --git a/sys/dev/uart/uart_dev_ns8250.h b/sys/dev/uart/uart_dev_ns8250.h
index 324ff72f6e5d..e8a17e96c268 100644
--- a/sys/dev/uart/uart_dev_ns8250.h
+++ b/sys/dev/uart/uart_dev_ns8250.h
@@ -41,6 +41,7 @@ struct ns8250_softc {
 	uint8_t		ier_mask;
 	uint8_t		ier_rxbits;
 	uint8_t		busy_detect;
+	int		noise_count;
 };
 
 extern struct uart_ops uart_ns8250_ops;