From Ache
[unix-history] / sys / i386 / isa / sio.c
index 9b2ddfe..a54b7bb 100644 (file)
@@ -31,7 +31,7 @@
  * SUCH DAMAGE.
  *
  *     from: @(#)com.c 7.5 (Berkeley) 5/16/91
  * SUCH DAMAGE.
  *
  *     from: @(#)com.c 7.5 (Berkeley) 5/16/91
- *     $Id: sio.c,v 1.21 1994/01/02 10:17:29 ache Exp $
+ *     $Id: sio.c,v 1.52 1994/05/31 18:18:46 ache Exp $
  */
 
 #include "sio.h"
  */
 
 #include "sio.h"
 #include "proc.h"
 #include "user.h"
 #include "conf.h"
 #include "proc.h"
 #include "user.h"
 #include "conf.h"
+#include "dkstat.h"
 #include "file.h"
 #include "uio.h"
 #include "kernel.h"
 #include "file.h"
 #include "uio.h"
 #include "kernel.h"
+#include "malloc.h"
 #include "syslog.h"
 
 #include "syslog.h"
 
+#include "i386/isa/icu.h"      /* XXX */
 #include "i386/isa/isa.h"
 #include "i386/isa/isa_device.h"
 #include "i386/isa/comreg.h"
 #include "i386/isa/ic/ns16550.h"
 
 #include "i386/isa/isa.h"
 #include "i386/isa/isa_device.h"
 #include "i386/isa/comreg.h"
 #include "i386/isa/ic/ns16550.h"
 
-#define FAKE_DCD(unit) ((unit) == comconsole)
 #define        LOTS_OF_EVENTS  64      /* helps separate urgent events from input */
 #define        RB_I_HIGH_WATER (RBSZ - 2 * RS_IBUFSIZE)
 #define        RB_I_LOW_WATER  ((RBSZ - 2 * RS_IBUFSIZE) * 7 / 8)
 #define        LOTS_OF_EVENTS  64      /* helps separate urgent events from input */
 #define        RB_I_HIGH_WATER (RBSZ - 2 * RS_IBUFSIZE)
 #define        RB_I_LOW_WATER  ((RBSZ - 2 * RS_IBUFSIZE) * 7 / 8)
 #define        TTY_BI          TTY_FE          /* XXX */
 #define        TTY_OE          TTY_PE          /* XXX */
 
 #define        TTY_BI          TTY_FE          /* XXX */
 #define        TTY_OE          TTY_PE          /* XXX */
 
-#ifndef COM_BIDIR
-#define        UNIT(x)         (minor(x))      /* XXX */
-#else /* COM_BIDIR */
-#define COM_UNITMASK    0x7f
-#define COM_CALLOUTMASK 0x80
-#define UNIT(x)         (minor(x) & COM_UNITMASK)
-#define CALLOUT(x)      (minor(x) & COM_CALLOUTMASK)
-#endif /* COM_BIDIR */
+#define        CALLOUT_MASK            0x80
+#define        CONTROL_MASK            0x60
+#define        CONTROL_INIT_STATE      0x20
+#define        CONTROL_LOCK_STATE      0x40
+#define        DEV_TO_UNIT(dev)        (MINOR_TO_UNIT(minor(dev)))
+#define        MINOR_MAGIC_MASK        (CALLOUT_MASK | CONTROL_MASK)
+#define        MINOR_TO_UNIT(mynor)    ((mynor) & ~MINOR_MAGIC_MASK)
 
 #ifdef COM_MULTIPORT
 /* checks in flags for multiport and which is multiport "master chip"
  * for a given card
  */
 
 #ifdef COM_MULTIPORT
 /* checks in flags for multiport and which is multiport "master chip"
  * for a given card
  */
-#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01)
-#define COM_MPMASTER(dev)    (((dev)->id_flags >> 8) & 0x0ff)
+#define        COM_ISMULTIPORT(dev)    ((dev)->id_flags & 0x01)
+#define        COM_MPMASTER(dev)       (((dev)->id_flags >> 8) & 0x0ff)
+#define COM_NOMASTER(dev)    ((dev)->id_flags & 0x04)
 #endif /* COM_MULTIPORT */
 
 #endif /* COM_MULTIPORT */
 
-#ifndef        FIFO_TRIGGER
-/*
- * This driver is fast enough to work with any value and for high values
- * to be only slightly more efficient.  Low values may be better because
- * they give lower latency.
- * TODO: always use low values for low speeds.  Mouse movements are jerky
- * if more than one packet arrives at once.  The low speeds used for
- * serial mice help avoid this, but not if (large) fifos are enabled.
- */
-#define FIFO_TRIGGER   FIFO_TRIGGER_14
-#endif
+#define        COM_NOFIFO(dev) ((dev)->id_flags & 0x02)
+#define        COM_QUIET(dev)  ((dev)->id_flags & 0x80)
 
 #define        com_scr         7       /* scratch register for 16450-16550 (R/W) */
 
 #define        com_scr         7       /* scratch register for 16450-16550 (R/W) */
-#define        schedsoftcom()  (ipending |= 1 << (16 + 4))     /* XXX */
 
 /*
  * Input buffer watermarks.
 
 /*
  * Input buffer watermarks.
  *     CS_CTS_OFLOW    = CCTS_OFLOW (maintained by comparam())
  *     CS_RTS_IFLOW    = CRTS_IFLOW (maintained by comparam())
  * TS_FLUSH is not used.
  *     CS_CTS_OFLOW    = CCTS_OFLOW (maintained by comparam())
  *     CS_RTS_IFLOW    = CRTS_IFLOW (maintained by comparam())
  * TS_FLUSH is not used.
- * Bug: I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
+ * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
+ * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state).
  */
 #define        CS_BUSY         0x80    /* output in progress */
 #define        CS_TTGO         0x40    /* output not stopped by XOFF */
 #define        CS_ODEVREADY    0x20    /* external device h/w ready (CTS) */
 #define        CS_CHECKMSR     1       /* check of MSR scheduled */
 #define        CS_CTS_OFLOW    2       /* use CTS output flow control */
  */
 #define        CS_BUSY         0x80    /* output in progress */
 #define        CS_TTGO         0x40    /* output not stopped by XOFF */
 #define        CS_ODEVREADY    0x20    /* external device h/w ready (CTS) */
 #define        CS_CHECKMSR     1       /* check of MSR scheduled */
 #define        CS_CTS_OFLOW    2       /* use CTS output flow control */
+#define        CS_DTR_OFF      0x10    /* DTR held off */
 #define        CS_ODONE        4       /* output completed */
 #define        CS_RTS_IFLOW    8       /* use RTS input flow control */
 
 #define        CS_ODONE        4       /* output completed */
 #define        CS_RTS_IFLOW    8       /* use RTS input flow control */
 
-static char    *error_desc[] = {
+static char const * const      error_desc[] = {
 #define        CE_OVERRUN                      0
        "silo overflow",
 #define        CE_INTERRUPT_BUF_OVERFLOW       1
 #define        CE_OVERRUN                      0
        "silo overflow",
 #define        CE_INTERRUPT_BUF_OVERFLOW       1
@@ -147,25 +141,24 @@ static    char    *error_desc[] = {
 
 /* types.  XXX - should be elsewhere */
 typedef u_int  Port_t;         /* hardware port */
 
 /* types.  XXX - should be elsewhere */
 typedef u_int  Port_t;         /* hardware port */
-typedef int    Bool_t;         /* promoted boolean */
 typedef u_char bool_t;         /* boolean */
 
 /* com device structure */
 struct com_s {
        u_char  state;          /* miscellaneous flag bits */
 typedef u_char bool_t;         /* boolean */
 
 /* com device structure */
 struct com_s {
        u_char  state;          /* miscellaneous flag bits */
+       bool_t  active_out;     /* nonzero if the callout device is open */
        u_char  cfcr_image;     /* copy of value written to CFCR */
        u_char  cfcr_image;     /* copy of value written to CFCR */
+       u_char  ftl;            /* current rx fifo trigger level */
+       u_char  ftl_init;       /* ftl_max for next open() */
+       u_char  ftl_max;        /* maximum ftl for curent open() */
        bool_t  hasfifo;        /* nonzero for 16550 UARTs */
        u_char  mcr_image;      /* copy of value written to MCR */
        bool_t  hasfifo;        /* nonzero for 16550 UARTs */
        u_char  mcr_image;      /* copy of value written to MCR */
-#ifdef COM_BIDIR
-       bool_t  bidir;          /* is this unit bidirectional? */
-       bool_t  active;         /* is the port active _at all_? */
-       bool_t  active_in;      /* is the incoming port in use? */
-       bool_t  active_out;     /* is the outgoing port in use? */
-#endif /* COM_BIDIR */
 #ifdef COM_MULTIPORT
        bool_t  multiport;      /* is this unit part of a multiport device? */
 #endif /* COM_MULTIPORT */
 #ifdef COM_MULTIPORT
        bool_t  multiport;      /* is this unit part of a multiport device? */
 #endif /* COM_MULTIPORT */
-       int     dtr_wait;       /* time to hold DTR down on close (* 1/HZ) */
+       int     dtr_wait;       /* time to hold DTR down on close (* 1/hz) */
+       u_int   tx_fifo_size;
+       u_int   wopeners;       /* # processes waiting for DCD in open() */
 
        /*
         * The high level of the driver never reads status registers directly
 
        /*
         * The high level of the driver never reads status registers directly
@@ -176,6 +169,7 @@ struct com_s {
        u_char  last_modem_status;      /* last MSR read by intr handler */
        u_char  prev_modem_status;      /* last MSR handled by high level */
 
        u_char  last_modem_status;      /* last MSR read by intr handler */
        u_char  prev_modem_status;      /* last MSR handled by high level */
 
+       u_char  hotchar;        /* ldisc-specific char to be handled ASAP */
        u_char  *ibuf;          /* start of input buffer */
        u_char  *ibufend;       /* end of input buffer */
        u_char  *ihighwater;    /* threshold in input buffer */
        u_char  *ibuf;          /* start of input buffer */
        u_char  *ibufend;       /* end of input buffer */
        u_char  *ihighwater;    /* threshold in input buffer */
@@ -194,6 +188,19 @@ struct com_s {
 
        struct tty      *tp;    /* cross reference */
 
 
        struct tty      *tp;    /* cross reference */
 
+       /* Initial state. */
+       struct termios  it_in;  /* should be in struct tty */
+       struct termios  it_out;
+
+       /* Lock state. */
+       struct termios  lt_in;  /* should be in struct tty */
+       struct termios  lt_out;
+
+#ifdef TIOCTIMESTAMP
+       bool_t  do_timestamp;
+       struct timeval  timestamp;
+#endif
+
        u_long  bytes_in;       /* statistics */
        u_long  bytes_out;
        u_int   delta_error_counts[CE_NTYPES];
        u_long  bytes_in;       /* statistics */
        u_long  bytes_out;
        u_int   delta_error_counts[CE_NTYPES];
@@ -209,50 +216,56 @@ struct com_s {
 };
 
 /*
 };
 
 /*
- * These functions in the com module ought to be declared (with a prototype)
- * in a com-driver system header.  The void ones may need to be int to match
- * ancient devswitch declarations, but they don't actually return anything.
+ * The public functions in the com module ought to be declared in a com-driver
+ * system header.
  */
 #define        Dev_t   int             /* promoted dev_t */
  */
 #define        Dev_t   int             /* promoted dev_t */
-struct consdev;
 
 
+/* Interrupt handling entry points. */
+void   siointr         __P((int unit));
+void   siopoll         __P((void));
+
+/* Device switch entry points. */
+int    sioopen         __P((Dev_t dev, int oflags, int devtype,
+                            struct proc *p));
 int    sioclose        __P((Dev_t dev, int fflag, int devtype,
                             struct proc *p));
 int    sioclose        __P((Dev_t dev, int fflag, int devtype,
                             struct proc *p));
-void   siointr         __P((int unit));
+int    sioread         __P((Dev_t dev, struct uio *uio, int ioflag));
+int    siowrite        __P((Dev_t dev, struct uio *uio, int ioflag));
 int    sioioctl        __P((Dev_t dev, int cmd, caddr_t data,
                             int fflag, struct proc *p));
 int    sioioctl        __P((Dev_t dev, int cmd, caddr_t data,
                             int fflag, struct proc *p));
+void   siostop         __P((struct tty *tp, int rw));
+#define        sioreset        noreset
+int    sioselect       __P((Dev_t dev, int rw, struct proc *p));
+#define        siommap         nommap
+#define        siostrategy     nostrategy
+
+/* Console device entry points. */
 int    siocngetc       __P((Dev_t dev));
 int    siocngetc       __P((Dev_t dev));
+struct consdev;
 void   siocninit       __P((struct consdev *cp));
 void   siocnprobe      __P((struct consdev *cp));
 void   siocnputc       __P((Dev_t dev, int c));
 void   siocninit       __P((struct consdev *cp));
 void   siocnprobe      __P((struct consdev *cp));
 void   siocnputc       __P((Dev_t dev, int c));
-int    sioopen         __P((Dev_t dev, int oflags, int devtype,
-                            struct proc *p));
-int    sioread         __P((Dev_t dev, struct uio *uio, int ioflag));
-int    sioselect       __P((Dev_t dev, int rw, struct proc *p));
-void   siostop         __P((struct tty *tp, int rw));
-int    siowrite        __P((Dev_t dev, struct uio *uio, int ioflag));
-void   softsio1        __P((void));
 
 static int     sioattach       __P((struct isa_device *dev));
 
 static int     sioattach       __P((struct isa_device *dev));
+static void    siodtrwakeup    __P((caddr_t chan, int ticks));
 static void    comflush        __P((struct com_s *com));
 static void    comhardclose    __P((struct com_s *com));
 static void    comflush        __P((struct com_s *com));
 static void    comhardclose    __P((struct com_s *com));
-static void    cominit         __P((int unit, int rate));
-#ifdef COM_MULTIPORT
-static  bool_t  comintr1        __P((struct com_s *com));
-#endif /* COM_MULTIPORT */
+static void    siointr1        __P((struct com_s *com));
 static void    commctl         __P((struct com_s *com, int bits, int how));
 static int     comparam        __P((struct tty *tp, struct termios *t));
 static int     sioprobe        __P((struct isa_device *dev));
 static void    commctl         __P((struct com_s *com, int bits, int how));
 static int     comparam        __P((struct tty *tp, struct termios *t));
 static int     sioprobe        __P((struct isa_device *dev));
-static void    compoll         __P((void));
-static  void    comstart        __P((struct tty *tp));
-static  void    comwakeup       __P((caddr_t chan, int ticks));
+static void    comstart        __P((struct tty *tp));
+static void    comwakeup       __P((caddr_t chan, int ticks));
 static int     tiocm_xxx2mcr   __P((int tiocm_xxx));
 
 /* table and macro for fast conversion from a unit number to its com struct */
 static struct com_s    *p_com_addr[NSIO];
 #define        com_addr(unit)  (p_com_addr[unit])
 
 static int     tiocm_xxx2mcr   __P((int tiocm_xxx));
 
 /* table and macro for fast conversion from a unit number to its com struct */
 static struct com_s    *p_com_addr[NSIO];
 #define        com_addr(unit)  (p_com_addr[unit])
 
-static struct com_s    com_structs[NSIO];
+#ifdef TIOCTIMESTAMP
+static  struct timeval intr_timestamp;
+#endif
 
 struct isa_driver      siodriver = {
        sioprobe, sioattach, "sio"
 
 struct isa_driver      siodriver = {
        sioprobe, sioattach, "sio"
@@ -263,15 +276,11 @@ static    int     comconsole = COMCONSOLE;
 #else
 static int     comconsole = -1;
 #endif
 #else
 static int     comconsole = -1;
 #endif
-static bool_t  comconsinit;
 static speed_t comdefaultrate = TTYDEF_SPEED;
 static u_int   com_events;     /* input chars + weighted output completions */
 static int     commajor;
 static speed_t comdefaultrate = TTYDEF_SPEED;
 static u_int   com_events;     /* input chars + weighted output completions */
 static int     commajor;
-struct tty     sio_tty[NSIO];
-extern struct tty      *constty;
-extern u_int   ipending;       /* XXX */
-extern int     tk_nin;         /* XXX */
-extern int     tk_rawcc;       /* XXX */
+struct tty     *sio_tty[NSIO];
+extern struct tty      *constty;       /* XXX */
 
 #ifdef KGDB
 #include "machine/remote-sl.h"
 
 #ifdef KGDB
 #include "machine/remote-sl.h"
@@ -312,7 +321,11 @@ sioprobe(dev)
 {
        static bool_t   already_init;
        Port_t          *com_ptr;
 {
        static bool_t   already_init;
        Port_t          *com_ptr;
+       bool_t          failures[10];
+       int             fn;
+       struct isa_device       *idev;
        Port_t          iobase;
        Port_t          iobase;
+       u_char          mcr_image;
        int             result;
 
        if (!already_init) {
        int             result;
 
        if (!already_init) {
@@ -320,6 +333,7 @@ sioprobe(dev)
                 * Turn off MCR_IENABLE for all likely serial ports.  An unused
                 * port with its MCR_IENABLE gate open will inhibit interrupts
                 * from any used port that shares the interrupt vector.
                 * Turn off MCR_IENABLE for all likely serial ports.  An unused
                 * port with its MCR_IENABLE gate open will inhibit interrupts
                 * from any used port that shares the interrupt vector.
+                * XXX the gate enable is elsewhere for some multiports.
                 */
                for (com_ptr = likely_com_ports;
                     com_ptr < &likely_com_ports[sizeof likely_com_ports
                 */
                for (com_ptr = likely_com_ports;
                     com_ptr < &likely_com_ports[sizeof likely_com_ports
@@ -328,8 +342,45 @@ sioprobe(dev)
                        outb(*com_ptr + com_mcr, 0);
                already_init = TRUE;
        }
                        outb(*com_ptr + com_mcr, 0);
                already_init = TRUE;
        }
+
+       /*
+        * If the port is on a multiport card and has a master port,
+        * initialize the common interrupt control register in the
+        * master and prepare to leave MCR_IENABLE clear in the mcr.
+        * Otherwise, prepare to set MCR_IENABLE in the mcr.
+        * Point idev to the device struct giving the correct id_irq.
+        * This is the struct for the master device if there is one.
+        */
+       idev = dev;
+       mcr_image = MCR_IENABLE;
+#ifdef COM_MULTIPORT
+       if (COM_ISMULTIPORT(dev)) {
+               if (!COM_NOMASTER(dev)) {
+                       idev = find_isadev(isa_devtab_tty, &siodriver,
+                                          COM_MPMASTER(dev));
+                       if (idev == NULL) {
+                               printf("sio%d: master device %d not found\n",
+                                      dev->id_unit, COM_MPMASTER(dev));
+                               return (0);
+                       }
+                       if (idev->id_irq == 0) {
+                               printf("sio%d: master device %d irq not configured\n",
+                                      dev->id_unit, COM_MPMASTER(dev));
+                               return (0);
+                       }
+                       outb(idev->id_iobase + com_scr, 0x80);
+                       mcr_image = 0;
+               }
+       }
+       else
+#endif /* COM_MULTIPORT */
+       if (idev->id_irq == 0) {
+               printf("sio%d: irq not configured\n", dev->id_unit);
+               return (0);
+       }
+
+       bzero(failures, sizeof failures);
        iobase = dev->id_iobase;
        iobase = dev->id_iobase;
-       result = IO_COMSIZE;
 
        /*
         * We don't want to get actual interrupts, just masked ones.
 
        /*
         * We don't want to get actual interrupts, just masked ones.
@@ -340,57 +391,119 @@ sioprobe(dev)
        disable_intr();
 
        /*
        disable_intr();
 
        /*
-        * Initialize the speed so that any junk in the THR or output fifo will
-        * be transmitted in a known time.  (There may be lots of junk after a
-        * soft reboot, and output interrupts don't work right after a master
-        * reset, at least for 16550s.  (The speed is undefined after MR, but
-        * MR empties the THR and the TSR so it's not clear why this matters)).
-        * Enable output interrupts (only) and check the following:
+        * XXX DELAY() reenables CPU interrupts.  This is a problem for
+        * shared interrupts after the first device using one has been
+        * successfully probed - config_isadev() has enabled the interrupt
+        * in the ICU.
+        */
+       outb(IO_ICU1 + 1, 0xff);
+
+       /*
+        * Initialize the speed and the word size and wait long enough to
+        * drain the maximum of 16 bytes of junk in device output queues.
+        * The speed is undefined after a master reset and must be set
+        * before relying on anything related to output.  There may be
+        * junk after a (very fast) soft reboot and (apparently) after
+        * master reset.
+        * XXX what about the UART bug avoided by waiting in comparam()?
+        * We don't want to to wait long enough to drain at 2 bps.
+        */
+       outb(iobase + com_cfcr, CFCR_DLAB);
+       outb(iobase + com_dlbl, COMBRD(9600) & 0xff);
+       outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8);
+       outb(iobase + com_cfcr, CFCR_8BITS);
+       DELAY((16 + 1) * 9600 / 10);
+
+       /* 
+        * Enable the interrupt gate and disable device interupts.  This
+        * should leave the device driving the interrupt line low and
+        * guarantee an edge trigger if an interrupt can be generated.
+        */
+       outb(iobase + com_mcr, mcr_image);
+       outb(iobase + com_ier, 0);
+
+       /*
+        * Attempt to set loopback mode so that we can send a null byte
+        * without annoying any external device.
+        */
+       outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK);
+
+       /*
+        * Attempt to generate an output interrupt.  On 8250's, setting
+        * IER_ETXRDY generates an interrupt independent of the current
+        * setting and independent of whether the THR is empty.  On 16450's,
+        * setting IER_ETXRDY generates an interrupt independent of the
+        * current setting.  On 16550A's, setting IER_ETXRDY only
+        * generates an interrupt when IER_ETXRDY is not already set.
+        */
+       outb(iobase + com_ier, IER_ETXRDY);
+
+       /*
+        * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate
+        * an interrupt.  They'd better generate one for actually doing
+        * output.  Loopback may be broken on the same incompatibles but
+        * it's unlikely to do more than allow the null byte out.
+        */
+       outb(iobase + com_data, 0);
+       DELAY((1 + 1) * 9600 / 10);
+
+       /*
+        * Turn off loopback mode so that the interrupt gate works again
+        * (MCR_IENABLE was hidden).  This should leave the device driving
+        * an interrupt line high.  It doesn't matter if the interrupt
+        * line oscillates while we are not looking at it, since interrupts
+        * are disabled.
+        */
+       outb(iobase + com_mcr, mcr_image);
+
+       /*
+        * Check that
         *      o the CFCR, IER and MCR in UART hold the values written to them
         *        (the values happen to be all distinct - this is good for
         *        avoiding false positive tests from bus echoes).
         *      o an output interrupt is generated and its vector is correct.
         *      o the interrupt goes away when the IIR in the UART is read.
         */
         *      o the CFCR, IER and MCR in UART hold the values written to them
         *        (the values happen to be all distinct - this is good for
         *        avoiding false positive tests from bus echoes).
         *      o an output interrupt is generated and its vector is correct.
         *      o the interrupt goes away when the IIR in the UART is read.
         */
-       outb(iobase + com_cfcr, CFCR_DLAB);
-       outb(iobase + com_dlbl, COMBRD(9600) & 0xff);
-       outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8);
-       outb(iobase + com_cfcr, CFCR_8BITS);    /* ensure IER is addressed */
-       outb(iobase + com_mcr, MCR_IENABLE);    /* open gate early */
-       outb(iobase + com_ier, 0);              /* ensure edge on next intr */
-       outb(iobase + com_ier, IER_ETXRDY);     /* generate interrupt */
-       DELAY((16 + 1) * 9600 / 10);            /* enough to drain 16 bytes */
-       if (   inb(iobase + com_cfcr) != CFCR_8BITS
-           || inb(iobase + com_ier) != IER_ETXRDY
-           || inb(iobase + com_mcr) != MCR_IENABLE
-#ifndef COM_MULTIPORT /* XXX - need to do more to enable interrupts */
-           || !isa_irq_pending(dev)
-#endif
-           || (inb(iobase + com_iir) & IIR_IMASK) != IIR_TXRDY
-           || isa_irq_pending(dev)
-           || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND)
-               result = 0;
+       failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS;
+       failures[1] = inb(iobase + com_ier) - IER_ETXRDY;
+       failures[2] = inb(iobase + com_mcr) - mcr_image;
+       if (idev->id_irq != 0)
+               failures[3] = isa_irq_pending(idev) ? 0 : 1;
+       failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY;
+       failures[5] = isa_irq_pending(idev) ? 1 : 0;
+       failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
 
        /*
         * Turn off all device interrupts and check that they go off properly.
 
        /*
         * Turn off all device interrupts and check that they go off properly.
-        * Leave MCR_IENABLE set.  It gates the OUT2 output of the UART to
+        * Leave MCR_IENABLE alone.  For ports without a master port, it gates
+        * the OUT2 output of the UART to
         * the ICU input.  Closing the gate would give a floating ICU input
         * (unless there is another device driving at) and spurious interrupts.
         * (On the system that this was first tested on, the input floats high
         * and gives a (masked) interrupt as soon as the gate is closed.)
         */
        outb(iobase + com_ier, 0);
         * the ICU input.  Closing the gate would give a floating ICU input
         * (unless there is another device driving at) and spurious interrupts.
         * (On the system that this was first tested on, the input floats high
         * and gives a (masked) interrupt as soon as the gate is closed.)
         */
        outb(iobase + com_ier, 0);
-       outb(iobase + com_mcr, MCR_IENABLE);    /* dummy to avoid bus echo */
-       if (   inb(iobase + com_ier) != 0
-           || isa_irq_pending(dev)
-           || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND)
-               result = 0;
+       outb(iobase + com_cfcr, CFCR_8BITS);    /* dummy to avoid bus echo */
+       failures[7] = inb(iobase + com_ier);
+       failures[8] = isa_irq_pending(idev) ? 1 : 0;
+       failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
 
 
+       outb(IO_ICU1 + 1, imen);        /* XXX */
        enable_intr();
        enable_intr();
+
+       result = IO_COMSIZE;
+       for (fn = 0; fn < sizeof failures; ++fn)
+               if (failures[fn]) {
+                       outb(iobase + com_mcr, 0);
+                       result = 0;
+                       if (!COM_QUIET(dev))
+                               printf("sio%d: probe test %d failed\n",
+                                      dev->id_unit, fn);
+               }
        return (result);
 }
 
        return (result);
 }
 
-static int                     /* XXX - should be void */
+static int
 sioattach(isdp)
        struct isa_device       *isdp;
 {
 sioattach(isdp)
        struct isa_device       *isdp;
 {
@@ -402,9 +515,9 @@ sioattach(isdp)
 
        iobase = isdp->id_iobase;
        unit = isdp->id_unit;
 
        iobase = isdp->id_iobase;
        unit = isdp->id_unit;
-       if (unit == comconsole)
-               DELAY(1000);    /* XXX */
-       s = spltty();
+       com = malloc(sizeof *com, M_TTYS, M_NOWAIT);
+       if (com == NULL)
+               return (0);
 
        /*
         * sioprobe() has initialized the device registers as follows:
 
        /*
         * sioprobe() has initialized the device registers as follows:
@@ -413,15 +526,14 @@ sioattach(isdp)
         *        data port is not hidden when we enable interrupts.
         *      o ier = 0.
         *        Interrupts are only enabled when the line is open.
         *        data port is not hidden when we enable interrupts.
         *      o ier = 0.
         *        Interrupts are only enabled when the line is open.
-        *      o mcr = MCR_IENABLE.
+        *      o mcr = MCR_IENABLE, or 0 if the port has a master port.
         *        Keeping MCR_DTR and MCR_RTS off might stop the external
         *        device from sending before we are ready.
         */
         *        Keeping MCR_DTR and MCR_RTS off might stop the external
         *        device from sending before we are ready.
         */
-
-       com = &com_structs[unit];
+       bzero(com, sizeof *com);
        com->cfcr_image = CFCR_8BITS;
        com->cfcr_image = CFCR_8BITS;
-       com->mcr_image = MCR_IENABLE;
-       com->dtr_wait = 200;
+       com->dtr_wait = 3 * hz;
+       com->tx_fifo_size = 1;
        com->iptr = com->ibuf = com->ibuf1;
        com->ibufend = com->ibuf1 + RS_IBUFSIZE;
        com->ihighwater = com->ibuf1 + RS_IHIGHWATER;
        com->iptr = com->ibuf = com->ibuf1;
        com->ibufend = com->ibuf1 + RS_IBUFSIZE;
        com->ihighwater = com->ibuf1 + RS_IHIGHWATER;
@@ -429,17 +541,30 @@ sioattach(isdp)
        com->data_port = iobase + com_data;
        com->int_id_port = iobase + com_iir;
        com->modem_ctl_port = iobase + com_mcr;
        com->data_port = iobase + com_data;
        com->int_id_port = iobase + com_iir;
        com->modem_ctl_port = iobase + com_mcr;
+       com->mcr_image = inb(com->modem_ctl_port);
        com->line_status_port = iobase + com_lsr;
        com->modem_status_port = iobase + com_msr;
        com->line_status_port = iobase + com_lsr;
        com->modem_status_port = iobase + com_msr;
-       com->tp = &sio_tty[unit];
-#ifdef COM_BIDIR
+
        /*
        /*
-        * if bidirectional ports possible, clear the bidir port info;
+        * We don't use all the flags from <sys/ttydefaults.h> since they
+        * are only relevant for logins.  It's important to have echo off
+        * initially so that the line doesn't start blathering before the
+        * echo flag can be turned off.
         */
         */
-       com->bidir = FALSE;
-       com->active = FALSE;
-       com->active_in = com->active_out = FALSE;
-#endif /* COM_BIDIR */
+       com->it_in.c_iflag = 0;
+       com->it_in.c_oflag = 0;
+       com->it_in.c_cflag = TTYDEF_CFLAG;
+       com->it_in.c_lflag = 0;
+       if (unit == comconsole) {
+               com->it_in.c_iflag = TTYDEF_IFLAG;
+               com->it_in.c_oflag = TTYDEF_OFLAG;
+               com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL;
+               com->it_in.c_lflag = TTYDEF_LFLAG;
+               com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL;
+       }
+       termioschars(&com->it_in);
+       com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate;
+       com->it_out = com->it_in;
 
        /* attempt to determine UART type */
        printf("sio%d: type", unit);
 
        /* attempt to determine UART type */
        printf("sio%d: type", unit);
@@ -475,8 +600,14 @@ sioattach(isdp)
                printf(" 16550?");
                break;
        case FIFO_TRIGGER_14:
                printf(" 16550?");
                break;
        case FIFO_TRIGGER_14:
-               com->hasfifo = TRUE;
                printf(" 16550A");
                printf(" 16550A");
+               if (COM_NOFIFO(isdp))
+                       printf(" fifo disabled");
+               else {
+                       com->hasfifo = TRUE;
+                       com->ftl_init = FIFO_TRIGGER_14;
+                       com->tx_fifo_size = 16;
+               }
                break;
        }
        outb(iobase + com_fifo, 0);
                break;
        }
        outb(iobase + com_fifo, 0);
@@ -484,32 +615,36 @@ determined_type: ;
 
 #ifdef COM_MULTIPORT
        if (COM_ISMULTIPORT(isdp)) {
 
 #ifdef COM_MULTIPORT
        if (COM_ISMULTIPORT(isdp)) {
-               struct isa_device *masterdev;
-
                com->multiport = TRUE;
                com->multiport = TRUE;
-               printf(" (multiport)");
-
-               /* set the master's common-interrupt-enable reg.,
-                * as appropriate. YYY See your manual
-                */
-               /* enable only common interrupt for port */
-               outb(com->modem_ctl_port, com->mcr_image = 0);
-
-               masterdev = find_isadev(isa_devtab_tty, &siodriver,
-                                       COM_MPMASTER(isdp));
-               outb(masterdev->id_iobase + com_scr, 0x80);
-       }
-       else
-               com->multiport = FALSE;
+               printf(" (multiport");
+               if (!COM_NOMASTER(isdp) && unit == COM_MPMASTER(isdp))
+                       printf(" master");
+               printf(")");
+        }
 #endif /* COM_MULTIPORT */
        printf("\n");
 
 #ifdef KGDB
        if (kgdb_dev == makedev(commajor, unit)) {
 #endif /* COM_MULTIPORT */
        printf("\n");
 
 #ifdef KGDB
        if (kgdb_dev == makedev(commajor, unit)) {
-               if (comconsole == unit)
+               if (unit == comconsole)
                        kgdb_dev = -1;  /* can't debug over console port */
                else {
                        kgdb_dev = -1;  /* can't debug over console port */
                else {
-                       cominit(unit, kgdb_rate);
+                       int divisor;
+
+                       /*
+                        * XXX now unfinished and broken.  Need to do
+                        * something more like a full open().  There's no
+                        * suitable interrupt handler so don't enable device
+                        * interrupts.  Watch out for null tp's.
+                        */
+                       outb(iobase + com_cfcr, CFCR_DLAB);
+                       divisor = ttspeedtab(kgdb_rate, comspeedtab);
+                       outb(iobase + com_dlbl, divisor & 0xFF);
+                       outb(iobase + com_dlbh, (u_int) divisor >> 8);
+                       outb(iobase + com_cfcr, CFCR_8BITS);
+                       outb(com->modem_status_port,
+                            com->mcr_image |= MCR_DTR | MCR_RTS);
+
                        if (kgdb_debug_init) {
                                /*
                                 * Print prefix of device name,
                        if (kgdb_debug_init) {
                                /*
                                 * Print prefix of device name,
@@ -517,23 +652,17 @@ determined_type: ;
                                 */
                                printf("sio%d: ", unit);
                                kgdb_connect(1);
                                 */
                                printf("sio%d: ", unit);
                                kgdb_connect(1);
-                       }
-                       else
+                       } else
                                printf("sio%d: kgdb enabled\n", unit);
                }
        }
 #endif
 
                                printf("sio%d: kgdb enabled\n", unit);
                }
        }
 #endif
 
-       /*
-        * Need to reset baud rate, etc. of next print so reset comconsinit.
-        */
-       if (unit == comconsole)
-               comconsinit = FALSE;
-
+       s = spltty();
        com_addr(unit) = com;
        splx(s);
        if (!comwakeup_started) {
        com_addr(unit) = com;
        splx(s);
        if (!comwakeup_started) {
-               comwakeup((caddr_t) NULL, 0);
+               comwakeup((caddr_t)NULL, 0);
                comwakeup_started = TRUE;
        }
        return (1);
                comwakeup_started = TRUE;
        }
        return (1);
@@ -547,224 +676,127 @@ sioopen(dev, flag, mode, p)
        int             mode;
        struct proc     *p;
 {
        int             mode;
        struct proc     *p;
 {
-#ifdef COM_BIDIR
-       bool_t          callout;
-#endif /* COM_BIDIR */
        struct com_s    *com;
        struct com_s    *com;
-       int             error = 0;
+       int             error;
        Port_t          iobase;
        Port_t          iobase;
+       int             mynor;
        int             s;
        struct tty      *tp;
        int             unit;
 
        int             s;
        struct tty      *tp;
        int             unit;
 
-       unit = UNIT(dev);
+       mynor = minor(dev);
+       unit = MINOR_TO_UNIT(mynor);
        if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL)
                return (ENXIO);
        if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL)
                return (ENXIO);
-#ifdef COM_BIDIR
-       /* if it's a callout device, and bidir not possible on that dev, die */
-       callout = CALLOUT(dev);
-       if (callout && !(com->bidir))
-               return (ENXIO);
-#endif /* COM_BIDIR */
-
-       tp = com->tp;
+       if (mynor & CONTROL_MASK)
+               return (0);
+       tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]);
        s = spltty();
        s = spltty();
-
-#ifdef COM_BIDIR
-
-bidir_open_top:
-       /* if it's bidirectional, we've gotta deal with it... */
-       if (com->bidir) {
-               if (callout) {
-                       if (com->active_in) {
-                           /* it's busy. die */
-                           splx(s);
-                           return (EBUSY);
-                       } else {
-                           /* it's ours.  lock it down, and set it up */
-                           com->active_out = TRUE;
+       /*
+        * We jump to this label after all non-interrupted sleeps to pick
+        * up any changes of the device state.
+        */
+open_top:
+       while (com->state & CS_DTR_OFF) {
+               error = tsleep((caddr_t)&com->dtr_wait, TTIPRI | PCATCH,
+                              "siodtr", 0);
+               if (error != 0)
+                       goto out;
+       }
+       if (tp->t_state & TS_ISOPEN) {
+               /*
+                * The device is open, so everything has been initialized.
+                * Handle conflicts.
+                */
+               if (mynor & CALLOUT_MASK) {
+                       if (!com->active_out) {
+                               error = EBUSY;
+                               goto out;
                        }
                } else {
                        if (com->active_out) {
                        }
                } else {
                        if (com->active_out) {
-                               /* it's busy, outgoing.  wait, if possible */
                                if (flag & O_NONBLOCK) {
                                if (flag & O_NONBLOCK) {
-                                   /* can't wait; bail */
-                                   splx(s);
-                                   return (EBUSY);
-                               } else {
-                                   /* wait for it... */
-                                   error = tsleep((caddr_t)&com->active_out,
-                                                  TTIPRI|PCATCH,
-                                                  "siooth",
-                                                  0);
-                                   /* if there was an error, take off. */
-                                   if (error != 0) {
-                                       splx(s);
-                                       return (error);
-                                   }
-                                   /* else take it from the top */
-                                   goto bidir_open_top;
-                               }
-                       } else if (com->prev_modem_status & MSR_DCD
-                                  || FAKE_DCD(unit)) {
-                               /* there's a carrier on the line; we win */
-                               com->active_in = TRUE;
-                       } else {
-                               /* there is no carrier on the line */
-                               if (flag & O_NONBLOCK) {
-                                   /* can't wait; let it open */
-                                   com->active_in = TRUE;
-                               } else {
-                                   /* put DTR & RTS up */
-                                   /* XXX - bring up RTS earlier? */
-                                   commctl(com, MCR_DTR | MCR_RTS, DMSET);
-                                   outb(com->iobase + com_ier, IER_EMSC);
-
-                                   /* wait for it... */
-                                   error = tsleep((caddr_t)&com->active_in,
-                                                  TTIPRI|PCATCH,
-                                                  "siodcd",
-                                                  0);
-
-                                   /* if not active, turn intrs and DTR off */
-                                   if (!com->active) {
-                                       outb(com->iobase + com_ier, 0);
-                                       commctl(com, MCR_DTR, DMBIC);
-                                   }
-
-                                   /* if there was an error, take off. */
-                                   if (error != 0) {
-                                       splx(s);
-                                       return (error);
-                                   }
-                                   /* else take it from the top */
-                                   goto bidir_open_top;
+                                       error = EBUSY;
+                                       goto out;
                                }
                                }
+                               error = tsleep((caddr_t)&com->active_out,
+                                              TTIPRI | PCATCH, "siobi", 0);
+                               if (error != 0)
+                                       goto out;
+                               goto open_top;
                        }
                }
                        }
                }
-       }
-
-       com->active = TRUE;
-#endif /* COM_BIDIR */
-
-       tp->t_oproc = comstart;
-       tp->t_param = comparam;
-       tp->t_dev = dev;
-       if (!(tp->t_state & TS_ISOPEN)) {
-               tp->t_state |= TS_WOPEN;
-               ttychars(tp);
-               if (tp->t_ispeed == 0) {
-                       /*
-                        * We no longer use the flags from <sys/ttydefaults.h>
-                        * since those are only relevant for logins.  It's
-                        * important to have echo off initially so that the
-                        * line doesn't start blathering before the echo flag
-                        * can be turned off.
-                        */
-                       tp->t_iflag = 0;
-#ifdef COMCONSOLE
-                       tp->t_oflag = TTYDEF_OFLAG;
-#else
-                       tp->t_oflag = 0;
-#endif
-                       tp->t_cflag = CREAD | CS8 | HUPCL;
-                       tp->t_lflag = 0;
-                       tp->t_ispeed = tp->t_ospeed = comdefaultrate;
+               if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
+                       error = EBUSY;
+                       goto out;
                }
                }
-
+       } else {
                /*
                /*
-                * XXX the full state after a first open() needs to be
-                * programmable and separate for callin and callout.
+                * The device isn't open, so there are no conflicts.
+                * Initialize it.  Initialization is done twice in many
+                * cases: to preempt sleeping callin opens if we are
+                * callout, and to complete a callin open after DCD rises.
                 */
                 */
-#ifdef COM_BIDIR
-               if (com->bidir) {
-                       if (callout)
-                               tp->t_cflag |= CLOCAL;
-                       else
-                               tp->t_cflag &= ~CLOCAL;
-               }
-#endif
-
+               tp->t_oproc = comstart;
+               tp->t_param = comparam;
+               tp->t_dev = dev;
+               tp->t_termios = mynor & CALLOUT_MASK
+                               ? com->it_out : com->it_in;
                commctl(com, MCR_DTR | MCR_RTS, DMSET);
                commctl(com, MCR_DTR | MCR_RTS, DMSET);
+               com->ftl_max = com->ftl_init;
+               ++com->wopeners;
                error = comparam(tp, &tp->t_termios);
                error = comparam(tp, &tp->t_termios);
+               --com->wopeners;
                if (error != 0)
                        goto out;
                if (error != 0)
                        goto out;
-               ttsetwater(tp);
-
                /*
                /*
-                * XXX temporary fix for deadlock in ttywait().
-                *
-                * If two processes wait for output to drain from the same
-                * tty, and the amount of output to drain is > 0 and
-                * <= tp->t_lowat, then the processes will take turns
-                * uselessly waking each other up until the output drains,
-                * with cpl higher than spltty() throughout.
-                *
-                * The sleep address and TS_ASLEEP flag ought to be different
-                * for the different events (output done) and (output almost
-                * done).
+                * XXX we should goto open_top if comparam() slept.
                 */
                 */
-               tp->t_lowat = 0;
-
+               ttsetwater(tp);
                iobase = com->iobase;
                if (com->hasfifo) {
                iobase = com->iobase;
                if (com->hasfifo) {
-                       /* (re)enable and drain FIFO */
-                       outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER
-                                               | FIFO_RCV_RST | FIFO_XMT_RST);
+                       /* Drain fifo. */
+                       outb(iobase + com_fifo,
+                            FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST
+                            | com->ftl);
                        DELAY(100);
                }
                disable_intr();
                (void) inb(com->line_status_port);
                (void) inb(com->data_port);
                        DELAY(100);
                }
                disable_intr();
                (void) inb(com->line_status_port);
                (void) inb(com->data_port);
-               com->last_modem_status =
-               com->prev_modem_status = inb(com->modem_status_port);
+                       com->prev_modem_status =
+                       com->last_modem_status = inb(com->modem_status_port);
                outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS
                                       | IER_EMSC);
                enable_intr();
                outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS
                                       | IER_EMSC);
                enable_intr();
-               if (com->prev_modem_status & MSR_DCD || FAKE_DCD(unit))
-                       tp->t_state |= TS_CARR_ON;
-       }
-       else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
-               splx(s);
-               return (EBUSY);
-       }
-       while (!(flag & O_NONBLOCK) && !(tp->t_cflag & CLOCAL)
-#ifdef COM_BIDIR
-               /* We went through a lot of trouble to open it,
-                * but it's certain we have a carrier now, so
-                * don't spend any time on it now.
+               /*
+                * Handle initial DCD.  Callout devices get a fake initial
+                * DCD (trapdoor DCD).  If we are callout, then any sleeping
+                * callin opens get woken up and resume sleeping on "siobi"
+                * instead of "siodcd".
                 */
                 */
-              && !(com->bidir)
-#endif /* COM_BIDIR */
-              && !(tp->t_state & TS_CARR_ON)) {
-               tp->t_state |= TS_WOPEN;
-               error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH,
-                                ttopen, 0);
+               if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK)
+                       (*linesw[tp->t_line].l_modem)(tp, 1);
+       }
+       /*
+        * Wait for DCD if necessary.
+        */
+       if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)
+           && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) {
+               ++com->wopeners;
+               error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0);
+               --com->wopeners;
                if (error != 0)
                if (error != 0)
