svn commit: r197721 - head/sys/dev/uart

Marcel Moolenaar marcel at FreeBSD.org
Fri Oct 2 22:30:45 UTC 2009


Author: marcel
Date: Fri Oct  2 22:30:44 2009
New Revision: 197721
URL: http://svn.freebsd.org/changeset/base/197721

Log:
  Fix RTS/CTS flow control, broken by the TTY overhaul. The new TTY
  interface is fairly simple WRT dealing with flow control, but
  needed 2 new RX buffer functions with "get-char-from-buf" separated
  from "advance-buf-pointer" so that the pointer could be advanced
  only when ttydisc_rint() succeeded.
  
  MFC after:	1 week

Modified:
  head/sys/dev/uart/uart_bus.h
  head/sys/dev/uart/uart_core.c
  head/sys/dev/uart/uart_tty.c

Modified: head/sys/dev/uart/uart_bus.h
==============================================================================
--- head/sys/dev/uart/uart_bus.h	Fri Oct  2 21:31:15 2009	(r197720)
+++ head/sys/dev/uart/uart_bus.h	Fri Oct  2 22:30:44 2009	(r197721)
@@ -96,6 +96,7 @@ struct uart_softc {
 	int		sc_opened:1;	/* This UART is open for business. */
 	int		sc_polled:1;	/* This UART has no interrupts. */
 	int		sc_txbusy:1;	/* This UART is transmitting. */
+	int		sc_isquelch:1;	/* This UART has input squelched. */
 
 	struct uart_devinfo *sc_sysdev;	/* System device (or NULL). */
 
@@ -141,6 +142,8 @@ int uart_bus_ipend(device_t dev);
 int uart_bus_probe(device_t dev, int regshft, int rclk, int rid, int chan);
 int uart_bus_sysdev(device_t dev);
 
+void uart_sched_softih(struct uart_softc *, uint32_t);
+
 int uart_tty_attach(struct uart_softc *);
 int uart_tty_detach(struct uart_softc *);
 void uart_tty_intr(void *arg);
@@ -175,6 +178,28 @@ uart_rx_get(struct uart_softc *sc)
 }
 
 static __inline int
