Mostly cosmetic and performance changes:

* Make the ring buffer size and water marks patchable, and allocate the buffer
  separately.
* Do the ttymalloc() at attach time.
* Reorganize the receive buffer so the status and data pair are next to each
  other.  This is slightly faster.
* Make sure we actually do turn off interrupts in comclose() if we have DDB
  configured and it's not the console.  (D'oh!!!!)
* When we exhaust the current transmit run, turn off transmit interrupts in
  comintr(), so we're fairly sure we don't get another one.
* Nuke the silly lsrmap[] idea; it's slower in the normal case.
* Cache the l_rint pointer in the soft interrupt routine.
This commit is contained in:
mycroft 1997-11-03 06:12:02 +00:00
parent 80a52b9e7d
commit 067898abb8

View File

@ -1,4 +1,4 @@
/* $NetBSD: com.c,v 1.126 1997/11/02 09:31:49 mycroft Exp $ */
/* $NetBSD: com.c,v 1.127 1997/11/03 06:12:02 mycroft Exp $ */
/*-
* Copyright (c) 1993, 1994, 1995, 1996, 1997
@ -92,6 +92,7 @@
#include <sys/syslog.h>
#include <sys/types.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <machine/intr.h>
#include <machine/bus.h>
@ -116,21 +117,11 @@ int comprobeHAYESP __P((bus_space_handle_t hayespioh, struct com_softc *sc));
static void com_enable_debugport __P((struct com_softc *));
#endif
void com_attach_subr __P((struct com_softc *sc));
void comdiag __P((void *));
int comspeed __P((long, long));
static u_char cflag2lcr __P((tcflag_t));
int comparam __P((struct tty *, struct termios *));
void comstart __P((struct tty *));
void comstop __P((struct tty *, int));
#ifdef __GENERIC_SOFT_INTERRUPTS
void comsoft __P((void *));
#else
#ifndef alpha
void comsoft __P((void));
#else
void comsoft __P((void *));
#endif
#endif
int comhwiflow __P((struct tty *, int));
void com_loadchannelregs __P((struct com_softc *));
@ -151,15 +142,36 @@ void comcnputc __P((dev_t, int));
void comcnpollc __P((dev_t, int));
#define integrate static inline
integrate void comrxint __P((struct com_softc *, struct tty *));
integrate void comtxint __P((struct com_softc *, struct tty *));
integrate void commsrint __P((struct com_softc *, struct tty *));
#ifdef __GENERIC_SOFT_INTERRUPTS
void comsoft __P((void *));
#else
#ifndef alpha
void comsoft __P((void));
#else
void comsoft __P((void *));
#endif
#endif
integrate void com_rxsoft __P((struct com_softc *, struct tty *));
integrate void com_txsoft __P((struct com_softc *, struct tty *));
integrate void com_stsoft __P((struct com_softc *, struct tty *));
integrate void com_schedrx __P((struct com_softc *));
void comdiag __P((void *));
struct cfdriver com_cd = {
NULL, "com", DV_TTY
};
/*
* Make this an option variable one can patch.
* But be warned: this must be a power of 2!
*/
u_int com_rbuf_size = COM_RING_SIZE;
/* Stop input when 3/4 of the ring is full; restart when only 1/4 is full. */
u_int com_rbuf_hiwat = (COM_RING_SIZE * 1) / 4;
u_int com_rbuf_lowat = (COM_RING_SIZE * 3) / 4;
static int comconsaddr;
static bus_space_tag_t comconstag;
static bus_space_handle_t comconsioh;
@ -350,6 +362,7 @@ com_attach_subr(sc)
int iobase = sc->sc_iobase;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
struct tty *tp;
#ifdef COM16650
u_int8_t lcr;
#endif
@ -470,6 +483,16 @@ com_attach_subr(sc)
}
#endif
tp = ttymalloc();
tp->t_oproc = comstart;
tp->t_param = comparam;
tp->t_hwiflow = comhwiflow;
tty_attach(tp);
sc->sc_tty = tp;
sc->sc_rbuf = malloc(com_rbuf_size << 1, M_DEVBUF, M_WAITOK);
sc->sc_ebuf = sc->sc_rbuf + (com_rbuf_size << 1);
if (!ISSET(sc->sc_hwflags, COM_HW_NOIEN))
SET(sc->sc_mcr, MCR_IENABLE);
@ -539,14 +562,6 @@ comopen(dev, flag, mode, p)
#endif
tp = sc->sc_tty;
if (tp == 0) {
sc->sc_tty = tp = ttymalloc();
tp->t_dev = dev;
tp->t_oproc = comstart;
tp->t_param = comparam;
tp->t_hwiflow = comhwiflow;
tty_attach(tp);
}
if (ISSET(tp->t_state, TS_ISOPEN) &&
ISSET(tp->t_state, TS_XCLUDE) &&
@ -564,6 +579,8 @@ comopen(dev, flag, mode, p)
if (!ISSET(tp->t_state, TS_ISOPEN)) {
struct termios t;
tp->t_dev = dev;
s2 = splserial();
/* Turn on interrupts. */
@ -612,8 +629,8 @@ comopen(dev, flag, mode, p)
s2 = splserial();
/* Clear the input ring, and unblock. */
sc->sc_rbput = sc->sc_rbget = 0;
sc->sc_rbavail = RXBUFSIZE;
sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
sc->sc_rbavail = com_rbuf_size;
com_iflush(sc);
CLR(sc->sc_rx_flags, RX_ANY_BLOCK);
com_hwiflow(sc);
@ -661,8 +678,7 @@ comclose(dev, flag, mode, p)
int flag, mode;
struct proc *p;
{
int unit = COMUNIT(dev);
struct com_softc *sc = com_cd.cd_devs[unit];
struct com_softc *sc = com_cd.cd_devs[COMUNIT(dev)];
struct tty *tp = sc->sc_tty;
int s;
@ -700,9 +716,8 @@ comclose(dev, flag, mode, p)
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE))
sc->sc_ier = IER_ERXRDY; /* interrupt on break */
else
#else
sc->sc_ier = 0;
#endif
sc->sc_ier = 0;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, com_ier, sc->sc_ier);
splx(s);
@ -765,8 +780,7 @@ comioctl(dev, cmd, data, flag, p)
int flag;
struct proc *p;
{
int unit = COMUNIT(dev);
struct com_softc *sc = com_cd.cd_devs[unit];
struct com_softc *sc = com_cd.cd_devs[COMUNIT(dev)];
struct tty *tp = sc->sc_tty;
int error;
@ -932,16 +946,16 @@ cflag2lcr(cflag)
u_char lcr = 0;
switch (ISSET(cflag, CSIZE)) {
case CS5:
case CS5:
SET(lcr, LCR_5BITS);
break;
case CS6:
case CS6:
SET(lcr, LCR_6BITS);
break;
case CS7:
case CS7:
SET(lcr, LCR_7BITS);
break;
case CS8:
case CS8:
SET(lcr, LCR_8BITS);
break;
}
@ -966,7 +980,7 @@ comparam(tp, t)
u_char lcr;
int s;
/* check requested parameters */
/* Check requested parameters. */
if (ospeed < 0)
return (EINVAL);
if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
@ -1059,7 +1073,7 @@ comparam(tp, t)
else
sc->sc_fifo = 0;
/* and copy to tty */
/* And copy to tty. */
tp->t_ispeed = 0;
tp->t_ospeed = t->c_ospeed;
tp->t_cflag = t->c_cflag;
@ -1086,8 +1100,8 @@ comparam(tp, t)
com_hwiflow(sc);
}
} else {
sc->sc_r_hiwat = RXHIWAT;
sc->sc_r_lowat = RXLOWAT;
sc->sc_r_hiwat = com_rbuf_hiwat;
sc->sc_r_lowat = com_rbuf_lowat;
}
splx(s);
@ -1104,7 +1118,6 @@ comparam(tp, t)
comstatus(sc, "comparam ");
#endif
/* Block or unblock as needed. */
if (!ISSET(t->c_cflag, CHWFLOW)) {
if (sc->sc_tx_stopped) {
sc->sc_tx_stopped = 0;
@ -1218,13 +1231,10 @@ comstart(tp)
int s;
s = spltty();
if (ISSET(tp->t_state, TS_BUSY))
if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
goto out;
if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP))
goto stopped;
if (sc->sc_tx_stopped)
goto stopped;
goto out;
if (tp->t_outq.c_cc <= tp->t_lowat) {
if (ISSET(tp->t_state, TS_ASLEEP)) {
@ -1233,7 +1243,7 @@ comstart(tp)
}
selwakeup(&tp->t_wsel);
if (tp->t_outq.c_cc == 0)
goto stopped;
goto out;
}
/* Grab the first contiguous region of buffer space. */
@ -1263,22 +1273,13 @@ comstart(tp)
{
int n;
n = sc->sc_fifolen;
if (n > sc->sc_tbc)
n = sc->sc_tbc;
n = sc->sc_tbc;
if (n > sc->sc_fifolen)
n = sc->sc_fifolen;
bus_space_write_multi_1(iot, ioh, com_data, sc->sc_tba, n);
sc->sc_tbc -= n;
sc->sc_tba += n;
}
splx(s);
return;
stopped:
/* Disable transmit completion interrupts if necessary. */
if (ISSET(sc->sc_ier, IER_ETXRDY)) {
CLR(sc->sc_ier, IER_ETXRDY);
bus_space_write_1(iot, ioh, com_ier, sc->sc_ier);
}
out:
splx(s);
return;
@ -1322,48 +1323,49 @@ comdiag(arg)
sc->sc_errors = 0;
splx(s);
log(LOG_WARNING,
"%s: %d silo overflow%s, %d ibuf flood%s\n",
log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf flood%s\n",
sc->sc_dev.dv_xname,
overflows, overflows == 1 ? "" : "s",
floods, floods == 1 ? "" : "s");
}
integrate void
comrxint(sc, tp)
struct com_softc *sc;
struct tty *tp;
com_rxsoft(sc, tp)
struct com_softc *sc;
struct tty *tp;
{
u_int get, cc, scc;
int code;
u_char lsr;
int s;
static int lsrmap[8] = {
0, TTY_PE,
TTY_FE, TTY_PE|TTY_FE,
TTY_FE, TTY_PE|TTY_FE,
TTY_FE, TTY_PE|TTY_FE
};
int (*rint) __P((int c, struct tty *tp)) = linesw[tp->t_line].l_rint;
u_char *get, *end;
u_int cc, scc;
u_char lsr;
int code;
int s;
end = sc->sc_ebuf;
get = sc->sc_rbget;
scc = cc = RXBUFSIZE - sc->sc_rbavail;
scc = cc = com_rbuf_size - sc->sc_rbavail;
if (cc == RXBUFSIZE) {
if (cc == com_rbuf_size) {
sc->sc_floods++;
if (sc->sc_errors++ == 0)
timeout(comdiag, sc, 60 * hz);
}
while (cc) {
lsr = sc->sc_lbuf[get];
lsr = get[1];
if (ISSET(lsr, LSR_OE)) {
sc->sc_overflows++;
if (sc->sc_errors++ == 0)
timeout(comdiag, sc, 60 * hz);
}
code = sc->sc_rbuf[get] |
lsrmap[(lsr & (LSR_BI|LSR_FE|LSR_PE)) >> 2];
if ((*linesw[tp->t_line].l_rint)(code, tp) == -1) {
code = get[0];
if (ISSET(lsr, LSR_BI | LSR_FE | LSR_PE)) {
if (ISSET(lsr, LSR_BI | LSR_FE))
SET(code, TTY_FE);
if (ISSET(lsr, LSR_PE))
SET(code, TTY_PE);
}
if ((*rint)(code, tp) == -1) {
/*
* The line discipline's buffer is out of space.
*/
@ -1375,7 +1377,9 @@ comrxint(sc, tp)
* know when there's more space available, so
* just drop the rest of the data.
*/
get = (get + cc) & RXBUFMASK;
get += cc << 1;
if (get >= end)
get -= com_rbuf_size << 1;
cc = 0;
} else {
/*
@ -1389,7 +1393,9 @@ comrxint(sc, tp)
}
break;
}
get = (get + 1) & RXBUFMASK;
get += 2;
if (get >= end)
get = sc->sc_rbuf;
cc--;
}
@ -1414,9 +1420,9 @@ comrxint(sc, tp)
}
integrate void
comtxint(sc, tp)
struct com_softc *sc;
struct tty *tp;
com_txsoft(sc, tp)
struct com_softc *sc;
struct tty *tp;
{
CLR(tp->t_state, TS_BUSY);
@ -1428,9 +1434,9 @@ comtxint(sc, tp)
}
integrate void
commsrint(sc, tp)
struct com_softc *sc;
struct tty *tp;
com_stsoft(sc, tp)
struct com_softc *sc;
struct tty *tp;
{
u_char msr, delta;
int s;
@ -1460,7 +1466,7 @@ commsrint(sc, tp)
#ifdef COM_DEBUG
if (com_debug)
comstatus(sc, "commsrint");
comstatus(sc, "com_stsoft");
#endif
}
@ -1505,17 +1511,17 @@ comsoft(arg)
if (sc->sc_rx_ready) {
sc->sc_rx_ready = 0;
comrxint(sc, tp);
com_rxsoft(sc, tp);
}
if (sc->sc_st_check) {
sc->sc_st_check = 0;
commsrint(sc, tp);
com_stsoft(sc, tp);
}
if (sc->sc_tx_done) {
sc->sc_tx_done = 0;
comtxint(sc, tp);
com_txsoft(sc, tp);
}
}
@ -1528,18 +1534,20 @@ comsoft(arg)
int
comintr(arg)
void *arg;
void *arg;
{
struct com_softc *sc = arg;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_char lsr, iir;
u_int put, cc;
u_char *put, *end;
u_int cc;
u_char lsr, iir;
iir = bus_space_read_1(iot, ioh, com_iir);
if (ISSET(iir, IIR_NOPEND))
return (0);
end = sc->sc_ebuf;
put = sc->sc_rbput;
cc = sc->sc_rbavail;
@ -1566,23 +1574,30 @@ comintr(arg)
if (ISSET(lsr, LSR_RCV_MASK) &&
!ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) {
for (; ISSET(lsr, LSR_RCV_MASK) && cc > 0; cc--) {
sc->sc_rbuf[put] =
bus_space_read_1(iot, ioh, com_data);
sc->sc_lbuf[put] = lsr;
put = (put + 1) & RXBUFMASK;
while (cc > 0) {
put[0] = bus_space_read_1(iot, ioh, com_data);
put[1] = lsr;
put += 2;
if (put >= end)
put = sc->sc_rbuf;
cc--;
lsr = bus_space_read_1(iot, ioh, com_lsr);
if (!ISSET(lsr, LSR_RCV_MASK))
break;
}
/*
* Current string of incoming characters ended because
* no more data was available. Schedule a receive event
* if any data was received. Drop any characters that
* we couldn't handle.
* no more data was available or we ran out of space.
* Schedule a receive event if any data was received.
* If we're out of space, turn off receive interrupts.
*/
sc->sc_rbput = put;
sc->sc_rbavail = cc;
if (!ISSET(sc->sc_rx_flags, RX_TTY_OVERFLOWED))
sc->sc_rx_ready = 1;
/*
* See if we are in danger of overflowing a buffer. If
* so, use hardware flow control to ease the pressure.
@ -1592,6 +1607,7 @@ comintr(arg)
SET(sc->sc_rx_flags, RX_IBUF_BLOCKED);
com_hwiflow(sc);
}
/*
* If we're out of space, disable receive interrupts
* until the queue has drained a bit.
@ -1615,7 +1631,7 @@ comintr(arg)
delta = msr ^ sc->sc_msr;
sc->sc_msr = msr;
if (ISSET(delta, sc->sc_msr_mask)) {
sc->sc_msr_delta |= delta;
SET(sc->sc_msr_delta, delta);
/*
* Stop output immediately if we lose the output
@ -1650,19 +1666,27 @@ comintr(arg)
sc->sc_tbc = sc->sc_heldtbc;
sc->sc_heldtbc = 0;
}
/* Output the next chunk of the contiguous buffer, if any. */
if (sc->sc_tbc > 0) {
int n;
n = sc->sc_fifolen;
if (n > sc->sc_tbc)
n = sc->sc_tbc;
n = sc->sc_tbc;
if (n > sc->sc_fifolen)
n = sc->sc_fifolen;
bus_space_write_multi_1(iot, ioh, com_data, sc->sc_tba, n);
sc->sc_tbc -= n;
sc->sc_tba += n;
} else if (sc->sc_tx_busy) {
sc->sc_tx_busy = 0;
sc->sc_tx_done = 1;
} else {
/* Disable transmit completion interrupts if necessary. */
if (ISSET(sc->sc_ier, IER_ETXRDY)) {
CLR(sc->sc_ier, IER_ETXRDY);
bus_space_write_1(iot, ioh, com_ier, sc->sc_ier);
}
if (sc->sc_tx_busy) {
sc->sc_tx_busy = 0;
sc->sc_tx_done = 1;
}
}
}
@ -1716,7 +1740,7 @@ com_common_putc(iot, ioh, c)
{
int s = splserial();
u_char stat;
register int timo;
int timo;
/* wait for any pending transmission to finish */
timo = 50000;