From Ache
[unix-history] / sys / i386 / isa / sio.c
index b09a702..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.43 1994/04/03 11:41:11 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 */
 
-#ifdef COM_BIDIR
-#define        CALLOUT(x)              (minor(x) & COM_CALLOUT_MASK)
-#define        COM_CALLOUT_MASK        0x80
-#define        COM_MINOR_MAGIC_MASK    0x80
-#else /* COM_BIDIR */
-#define        COM_MINOR_MAGIC_MASK    0
-#endif /* COM_BIDIR */
-
-#define        UNIT(x)                 (minor(x) & ~COM_MINOR_MAGIC_MASK)
+#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"
 
 #ifdef COM_MULTIPORT
 /* checks in flags for multiport and which is multiport "master chip"
  */
 #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 */
 
 #define        COM_NOFIFO(dev) ((dev)->id_flags & 0x02)
 #endif /* COM_MULTIPORT */
 
 #define        COM_NOFIFO(dev) ((dev)->id_flags & 0x02)
-
-#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_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) */
 
-#ifndef setsofttty
-#define        OLD_INTERRUPT_HANDLING  /* XXX FreeBSD-1.1 and earlier */
-#define        setsofttty()    (ipending |= 1 << 4)    /* XXX requires owning IRQ4 */
-extern u_int           ipending;       /* XXX */
-void   softsio1        __P((void));
-#endif
-
 /*
  * Input buffer watermarks.
  * The external device is asked to stop sending when the buffer exactly reaches
 /*
  * Input buffer watermarks.
  * The external device is asked to stop sending when the buffer exactly reaches
@@ -132,17 +115,19 @@ void      softsio1        __P((void));
  *     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
@@ -161,20 +146,19 @@ typedef u_char    bool_t;         /* boolean */
 /* com device structure */
 struct com_s {
        u_char  state;          /* miscellaneous flag bits */
 /* 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   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
@@ -185,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 */
@@ -203,6 +188,14 @@ 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;
 #ifdef TIOCTIMESTAMP
        bool_t  do_timestamp;
        struct timeval  timestamp;
@@ -255,6 +248,7 @@ void        siocnprobe      __P((struct consdev *cp));
 void   siocnputc       __P((Dev_t dev, int c));
 
 static int     sioattach       __P((struct isa_device *dev));
 void   siocnputc       __P((Dev_t dev, int c));
 
 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    siointr1        __P((struct com_s *com));
 static void    comflush        __P((struct com_s *com));
 static void    comhardclose    __P((struct com_s *com));
 static void    siointr1        __P((struct com_s *com));
@@ -269,8 +263,6 @@ static      int     tiocm_xxx2mcr   __P((int tiocm_xxx));
 static struct com_s    *p_com_addr[NSIO];
 #define        com_addr(unit)  (p_com_addr[unit])
 
 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
 #ifdef TIOCTIMESTAMP
 static  struct timeval intr_timestamp;
 #endif
@@ -287,18 +279,8 @@ static     int     comconsole = -1;
 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;
-#ifdef DONT_MALLOC_TTYS
-#define TB_OUT(tp)     (&(tp)->t_out)
-#define TB_RAW(tp)     (&(tp)->t_raw)
-struct tty     sio_tty[NSIO];
-#else
-#define TB_OUT(tp)     ((tp)->t_out)
-#define TB_RAW(tp)     ((tp)->t_raw)
 struct tty     *sio_tty[NSIO];
 struct tty     *sio_tty[NSIO];
-#endif
-extern struct tty      *constty;
-extern int     tk_nin;         /* XXX */
-extern int     tk_rawcc;       /* XXX */
+extern struct tty      *constty;       /* XXX */
 
 #ifdef KGDB
 #include "machine/remote-sl.h"
 
 #ifdef KGDB
 #include "machine/remote-sl.h"
@@ -339,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) {
@@ -347,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
@@ -355,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.
@@ -367,55 +391,115 @@ 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;
-       if (result == 0)
-               outb(iobase + com_mcr, 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);
 }
 