+uart_rx_next(struct uart_softc *sc)
+{
+	int ptr;
+
+	ptr = sc->sc_rxget;
+	if (ptr == sc->sc_rxput)
+		return (-1);
+	ptr += 1;
+	sc->sc_rxget = (ptr < sc->sc_rxbufsz) ? ptr : 0;
+	return (0);
+}
+
+static __inline int
+uart_rx_peek(struct uart_softc *sc)
+{
+	int ptr;
+
+	ptr = sc->sc_rxget;
+	return ((ptr == sc->sc_rxput) ? -1 : sc->sc_rxbuf[ptr]);
+}
+
+static __inline int
 uart_rx_put(struct uart_softc *sc, int xc)
 {
 	int ptr;

Modified: head/sys/dev/uart/uart_core.c
==============================================================================
--- head/sys/dev/uart/uart_core.c	Fri Oct  2 21:31:15 2009	(r197720)
+++ head/sys/dev/uart/uart_core.c	Fri Oct  2 22:30:44 2009	(r197721)
@@ -91,7 +91,7 @@ uart_getrange(struct uart_class *uc)
  * Schedule a soft interrupt. We do this on the 0 to !0 transition
  * of the TTY pending interrupt status.
  */
-static void
+void
 uart_sched_softih(struct uart_softc *sc, uint32_t ipend)
 {
 	uint32_t new, old;

Modified: head/sys/dev/uart/uart_tty.c
==============================================================================
--- head/sys/dev/uart/uart_tty.c	Fri Oct  2 21:31:15 2009	(r197720)
+++ head/sys/dev/uart/uart_tty.c	Fri Oct  2 22:30:44 2009	(r197721)
@@ -166,27 +166,6 @@ uart_tty_outwakeup(struct tty *tp)
 	if (sc == NULL || sc->sc_leaving)
 		return;
 
-	/*
-	 * Handle input flow control. Note that if we have hardware support,
-	 * we don't do anything here. We continue to receive until our buffer
-	 * is full. At that time we cannot empty the UART itself and it will
-	 * de-assert RTS for us. In that situation we're completely stuffed.
-	 * Without hardware support, we need to toggle RTS ourselves.
-	 */
-	if ((tp->t_termios.c_cflag & CRTS_IFLOW) && !sc->sc_hwiflow) {
-#if 0
-		/*if ((tp->t_state & TS_TBLOCK) &&
-		    (sc->sc_hwsig & SER_RTS))
-			UART_SETSIG(sc, SER_DRTS);
-		else */ if (/*!(tp->t_state & TS_TBLOCK) &&*/
-		    !(sc->sc_hwsig & SER_RTS))
-			UART_SETSIG(sc, SER_DRTS|SER_RTS);
-#endif
-		/* XXX: we should use inwakeup to implement this! */
-		if (!(sc->sc_hwsig & SER_RTS))
-			UART_SETSIG(sc, SER_DRTS|SER_RTS);
-	}
-
 	if (sc->sc_txbusy)
 		return;
 
@@ -195,6 +174,23 @@ uart_tty_outwakeup(struct tty *tp)
 		UART_TRANSMIT(sc);
 }
 
+static void
+uart_tty_inwakeup(struct tty *tp)
+{
+	struct uart_softc *sc;
+
+	sc = tty_softc(tp);
+	if (sc == NULL || sc->sc_leaving)
+		return;
+
+	if (sc->sc_isquelch) {
+		if ((tp->t_termios.c_cflag & CRTS_IFLOW) && !sc->sc_hwiflow)
+			UART_SETSIG(sc, SER_DRTS|SER_RTS);
+		sc->sc_isquelch = 0;
+		uart_sched_softih(sc, SER_INT_RXREADY);
+	}
+}
+
 static int
 uart_tty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
 {
@@ -252,9 +248,9 @@ uart_tty_param(struct tty *tp, struct te
 	UART_SETSIG(sc, SER_DDTR | SER_DTR);
 	/* Set input flow control state. */
 	if (!sc->sc_hwiflow) {
-		/* if ((t->c_cflag & CRTS_IFLOW) && (tp->t_state & TS_TBLOCK))
+		if ((t->c_cflag & CRTS_IFLOW) && sc->sc_isquelch)
 			UART_SETSIG(sc, SER_DRTS);
-		else */
+		else
 			UART_SETSIG(sc, SER_DRTS | SER_RTS);
 	} else
 		UART_IOCTL(sc, UART_IOCTL_IFLOW, (t->c_cflag & CRTS_IFLOW));
@@ -294,8 +290,8 @@ uart_tty_intr(void *arg)
 	tty_lock(tp);
 
 	if (pend & SER_INT_RXREADY) {
-		while (!uart_rx_empty(sc) /* && !(tp->t_state & TS_TBLOCK)*/) {
-			xc = uart_rx_get(sc);
+		while (!uart_rx_empty(sc) && !sc->sc_isquelch) {
+			xc = uart_rx_peek(sc);
 			c = xc & 0xff;
 			if (xc & UART_STAT_FRAMERR)
 				err |= TRE_FRAMING;
@@ -303,7 +299,13 @@ uart_tty_intr(void *arg)
 				err |= TRE_OVERRUN;
 			if (xc & UART_STAT_PARERR)
 				err |= TRE_PARITY;
-			ttydisc_rint(tp, c, err);
+			if (ttydisc_rint(tp, c, err) != 0) {
+				sc->sc_isquelch = 1;
+				if ((tp->t_termios.c_cflag & CRTS_IFLOW) &&
+				    !sc->sc_hwiflow)
+					UART_SETSIG(sc, SER_DRTS);
+			} else
+				uart_rx_next(sc);
 		}
 	}
 
@@ -344,6 +346,7 @@ static struct ttydevsw uart_tty_class = 
 	.tsw_open	= uart_tty_open,
 	.tsw_close	= uart_tty_close,
 	.tsw_outwakeup	= uart_tty_outwakeup,
+	.tsw_inwakeup	= uart_tty_inwakeup,
 	.tsw_ioctl	= uart_tty_ioctl,
 	.tsw_param	= uart_tty_param,
 	.tsw_modem	= uart_tty_modem,


More information about the svn-src-all mailing list