-                       break;
+                       goto out;
+               goto open_top;
        }
        }
+       error = (*linesw[tp->t_line].l_open)(dev, tp, 0);
+       if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
+               com->active_out = TRUE;
 out:
        splx(s);
 out:
        splx(s);
-       if (error == 0)
-               error = (*linesw[tp->t_line].l_open)(dev, tp, 0);
-
-#ifdef COM_BIDIR
-       /* wakeup sleepers */
-       wakeup((caddr_t) &com->active_in);
-#endif /* COM_BIDIR */
-
-       /*
-        * XXX - the next step was once not done, so interrupts, DTR and RTS
-        * remained hot if the process was killed while it was sleeping
-        * waiting for carrier.  Now there is the opposite problem.  If several
-        * processes are sleeping waiting for carrier on the same line and one
-        * is killed, interrupts are turned off so the other processes will
-        * never see the carrier rise.
-        */
-       if (error != 0 && !(tp->t_state & TS_ISOPEN))
+       if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0)
                comhardclose(com);
                comhardclose(com);
-       tp->t_state &= ~TS_WOPEN;
-
        return (error);
 }
 
        return (error);
 }
 
@@ -777,13 +809,21 @@ sioclose(dev, flag, mode, p)
        struct proc     *p;
 {
        struct com_s    *com;
        struct proc     *p;
 {
        struct com_s    *com;
+       int             mynor;
+       int             s;
        struct tty      *tp;
 
        struct tty      *tp;
 
-       com = com_addr(UNIT(dev));
+       mynor = minor(dev);
+       if (mynor & CONTROL_MASK)
+               return (0);
+       com = com_addr(MINOR_TO_UNIT(mynor));
        tp = com->tp;
        tp = com->tp;
+       s = spltty();
        (*linesw[tp->t_line].l_close)(tp, flag);
        (*linesw[tp->t_line].l_close)(tp, flag);
+       siostop(tp, FREAD | FWRITE);
        comhardclose(com);
        ttyclose(tp);
        comhardclose(com);
        ttyclose(tp);
+       splx(s);
        return (0);
 }
 
        return (0);
 }
 