@@ -431,7 +515,9 @@ sioattach(isdp)
 
        iobase = isdp->id_iobase;
        unit = isdp->id_unit;
 
        iobase = isdp->id_iobase;
        unit = isdp->id_unit;
-       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:
@@ -440,14 +526,12 @@ 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];       /* XXX malloc it */
+       bzero(com, sizeof *com);
        com->cfcr_image = CFCR_8BITS;
        com->cfcr_image = CFCR_8BITS;
-       com->mcr_image = MCR_IENABLE;
        com->dtr_wait = 3 * hz;
        com->tx_fifo_size = 1;
        com->iptr = com->ibuf = com->ibuf1;
        com->dtr_wait = 3 * hz;
        com->tx_fifo_size = 1;
        com->iptr = com->ibuf = com->ibuf1;
@@ -457,11 +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;
-#ifdef DONT_MALLOC_TTYS
-       com->tp = &sio_tty[unit];
-#endif
+
+       /*
+        * 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->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);
@@ -502,6 +605,7 @@ sioattach(isdp)
                        printf(" fifo disabled");
                else {
                        com->hasfifo = TRUE;
                        printf(" fifo disabled");
                else {
                        com->hasfifo = TRUE;
+                       com->ftl_init = FIFO_TRIGGER_14;
                        com->tx_fifo_size = 16;
                }
                break;
                        com->tx_fifo_size = 16;
                }
                break;
@@ -511,28 +615,18 @@ 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 {
                        int divisor;
                        kgdb_dev = -1;  /* can't debug over console port */
                else {
                        int divisor;
@@ -564,10 +658,11 @@ determined_type: ;
        }
 #endif
 
        }
 #endif
 
+       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);
@@ -581,227 +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;
-       bool_t          got_status = FALSE;
+       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 */
-
-#ifdef DONT_MALLOC_TTYS
-       tp = com->tp;
-#else
-       sio_tty[unit] = ttymalloc(sio_tty[unit]);
-       tp = com->tp = sio_tty[unit];
-#endif
+       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:
-       got_status = FALSE;
-       /* 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) {
-                                   /* 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;
-                               }
-                       }
-                       disable_intr();
-                       com->prev_modem_status =
-                       com->last_modem_status = inb(com->modem_status_port);
-                       enable_intr();
-                       got_status = TRUE;
-                       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) {
                                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 don't use all 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;
-                       tp->t_oflag = 0;
-                       tp->t_cflag = CREAD | CS8;
-#ifdef COM_BIDIR
-                       if (com->bidir && !callout)
-                               tp->t_cflag |= HUPCL;
-#endif
-                       tp->t_lflag = 0;
-                       tp->t_ispeed = tp->t_ospeed = comdefaultrate;
-                       if (unit == comconsole) {
-                               tp->t_iflag = TTYDEF_IFLAG;
-                               tp->t_oflag = TTYDEF_OFLAG;
-                               tp->t_cflag = TTYDEF_CFLAG;
-                               tp->t_lflag = TTYDEF_LFLAG;
-                       }
+               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;
+               /*
+                * XXX we should goto open_top if comparam() slept.
+                */
                ttsetwater(tp);
                iobase = com->iobase;
                if (com->hasfifo) {
                ttsetwater(tp);
                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);
-               if (!got_status)
                        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();
                        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();
-               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)TB_RAW(tp), 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:
 out:
-       if (error == 0)
-               error = (*linesw[tp->t_line].l_open)(dev, tp, 0);
        splx(s);
        splx(s);
-
-#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);
 }
 
@@ -814,10 +809,14 @@ 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;
 
        int             s;
        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;
        s = spltty();
        (*linesw[tp->t_line].l_close)(tp, flag);
        tp = com->tp;
        s = spltty();
        (*linesw[tp->t_line].l_close)(tp, flag);
@@ -837,7 +836,7 @@ comhardclose(com)
        struct tty      *tp;
        int             unit;
 
        struct tty      *tp;
        int             unit;
 
-       unit = com - &com_structs[0];
+       unit = DEV_TO_UNIT(com->tp->t_dev);
        iobase = com->iobase;
        s = spltty();
 #ifdef TIOCTIMESTAMP
        iobase = com->iobase;
        s = spltty();
 #ifdef TIOCTIMESTAMP
