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
This commit is contained in:
parent
afd8e45b45
commit
0acb3c4aac
@ -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);
|
||||
@ -174,6 +177,28 @@ uart_rx_get(struct uart_softc *sc)
|
||||
return (xc);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user