@@ -794,39 +834,45 @@ comhardclose(com)
        Port_t          iobase;
        int             s;
        struct tty      *tp;
        Port_t          iobase;
        int             s;
        struct tty      *tp;
+       int             unit;
 
 
-       s = spltty();
+       unit = DEV_TO_UNIT(com->tp->t_dev);
        iobase = com->iobase;
        iobase = com->iobase;
+       s = spltty();
+#ifdef TIOCTIMESTAMP
+       com->do_timestamp = 0;
+#endif
        outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
 #ifdef KGDB
        /* do not disable interrupts or hang up if debugging */
        outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
 #ifdef KGDB
        /* do not disable interrupts or hang up if debugging */
-       if (kgdb_dev != makedev(commajor, com - &com_structs[0]))
+       if (kgdb_dev != makedev(commajor, unit))
 #endif
        {
                outb(iobase + com_ier, 0);
                tp = com->tp;
 #endif
        {
                outb(iobase + com_ier, 0);
                tp = com->tp;
-               if (tp->t_cflag & HUPCL || tp->t_state & TS_WOPEN
+               if (tp->t_cflag & HUPCL
+                   /*
+                    * XXX we will miss any carrier drop between here and the
+                    * next open.  Perhaps we should watch DCD even when the
+                    * port is closed; it is not sufficient to check it at
+                    * the next open because it might go up and down while
+                    * we're not watching.
+                    */
+                   || !com->active_out
+                      && !(com->prev_modem_status & MSR_DCD)
+                      && !(com->it_in.c_cflag & CLOCAL)
                    || !(tp->t_state & TS_ISOPEN)) {
                        commctl(com, MCR_RTS, DMSET);
                    || !(tp->t_state & TS_ISOPEN)) {
                        commctl(com, MCR_RTS, DMSET);
-                       if (com->dtr_wait != 0)
-                               /*
-                                * Uninterruptible sleep since we want to
-                                * wait a fixed time.
-                                * XXX - delay in open() (if necessary),
-                                * not here (always).
-                                */
-                               tsleep((caddr_t)&com->dtr_wait, TTIPRI,
-                                      "sioclose", com->dtr_wait);
+                       if (com->dtr_wait != 0) {
+                               timeout(siodtrwakeup, (caddr_t)com,
+                                       com->dtr_wait);
+                               com->state |= CS_DTR_OFF;
+                       }
                }
        }
                }
        }