@@ -851,43 +850,29 @@ comhardclose(com)
        {
                outb(iobase + com_ier, 0);
                tp = com->tp;
        {
                outb(iobase + com_ier, 0);
                tp = com->tp;
-               if (tp->t_cflag & HUPCL || tp->t_state & TS_WOPEN
-#ifdef COM_BIDIR
+               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
                    /*
                     * 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.  And we shouldn't look at DCD if
-                    * CLOCAL is set (here or for the dialin device ...).
-                    * When the termios state is reinitialized for initial
-                    * opens, the correct CLOCAL bit will be
-                    * ((the bit now) & (the initial bit)).
+                    * we're not watching.
                     */
                     */
-                   || com->active_in
-                      && !(com->prev_modem_status & MSR_DCD) && !FAKE_DCD(unit)
-#endif
+                   || !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);
 }
 
@@ -897,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));
 }
 
@@ -908,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
@@ -922,6 +918,18 @@ 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
 #ifdef TIOCTIMESTAMP
 /* Interrupt routine for timekeeping purposes */
 void
@@ -940,8 +948,8 @@ siointr(unit)
 #ifndef COM_MULTIPORT
        siointr1(com_addr(unit));
 #else /* COM_MULTIPORT */
 #ifndef COM_MULTIPORT
        siointr1(com_addr(unit));
 #else /* COM_MULTIPORT */
-       bool_t          possibly_more_intrs;
        struct com_s    *com;
        struct com_s    *com;
+       bool_t          possibly_more_intrs;
 
        /*
         * Loop until there is no activity on any port.  This is necessary
 
        /*
         * Loop until there is no activity on any port.  This is necessary
@@ -990,9 +998,7 @@ siointr1(com)
                        else
                                recv_data = inb(com->data_port);
                        ++com->bytes_in;
                        else
                                recv_data = inb(com->data_port);
                        ++com->bytes_in;
-                       /* XXX reduce SLIP input latency */
-#define        FRAME_END       0xc0
-                       if (recv_data == FRAME_END)
+                       if (com->hotchar != 0 && recv_data == com->hotchar)
                                setsofttty();
 #ifdef KGDB
                        /* trap into kgdb? (XXX - needs testing and optim) */
                                setsofttty();
 #ifdef KGDB
                        /* trap into kgdb? (XXX - needs testing and optim) */
@@ -1118,26 +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));
+       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;
        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 sticky bits not yet implemented */
-       if (com->bidir && com->active_in && p->p_ucred->cr_uid != 0)
-               tp->t_cflag &= ~CLOCAL;
-#endif
-
        if (error >= 0)
                return (error);
        if (error >= 0)
                return (error);
-
        iobase = com->iobase;
        s = spltty();
        switch (cmd) {
        iobase = com->iobase;
        s = spltty();
        switch (cmd) {
@@ -1185,52 +1240,14 @@ 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);
-               }
-
-               /* if it's the console, can't do it (XXX why?) */
-               if (UNIT(dev) == comconsole) {
-                       splx(s);
-                       return(ENOTTY);
+                       return (EPERM);
                }
                }
-               com->dtr_wait = *(int *)data;
+               com->dtr_wait = *(int *)data * 100 / hz;
                break;
        case TIOCMGDTRWAIT:
                *(int *)data = com->dtr_wait;
                break;
        case TIOCMGDTRWAIT:
                *(int *)data = com->dtr_wait;
@@ -1261,7 +1278,7 @@ 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 = TB_OUT(com->tp);
+       rbp = com->tp->t_out;
        rbp->rb_hd += com->ocount;
        rbp->rb_hd = RB_ROLLOVER(rbp, rbp->rb_hd);
        com->ocount = 0;
        rbp->rb_hd += com->ocount;
        rbp->rb_hd = RB_ROLLOVER(rbp, rbp->rb_hd);
        com->ocount = 0;
