summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authormarcel <marcel@FreeBSD.org>2009-10-02 22:30:44 +0000
committermarcel <marcel@FreeBSD.org>2009-10-02 22:30:44 +0000
commit6a8474f5b04346fb12ab646b3e74f79cb3e9d791 (patch)
treec6fe318664ecc37270ff8564c51d113fe9d87934 /sys/dev
parent86b3bcad7dc713b1ee5da3749b9f6e8662a9f6ba (diff)
downloadFreeBSD-src-6a8474f5b04346fb12ab646b3e74f79cb3e9d791.zip
FreeBSD-src-6a8474f5b04346fb12ab646b3e74f79cb3e9d791.tar.gz
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
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/uart/uart_bus.h25
-rw-r--r--sys/dev/uart/uart_core.c2
-rw-r--r--sys/dev/uart/uart_tty.c55
3 files changed, 55 insertions, 27 deletions
diff --git a/sys/dev/uart/uart_bus.h b/sys/dev/uart/uart_bus.h
index 7154d85..b1498f5 100644
--- a/sys/dev/uart/uart_bus.h
+++ b/sys/dev/uart/uart_bus.h
@@ -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;
diff --git a/sys/dev/uart/uart_core.c b/sys/dev/uart/uart_core.c
index 932c08a..98b044f 100644
--- a/sys/dev/uart/uart_core.c
+++ b/sys/dev/uart/uart_core.c
@@ -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;
diff --git a/sys/dev/uart/uart_tty.c b/sys/dev/uart/uart_tty.c
index cb77f65..2f442f7 100644
--- a/sys/dev/uart/uart_tty.c
+++ b/sys/dev/uart/uart_tty.c
@@ -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 termios *t)
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,
OpenPOWER on IntegriCloud