diff --git a/sys/dev/ic/com.c b/sys/dev/ic/com.c index f6e6b79ef804..1c4bb6c3d701 100644 --- a/sys/dev/ic/com.c +++ b/sys/dev/ic/com.c @@ -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 #include #include +#include #include #include @@ -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;