@@ -1271,26 +1288,10 @@ comflush(com)
 void
 siopoll()
 {
 void
 siopoll()
 {
-#ifdef OLD_INTERRUPT_HANDLING
-       static bool_t   awake = FALSE;
-       int             s;
-#endif
        int             unit;
 
        if (com_events == 0)
                return;
        int             unit;
 
        if (com_events == 0)
                return;
-
-#ifdef OLD_INTERRUPT_HANDLING
-       disable_intr();
-       if (awake) {
-               enable_intr();
-               return;
-       }
-       awake = TRUE;
-       enable_intr();
-       s = spltty();
-#endif
-
 repeat:
        for (unit = 0; unit < NSIO; ++unit) {
                u_char          *buf;
 repeat:
        for (unit = 0; unit < NSIO; ++unit) {
                u_char          *buf;
@@ -1303,16 +1304,25 @@ repeat:
                if (com == NULL)
                        continue;
                tp = com->tp;
                if (com == NULL)
                        continue;
                tp = com->tp;
-#ifdef DONT_MALLOC_TTYS
                if (tp == NULL)
                        continue;
                if (tp == NULL)
                        continue;
-#endif
 
                /* switch the role of the low-level input buffers */
                if (com->iptr == (ibuf = com->ibuf)) {
                        buf = NULL;     /* not used, but compiler can't tell */
                        incc = 0;
                } else {
 
                /* switch the role of the low-level input buffers */
                if (com->iptr == (ibuf = com->ibuf)) {
                        buf = NULL;     /* not used, but compiler can't tell */
                        incc = 0;
                } 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;
@@ -1356,15 +1366,9 @@ 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 */
                }
 
                /* XXX */
@@ -1378,13 +1382,29 @@ repeat:
                                delta = com->delta_error_counts[errnum];
                                com->delta_error_counts[errnum] = 0;
                                enable_intr();
                                delta = com->delta_error_counts[errnum];
                                com->delta_error_counts[errnum] = 0;
                                enable_intr();
-                               if (delta != 0) {
-                                       total =
-                                       com->error_counts[errnum] += delta;
+                               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]);
                                }
                        }
                }
                                }
                        }
                }
@@ -1399,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(TB_RAW(tp)) + 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.
@@ -1426,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(TB_RAW(tp), (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
@@ -1462,11 +1482,6 @@ repeat:
        }
        if (com_events >= LOTS_OF_EVENTS)
                goto repeat;
        }
        if (com_events >= LOTS_OF_EVENTS)
                goto repeat;
-
-#ifdef OLD_INTERRUPT_HANDLING
-       splx(s);
-       awake = FALSE;
-#endif
 }
 
 static int
 }
 
 static int
@@ -1487,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();
@@ -1522,6 +1537,22 @@ 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
        /*
         * Some UARTs lock up if the divisor latch registers are selected
         * while the UART is doing output (they refuse to transmit anything
@@ -1536,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)TB_RAW(tp), 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();
@@ -1577,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;
@@ -1591,9 +1622,6 @@ retry:
         * Recover from fiddling with CS_TTGO.  We used to call siointr1()
         * unconditionally, but that defeated the careful discarding of
         * stale input in sioopen().
         * Recover from fiddling with CS_TTGO.  We used to call siointr1()
         * unconditionally, but that defeated the careful discarding of
         * stale input in sioopen().
-        *
-        * XXX sioopen() is not careful waiting for carrier for the callout
-        * case.
         */
        if (com->state >= (CS_BUSY | CS_TTGO))
                siointr1(com);
         */
        if (com->state >= (CS_BUSY | CS_TTGO))
                siointr1(com);
@@ -1611,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();
@@ -1634,26 +1662,17 @@ comstart(tp)
        enable_intr();
        if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
                goto out;
        enable_intr();
        if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
                goto out;