-
-#ifdef COM_BIDIR
-       com->active = com->active_in = com->active_out = FALSE;
-
-       /* wakeup sleepers who are waiting for out to finish */
-       wakeup((caddr_t) &com->active_out);
-#endif /* COM_BIDIR */
-
+       com->active_out = FALSE;
+       wakeup((caddr_t)&com->active_out);
+       wakeup(TSA_CARR_ON(tp));        /* restart any wopeners */
        splx(s);
 }
 
        splx(s);
 }
 
@@ -836,8 +882,13 @@ sioread(dev, uio, flag)
        struct uio      *uio;
        int             flag;
 {
        struct uio      *uio;
        int             flag;
 {
-       struct tty      *tp = com_addr(UNIT(dev))->tp;
+       int             mynor;
+       struct tty      *tp;
 
 
+       mynor = minor(dev);
+       if (mynor & CONTROL_MASK)
+               return (ENODEV);
+       tp = com_addr(MINOR_TO_UNIT(mynor))->tp;
        return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
 }
 
        return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
 }
 
@@ -847,9 +898,15 @@ siowrite(dev, uio, flag)
        struct uio      *uio;
        int             flag;
 {
        struct uio      *uio;
        int             flag;
 {
-       int             unit = UNIT(dev);
-       struct tty      *tp = com_addr(unit)->tp;
+       int             mynor;
+       struct tty      *tp;
+       int             unit;
 
 
+       mynor = minor(dev);
+       if (mynor & CONTROL_MASK)
+               return (ENODEV);
+       unit = MINOR_TO_UNIT(mynor);
+       tp = com_addr(unit)->tp;
        /*
         * (XXX) We disallow virtual consoles if the physical console is
         * a serial port.  This is in case there is a display attached that
        /*
         * (XXX) We disallow virtual consoles if the physical console is
         * a serial port.  This is in case there is a display attached that
@@ -861,19 +918,37 @@ siowrite(dev, uio, flag)
        return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
 }
 
        return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
 }
 
+static void
+siodtrwakeup(chan, ticks)
+       caddr_t chan;
+       int     ticks;
+{
+       struct com_s    *com;
+
+       com = (struct com_s *)chan;
+       com->state &= ~CS_DTR_OFF;
+       wakeup((caddr_t)&com->dtr_wait);
+}
+
+#ifdef TIOCTIMESTAMP
+/* Interrupt routine for timekeeping purposes */
+void
+siointrts(unit)
+       int unit;
+{
+       microtime(&intr_timestamp);
+       siointr(unit);
+}
+#endif
+
 void
 siointr(unit)
        int     unit;
 {
 void
 siointr(unit)
        int     unit;
 {
-       struct com_s    *com;
 #ifndef COM_MULTIPORT
 #ifndef COM_MULTIPORT
-       u_char          line_status;
-       u_char          modem_status;
-       u_char          *ioptr;
-       u_char          recv_data;
-
-       com = com_addr(unit);
+       siointr1(com_addr(unit));
 #else /* COM_MULTIPORT */
 #else /* COM_MULTIPORT */
+       struct com_s    *com;
        bool_t          possibly_more_intrs;
 
        /*
        bool_t          possibly_more_intrs;
 
        /*
@@ -882,56 +957,49 @@ siointr(unit)
         * If the IRQ signal is just an OR of the IRQ signals from several
         * devices, then the edge from one may be lost because another is
         * on.
         * If the IRQ signal is just an OR of the IRQ signals from several
         * devices, then the edge from one may be lost because another is
         * on.
-        *
-        * XXX getting the status from comintr1() is not best and may be
-        * incorrect.  It would be better to test the int_id's in a tight
-        * loop.  If each is off when it is tested, then they all must
-        * have been off at the start.
         */
        do {
                possibly_more_intrs = FALSE;
                for (unit = 0; unit < NSIO; ++unit) {
                        com = com_addr(unit);
         */
        do {
                possibly_more_intrs = FALSE;
                for (unit = 0; unit < NSIO; ++unit) {
                        com = com_addr(unit);
-                       if (com != NULL) {
-                               /*
-                                * XXX call comintr1() instead of here from
-                                * comwakeup().  The interrupt edge problem
-                                * only exists for real interrupts.
-                                */
-                               possibly_more_intrs |= comintr1(com);
+                       if (com != NULL
+                           && (inb(com->int_id_port) & IIR_IMASK)
+                              != IIR_NOPEND) {
+                               siointr1(com);
+                               possibly_more_intrs = TRUE;
                        }
                }
        } while (possibly_more_intrs);
                        }
                }
        } while (possibly_more_intrs);
-       return;
+#endif /* COM_MULTIPORT */
 }
 
 }
 
-static bool_t
-comintr1(com)
+static void
+siointr1(com)
        struct com_s *com;
 {
        struct com_s *com;
 {
-       u_char          line_status;
-       u_char          modem_status;
-       u_char          *ioptr;
-       u_char          recv_data;
-       bool_t          donesomething;
-
-       donesomething = FALSE;
-#endif /* COM_MULTIPORT */
-
+       u_char  line_status;
+       u_char  modem_status;
+       u_char  *ioptr;
+       u_char  recv_data;
+
+#ifdef TIOCTIMESTAMP
+       if (com->do_timestamp)
+               /* XXX a little bloat here... */
+               com->timestamp = intr_timestamp;
+#endif
        while (TRUE) {
                line_status = inb(com->line_status_port);
 
                /* input event? (check first to help avoid overruns) */
                while (line_status & LSR_RCV_MASK) {
                        /* break/unnattached error bits or real input? */
        while (TRUE) {
                line_status = inb(com->line_status_port);
 
                /* input event? (check first to help avoid overruns) */
                while (line_status & LSR_RCV_MASK) {
                        /* break/unnattached error bits or real input? */
-#ifdef COM_MULTIPORT
-                       donesomething = TRUE;
-#endif /* COM_MULTIPORT */
                        if (!(line_status & LSR_RXRDY))
                                recv_data = 0;
                        else
                                recv_data = inb(com->data_port);
                        ++com->bytes_in;
                        if (!(line_status & LSR_RXRDY))
                                recv_data = 0;
                        else
                                recv_data = inb(com->data_port);
                        ++com->bytes_in;
+                       if (com->hotchar != 0 && recv_data == com->hotchar)
+                               setsofttty();
 #ifdef KGDB
                        /* trap into kgdb? (XXX - needs testing and optim) */
                        if (recv_data == FRAME_END
 #ifdef KGDB
                        /* trap into kgdb? (XXX - needs testing and optim) */
                        if (recv_data == FRAME_END
@@ -946,9 +1014,9 @@ comintr1(com)
                                CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW);
                        else {
                                ++com_events;
                                CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW);
                        else {
                                ++com_events;
-#if 0
+#if 0 /* for testing input latency vs efficiency */
 if (com->iptr - com->ibuf == 8)
 if (com->iptr - com->ibuf == 8)
-       schedsoftcom();
+       setsofttty();
 #endif
                                ioptr[0] = recv_data;
                                ioptr[CE_INPUT_OFFSET] = line_status;
 #endif
                                ioptr[0] = recv_data;
                                ioptr[CE_INPUT_OFFSET] = line_status;
@@ -978,14 +1046,11 @@ if (com->iptr - com->ibuf == 8)
                         * UARTs mess them up, and it's easy to remember the
                         * previous bits and calculate the delta.
                         */
                         * UARTs mess them up, and it's easy to remember the
                         * previous bits and calculate the delta.
                         */
-#ifdef COM_MULTIPORT
-                       donesomething = TRUE;
-#endif /* COM_MULTIPORT */
                        com->last_modem_status = modem_status;
                        if (!(com->state & CS_CHECKMSR)) {
                                com_events += LOTS_OF_EVENTS;
                                com->state |= CS_CHECKMSR;
                        com->last_modem_status = modem_status;
                        if (!(com->state & CS_CHECKMSR)) {
                                com_events += LOTS_OF_EVENTS;
                                com->state |= CS_CHECKMSR;
-                               schedsoftcom();
+                               setsofttty();
                        }
 
                        /* handle CTS change immediately for crisp flow ctl */
                        }
 
                        /* handle CTS change immediately for crisp flow ctl */
@@ -1000,36 +1065,43 @@ if (com->iptr - com->ibuf == 8)
                /* output queued and everything ready? */
                if (line_status & LSR_TXRDY
                    && com->state >= (CS_ODEVREADY | CS_BUSY | CS_TTGO)) {
                /* output queued and everything ready? */
                if (line_status & LSR_TXRDY
                    && com->state >= (CS_ODEVREADY | CS_BUSY | CS_TTGO)) {
-#ifdef COM_MULTIPORT
-                       donesomething = TRUE;
-#endif /* COM_MULTIPORT */
                        ioptr = com->optr;
                        ioptr = com->optr;
-                       outb(com->data_port, *ioptr);
-                       ++com->bytes_out;
-                       com->optr = ++ioptr;
+                       if (com->tx_fifo_size > 1) {
+                               u_int   ocount;
+
+                               ocount = com->obufend - ioptr;
+                               if (ocount > com->tx_fifo_size)
+                                       ocount = com->tx_fifo_size;
+                               com->bytes_out += ocount;
+                               do
+                                       outb(com->data_port, *ioptr++);
+                               while (--ocount != 0);
+                       } else {
+                               outb(com->data_port, *ioptr++);
+                               ++com->bytes_out;
+                       }
+                       com->optr = ioptr;
                        if (ioptr >= com->obufend) {
                                /* output just completed */
                                com_events += LOTS_OF_EVENTS;
                                com->state ^= (CS_ODONE | CS_BUSY);
                        if (ioptr >= com->obufend) {
                                /* output just completed */
                                com_events += LOTS_OF_EVENTS;
                                com->state ^= (CS_ODONE | CS_BUSY);
-                               schedsoftcom(); /* handle at high level ASAP */
+                               setsofttty();   /* handle at high level ASAP */
                        }
                }
 
                /* finished? */
                        }
                }
 
                /* finished? */
+#ifndef COM_MULTIPORT
                if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
                if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
-#ifdef COM_MULTIPORT
-                       return (donesomething);
-#else
-                       return;
 #endif /* COM_MULTIPORT */
 #endif /* COM_MULTIPORT */
+                       return;
        }
 }
 
 static int
 tiocm_xxx2mcr(tiocm_xxx)
        }
 }
 
 static int
 tiocm_xxx2mcr(tiocm_xxx)
-       int tiocm_xxx;
+       int     tiocm_xxx;
 {
 {
-       int mcr;
+       int     mcr;
 
        mcr = 0;
        if (tiocm_xxx & TIOCM_DTR)
 
        mcr = 0;
        if (tiocm_xxx & TIOCM_DTR)
@@ -1052,28 +1124,75 @@ sioioctl(dev, cmd, data, flag, p)
        Port_t          iobase;
        int             mcr;
        int             msr;
        Port_t          iobase;
        int             mcr;
        int             msr;
+       int             mynor;
        int             s;
        int             tiocm_xxx;
        struct tty      *tp;
 
        int             s;
        int             tiocm_xxx;
        struct tty      *tp;
 
-       com = com_addr(UNIT(dev));
-       tp = com->tp;
+       mynor = minor(dev);
+       com = com_addr(MINOR_TO_UNIT(mynor));
+       if (mynor & CONTROL_MASK) {
+               struct termios *ct;
 
 
+               switch (mynor & CONTROL_MASK) {
+               case CONTROL_INIT_STATE:
+                       ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in;
+                       break;
+               case CONTROL_LOCK_STATE:
+                       ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in;
+                       break;
+               default:
+                       return (ENODEV);        /* /dev/nodev */
+               }
+               switch (cmd) {
+               case TIOCSETA:
+                       error = suser(p->p_ucred, &p->p_acflag);
+                       if (error)
+                               return (error);
+                       *ct = *(struct termios *)data;
+                       return (0);
+               case TIOCGETA:
+                       *(struct termios *)data = *ct;
+                       return (0);
+               case TIOCGETD:
+                       *(int *)data = TTYDISC;
+                       return (0);
+               case TIOCGWINSZ:
+                       bzero(data, sizeof(struct winsize));
+                       return (0);
+               default:
+                       return (ENOTTY);
+               }
+       }
+       tp = com->tp;
+       if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
+               int cc;
+               struct termios *dt = (struct termios *)data;
+               struct termios *lt = mynor & CALLOUT_MASK
+                                    ? &com->lt_out : &com->lt_in;
+
+               dt->c_iflag = (tp->t_iflag & lt->c_iflag)
+                             | (dt->c_iflag & ~lt->c_iflag);
+               dt->c_oflag = (tp->t_oflag & lt->c_oflag)
+                             | (dt->c_oflag & ~lt->c_oflag);
+               dt->c_cflag = (tp->t_cflag & lt->c_cflag)
+                             | (dt->c_cflag & ~lt->c_cflag);
+               dt->c_lflag = (tp->t_lflag & lt->c_lflag)
+                             | (dt->c_lflag & ~lt->c_lflag);
+               for (cc = 0; cc < NCCS; ++cc)
+                       if (lt->c_cc[cc] != 0)
+                               dt->c_cc[cc] = tp->t_cc[cc];
+               if (lt->c_ispeed != 0)
+                       dt->c_ispeed = tp->t_ispeed;
+               if (lt->c_ospeed != 0)
+                       dt->c_ospeed = tp->t_ospeed;
+       }
        error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
        if (error >= 0)
                return (error);
        error = ttioctl(tp, cmd, data, flag);
        error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
        if (error >= 0)
                return (error);
        error = ttioctl(tp, cmd, data, flag);
-#ifdef COM_BIDIR
-       /* XXX: plug security hole while stucky bits not yet implemented */
-
-       if (com->bidir && com->active_in && p->p_ucred->cr_uid != 0)
-               tp->t_cflag &= ~CLOCAL;
-#endif
-       if (error >= 0) {
-               tp->t_lowat = 0;        /* XXX part of ttywait() deadlock fix */
+       if (error >= 0)
                return (error);
                return (error);
-       }
-
        iobase = com->iobase;
        s = spltty();
        switch (cmd) {
        iobase = com->iobase;
        s = spltty();
        switch (cmd) {
@@ -1121,57 +1240,24 @@ sioioctl(dev, cmd, data, flag, p)
                        tiocm_xxx |= TIOCM_RI;
                *(int *)data = tiocm_xxx;
                break;
                        tiocm_xxx |= TIOCM_RI;
                *(int *)data = tiocm_xxx;
                break;
-#ifdef COM_BIDIR
-       case TIOCMSBIDIR:
-               /* must be root to set bidir. capability */
-               error = suser(p->p_ucred, &p->p_acflag);
-               if (error != 0) {
-                       splx(s);
-                       return(EPERM);
-               }
-
-               /* if it's the console, can't do it (XXX why?) */
-               if (UNIT(dev) == comconsole) {
-                       splx(s);
-                       return(ENOTTY);
-               }
-
-#if 0
-               /* XXX - can't do the next, for obvious reasons...
-                * but there are problems to be looked at...
-                */
-               /* if the port is active, don't do it */
-               if (com->active) {
-                       splx(s);
-                       return(EBUSY);
-               }
-#endif
-
-               com->bidir = *(int *)data;
-               break;
-       case TIOCMGBIDIR:
-               *(int *)data = com->bidir;
-               break;
-#endif /* COM_BIDIR */
        case TIOCMSDTRWAIT:
                /* must be root since the wait applies to following logins */
                error = suser(p->p_ucred, &p->p_acflag);
                if (error != 0) {
                        splx(s);
        case TIOCMSDTRWAIT:
                /* must be root since the wait applies to following logins */
                error = suser(p->p_ucred, &p->p_acflag);
                if (error != 0) {
                        splx(s);
-                       return(EPERM);
+                       return (EPERM);
                }
                }
-
-               /* if it's the console, can't do it (XXX why?) */
-               if (UNIT(dev) == comconsole) {
-                       splx(s);
-                       return(ENOTTY);
-               }
-
-               com->dtr_wait = *(int *)data;
+               com->dtr_wait = *(int *)data * 100 / hz;
                break;
        case TIOCMGDTRWAIT:
                *(int *)data = com->dtr_wait;
                break;
                break;
        case TIOCMGDTRWAIT:
                *(int *)data = com->dtr_wait;
                break;
+#ifdef TIOCTIMESTAMP
+       case TIOCTIMESTAMP:
+               com->do_timestamp = TRUE;
+               *(struct timeval *)data = com->timestamp;
+               break;
+#endif
        default:
                splx(s);
                return (ENOTTY);
        default:
                splx(s);
                return (ENOTTY);
@@ -1192,34 +1278,24 @@ comflush(com)
                com_events -= LOTS_OF_EVENTS;
        com->state &= ~(CS_ODONE | CS_BUSY);
        enable_intr();
                com_events -= LOTS_OF_EVENTS;
        com->state &= ~(CS_ODONE | CS_BUSY);
        enable_intr();
-       rbp = &com->tp->t_out;
+       rbp = com->tp->t_out;
        rbp->rb_hd += com->ocount;
        rbp->rb_hd = RB_ROLLOVER(rbp, rbp->rb_hd);
        com->ocount = 0;
        com->tp->t_state &= ~TS_BUSY;
 }
 
        rbp->rb_hd += com->ocount;
        rbp->rb_hd = RB_ROLLOVER(rbp, rbp->rb_hd);
        com->ocount = 0;
        com->tp->t_state &= ~TS_BUSY;
 }
 
-static void
-compoll()
+void
+siopoll()
 {
 {
-       static bool_t   awake = FALSE;
-       struct com_s    *com;
-       int             s;
        int             unit;
 
        if (com_events == 0)
                return;
        int             unit;
 
        if (com_events == 0)
                return;
-       disable_intr();
-       if (awake) {
-               enable_intr();
-               return;
-       }
-       awake = TRUE;
-       enable_intr();
-       s = spltty();
 repeat:
        for (unit = 0; unit < NSIO; ++unit) {
 repeat:
        for (unit = 0; unit < NSIO; ++unit) {
-               u_char          *buf;
+               u_char          *buf;
+               struct com_s    *com;
                u_char          *ibuf;
                int             incc;
                struct tty      *tp;
                u_char          *ibuf;
                int             incc;
                struct tty      *tp;
@@ -1228,12 +1304,25 @@ repeat:
                if (com == NULL)
                        continue;
                tp = com->tp;
                if (com == NULL)
                        continue;
                tp = com->tp;
+               if (tp == NULL)
+                       continue;
 
 
-               buf = NULL;     /* avoid compiler warning */
                /* switch the role of the low-level input buffers */
                /* switch the role of the low-level input buffers */
-               if (com->iptr == (ibuf = com->ibuf))
+               if (com->iptr == (ibuf = com->ibuf)) {
+                       buf = NULL;     /* not used, but compiler can't tell */
                        incc = 0;
                        incc = 0;
-               else {
+               } else {
+                       /*
+                        * Prepare to reduce input latency for packet
+                        * discplines with a end of packet character.
+                        * XXX should be elsewhere.
+                        */
+                       if (tp->t_line == SLIPDISC)
+                               com->hotchar = 0xc0;
+                       else if (tp->t_line == PPPDISC)
+                               com->hotchar = 0x7e;
+                       else
+                               com->hotchar = 0;
                        buf = ibuf;
                        disable_intr();
                        incc = com->iptr - buf;
                        buf = ibuf;
                        disable_intr();
                        incc = com->iptr - buf;
@@ -1251,7 +1340,15 @@ repeat:
                         * of input, so enable RTS if it is now disabled and
                         * there is room in the high-level buffer.
                         */
                         * of input, so enable RTS if it is now disabled and
                         * there is room in the high-level buffer.
                         */
-                       if (!(com->mcr_image & MCR_RTS)
+                       /*
+                        * XXX this used not to look at CS_RTS_IFLOW.  The
+                        * change is to allow full control of MCR_RTS via
+                        * ioctls after turning CS_RTS_IFLOW off.  Check
+                        * for races.  We shouldn't allow the ioctls while
+                        * CS_RTS_IFLOW is on.
+                        */
+                       if ((com->state & CS_RTS_IFLOW)
+                           && !(com->mcr_image & MCR_RTS)
                            && !(tp->t_state & TS_RTS_IFLOW))
                                outb(com->modem_ctl_port,
                                     com->mcr_image |= MCR_RTS);
                            && !(tp->t_state & TS_RTS_IFLOW))
                                outb(com->modem_ctl_port,
                                     com->mcr_image |= MCR_RTS);
@@ -1269,40 +1366,45 @@ repeat:
                        com_events -= LOTS_OF_EVENTS;
                        com->state &= ~CS_CHECKMSR;
                        enable_intr();
                        com_events -= LOTS_OF_EVENTS;
                        com->state &= ~CS_CHECKMSR;
                        enable_intr();
-                       if (delta_modem_status & MSR_DCD && !FAKE_DCD(unit)) {
-                               if (com->prev_modem_status & MSR_DCD) {
-                                       (*linesw[tp->t_line].l_modem)(tp, 1);
-#ifdef COM_BIDIR
-                                       wakeup((caddr_t) &com->active_in);
-#endif /* COM_BIDIR */
-                               }
-                               else
-                                       (*linesw[tp->t_line].l_modem)(tp, 0);
-                       }
+                       if (delta_modem_status & MSR_DCD)
+                               (*linesw[tp->t_line].l_modem)
+                                       (tp, com->prev_modem_status & MSR_DCD);
                }
 
                /* XXX */
                if (TRUE) {
                }
 
                /* XXX */
                if (TRUE) {
-                       u_int delta;
-                       u_int delta_error_counts[CE_NTYPES];
-                       int errnum;
-                       u_long total;
+                       u_int   delta;
+                       int     errnum;
+                       u_long  total;
 
 
-                       disable_intr();
-                       bcopy(com->delta_error_counts, delta_error_counts,
-                             sizeof delta_error_counts);
-                       bzero(com->delta_error_counts,
-                             sizeof delta_error_counts);
-                       enable_intr();
                        for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
                        for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
-                               delta = delta_error_counts[errnum];
-                               if (delta != 0) {
-                                       total =
-                                       com->error_counts[errnum] += delta;
+                               disable_intr();
+                               delta = com->delta_error_counts[errnum];
+                               com->delta_error_counts[errnum] = 0;
+                               enable_intr();
+                               if (delta == 0 || !(tp->t_state & TS_ISOPEN))
+                                       continue;
+                               total = com->error_counts[errnum] += delta;
                                        log(LOG_WARNING,
                                        "sio%d: %u more %s%s (total %lu)\n",
                                            unit, delta, error_desc[errnum],
                                            delta == 1 ? "" : "s", total);
                                        log(LOG_WARNING,
                                        "sio%d: %u more %s%s (total %lu)\n",
                                            unit, delta, error_desc[errnum],
                                            delta == 1 ? "" : "s", total);
+                               if (errnum == CE_OVERRUN && com->hasfifo
+                                   && com->ftl > FIFO_TRIGGER_1) {
+                                       static  u_char ftl_in_bytes[] =
+                                               { 1, 4, 8, 14, };
+
+                                       com->ftl_init = FIFO_TRIGGER_8;
+#define        FIFO_TRIGGER_DELTA      FIFO_TRIGGER_4
+                                       com->ftl_max =
+                                       com->ftl -= FIFO_TRIGGER_DELTA;
+                                       outb(com->iobase + com_fifo,
+                                            FIFO_ENABLE | com->ftl);
+                                       log(LOG_WARNING,
+                               "sio%d: reduced fifo trigger level to %d\n",
+                                           unit,
+                                           ftl_in_bytes[com->ftl
+                                                        / FIFO_TRIGGER_DELTA]);
                                }
                        }
                }
                                }
                        }
                }
@@ -1317,7 +1419,7 @@ repeat:
                if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
                        continue;
                if (com->state & CS_RTS_IFLOW
                if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
                        continue;
                if (com->state & CS_RTS_IFLOW
-                   && RB_LEN(&tp->t_raw) + incc >= RB_I_HIGH_WATER
+                   && RB_LEN(tp->t_raw) + incc >= RB_I_HIGH_WATER
                    && !(tp->t_state & TS_RTS_IFLOW)
                    /*
                     * XXX - need RTS flow control for all line disciplines.
                    && !(tp->t_state & TS_RTS_IFLOW)
                    /*
                     * XXX - need RTS flow control for all line disciplines.
@@ -1344,7 +1446,7 @@ repeat:
                        tk_rawcc += incc;
                        tp->t_rawcc += incc;
                        com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
                        tk_rawcc += incc;
                        tp->t_rawcc += incc;
                        com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
-                               += incc - rb_write(&tp->t_raw, (char *) buf,
+                               += incc - rb_write(tp->t_raw, (char *) buf,
                                                   incc);
                        ttwakeup(tp);
                        if (tp->t_state & TS_TTSTOP
                                                   incc);
                        ttwakeup(tp);
                        if (tp->t_state & TS_TTSTOP
@@ -1354,8 +1456,7 @@ repeat:
                                tp->t_lflag &= ~FLUSHO;
                                ttstart(tp);
                        }
                                tp->t_lflag &= ~FLUSHO;
                                ttstart(tp);
                        }
-               }
-               else {
+               } else {
                        do {
                                u_char  line_status;
                                int     recv_data;
                        do {
                                u_char  line_status;
                                int     recv_data;
@@ -1381,8 +1482,6 @@ repeat:
        }
        if (com_events >= LOTS_OF_EVENTS)
                goto repeat;
        }
        if (com_events >= LOTS_OF_EVENTS)
                goto repeat;
-       splx(s);
-       awake = FALSE;
 }
 
 static int
 }
 
 static int
@@ -1403,11 +1502,11 @@ comparam(tp, t)
        divisor = ttspeedtab(t->c_ospeed, comspeedtab);
        if (t->c_ispeed == 0)
                t->c_ispeed = t->c_ospeed;
        divisor = ttspeedtab(t->c_ospeed, comspeedtab);
        if (t->c_ispeed == 0)
                t->c_ispeed = t->c_ospeed;
-       if (divisor < 0 || t->c_ispeed != t->c_ospeed)
+       if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed)
                return (EINVAL);
 
        /* parameters are OK, convert them to the com struct and the device */
                return (EINVAL);
 
        /* parameters are OK, convert them to the com struct and the device */
-       unit = UNIT(tp->t_dev);
+       unit = DEV_TO_UNIT(tp->t_dev);
        com = com_addr(unit);
        iobase = com->iobase;
        s = spltty();
        com = com_addr(unit);
        iobase = com->iobase;
        s = spltty();
@@ -1438,12 +1537,28 @@ comparam(tp, t)
        if (cflag & CSTOPB)
                cfcr |= CFCR_STOPB;
 
        if (cflag & CSTOPB)
                cfcr |= CFCR_STOPB;
 
+       if (com->hasfifo) {
+               /*
+                * Use a fifo trigger level low enough so that the input
+                * latency from the fifo is less than about 16 msec and
+                * the total latency is less than about 30 msec.  These
+                * latencies are reasonable for humans.  Serial comms
+                * protocols shouldn't expect anything better since modem
+                * latencies are larger.
+                */
+               com->ftl = t->c_ospeed <= 4800
+                          ? FIFO_TRIGGER_1 : FIFO_TRIGGER_14;
+               if (com->ftl > com->ftl_max)
+                       com->ftl = com->ftl_max;
+               outb(iobase + com_fifo, FIFO_ENABLE | com->ftl);
+       }
+
        /*
         * Some UARTs lock up if the divisor latch registers are selected
         * while the UART is doing output (they refuse to transmit anything
         * more until given a hard reset).  Fix this by stopping filling
         * the device buffers and waiting for them to drain.  Reading the
        /*
         * Some UARTs lock up if the divisor latch registers are selected
         * while the UART is doing output (they refuse to transmit anything
         * more until given a hard reset).  Fix this by stopping filling
         * the device buffers and waiting for them to drain.  Reading the
-        * line status port outside of siointr() might lose some receiver
+        * line status port outside of siointr1() might lose some receiver
         * error bits, but that is acceptable here.
         */
        disable_intr();
         * error bits, but that is acceptable here.
         */
        disable_intr();
@@ -1452,8 +1567,8 @@ retry:
        enable_intr();
        while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
               != (LSR_TSRE | LSR_TXRDY)) {
        enable_intr();
        while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
               != (LSR_TSRE | LSR_TXRDY)) {
-               error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH,
-                                "sioparam", 1);
+               error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH,
+                                "siotx", hz / 100);
                if (error != 0 && error != EAGAIN) {
                        if (!(tp->t_state & TS_TTSTOP)) {
                                disable_intr();
                if (error != 0 && error != EAGAIN) {
                        if (!(tp->t_state & TS_TTSTOP)) {
                                disable_intr();
@@ -1469,12 +1584,12 @@ retry:
 
        /*
         * XXX - clearing CS_TTGO is not sufficient to stop further output,
 
        /*
         * XXX - clearing CS_TTGO is not sufficient to stop further output,
-        * because compoll() calls comstart() which set it again because
-        * TS_TTSTOP is set.  Clearing TS_TTSTOP would not be sufficient,
-        * for similar reasons.
+        * because siopoll() calls comstart() which usually sets it again
+        * because TS_TTSTOP is clear.  Setting TS_TTSTOP would not be
+        * sufficient, for similar reasons.
         */
        if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
         */
        if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
-              != (LSR_TSRE | LSR_TXRDY))
+           != (LSR_TSRE | LSR_TXRDY))
                goto retry;
 
        if (divisor != 0) {
                goto retry;
 
        if (divisor != 0) {
@@ -1493,7 +1608,7 @@ retry:
        /*
         * Set up state to handle output flow control.
         * XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
        /*
         * Set up state to handle output flow control.
         * XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
-        * Now has 16+ msec latency, while CTS flow has 50- usec latency.
+        * Now has 10+ msec latency, while CTS flow has 50- usec latency.
         */
        com->state &= ~CS_CTS_OFLOW;
        com->state |= CS_ODEVREADY;
         */
        com->state &= ~CS_CTS_OFLOW;
        com->state |= CS_ODEVREADY;
@@ -1503,7 +1618,14 @@ retry:
                        com->state &= ~CS_ODEVREADY;
        }
 
                        com->state &= ~CS_ODEVREADY;
        }
 
-       siointr(unit);          /* recover from fiddling with CS_TTGO */
+       /*
+        * Recover from fiddling with CS_TTGO.  We used to call siointr1()
+        * unconditionally, but that defeated the careful discarding of
+        * stale input in sioopen().
+        */
+       if (com->state >= (CS_BUSY | CS_TTGO))
+               siointr1(com);
+
        enable_intr();
        splx(s);
        return (0);
        enable_intr();
        splx(s);
        return (0);
@@ -1517,7 +1639,7 @@ comstart(tp)
        int             s;
        int             unit;
 
        int             s;
        int             unit;
 
-       unit = UNIT(tp->t_dev);
+       unit = DEV_TO_UNIT(tp->t_dev);
        com = com_addr(unit);
        s = spltty();
        disable_intr();
        com = com_addr(unit);
        s = spltty();
        disable_intr();
@@ -1528,38 +1650,32 @@ comstart(tp)
        if (tp->t_state & TS_RTS_IFLOW) {
                if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
                        outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
        if (tp->t_state & TS_RTS_IFLOW) {
                if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
                        outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
-       }
-       else {
+       } else {
+               /*
+                * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off.  Set it
+                * appropriately in comparam() if RTS-flow is being changed.
+                * Check for races.
+                */
                if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater)
                        outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
        }
        enable_intr();
        if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
                goto out;
                if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater)
                        outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
        }
        enable_intr();
        if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
                goto out;
-       if (RB_LEN(&tp->t_out) <= tp->t_lowat) {
-               if (tp->t_state & TS_ASLEEP) {
-                       tp->t_state &= ~TS_ASLEEP;
-                       wakeup((caddr_t)&tp->t_out);
-               }
-               if (tp->t_wsel) {
-                       selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
-                       tp->t_wsel = 0;
-                       tp->t_state &= ~TS_WCOLL;
-               }
-       }
+       if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel)
+               ttwwakeup(tp);
        if (com->ocount != 0) {
                disable_intr();
        if (com->ocount != 0) {
                disable_intr();
-               siointr(unit);
+               siointr1(com);
                enable_intr();
                enable_intr();
-       }
-       else if (RB_LEN(&tp->t_out) != 0) {
+       } else if (RB_LEN(tp->t_out) != 0) {
                tp->t_state |= TS_BUSY;
                tp->t_state |= TS_BUSY;
-               com->ocount = RB_CONTIGGET(&tp->t_out);
+               com->ocount = RB_CONTIGGET(tp->t_out);
                disable_intr();
                disable_intr();
-               com->obufend = (com->optr = (u_char *) tp->t_out.rb_hd)
+               com->obufend = (com->optr = (u_char *)tp->t_out->rb_hd)
                              + com->ocount;
                com->state |= CS_BUSY;
                              + com->ocount;
                com->state |= CS_BUSY;
-               siointr(unit);  /* fake interrupt to start output */
+               siointr1(com);  /* fake interrupt to start output */
                enable_intr();
        }
 out:
                enable_intr();
        }
 out:
@@ -1573,7 +1689,7 @@ siostop(tp, rw)
 {
        struct com_s    *com;
 
 {
        struct com_s    *com;
 
-       com = com_addr(UNIT(tp->t_dev));
+       com = com_addr(DEV_TO_UNIT(tp->t_dev));
        if (rw & FWRITE)
                comflush(com);
        disable_intr();
        if (rw & FWRITE)
                comflush(com);
        disable_intr();
@@ -1588,6 +1704,17 @@ siostop(tp, rw)
        enable_intr();
 }
 
        enable_intr();
 }
 
+int
+sioselect(dev, rw, p)
+       dev_t           dev;
+       int             rw;
+       struct proc     *p;
+{
+       if (minor(dev) & CONTROL_MASK)
+               return (ENODEV);
+       return (ttselect(dev & ~MINOR_MAGIC_MASK, rw, p));
+}
+
 static void
 commctl(com, bits, how)
        struct com_s    *com;
 static void
 commctl(com, bits, how)
        struct com_s    *com;
@@ -1612,43 +1739,119 @@ commctl(com, bits, how)
 
 static void
 comwakeup(chan, ticks)
 
 static void
 comwakeup(chan, ticks)
-       caddr_t chan;
-       int ticks;
+       caddr_t chan;
+       int     ticks;
 {
 {
-       struct com_s    *com;
-       int             unit;
+       int     unit;
+
+       timeout(comwakeup, (caddr_t)NULL, hz / 100);
 
 
-       timeout(comwakeup, (caddr_t) NULL, hz / 100);
-       if (com_events != 0)
-               /* schedule compoll() to run when the cpl allows */
-               schedsoftcom();
+       if (com_events != 0) {
+               int     s;
+
+               s = splsofttty();
+               siopoll();
+               splx(s);
+       }
 
        /* recover from lost output interrupts */
        for (unit = 0; unit < NSIO; ++unit) {
 
        /* recover from lost output interrupts */
        for (unit = 0; unit < NSIO; ++unit) {
+               struct com_s    *com;
+
                com = com_addr(unit);
                if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) {
                        disable_intr();
                com = com_addr(unit);
                if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) {
                        disable_intr();
-                       siointr(unit);
+                       siointr1(com);
                        enable_intr();
                }
        }
                        enable_intr();
                }
        }
-       return;
-}
-
-void
-softsio1()
-{
-       compoll();
 }
 
 /*
  * Following are all routines needed for SIO to act as console
 }
 
 /*
  * Following are all routines needed for SIO to act as console
- * XXX - not tested in this version
- * XXX - i386/cons.c only knows about the com driver (NCOM and not NSIO)
- * XXX - check that the corresponding serial interrupts are never enabled
  */
 #include "i386/i386/cons.h"
 
  */
 #include "i386/i386/cons.h"
 
+struct siocnstate {
+       u_char  dlbl;
+       u_char  dlbh;
+       u_char  ier;
+       u_char  cfcr;
+       u_char  mcr;
+};
+
+static Port_t  siocniobase;
+
+static void siocnclose __P((struct siocnstate *sp));
+static void siocnopen  __P((struct siocnstate *sp));
+static void siocntxwait        __P((void));
+
+static void
+siocntxwait()
+{
+       int     timo;
+
+       /*
+        * Wait for any pending transmission to finish.  Required to avoid
+        * the UART lockup bug when the speed is changed, and for normal
+        * transmits.
+        */
+       timo = 100000;
+       while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY))
+              != (LSR_TSRE | LSR_TXRDY) && --timo != 0)
+               ;
+}
+
+static void
+siocnopen(sp)
+       struct siocnstate       *sp;
+{
+       int     divisor;
+       Port_t  iobase;
+
+       /*
+        * Save all the device control registers except the fifo register
+        * and set our default ones (cs8 -parenb speed=comdefaultrate).
+        * We can't save the fifo register since it is read-only.
+        */
+       iobase = siocniobase;
+       sp->ier = inb(iobase + com_ier);
+       outb(iobase + com_ier, 0);      /* spltty() doesn't stop siointr() */
+       siocntxwait();
+       sp->cfcr = inb(iobase + com_cfcr);
+       outb(iobase + com_cfcr, CFCR_DLAB);
+       sp->dlbl = inb(iobase + com_dlbl);
+       sp->dlbh = inb(iobase + com_dlbh);
+       divisor = ttspeedtab(comdefaultrate, comspeedtab);
+       outb(iobase + com_dlbl, divisor & 0xFF);
+       outb(iobase + com_dlbh, (u_int) divisor >> 8);
+       outb(iobase + com_cfcr, CFCR_8BITS);
+       sp->mcr = inb(iobase + com_mcr);
+       outb(iobase + com_mcr, MCR_DTR | MCR_RTS);
+}
+
+static void
+siocnclose(sp)
+       struct siocnstate       *sp;
+{
+       Port_t  iobase;
+
+       /*
+        * Restore the device control registers.
+        */
+       siocntxwait();
+       iobase = siocniobase;
+       outb(iobase + com_cfcr, CFCR_DLAB);
+       outb(iobase + com_dlbl, sp->dlbl);
+       outb(iobase + com_dlbh, sp->dlbh);
+       outb(iobase + com_cfcr, sp->cfcr);
+       /*
+        * XXX damp oscllations of MCR_DTR and MCR_RTS by not restoring them.
+        */
+       outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS);
+       outb(iobase + com_ier, sp->ier);
+}
+
 void
 siocnprobe(cp)
        struct consdev  *cp;
 void
 siocnprobe(cp)
        struct consdev  *cp;
@@ -1656,20 +1859,19 @@ siocnprobe(cp)
        int     unit;
 
        /* locate the major number */
        int     unit;
 
        /* locate the major number */
+       /* XXX - should be elsewhere since KGDB uses it */
        for (commajor = 0; commajor < nchrdev; commajor++)
                if (cdevsw[commajor].d_open == sioopen)
                        break;
 
        /* XXX: ick */
        for (commajor = 0; commajor < nchrdev; commajor++)
                if (cdevsw[commajor].d_open == sioopen)
                        break;
 
        /* XXX: ick */
-       unit = UNIT(CONUNIT);
-       com_addr(unit) = &com_structs[unit];
-       com_addr(unit)->iobase = CONADDR;
+       unit = DEV_TO_UNIT(CONUNIT);
+       siocniobase = CONADDR;
 
        /* make sure hardware exists?  XXX */
 
        /* initialize required fields */
        cp->cn_dev = makedev(commajor, unit);
 
        /* make sure hardware exists?  XXX */
 
        /* initialize required fields */
        cp->cn_dev = makedev(commajor, unit);
-       cp->cn_tp = &sio_tty[unit];
 #ifdef COMCONSOLE
        cp->cn_pri = CN_REMOTE;         /* Force a serial port console */
 #else
 #ifdef COMCONSOLE
        cp->cn_pri = CN_REMOTE;         /* Force a serial port console */
 #else
@@ -1681,43 +1883,11 @@ void
 siocninit(cp)
        struct consdev  *cp;
 {
 siocninit(cp)
        struct consdev  *cp;
 {
-       int     unit;
-
-       unit = UNIT(cp->cn_dev);
-       cominit(unit, comdefaultrate);
-       comconsole = unit;
-       comconsinit = TRUE;
-}
-
-static void
-cominit(unit, rate)
-       int     unit;
-       int     rate;
-{
-       Port_t  iobase;
-       int     s;
-
-       iobase = com_addr(unit)->iobase;
-       s = splhigh();
-       outb(iobase + com_cfcr, CFCR_DLAB);
-       rate = ttspeedtab(comdefaultrate, comspeedtab);
-       outb(iobase + com_dlbl, rate & 0xFF);
-       outb(iobase + com_dlbh, rate >> 8);
-       outb(iobase + com_cfcr, CFCR_8BITS);
-       outb(iobase + com_fifo,
-            FIFO_ENABLE | FIFO_TRIGGER | FIFO_RCV_RST | FIFO_XMT_RST);
-       DELAY(100);
-       (void) inb(iobase + com_lsr);
-       (void) inb(iobase + com_data);
-       (void) inb(iobase + com_msr);
-
        /*
        /*
-        * XXX - fishy to enable interrupts and then poll.
-        * It shouldn't be necessary to ready the iir.
+        * XXX can delete more comconsole stuff now that i/o routines are
+        * fairly reentrant.
         */
         */
-       outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC);
-       (void) inb(iobase + com_iir);
-       splx(s);
+       comconsole = DEV_TO_UNIT(cp->cn_dev);
 }
 
 int
 }
 
 int
@@ -1727,13 +1897,15 @@ siocngetc(dev)
        int     c;
        Port_t  iobase;
        int     s;
        int     c;
        Port_t  iobase;
        int     s;
+       struct siocnstate       sp;
 
 
-       iobase = com_addr(UNIT(dev))->iobase;
-       s = splhigh();
+       iobase = siocniobase;
+       s = spltty();
+       siocnopen(&sp);
        while (!(inb(iobase + com_lsr) & LSR_RXRDY))
                ;
        c = inb(iobase + com_data);
        while (!(inb(iobase + com_lsr) & LSR_RXRDY))
                ;
        c = inb(iobase + com_data);
-       (void) inb(iobase + com_iir);
+       siocnclose(&sp);
        splx(s);
        return (c);
 }
        splx(s);
        return (c);
 }
