summaryrefslogtreecommitdiff
path: root/sys/dev/ic/z8530tty.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ic/z8530tty.c')
-rw-r--r--sys/dev/ic/z8530tty.c1817
1 files changed, 1120 insertions, 697 deletions
diff --git a/sys/dev/ic/z8530tty.c b/sys/dev/ic/z8530tty.c
index e22122b010d..c31c146b998 100644
--- a/sys/dev/ic/z8530tty.c
+++ b/sys/dev/ic/z8530tty.c
@@ -1,5 +1,35 @@
-/* $OpenBSD: z8530tty.c,v 1.23 2010/07/02 17:27:01 nicm Exp $ */
-/* $NetBSD: z8530tty.c,v 1.13 1996/10/16 20:42:14 gwr Exp $ */
+/* $OpenBSD: z8530tty.c,v 1.24 2013/04/21 14:44:16 sebastia Exp $ */
+/* $NetBSD: z8530tty.c,v 1.77 2001/05/30 15:24:24 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998, 1999
+ * Charles M. Hannum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Charles M. Hannum.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
/*
* Copyright (c) 1994 Gordon W. Ross
@@ -57,9 +87,12 @@
* into independent child drivers.
*
* RTS/CTS flow-control support was a collaboration of:
- * Gordon Ross <gwr@netbsd.org>,
+ * Gordon Ross <gwr@NetBSD.org>,
* Bill Studenmund <wrstuden@loki.stanford.edu>
* Ian Dall <Ian.Dall@dsto.defence.gov.au>
+ *
+ * The driver was massively overhauled in November 1997 by Charles Hannum,
+ * fixing *many* bugs, and substantially improving performance.
*/
#include <sys/param.h>
@@ -78,9 +111,7 @@
#include <dev/ic/z8530reg.h>
#include <machine/z8530var.h>
-#ifdef KGDB
-extern int zs_check_kgdb();
-#endif
+#include <dev/cons.h>
/*
* Allow the MD var.h to override the default CFLAG so that
@@ -99,39 +130,41 @@ extern int zs_check_kgdb();
#define ZSTTY_RING_SIZE 2048
#endif
+struct cfdriver zstty_cd = {
+ NULL, "zstty", DV_TTY
+};
+
/*
* Make this an option variable one can patch.
* But be warned: this must be a power of 2!
*/
-int zstty_rbuf_size = ZSTTY_RING_SIZE;
+u_int zstty_rbuf_size = ZSTTY_RING_SIZE;
-/* This should usually be 3/4 of ZSTTY_RING_SIZE */
-int zstty_rbuf_hiwat = (ZSTTY_RING_SIZE - (ZSTTY_RING_SIZE >> 2));
+/* Stop input when 3/4 of the ring is full; restart when only 1/4 is full. */
+u_int zstty_rbuf_hiwat = (ZSTTY_RING_SIZE * 1) / 4;
+u_int zstty_rbuf_lowat = (ZSTTY_RING_SIZE * 3) / 4;
struct zstty_softc {
struct device zst_dev; /* required first: base device */
struct tty *zst_tty;
struct zs_chanstate *zst_cs;
- int zst_hwflags; /* see z8530var.h */
- int zst_swflags; /* TIOCFLAG_SOFTCAR, ... <ttycom.h> */
+ struct timeout zst_diag_ch;
- /*
- * Printing an overrun error message often takes long enough to
- * cause another overrun, so we only print one per second.
- */
- long zst_rotime; /* time of last ring overrun */
- long zst_fotime; /* time of last fifo overrun */
+ u_int zst_overflows,
+ zst_floods,
+ zst_errors;
- /*
- * The receive ring buffer.
- */
- int zst_rbget; /* ring buffer `get' index */
- volatile int zst_rbput; /* ring buffer `put' index */
- int zst_ringmask;
- int zst_rbhiwat;
+ int zst_hwflags, /* see z8530var.h */
+ zst_swflags; /* TIOCFLAG_SOFTCAR, ... <ttycom.h> */
- u_short *zst_rbuf; /* rr1, data pairs */
+ u_int zst_r_hiwat,
+ zst_r_lowat;
+ uint8_t *volatile zst_rbget,
+ *volatile zst_rbput;
+ volatile u_int zst_rbavail;
+ uint8_t *zst_rbuf,
+ *zst_ebuf;
/*
* The transmit byte count and address are used for pseudo-DMA
@@ -139,57 +172,76 @@ struct zstty_softc {
* to get pending changes done; heldtbc is used for this. It can
* also be stopped for ^S; this sets TS_TTSTOP in tp->t_state.
*/
- int zst_tbc; /* transmit byte count */
- caddr_t zst_tba; /* transmit buffer address */
- int zst_heldtbc; /* held tbc while xmission stopped */
+ uint8_t *zst_tba; /* transmit buffer address */
+ u_int zst_tbc, /* transmit byte count */
+ zst_heldtbc; /* held tbc while xmission stopped */
/* Flags to communicate with zstty_softint() */
- volatile char zst_rx_blocked; /* input block at ring */
- volatile char zst_rx_overrun; /* ring overrun */
- volatile char zst_tx_busy; /* working on an output chunk */
- volatile char zst_tx_done; /* done with one output chunk */
- volatile char zst_tx_stopped; /* H/W level stop (lost CTS) */
- volatile char zst_st_check; /* got a status interrupt */
- char pad[2];
+ volatile uint8_t zst_rx_flags, /* receiver blocked */
+#define RX_TTY_BLOCKED 0x01
+#define RX_TTY_OVERFLOWED 0x02
+#define RX_IBUF_BLOCKED 0x04
+#define RX_IBUF_OVERFLOWED 0x08
+#define RX_ANY_BLOCK 0x0f
+ zst_tx_busy, /* working on an output chunk */
+ zst_tx_done, /* done with one output chunk */
+ zst_tx_stopped, /* H/W level stop (lost CTS) */
+ zst_st_check, /* got a status interrupt */
+ zst_rx_ready;
+
+ /* PPS signal on DCD, with or without inkernel clock disciplining */
+ uint8_t zst_ppsmask; /* pps signal mask */
+ uint8_t zst_ppsassert; /* pps leading edge */
+ uint8_t zst_ppsclear; /* pps trailing edge */
};
-
/* Definition of the driver for autoconfig. */
-static int zstty_match(struct device *, void *, void *);
-static void zstty_attach(struct device *, struct device *, void *);
+int zstty_match(struct device *, void *, void *);
+void zstty_attach(struct device *, struct device *, void *);
-struct cfattach zstty_ca = {
+const struct cfattach zstty_ca = {
sizeof(struct zstty_softc), zstty_match, zstty_attach
};
-struct cfdriver zstty_cd = {
- NULL, "zstty", DV_TTY
-};
+cdev_decl(zs);
struct zsops zsops_tty;
-/* Routines called from other code. */
-cdev_decl(zs); /* open, close, read, write, ioctl, stop, ... */
-
-static void zsstart(struct tty *);
-static int zsparam(struct tty *, struct termios *);
-static void zs_modem(struct zstty_softc *zst, int onoff);
-static int zshwiflow(struct tty *, int);
-static void zs_hwiflow(struct zstty_softc *, int);
-static void zstty_rxint(register struct zs_chanstate *);
-static void zstty_txint(register struct zs_chanstate *);
-static void zstty_stint(register struct zs_chanstate *);
-static void zstty_softint(struct zs_chanstate *);
-static void zsoverrun(struct zstty_softc *, long *, char *);
+void zs_shutdown(struct zstty_softc *);
+void zsstart(struct tty *);
+int zsparam(struct tty *, struct termios *);
+void zs_modem(struct zstty_softc *, int);
+void tiocm_to_zs(struct zstty_softc *, u_long, int);
+int zs_to_tiocm(struct zstty_softc *);
+int zshwiflow(struct tty *, int);
+void zs_hwiflow(struct zstty_softc *);
+void zs_maskintr(struct zstty_softc *);
+
+struct zstty_softc *zs_device_lookup(struct cfdriver *, int);
+
+/* Low-level routines. */
+void zstty_rxint(struct zs_chanstate *);
+void zstty_stint(struct zs_chanstate *, int);
+void zstty_txint(struct zs_chanstate *);
+void zstty_softint(struct zs_chanstate *);
+void zstty_diag(void *);
+
+#define ZSUNIT(x) (minor(x) & 0x7f)
+#define ZSDIALOUT(x) (minor(x) & 0x80)
+
+struct zstty_softc *
+zs_device_lookup(struct cfdriver *cf, int unit)
+{
+ return (struct zstty_softc *)device_lookup(cf, unit);
+}
+
/*
* zstty_match: how is this zs channel configured?
*/
-int
-zstty_match(parent, match, aux)
- struct device *parent;
- void *match, *aux;
+int
+zstty_match(struct device *parent, void *vcf, void *aux)
{
- struct cfdata *cf = match;
+ struct cfdata *cf = vcf;
struct zsc_attach_args *args = aux;
/* Exact match is better than wildcard. */
@@ -203,55 +255,84 @@ zstty_match(parent, match, aux)
return 0;
}
-void
-zstty_attach(parent, self, aux)
- struct device *parent, *self;
- void *aux;
-
+void
+zstty_attach(struct device *parent, struct device *self, void *aux)
{
- struct zsc_softc *zsc = (void *) parent;
- struct zstty_softc *zst = (void *) self;
+ struct zsc_softc *zsc = (struct zsc_softc *)parent;
+ struct zstty_softc *zst = (struct zstty_softc *)self;
+ struct cfdata *cf = self->dv_cfdata;
struct zsc_attach_args *args = aux;
struct zs_chanstate *cs;
- struct cfdata *cf;
struct tty *tp;
- int channel, tty_unit;
+ int channel, s, tty_unit;
dev_t dev;
+ const char *i, *o;
+ int dtr_on;
+ int resetbit;
+
+ timeout_set(&zst->zst_diag_ch, zstty_diag, zst);
- cf = zst->zst_dev.dv_cfdata;
tty_unit = zst->zst_dev.dv_unit;
channel = args->channel;
- cs = &zsc->zsc_cs[channel];
+ cs = zsc->zsc_cs[channel];
cs->cs_private = zst;
cs->cs_ops = &zsops_tty;
zst->zst_cs = cs;
zst->zst_swflags = cf->cf_flags; /* softcar, etc. */
zst->zst_hwflags = args->hwflags;
- dev = makedev(ZSTTY_MAJOR, tty_unit);
+ dev = makedev(zs_major, tty_unit);
if (zst->zst_swflags)
printf(" flags 0x%x", zst->zst_swflags);
- if (zst->zst_hwflags & ZS_HWFLAG_CONSOLE)
- printf(": console");
- else {
+ /*
+ * Check whether we serve as a console device.
+ * XXX - split console input/output channels aren't
+ * supported yet on /dev/console
+ */
+ i = o = NULL;
+ if ((zst->zst_hwflags & ZS_HWFLAG_CONSOLE_INPUT) != 0) {
+ i = " input";
+ if ((args->hwflags & ZS_HWFLAG_USE_CONSDEV) != 0) {
+ args->consdev->cn_dev = dev;
+ cn_tab->cn_pollc = args->consdev->cn_pollc;
+ cn_tab->cn_getc = args->consdev->cn_getc;
+ }
+ cn_tab->cn_dev = dev;
+ }
+ if ((zst->zst_hwflags & ZS_HWFLAG_CONSOLE_OUTPUT) != 0) {
+ o = " output";
+ if ((args->hwflags & ZS_HWFLAG_USE_CONSDEV) != 0) {
+ cn_tab->cn_putc = args->consdev->cn_putc;
+ }
+ cn_tab->cn_dev = dev;
+ }
+ if (i != NULL || o != NULL) {
+ printf(": console%s", i ? (o ? "" : i) : o);
+ }
+
#ifdef KGDB
+ if (zs_check_kgdb(cs, dev)) {
/*
- * Allow kgdb to "take over" this port. If this port is
- * NOT the kgdb port, zs_check_kgdb() will return zero.
- * If it IS the kgdb port, it will print "kgdb,...\n"
- * and then return non-zero.
+ * Allow kgdb to "take over" this port. Returns true
+ * if this serial port is in-use by kgdb.
*/
- if (zs_check_kgdb(cs, dev)) {
- /*
- * This is the kgdb port (exclusive use)
- * so skip the normal attach code.
- */
- return;
- }
-#endif
+ printf(": kgdb\n");
+ /*
+ * This is the kgdb port (exclusive use)
+ * so skip the normal attach code.
+ */
+ return;
}
+#endif
+
+#if defined(__sparc__) || defined(__sparc64__)
+ if (strcmp(args->type, "keyboard") == 0 ||
+ strcmp(args->type, "mouse") == 0)
+ printf(": %s", args->type);
+#endif
+
printf("\n");
tp = ttymalloc(0);
@@ -261,40 +342,64 @@ zstty_attach(parent, self, aux)
tp->t_hwiflow = zshwiflow;
zst->zst_tty = tp;
- zst->zst_rbhiwat = zstty_rbuf_size; /* impossible value */
- zst->zst_ringmask = zstty_rbuf_size - 1;
- zst->zst_rbuf = malloc(zstty_rbuf_size * sizeof(zst->zst_rbuf[0]),
- M_DEVBUF, M_WAITOK);
+ zst->zst_rbuf = malloc(zstty_rbuf_size << 1, M_DEVBUF, M_WAITOK);
+ zst->zst_ebuf = zst->zst_rbuf + (zstty_rbuf_size << 1);
+ /* Disable the high water mark. */
+ zst->zst_r_hiwat = 0;
+ zst->zst_r_lowat = 0;
+ zst->zst_rbget = zst->zst_rbput = zst->zst_rbuf;
+ zst->zst_rbavail = zstty_rbuf_size;
+
+ /* if there are no enable/disable functions, assume the device
+ is always enabled */
+ if (!cs->enable)
+ cs->enabled = 1;
/*
* Hardware init
*/
- if (zst->zst_hwflags & ZS_HWFLAG_CONSOLE) {
- /* This unit is the console. */
- zst->zst_swflags |= TIOCFLAG_SOFTCAR;
- /* Call _param so interrupts get enabled. */
- cs->cs_defspeed = zs_getspeed(cs);
- tp->t_ispeed = cs->cs_defspeed;
- tp->t_ospeed = cs->cs_defspeed;
- tp->t_cflag = ZSTTY_DEF_CFLAG;
- (void) zsparam(tp, &tp->t_termios);
- } else {
- /* Not the console; may need reset. */
- int reset, s;
- reset = (channel == 0) ?
- ZSWR9_A_RESET : ZSWR9_B_RESET;
+ dtr_on = 0;
+ resetbit = 0;
+ if (ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) {
+ /* Call zsparam similar to open. */
+ struct termios t;
+
+ /* Wait a while for previous console output to complete */
+ DELAY(10000);
+
+ /* Setup the "new" parameters in t. */
+ t.c_ispeed = 0;
+ t.c_ospeed = cs->cs_defspeed;
+ t.c_cflag = cs->cs_defcflag;
+
s = splzs();
- zs_write_reg(cs, 9, reset);
+
+ /*
+ * Turn on receiver and status interrupts.
+ * We defer the actual write of the register to zsparam(),
+ * but we must make sure status interrupts are turned on by
+ * the time zsparam() reads the initial rr0 state.
+ */
+ SET(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_TIE | ZSWR1_SIE);
+
splx(s);
+
+ /* Make sure zsparam will see changes. */
+ tp->t_ospeed = 0;
+ (void)zsparam(tp, &t);
+
+ /* Make sure DTR is on now. */
+ dtr_on = 1;
+ } else if (!ISSET(zst->zst_hwflags, ZS_HWFLAG_NORESET)) {
+ /* Not the console; may need reset. */
+ resetbit = (channel == 0) ? ZSWR9_A_RESET : ZSWR9_B_RESET;
}
- /*
- * Initialize state of modem control lines (DTR).
- * If softcar is set, turn on DTR now and leave it.
- * otherwise, turn off DTR now, and raise in open.
- * (Keeps modem from answering too early.)
- */
- zs_modem(zst, (zst->zst_swflags & TIOCFLAG_SOFTCAR) ? 1 : 0);
+ s = splzs();
+ if (resetbit)
+ zs_write_reg(cs, 9, resetbit);
+ zs_modem(zst, dtr_on);
+ splx(s);
}
@@ -302,42 +407,81 @@ zstty_attach(parent, self, aux)
* Return pointer to our tty.
*/
struct tty *
-zstty(dev)
- dev_t dev;
+zstty(dev_t dev)
{
- struct zstty_softc *zst;
- int unit = minor(dev);
+ struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev));
-#ifdef DIAGNOSTIC
- if (unit >= zstty_cd.cd_ndevs)
- panic("zstty");
-#endif
- zst = zstty_cd.cd_devs[unit];
return (zst->zst_tty);
}
+void
+zs_shutdown(struct zstty_softc *zst)
+{
+ struct zs_chanstate *cs = zst->zst_cs;
+ struct tty *tp = zst->zst_tty;
+ int s;
+
+ s = splzs();
+
+ /* If we were asserting flow control, then deassert it. */
+ SET(zst->zst_rx_flags, RX_IBUF_BLOCKED);
+ zs_hwiflow(zst);
+
+ /* Clear any break condition set with TIOCSBRK. */
+ zs_break(cs, 0);
+
+ /* Turn off PPS capture on last close. */
+ zst->zst_ppsmask = 0;
+
+ /*
+ * Hang up if necessary. Wait a bit, so the other side has time to
+ * notice even if we immediately open the port again.
+ */
+ if (ISSET(tp->t_cflag, HUPCL) || ISSET(tp->t_state, TS_WOPEN)) {
+ zs_modem(zst, 0);
+ /* hold low for 1 second */
+ (void)tsleep(cs, TTIPRI, ttclos, hz);
+ }
+
+ /* Turn off interrupts if not the console. */
+ if (!ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) {
+ CLR(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_TIE | ZSWR1_SIE);
+ cs->cs_creg[1] = cs->cs_preg[1];
+ zs_write_reg(cs, 1, cs->cs_creg[1]);
+ }
+
+ /* Call the power management hook. */
+ if (cs->disable) {
+#ifdef DIAGNOSTIC
+ if (!cs->enabled)
+ panic("%s: not enabled?", __func__);
+#endif
+ (*cs->disable)(zst->zst_cs);
+ }
+
+ splx(s);
+}
+
/*
* Open a zs serial (tty) port.
*/
int
-zsopen(dev, flags, mode, p)
- dev_t dev;
- int flags;
- int mode;
- struct proc *p;
+zsopen(dev_t dev, int flags, int mode, struct proc *p)
{
- register struct tty *tp;
- register struct zs_chanstate *cs;
struct zstty_softc *zst;
- int error, s, unit;
+ struct zs_chanstate *cs;
+ struct tty *tp;
+ int s;
+#if IPL_ZS != IPL_TTY
+ int s2;
+#endif
+ int error;
- unit = minor(dev);
- if (unit >= zstty_cd.cd_ndevs)
- return (ENXIO);
- zst = zstty_cd.cd_devs[unit];
+ zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev));
if (zst == NULL)
return (ENXIO);
+
tp = zst->zst_tty;
cs = zst->zst_cs;
@@ -345,78 +489,204 @@ zsopen(dev, flags, mode, p)
if (tp == NULL)
return (EBUSY);
- /* It's simpler to do this up here. */
- if (((tp->t_state & (TS_ISOPEN | TS_XCLUDE))
- == (TS_ISOPEN | TS_XCLUDE))
- && (suser(p, 0) != 0) )
- {
+ if (ISSET(tp->t_state, TS_ISOPEN) &&
+ ISSET(tp->t_state, TS_XCLUDE) &&
+ suser(p, 0) != 0)
return (EBUSY);
- }
s = spltty();
- if ((tp->t_state & TS_ISOPEN) == 0) {
- /* First open. */
+ /*
+ * Do the following iff this is a first open.
+ */
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+ struct termios t;
+
+ tp->t_dev = dev;
+
+ /* Call the power management hook. */
+ if (cs->enable) {
+ if ((*cs->enable)(cs)) {
+ splx(s);
+ printf("%s: device enable failed\n",
+ zst->zst_dev.dv_xname);
+ return (EIO);
+ }
+ }
+
+ /*
+ * Initialize the termios status to the defaults. Add in the
+ * sticky bits from TIOCSFLAGS.
+ */
+ t.c_ispeed = 0;
+ t.c_ospeed = cs->cs_defspeed;
+ t.c_cflag = cs->cs_defcflag;
+ if (ISSET(zst->zst_swflags, TIOCFLAG_CLOCAL))
+ SET(t.c_cflag, CLOCAL);
+ if (ISSET(zst->zst_swflags, TIOCFLAG_CRTSCTS))
+ SET(t.c_cflag, CRTSCTS);
+ if (ISSET(zst->zst_swflags, TIOCFLAG_MDMBUF))
+ SET(t.c_cflag, MDMBUF);
+
+#if IPL_ZS != IPL_TTY
+ s2 = splzs();
+#endif
+
+ /*
+ * Turn on receiver and status interrupts.
+ * We defer the actual write of the register to zsparam(),
+ * but we must make sure status interrupts are turned on by
+ * the time zsparam() reads the initial rr0 state.
+ */
+ SET(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_TIE | ZSWR1_SIE);
+
+ /* Clear PPS capture state on first open. */
+ zst->zst_ppsmask = 0;
+
+#if IPL_ZS != IPL_TTY
+ splx(s2);
+#endif
+
+ /* Make sure zsparam will see changes. */
+ tp->t_ospeed = 0;
+ (void)zsparam(tp, &t);
+
+ /*
+ * Note: zsparam has done: cflag, ispeed, ospeed
+ * so we just need to do: iflag, oflag, lflag, cc
+ * For "raw" mode, just leave all zeros.
+ */
+ if (!ISSET(zst->zst_hwflags, ZS_HWFLAG_RAW)) {
+ tp->t_iflag = TTYDEF_IFLAG;
+ tp->t_oflag = TTYDEF_OFLAG;
+ tp->t_lflag = TTYDEF_LFLAG;
+ } else {
+ tp->t_iflag = 0;
+ tp->t_oflag = 0;
+ tp->t_lflag = 0;
+ }
ttychars(tp);
- tp->t_iflag = TTYDEF_IFLAG;
- tp->t_oflag = TTYDEF_OFLAG;
- tp->t_cflag = ZSTTY_DEF_CFLAG;
- if (zst->zst_swflags & TIOCFLAG_CLOCAL)
- tp->t_cflag |= CLOCAL;
- if (zst->zst_swflags & TIOCFLAG_CRTSCTS)
- tp->t_cflag |= CRTSCTS;
- if (zst->zst_swflags & TIOCFLAG_MDMBUF)
- tp->t_cflag |= MDMBUF;
- tp->t_lflag = TTYDEF_LFLAG;
- tp->t_ispeed = tp->t_ospeed = cs->cs_defspeed;
- (void) zsparam(tp, &tp->t_termios);
ttsetwater(tp);
- /* Flush any pending input. */
- zst->zst_rbget = zst->zst_rbput;
- zs_iflush(cs); /* XXX */
- /* Turn on DTR */
- zs_modem(zst, 1);
- if (zst->zst_swflags & TIOCFLAG_SOFTCAR) {
- tp->t_state |= TS_CARR_ON;
+
+ if (ZSDIALOUT(dev))
+ SET(tp->t_state, TS_CARR_ON);
+ else
+ CLR(tp->t_state, TS_CARR_ON);
+
+#if IPL_ZS != IPL_TTY
+ s2 = splzs();
+#endif
+
+ /* Clear the input ring, and unblock. */
+ zst->zst_rbget = zst->zst_rbput = zst->zst_rbuf;
+ zst->zst_rbavail = zstty_rbuf_size;
+ zs_iflush(cs);
+ CLR(zst->zst_rx_flags, RX_ANY_BLOCK);
+ zs_hwiflow(zst);
+
+#if IPL_ZS != IPL_TTY
+ splx(s2);
+#endif
+ }
+
+ if (ZSDIALOUT(dev)) {
+ if (ISSET(tp->t_state, TS_ISOPEN)) {
+ /* someone already is dialed in... */
+ splx(s);
+ return EBUSY;
}
+ cs->cs_cua = 1;
}
+
error = 0;
+ /* wait for carrier if necessary */
+ if (ISSET(flags, O_NONBLOCK)) {
+ if (!ZSDIALOUT(dev) && cs->cs_cua) {
+ /* Opening TTY non-blocking... but the CUA is busy */
+ error = EBUSY;
+ }
+ } else
+ while (cs->cs_cua ||
+ (!ISSET(tp->t_cflag, CLOCAL) && !ISSET(tp->t_state, TS_CARR_ON))) {
+ int rr0;
- /* Wait for carrier. */
- for (;;) {
+ error = 0;
+ SET(tp->t_state, TS_WOPEN);
- /* Might never get status intr if carrier already on. */
- cs->cs_rr0 = zs_read_csr(cs);
- if (cs->cs_rr0 & ZSRR0_DCD) {
- tp->t_state |= TS_CARR_ON;
- break;
+ if (!ZSDIALOUT(dev) && !cs->cs_cua) {
+ /*
+ * Turn on DTR. We must always do this on non-CUA
+ * devices, even if carrier is not present, because
+ * otherwise we'd have to use TIOCSDTR immediately
+ * after setting CLOCAL, which applications do not
+ * expect. We always assert DTR while the device is
+ * open unless explicitly requested to deassert it.
+ */
+#if IPL_ZS != IPL_TTY
+ s2 = splzs();
+#endif
+ zs_modem(zst, 1);
+ rr0 = zs_read_csr(cs);
+#if IPL_ZS != IPL_TTY
+ splx(s2);
+#endif
+
+ /* loop, turning on the device, until carrier present */
+ if (ISSET(rr0, ZSRR0_DCD) ||
+ ISSET(zst->zst_swflags, TIOCFLAG_SOFTCAR))
+ SET(tp->t_state, TS_CARR_ON);
}
- if ((tp->t_state & TS_CARR_ON) ||
- (tp->t_cflag & CLOCAL) ||
- (flags & O_NONBLOCK) )
- {
+ if ((ISSET(tp->t_cflag, CLOCAL) ||
+ ISSET(tp->t_state, TS_CARR_ON)) && !cs->cs_cua)
break;
+
+ error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
+ ttopen, 0);
+
+ if (!ZSDIALOUT(dev) && cs->cs_cua && error == EINTR) {
+ error = 0;
+ continue;
}
- tp->t_state |= TS_WOPEN;
- error = ttysleep(tp, (caddr_t)&tp->t_rawq,
- TTIPRI | PCATCH, ttopen, 0);
if (error) {
- if ((tp->t_state & TS_ISOPEN) == 0) {
- /* Never get here with softcar */
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+#if IPL_ZS != IPL_TTY
+ s2 = splzs();
+#endif
zs_modem(zst, 0);
- tp->t_state &= ~TS_WOPEN;
+#if IPL_ZS != IPL_TTY
+ splx(s2);
+#endif
+ CLR(tp->t_state, TS_WOPEN);
ttwakeup(tp);
}
+ if (ZSDIALOUT(dev))
+ cs->cs_cua = 0;
+ CLR(tp->t_state, TS_WOPEN);
break;
}
+ if (!ZSDIALOUT(dev) && cs->cs_cua)
+ continue;
}
splx(s);
if (error == 0)
- error = linesw[tp->t_line].l_open(dev, tp, p);
+ error = ((*linesw[tp->t_line].l_open)(dev, tp, p));
+ if (error)
+ goto bad;
+
+ return (0);
+
+bad:
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+ /*
+ * We failed to open the device, and nobody else had it opened.
+ * Clean up the state as appropriate.
+ */
+ zs_shutdown(zst);
+ }
return (error);
}
@@ -425,40 +695,33 @@ zsopen(dev, flags, mode, p)
* Close a zs serial port.
*/
int
-zsclose(dev, flags, mode, p)
- dev_t dev;
- int flags;
- int mode;
- struct proc *p;
+zsclose(dev_t dev, int flags, int mode, struct proc *p)
{
- struct zstty_softc *zst;
- register struct zs_chanstate *cs;
- register struct tty *tp;
- int hup;
-
- zst = zstty_cd.cd_devs[minor(dev)];
- cs = zst->zst_cs;
- tp = zst->zst_tty;
+ struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev));
+ struct zs_chanstate *cs = zst->zst_cs;
+ struct tty *tp = zst->zst_tty;
+ int s;
/* XXX This is for cons.c. */
- if ((tp->t_state & TS_ISOPEN) == 0)
+ if (!ISSET(tp->t_state, TS_ISOPEN))
return 0;
(*linesw[tp->t_line].l_close)(tp, flags, p);
- hup = tp->t_cflag & HUPCL;
- if (zst->zst_swflags & TIOCFLAG_SOFTCAR)
- hup = 0;
- if (hup) {
- zs_modem(zst, 0);
- /* hold low for 1 second */
- (void) tsleep((caddr_t)cs, TTIPRI, ttclos, hz);
- }
- if (cs->cs_creg[5] & ZSWR5_BREAK) {
- zs_break(cs, 0);
- }
- /* XXX - turn off interrupts? */
+ s = spltty();
+ cs->cs_cua = 0;
ttyclose(tp);
+ splx(s);
+
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+ /*
+ * Although we got a last close, the device may still be in
+ * use; e.g. if this was the dialout node, and there are still
+ * processes waiting for carrier on the non-dialout node.
+ */
+ zs_shutdown(zst);
+ }
+
return (0);
}
@@ -466,62 +729,51 @@ zsclose(dev, flags, mode, p)
* Read/write zs serial port.
*/
int
-zsread(dev, uio, flags)
- dev_t dev;
- struct uio *uio;
- int flags;
+zsread(dev_t dev, struct uio *uio, int flags)
{
- register struct zstty_softc *zst;
- register struct tty *tp;
+ struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev));
+ struct tty *tp = zst->zst_tty;
- zst = zstty_cd.cd_devs[minor(dev)];
- tp = zst->zst_tty;
- return (linesw[tp->t_line].l_read(tp, uio, flags));
+ return (*linesw[tp->t_line].l_read)(tp, uio, flags);
}
int
-zswrite(dev, uio, flags)
- dev_t dev;
- struct uio *uio;
- int flags;
+zswrite(dev_t dev, struct uio *uio, int flags)
{
- register struct zstty_softc *zst;
- register struct tty *tp;
+ struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev));
+ struct tty *tp = zst->zst_tty;
- zst = zstty_cd.cd_devs[minor(dev)];
- tp = zst->zst_tty;
- return (linesw[tp->t_line].l_write(tp, uio, flags));
+ return (*linesw[tp->t_line].l_write)(tp, uio, flags);
}
-#define TIOCFLAG_ALL (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | \
- TIOCFLAG_CRTSCTS | TIOCFLAG_MDMBUF )
-
int
-zsioctl(dev, cmd, data, flag, p)
- dev_t dev;
- u_long cmd;
- caddr_t data;
- int flag;
- struct proc *p;
+zsioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
- register struct zstty_softc *zst;
- register struct zs_chanstate *cs;
- register struct tty *tp;
- register int error, tmp;
-
- zst = zstty_cd.cd_devs[minor(dev)];
- cs = zst->zst_cs;
- tp = zst->zst_tty;
+ struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev));
+ struct zs_chanstate *cs = zst->zst_cs;
+ struct tty *tp = zst->zst_tty;
+ int error;
+ int s;
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
if (error >= 0)
return (error);
+
error = ttioctl(tp, cmd, data, flag, p);
if (error >= 0)
return (error);
- switch (cmd) {
+#ifdef ZS_MD_IOCTL
+ error = ZS_MD_IOCTL;
+ if (error >= 0)
+ return (error);
+#endif /* ZS_MD_IOCTL */
+
+ error = 0;
+
+ s = splzs();
+ switch (cmd) {
case TIOCSBRK:
zs_break(cs, 1);
break;
@@ -536,17 +788,9 @@ zsioctl(dev, cmd, data, flag, p)
case TIOCSFLAGS:
error = suser(p, 0);
- if (error != 0)
- return (EPERM);
- tmp = *(int *)data;
- /* Check for random bits... */
- if (tmp & ~TIOCFLAG_ALL)
- return(EINVAL);
- /* Silently enforce softcar on the console. */
- if (zst->zst_hwflags & ZS_HWFLAG_CONSOLE)
- tmp |= TIOCFLAG_SOFTCAR;
- /* These flags take effect during open. */
- zst->zst_swflags = tmp;
+ if (error)
+ break;
+ zst->zst_swflags = *(int *)data;
break;
case TIOCSDTR:
@@ -560,72 +804,68 @@ zsioctl(dev, cmd, data, flag, p)
case TIOCMSET:
case TIOCMBIS:
case TIOCMBIC:
+ tiocm_to_zs(zst, cmd, *(int *)data);
+ break;
+
case TIOCMGET:
+ *(int *)data = zs_to_tiocm(zst);
+ break;
+
default:
- return (ENOTTY);
+ error = ENOTTY;
+ break;
}
- return (0);
+
+ splx(s);
+
+ return (error);
}
/*
* Start or restart transmission.
*/
-static void
-zsstart(tp)
- register struct tty *tp;
+void
+zsstart(struct tty *tp)
{
- register struct zstty_softc *zst;
- register struct zs_chanstate *cs;
- register int s, nch;
-
- zst = zstty_cd.cd_devs[minor(tp->t_dev)];
- cs = zst->zst_cs;
+ struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(tp->t_dev));
+ struct zs_chanstate *cs = zst->zst_cs;
+ u_char *tba;
+ int tbc, rr0;
+ int s;
s = spltty();
+ if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
+ goto out;
+ if (zst->zst_tx_stopped)
+ goto out;
- /*
- * If currently active or delaying, no need to do anything.
- */
- if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))
+ ttwakeupwr(tp);
+ if (tp->t_outq.c_cc == 0)
goto out;
- /*
- * If under CRTSCTS hfc and halted, do nothing
- */
- if (tp->t_cflag & CRTSCTS)
- if (zst->zst_tx_stopped)
- goto out;
+ /* Grab the first contiguous region of buffer space. */
+ tba = tp->t_outq.c_cf;
+ tbc = ndqb(&tp->t_outq, 0);
- /*
- * If there are sleepers, and output has drained below low
- * water mark, awaken.
- */
- ttwakeupwr(tp);
+#if IPL_ZS != IPL_TTY
+ (void)splzs();
+#endif
- nch = ndqb(&tp->t_outq, 0); /* XXX */
- (void) splzs();
+ zst->zst_tba = tba;
+ zst->zst_tbc = tbc;
+ SET(tp->t_state, TS_BUSY);
+ zst->zst_tx_busy = 1;
- if (nch) {
- register char *p = tp->t_outq.c_cf;
+ do {
+ rr0 = zs_read_csr(cs);
+ if ((rr0 & ZSRR0_TX_READY) == 0)
+ break;
+
+ zs_write_data(cs, *zst->zst_tba);
+ zst->zst_tbc--;
+ zst->zst_tba++;
+ } while (zst->zst_tbc > 0);
- /* mark busy, enable tx done interrupts, & send first byte */
- tp->t_state |= TS_BUSY;
- zst->zst_tx_busy = 1;
- cs->cs_preg[1] |= ZSWR1_TIE;
- cs->cs_creg[1] = cs->cs_preg[1];
- zs_write_reg(cs, 1, cs->cs_creg[1]);
- zs_write_data(cs, *p);
- zst->zst_tba = p + 1;
- zst->zst_tbc = nch - 1;
- } else {
- /*
- * Nothing to send, turn off transmit done interrupts.
- * This is useful if something is doing polled output.
- */
- cs->cs_preg[1] &= ~ZSWR1_TIE;
- cs->cs_creg[1] = cs->cs_preg[1];
- zs_write_reg(cs, 1, cs->cs_creg[1]);
- }
out:
splx(s);
}
@@ -634,263 +874,388 @@ out:
* Stop output, e.g., for ^S or output flush.
*/
int
-zsstop(tp, flag)
- struct tty *tp;
- int flag;
+zsstop(struct tty *tp, int flag)
{
- register struct zstty_softc *zst;
- register struct zs_chanstate *cs;
- register int s;
-
- zst = zstty_cd.cd_devs[minor(tp->t_dev)];
- cs = zst->zst_cs;
+ struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(tp->t_dev));
+ int s;
s = splzs();
- if (tp->t_state & TS_BUSY) {
- /*
- * Device is transmitting; must stop it.
- * Also clear _heldtbc to prevent any
- * flow-control event from resuming.
- */
+ if (ISSET(tp->t_state, TS_BUSY)) {
+ /* Stop transmitting at the next chunk. */
zst->zst_tbc = 0;
zst->zst_heldtbc = 0;
- if ((tp->t_state & TS_TTSTOP) == 0)
- tp->t_state |= TS_FLUSH;
+ if (!ISSET(tp->t_state, TS_TTSTOP))
+ SET(tp->t_state, TS_FLUSH);
}
splx(s);
- return (0);
+ return 0;
}
/*
* Set ZS tty parameters from termios.
* XXX - Should just copy the whole termios after
* making sure all the changes could be done.
- * XXX - Only whack the UART when params change...
*/
-static int
-zsparam(tp, t)
- register struct tty *tp;
- register struct termios *t;
+int
+zsparam(struct tty *tp, struct termios *t)
{
- register struct zstty_softc *zst;
- register struct zs_chanstate *cs;
- register int s, bps, cflag, tconst;
- u_char tmp3, tmp4, tmp5;
-
- zst = zstty_cd.cd_devs[minor(tp->t_dev)];
- cs = zst->zst_cs;
+ struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(tp->t_dev));
+ struct zs_chanstate *cs = zst->zst_cs;
+ int ospeed;
+ tcflag_t cflag;
+ uint8_t tmp3, tmp4, tmp5;
+ int s, error;
+
+ ospeed = t->c_ospeed;
+ cflag = t->c_cflag;
- /* XXX: Need to use an MD function for this. */
- bps = t->c_ospeed;
- if (bps < 0 || (t->c_ispeed && t->c_ispeed != bps))
+ /* Check requested parameters. */
+ if (ospeed < 0)
return (EINVAL);
- if (bps == 0) {
- /* stty 0 => drop DTR and RTS */
- zs_modem(zst, 0);
- return (0);
- }
- tconst = BPS_TO_TCONST(cs->cs_brg_clk, bps);
- if (tconst < 0)
+ if (t->c_ispeed && t->c_ispeed != ospeed)
return (EINVAL);
- /* Convert back to make sure we can do it. */
- bps = TCONST_TO_BPS(cs->cs_brg_clk, tconst);
- if (bps != t->c_ospeed)
- return (EINVAL);
- tp->t_ispeed = tp->t_ospeed = bps;
+ /*
+ * For the console, always force CLOCAL and !HUPCL, so that the port
+ * is always active.
+ */
+ if (ISSET(zst->zst_swflags, TIOCFLAG_SOFTCAR) ||
+ ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) {
+ SET(cflag, CLOCAL);
+ CLR(cflag, HUPCL);
+ }
- cflag = t->c_cflag;
- tp->t_cflag = cflag;
+ /*
+ * Only whack the UART when params change.
+ * Some callers need to clear tp->t_ospeed
+ * to make sure initialization gets done.
+ */
+ if (tp->t_ospeed == ospeed &&
+ tp->t_cflag == cflag)
+ return (0);
/*
- * Block interrupts so that state will not
- * be altered until we are done setting it up.
+ * Call MD functions to deal with changed
+ * clock modes or H/W flow control modes.
+ * The BRG divisor is set now. (reg 12,13)
*/
- s = splzs();
+ error = zs_set_speed(cs, ospeed);
+ if (error)
+ return (error);
+ error = zs_set_modes(cs, cflag);
+ if (error)
+ return (error);
/*
+ * Block interrupts so that state will not
+ * be altered until we are done setting it up.
+ *
* Initial values in cs_preg are set before
* our attach routine is called. The master
* interrupt enable is handled by zsc.c
+ *
*/
+ s = splzs();
- cs->cs_preg[12] = tconst;
- cs->cs_preg[13] = tconst >> 8;
-
- switch (cflag & CSIZE) {
+ /*
+ * Recalculate which status ints to enable.
+ */
+ zs_maskintr(zst);
+
+ /* Recompute character size bits. */
+ tmp3 = cs->cs_preg[3];
+ tmp5 = cs->cs_preg[5];
+ CLR(tmp3, ZSWR3_RXSIZE);
+ CLR(tmp5, ZSWR5_TXSIZE);
+ switch (ISSET(cflag, CSIZE)) {
case CS5:
- tmp3 = ZSWR3_RX_5;
- tmp5 = ZSWR5_TX_5;
+ SET(tmp3, ZSWR3_RX_5);
+ SET(tmp5, ZSWR5_TX_5);
break;
case CS6:
- tmp3 = ZSWR3_RX_6;
- tmp5 = ZSWR5_TX_6;
+ SET(tmp3, ZSWR3_RX_6);
+ SET(tmp5, ZSWR5_TX_6);
break;
case CS7:
- tmp3 = ZSWR3_RX_7;
- tmp5 = ZSWR5_TX_7;
+ SET(tmp3, ZSWR3_RX_7);
+ SET(tmp5, ZSWR5_TX_7);
break;
case CS8:
- default:
- tmp3 = ZSWR3_RX_8;
- tmp5 = ZSWR5_TX_8;
+ SET(tmp3, ZSWR3_RX_8);
+ SET(tmp5, ZSWR5_TX_8);
break;
}
-
- cs->cs_preg[3] = tmp3 | ZSWR3_RX_ENABLE;
- cs->cs_preg[5] = tmp5 | ZSWR5_TX_ENABLE | ZSWR5_DTR | ZSWR5_RTS;
-
- tmp4 = ZSWR4_CLK_X16 | (cflag & CSTOPB ? ZSWR4_TWOSB : ZSWR4_ONESB);
- if ((cflag & PARODD) == 0)
- tmp4 |= ZSWR4_EVENP;
- if (cflag & PARENB)
- tmp4 |= ZSWR4_PARENB;
- cs->cs_preg[4] = tmp4;
+ cs->cs_preg[3] = tmp3;
+ cs->cs_preg[5] = tmp5;
/*
- * Output hardware flow control on the chip is horrendous:
- * if carrier detect drops, the receiver is disabled.
- * Therefore, NEVER set the HFC bit, and instead use
- * the status interrupts to detect CTS changes.
+ * Recompute the stop bits and parity bits. Note that
+ * zs_set_speed() may have set clock selection bits etc.
+ * in wr4, so those must preserved.
*/
- if (cflag & CRTSCTS) {
- zst->zst_rbhiwat = zstty_rbuf_hiwat;
- cs->cs_preg[15] |= ZSWR15_CTS_IE;
- } else {
- zst->zst_rbhiwat = zstty_rbuf_size; /* impossible value */
- cs->cs_preg[15] &= ~ZSWR15_CTS_IE;
- }
+ tmp4 = cs->cs_preg[4];
+ CLR(tmp4, ZSWR4_SBMASK | ZSWR4_PARMASK);
+ if (ISSET(cflag, CSTOPB))
+ SET(tmp4, ZSWR4_TWOSB);
+ else
+ SET(tmp4, ZSWR4_ONESB);
+ if (!ISSET(cflag, PARODD))
+ SET(tmp4, ZSWR4_EVENP);
+ if (ISSET(cflag, PARENB))
+ SET(tmp4, ZSWR4_PARENB);
+ cs->cs_preg[4] = tmp4;
+
+ /* And copy to tty. */
+ tp->t_ispeed = 0;
+ tp->t_ospeed = ospeed;
+ tp->t_cflag = cflag;
/*
* If nothing is being transmitted, set up new current values,
* else mark them as pending.
*/
- if (cs->cs_heldchange == 0) {
+ if (!cs->cs_heldchange) {
if (zst->zst_tx_busy) {
zst->zst_heldtbc = zst->zst_tbc;
zst->zst_tbc = 0;
- cs->cs_heldchange = 0xFF; /* XXX */
- } else {
+ cs->cs_heldchange = 1;
+ } else
zs_loadchannelregs(cs);
+ }
+
+ /*
+ * If hardware flow control is disabled, turn off the buffer water
+ * marks and unblock any soft flow control state. Otherwise, enable
+ * the water marks.
+ */
+ if (!ISSET(cflag, CHWFLOW)) {
+ zst->zst_r_hiwat = 0;
+ zst->zst_r_lowat = 0;
+ if (ISSET(zst->zst_rx_flags, RX_TTY_OVERFLOWED)) {
+ CLR(zst->zst_rx_flags, RX_TTY_OVERFLOWED);
+ zst->zst_rx_ready = 1;
+ cs->cs_softreq = 1;
}
+ if (ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED|RX_IBUF_BLOCKED)) {
+ CLR(zst->zst_rx_flags, RX_TTY_BLOCKED|RX_IBUF_BLOCKED);
+ zs_hwiflow(zst);
+ }
+ } else {
+ zst->zst_r_hiwat = zstty_rbuf_hiwat;
+ zst->zst_r_lowat = zstty_rbuf_lowat;
}
+
+ /*
+ * Force a recheck of the hardware carrier and flow control status,
+ * since we may have changed which bits we're looking at.
+ */
+ zstty_stint(cs, 1);
+
splx(s);
+
+ /*
+ * If hardware flow control is disabled, unblock any hard flow control
+ * state.
+ */
+ if (!ISSET(cflag, CHWFLOW)) {
+ if (zst->zst_tx_stopped) {
+ zst->zst_tx_stopped = 0;
+ zsstart(tp);
+ }
+ }
+
+ zstty_softint(cs);
+
return (0);
}
/*
+ * Compute interrupt enable bits and set in the pending bits. Called both
+ * in zsparam() and when PPS (pulse per second timing) state changes.
+ * Must be called at splzs().
+ */
+void
+zs_maskintr(struct zstty_softc *zst)
+{
+ struct zs_chanstate *cs = zst->zst_cs;
+ uint8_t tmp15;
+
+ cs->cs_rr0_mask = cs->cs_rr0_cts | cs->cs_rr0_dcd;
+ if (zst->zst_ppsmask != 0)
+ cs->cs_rr0_mask |= cs->cs_rr0_pps;
+ tmp15 = cs->cs_preg[15];
+ if (ISSET(cs->cs_rr0_mask, ZSRR0_DCD))
+ SET(tmp15, ZSWR15_DCD_IE);
+ else
+ CLR(tmp15, ZSWR15_DCD_IE);
+ if (ISSET(cs->cs_rr0_mask, ZSRR0_CTS))
+ SET(tmp15, ZSWR15_CTS_IE);
+ else
+ CLR(tmp15, ZSWR15_CTS_IE);
+ cs->cs_preg[15] = tmp15;
+}
+
+
+/*
* Raise or lower modem control (DTR/RTS) signals. If a character is
* in transmission, the change is deferred.
+ * Called at splzs().
*/
-static void
-zs_modem(zst, onoff)
- struct zstty_softc *zst;
- int onoff;
+void
+zs_modem(struct zstty_softc *zst, int onoff)
{
- struct zs_chanstate *cs;
- struct tty *tp;
- int s, bis, and;
+ struct zs_chanstate *cs = zst->zst_cs, *ccs;
- cs = zst->zst_cs;
- tp = zst->zst_tty;
+ if (cs->cs_wr5_dtr == 0)
+ return;
- if (onoff) {
- bis = ZSWR5_DTR | ZSWR5_RTS;
- and = ~0;
- } else {
- bis = 0;
- and = ~(ZSWR5_DTR | ZSWR5_RTS);
+ ccs = (cs->cs_ctl_chan != NULL ? cs->cs_ctl_chan : cs);
+
+ if (onoff)
+ SET(ccs->cs_preg[5], cs->cs_wr5_dtr);
+ else
+ CLR(ccs->cs_preg[5], cs->cs_wr5_dtr);
+
+ if (!cs->cs_heldchange) {
+ if (zst->zst_tx_busy) {
+ zst->zst_heldtbc = zst->zst_tbc;
+ zst->zst_tbc = 0;
+ cs->cs_heldchange = 1;
+ } else
+ zs_loadchannelregs(cs);
}
- s = splzs();
- cs->cs_preg[5] = (cs->cs_preg[5] | bis) & and;
- if (cs->cs_heldchange == 0) {
+}
+
+/*
+ * Set modem bits.
+ * Called at splzs().
+ */
+void
+tiocm_to_zs(struct zstty_softc *zst, u_long how, int ttybits)
+{
+ struct zs_chanstate *cs = zst->zst_cs, *ccs;
+ uint8_t zsbits;
+
+ ccs = (cs->cs_ctl_chan != NULL ? cs->cs_ctl_chan : cs);
+
+ zsbits = 0;
+ if (ISSET(ttybits, TIOCM_DTR))
+ SET(zsbits, ZSWR5_DTR);
+ if (ISSET(ttybits, TIOCM_RTS))
+ SET(zsbits, ZSWR5_RTS);
+
+ switch (how) {
+ case TIOCMBIC:
+ CLR(ccs->cs_preg[5], zsbits);
+ break;
+
+ case TIOCMBIS:
+ SET(ccs->cs_preg[5], zsbits);
+ break;
+
+ case TIOCMSET:
+ CLR(ccs->cs_preg[5], ZSWR5_RTS | ZSWR5_DTR);
+ SET(ccs->cs_preg[5], zsbits);
+ break;
+ }
+
+ if (!cs->cs_heldchange) {
if (zst->zst_tx_busy) {
zst->zst_heldtbc = zst->zst_tbc;
zst->zst_tbc = 0;
- cs->cs_heldchange = (1<<5);
- } else {
- cs->cs_creg[5] = cs->cs_preg[5];
- zs_write_reg(cs, 5, cs->cs_creg[5]);
- }
+ cs->cs_heldchange = 1;
+ } else
+ zs_loadchannelregs(cs);
}
- splx(s);
+}
+
+/*
+ * Get modem bits.
+ * Called at splzs().
+ */
+int
+zs_to_tiocm(struct zstty_softc *zst)
+{
+ struct zs_chanstate *cs = zst->zst_cs, *ccs;
+ uint8_t zsbits;
+ int ttybits = 0;
+
+ ccs = (cs->cs_ctl_chan != NULL ? cs->cs_ctl_chan : cs);
+
+ zsbits = ccs->cs_preg[5];
+ if (ISSET(zsbits, ZSWR5_DTR))
+ SET(ttybits, TIOCM_DTR);
+ if (ISSET(zsbits, ZSWR5_RTS))
+ SET(ttybits, TIOCM_RTS);
+
+ zsbits = cs->cs_rr0;
+ if (ISSET(zsbits, ZSRR0_DCD))
+ SET(ttybits, TIOCM_CD);
+ if (ISSET(zsbits, ZSRR0_CTS))
+ SET(ttybits, TIOCM_CTS);
+
+ return (ttybits);
}
/*
* Try to block or unblock input using hardware flow-control.
* This is called by kern/tty.c if MDMBUF|CRTSCTS is set, and
* if this function returns non-zero, the TS_TBLOCK flag will
- * be set or cleared according to the "stop" arg passed.
+ * be set or cleared according to the "block" arg passed.
*/
int
-zshwiflow(tp, stop)
- struct tty *tp;
- int stop;
+zshwiflow(struct tty *tp, int block)
{
- register struct zstty_softc *zst;
+ struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(tp->t_dev));
+ struct zs_chanstate *cs = zst->zst_cs;
int s;
- zst = zstty_cd.cd_devs[minor(tp->t_dev)];
+ if (cs->cs_wr5_rts == 0)
+ return (0);
s = splzs();
- if (stop) {
- /*
- * The tty layer is asking us to block input.
- * If we already did it, just return TRUE.
- */
- if (zst->zst_rx_blocked)
- goto out;
- zst->zst_rx_blocked = 1;
+ if (block) {
+ if (!ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED)) {
+ SET(zst->zst_rx_flags, RX_TTY_BLOCKED);
+ zs_hwiflow(zst);
+ }
} else {
- /*
- * The tty layer is asking us to resume input.
- * The input ring is always empty by now.
- */
- zst->zst_rx_blocked = 0;
+ if (ISSET(zst->zst_rx_flags, RX_TTY_OVERFLOWED)) {
+ CLR(zst->zst_rx_flags, RX_TTY_OVERFLOWED);
+ zst->zst_rx_ready = 1;
+ cs->cs_softreq = 1;
+ }
+ if (ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED)) {
+ CLR(zst->zst_rx_flags, RX_TTY_BLOCKED);
+ zs_hwiflow(zst);
+ }
}
- zs_hwiflow(zst, stop);
- out:
splx(s);
- return 1;
+ return (1);
}
/*
* Internal version of zshwiflow
- * called at splzs
+ * Called at splzs()
*/
-static void
-zs_hwiflow(zst, stop)
- register struct zstty_softc *zst;
- int stop;
+void
+zs_hwiflow(struct zstty_softc *zst)
{
- register struct zs_chanstate *cs;
- register struct tty *tp;
- register int bis, and;
+ struct zs_chanstate *cs = zst->zst_cs, *ccs;
- cs = zst->zst_cs;
- tp = zst->zst_tty;
+ if (cs->cs_wr5_rts == 0)
+ return;
- if (stop) {
- /* Block input (Lower RTS) */
- bis = 0;
- and = ~ZSWR5_RTS;
- } else {
- /* Unblock input (Raise RTS) */
- bis = ZSWR5_RTS;
- and = ~0;
- }
+ ccs = (cs->cs_ctl_chan != NULL ? cs->cs_ctl_chan : cs);
- cs->cs_preg[5] = (cs->cs_preg[5] | bis) & and;
- if (cs->cs_heldchange == 0) {
- if (zst->zst_tx_busy) {
- zst->zst_heldtbc = zst->zst_tbc;
- zst->zst_tbc = 0;
- cs->cs_heldchange = (1<<5);
- } else {
- cs->cs_creg[5] = cs->cs_preg[5];
- zs_write_reg(cs, 5, cs->cs_creg[5]);
- }
+ if (ISSET(zst->zst_rx_flags, RX_ANY_BLOCK)) {
+ CLR(ccs->cs_preg[5], cs->cs_wr5_rts);
+ CLR(ccs->cs_creg[5], cs->cs_wr5_rts);
+ } else {
+ SET(ccs->cs_preg[5], cs->cs_wr5_rts);
+ SET(ccs->cs_creg[5], cs->cs_wr5_rts);
}
+ zs_write_reg(ccs, 5, ccs->cs_creg[5]);
}
@@ -898,140 +1263,146 @@ zs_hwiflow(zst, stop)
* Interface to the lower layer (zscc)
****************************************************************/
+void zstty_rxsoft(struct zstty_softc *, struct tty *);
+void zstty_txsoft(struct zstty_softc *, struct tty *);
+void zstty_stsoft(struct zstty_softc *, struct tty *);
+void zstty_diag(void *);
/*
- * receiver ready interrupt.
- * called at splzs
+ * Receiver Ready interrupt.
+ * Called at splzs().
*/
-static void
-zstty_rxint(cs)
- register struct zs_chanstate *cs;
+void
+zstty_rxint(struct zs_chanstate *cs)
{
- register struct zstty_softc *zst;
- register int cc, put, put_next, ringmask;
- register u_char c, rr0, rr1;
- register u_short ch_rr1;
+ struct zstty_softc *zst = cs->cs_private;
+ uint8_t *put, *end;
+ u_int cc;
+ uint8_t rr0, rr1, c;
- zst = cs->cs_private;
+ end = zst->zst_ebuf;
put = zst->zst_rbput;
- ringmask = zst->zst_ringmask;
-
-nextchar:
-
- /*
- * First read the status, because reading the received char
- * destroys the status of this char.
- */
- rr1 = zs_read_reg(cs, 1);
- c = zs_read_data(cs);
- ch_rr1 = (c << 8) | rr1;
+ cc = zst->zst_rbavail;
- if (ch_rr1 & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) {
- /* Clear the receive error. */
- zs_write_csr(cs, ZSWR0_RESET_ERRORS);
- }
+ while (cc > 0) {
+ /*
+ * First read the status, because reading the received char
+ * destroys the status of this char.
+ */
+ rr1 = zs_read_reg(cs, 1);
+ c = zs_read_data(cs);
- /* XXX: Check for the stop character? */
+ if (ISSET(rr1, ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) {
+ /* Clear the receive error. */
+ zs_write_csr(cs, ZSWR0_RESET_ERRORS);
+ }
- zst->zst_rbuf[put] = ch_rr1;
- put_next = (put + 1) & ringmask;
+ put[0] = c;
+ put[1] = rr1;
+ put += 2;
+ if (put >= end)
+ put = zst->zst_rbuf;
+ cc--;
- /* Would overrun if increment makes (put==get). */
- if (put_next == zst->zst_rbget) {
- zst->zst_rx_overrun = 1;
- } else {
- /* OK, really increment. */
- put = put_next;
+ rr0 = zs_read_csr(cs);
+ if (!ISSET(rr0, ZSRR0_RX_READY))
+ break;
}
- /* Keep reading until the FIFO is empty. */
- rr0 = zs_read_csr(cs);
- if (rr0 & ZSRR0_RX_READY)
- goto nextchar;
-
- /* Done reading. */
+ /*
+ * Current string of incoming characters ended because
+ * 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.
+ */
zst->zst_rbput = put;
+ zst->zst_rbavail = cc;
+ if (!ISSET(zst->zst_rx_flags, RX_TTY_OVERFLOWED)) {
+ zst->zst_rx_ready = 1;
+ cs->cs_softreq = 1;
+ }
/*
- * If ring is getting too full, try to block input.
+ * See if we are in danger of overflowing a buffer. If
+ * so, use hardware flow control to ease the pressure.
*/
- cc = put - zst->zst_rbget;
- if (cc < 0)
- cc += zstty_rbuf_size;
- if ((cc > zst->zst_rbhiwat) && (zst->zst_rx_blocked == 0)) {
- zst->zst_rx_blocked = 1;
- zs_hwiflow(zst, 1);
+ if (!ISSET(zst->zst_rx_flags, RX_IBUF_BLOCKED) &&
+ cc < zst->zst_r_hiwat) {
+ SET(zst->zst_rx_flags, RX_IBUF_BLOCKED);
+ zs_hwiflow(zst);
}
- /* Ask for softint() call. */
- cs->cs_softreq = 1;
+ /*
+ * If we're out of space, disable receive interrupts
+ * until the queue has drained a bit.
+ */
+ if (!cc) {
+ SET(zst->zst_rx_flags, RX_IBUF_OVERFLOWED);
+ CLR(cs->cs_preg[1], ZSWR1_RIE);
+ cs->cs_creg[1] = cs->cs_preg[1];
+ zs_write_reg(cs, 1, cs->cs_creg[1]);
+ }
}
/*
- * transmitter ready interrupt. (splzs)
+ * Transmitter Ready interrupt.
+ * Called at splzs().
*/
-static void
-zstty_txint(cs)
- register struct zs_chanstate *cs;
+void
+zstty_txint(struct zs_chanstate *cs)
{
- register struct zstty_softc *zst;
- register int count;
+ struct zstty_softc *zst = cs->cs_private;
+ int rr0;
- zst = cs->cs_private;
+ zs_write_csr(cs, ZSWR0_RESET_TXINT);
/*
- * If we suspended output for a "held" change,
- * then handle that now and resume.
- * Do flow-control changes ASAP.
- * When the only change is for flow control,
- * avoid hitting other registers, because that
- * often makes the stupid zs drop input...
+ * If we've delayed a parameter change, do it now, and restart
+ * output.
*/
if (cs->cs_heldchange) {
- if (cs->cs_heldchange == (1<<5)) {
- /* Avoid whacking the chip... */
- cs->cs_creg[5] = cs->cs_preg[5];
- zs_write_reg(cs, 5, cs->cs_creg[5]);
- } else
- zs_loadchannelregs(cs);
+ zs_loadchannelregs(cs);
cs->cs_heldchange = 0;
- count = zst->zst_heldtbc;
- } else
- count = zst->zst_tbc;
+ zst->zst_tbc = zst->zst_heldtbc;
+ zst->zst_heldtbc = 0;
+ }
+
+ while (zst->zst_tbc > 0) {
+ rr0 = zs_read_csr(cs);
+ if ((rr0 & ZSRR0_TX_READY) == 0)
+ break;
- /*
- * If our transmit buffer still has data,
- * just send the next character.
- */
- if (count > 0) {
- /* Send the next char. */
- zst->zst_tbc = --count;
zs_write_data(cs, *zst->zst_tba);
+ zst->zst_tbc--;
zst->zst_tba++;
- return;
}
- zs_write_csr(cs, ZSWR0_RESET_TXINT);
-
- /* Ask the softint routine for more output. */
- zst->zst_tx_busy = 0;
- zst->zst_tx_done = 1;
- cs->cs_softreq = 1;
+ if (zst->zst_tbc == 0) {
+ if (zst->zst_tx_busy) {
+ zst->zst_tx_busy = 0;
+ zst->zst_tx_done = 1;
+ cs->cs_softreq = 1;
+ }
+ }
}
+#ifdef DDB
+#include <ddb/db_var.h>
+#define DB_CONSOLE db_console
+#else
+#define DB_CONSOLE 0
+#endif
+
/*
- * status change interrupt. (splzs)
+ * Status Change interrupt.
+ * Called at splzs().
*/
-static void
-zstty_stint(cs)
- register struct zs_chanstate *cs;
+void
+zstty_stint(struct zs_chanstate *cs, int force)
{
- register struct zstty_softc *zst;
- register struct tty *tp;
- register u_char rr0;
-
- zst = cs->cs_private;
- tp = zst->zst_tty;
+ struct zstty_softc *zst = cs->cs_private;
+ struct tty *tp = zst->zst_tty;
+ uint8_t rr0, delta;
rr0 = zs_read_csr(cs);
zs_write_csr(cs, ZSWR0_RESET_STATUS);
@@ -1040,59 +1411,199 @@ zstty_stint(cs)
* Check here for console break, so that we can abort
* even when interrupts are locking up the machine.
*/
- if ((rr0 & ZSRR0_BREAK) &&
- (zst->zst_hwflags & ZS_HWFLAG_CONSOLE))
- {
- zs_abort();
- return;
- }
+ if ((zst->zst_hwflags & ZS_HWFLAG_CONSOLE_INPUT) &&
+ ISSET(rr0, ZSRR0_BREAK) && DB_CONSOLE)
+ zs_abort(cs);
- /*
- * Need to handle CTS output flow control here.
- * Output remains stopped as long as either the
- * zst_tx_stopped or TS_TTSTOP flag is set.
- * Never restart here; the softint routine will
- * do that after things are ready to move.
- */
- if (((rr0 & ZSRR0_CTS) == 0) && (tp->t_cflag & CRTSCTS)) {
- zst->zst_tbc = 0;
- zst->zst_heldtbc = 0;
- zst->zst_tx_stopped = 1;
- }
-
- /*
- * We have to accumulate status line changes here.
- * Otherwise, if we get multiple status interrupts
- * before the softint runs, we could fail to notice
- * some status line changes in the softint routine.
- * Fix from Bill Studenmund, October 1996.
- */
- cs->cs_rr0_delta |= (cs->cs_rr0 ^ rr0);
+ if (!force)
+ delta = rr0 ^ cs->cs_rr0;
+ else
+ delta = cs->cs_rr0_mask;
ttytstamp(tp, cs->cs_rr0 & ZSRR0_CTS, rr0 & ZSRR0_CTS,
cs->cs_rr0 & ZSRR0_DCD, rr0 & ZSRR0_DCD);
cs->cs_rr0 = rr0;
- zst->zst_st_check = 1;
- /* Ask for softint() call. */
- cs->cs_softreq = 1;
+ if (ISSET(delta, cs->cs_rr0_mask)) {
+ SET(cs->cs_rr0_delta, delta);
+
+ /*
+ * Stop output immediately if we lose the output
+ * flow control signal or carrier detect.
+ */
+ if (ISSET(~rr0, cs->cs_rr0_mask)) {
+ zst->zst_tbc = 0;
+ zst->zst_heldtbc = 0;
+ }
+
+ zst->zst_st_check = 1;
+ cs->cs_softreq = 1;
+ }
}
-/*
- * Print out a ring or fifo overrun error message.
- */
-static void
-zsoverrun(zst, ptime, what)
- struct zstty_softc *zst;
- long *ptime;
- char *what;
+void
+zstty_diag(void *arg)
{
+ struct zstty_softc *zst = arg;
+ int overflows, floods;
+ int s;
- if (*ptime != time_second) {
- *ptime = time_second;
- log(LOG_WARNING, "%s: %s overrun\n",
- zst->zst_dev.dv_xname, what);
+ s = splzs();
+ overflows = zst->zst_overflows;
+ zst->zst_overflows = 0;
+ floods = zst->zst_floods;
+ zst->zst_floods = 0;
+ zst->zst_errors = 0;
+ splx(s);
+
+ log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf flood%s\n",
+ zst->zst_dev.dv_xname,
+ overflows, overflows == 1 ? "" : "s",
+ floods, floods == 1 ? "" : "s");
+}
+
+void
+zstty_rxsoft(struct zstty_softc *zst, struct tty *tp)
+{
+ struct zs_chanstate *cs = zst->zst_cs;
+ int (*rint)(int, struct tty *) = linesw[tp->t_line].l_rint;
+ uint8_t *get, *end;
+ u_int cc, scc;
+ uint8_t rr1;
+ int code;
+ int s;
+
+ end = zst->zst_ebuf;
+ get = zst->zst_rbget;
+ scc = cc = zstty_rbuf_size - zst->zst_rbavail;
+
+ if (cc == zstty_rbuf_size) {
+ zst->zst_floods++;
+ if (zst->zst_errors++ == 0)
+ timeout_add_sec(&zst->zst_diag_ch, 60);
+ }
+
+ /* If not yet open, drop the entire buffer content here */
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+ get += cc << 1;
+ if (get >= end)
+ get -= zstty_rbuf_size << 1;
+ cc = 0;
+ }
+ while (cc) {
+ code = get[0];
+ rr1 = get[1];
+ if (ISSET(rr1, ZSRR1_DO | ZSRR1_FE | ZSRR1_PE)) {
+ if (ISSET(rr1, ZSRR1_DO)) {
+ zst->zst_overflows++;
+ if (zst->zst_errors++ == 0)
+ timeout_add_sec(&zst->zst_diag_ch, 60);
+ }
+ if (ISSET(rr1, ZSRR1_FE))
+ SET(code, TTY_FE);
+ if (ISSET(rr1, ZSRR1_PE))
+ SET(code, TTY_PE);
+ }
+ if ((*rint)(code, tp) == -1) {
+ /*
+ * The line discipline's buffer is out of space.
+ */
+ if (!ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED)) {
+ /*
+ * We're either not using flow control, or the
+ * line discipline didn't tell us to block for
+ * some reason. Either way, we have no way to
+ * know when there's more space available, so
+ * just drop the rest of the data.
+ */
+ get += cc << 1;
+ if (get >= end)
+ get -= zstty_rbuf_size << 1;
+ cc = 0;
+ } else {
+ /*
+ * Don't schedule any more receive processing
+ * until the line discipline tells us there's
+ * space available (through comhwiflow()).
+ * Leave the rest of the data in the input
+ * buffer.
+ */
+ SET(zst->zst_rx_flags, RX_TTY_OVERFLOWED);
+ }
+ break;
+ }
+ get += 2;
+ if (get >= end)
+ get = zst->zst_rbuf;
+ cc--;
+ }
+
+ if (cc != scc) {
+ zst->zst_rbget = get;
+ s = splzs();
+ cc = zst->zst_rbavail += scc - cc;
+ /* Buffers should be ok again, release possible block. */
+ if (cc >= zst->zst_r_lowat) {
+ if (ISSET(zst->zst_rx_flags, RX_IBUF_OVERFLOWED)) {
+ CLR(zst->zst_rx_flags, RX_IBUF_OVERFLOWED);
+ SET(cs->cs_preg[1], ZSWR1_RIE);
+ cs->cs_creg[1] = cs->cs_preg[1];
+ zs_write_reg(cs, 1, cs->cs_creg[1]);
+ }
+ if (ISSET(zst->zst_rx_flags, RX_IBUF_BLOCKED)) {
+ CLR(zst->zst_rx_flags, RX_IBUF_BLOCKED);
+ zs_hwiflow(zst);
+ }
+ }
+ splx(s);
+ }
+}
+
+void
+zstty_txsoft(struct zstty_softc *zst, struct tty *tp)
+{
+ int s;
+
+ CLR(tp->t_state, TS_BUSY);
+ if (ISSET(tp->t_state, TS_FLUSH))
+ CLR(tp->t_state, TS_FLUSH);
+ else {
+ s = splzs();
+ ndflush(&tp->t_outq, (int)(zst->zst_tba - tp->t_outq.c_cf));
+ splx(s);
+ }
+ (*linesw[tp->t_line].l_start)(tp);
+}
+
+void
+zstty_stsoft(struct zstty_softc *zst, struct tty *tp)
+{
+ struct zs_chanstate *cs = zst->zst_cs;
+ uint8_t rr0, delta;
+ int s;
+
+ s = splzs();
+ rr0 = cs->cs_rr0;
+ delta = cs->cs_rr0_delta;
+ cs->cs_rr0_delta = 0;
+ splx(s);
+
+ if (ISSET(delta, cs->cs_rr0_dcd)) {
+ /*
+ * Inform the tty layer that carrier detect changed.
+ */
+ (void)(*linesw[tp->t_line].l_modem)(tp, ISSET(rr0, ZSRR0_DCD));
+ }
+
+ if (ISSET(delta, cs->cs_rr0_cts)) {
+ /* Block or unblock output according to flow control. */
+ if (ISSET(rr0, cs->cs_rr0_cts)) {
+ zst->zst_tx_stopped = 0;
+ (*linesw[tp->t_line].l_start)(tp);
+ } else {
+ zst->zst_tx_stopped = 1;
+ }
}
}
@@ -1108,115 +1619,28 @@ zsoverrun(zst, ptime, what)
* Note: an "input blockage" condition is assumed to exist if
* EITHER the TS_TBLOCK flag or zst_rx_blocked flag is set.
*/
-static void
-zstty_softint(cs)
- struct zs_chanstate *cs;
+void
+zstty_softint(struct zs_chanstate *cs)
{
- register struct zstty_softc *zst;
- register struct linesw *line;
- register struct tty *tp;
- register int get, c, s;
- int ringmask, overrun;
- register u_short ring_data;
- register u_char rr0, delta;
-
- zst = cs->cs_private;
- tp = zst->zst_tty;
- line = &linesw[tp->t_line];
- ringmask = zst->zst_ringmask;
- overrun = 0;
+ struct zstty_softc *zst = cs->cs_private;
+ struct tty *tp = zst->zst_tty;
+ int s;
- /*
- * Raise to tty priority while servicing the ring.
- */
s = spltty();
- if (zst->zst_rx_overrun) {
- zst->zst_rx_overrun = 0;
- zsoverrun(zst, &zst->zst_rotime, "ring");
+ if (zst->zst_rx_ready) {
+ zst->zst_rx_ready = 0;
+ zstty_rxsoft(zst, tp);
}
- /*
- * Copy data from the receive ring into the tty layer.
- */
- get = zst->zst_rbget;
- while (get != zst->zst_rbput) {
- ring_data = zst->zst_rbuf[get];
- get = (get + 1) & ringmask;
-
- if (ring_data & ZSRR1_DO)
- overrun++;
- /* low byte of ring_data is rr1 */
- c = (ring_data >> 8) & 0xff;
- if (ring_data & ZSRR1_FE)
- c |= TTY_FE;
- if (ring_data & ZSRR1_PE)
- c |= TTY_PE;
-
- line->l_rint(c, tp);
- }
- zst->zst_rbget = get;
-
- /*
- * If the overrun flag is set now, it was set while
- * copying char/status pairs from the ring, which
- * means this was a hardware (fifo) overrun.
- */
- if (overrun) {
- zsoverrun(zst, &zst->zst_fotime, "fifo");
- }
-
- /*
- * We have emptied the input ring. Maybe unblock input.
- * Note: an "input blockage" condition is assumed to exist
- * when EITHER zst_rx_blocked or the TS_TBLOCK flag is set,
- * so unblock here ONLY if TS_TBLOCK has not been set.
- */
- if (zst->zst_rx_blocked && ((tp->t_state & TS_TBLOCK) == 0)) {
- (void) splzs();
- zst->zst_rx_blocked = 0;
- zs_hwiflow(zst, 0); /* unblock input */
- (void) spltty();
- }
-
- /*
- * Do any deferred work for status interrupts.
- * The rr0 was saved in the h/w interrupt to
- * avoid another splzs in here.
- */
if (zst->zst_st_check) {
zst->zst_st_check = 0;
-
- rr0 = cs->cs_rr0;
- delta = cs->cs_rr0_delta;
- cs->cs_rr0_delta = 0;
- if (delta & ZSRR0_DCD) {
- c = ((rr0 & ZSRR0_DCD) != 0);
- if (line->l_modem(tp, c) == 0)
- zs_modem(zst, c);
- }
- if ((delta & ZSRR0_CTS) && (tp->t_cflag & CRTSCTS)) {
- /*
- * Only do restart here. Stop is handled
- * at the h/w interrupt level.
- */
- if (rr0 & ZSRR0_CTS) {
- zst->zst_tx_stopped = 0;
- tp->t_state &= ~TS_TTSTOP;
- (*line->l_start)(tp);
- }
- }
+ zstty_stsoft(zst, tp);
}
if (zst->zst_tx_done) {
zst->zst_tx_done = 0;
- tp->t_state &= ~TS_BUSY;
- if (tp->t_state & TS_FLUSH)
- tp->t_state &= ~TS_FLUSH;
- else
- ndflush(&tp->t_outq, zst->zst_tba -
- (caddr_t) tp->t_outq.c_cf);
- line->l_start(tp);
+ zstty_txsoft(zst, tp);
}
splx(s);
@@ -1228,4 +1652,3 @@ struct zsops zsops_tty = {
zstty_txint, /* xmit buffer empty */
zstty_softint, /* process software interrupt */
};
-