-       if (RB_LEN(TB_OUT(tp)) <= tp->t_lowat) {
-               if (tp->t_state & TS_ASLEEP) {
-                       tp->t_state &= ~TS_ASLEEP;
-                       wakeup((caddr_t)TB_OUT(tp));
-               }
-               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();
                siointr1(com);
                enable_intr();
        if (com->ocount != 0) {
                disable_intr();
                siointr1(com);
                enable_intr();
-       } else if (RB_LEN(TB_OUT(tp)) != 0) {
+       } else if (RB_LEN(tp->t_out) != 0) {
                tp->t_state |= TS_BUSY;
                tp->t_state |= TS_BUSY;
-               com->ocount = RB_CONTIGGET(TB_OUT(tp));
+               com->ocount = RB_CONTIGGET(tp->t_out);
                disable_intr();
                disable_intr();
-               com->obufend = (com->optr = (u_char *)TB_OUT(tp)->rb_hd)
+               com->obufend = (com->optr = (u_char *)tp->t_out->rb_hd)
                              + com->ocount;
                com->state |= CS_BUSY;
                siointr1(com);  /* fake interrupt to start output */
                              + com->ocount;
                com->state |= CS_BUSY;
                siointr1(com);  /* fake interrupt to start output */
@@ -1670,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();
@@ -1691,7 +1710,9 @@ sioselect(dev, rw, p)
        int             rw;
        struct proc     *p;
 {
        int             rw;
        struct proc     *p;
 {
-       return (ttselect(dev & ~COM_MINOR_MAGIC_MASK, rw, p));
+       if (minor(dev) & CONTROL_MASK)
+               return (ENODEV);
+       return (ttselect(dev & ~MINOR_MAGIC_MASK, rw, p));
 }
 
 static void
 }
 
 static void
@@ -1723,16 +1744,14 @@ comwakeup(chan, ticks)
 {
        int     unit;
 
 {
        int     unit;
 
-       timeout(comwakeup, (caddr_t) NULL, hz / 100);
+       timeout(comwakeup, (caddr_t)NULL, hz / 100);
 
        if (com_events != 0) {
 
        if (com_events != 0) {
-#ifndef OLD_INTERRUPT_HANDLING
-               int     s = splsofttty();
-#endif
+               int     s;
+
+               s = splsofttty();
                siopoll();
                siopoll();
-#ifndef OLD_INTERRUPT_HANDLING
                splx(s);
                splx(s);
-#endif
        }
 
        /* recover from lost output interrupts */
        }
 
        /* recover from lost output interrupts */
@@ -1748,14 +1767,6 @@ comwakeup(chan, ticks)
        }
 }
 
        }
 }
 
-#ifdef OLD_INTERRUPT_HANDLING
-void
-softsio1()
-{
-       siopoll();
-}
-#endif
-
 /*
  * Following are all routines needed for SIO to act as console
  */
 /*
  * Following are all routines needed for SIO to act as console
  */
@@ -1771,6 +1782,10 @@ struct siocnstate {
 
 static Port_t  siocniobase;
 
 
 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()
 {
 static void
 siocntxwait()
 {
@@ -1831,7 +1846,7 @@ siocnclose(sp)
        outb(iobase + com_dlbh, sp->dlbh);
        outb(iobase + com_cfcr, sp->cfcr);
        /*
        outb(iobase + com_dlbh, sp->dlbh);
        outb(iobase + com_cfcr, sp->cfcr);
        /*
-        * XXX damp osicllations of MCR_DTR or MCR_RTS by not restoring them.
+        * 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);
         */
        outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS);
        outb(iobase + com_ier, sp->ier);
@@ -1850,7 +1865,7 @@ siocnprobe(cp)
                        break;
 
        /* XXX: ick */
                        break;
 
        /* XXX: ick */
-       unit = UNIT(CONUNIT);
+       unit = DEV_TO_UNIT(CONUNIT);
        siocniobase = CONADDR;
 
        /* make sure hardware exists?  XXX */
        siocniobase = CONADDR;
 
        /* make sure hardware exists?  XXX */
@@ -1872,7 +1887,7 @@ siocninit(cp)
         * XXX can delete more comconsole stuff now that i/o routines are
         * fairly reentrant.
         */
         * XXX can delete more comconsole stuff now that i/o routines are
         * fairly reentrant.
         */
-       comconsole = UNIT(cp->cn_dev);
+       comconsole = DEV_TO_UNIT(cp->cn_dev);
 }
 
 int
 }
 
 int