@@ -1743,76 +1915,15 @@ siocnputc(dev, c)
        dev_t   dev;
        int     c;
 {
        dev_t   dev;
        int     c;
 {
-       Port_t  iobase;
        int     s;
        int     s;
-       int     timo;
-
-       iobase = com_addr(UNIT(dev))->iobase;
-       s = splhigh();
-#ifdef KGDB
-       if (dev != kgdb_dev)
-#endif
-       if (!comconsinit) {
-               cominit(UNIT(dev), comdefaultrate);
-               comconsinit = TRUE;
-       }
-       /* wait for any pending transmission to finish */
-       timo = 50000;
-       while (!(inb(iobase + com_lsr) & LSR_TXRDY) && --timo)
-               ;
-       outb(iobase + com_data, c);
-       /* wait for this transmission to complete */
-       timo = 1500000;
-       while (!(inb(iobase + com_lsr) & LSR_TXRDY) && --timo)
-               ;
-       /* clear any interrupts generated by this transmission */
-       (void) inb(iobase + com_iir);
-       splx(s);
-}
+       struct siocnstate       sp;
 
 
-/*
- * XXX - sioselect() is almost a copy of ttselect().  It is required because
- * ttselect() can't determine the tty struct because stuff is encoded in the
- * high bit of the minor number.
- */
-int
-sioselect(dev, rw, p)
-       dev_t dev;
-       int rw;
-       struct proc *p;
-{
-       struct tty *tp = &sio_tty[UNIT(dev)];
-       int nread;
-       int s = spltty();
-        struct proc *selp;
-
-       switch (rw) {
-
-       case FREAD:
-               nread = ttnread(tp);
-               if (nread > 0 ||
-                  ((tp->t_cflag&CLOCAL) == 0 && (tp->t_state&TS_CARR_ON) == 0))
-                       goto win;
-               if (tp->t_rsel && (selp = pfind(tp->t_rsel)) && selp->p_wchan == (caddr_t)&selwait)
-                       tp->t_state |= TS_RCOLL;
-               else
-                       tp->t_rsel = p->p_pid;
-               break;
-
-       case FWRITE:
-               if (RB_LEN(&tp->t_out) <= tp->t_lowat)
-                       goto win;
-               if (tp->t_wsel && (selp = pfind(tp->t_wsel)) && selp->p_wchan == (caddr_t)&selwait)
-                       tp->t_state |= TS_WCOLL;
-               else
-                       tp->t_wsel = p->p_pid;
-               break;
-       }
-       splx(s);
-       return (0);
-  win:
+       s = spltty();
+       siocnopen(&sp);
+       siocntxwait();
+       outb(siocniobase + com_data, c);
+       siocnclose(&sp);
        splx(s);
        splx(s);
-       return (1);
 }
 
 #endif /* NSIO > 0 */
 }
 
 #endif /* NSIO > 0 */