z8530 reorganization from gwr (which i think isn't nearly as machine
authorderaadt <deraadt@openbsd.org>
Wed, 31 Jan 1996 07:01:06 +0000 (07:01 +0000)
committerderaadt <deraadt@openbsd.org>
Wed, 31 Jan 1996 07:01:06 +0000 (07:01 +0000)
independent or correct as he thinks it is)

12 files changed:
sys/dev/ic/z8530.doc [new file with mode: 0644]
sys/dev/ic/z8530reg.h
sys/dev/ic/z8530sc.c [new file with mode: 0644]
sys/dev/ic/z8530sc.h [new file with mode: 0644]
sys/dev/ic/z8530tty.c [new file with mode: 0644]
sys/dev/sun/event.c [new file with mode: 0644]
sys/dev/sun/event_var.h [new file with mode: 0644]
sys/dev/sun/kbd.c [new file with mode: 0644]
sys/dev/sun/kbd_tables.c [new file with mode: 0644]
sys/dev/sun/kbd_tables.h [new file with mode: 0644]
sys/dev/sun/kbd_xlate.h [new file with mode: 0644]
sys/dev/sun/ms.c [new file with mode: 0644]

diff --git a/sys/dev/ic/z8530.doc b/sys/dev/ic/z8530.doc
new file mode 100644 (file)
index 0000000..ba0c5af
--- /dev/null
@@ -0,0 +1,76 @@
+$NetBSD: z8530.doc,v 1.1 1996/01/24 01:07:21 gwr Exp $
+
+Here are the results of my reorganization work on the zs driver.
+Everything compiles and appears to work.
+
+Enjoy!
+Gordon Ross
+
+----------------------------------------------------------------
+This design has a "zs controller" named "zsc" with three
+flavors of child drivers that can be attached above it.
+The three child drivers are:
+       zstty:  normal RS-232 line
+       kbd:    sun keyboard
+       ms:     sun mouse
+
+Note that the machine-dependent parts are:
+       arch/sun3/include/z8530var.h
+       arch/sun3/dev/zs.c
+(replicate those for new systems)
+
+The Sun hardware dependent parts (Sun3 and SPARC) are:
+       kd*     sun keyboard/display console stuff
+       event*  sun "firm event" stuff
+
+----------------------------------------------------------------
+Here is how the autoconfig looks on a sun3:
+
+zsc0 at obio0 addr 0x0 level 6 softpri 3
+kbd0 at zsc0 channel 0
+ms0 at zsc0 channel 1
+zsc1 at obio0 addr 0x20000 level 6 softpri 3
+zstty0 at zsc1 channel 0 (console)
+zstty1 at zsc1 channel 1
+
+
+----------------------------------------------------------------
+Config lines:
+
+In any config file (i.e. GENERIC, NEW_ZS, ...)
+Note {kbd,ms} are sun-specific.
+
+#
+# New console (zs) stuff
+#
+zsc0 at obio? addr ?
+zsc1 at obio? addr ?
+kbd0 at zsc0 channel 0
+ms0  at zsc0 channel 1
+zstty* at zsc? channel ?
+
+
+----------------------------------------------------------------
+In conf/files.sun3
+
+#
+# Console (zs) related stuff
+#
+device zsc at obio {channel = -1}
+file arch/sun3/dev/zs.c                zsc needs-flag
+file dev/ic/z8530sc.c                  zsc
+
+device zstty at zsc: tty
+file dev/ic/z8530tty.c                 zstty needs-flag
+
+define zsevent
+file dev/sun/event.c                   zsevent
+
+device kbd at zsc: zsevent
+file dev/sun/kbd.c                     kbd needs-flag
+file dev/sun/kbd_tables.c              kbd
+file arch/sun3/dev/kd.c                kbd
+
+device ms at zsc: zsevent
+file dev/sun/ms.c                      ms needs-flag
+
index 30abf62..a973bd6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $NetBSD: z8530reg.h,v 1.4 1995/08/20 13:24:05 leo Exp $ */
+/*     $NetBSD: z8530reg.h,v 1.5 1996/01/24 19:21:40 gwr Exp $ */
 
 /*
  * Copyright (c) 1992, 1993
  * differently for the two channels.  We can, however, ignore this much
  * of the time.
  */
-#ifndef LOCORE
+#if 0  /* Example only! */
+/*
+ * The layout of this structure is hardware-dependent!
+ * Define these in some machine-dependent place.
+ */
 struct zschan {
-#if (BYTE_ORDER == BIG_ENDIAN) && !defined(atari)
-       volatile u_char zc_csr;         /* ctrl,status, and indirect access */
-       u_char          zc_xxx0;
-       volatile u_char zc_data;        /* data */
-       u_char          zc_xxx1;
-#else
-       u_char          zc_xxx0;
-       volatile u_char zc_csr;         /* ctrl,status, and indirect access */
-       u_char          zc_xxx1;
-       volatile u_char zc_data;        /* data */
-#endif
+       volatile u_char zc_csr;         /* ctrl, status, or reg. number */
+       volatile u_char zc_data;        /* data or numbered register */
 };
-
 struct zsdevice {
        struct  zschan zs_chan[2];
 };
-#endif
+#endif /* Example only! */
 
 /*
  * Some of the names in this files were chosen to make the hsis driver
diff --git a/sys/dev/ic/z8530sc.c b/sys/dev/ic/z8530sc.c
new file mode 100644 (file)
index 0000000..7b7ef11
--- /dev/null
@@ -0,0 +1,326 @@
+/*     $NetBSD: z8530sc.c,v 1.1 1996/01/24 01:07:23 gwr Exp $  */
+
+/*
+ * Copyright (c) 1994 Gordon W. Ross
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)zs.c        8.1 (Berkeley) 7/19/93
+ */
+
+/*
+ * Zilog Z8530 Dual UART driver (common part)
+ *
+ * This file contains the machine-independent parts of the
+ * driver common to tty and keyboard/mouse sub-drivers.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <dev/ic/z8530reg.h>
+#include <machine/z8530var.h>
+
+int
+zs_break(cs, set)
+       struct zs_chanstate *cs;
+       int set;
+{
+       int s;
+
+       s = splzs();
+       if (set) {
+               cs->cs_preg[5] |= ZSWR5_BREAK;
+               cs->cs_creg[5] |= ZSWR5_BREAK;
+       } else {
+               cs->cs_preg[5] &= ~ZSWR5_BREAK;
+               cs->cs_creg[5] &= ~ZSWR5_BREAK;
+       }
+       ZS_WRITE(cs, 5, cs->cs_creg[5]);
+       splx(s);
+}
+
+
+/*
+ * Compute the current baud rate given a ZSCC channel.
+ */
+int
+zs_getspeed(cs)
+       struct zs_chanstate *cs;
+{
+       int tconst;
+
+       tconst = ZS_READ(cs, 12);
+       tconst |= ZS_READ(cs, 13) << 8;
+       return (TCONST_TO_BPS(cs->cs_pclk_div16, tconst));
+}
+
+/*
+ * drain on-chip fifo
+ */
+void
+zs_iflush(cs)
+       struct zs_chanstate *cs;
+{
+       u_char c, rr0, rr1;
+
+       for (;;) {
+               /* Is there input available? */
+               rr0 = *(cs->cs_reg_csr);
+               ZS_DELAY();
+               if ((rr0 & ZSRR0_RX_READY) == 0)
+                       break;
+
+               /* Read the data. */
+               c = *(cs->cs_reg_data);
+               ZS_DELAY();
+
+               /* Need to read status register too? */
+               rr1 = ZS_READ(cs, 1);
+               if (rr1 & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) {
+                       /* Clear the receive error. */
+                       *(cs->cs_reg_csr) = ZSWR0_RESET_ERRORS;
+                       ZS_DELAY();
+               }
+       }
+}
+       
+
+/*
+ * Write the given register set to the given zs channel in the proper order.
+ * The channel must not be transmitting at the time.  The receiver will
+ * be disabled for the time it takes to write all the registers.
+ * Call this with interrupts disabled.
+ */
+void
+zs_loadchannelregs(cs)
+       struct zs_chanstate *cs;
+{
+       u_char *reg;
+       int i;
+
+       /* Copy "pending" regs to "current" */
+       bcopy((caddr_t)cs->cs_preg, (caddr_t)cs->cs_creg, 16);
+       reg = cs->cs_creg;      /* current regs */
+
+       *(cs->cs_reg_csr) = ZSM_RESET_ERR;      /* XXX: reset error condition */
+       ZS_DELAY();
+
+#if 1
+       /*
+        * XXX: Is this really a good idea?
+        * XXX: Should go elsewhere! -gwr
+        */
+       zs_iflush(cs);  /* XXX */
+#endif
+
+       /* baud clock divisor, stop bits, parity */
+       ZS_WRITE(cs, 4, reg[4]);
+
+       /* misc. TX/RX control bits */
+       ZS_WRITE(cs, 10, reg[10]);
+
+       /* char size, enable (RX/TX) */
+       ZS_WRITE(cs, 3, reg[3] & ~ZSWR3_RX_ENABLE);
+       ZS_WRITE(cs, 5, reg[5] & ~ZSWR5_TX_ENABLE);
+
+       /* interrupt enables: TX, TX, STATUS */
+       ZS_WRITE(cs, 1, reg[1]);
+
+#if 0
+       /*
+        * Registers 2 and 9 are special because they are
+        * actually common to both channels, but must be
+        * programmed through channel A.  The "zsc" attach
+        * function takes care of setting these registers
+        * and they should not be touched thereafter.
+        */
+       /* interrupt vector */
+       ZS_WRITE(cs, 2, reg[2]);
+       /* master interrupt control */
+       ZS_WRITE(cs, 9, reg[9]);
+#endif
+
+       /* clock mode control */
+       ZS_WRITE(cs, 11, reg[11]);
+
+       /* baud rate (lo/hi) */
+       ZS_WRITE(cs, 12, reg[12]);
+       ZS_WRITE(cs, 13, reg[13]);
+
+       /* Misc. control bits */
+       ZS_WRITE(cs, 14, reg[14]);
+
+       /* which lines cause status interrupts */
+       ZS_WRITE(cs, 15, reg[15]);
+
+       /* char size, enable (RX/TX)*/
+       ZS_WRITE(cs, 3, reg[3]);
+       ZS_WRITE(cs, 5, reg[5]);
+}
+
+
+/*
+ * ZS hardware interrupt.  Scan all ZS channels.  NB: we know here that
+ * channels are kept in (A,B) pairs.
+ *
+ * Do just a little, then get out; set a software interrupt if more
+ * work is needed.
+ *
+ * We deliberately ignore the vectoring Zilog gives us, and match up
+ * only the number of `reset interrupt under service' operations, not
+ * the order.
+ */
+int
+zsc_intr_hard(arg)
+       void *arg;
+{
+       register struct zsc_softc *zsc = arg;
+       register struct zs_chanstate *cs_a;
+       register struct zs_chanstate *cs_b;
+       register int rval, soft;
+       register u_char rr3;
+
+       cs_a = &zsc->zsc_cs[0];
+       cs_b = &zsc->zsc_cs[1];
+       rval = 0;
+       soft = 0;
+
+       /* Note: only channel A has an RR3 */
+       rr3 = ZS_READ(cs_a, 3);
+
+       /* Handle receive interrupts first. */
+       if (rr3 & ZSRR3_IP_A_RX)
+               (*cs_a->cs_ops->zsop_rxint)(cs_a);
+       if (rr3 & ZSRR3_IP_B_RX)
+               (*cs_b->cs_ops->zsop_rxint)(cs_b);
+
+       /* Handle transmit done interrupts. */
+       if (rr3 & ZSRR3_IP_A_TX)
+               (*cs_a->cs_ops->zsop_txint)(cs_a);
+       if (rr3 & ZSRR3_IP_B_TX)
+               (*cs_b->cs_ops->zsop_txint)(cs_b);
+
+       /* Handle status interrupts. */
+       if (rr3 & ZSRR3_IP_A_STAT)
+               (*cs_a->cs_ops->zsop_stint)(cs_a);
+       if (rr3 & ZSRR3_IP_B_STAT)
+               (*cs_b->cs_ops->zsop_stint)(cs_b);
+
+       /* Clear interrupt. */
+       if (rr3 & (ZSRR3_IP_A_RX | ZSRR3_IP_A_TX | ZSRR3_IP_A_STAT)) {
+               *(cs_a->cs_reg_csr) = ZSWR0_CLR_INTR;
+               ZS_DELAY();
+               rval |= 1;
+       }
+       if (rr3 & (ZSRR3_IP_B_RX | ZSRR3_IP_B_TX | ZSRR3_IP_B_STAT)) {
+               *(cs_b->cs_reg_csr) = ZSWR0_CLR_INTR;
+               ZS_DELAY();
+               rval |= 2;
+       }
+
+       if ((cs_a->cs_softreq) || (cs_b->cs_softreq))
+       {
+               /* This is a machine-dependent function. */
+               zsc_req_softint(zsc);
+       }
+
+       return (rval);
+}
+
+
+/*
+ * ZS software interrupt.  Scan all channels for deferred interrupts.
+ */
+int
+zsc_intr_soft(arg)
+       void *arg;
+{
+       register struct zsc_softc *zsc = arg;
+       register struct zs_chanstate *cs;
+       register int req, rval, s, unit;
+
+       rval = 0;
+       for (unit = 0; unit < 2; unit++) {
+               cs = &zsc->zsc_cs[unit];
+
+               s = splzs();
+               req = cs->cs_softreq;
+               cs->cs_softreq = 0;
+               splx(s);
+
+               if (req) {
+                       (*cs->cs_ops->zsop_softint)(cs);
+                       rval = 1;
+               }
+       }
+       return (rval);
+}
+
+
+static int
+zsnull_intr(cs)
+       struct zs_chanstate *cs;
+{
+       ZS_WRITE(cs,  1, 0);
+       ZS_WRITE(cs, 15, 0);
+}
+
+static int
+zsnull_softint(cs)
+       struct zs_chanstate *cs;
+{
+}
+
+struct zsops zsops_null = {
+       zsnull_intr,    /* receive char available */
+       zsnull_intr,    /* external/status */
+       zsnull_intr,    /* xmit buffer empty */
+       zsnull_softint, /* process software interrupt */
+};
diff --git a/sys/dev/ic/z8530sc.h b/sys/dev/ic/z8530sc.h
new file mode 100644 (file)
index 0000000..57ec9c4
--- /dev/null
@@ -0,0 +1,111 @@
+/*     $NetBSD: z8530sc.h,v 1.1 1996/01/24 01:07:24 gwr Exp $  */
+
+/*
+ * Copyright (c) 1994 Gordon W. Ross
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)zsvar.h     8.1 (Berkeley) 6/11/93
+ */
+
+
+/*
+ * Function vector - per channel
+ */
+struct zsops {
+       int     (*zsop_rxint)();        /* receive char available */
+       int     (*zsop_stint)();        /* external/status */
+       int     (*zsop_txint)();        /* xmit buffer empty */
+       int     (*zsop_softint)();      /* process software interrupt */
+};
+
+extern struct zsops zsops_null;
+
+
+/*
+ * Software state, per zs channel.
+ */
+struct zs_chanstate {
+
+       /* Pointers to the device registers. */
+       volatile u_char *cs_reg_csr;    /* ctrl, status, and reg. number. */
+       volatile u_char *cs_reg_data;   /* data or numbered register */
+
+       int     cs_channel;             /* sub-unit number */
+       void *cs_private;       /* sub-driver data pointer */
+       struct zsops *cs_ops;
+
+       int cs_pclk_div16;              /* PCLK / 16 */
+       int     cs_defspeed;            /* default baud rate (from PROM) */
+
+       /*
+        * We must keep a copy of the write registers as they are
+        * mostly write-only and we sometimes need to set and clear
+        * individual bits (e.g., in WR3).  Not all of these are
+        * needed but 16 bytes is cheap and this makes the addressing
+        * simpler.  Unfortunately, we can only write to some registers
+        * when the chip is not actually transmitting, so whenever
+        * we are expecting a `transmit done' interrupt the preg array
+        * is allowed to `get ahead' of the current values.  In a
+        * few places we must change the current value of a register,
+        * rather than (or in addition to) the pending value; for these
+        * cs_creg[] contains the current value.
+        */
+       u_char  cs_creg[16];            /* current values */
+       u_char  cs_preg[16];            /* pending values */
+
+       u_char  cs_heldchange;          /* change pending (creg != preg) */
+       u_char  cs_rr0;                 /* last rr0 processed */
+
+       char    cs_softreq;             /* need soft interrupt call */
+       char    cs__spare;
+};
+
+struct zsc_softc {
+       struct  device zsc_dev;         /* required first: base device */
+       struct  zs_chanstate zsc_cs[2]; /* channel A and B soft state */
+};
+
+struct zsc_attach_args {
+       int channel;    /* two serial channels per zsc */
+       int hwflags;
+};
+#define ZS_HWFLAG_CONSOLE 1
+
diff --git a/sys/dev/ic/z8530tty.c b/sys/dev/ic/z8530tty.c
new file mode 100644 (file)
index 0000000..276ceb0
--- /dev/null
@@ -0,0 +1,1042 @@
+/*     $NetBSD: z8530tty.c,v 1.1 1996/01/24 01:07:25 gwr Exp $ */
+
+/*
+ * Copyright (c) 1994 Gordon W. Ross
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)zs.c        8.1 (Berkeley) 7/19/93
+ */
+
+/*
+ * Zilog Z8530 Dual UART driver (tty interface)
+ *
+ * This is the "slave" driver that will be attached to
+ * the "zsc" driver for plain "tty" async. serial lines.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <dev/ic/z8530reg.h>
+#include <machine/z8530var.h>
+
+#ifdef KGDB
+extern int zs_check_kgdb();
+#endif
+
+/*
+ * Allow the MD var.h to override the default CFLAG so that
+ * console messages during boot come out with correct parity.
+ */
+#ifndef        ZSTTY_DEF_CFLAG
+#define        ZSTTY_DEF_CFLAG TTYDEF_CFLAG
+#endif
+
+/*
+ * How many input characters we can buffer.
+ * The port-specific var.h may override this.
+ * Note: must be a power of two!
+ */
+#ifndef        ZSTTY_RING_SIZE
+#define        ZSTTY_RING_SIZE 1024
+#endif
+#define ZSTTY_RING_MASK (ZSTTY_RING_SIZE-1)
+
+struct zstty_softc {
+       struct  device zst_dev;         /* required first: base device */
+       struct  tty *zst_tty;
+       struct  zs_chanstate *zst_cs;
+
+       int zst_hwflags;        /* see z8530var.h */
+       int zst_swflags;        /* TIOCFLAG_SOFTCAR, ... <ttycom.h> */
+
+       /* Flags to communicate with zstty_softint() */
+       volatile int zst_intr_flags;
+#define        INTR_RX_OVERRUN 1
+#define INTR_TX_EMPTY   2
+#define INTR_ST_CHECK   4
+
+       /*
+        * The transmit byte count and address are used for pseudo-DMA
+        * output in the hardware interrupt code.  PDMA can be suspended
+        * to get pending changes done; heldtbc is used for this.  It can
+        * also be stopped for ^S; this sets TS_TTSTOP in tp->t_state.
+        */
+       int     zst_tbc;                        /* transmit byte count */
+       caddr_t zst_tba;                        /* transmit buffer address */
+       int     zst_heldtbc;            /* held tbc while xmission stopped */
+
+       /*
+        * Printing an overrun error message often takes long enough to
+        * cause another overrun, so we only print one per second.
+        */
+       long    zst_rotime;             /* time of last ring overrun */
+       long    zst_fotime;             /* time of last fifo overrun */
+
+       /*
+        * The receive ring buffer.
+        */
+       u_int   zst_rbget;      /* ring buffer `get' index */
+       volatile u_int  zst_rbput;      /* ring buffer `put' index */
+       u_short zst_rbuf[ZSTTY_RING_SIZE]; /* rr1, data pairs */
+};
+
+
+/* Definition of the driver for autoconfig. */
+static int     zstty_match(struct device *, void *, void *);
+static void    zstty_attach(struct device *, struct device *, void *);
+
+struct cfdriver zsttycd = {
+       NULL, "zstty", zstty_match, zstty_attach,
+       DV_TTY, sizeof(struct zstty_softc), NULL,
+};
+
+struct zsops zsops_tty;
+
+/* Routines called from other code. */
+cdev_decl(zs); /* open, close, read, write, ioctl, stop, ... */
+
+static void    zsstart(struct tty *);
+static int     zsparam(struct tty *, struct termios *);
+static void zs_modem(struct zstty_softc *zst, int onoff);
+
+/*
+ * zstty_match: how is this zs channel configured?
+ */
+int 
+zstty_match(parent, match, aux)
+       struct device *parent;
+       void   *match, *aux;
+{
+       struct cfdata *cf = match;
+       struct zsc_attach_args *args = aux;
+
+       /* Exact match is better than wildcard. */
+       if (cf->cf_loc[0] == args->channel)
+               return 2;
+
+       /* This driver accepts wildcard. */
+       if (cf->cf_loc[0] == -1)
+               return 1;
+
+       return 0;
+}
+
+void 
+zstty_attach(parent, self, aux)
+       struct device *parent, *self;
+       void   *aux;
+
+{
+       struct zsc_softc *zsc = (void *) parent;
+       struct zstty_softc *zst = (void *) self;
+       struct zsc_attach_args *args = aux;
+       struct zs_chanstate *cs;
+       struct cfdata *cf;
+       struct tty *tp;
+       int channel, tty_unit;
+       dev_t dev;
+
+       cf = zst->zst_dev.dv_cfdata;
+       tty_unit = cf->cf_unit;
+       channel = args->channel;
+       cs = &zsc->zsc_cs[channel];
+       cs->cs_private = zst;
+       cs->cs_ops = &zsops_tty;
+
+       zst->zst_cs = cs;
+       zst->zst_swflags = cf->cf_flags;        /* softcar, etc. */
+       zst->zst_hwflags = args->hwflags;
+       dev = makedev(ZSTTY_MAJOR, tty_unit);
+
+       if (zst->zst_swflags)
+               printf(" flags 0x%x", zst->zst_swflags);
+
+       if (zst->zst_hwflags & ZS_HWFLAG_CONSOLE)
+               printf(" (console)");
+       else {
+#ifdef KGDB
+               /*
+                * Allow kgdb to "take over" this port.  If this port is
+                * NOT the kgdb port, zs_check_kgdb() will return zero.
+                * If it IS the kgdb port, it will print "kgdb,...\n"
+                * and then return non-zero.
+                */
+               if (zs_check_kgdb(cs, dev)) {
+                       /*
+                        * This is the kgdb port (exclusive use)
+                        * so skip the normal attach code.
+                        */
+                       return;
+               }
+#endif
+       }
+       printf("\n");
+
+       tp = zst->zst_tty = ttymalloc();
+       tp->t_dev = dev;
+       tp->t_oproc = zsstart;
+       tp->t_param = zsparam;
+       tp->t_sc = zst;         /* XXX - Quick access! */
+
+       /*
+        * Hardware init
+        */
+       if (zst->zst_hwflags & ZS_HWFLAG_CONSOLE) {
+               /* This unit is the console. */
+               zst->zst_swflags |= TIOCFLAG_SOFTCAR;
+               /* Call _param so interrupts get enabled. */
+               cs->cs_defspeed = zs_getspeed(cs);
+               tp->t_ispeed = cs->cs_defspeed;
+               tp->t_ospeed = cs->cs_defspeed;
+               tp->t_cflag = ZSTTY_DEF_CFLAG;
+               (void) zsparam(tp, &tp->t_termios);
+       } else {
+               /* Not the console; may need reset. */
+               int reset, s;
+               reset = (channel == 0) ?
+                       ZSWR9_A_RESET : ZSWR9_B_RESET;
+               s = splzs();
+               ZS_WRITE(cs, 9, reset);
+               splx(s);
+       }
+
+       /*
+        * Initialize state of modem control lines (DTR).
+        * If softcar is set, turn on DTR now and leave it.
+        * otherwise, turn off DTR now, and raise in open.
+        * (Keeps modem from answering too early.)
+        */
+       zs_modem(zst, (zst->zst_swflags & TIOCFLAG_SOFTCAR) ? 1 : 0);
+}
+
+
+/*
+ * Return pointer to our tty.
+ */
+struct tty *
+zstty(dev)
+       dev_t dev;
+{
+       struct zstty_softc *zst;
+       int unit = minor(dev);
+
+#ifdef DIAGNOSTIC
+       if (unit >= zsttycd.cd_ndevs)
+               panic("zstty");
+#endif
+       zst = zsttycd.cd_devs[unit];
+       return (zst->zst_tty);
+}
+
+
+/*
+ * Open a zs serial (tty) port.
+ */
+int
+zsopen(dev, flags, mode, p)
+       dev_t dev;
+       int flags;
+       int mode;
+       struct proc *p;
+{
+       register struct tty *tp;
+       register struct zs_chanstate *cs;
+       struct zstty_softc *zst;
+       int error, s, unit;
+
+       unit = minor(dev);
+       if (unit >= zsttycd.cd_ndevs)
+               return (ENXIO);
+       zst = zsttycd.cd_devs[unit];
+       if (zst == NULL)
+               return (ENXIO);
+       tp = zst->zst_tty;
+       cs = zst->zst_cs;
+
+       /* If KGDB took the line, then tp==NULL */
+       if (tp == NULL)
+               return (EBUSY);
+
+       /* It's simpler to do this up here. */
+       if (((tp->t_state & (TS_ISOPEN | TS_XCLUDE))
+            ==             (TS_ISOPEN | TS_XCLUDE))
+           && (p->p_ucred->cr_uid != 0) )
+       {
+               return (EBUSY);
+       }
+
+       s = spltty();
+
+       if ((tp->t_state & TS_ISOPEN) == 0) {
+               /* First open. */
+               ttychars(tp);
+               tp->t_iflag = TTYDEF_IFLAG;
+               tp->t_oflag = TTYDEF_OFLAG;
+               tp->t_cflag = ZSTTY_DEF_CFLAG;
+               if (zst->zst_swflags & TIOCFLAG_CLOCAL)
+                       tp->t_cflag |= CLOCAL;
+               if (zst->zst_swflags & TIOCFLAG_CRTSCTS)
+                       tp->t_cflag |= CRTSCTS;
+               if (zst->zst_swflags & TIOCFLAG_MDMBUF)
+                       tp->t_cflag |= MDMBUF;
+               tp->t_lflag = TTYDEF_LFLAG;
+               tp->t_ispeed = tp->t_ospeed = cs->cs_defspeed;
+               (void) zsparam(tp, &tp->t_termios);
+               ttsetwater(tp);
+               /* Flush any pending input. */
+               zst->zst_rbget = zst->zst_rbput;
+               zs_iflush(cs);  /* XXX */
+               /* Turn on DTR */
+               zs_modem(zst, 1);
+               if (zst->zst_swflags & TIOCFLAG_SOFTCAR) {
+                       tp->t_state |= TS_CARR_ON;
+               }
+       }
+       error = 0;
+
+       /* Wait for carrier. */
+       for (;;) {
+               register int rr0;
+
+               /* Might never get status intr if carrier already on. */
+               rr0 = *(cs->cs_reg_csr);
+               ZS_DELAY();
+               if (rr0 & ZSRR0_DCD) {
+                       tp->t_state |= TS_CARR_ON;
+                       break;
+               }
+
+               if ((tp->t_state & TS_CARR_ON) ||
+                   (tp->t_cflag & CLOCAL) ||
+                   (flags & O_NONBLOCK) )
+               {
+                       break;
+               }
+
+               tp->t_state |= TS_WOPEN;
+               error = ttysleep(tp, (caddr_t)&tp->t_rawq,
+                       TTIPRI | PCATCH, ttopen, 0);
+               if (error) {
+                       if ((tp->t_state & TS_ISOPEN) == 0) {
+                               /* Never get here with softcar */
+                               zs_modem(zst, 0);
+                               tp->t_state &= ~TS_WOPEN;
+                               ttwakeup(tp);
+                       }
+                       break;
+               }
+       }
+
+       splx(s);
+
+       if (error == 0)
+               error = linesw[tp->t_line].l_open(dev, tp);
+
+       return (error);
+}
+
+/*
+ * Close a zs serial port.
+ */
+int
+zsclose(dev, flags, mode, p)
+       dev_t dev;
+       int flags;
+       int mode;
+       struct proc *p;
+{
+       struct zstty_softc *zst;
+       register struct zs_chanstate *cs;
+       register struct tty *tp;
+       struct zsinfo *zi;
+       int hup, s;
+
+       zst = zsttycd.cd_devs[minor(dev)];
+       cs = zst->zst_cs;
+       tp = zst->zst_tty;
+
+       /* XXX This is for cons.c. */
+       if ((tp->t_state & TS_ISOPEN) == 0)
+               return 0;
+
+       (*linesw[tp->t_line].l_close)(tp, flags);
+       hup = tp->t_cflag & HUPCL;
+       if (zst->zst_swflags & TIOCFLAG_SOFTCAR)
+               hup = 0;
+       if (hup) {
+               zs_modem(zst, 0);
+               /* hold low for 1 second */
+               (void) tsleep((caddr_t)cs, TTIPRI, ttclos, hz);
+       }
+       if (cs->cs_creg[5] & ZSWR5_BREAK) {
+               zs_break(cs, 0);
+       }
+       /* XXX - turn off interrupts? */
+
+       ttyclose(tp);
+       return (0);
+}
+
+/*
+ * Read/write zs serial port.
+ */
+int
+zsread(dev, uio, flags)
+       dev_t dev;
+       struct uio *uio;
+       int flags;
+{
+       register struct zstty_softc *zst;
+       register struct tty *tp;
+
+       zst = zsttycd.cd_devs[minor(dev)];
+       tp = zst->zst_tty;
+       return (linesw[tp->t_line].l_read(tp, uio, flags));
+}
+
+int
+zswrite(dev, uio, flags)
+       dev_t dev;
+       struct uio *uio;
+       int flags;
+{
+       register struct zstty_softc *zst;
+       register struct tty *tp;
+
+       zst = zsttycd.cd_devs[minor(dev)];
+       tp = zst->zst_tty;
+       return (linesw[tp->t_line].l_write(tp, uio, flags));
+}
+
+#define TIOCFLAG_ALL (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | \
+                      TIOCFLAG_CRTSCTS | TIOCFLAG_MDMBUF )
+
+int
+zsioctl(dev, cmd, data, flag, p)
+       dev_t dev;
+       u_long cmd;
+       caddr_t data;
+       int flag;
+       struct proc *p;
+{
+       register struct zstty_softc *zst;
+       register struct zs_chanstate *cs;
+       register struct tty *tp;
+       register int error, tmp;
+
+       zst = zsttycd.cd_devs[minor(dev)];
+       cs = zst->zst_cs;
+       tp = zst->zst_tty;
+
+       error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
+       if (error >= 0)
+               return (error);
+       error = ttioctl(tp, cmd, data, flag, p);
+       if (error >= 0)
+               return (error);
+
+       switch (cmd) {
+
+       case TIOCSBRK:
+               zs_break(cs, 1);
+               break;
+
+       case TIOCCBRK:
+               zs_break(cs, 0);
+               break;
+
+       case TIOCGFLAGS:
+               *(int *)data = zst->zst_swflags;
+               break;
+
+       case TIOCSFLAGS:
+               error = suser(p->p_ucred, &p->p_acflag);
+               if (error != 0)
+                       return (EPERM);
+               tmp = *(int *)data;
+               /* Check for random bits... */
+               if (tmp & ~TIOCFLAG_ALL)
+                       return(EINVAL);
+               /* Silently enforce softcar on the console. */
+               if (zst->zst_hwflags & ZS_HWFLAG_CONSOLE)
+                       tmp |= TIOCFLAG_SOFTCAR;
+               /* These flags take effect during open. */
+               zst->zst_swflags = tmp;
+               break;
+
+       case TIOCSDTR:
+               zs_modem(zst, 1);
+               break;
+
+       case TIOCCDTR:
+               zs_modem(zst, 0);
+               break;
+
+       case TIOCMSET:
+       case TIOCMBIS:
+       case TIOCMBIC:
+       case TIOCMGET:
+       default:
+               return (ENOTTY);
+       }
+       return (0);
+}
+
+/*
+ * Start or restart transmission.
+ */
+static void
+zsstart(tp)
+       register struct tty *tp;
+{
+       register struct zstty_softc *zst;
+       register struct zs_chanstate *cs;
+       register int s, nch;
+
+       zst = tp->t_sc;
+       cs = zst->zst_cs;
+
+       s = spltty();
+
+       /*
+        * If currently active or delaying, no need to do anything.
+        */
+       if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))
+               goto out;
+
+       /*
+        * If there are sleepers, and output has drained below low
+        * water mark, awaken.
+        */
+       if (tp->t_outq.c_cc <= tp->t_lowat) {
+               if (tp->t_state & TS_ASLEEP) {
+                       tp->t_state &= ~TS_ASLEEP;
+                       wakeup((caddr_t)&tp->t_outq);
+               }
+               selwakeup(&tp->t_wsel);
+       }
+
+       nch = ndqb(&tp->t_outq, 0);     /* XXX */
+       if (nch) {
+               register char *p = tp->t_outq.c_cf;
+
+               /* mark busy, enable tx done interrupts, & send first byte */
+               tp->t_state |= TS_BUSY;
+               (void) splzs();
+
+               cs->cs_preg[1] |= ZSWR1_TIE;
+               cs->cs_creg[1] |= ZSWR1_TIE;
+               ZS_WRITE(cs, 1, cs->cs_creg[1]);
+               *(cs->cs_reg_data) = *p;
+               ZS_DELAY();
+               zst->zst_tba = p + 1;
+               zst->zst_tbc = nch - 1;
+       } else {
+               /*
+                * Nothing to send, turn off transmit done interrupts.
+                * This is useful if something is doing polled output.
+                */
+               (void) splzs();
+               cs->cs_preg[1] &= ~ZSWR1_TIE;
+               cs->cs_creg[1] &= ~ZSWR1_TIE;
+               ZS_WRITE(cs, 1, cs->cs_creg[1]);
+       }
+out:
+       splx(s);
+}
+
+/*
+ * Stop output, e.g., for ^S or output flush.
+ */
+int
+zsstop(tp, flag)
+       struct tty *tp;
+       int flag;
+{
+       register struct zstty_softc *zst;
+       register struct zs_chanstate *cs;
+       register int s;
+
+       zst = tp->t_sc;
+       cs = zst->zst_cs;
+
+       s = splzs();
+       if (tp->t_state & TS_BUSY) {
+               /*
+                * Device is transmitting; must stop it.
+                */
+               zst->zst_tbc = 0;
+               if ((tp->t_state & TS_TTSTOP) == 0)
+                       tp->t_state |= TS_FLUSH;
+       }
+       splx(s);
+       return (0);
+}
+
+/*
+ * Set ZS tty parameters from termios.
+ * XXX - Should just copy the whole termios after
+ * making sure all the changes could be done.
+ * XXX - Only whack the UART when params change...
+ */
+static int
+zsparam(tp, t)
+       register struct tty *tp;
+       register struct termios *t;
+{
+       register struct zstty_softc *zst;
+       register struct zs_chanstate *cs;
+       register int s, bps, cflag, tconst;
+       u_char tmp3, tmp4, tmp5, reset;
+
+       zst = tp->t_sc;
+       cs = zst->zst_cs;
+
+       /*
+        * Because PCLK is only run at 4.9 MHz, the fastest we
+        * can go is 51200 baud (this corresponds to TC=1).
+        * This is somewhat unfortunate as there is no real
+        * reason we should not be able to handle higher rates.
+        */
+       bps = t->c_ospeed;
+       if (bps < 0 || (t->c_ispeed && t->c_ispeed != bps))
+               return (EINVAL);
+       if (bps == 0) {
+               /* stty 0 => drop DTR and RTS */
+               zs_modem(zst, 0);
+               return (0);
+       }
+       tconst = BPS_TO_TCONST(cs->cs_pclk_div16, bps);
+       if (tconst < 0)
+               return (EINVAL);
+
+       /* Convert back to make sure we can do it. */
+       bps = TCONST_TO_BPS(cs->cs_pclk_div16, tconst);
+       if (bps != t->c_ospeed)
+               return (EINVAL);
+       tp->t_ispeed = tp->t_ospeed = bps;
+
+       cflag = t->c_cflag;
+       tp->t_cflag = cflag;
+
+       /*
+        * Block interrupts so that state will not
+        * be altered until we are done setting it up.
+        */
+       s = splzs();
+
+       /*
+        * Initial values in cs_preg are set before
+        * our attach routine is called.  The master
+        * interrupt enable is handled by zsc.c
+        */
+
+       cs->cs_preg[12] = tconst;
+       cs->cs_preg[13] = tconst >> 8;
+
+       switch (cflag & CSIZE) {
+       case CS5:
+               tmp3 = ZSWR3_RX_5;
+               tmp5 = ZSWR5_TX_5;
+               break;
+       case CS6:
+               tmp3 = ZSWR3_RX_6;
+               tmp5 = ZSWR5_TX_6;
+               break;
+       case CS7:
+               tmp3 = ZSWR3_RX_7;
+               tmp5 = ZSWR5_TX_7;
+               break;
+       case CS8:
+       default:
+               tmp3 = ZSWR3_RX_8;
+               tmp5 = ZSWR5_TX_8;
+               break;
+       }
+
+       /*
+        * Output hardware flow control on the chip is horrendous: if
+        * carrier detect drops, the receiver is disabled.  Hence we
+        * can only do this when the carrier is on.
+        */
+       tmp3 |= ZSWR3_RX_ENABLE;
+       if (cflag & CCTS_OFLOW) {
+               if (*(cs->cs_reg_csr) & ZSRR0_DCD)
+                       tmp3 |= ZSWR3_HFC;
+               ZS_DELAY();
+       }
+
+       cs->cs_preg[3] = tmp3;
+       cs->cs_preg[5] = tmp5 | ZSWR5_TX_ENABLE | ZSWR5_DTR | ZSWR5_RTS;
+
+       tmp4 = ZSWR4_CLK_X16 | (cflag & CSTOPB ? ZSWR4_TWOSB : ZSWR4_ONESB);
+       if ((cflag & PARODD) == 0)
+               tmp4 |= ZSWR4_EVENP;
+       if (cflag & PARENB)
+               tmp4 |= ZSWR4_PARENB;
+       cs->cs_preg[4] = tmp4;
+
+       /*
+        * If nothing is being transmitted, set up new current values,
+        * else mark them as pending.
+        */
+       if (cs->cs_heldchange == 0) {
+               if (tp->t_state & TS_BUSY) {
+                       zst->zst_heldtbc = zst->zst_tbc;
+                       zst->zst_tbc = 0;
+                       cs->cs_heldchange = 1;
+               } else {
+                       zs_loadchannelregs(cs);
+               }
+       }
+       splx(s);
+       return (0);
+}
+
+/*
+ * Raise or lower modem control (DTR/RTS) signals.  If a character is
+ * in transmission, the change is deferred.
+ */
+static void
+zs_modem(zst, onoff)
+       struct zstty_softc *zst;
+       int onoff;
+{
+       struct zs_chanstate *cs;
+       struct tty *tp;
+       int s, bis, and;
+
+       cs = zst->zst_cs;
+       tp = zst->zst_tty;
+
+       if (onoff) {
+               bis = ZSWR5_DTR | ZSWR5_RTS;
+               and = ~0;
+       } else {
+               bis = 0;
+               and = ~(ZSWR5_DTR | ZSWR5_RTS);
+       }
+       s = splzs();
+       cs->cs_preg[5] = (cs->cs_preg[5] | bis) & and;
+       if (cs->cs_heldchange == 0) {
+               if (tp->t_state & TS_BUSY) {
+                       zst->zst_heldtbc = zst->zst_tbc;
+                       zst->zst_tbc = 0;
+                       cs->cs_heldchange = 1;
+               } else {
+                       cs->cs_creg[5] = (cs->cs_creg[5] | bis) & and;
+                       ZS_WRITE(cs, 5, cs->cs_creg[5]);
+               }
+       }
+       splx(s);
+}
+
+
+/****************************************************************
+ * Interface to the lower layer (zscc)
+ ****************************************************************/
+
+static int
+zstty_rxint(cs)
+       register struct zs_chanstate *cs;
+{
+       register struct zstty_softc *zst;
+       register put, put_next;
+       register u_char c, rr0, rr1;
+
+       zst = cs->cs_private;
+       put = zst->zst_rbput;
+
+nextchar:
+       /* Read the input data ASAP. */
+       c = *(cs->cs_reg_data);
+       ZS_DELAY();
+
+       /* Save the status register too. */
+       rr1 = ZS_READ(cs, 1);
+
+       if (rr1 & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) {
+               /* Clear the receive error. */
+               *(cs->cs_reg_csr) = ZSWR0_RESET_ERRORS;
+               ZS_DELAY();
+       }
+
+       zst->zst_rbuf[put] = (c << 8) | rr1;
+       put_next = (put + 1) & ZSTTY_RING_MASK;
+
+       /* Would overrun if increment makes (put==get). */
+       if (put_next == zst->zst_rbget) {
+               zst->zst_intr_flags |= INTR_RX_OVERRUN;
+       } else {
+               /* OK, really increment. */
+               put = put_next;
+       }
+
+       /* Keep reading until the FIFO is empty. */
+       rr0 = *(cs->cs_reg_csr);
+       ZS_DELAY();
+       if (rr0 & ZSRR0_RX_READY)
+               goto nextchar;
+
+       /* Done reading. */
+       zst->zst_rbput = put;
+
+       /* Ask for softint() call. */
+       cs->cs_softreq = 1;
+       return(1);
+}
+
+static int
+zstty_txint(cs)
+       register struct zs_chanstate *cs;
+{
+       register struct zstty_softc *zst;
+       register int count, rval;
+
+       zst = cs->cs_private;
+       count = zst->zst_tbc;
+
+       if (count > 0) {
+               /* Send the next char. */
+               *(cs->cs_reg_data) = *zst->zst_tba++;
+               ZS_DELAY();
+               zst->zst_tbc = --count;
+               rval = 0;
+       } else {
+               /* Nothing more to send. */
+               *(cs->cs_reg_csr) = ZSWR0_RESET_TXINT;
+               ZS_DELAY();
+               zst->zst_intr_flags |= INTR_TX_EMPTY;
+               rval = 1;       /* want softcall */
+       }
+
+       cs->cs_softreq = rval;
+       return (rval);
+}
+
+static int
+zstty_stint(cs)
+       register struct zs_chanstate *cs;
+{
+       register struct zstty_softc *zst;
+       register int rr0;
+
+       zst = cs->cs_private;
+
+       rr0 = *(cs->cs_reg_csr);
+       ZS_DELAY();
+
+       *(cs->cs_reg_csr) = ZSWR0_RESET_STATUS;
+       ZS_DELAY();
+
+       if ((rr0 & ZSRR0_BREAK) && 
+               (zst->zst_hwflags & ZS_HWFLAG_CONSOLE))
+       {
+               zs_abort();
+               return (0);
+       }
+
+       zst->zst_intr_flags |= INTR_ST_CHECK;
+       /* Ask for softint() call. */
+       cs->cs_softreq = 1;
+       return (1);
+}
+
+/*
+ * Print out a ring or fifo overrun error message.
+ */
+static void
+zsoverrun(zst, ptime, what)
+       struct zstty_softc *zst;
+       long *ptime;
+       char *what;
+{
+
+       if (*ptime != time.tv_sec) {
+               *ptime = time.tv_sec;
+               log(LOG_WARNING, "%s: %s overrun\n",
+                       zst->zst_dev.dv_xname, what);
+       }
+}
+
+static int
+zstty_softint(cs)
+       struct zs_chanstate *cs;
+{
+       register struct zstty_softc *zst;
+       register struct linesw *line;
+       register struct tty *tp;
+       register int get, c, s;
+       int intr_flags;
+       register u_short ring_data;
+       register u_char rr0, rr1;
+
+       zst  = cs->cs_private;
+       tp   = zst->zst_tty;
+       line = &linesw[tp->t_line];
+
+       /* Atomically get and clear flags. */
+       s = splzs();
+       intr_flags = zst->zst_intr_flags;
+       zst->zst_intr_flags = 0;
+       splx(s);
+
+       if (intr_flags & INTR_RX_OVERRUN) {
+               /* May turn this on again below. */
+               intr_flags &= ~INTR_RX_OVERRUN;
+               zsoverrun(zst, "ring");
+       }
+
+       /*
+        * Copy data from the receive ring into the tty layer.
+        */
+       get = zst->zst_rbget;
+       while (get != zst->zst_rbput) {
+               ring_data = zst->zst_rbuf[get];
+               get = (get + 1) & ZSTTY_RING_MASK;
+
+               if (ring_data & ZSRR1_DO)
+                       intr_flags |= INTR_RX_OVERRUN;
+               /* low byte of ring_data is rr1 */
+               c = (ring_data >> 8) & 0xff;
+               if (ring_data & ZSRR1_FE)
+                       c |= TTY_FE;
+               if (ring_data & ZSRR1_PE)
+                       c |= TTY_PE;
+
+               line->l_rint(c, tp);
+       }
+       zst->zst_rbget = get;
+
+       /* If set, it is from the loop above. */
+       if (intr_flags & INTR_RX_OVERRUN) {
+               zsoverrun(zst, "fifo");
+       }
+
+       if (intr_flags & INTR_TX_EMPTY) {
+               /*
+                * Transmit done.  Change registers and resume,
+                * or just clear BUSY.
+                */
+               if (cs->cs_heldchange) {
+                       s = splzs();
+                       rr0 = *(cs->cs_reg_csr);
+                       ZS_DELAY();
+                       if ((rr0 & ZSRR0_DCD) == 0)
+                               cs->cs_preg[3] &= ~ZSWR3_HFC;
+                       zs_loadchannelregs(cs);
+                       splx(s);
+                       cs->cs_heldchange = 0;
+
+                       if (zst->zst_heldtbc &&
+                               (tp->t_state & TS_TTSTOP) == 0)
+                       {
+                               zst->zst_tbc = zst->zst_heldtbc - 1;
+                               *(cs->cs_reg_data) = *zst->zst_tba++;
+                               ZS_DELAY();
+                               goto tx_resumed;
+                       }
+               }
+               tp->t_state &= ~TS_BUSY;
+               if (tp->t_state & TS_FLUSH)
+                       tp->t_state &= ~TS_FLUSH;
+               else
+                       ndflush(&tp->t_outq, zst->zst_tba -
+                                       (caddr_t) tp->t_outq.c_cf);
+               line->l_start(tp);
+       tx_resumed:
+       }
+
+       if (intr_flags & INTR_ST_CHECK) {
+               /*
+                * Status line change.
+                *
+                * The chip's hardware flow control is, as noted in zsreg.h,
+                * busted---if the DCD line goes low the chip shuts off the
+                * receiver (!).  If we want hardware CTS flow control but do
+                * not have it, and carrier is now on, turn HFC on; if we have
+                * HFC now but carrier has gone low, turn it off.
+                */
+               s = splzs();
+               rr0 = *(cs->cs_reg_csr);
+               if (rr0 & ZSRR0_DCD) {
+                       if (tp->t_cflag & CCTS_OFLOW &&
+                               (cs->cs_creg[3] & ZSWR3_HFC) == 0) {
+                               cs->cs_creg[3] |= ZSWR3_HFC;
+                               ZS_WRITE(cs, 3, cs->cs_creg[3]);
+                       }
+               } else {
+                       if (cs->cs_creg[3] & ZSWR3_HFC) {
+                               cs->cs_creg[3] &= ~ZSWR3_HFC;
+                               ZS_WRITE(cs, 3, cs->cs_creg[3]);
+                       }
+               }
+               splx(s);
+
+               /* Was there a change on DCD? */
+               if ((rr0 ^ cs->cs_rr0) & ZSRR0_DCD) {
+                       c = ((rr0 & ZSRR0_DCD) != 0);
+                       if (line->l_modem(tp, c) == 0)
+                               zs_modem(zst, c);
+               }
+               cs->cs_rr0 = rr0;
+       }
+
+       return (1);
+}
+
+struct zsops zsops_tty = {
+       zstty_rxint,    /* receive char available */
+       zstty_stint,    /* external/status */
+       zstty_txint,    /* xmit buffer empty */
+       zstty_softint,  /* process software interrupt */
+};
+
diff --git a/sys/dev/sun/event.c b/sys/dev/sun/event.c
new file mode 100644 (file)
index 0000000..0846337
--- /dev/null
@@ -0,0 +1,171 @@
+/*     $NetBSD: event.c,v 1.1.1.1 1996/01/24 01:15:34 gwr Exp $        */
+
+/*
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)event.c     8.1 (Berkeley) 6/11/93
+ */
+
+/*
+ * Internal `Firm_event' interface for the keyboard and mouse drivers.
+ */
+
+#include <sys/param.h>
+#include <sys/fcntl.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+
+#include <machine/vuid_event.h>
+#include "event_var.h"
+
+/*
+ * Initialize a firm_event queue.
+ */
+void
+ev_init(ev)
+       register struct evvar *ev;
+{
+
+       ev->ev_get = ev->ev_put = 0;
+       ev->ev_q = malloc((u_long)EV_QSIZE * sizeof(struct firm_event),
+           M_DEVBUF, M_WAITOK);
+       bzero((caddr_t)ev->ev_q, EV_QSIZE * sizeof(struct firm_event));
+}
+
+/*
+ * Tear down a firm_event queue.
+ */
+void
+ev_fini(ev)
+       register struct evvar *ev;
+{
+
+       free(ev->ev_q, M_DEVBUF);
+}
+
+/*
+ * User-level interface: read, select.
+ * (User cannot write an event queue.)
+ */
+int
+ev_read(ev, uio, flags)
+       register struct evvar *ev;
+       struct uio *uio;
+       int flags;
+{
+       int s, n, cnt, error;
+
+       /*
+        * Make sure we can return at least 1.
+        */
+       if (uio->uio_resid < sizeof(struct firm_event))
+               return (EMSGSIZE);      /* ??? */
+       s = splev();
+       while (ev->ev_get == ev->ev_put) {
+               if (flags & IO_NDELAY) {
+                       splx(s);
+                       return (EWOULDBLOCK);
+               }
+               ev->ev_wanted = 1;
+               error = tsleep((caddr_t)ev, PEVENT | PCATCH, "firm_event", 0);
+               if (error) {
+                       splx(s);
+                       return (error);
+               }
+       }
+       /*
+        * Move firm_events from tail end of queue (there is at least one
+        * there).
+        */
+       if (ev->ev_put < ev->ev_get)
+               cnt = EV_QSIZE - ev->ev_get;    /* events in [get..QSIZE) */
+       else
+               cnt = ev->ev_put - ev->ev_get;  /* events in [get..put) */
+       splx(s);
+       n = howmany(uio->uio_resid, sizeof(struct firm_event));
+       if (cnt > n)
+               cnt = n;
+       error = uiomove((caddr_t)&ev->ev_q[ev->ev_get],
+           cnt * sizeof(struct firm_event), uio);
+       n -= cnt;
+       /*
+        * If we do not wrap to 0, used up all our space, or had an error,
+        * stop.  Otherwise move from front of queue to put index, if there
+        * is anything there to move.
+        */
+       if ((ev->ev_get = (ev->ev_get + cnt) % EV_QSIZE) != 0 ||
+           n == 0 || error || (cnt = ev->ev_put) == 0)
+               return (error);
+       if (cnt > n)
+               cnt = n;
+       error = uiomove((caddr_t)&ev->ev_q[0],
+           cnt * sizeof(struct firm_event), uio);
+       ev->ev_get = cnt;
+       return (error);
+}
+
+int
+ev_select(ev, rw, p)
+       register struct evvar *ev;
+       int rw;
+       struct proc *p;
+{
+       int s = splev();
+
+       switch (rw) {
+
+       case FREAD:
+               /* succeed if there is something to read */
+               if (ev->ev_get != ev->ev_put) {
+                       splx(s);
+                       return (1);
+               }
+               selrecord(p, &ev->ev_sel);
+               break;
+
+       case FWRITE:
+               return (1);     /* always fails => never blocks */
+       }
+       splx(s);
+       return (0);
+}
diff --git a/sys/dev/sun/event_var.h b/sys/dev/sun/event_var.h
new file mode 100644 (file)
index 0000000..0f29b0d
--- /dev/null
@@ -0,0 +1,88 @@
+/*     $NetBSD: event_var.h,v 1.1.1.1 1996/01/24 01:15:34 gwr Exp $    */
+
+/*
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     from: @(#)event_var.h   8.1 (Berkeley) 6/11/93
+ *     from: Hdr: event_var.h,v 1.5 92/11/26 01:11:51 torek Exp  (LBL)
+ */
+
+/*
+ * Internal `Firm_event' interface for the keyboard and mouse drivers.
+ * The drivers are expected not to place events in the queue above spltty(),
+ * i.e., are expected to run off serial ports.
+ */
+
+/* EV_QSIZE should be a power of two so that `%' is fast */
+#define        EV_QSIZE        256     /* may need tuning; this uses 2k */
+
+struct evvar {
+       u_int   ev_get;         /* get (read) index (modified synchronously) */
+       volatile u_int ev_put;  /* put (write) index (modified by interrupt) */
+       struct  selinfo ev_sel; /* process selecting */
+       struct  proc *ev_io;    /* process that opened queue (can get SIGIO) */
+       char    ev_wanted;      /* wake up on input ready */
+       char    ev_async;       /* send SIGIO on input ready */
+       struct  firm_event *ev_q;/* circular buffer (queue) of events */
+};
+
+#define        splev() spltty()
+
+#define        EV_WAKEUP(ev) { \
+       selwakeup(&(ev)->ev_sel); \
+       if ((ev)->ev_wanted) { \
+               (ev)->ev_wanted = 0; \
+               wakeup((caddr_t)(ev)); \
+       } \
+       if ((ev)->ev_async) \
+               psignal((ev)->ev_io, SIGIO); \
+}
+
+void   ev_init __P((struct evvar *));
+void   ev_fini __P((struct evvar *));
+int    ev_read __P((struct evvar *, struct uio *, int));
+int    ev_select __P((struct evvar *, int, struct proc *));
+
+/*
+ * PEVENT is set just above PSOCK, which is just above TTIPRI, on the
+ * theory that mouse and keyboard `user' input should be quick.
+ */
+#define        PEVENT  23
diff --git a/sys/dev/sun/kbd.c b/sys/dev/sun/kbd.c
new file mode 100644 (file)
index 0000000..999663a
--- /dev/null
@@ -0,0 +1,1353 @@
+/*     $NetBSD: kbd.c,v 1.1.1.1 1996/01/24 01:15:35 gwr Exp $  */
+
+/*
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)kbd.c       8.2 (Berkeley) 10/30/93
+ */
+
+/*
+ * Keyboard driver (/dev/kbd -- note that we do not have minor numbers
+ * [yet?]).  Translates incoming bytes to ASCII or to `firm_events' and
+ * passes them up to the appropriate reader.
+ */
+
+/*
+ * Zilog Z8530 Dual UART driver (keyboard interface)
+ *
+ * This is the "slave" driver that will be attached to
+ * the "zsc" driver for a Sun keyboard.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+/* #include <sys/tty.h> */
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <dev/ic/z8530reg.h>
+#include <machine/z8530var.h>
+#include <machine/vuid_event.h>
+#include <machine/kbd.h>
+#include <machine/kbio.h>
+
+#include "event_var.h"
+#include "kbd_xlate.h"
+
+/*
+ * Ideas:
+ * /dev/kbd is not a tty (plain device)
+ */
+
+/*
+ * How many input characters we can buffer.
+ * The port-specific var.h may override this.
+ * Note: must be a power of two!
+ */
+#define        KBD_RX_RING_SIZE        256
+#define KBD_RX_RING_MASK (KBD_RX_RING_SIZE-1)
+/*
+ * Output buffer.  Only need a few chars.
+ */
+#define        KBD_TX_RING_SIZE        16
+#define KBD_TX_RING_MASK (KBD_TX_RING_SIZE-1)
+/*
+ * Keyboard serial line speed is fixed at 1200 bps.
+ */
+#define KBD_BPS 1200
+#define KBD_RESET_TIMO 1000 /* mS. */
+
+/*
+ * XXX - Historical comment - no longer quite right...
+ * Keyboard driver state.  The ascii and kbd links go up and down and
+ * we just sit in the middle doing translation.  Note that it is possible
+ * to get just one of the two links, in which case /dev/kbd is unavailable.
+ * The downlink supplies us with `internal' open and close routines which
+ * will enable dataflow across the downlink.  We promise to call open when
+ * we are willing to take keystrokes, and to call close when we are not.
+ * If /dev/kbd is not the console tty input source, we do this whenever
+ * /dev/kbd is in use; otherwise we just leave it open forever.
+ */
+struct kbd_softc {
+       struct  device k_dev;           /* required first: base device */
+       struct  zs_chanstate *k_cs;
+
+       /* Flags to communicate with kbd_softint() */
+       volatile int k_intr_flags;
+#define        INTR_RX_OVERRUN 1
+#define INTR_TX_EMPTY   2
+#define INTR_ST_CHECK   4
+
+       /* Transmit state */
+       volatile int k_txflags;
+#define        K_TXBUSY 1
+#define K_TXWANT 2
+
+       /*
+        * State of upper interface.
+        */
+       int     k_isopen;               /* set if open has been done */
+       int     k_evmode;               /* set if we should produce events */
+       struct  evvar k_events;         /* event queue state */
+
+       /*
+        * ACSI translation state
+        */
+       int k_repeat_start;     /* initial delay */
+       int k_repeat_step;      /* inter-char delay */
+       int     k_repeatsym;            /* repeating symbol */
+       int     k_repeating;            /* we've called timeout() */
+       struct  kbd_state k_state;      /* ASCII translation state */
+
+       /*
+        * Magic sequence stuff (L1-A)
+        */
+       char k_isconsole;
+       char k_magic1_down;
+       u_char k_magic1;        /* L1 */
+       u_char k_magic2;        /* A */
+
+       /*
+        * The transmit ring buffer.
+        */
+       volatile u_int  k_tbget;        /* transmit buffer `get' index */
+       volatile u_int  k_tbput;        /* transmit buffer `put' index */
+       u_char  k_tbuf[KBD_TX_RING_SIZE]; /* data */
+
+       /*
+        * The receive ring buffer.
+        */
+       u_int   k_rbget;        /* ring buffer `get' index */
+       volatile u_int  k_rbput;        /* ring buffer `put' index */
+       u_short k_rbuf[KBD_RX_RING_SIZE]; /* rr1, data pairs */
+
+};
+
+/* Prototypes */
+void   kbd_ascii(struct tty *);
+void   kbd_serial(struct tty *, void (*)(), void (*)());
+int    kbd_iopen(int unit);
+void   kbd_was_reset(struct kbd_softc *);
+void   kbd_new_layout(struct kbd_softc *);
+void   kbd_rint(int);
+int    kbd_docmd(struct kbd_softc *k, int cmd);
+void   kbd_output(struct kbd_softc *k, int c);
+void   kbd_start_tx(struct kbd_softc *k);
+void   kbd_repeat(void *arg);
+
+extern void kd_input(int ascii);
+
+cdev_decl(kbd);        /* open, close, read, write, ioctl, stop, ... */
+
+struct zsops zsops_kbd;
+
+/****************************************************************
+ * Definition of the driver for autoconfig.
+ ****************************************************************/
+
+static int     kbd_match(struct device *, void *, void *);
+static void    kbd_attach(struct device *, struct device *, void *);
+
+struct cfdriver kbdcd = {
+       NULL, "kbd", kbd_match, kbd_attach,
+       DV_DULL, sizeof(struct kbd_softc), NULL,
+};
+
+
+/*
+ * kbd_match: how is this zs channel configured?
+ */
+int 
+kbd_match(parent, match, aux)
+       struct device *parent;
+       void   *match, *aux;
+{
+       struct cfdata *cf = match;
+       struct zsc_attach_args *args = aux;
+
+       /* Exact match required for keyboard. */
+       if (cf->cf_loc[0] == args->channel)
+               return 2;
+
+       return 0;
+}
+
+void 
+kbd_attach(parent, self, aux)
+       struct device *parent, *self;
+       void   *aux;
+
+{
+       struct zsc_softc *zsc = (void *) parent;
+       struct kbd_softc *k = (void *) self;
+       struct zsc_attach_args *args = aux;
+       struct zs_chanstate *cs;
+       struct cfdata *cf;
+       int channel, kbd_unit;
+       int reset, s, tconst;
+
+       cf = k->k_dev.dv_cfdata;
+       kbd_unit = cf->cf_unit;
+       channel = args->channel;
+       cs = &zsc->zsc_cs[channel];
+       cs->cs_private = k;
+       cs->cs_ops = &zsops_kbd;
+       k->k_cs = cs;
+
+       if (args->hwflags & ZS_HWFLAG_CONSOLE) {
+               k->k_isconsole = 1;
+               printf(" (console)");
+       }
+       printf("\n");
+
+       /* Initialize the speed, etc. */
+       tconst = BPS_TO_TCONST(cs->cs_pclk_div16, KBD_BPS);
+       s = splzs();
+       if (k->k_isconsole == 0) {
+               /* Not the console; may need reset. */
+               reset = (channel == 0) ?
+                       ZSWR9_A_RESET : ZSWR9_B_RESET;
+               ZS_WRITE(cs, 9, reset);
+       }
+       /* These are OK as set by zscc: WR3, WR4, WR5 */
+       cs->cs_preg[5] |= ZSWR5_DTR | ZSWR5_RTS;
+       cs->cs_preg[12] = tconst;
+       cs->cs_preg[13] = tconst >> 8;
+       zs_loadchannelregs(cs);
+       splx(s);
+
+       /* Do this before any calls to kbd_rint(). */
+       kbd_xlate_init(&k->k_state);
+
+       /* XXX - Do this in open? */
+       k->k_repeat_start = hz/2;
+       k->k_repeat_step = hz/20;
+
+       /* Magic sequence. */
+       k->k_magic1 = KBD_L1;
+       k->k_magic2 = KBD_A;
+
+       /* Now attach the (kd) pseudo-driver. */
+       kd_init(kbd_unit);
+}
+
+
+/****************************************************************
+ *  Entry points for /dev/kbd
+ *  (open,close,read,write,...)
+ ****************************************************************/
+
+/*
+ * Open:
+ * Check exclusion, open actual device (_iopen),
+ * setup event channel, clear ASCII repeat stuff.
+ */
+int
+kbdopen(dev, flags, mode, p)
+       dev_t dev;
+       int flags, mode;
+       struct proc *p;
+{
+       struct kbd_softc *k;
+       int error, s, unit;
+
+       unit = minor(dev);
+       if (unit >= kbdcd.cd_ndevs)
+               return (ENXIO);
+       k = kbdcd.cd_devs[unit];
+       if (k == NULL)
+               return (ENXIO);
+
+       /* Exclusive open required for /dev/kbd */
+       if (k->k_events.ev_io)
+               return (EBUSY);
+       k->k_events.ev_io = p;
+
+       if ((error = kbd_iopen(unit)) != 0) {
+               k->k_events.ev_io = NULL;
+               return (error);
+       }
+       ev_init(&k->k_events);
+       k->k_evmode = 1;        /* XXX: OK? */
+
+       if (k->k_repeating) {
+               k->k_repeating = 0;
+               untimeout(kbd_repeat, k);
+       }
+
+       return (0);
+}
+
+/*
+ * Close:
+ * Turn off event mode, dump the queue, and close the keyboard
+ * unless it is supplying console input.
+ */
+int
+kbdclose(dev, flags, mode, p)
+       dev_t dev;
+       int flags, mode;
+       struct proc *p;
+{
+       struct kbd_softc *k;
+
+       k = kbdcd.cd_devs[minor(dev)];
+       k->k_evmode = 0;
+       ev_fini(&k->k_events);
+       k->k_events.ev_io = NULL;
+       return (0);
+}
+
+int
+kbdread(dev, uio, flags)
+       dev_t dev;
+       struct uio *uio;
+       int flags;
+{
+       struct kbd_softc *k;
+
+       k = kbdcd.cd_devs[minor(dev)];
+       return (ev_read(&k->k_events, uio, flags));
+}
+
+/* this routine should not exist, but is convenient to write here for now */
+int
+kbdwrite(dev, uio, flags)
+       dev_t dev;
+       struct uio *uio;
+       int flags;
+{
+
+       return (EOPNOTSUPP);
+}
+
+int
+kbdselect(dev, rw, p)
+       dev_t dev;
+       int rw;
+       struct proc *p;
+{
+       struct kbd_softc *k;
+
+       k = kbdcd.cd_devs[minor(dev)];
+       return (ev_select(&k->k_events, rw, p));
+}
+
+static int kbd_oldkeymap __P((struct kbd_state *ks,
+       u_long cmd, struct okiockey *okio));
+
+static int kbd_iockeymap __P((struct kbd_state *ks,
+       u_long cmd, struct kiockeymap *kio));
+
+int
+kbdioctl(dev, cmd, data, flag, p)
+       dev_t dev;
+       u_long cmd;
+       register caddr_t data;
+       int flag;
+       struct proc *p;
+{
+       struct kbd_softc *k;
+       struct kbd_state *ks;
+       int *ip;
+       int error = 0;
+
+       k = kbdcd.cd_devs[minor(dev)];
+       ks = &k->k_state;
+
+       switch (cmd) {
+
+       case KIOCTRANS:         /* Set translation mode */
+               ip = (int *)data;
+               /* We only support "raw" mode on /dev/kbd */
+               if (*ip != TR_UNTRANS_EVENT)
+                       error = EINVAL;
+               break;
+
+       case KIOCGTRANS:        /* Get translation mode */
+               ip = (int *)data;
+               /* We only support "raw" mode on /dev/kbd */
+               *ip = TR_UNTRANS_EVENT;
+               break;
+
+#ifdef KIOCGETKEY
+       case KIOCGETKEY:        /* Get keymap entry (old format) */
+               error = kbd_oldkeymap(ks, cmd, (struct okiockey *)data);
+               break;
+#endif KIOCGETKEY */
+
+       case KIOCSKEY:          /* Set keymap entry */
+               /* Don't let just anyone hose the keyboard. */
+               if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
+                       return (error);
+               /* fallthrough */
+       case KIOCGKEY:          /* Get keymap entry */
+               error = kbd_iockeymap(ks, cmd, (struct kiockeymap *)data);
+               break;
+
+       case KIOCCMD:   /* Send a command to the keyboard */
+               /*
+                * ``unimplemented commands are ignored'' (blech)
+                * so cannot check return value from kbd_docmd
+                */
+               error = kbd_drain_tx(k);
+               if (error == 0) {
+                       (void) kbd_docmd(k, *(int *)data);
+               }
+               break;
+
+       case KIOCTYPE:  /* Get keyboard type */
+               ip = (int *)data;
+               *ip = ks->kbd_id;
+               break;
+
+       case KIOCSDIRECT:       /* where to send input */
+               ip = (int *)data;
+               k->k_evmode = *ip;
+               break;
+
+       case KIOCLAYOUT:        /* Get keyboard layout */
+               *data = ks->kbd_layout;
+               break;
+
+       case KIOCSLED:
+               error = kbd_set_leds(k, data);
+               break;
+
+       case KIOCGLED:
+               *(char *)data = ks->kbd_leds;
+               break;
+
+       case FIONBIO:           /* we will remove this someday (soon???) */
+               break;
+
+       case FIOASYNC:
+               k->k_events.ev_async = *(int *)data != 0;
+               break;
+
+       case TIOCSPGRP:
+               ip = (int *)data;
+               if (*ip != k->k_events.ev_io->p_pgid)
+                       error = EPERM;
+               break;
+
+       }
+
+       return (error);
+}
+
+/****************************************************************
+ * ioctl helpers
+ ****************************************************************/
+
+/*
+ * Get/Set keymap entry
+ */
+int
+kbd_iockeymap(ks, cmd, kio)
+       struct kbd_state *ks;
+       u_long cmd;
+       struct kiockeymap *kio;
+{
+       struct keymap *km;
+       u_int station;
+
+       switch (kio->kio_tablemask) {
+       case KIOC_NOMASK:
+               km = ks->kbd_k.k_normal;
+               break;
+       case KIOC_SHIFTMASK:
+               km = ks->kbd_k.k_shifted;
+               break;
+       case KIOC_CTRLMASK:
+               km = ks->kbd_k.k_control;
+               break;
+       case KIOC_UPMASK:
+               km = ks->kbd_k.k_release;
+               break;
+       default:
+               /* Silently ignore unsupported masks */
+               return (0);
+       }
+
+       /* Range-check the table position. */
+       station = kio->kio_station;
+       if (station >= KEYMAP_SIZE)
+               return (EINVAL);
+
+       switch (cmd) {
+
+       case KIOCGKEY:  /* Get keymap entry */
+               kio->kio_entry = km->keymap[station];
+               break;
+
+       case KIOCSKEY:  /* Set keymap entry */
+               km->keymap[station] = kio->kio_entry;
+               break;
+
+       default:
+               return(ENOTTY);
+       }
+       return (0);
+}
+
+#ifdef KIOCGETKEY
+/*
+ * Get/Set keymap entry,
+ * old format (compatibility)
+ */
+int
+kbd_oldkeymap(ks, cmd, kio)
+       struct kbd_state *ks;
+       u_long cmd;
+       struct okiockey *kio;
+{
+       int error = 0;
+
+       switch (cmd) {
+
+       case KIOCGETKEY:
+               if (kio->kio_station == 118) {
+                       /*
+                        * This is X11 asking if a type 3 keyboard is
+                        * really a type 3 keyboard.  Say yes, it is,
+                        * by reporting key station 118 as a "hole".
+                        * Note old (SunOS 3.5) definition of HOLE!
+                        */
+                       kio->kio_entry = 0xA2;
+                       break;
+               }
+               /* fall through */
+
+       default:
+               error = ENOTTY;
+               break;
+       }
+
+       return (error);
+}
+#endif /* KIOCGETKEY */
+
+/****************************************************************
+ * middle layers:
+ *  - keysym to ASCII sequence
+ *  - raw key codes to keysym
+ ****************************************************************/
+
+
+/*
+ * Initialization done by either kdcninit or kbd_iopen
+ */
+void
+kbd_xlate_init(ks)
+       struct kbd_state *ks;
+{
+       struct keyboard *ktbls;
+       int id;
+
+       id = ks->kbd_id;
+       if (id < KBD_MIN_TYPE)
+               id = KBD_MIN_TYPE;
+       if (id > kbd_max_type)
+               id = kbd_max_type;
+       ktbls = keyboards[id];
+
+       ks->kbd_k = *ktbls;     /* struct assignment */
+       ks->kbd_modbits = 0;
+}
+
+/*
+ * Turn keyboard up/down codes into a KEYSYM.
+ * Note that the "kd" driver uses this too!
+ */
+int
+kbd_code_to_keysym(ks, c)
+       register struct kbd_state *ks;
+       register int c;
+{
+       struct keymap *km;
+       int keysym;
+
+       /*
+        * Get keymap pointer.  One of these:
+        * release, control, shifted, normal, ...
+        */
+       if (KEY_UP(c))
+               km = ks->kbd_k.k_release;
+       else {
+               if (ks->kbd_modbits & KBMOD_CTRL_MASK)
+                       km = ks->kbd_k.k_control;
+               else {
+                       if (ks->kbd_modbits & KBMOD_SHIFT_MASK)
+                               km = ks->kbd_k.k_shifted;
+                       else
+                               km = ks->kbd_k.k_normal;
+               }
+       }
+       if (km == NULL) {
+               /*
+                * Do not know how to translate yet.
+                * We will find out when a RESET comes along.
+                */
+               keysym = KEYSYM_NOP;
+       } else
+               keysym = km->keymap[KEY_CODE(c)];
+
+       return (keysym);
+}
+
+void
+kbd_input_string(k, str)
+       struct kbd_softc *k;
+       char *str;
+{
+       while (*str) {
+               kd_input(*str);
+               str++;
+       }
+}
+
+void
+kbd_input_funckey(k, keysym)
+       struct kbd_softc *k;
+       register int keysym;
+{
+       register int n;
+       char str[12];
+
+       /*
+        * Format the F-key sequence and send as a string.
+        * XXX: Ugly compatibility mappings.
+        */
+       n = 0xC0 + (keysym & 0x3F);
+       sprintf(str, "\033[%dz", n);
+       kbd_input_string(k, str);
+}
+
+/*
+ * This is called by kbd_input_raw() or by kb_repeat()
+ * to deliver ASCII input.  Called at splsoftclock()
+ * XXX: Raise to spltty before calling kd_input() ?
+ */
+void
+kbd_input_keysym(k, keysym)
+       struct kbd_softc *k;
+       register int keysym;
+{
+       struct kbd_state *ks = &k->k_state;
+       register int class, data;
+
+       class = KEYSYM_CLASS(keysym);
+       switch (class) {
+
+       case KEYSYM_ASCII:
+               data = KEYSYM_DATA(keysym);
+               if (ks->kbd_modbits & KBMOD_META_MASK)
+                       data |= 0x80;
+               kd_input(data);
+               break;
+
+       case KEYSYM_STRING:
+               data = keysym & 0xF;
+               kbd_input_string(k, kbd_stringtab[data]);
+               break;
+
+       case KEYSYM_FUNC:
+               kbd_input_funckey(k, keysym);
+               break;
+
+       case KEYSYM_CLRMOD:
+               data = 1 << (keysym & 0x1F);
+               ks->kbd_modbits &= ~data;
+               break;
+
+       case KEYSYM_SETMOD:
+               data = 1 << (keysym & 0x1F);
+               ks->kbd_modbits |= data;
+               break;
+
+       case KEYSYM_INVMOD:
+               data = 1 << (keysym & 0x1F);
+               ks->kbd_modbits ^= data;
+               break;
+
+       case KEYSYM_ALL_UP:
+               ks->kbd_modbits &= ~0xFFFF;
+               break;
+
+       case KEYSYM_SPECIAL:
+               if (keysym == KEYSYM_NOP)
+                       break;
+               /* fall through */
+       default:
+               log(LOG_WARNING, "%s: unexpected keysym 0x%x\n",
+                       k->k_dev.dv_xname, keysym);
+               break;
+       }
+}
+
+/*
+ * This is the autorepeat timeout function.
+ * (called at splsoftclock)
+ */
+void
+kbd_repeat(void *arg)
+{
+       struct kbd_softc *k = (struct kbd_softc *)arg;
+
+       if (k->k_repeating && k->k_repeatsym >= 0) {
+               kbd_input_keysym(k, k->k_repeatsym);
+               timeout(kbd_repeat, k, k->k_repeat_step);
+       }
+}
+
+/*
+ * Called by our kbd_softint() routine on input,
+ * which passes the raw hardware scan codes.
+ * Note: this is called at splsoftclock()
+ */
+void
+kbd_input_raw(k, c)
+       struct kbd_softc *k;
+       register int c;
+{
+       struct kbd_state *ks = &k->k_state;
+       struct firm_event *fe;
+       int put, keysym;
+
+       /* XXX - Input errors already handled. */
+
+       /* Are we expecting special input? */
+       if (ks->kbd_expect) {
+               if (ks->kbd_expect & KBD_EXPECT_IDCODE) {
+                       /* We read a KBD_RESET last time. */
+                       ks->kbd_id = c;
+                       kbd_was_reset(k);
+               }
+               if (ks->kbd_expect & KBD_EXPECT_LAYOUT) {
+                       /* We read a KBD_LAYOUT last time. */
+                       ks->kbd_layout = c;
+                       kbd_new_layout(k);
+               }
+               ks->kbd_expect = 0;
+               return;
+       }
+
+       /* Is this one of the "special" input codes? */
+       if (KBD_SPECIAL(c)) {
+               switch (c) {
+               case KBD_RESET:
+                       ks->kbd_expect |= KBD_EXPECT_IDCODE;
+                       /* Fake an "all-up" to resync. translation. */
+                       c = KBD_IDLE;
+                       break;
+
+               case KBD_LAYOUT:
+                       ks->kbd_expect |= KBD_EXPECT_LAYOUT;
+                       return;
+
+               case KBD_ERROR:
+                       log(LOG_WARNING, "%s: received error indicator\n",
+                               k->k_dev.dv_xname);
+                       return;
+
+               case KBD_IDLE:
+                       /* Let this go to the translator. */
+                       break;
+               }
+       }
+
+       /*
+        * If /dev/kbd is not connected in event mode, 
+        * translate and send upstream (to console).
+        */
+       if (!k->k_evmode) {
+
+               /* Any input stops auto-repeat (i.e. key release). */
+               if (k->k_repeating) {
+                       k->k_repeating = 0;
+                       untimeout(kbd_repeat, k);
+               }
+
+               /* Translate this code to a keysym */
+               keysym = kbd_code_to_keysym(ks, c);
+
+               /* Pass up to the next layer. */
+               kbd_input_keysym(k, keysym);
+
+               /* Does this symbol get auto-repeat? */
+               if (KEYSYM_NOREPEAT(keysym))
+                       return;
+
+               /* Setup for auto-repeat after initial delay. */
+               k->k_repeating = 1;
+               k->k_repeatsym = keysym;
+               timeout(kbd_repeat, k, k->k_repeat_start);
+               return;
+       }
+
+       /*
+        * IDLEs confuse the MIT X11R4 server badly, so we must drop them.
+        * This is bad as it means the server will not automatically resync
+        * on all-up IDLEs, but I did not drop them before, and the server
+        * goes crazy when it comes time to blank the screen....
+        */
+       if (c == KBD_IDLE)
+               return;
+
+       /*
+        * Keyboard is generating events.  Turn this keystroke into an
+        * event and put it in the queue.  If the queue is full, the
+        * keystroke is lost (sorry!).
+        */
+       put = k->k_events.ev_put;
+       fe = &k->k_events.ev_q[put];
+       put = (put + 1) % EV_QSIZE;
+       if (put == k->k_events.ev_get) {
+               log(LOG_WARNING, "%s: event queue overflow\n",
+                       k->k_dev.dv_xname); /* ??? */
+               return;
+       }
+       fe->id = KEY_CODE(c);
+       fe->value = KEY_UP(c) ? VKEY_UP : VKEY_DOWN;
+       fe->time = time;
+       k->k_events.ev_put = put;
+       EV_WAKEUP(&k->k_events);
+}
+
+/****************************************************************
+ * Interface to the lower layer (zscc)
+ ****************************************************************/
+
+static int
+kbd_rxint(cs)
+       register struct zs_chanstate *cs;
+{
+       register struct kbd_softc *k;
+       register int put, put_next;
+       register u_char c, rr1;
+
+       k = cs->cs_private;
+       put = k->k_rbput;
+
+       /* Read the input data ASAP. */
+       c = *(cs->cs_reg_data);
+       ZS_DELAY();
+
+       /* Save the status register too. */
+       rr1 = ZS_READ(cs, 1);
+
+       if (rr1 & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) {
+               /* Clear the receive error. */
+               *(cs->cs_reg_csr) = ZSWR0_RESET_ERRORS;
+               ZS_DELAY();
+       }
+
+       /*
+        * Check NOW for a console abort sequence, so that we can
+        * abort even when interrupts are locking up the machine.
+        */
+       if (k->k_magic1_down) {
+               /* The last keycode was "MAGIC1" down. */
+               k->k_magic1_down = 0;
+               if ((c == k->k_magic2) && k->k_isconsole) {
+                       /* Magic "L1-A" sequence; enter debugger. */
+                       zs_abort();
+                       /* Debugger done.  Fake L1-up to finish it. */
+                       c = k->k_magic1 | KBD_UP;
+               }
+       }
+       if (c == k->k_magic1) {
+               k->k_magic1_down = 1;
+       }
+
+       k->k_rbuf[put] = (c << 8) | rr1;
+       put_next = (put + 1) & KBD_RX_RING_MASK;
+
+       /* Would overrun if increment makes (put==get). */
+       if (put_next == k->k_rbget) {
+               k->k_intr_flags |= INTR_RX_OVERRUN;
+       } else {
+               /* OK, really increment. */
+               put = put_next;
+       }
+
+       /* Done reading. */
+       k->k_rbput = put;
+
+       /* Ask for softint() call. */
+       cs->cs_softreq = 1;
+       return(1);
+}
+
+
+static int
+kbd_txint(cs)
+       register struct zs_chanstate *cs;
+{
+       register struct kbd_softc *k;
+       register int count, rval;
+
+       k = cs->cs_private;
+
+       *(cs->cs_reg_csr) = ZSWR0_RESET_TXINT;
+       ZS_DELAY();
+
+       k->k_intr_flags |= INTR_TX_EMPTY;
+       /* Ask for softint() call. */
+       cs->cs_softreq = 1;
+       return (1);
+}
+
+
+static int
+kbd_stint(cs)
+       register struct zs_chanstate *cs;
+{
+       register struct kbd_softc *k;
+       register int rr0;
+
+       k = cs->cs_private;
+
+       rr0 = *(cs->cs_reg_csr);
+       ZS_DELAY();
+
+       *(cs->cs_reg_csr) = ZSWR0_RESET_STATUS;
+       ZS_DELAY();
+
+#if 0
+       if (rr0 & ZSRR0_BREAK) {
+               /* Keyboard unplugged? */
+               zs_abort();
+               return (0);
+       }
+#endif
+
+       k->k_intr_flags |= INTR_ST_CHECK;
+       /* Ask for softint() call. */
+       cs->cs_softreq = 1;
+       return (1);
+}
+
+/*
+ * Get input from the recieve ring and pass it on.
+ * Note: this is called at splsoftclock()
+ */
+static int
+kbd_softint(cs)
+       struct zs_chanstate *cs;
+{
+       register struct kbd_softc *k;
+       register int get, c, s;
+       int intr_flags;
+       register u_short ring_data;
+       register u_char rr0, rr1;
+
+       k = cs->cs_private;
+
+       /* Atomically get and clear flags. */
+       s = splzs();
+       intr_flags = k->k_intr_flags;
+       k->k_intr_flags = 0;
+       splx(s);
+
+       /*
+        * Copy data from the receive ring to the event layer.
+        */
+       get = k->k_rbget;
+       while (get != k->k_rbput) {
+               ring_data = k->k_rbuf[get];
+               get = (get + 1) & KBD_RX_RING_MASK;
+
+               /* low byte of ring_data is rr1 */
+               c = (ring_data >> 8) & 0xff;
+
+               if (ring_data & ZSRR1_DO)
+                       intr_flags |= INTR_RX_OVERRUN;
+               if (ring_data & (ZSRR1_FE | ZSRR1_PE)) {
+                       /*
+                        * After garbage, flush pending input, and
+                        * send a reset to resync key translation.
+                        */
+                       log(LOG_ERR, "%s: input error (0x%x)\n",
+                               k->k_dev.dv_xname, ring_data);
+                       get = k->k_rbput; /* flush */
+                       goto send_reset;
+               }
+
+               /* Pass this up to the "middle" layer. */
+               kbd_input_raw(k, c);
+       }
+       if (intr_flags & INTR_RX_OVERRUN) {
+               log(LOG_ERR, "%s: input overrun\n",
+                   k->k_dev.dv_xname);
+       send_reset:
+               /* Send a reset to resync translation. */
+               kbd_output(k, KBD_CMD_RESET);
+               kbd_start_tx(k);
+       }
+       k->k_rbget = get;
+
+       if (intr_flags & INTR_TX_EMPTY) {
+               /*
+                * Transmit done.  Try to send more, or
+                * clear busy and wakeup drain waiters.
+                */
+               k->k_txflags &= ~K_TXBUSY;
+               kbd_start_tx(k);
+       }
+
+       if (intr_flags & INTR_ST_CHECK) {
+               /*
+                * Status line change.  (Not expected.)
+                */
+               log(LOG_ERR, "%s: status interrupt?\n",
+                   k->k_dev.dv_xname);
+       }
+
+       return (1);
+}
+
+struct zsops zsops_kbd = {
+       kbd_rxint,      /* receive char available */
+       kbd_stint,      /* external/status */
+       kbd_txint,      /* xmit buffer empty */
+       kbd_softint,    /* process software interrupt */
+};
+
+/****************************************************************
+ * misc...
+ ****************************************************************/
+
+/*
+ * Initialization to be done at first open.
+ * This is called from kbdopen or kdopen (in kd.c)
+ */
+int
+kbd_iopen(unit)
+       int unit;
+{
+       struct kbd_softc *k;
+       struct kbd_state *ks;
+       int error, s;
+
+       if (unit >= kbdcd.cd_ndevs)
+               return (ENXIO);
+       k = kbdcd.cd_devs[unit];
+       if (k == NULL)
+               return (ENXIO);
+       ks = &k->k_state;
+       error = 0;
+
+       /* Tolerate extra calls. */
+       if (k->k_isopen)
+               return (error);
+
+       s = spltty();
+
+       /* Reset the keyboard and find out its type. */
+       kbd_output(k, KBD_CMD_RESET);
+       kbd_start_tx(k);
+       kbd_drain_tx(k);
+       /* The wakeup for this is in kbd_was_reset(). */
+       error = tsleep((caddr_t)&ks->kbd_id,
+                                  PZERO | PCATCH, devopn, hz);
+       if (error == EWOULDBLOCK) {     /* no response */
+               error = 0;
+               log(LOG_ERR, "%s: reset failed\n",
+                       k->k_dev.dv_xname);
+               /*
+                * Allow the open anyway (to keep getty happy)
+                * but assume the "least common denominator".
+                */
+               ks->kbd_id = KB_SUN2;
+       }
+
+       /* Earlier than type 4 does not know "layout". */
+       if (ks->kbd_id < KB_SUN4)
+               goto out;
+
+       /* Ask for the layout. */
+       kbd_output(k, KBD_CMD_GETLAYOUT);
+       kbd_start_tx(k);
+       kbd_drain_tx(k);
+       /* The wakeup for this is in kbd_new_layout(). */
+       error = tsleep((caddr_t)&ks->kbd_layout,
+                                  PZERO | PCATCH, devopn, hz);
+       if (error == EWOULDBLOCK) {     /* no response */
+               error = 0;
+               log(LOG_ERR, "%s: no response to get_layout\n",
+                       k->k_dev.dv_xname);
+               ks->kbd_layout = 0;
+       }
+
+out:
+       splx(s);
+
+       if (error == 0)
+               k->k_isopen = 1;
+
+       return error;
+}
+
+void
+kbd_was_reset(k)
+       struct kbd_softc *k;
+{
+       struct kbd_state *ks = &k->k_state;
+
+       /*
+        * On first identification, wake up anyone waiting for type
+        * and set up the table pointers.
+        */
+       wakeup((caddr_t)&ks->kbd_id);
+
+       /* Restore keyclick, if necessary */
+       switch (ks->kbd_id) {
+
+       case KB_SUN2:
+               /* Type 2 keyboards don't support keyclick */
+               break;
+
+       case KB_SUN3:
+               /* Type 3 keyboards come up with keyclick on */
+               if (!ks->kbd_click)
+                       (void) kbd_docmd(k, KBD_CMD_NOCLICK);
+               break;
+
+       case KB_SUN4:
+               /* Type 4 keyboards come up with keyclick off */
+               if (ks->kbd_click)
+                       (void) kbd_docmd(k, KBD_CMD_CLICK);
+               break;
+       }
+
+       /* LEDs are off after reset. */
+       ks->kbd_leds = 0;
+}
+
+void
+kbd_new_layout(k)
+       struct kbd_softc *k;
+{
+       struct kbd_state *ks = &k->k_state;
+
+       /*
+        * On first identification, wake up anyone waiting for type
+        * and set up the table pointers.
+        */
+       wakeup((caddr_t)&ks->kbd_layout);
+
+       /* XXX: switch decoding tables? */
+}
+
+
+/*
+ * Wait for output to finish.
+ * Called with user context.
+ */
+int
+kbd_drain_tx(k)
+       struct kbd_softc *k;
+{
+       int error, s;
+
+       error = 0;
+       s = spltty();
+       while (k->k_txflags & K_TXBUSY) {
+               k->k_txflags |= K_TXWANT;
+               error = tsleep((caddr_t)&k->k_txflags,
+                                          PZERO | PCATCH, "kbdout", 0);
+       }
+       splx(s);
+       return (error);
+}
+
+/*
+ * Send out a byte to the keyboard (i.e. reset)
+ * Called with user context.
+ */
+void
+kbd_output(k, c)
+       struct kbd_softc *k;
+       int c;  /* the data */
+{
+       struct zs_chanstate *cs = k->k_cs;
+       int put, s;
+
+       s = spltty();
+       put = k->k_tbput;
+       k->k_tbuf[put] = (u_char)c;
+       put = (put + 1) & KBD_TX_RING_MASK;
+
+       /* Would overrun if increment makes (put==get). */
+       if (put == k->k_tbget) {
+               log(LOG_WARNING, "%s: output overrun\n",
+            k->k_dev.dv_xname);
+       } else {
+               /* OK, really increment. */
+               k->k_tbput = put;
+       }
+
+       splx(s);
+}
+
+void
+kbd_start_tx(k)
+    struct kbd_softc *k;
+{
+       struct zs_chanstate *cs = k->k_cs;
+       int get, s;
+       u_char c;
+
+       s = spltty();
+       if (k->k_txflags & K_TXBUSY)
+               goto out;
+
+       /* Is there anything to send? */
+       get = k->k_tbget;
+       if (get == k->k_tbput) {
+               /* Nothing to send.  Wake drain waiters. */
+               if (k->k_txflags & K_TXWANT) {
+                       k->k_txflags &= ~K_TXWANT;
+                       wakeup((caddr_t)&k->k_txflags);
+               }
+               goto out;
+       }
+
+       /* Have something to send. */
+       c = k->k_tbuf[get];
+       get = (get + 1) & KBD_TX_RING_MASK;
+       k->k_tbget = get;
+       k->k_txflags |= K_TXBUSY;
+
+       /* Need splzs to avoid interruption of the delay. */
+       (void) splzs();
+       *(cs->cs_reg_data) = c;
+       ZS_DELAY();
+
+out:
+       splx(s);
+}
+
+
+int
+kbd_set_leds(k, data)
+       struct kbd_softc *k;
+       caddr_t data;
+{
+       struct kbd_state *ks = &k->k_state;
+       int error, s;
+       char new_leds;
+
+       error = 0;
+       new_leds = *(char*)data;
+       s = spltty();
+
+       /* Don't send unless state changes. */
+       if (ks->kbd_leds == new_leds)
+               goto out;
+       ks->kbd_leds = new_leds;
+
+       /* Only type 4 and later has LEDs anyway. */
+       if (ks->kbd_id < 4)
+               goto out;
+
+       error = kbd_drain_tx(k);
+       kbd_output(k, KBD_CMD_SETLED);
+       kbd_output(k, new_leds);
+       kbd_start_tx(k);
+
+out:
+       splx(s);
+       return(error);
+}
+
+
+/*
+ * Execute a keyboard command; return 0 on success.
+ */
+int
+kbd_docmd(k, cmd)
+       struct kbd_softc *k;
+       int cmd;
+{
+       struct kbd_state *ks = &k->k_state;
+       int error, s;
+
+       switch (cmd) {
+
+       case KBD_CMD_BELL:
+       case KBD_CMD_NOBELL:
+               /* Supported by type 2, 3, and 4 keyboards */
+               break;
+
+       case KBD_CMD_CLICK:
+               /* Unsupported by type 2 keyboards */
+               if (ks->kbd_id != KB_SUN2) {
+                       ks->kbd_click = 1;
+                       break;
+               }
+               return (EINVAL);
+
+       case KBD_CMD_NOCLICK:
+               /* Unsupported by type 2 keyboards */
+               if (ks->kbd_id != KB_SUN2) {
+                       ks->kbd_click = 0;
+                       break;
+               }
+               return (EINVAL);
+
+       default:
+               return (EINVAL);        /* ENOTTY? EOPNOTSUPP? */
+       }
+
+       kbd_output(k, cmd);
+       kbd_start_tx(k);
+       return (0);
+}
+
diff --git a/sys/dev/sun/kbd_tables.c b/sys/dev/sun/kbd_tables.c
new file mode 100644 (file)
index 0000000..8f9fc34
--- /dev/null
@@ -0,0 +1,907 @@
+/*     $NetBSD: kbd_tables.c,v 1.1.1.1 1996/01/24 01:15:35 gwr Exp $   */
+
+/*
+ * Copyright (c) 1996 Gordon W. Ross
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 4. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Gordon Ross
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Keyboard translation tables.  (See kbd_tables.h)
+ */
+
+#define        _KERNEL 1
+#include "kbd_tables.h"
+
+
+/*
+ * Toggle keys are not yet supported, but could be with
+ * fairly simple changes.  Both CapsLock and NumLock
+ * could be easily done with "post-translation" on the
+ * keysymbols from these tables (WITHOUT new tables).
+ *
+ * The "compose" operations are not implemented.
+ */
+
+#define        KEYSYM_COMPOSE KEYSYM_NOP
+
+
+/*
+ * Key release codes are decoded in this map.
+ */
+struct keymap keymap_release = {
+    /*   0:             */     KEYSYM_HOLE,
+    /*   1: L1/Stop     */     KEYSYM_NOP,
+    /*   2:             */     KEYSYM_HOLE,
+    /*   3: L2/Again    */     KEYSYM_NOP,
+    /*   4:             */     KEYSYM_HOLE,
+    /*   5: F1          */     KEYSYM_NOP,
+    /*   6: F2          */     KEYSYM_NOP,
+    /*   7: F10         */     KEYSYM_NOP,
+    /*   8: F3          */     KEYSYM_NOP,
+    /*   9: F11         */     KEYSYM_NOP,
+    /*  10: F4          */     KEYSYM_NOP,
+    /*  11: F12         */     KEYSYM_NOP,
+    /*  12: F5          */     KEYSYM_NOP,
+    /*  13: AltGraph    */     KEYSYM_CLRMOD | KBMOD_ALTGRAPH,
+    /*  14: F6          */     KEYSYM_NOP,
+    /*  15:             */     KEYSYM_HOLE,
+    /*  16: F7          */     KEYSYM_NOP,
+    /*  17: F8          */     KEYSYM_NOP,
+    /*  18: F9          */     KEYSYM_NOP,
+    /*  19: Alt_L       */     KEYSYM_CLRMOD | KBMOD_ALT_L,
+    /*  20: T5_Up       */     KEYSYM_HOLE,
+    /*  21: R1/Pause    */     KEYSYM_NOP,
+    /*  22: R2/Print    */     KEYSYM_NOP,
+    /*  23: R3/Break    */     KEYSYM_NOP,
+    /*  24: T5_Left     */     KEYSYM_HOLE,
+    /*  25: L3/Props    */     KEYSYM_NOP,
+    /*  26: L4/Undo     */     KEYSYM_NOP,
+    /*  27: T5_Down     */     KEYSYM_HOLE,
+    /*  28: T5_Right    */     KEYSYM_HOLE,
+    /*  29: Esc         */     KEYSYM_NOP,
+    /*  30: 1           */     KEYSYM_NOP,
+    /*  31: 2           */     KEYSYM_NOP,
+    /*  32: 3           */     KEYSYM_NOP,
+    /*  33: 4           */     KEYSYM_NOP,
+    /*  34: 5           */     KEYSYM_NOP,
+    /*  35: 6           */     KEYSYM_NOP,
+    /*  36: 7           */     KEYSYM_NOP,
+    /*  37: 8           */     KEYSYM_NOP,
+    /*  38: 9           */     KEYSYM_NOP,
+    /*  39: 0           */     KEYSYM_NOP,
+    /*  40: minus       */     KEYSYM_NOP,
+    /*  41: equal       */     KEYSYM_NOP,
+    /*  42: grave/tilde */     KEYSYM_NOP,
+    /*  43: BackSpace   */     KEYSYM_NOP,
+    /*  44:             */     KEYSYM_HOLE,
+    /*  45: R4/KP_Equal */     KEYSYM_NOP,
+    /*  46: R5/KP_Div   */     KEYSYM_NOP,
+    /*  47: R6/KP_Mult  */     KEYSYM_NOP,
+    /*  48:             */     KEYSYM_NOP,
+    /*  49: L5/Front    */     KEYSYM_NOP,
+    /*  50: KP_Delete   */     KEYSYM_NOP,
+    /*  51: L6/Copy     */     KEYSYM_NOP,
+    /*  52:             */     KEYSYM_HOLE,
+    /*  53: Tab         */     KEYSYM_NOP,
+    /*  54: Q           */     KEYSYM_NOP,
+    /*  55: W           */     KEYSYM_NOP,
+    /*  56: E           */     KEYSYM_NOP,
+    /*  57: R           */     KEYSYM_NOP,
+    /*  58: T           */     KEYSYM_NOP,
+    /*  59: Y           */     KEYSYM_NOP,
+    /*  60: U           */     KEYSYM_NOP,
+    /*  61: I           */     KEYSYM_NOP,
+    /*  62: O           */     KEYSYM_NOP,
+    /*  63: P           */     KEYSYM_NOP,
+    /*  64: [           */     KEYSYM_NOP,
+    /*  65: ]           */     KEYSYM_NOP,
+    /*  66: Delete      */     KEYSYM_NOP,
+    /*  67: Compose     */     KEYSYM_NOP,
+    /*  68: R7/Home     */     KEYSYM_NOP,
+    /*  69: R8/Up       */     KEYSYM_NOP,
+    /*  70: R9/PgUp     */     KEYSYM_NOP,
+    /*  71: KP_Minus    */     KEYSYM_NOP,
+    /*  72: L7/Open     */     KEYSYM_NOP,
+    /*  73: L8/Paste    */     KEYSYM_NOP,
+    /*  74:             */     KEYSYM_HOLE,
+    /*  75:             */     KEYSYM_HOLE,
+    /*  76: Ctrl_L      */     KEYSYM_CLRMOD | KBMOD_CTRL_L,
+    /*  77: A           */     KEYSYM_NOP,
+    /*  78: S           */     KEYSYM_NOP,
+    /*  79: D           */     KEYSYM_NOP,
+    /*  80: F           */     KEYSYM_NOP,
+    /*  81: G           */     KEYSYM_NOP,
+    /*  82: H           */     KEYSYM_NOP,
+    /*  83: J           */     KEYSYM_NOP,
+    /*  84: K           */     KEYSYM_NOP,
+    /*  85: L           */     KEYSYM_NOP,
+    /*  86: ;           */     KEYSYM_NOP,
+    /*  87: apostr.     */     KEYSYM_NOP,
+    /*  88: backslash   */     KEYSYM_NOP,
+    /*  89: Return      */     KEYSYM_NOP,
+    /*  90: KP_Enter    */     KEYSYM_NOP,
+    /*  91: R10/Left    */     KEYSYM_NOP,
+    /*  92: R11/KP_5    */     KEYSYM_NOP,
+    /*  93: R12/Right   */     KEYSYM_NOP,
+    /*  94: KP_Insert   */     KEYSYM_NOP,
+    /*  95: L9/Find     */     KEYSYM_NOP,
+    /*  96:             */     KEYSYM_HOLE,
+    /*  97: L10/Cut     */     KEYSYM_NOP,
+    /*  98: Num_Lock    */     KEYSYM_NOP,
+    /*  99: Shift_L     */     KEYSYM_CLRMOD | KBMOD_SHIFT_L,
+    /* 100: Z           */     KEYSYM_NOP,
+    /* 101: X           */     KEYSYM_NOP,
+    /* 102: C           */     KEYSYM_NOP,
+    /* 103: V           */     KEYSYM_NOP,
+    /* 104: B           */     KEYSYM_NOP,
+    /* 105: N           */     KEYSYM_NOP,
+    /* 106: M           */     KEYSYM_NOP,
+    /* 107: ,           */     KEYSYM_NOP,
+    /* 108: .           */     KEYSYM_NOP,
+    /* 109: /           */     KEYSYM_NOP,
+    /* 110: Shift_R     */     KEYSYM_CLRMOD | KBMOD_SHIFT_R,
+    /* 111: Linefeed    */     KEYSYM_NOP,
+    /* 112: R13/End     */     KEYSYM_NOP,
+    /* 113: R14/Down    */     KEYSYM_NOP,
+    /* 114: R15/PgDn    */     KEYSYM_NOP,
+    /* 115:             */     KEYSYM_HOLE,
+    /* 116:             */     KEYSYM_HOLE,
+    /* 117:             */     KEYSYM_HOLE,
+    /* 118: L16/Help    */     KEYSYM_NOP,
+    /* 119: CapsLock    */     KEYSYM_NOP,
+    /* 120: Meta_L      */     KEYSYM_CLRMOD | KBMOD_META_L,
+    /* 121: SpaceBar    */     KEYSYM_NOP,
+    /* 122: Meta_R      */     KEYSYM_CLRMOD | KBMOD_META_R,
+    /* 123:             */     KEYSYM_HOLE,
+    /* 124:             */     KEYSYM_HOLE,
+    /* 125: KP_Add      */     KEYSYM_NOP,
+    /* 126:             */     KEYSYM_LAYOUT,  /* layout next */
+    /* 127:             */     KEYSYM_RESET,   /* kbd ID next */
+};
+
+\f
+/*
+ * This map is used when a control key is down.
+ */
+#define        CTL(c)  ((c)&0x1F)
+struct keymap keymap_control = {
+    /*   0:             */     KEYSYM_HOLE,
+    /*   1: L1/Stop     */     KEYSYM_NOP,
+    /*   2:             */     KEYSYM_HOLE,
+    /*   3: L2/Again    */     KEYSYM_NOP,
+    /*   4:             */     KEYSYM_HOLE,
+    /*   5: F1          */     KEYSYM_NOP,
+    /*   6: F2          */     KEYSYM_NOP,
+    /*   7: F10         */     KEYSYM_NOP,
+    /*   8: F3          */     KEYSYM_NOP,
+    /*   9: F11         */     KEYSYM_NOP,
+    /*  10: F4          */     KEYSYM_NOP,
+    /*  11: F12         */     KEYSYM_NOP,
+    /*  12: F5          */     KEYSYM_NOP,
+    /*  13: AltGraph    */     KEYSYM_SETMOD | KBMOD_ALTGRAPH,
+    /*  14: F6          */     KEYSYM_NOP,
+    /*  15:             */     KEYSYM_HOLE,
+    /*  16: F7          */     KEYSYM_NOP,
+    /*  17: F8          */     KEYSYM_NOP,
+    /*  18: F9          */     KEYSYM_NOP,
+    /*  19: Alt_L       */     KEYSYM_SETMOD | KBMOD_ALT_L,
+    /*  20: T5_Up       */     KEYSYM_HOLE,
+    /*  21: R1/Pause    */     KEYSYM_NOP,
+    /*  22: R2/Print    */     KEYSYM_NOP,
+    /*  23: R3/Break    */     KEYSYM_NOP,
+    /*  24: T5_Left     */     KEYSYM_HOLE,
+    /*  25: L3/Props    */     KEYSYM_NOP,
+    /*  26: L4/Undo     */     KEYSYM_NOP,
+    /*  27: T5_Down     */     KEYSYM_HOLE,
+    /*  28: T5_Right    */     KEYSYM_HOLE,
+    /*  29: Esc         */     0x1b,
+    /*  30: 1           */     KEYSYM_NOP,
+    /*  31: 2           */     CTL('@'),
+    /*  32: 3           */     KEYSYM_NOP,
+    /*  33: 4           */     KEYSYM_NOP,
+    /*  34: 5           */     KEYSYM_NOP,
+    /*  35: 6           */     CTL('^'),
+    /*  36: 7           */     KEYSYM_NOP,
+    /*  37: 8           */     KEYSYM_NOP,
+    /*  38: 9           */     KEYSYM_NOP,
+    /*  39: 0           */     KEYSYM_NOP,
+    /*  40: minus _     */     CTL('_'),
+    /*  41: equal       */     KEYSYM_NOP,
+    /*  42: grave/tilde */     CTL('~'),
+    /*  43: BackSpace   */     '\b',
+    /*  44:             */     KEYSYM_HOLE,
+    /*  45: R4/KP_Equal */     KEYSYM_NOP,
+    /*  46: R5/KP_Div   */     KEYSYM_NOP,
+    /*  47: R6/KP_Mult  */     KEYSYM_NOP,
+    /*  48:             */     KEYSYM_NOP,
+    /*  49: L5/Front    */     KEYSYM_NOP,
+    /*  50: KP_Delete   */     KEYSYM_NOP,
+    /*  51: L6/Copy     */     KEYSYM_NOP,
+    /*  52:             */     KEYSYM_HOLE,
+    /*  53: Tab         */     '\t',
+    /*  54: Q           */     CTL('Q'),
+    /*  55: W           */     CTL('W'),
+    /*  56: E           */     CTL('E'),
+    /*  57: R           */     CTL('R'),
+    /*  58: T           */     CTL('T'),
+    /*  59: Y           */     CTL('Y'),
+    /*  60: U           */     CTL('U'),
+    /*  61: I           */     CTL('I'),
+    /*  62: O           */     CTL('O'),
+    /*  63: P           */     CTL('P'),
+    /*  64: [           */     CTL('['),
+    /*  65: ]           */     CTL(']'),
+    /*  66: Delete      */     0x7f,
+    /*  67: Compose     */     KEYSYM_COMPOSE,
+    /*  68: R7/Home     */     CTL('A'),       /* emacs */
+    /*  69: R8/Up       */     CTL('P'),       /* emacs */
+    /*  70: R9/PgUp     */     0x80|'v',       /* emacs */
+    /*  71: KP_Minus    */     KEYSYM_NOP,
+    /*  72: L7/Open     */     KEYSYM_NOP,
+    /*  73: L8/Paste    */     KEYSYM_NOP,
+    /*  74:             */     KEYSYM_HOLE,
+    /*  75:             */     KEYSYM_HOLE,
+    /*  76: Ctrl_L      */     KEYSYM_SETMOD | KBMOD_CTRL_L,
+    /*  77: A           */     CTL('A'),
+    /*  78: S           */     CTL('S'),
+    /*  79: D           */     CTL('D'),
+    /*  80: F           */     CTL('F'),
+    /*  81: G           */     CTL('G'),
+    /*  82: H           */     CTL('H'),
+    /*  83: J           */     CTL('J'),
+    /*  84: K           */     CTL('K'),
+    /*  85: L           */     CTL('L'),
+    /*  86: ;           */     KEYSYM_NOP,
+    /*  87: apostr.     */     KEYSYM_NOP,
+    /*  88: backslash   */     CTL('\\'),
+    /*  89: Return      */     '\r',
+    /*  90: KP_Enter    */     KEYSYM_NOP,
+    /*  91: R10/Left    */     CTL('B'),       /* emacs */
+    /*  92: R11/KP_5    */     KEYSYM_NOP,
+    /*  93: R12/Right   */     CTL('F'),       /* emacs */
+    /*  94: KP_Insert   */     KEYSYM_NOP,
+    /*  95: L9/Find     */     KEYSYM_NOP,
+    /*  96:             */     KEYSYM_HOLE,
+    /*  97: L10/Cut     */     KEYSYM_NOP,
+    /*  98: Num_Lock    */     KEYSYM_INVMOD | KBMOD_NUMLOCK,
+    /*  99: Shift_L     */     KEYSYM_SETMOD | KBMOD_SHIFT_L,
+    /* 100: Z           */     CTL('Z'),
+    /* 101: X           */     CTL('X'),
+    /* 102: C           */     CTL('C'),
+    /* 103: V           */     CTL('V'),
+    /* 104: B           */     CTL('B'),
+    /* 105: N           */     CTL('N'),
+    /* 106: M           */     CTL('M'),
+    /* 107: ,           */     KEYSYM_NOP,
+    /* 108: .           */     KEYSYM_NOP,
+    /* 109: / ?         */     CTL('?'),
+    /* 110: Shift_R     */     KEYSYM_SETMOD | KBMOD_SHIFT_R,
+    /* 111: Linefeed    */     '\n',
+    /* 112: R13/End     */     CTL('E'),       /* emacs */
+    /* 113: R14/Down    */     CTL('N'),       /* emacs */
+    /* 114: R15/PgDn    */     CTL('V'),       /* emacs */
+    /* 115:             */     KEYSYM_HOLE,
+    /* 116:             */     KEYSYM_HOLE,
+    /* 117:             */     KEYSYM_HOLE,
+    /* 118: L16/Help    */     KEYSYM_NOP,
+    /* 119: CapsLock    */     KEYSYM_INVMOD | KBMOD_CAPSLOCK,
+    /* 120: Meta_L      */     KEYSYM_SETMOD | KBMOD_META_L,
+    /* 121: SpaceBar    */     CTL(' '),
+    /* 122: Meta_R      */     KEYSYM_SETMOD | KBMOD_META_R,
+    /* 123:             */     KEYSYM_HOLE,
+    /* 124:             */     KEYSYM_HOLE,
+    /* 125: KP_Add      */     KEYSYM_NOP,
+    /* 126:             */     KEYSYM_HW_ERR,
+    /* 127:             */     KEYSYM_ALL_UP,
+};
+#undef CTL
+
+\f
+/*
+ * Keymaps for the "type 3" keyboard.
+ * (lower-case, upper-case)
+ */
+
+struct keymap keymap_s3_lc = {
+    /*   0:             */     KEYSYM_HOLE,
+    /*   1: L1/Stop     */     KEYSYM_FUNC_L(1),
+    /*   2:             */     KEYSYM_HOLE,
+    /*   3: L2/Again    */     KEYSYM_FUNC_L(2),
+    /*   4:             */     KEYSYM_HOLE,
+    /*   5: F1          */     KEYSYM_FUNC_F(1),
+    /*   6: F2          */     KEYSYM_FUNC_F(2),
+    /*   7: F10         */     KEYSYM_HOLE,
+    /*   8: F3          */     KEYSYM_FUNC_F(3),
+    /*   9: F11         */     KEYSYM_HOLE,
+    /*  10: F4          */     KEYSYM_FUNC_F(4),
+    /*  11: F12         */     KEYSYM_HOLE,
+    /*  12: F5          */     KEYSYM_FUNC_F(5),
+    /*  13: AltGraph    */     KEYSYM_HOLE,
+    /*  14: F6          */     KEYSYM_FUNC_F(6),
+    /*  15:             */     KEYSYM_HOLE,
+    /*  16: F7          */     KEYSYM_FUNC_F(7),
+    /*  17: F8          */     KEYSYM_FUNC_F(8),
+    /*  18: F9          */     KEYSYM_FUNC_F(9),
+    /*  19: Alt_L       */     KEYSYM_SETMOD | KBMOD_ALT_L,
+    /*  20: T5_Up       */     KEYSYM_HOLE,
+    /*  21: R1/Pause    */     KEYSYM_FUNC_R(1),
+    /*  22: R2/Print    */     KEYSYM_FUNC_R(2),
+    /*  23: R3/Break    */     KEYSYM_FUNC_R(3),
+    /*  24: T5_Left     */     KEYSYM_HOLE,
+    /*  25: L3/Props    */     KEYSYM_FUNC_L(3),
+    /*  26: L4/Undo     */     KEYSYM_FUNC_L(4),
+    /*  27: T5_Down     */     KEYSYM_HOLE,
+    /*  28: T5_Right    */     KEYSYM_HOLE,
+    /*  29: Esc         */     0x1b,
+    /*  30: 1           */     '1',
+    /*  31: 2           */     '2',
+    /*  32: 3           */     '3',
+    /*  33: 4           */     '4',
+    /*  34: 5           */     '5',
+    /*  35: 6           */     '6',
+    /*  36: 7           */     '7',
+    /*  37: 8           */     '8',
+    /*  38: 9           */     '9',
+    /*  39: 0           */     '0',
+    /*  40: minus       */     '-',
+    /*  41: equal       */     '=',
+    /*  42: grave/tilde */     '`',
+    /*  43: BackSpace   */     '\b',
+    /*  44:             */     KEYSYM_HOLE,
+    /*  45: R4/KP_Equal */     KEYSYM_FUNC_R(4),
+    /*  46: R5/KP_Div   */     KEYSYM_FUNC_R(5),
+    /*  47: R6/KP_Mult  */     KEYSYM_FUNC_R(6),
+    /*  48:             */     KEYSYM_HOLE,
+    /*  49: L5/Front    */     KEYSYM_FUNC_L(5),
+    /*  50: KP_Delete   */     KEYSYM_HOLE,
+    /*  51: L6/Copy     */     KEYSYM_FUNC_L(6),
+    /*  52:             */     KEYSYM_HOLE,
+    /*  53: Tab         */     '\t',
+    /*  54: Q           */     'q',
+    /*  55: W           */     'w',
+    /*  56: E           */     'e',
+    /*  57: R           */     'r',
+    /*  58: T           */     't',
+    /*  59: Y           */     'y',
+    /*  60: U           */     'u',
+    /*  61: I           */     'i',
+    /*  62: O           */     'o',
+    /*  63: P           */     'p',
+    /*  64: [           */     '[',
+    /*  65: ]           */     ']',
+    /*  66: Delete      */     0x7f,
+    /*  67: Compose     */     KEYSYM_HOLE,
+    /*  68: R7/Home     */     KEYSYM_FUNC_R(7),
+    /*  69: R8/Up       */     KEYSYM_STRING | 1,
+    /*  70: R9/PgUp     */     KEYSYM_FUNC_R(9),
+    /*  71: KP_Minus    */     KEYSYM_HOLE,
+    /*  72: L7/Open     */     KEYSYM_FUNC_L(7),
+    /*  73: L8/Paste    */     KEYSYM_FUNC_L(8),
+    /*  74:             */     KEYSYM_HOLE,
+    /*  75:             */     KEYSYM_HOLE,
+    /*  76: Ctrl_L      */     KEYSYM_SETMOD | KBMOD_CTRL_L,
+    /*  77: A           */     'a',
+    /*  78: S           */     's',
+    /*  79: D           */     'd',
+    /*  80: F           */     'f',
+    /*  81: G           */     'g',
+    /*  82: H           */     'h',
+    /*  83: J           */     'j',
+    /*  84: K           */     'k',
+    /*  85: L           */     'l',
+    /*  86: ;           */     ';',
+    /*  87: apostr.     */     '\'',
+    /*  88: backslash   */     '\\',
+    /*  89: Return      */     '\r',
+    /*  90: KP_Enter    */     KEYSYM_HOLE,
+    /*  91: R10/Left    */     KEYSYM_STRING | 3,
+    /*  92: R11/KP_5    */     KEYSYM_FUNC_R(11),
+    /*  93: R12/Right   */     KEYSYM_STRING | 4,
+    /*  94: KP_Insert   */     KEYSYM_HOLE,
+    /*  95: L9/Find     */     KEYSYM_FUNC_L(9),
+    /*  96:             */     KEYSYM_HOLE,
+    /*  97: L10/Cut     */     KEYSYM_FUNC_L(10),
+    /*  98: Num_Lock    */     KEYSYM_HOLE,
+    /*  99: Shift_L     */     KEYSYM_SETMOD | KBMOD_SHIFT_L,
+    /* 100: Z           */     'z',
+    /* 101: X           */     'x',
+    /* 102: C           */     'c',
+    /* 103: V           */     'v',
+    /* 104: B           */     'b',
+    /* 105: N           */     'n',
+    /* 106: M           */     'm',
+    /* 107: ,           */     ',',
+    /* 108: .           */     '.',
+    /* 109: /           */     '/',
+    /* 110: Shift_R     */     KEYSYM_SETMOD | KBMOD_SHIFT_R,
+    /* 111: Linefeed    */     '\n',
+    /* 112: R13/End     */     KEYSYM_FUNC_R(13),
+    /* 113: R14/Down    */     KEYSYM_STRING | 2,
+    /* 114: R15/PgDn    */     KEYSYM_FUNC_R(15),
+    /* 115:             */     KEYSYM_HOLE,
+    /* 116:             */     KEYSYM_HOLE,
+    /* 117:             */     KEYSYM_HOLE,
+    /* 118: L16/Help    */     KEYSYM_HOLE,
+    /* 119: CapsLock    */     KEYSYM_INVMOD | KBMOD_CAPSLOCK,
+    /* 120: Meta_L      */     KEYSYM_SETMOD | KBMOD_META_L,
+    /* 121: SpaceBar    */     ' ',
+    /* 122: Meta_R      */     KEYSYM_SETMOD | KBMOD_META_R,
+    /* 123:             */     KEYSYM_HOLE,
+    /* 124:             */     KEYSYM_HOLE,
+    /* 125: KP_Add      */     KEYSYM_HOLE,
+    /* 126:             */     KEYSYM_HW_ERR,
+    /* 127:             */     KEYSYM_ALL_UP,
+};
+
+\f
+struct keymap keymap_s3_uc = {
+    /*   0:             */     KEYSYM_HOLE,
+    /*   1: L1/Stop     */     KEYSYM_FUNC_L(1),
+    /*   2:             */     KEYSYM_HOLE,
+    /*   3: L2/Again    */     KEYSYM_FUNC_L(2),
+    /*   4:             */     KEYSYM_HOLE,
+    /*   5: F1          */     KEYSYM_FUNC_F(1),
+    /*   6: F2          */     KEYSYM_FUNC_F(2),
+    /*   7: F10         */     KEYSYM_HOLE,
+    /*   8: F3          */     KEYSYM_FUNC_F(3),
+    /*   9: F11         */     KEYSYM_HOLE,
+    /*  10: F4          */     KEYSYM_FUNC_F(4),
+    /*  11: F12         */     KEYSYM_HOLE,
+    /*  12: F5          */     KEYSYM_FUNC_F(5),
+    /*  13: AltGraph    */     KEYSYM_HOLE,
+    /*  14: F6          */     KEYSYM_FUNC_F(6),
+    /*  15:             */     KEYSYM_HOLE,
+    /*  16: F7          */     KEYSYM_FUNC_F(7),
+    /*  17: F8          */     KEYSYM_FUNC_F(8),
+    /*  18: F9          */     KEYSYM_FUNC_F(9),
+    /*  19: Alt_L       */     KEYSYM_SETMOD | KBMOD_ALT_L,
+    /*  20: T5_Up       */     KEYSYM_HOLE,
+    /*  21: R1/Pause    */     KEYSYM_FUNC_R(1),
+    /*  22: R2/Print    */     KEYSYM_FUNC_R(2),
+    /*  23: R3/Break    */     KEYSYM_FUNC_R(3),
+    /*  24: T5_Left     */     KEYSYM_HOLE,
+    /*  25: L3/Props    */     KEYSYM_FUNC_L(3),
+    /*  26: L4/Undo     */     KEYSYM_FUNC_L(4),
+    /*  27: T5_Down     */     KEYSYM_HOLE,
+    /*  28: T5_Right    */     KEYSYM_HOLE,
+    /*  29: Esc         */     0x1b,
+    /*  30: 1           */     '!',
+    /*  31: 2           */     '@',
+    /*  32: 3           */     '#',
+    /*  33: 4           */     '$',
+    /*  34: 5           */     '%',
+    /*  35: 6           */     '^',
+    /*  36: 7           */     '&',
+    /*  37: 8           */     '*',
+    /*  38: 9           */     '(',
+    /*  39: 0           */     ')',
+    /*  40: minus       */     '_',
+    /*  41: equal       */     '+',
+    /*  42: grave/tilde */     '~',
+    /*  43: BackSpace   */     '\b',
+    /*  44:             */     KEYSYM_HOLE,
+    /*  45: R4/KP_Equal */     KEYSYM_FUNC_R(4),
+    /*  46: R5/KP_Div   */     KEYSYM_FUNC_R(5),
+    /*  47: R6/KP_Mult  */     KEYSYM_FUNC_R(6),
+    /*  48:             */     KEYSYM_HOLE,
+    /*  49: L5/Front    */     KEYSYM_FUNC_L(5),
+    /*  50: KP_Delete   */     KEYSYM_HOLE,
+    /*  51: L6/Copy     */     KEYSYM_FUNC_L(6),
+    /*  52:             */     KEYSYM_HOLE,
+    /*  53: Tab         */     '\t',
+    /*  54: Q           */     'Q',
+    /*  55: W           */     'W',
+    /*  56: E           */     'E',
+    /*  57: R           */     'R',
+    /*  58: T           */     'T',
+    /*  59: Y           */     'Y',
+    /*  60: U           */     'U',
+    /*  61: I           */     'I',
+    /*  62: O           */     'O',
+    /*  63: P           */     'P',
+    /*  64: [           */     '{',
+    /*  65: ]           */     '}',
+    /*  66: Delete      */     0x7f,
+    /*  67: Compose     */     KEYSYM_HOLE,
+    /*  68: R7/Home     */     KEYSYM_FUNC_R(7),
+    /*  69: R8/Up       */     KEYSYM_STRING | 1,
+    /*  70: R9/PgUp     */     KEYSYM_FUNC_R(9),
+    /*  71: KP_Minus    */     KEYSYM_HOLE,
+    /*  72: L7/Open     */     KEYSYM_FUNC_L(7),
+    /*  73: L8/Paste    */     KEYSYM_FUNC_L(8),
+    /*  74:             */     KEYSYM_HOLE,
+    /*  75:             */     KEYSYM_HOLE,
+    /*  76: Ctrl_L      */     KEYSYM_SETMOD | KBMOD_CTRL_L,
+    /*  77: A           */     'A',
+    /*  78: S           */     'S',
+    /*  79: D           */     'D',
+    /*  80: F           */     'F',
+    /*  81: G           */     'G',
+    /*  82: H           */     'H',
+    /*  83: J           */     'J',
+    /*  84: K           */     'K',
+    /*  85: L           */     'L',
+    /*  86: ;           */     ':',
+    /*  87: apostr.     */     '"',
+    /*  88: backslash   */     '|',
+    /*  89: Return      */     '\r',
+    /*  90: KP_Enter    */     KEYSYM_HOLE,
+    /*  91: R10/Left    */     KEYSYM_STRING | 3,
+    /*  92: R11/KP_5    */     KEYSYM_FUNC_R(11),
+    /*  93: R12/Right   */     KEYSYM_STRING | 4,
+    /*  94: KP_Insert   */     KEYSYM_HOLE,
+    /*  95: L9/Find     */     KEYSYM_FUNC_L(9),
+    /*  96:             */     KEYSYM_HOLE,
+    /*  97: L10/Cut     */     KEYSYM_FUNC_L(10),
+    /*  98: Num_Lock    */     KEYSYM_HOLE,
+    /*  99: Shift_L     */     KEYSYM_SETMOD | KBMOD_SHIFT_L,
+    /* 100: Z           */     'Z',
+    /* 101: X           */     'X',
+    /* 102: C           */     'C',
+    /* 103: V           */     'V',
+    /* 104: B           */     'B',
+    /* 105: N           */     'N',
+    /* 106: M           */     'M',
+    /* 107: ,           */     '<',
+    /* 108: .           */     '>',
+    /* 109: /           */     '?',
+    /* 110: Shift_R     */     KEYSYM_SETMOD | KBMOD_SHIFT_R,
+    /* 111: Linefeed    */     '\n',
+    /* 112: R13/End     */     KEYSYM_FUNC_R(13),
+    /* 113: R14/Down    */     KEYSYM_STRING | 2,
+    /* 114: R15/PgDn    */     KEYSYM_FUNC_R(15),
+    /* 115:             */     KEYSYM_HOLE,
+    /* 116:             */     KEYSYM_HOLE,
+    /* 117:             */     KEYSYM_HOLE,
+    /* 118: L16/Help    */     KEYSYM_HOLE,
+    /* 119: CapsLock    */     KEYSYM_INVMOD | KBMOD_CAPSLOCK,
+    /* 120: Meta_L      */     KEYSYM_SETMOD | KBMOD_META_L,
+    /* 121: SpaceBar    */     ' ',
+    /* 122: Meta_R      */     KEYSYM_SETMOD | KBMOD_META_R,
+    /* 123:             */     KEYSYM_HOLE,
+    /* 124:             */     KEYSYM_HOLE,
+    /* 125: KP_Add      */     KEYSYM_HOLE,
+    /* 126:             */     KEYSYM_HW_ERR,
+    /* 127:             */     KEYSYM_ALL_UP,
+};
+
+\f
+/*
+ * Keymaps for the "type 4" keyboard.
+ * (lower-case, upper-case)
+ */
+
+struct keymap keymap_s4_lc = {
+    /*   0:             */     KEYSYM_HOLE,
+    /*   1: L1/Stop     */     KEYSYM_FUNC_L(1),
+    /*   2:             */     KEYSYM_HOLE,
+    /*   3: L2/Again    */     KEYSYM_FUNC_L(2),
+    /*   4:             */     KEYSYM_HOLE,
+    /*   5: F1          */     KEYSYM_FUNC_F(1),
+    /*   6: F2          */     KEYSYM_FUNC_F(2),
+    /*   7: F10         */     KEYSYM_FUNC_F(10),
+    /*   8: F3          */     KEYSYM_FUNC_F(3),
+    /*   9: F11         */     KEYSYM_FUNC_F(11),
+    /*  10: F4          */     KEYSYM_FUNC_F(4),
+    /*  11: F12         */     KEYSYM_FUNC_F(12),
+    /*  12: F5          */     KEYSYM_FUNC_F(5),
+    /*  13: AltGraph    */     KEYSYM_SETMOD | KBMOD_ALTGRAPH,
+    /*  14: F6          */     KEYSYM_FUNC_F(6),
+    /*  15:             */     KEYSYM_HOLE,
+    /*  16: F7          */     KEYSYM_FUNC_F(7),
+    /*  17: F8          */     KEYSYM_FUNC_F(8),
+    /*  18: F9          */     KEYSYM_FUNC_F(9),
+    /*  19: Alt_L       */     KEYSYM_SETMOD | KBMOD_ALT_L,
+    /*  20: T5_Up       */     KEYSYM_HOLE,
+    /*  21: R1/Pause    */     KEYSYM_FUNC_R(1),
+    /*  22: R2/Print    */     KEYSYM_FUNC_R(2),
+    /*  23: R3/Break    */     KEYSYM_FUNC_R(3),
+    /*  24: T5_Left     */     KEYSYM_HOLE,
+    /*  25: L3/Props    */     KEYSYM_FUNC_L(3),
+    /*  26: L4/Undo     */     KEYSYM_FUNC_L(4),
+    /*  27: T5_Down     */     KEYSYM_HOLE,
+    /*  28: T5_Right    */     KEYSYM_HOLE,
+    /*  29: Esc         */     0x1b,
+    /*  30: 1           */     '1',
+    /*  31: 2           */     '2',
+    /*  32: 3           */     '3',
+    /*  33: 4           */     '4',
+    /*  34: 5           */     '5',
+    /*  35: 6           */     '6',
+    /*  36: 7           */     '7',
+    /*  37: 8           */     '8',
+    /*  38: 9           */     '9',
+    /*  39: 0           */     '0',
+    /*  40: minus       */     '-',
+    /*  41: equal       */     '=',
+    /*  42: grave/tilde */     '`',
+    /*  43: BackSpace   */     '\b',
+    /*  44:             */     KEYSYM_HOLE,
+    /*  45: R4/KP_Equal */     KEYSYM_FUNC_R(4),
+    /*  46: R5/KP_Div   */     KEYSYM_FUNC_R(5),
+    /*  47: R6/KP_Mult  */     KEYSYM_FUNC_R(6),
+    /*  48:             */     KEYSYM_FUNC_N(13),
+    /*  49: L5/Front    */     KEYSYM_FUNC_L(5),
+    /*  50: KP_Delete   */     KEYSYM_FUNC_N(10),
+    /*  51: L6/Copy     */     KEYSYM_FUNC_L(6),
+    /*  52:             */     KEYSYM_HOLE,
+    /*  53: Tab         */     '\t',
+    /*  54: Q           */     'q',
+    /*  55: W           */     'w',
+    /*  56: E           */     'e',
+    /*  57: R           */     'r',
+    /*  58: T           */     't',
+    /*  59: Y           */     'y',
+    /*  60: U           */     'u',
+    /*  61: I           */     'i',
+    /*  62: O           */     'o',
+    /*  63: P           */     'p',
+    /*  64: [           */     '[',
+    /*  65: ]           */     ']',
+    /*  66: Delete      */     0x7f,
+    /*  67: Compose     */     KEYSYM_COMPOSE,
+    /*  68: R7/Home     */     KEYSYM_FUNC_R(7),
+    /*  69: R8/Up       */     KEYSYM_STRING | 1,
+    /*  70: R9/PgUp     */     KEYSYM_FUNC_R(9),
+    /*  71: KP_Minus    */     KEYSYM_FUNC_N(15),
+    /*  72: L7/Open     */     KEYSYM_FUNC_L(7),
+    /*  73: L8/Paste    */     KEYSYM_FUNC_L(8),
+    /*  74:             */     KEYSYM_HOLE,
+    /*  75:             */     KEYSYM_HOLE,
+    /*  76: Ctrl_L      */     KEYSYM_SETMOD | KBMOD_CTRL_L,
+    /*  77: A           */     'a',
+    /*  78: S           */     's',
+    /*  79: D           */     'd',
+    /*  80: F           */     'f',
+    /*  81: G           */     'g',
+    /*  82: H           */     'h',
+    /*  83: J           */     'j',
+    /*  84: K           */     'k',
+    /*  85: L           */     'l',
+    /*  86: ;           */     ';',
+    /*  87: apostr.     */     '\'',
+    /*  88: backslash   */     '\\',
+    /*  89: Return      */     '\r',
+    /*  90: KP_Enter    */     KEYSYM_FUNC_N(11),
+    /*  91: R10/Left    */     KEYSYM_STRING | 3,
+    /*  92: R11/KP_5    */     KEYSYM_FUNC_R(11),
+    /*  93: R12/Right   */     KEYSYM_STRING | 4,
+    /*  94: KP_Insert   */     KEYSYM_FUNC_N(8),
+    /*  95: L9/Find     */     KEYSYM_FUNC_L(9),
+    /*  96:             */     KEYSYM_HOLE,
+    /*  97: L10/Cut     */     KEYSYM_FUNC_L(10),
+    /*  98: Num_Lock    */     KEYSYM_INVMOD | KBMOD_NUMLOCK,
+    /*  99: Shift_L     */     KEYSYM_SETMOD | KBMOD_SHIFT_L,
+    /* 100: Z           */     'z',
+    /* 101: X           */     'x',
+    /* 102: C           */     'c',
+    /* 103: V           */     'v',
+    /* 104: B           */     'b',
+    /* 105: N           */     'n',
+    /* 106: M           */     'm',
+    /* 107: ,           */     ',',
+    /* 108: .           */     '.',
+    /* 109: /           */     '/',
+    /* 110: Shift_R     */     KEYSYM_SETMOD | KBMOD_SHIFT_R,
+    /* 111: Linefeed    */     '\n',
+    /* 112: R13/End     */     KEYSYM_FUNC_R(13),
+    /* 113: R14/Down    */     KEYSYM_STRING | 2,
+    /* 114: R15/PgDn    */     KEYSYM_FUNC_R(15),
+    /* 115:             */     KEYSYM_HOLE,
+    /* 116:             */     KEYSYM_HOLE,
+    /* 117:             */     KEYSYM_HOLE,
+    /* 118: L16/Help    */     KEYSYM_FUNC_L(16),
+    /* 119: CapsLock    */     KEYSYM_INVMOD | KBMOD_CAPSLOCK,
+    /* 120: Meta_L      */     KEYSYM_SETMOD | KBMOD_META_L,
+    /* 121: SpaceBar    */     ' ',
+    /* 122: Meta_R      */     KEYSYM_SETMOD | KBMOD_META_R,
+    /* 123:             */     KEYSYM_HOLE,
+    /* 124:             */     KEYSYM_HOLE,
+    /* 125: KP_Add      */     KEYSYM_FUNC_N(14),
+    /* 126:             */     KEYSYM_HW_ERR,
+    /* 127:             */     KEYSYM_ALL_UP,
+};
+
+\f
+struct keymap keymap_s4_uc = {
+    /*   0:             */     KEYSYM_HOLE,
+    /*   1: L1/Stop     */     KEYSYM_FUNC_L(1),
+    /*   2:             */     KEYSYM_HOLE,
+    /*   3: L2/Again    */     KEYSYM_FUNC_L(2),
+    /*   4:             */     KEYSYM_HOLE,
+    /*   5: F1          */     KEYSYM_FUNC_F(1),
+    /*   6: F2          */     KEYSYM_FUNC_F(2),
+    /*   7: F10         */     KEYSYM_FUNC_F(10),
+    /*   8: F3          */     KEYSYM_FUNC_F(3),
+    /*   9: F11         */     KEYSYM_FUNC_F(11),
+    /*  10: F4          */     KEYSYM_FUNC_F(4),
+    /*  11: F12         */     KEYSYM_FUNC_F(12),
+    /*  12: F5          */     KEYSYM_FUNC_F(5),
+    /*  13: AltGraph    */     KEYSYM_SETMOD | KBMOD_ALTGRAPH,
+    /*  14: F6          */     KEYSYM_FUNC_F(6),
+    /*  15:             */     KEYSYM_HOLE,
+    /*  16: F7          */     KEYSYM_FUNC_F(7),
+    /*  17: F8          */     KEYSYM_FUNC_F(8),
+    /*  18: F9          */     KEYSYM_FUNC_F(9),
+    /*  19: Alt_L       */     KEYSYM_SETMOD | KBMOD_ALT_L,
+    /*  20: T5_Up       */     KEYSYM_HOLE,
+    /*  21: R1/Pause    */     KEYSYM_FUNC_R(1),
+    /*  22: R2/Print    */     KEYSYM_FUNC_R(2),
+    /*  23: R3/Break    */     KEYSYM_FUNC_R(3),
+    /*  24: T5_Left     */     KEYSYM_HOLE,
+    /*  25: L3/Props    */     KEYSYM_FUNC_L(3),
+    /*  26: L4/Undo     */     KEYSYM_FUNC_L(4),
+    /*  27: T5_Down     */     KEYSYM_HOLE,
+    /*  28: T5_Right    */     KEYSYM_HOLE,
+    /*  29: Esc         */     0x1b,
+    /*  30: 1           */     '!',
+    /*  31: 2           */     '@',
+    /*  32: 3           */     '#',
+    /*  33: 4           */     '$',
+    /*  34: 5           */     '%',
+    /*  35: 6           */     '^',
+    /*  36: 7           */     '&',
+    /*  37: 8           */     '*',
+    /*  38: 9           */     '(',
+    /*  39: 0           */     ')',
+    /*  40: minus       */     '_',
+    /*  41: equal       */     '+',
+    /*  42: grave/tilde */     '~',
+    /*  43: BackSpace   */     '\b',
+    /*  44:             */     KEYSYM_HOLE,
+    /*  45: R4/KP_Equal */     KEYSYM_FUNC_R(4),
+    /*  46: R5/KP_Div   */     KEYSYM_FUNC_R(5),
+    /*  47: R6/KP_Mult  */     KEYSYM_FUNC_R(6),
+    /*  48:             */     KEYSYM_FUNC_N(13),
+    /*  49: L5/Front    */     KEYSYM_FUNC_L(5),
+    /*  50: KP_Delete   */     KEYSYM_FUNC_N(10),
+    /*  51: L6/Copy     */     KEYSYM_FUNC_L(6),
+    /*  52:             */     KEYSYM_HOLE,
+    /*  53: Tab         */     '\t',
+    /*  54: Q           */     'Q',
+    /*  55: W           */     'W',
+    /*  56: E           */     'E',
+    /*  57: R           */     'R',
+    /*  58: T           */     'T',
+    /*  59: Y           */     'Y',
+    /*  60: U           */     'U',
+    /*  61: I           */     'I',
+    /*  62: O           */     'O',
+    /*  63: P           */     'P',
+    /*  64: [           */     '{',
+    /*  65: ]           */     '}',
+    /*  66: Delete      */     0x7f,
+    /*  67: Compose     */     KEYSYM_COMPOSE,
+    /*  68: R7/Home     */     KEYSYM_FUNC_R(7),
+    /*  69: R8/Up       */     KEYSYM_STRING | 1,
+    /*  70: R9/PgUp     */     KEYSYM_FUNC_R(9),
+    /*  71: KP_Minus    */     KEYSYM_FUNC_N(15),
+    /*  72: L7/Open     */     KEYSYM_FUNC_L(7),
+    /*  73: L8/Paste    */     KEYSYM_FUNC_L(8),
+    /*  74:             */     KEYSYM_HOLE,
+    /*  75:             */     KEYSYM_HOLE,
+    /*  76: Ctrl_L      */     KEYSYM_SETMOD | KBMOD_CTRL_L,
+    /*  77: A           */     'A',
+    /*  78: S           */     'S',
+    /*  79: D           */     'D',
+    /*  80: F           */     'F',
+    /*  81: G           */     'G',
+    /*  82: H           */     'H',
+    /*  83: J           */     'J',
+    /*  84: K           */     'K',
+    /*  85: L           */     'L',
+    /*  86: ;           */     ':',
+    /*  87: apostr.     */     '"',
+    /*  88: backslash   */     '|',
+    /*  89: Return      */     '\r',
+    /*  90: KP_Enter    */     KEYSYM_FUNC_N(11),
+    /*  91: R10/Left    */     KEYSYM_STRING | 3,
+    /*  92: R11/KP_5    */     KEYSYM_FUNC_R(11),
+    /*  93: R12/Right   */     KEYSYM_STRING | 4,
+    /*  94: KP_Insert   */     KEYSYM_FUNC_N(8),
+    /*  95: L9/Find     */     KEYSYM_FUNC_L(9),
+    /*  96:             */     KEYSYM_HOLE,
+    /*  97: L10/Cut     */     KEYSYM_FUNC_L(10),
+    /*  98: Num_Lock    */     KEYSYM_INVMOD | KBMOD_NUMLOCK,
+    /*  99: Shift_L     */     KEYSYM_SETMOD | KBMOD_SHIFT_L,
+    /* 100: Z           */     'Z',
+    /* 101: X           */     'X',
+    /* 102: C           */     'C',
+    /* 103: V           */     'V',
+    /* 104: B           */     'B',
+    /* 105: N           */     'N',
+    /* 106: M           */     'M',
+    /* 107: ,           */     '<',
+    /* 108: .           */     '>',
+    /* 109: /           */     '?',
+    /* 110: Shift_R     */     KEYSYM_SETMOD | KBMOD_SHIFT_R,
+    /* 111: Linefeed    */     '\n',
+    /* 112: R13/End     */     KEYSYM_FUNC_R(13),
+    /* 113: R14/Down    */     KEYSYM_STRING | 2,
+    /* 114: R15/PgDn    */     KEYSYM_FUNC_R(15),
+    /* 115:             */     KEYSYM_HOLE,
+    /* 116:             */     KEYSYM_HOLE,
+    /* 117:             */     KEYSYM_HOLE,
+    /* 118: L16/Help    */     KEYSYM_FUNC_L(16),
+    /* 119: CapsLock    */     KEYSYM_INVMOD | KBMOD_CAPSLOCK,
+    /* 120: Meta_L      */     KEYSYM_SETMOD | KBMOD_META_L,
+    /* 121: SpaceBar    */     ' ',
+    /* 122: Meta_R      */     KEYSYM_SETMOD | KBMOD_META_R,
+    /* 123:             */     KEYSYM_HOLE,
+    /* 124:             */     KEYSYM_HOLE,
+    /* 125: KP_Add      */     KEYSYM_FUNC_N(14),
+    /* 126:             */     KEYSYM_HW_ERR,
+    /* 127:             */     KEYSYM_ALL_UP,
+};
+
+\f
+
+/*
+ * Strings indexed by:  (KEYSYM_STRING | idx)
+ */
+char kbd_stringtab[16][10] = {
+       { 0x1b, '[', 'H', 0 },  /* Home */
+       { 0x1b, '[', 'A', 0 },  /* Up   */
+       { 0x1b, '[', 'B', 0 },  /* Down */
+       { 0x1b, '[', 'D', 0 },  /* Left */
+       { 0x1b, '[', 'C', 0 },  /* Right */
+};
+
+/*
+ * Keyboard descriptions for each type.
+ */
+
+/* Treat type 2 as type 3 (close enough) */
+#define        kbd_type2 kbd_type3
+
+static struct keyboard kbd_type3 = {
+       &keymap_release,
+       &keymap_control,
+       &keymap_s3_lc,
+       &keymap_s3_uc,
+};
+
+static struct keyboard kbd_type4 = {
+       &keymap_release,
+       &keymap_control,
+       &keymap_s4_lc,
+       &keymap_s4_uc,
+};
+
+/* Treat type 5 as type 4 (close enough) */
+#define        kbd_type5 kbd_type4
+
+struct keyboard * keyboards[] = {
+       0, /* type 0 */
+       0, /* type 1 */
+       &kbd_type2,
+       &kbd_type3,
+       &kbd_type4,
+       &kbd_type5,
+};             
+int kbd_max_type = 5;
diff --git a/sys/dev/sun/kbd_tables.h b/sys/dev/sun/kbd_tables.h
new file mode 100644 (file)
index 0000000..e8373d2
--- /dev/null
@@ -0,0 +1,149 @@
+/*     $NetBSD: kbd_tables.h,v 1.1.1.1 1996/01/24 01:15:35 gwr Exp $   */
+
+/*
+ * Copyright (c) 1996 Gordon W. Ross
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 4. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Gordon Ross
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Keyboard translation tables.  These tables contain
+ * "Key symbols" (or "keysyms", to use X terminology).
+ * The key symbol space is divided into "classes" where
+ * the high 8 bits determine the symbol class, and the
+ * low 8 bits are interpreted in a way specific to each
+ * class.  The simplest class is ASCII, which is defined
+ * as class zero in order to simplify table definition.
+ *
+ * For convenience in the driver, all keysyms that
+ * deserve autorepeat are below 0x8000.  The driver
+ * uses the following macro to determine if a keysym
+ * should NOT get autorepeat.
+ */
+#define KEYSYM_NOREPEAT(sym) ((sym) & 0x8000)
+
+#define KEYSYM_CLASS(sym) ((sym) & 0xFF00)
+#define KEYSYM_DATA(sym)  ((sym) & 0x00FF)
+
+/*
+ * The ACSII class.  Low 8 bits are the character.
+ */
+#define KEYSYM_ASCII 0
+
+/*
+ * The "special" class.
+ * We don't expect to receive any of these often,
+ * except for KEYSYM_NOP.  All can be ignored,
+ * because HW_ERR, LAYOUT, RESET, are all handled
+ * at a lower layer, before the translation code.
+ */
+#define KEYSYM_SPECIAL 0x8300
+#define KEYSYM_NOP     0x8300
+#define KEYSYM_OOPS    0x8301
+#define KEYSYM_HOLE    0x8302
+#define KEYSYM_HW_ERR  0x8307  /* kbd sent 0x7e */
+#define KEYSYM_LAYOUT  0x830e  /* kbd sent 0xfe */
+#define KEYSYM_RESET   0x830f  /* kbd sent 0xff */
+
+
+/*
+ * Floating accents class:  (not implemented)
+ * umlaut, circumflex, tilde, cedilla, acute, grave,...
+ */
+#define        KEYSYM_ACCENT 0x0400
+
+/*
+ * The string entry class.
+ * The low 4 bits select one of the entries from
+ * the string table.  (see kbd_stringtab[])
+ * By default, the string table has ANSI movement
+ * sequences for the arrow keys.
+ */
+#define        KEYSYM_STRING 0x0500
+
+/*
+ * Function keys (compatible with SunOS 4.1)
+ * L:left, R:right, F:func(top), N:numeric
+ */
+#define        KEYSYM_FUNC   0x0600
+#define        KEYSYM_FUNC_L(x) (KEYSYM_FUNC | ((x) - 1))
+#define        KEYSYM_FUNC_R(x) (KEYSYM_FUNC | ((x) - 1 + 0x10))
+#define        KEYSYM_FUNC_F(x) (KEYSYM_FUNC | ((x) - 1 + 0x20))
+#define        KEYSYM_FUNC_N(x) (KEYSYM_FUNC | ((x) - 1 + 0x30))
+
+/*
+ * Modifier symbols, to set/clear/toggle a modifier
+ * The low 5 bits define the position of a modifier
+ * bit to be cleared, set, or inverted.  The meanings
+ * of the modifier bit positions are defined below.
+ */
+#define KEYSYM_CLRMOD 0x9000
+#define KEYSYM_SETMOD 0x9100
+#define KEYSYM_INVMOD 0x9200
+#define KEYSYM_ALL_UP 0x9300
+
+/*
+ * Modifier indices.
+ * (logical OR with {CLR,SET,TOG}MOD above)
+ */
+#define        KBMOD_CTRL_L    0
+#define        KBMOD_CTRL_R    1
+#define        KBMOD_SHIFT_L   2
+#define        KBMOD_SHIFT_R   3
+#define        KBMOD_META_L    4
+#define        KBMOD_META_R    5
+#define KBMOD_ALT_L    6
+#define KBMOD_ALT_R    7
+/* Note 0-15 are cleared by ALL_UP */
+#define KBMOD_CAPSLOCK  16
+#define KBMOD_NUMLOCK  17
+
+#define        KBMOD_ALTGRAPH  KBMOD_ALT_R
+
+
+#ifdef _KERNEL
+
+#define KEYMAP_SIZE 128
+
+struct keymap {
+       unsigned short  keymap[KEYMAP_SIZE];
+};
+
+struct keyboard {
+       struct keymap   *k_release;     /* Key released */
+       struct keymap   *k_control;     /* Ctrl is down */
+       struct keymap   *k_normal;      /* No shifts */
+       struct keymap   *k_shifted;     /* Shift is down */
+       /* capslock? numlock? */
+};
+
+extern char kbd_stringtab[16][10];
+extern struct keyboard * keyboards[];
+extern int kbd_max_type;
+#define        KBD_MIN_TYPE 2
+
+#endif /* _KERNEL */
diff --git a/sys/dev/sun/kbd_xlate.h b/sys/dev/sun/kbd_xlate.h
new file mode 100644 (file)
index 0000000..263c80a
--- /dev/null
@@ -0,0 +1,83 @@
+/*     $NetBSD: kbd_xlate.h,v 1.1.1.1 1996/01/24 01:15:35 gwr Exp $    */
+
+/*
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)kbd.c       8.2 (Berkeley) 10/30/93
+ */
+
+/*
+ * This defines the interface provided by kbd_xlate()
+ */
+
+#include <dev/sun/kbd_tables.h>
+
+#define        KEY_CODE(c)     ((c) & KBD_KEYMASK)     /* keyboard code index */
+#define        KEY_UP(c)       ((c) & KBD_UP)          /* true => key went up */
+
+#define KBMOD_CTRL_MASK        ((1 << KBMOD_CTRL_L)  | (1 << KBMOD_CTRL_R))
+#define KBMOD_SHIFT_MASK       ((1 << KBMOD_SHIFT_L) | (1 << KBMOD_SHIFT_R))
+#define KBMOD_META_MASK        ((1 << KBMOD_META_L)  | (1 << KBMOD_META_R))
+#define KBMOD_ALT_MASK         ((1 << KBMOD_ALT_L)   | (1 << KBMOD_ALT_R))
+
+
+/*
+ * Keycode translator state.
+ * We need to remember the state of the keyboard's shift and
+ * control keys, and we need a per-type translation table.
+ */
+struct kbd_state {
+       struct keyboard kbd_k;  /* table pointers */
+       int     kbd_modbits;            /* modifier keys */
+       int kbd_expect;                 /* expect ID or layout byte */
+#define        KBD_EXPECT_IDCODE       1
+#define        KBD_EXPECT_LAYOUT       2
+
+       u_char  kbd_id;         /* a place to store the ID */
+       u_char  kbd_layout;     /* which keyboard layout */
+       u_char  kbd_click;      /* true => keyclick enabled */
+       u_char  kbd_leds;       /* LED state */
+
+};
+
+extern void kbd_xlate_init __P((struct kbd_state *ks));
+extern int kbd_code_to_keysym __P((struct kbd_state *ks, int c));
+
diff --git a/sys/dev/sun/ms.c b/sys/dev/sun/ms.c
new file mode 100644 (file)
index 0000000..275481c
--- /dev/null
@@ -0,0 +1,633 @@
+/*     $NetBSD: ms.c,v 1.1.1.1 1996/01/24 01:15:35 gwr Exp $   */
+
+/*
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)ms.c        8.1 (Berkeley) 6/11/93
+ */
+
+/*
+ * Mouse driver (/dev/mouse)
+ */
+
+/*
+ * Zilog Z8530 Dual UART driver (mouse interface)
+ *
+ * This is the "slave" driver that will be attached to
+ * the "zsc" driver for a Sun mouse.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <dev/ic/z8530reg.h>
+#include <machine/z8530var.h>
+#include <machine/vuid_event.h>
+
+#include "event_var.h"
+
+/*
+ * How many input characters we can buffer.
+ * The port-specific var.h may override this.
+ * Note: must be a power of two!
+ */
+#define        MS_RX_RING_SIZE 256
+#define MS_RX_RING_MASK (MS_RX_RING_SIZE-1)
+/*
+ * Output buffer.  Only need a few chars.
+ */
+#define        MS_TX_RING_SIZE 16
+#define MS_TX_RING_MASK (MS_TX_RING_SIZE-1)
+/*
+ * Keyboard serial line speed is fixed at 1200 bps.
+ */
+#define MS_BPS 1200
+
+/*
+ * Mouse state.  A Mouse Systems mouse is a fairly simple device,
+ * producing five-byte blobs of the form:
+ *
+ *     b dx dy dx dy
+ *
+ * where b is the button state, encoded as 0x80|(~buttons)---there are
+ * three buttons (4=left, 2=middle, 1=right)---and dx,dy are X and Y
+ * delta values, none of which have are in [0x80..0x87].  (This lets
+ * us sync up with the mouse after an error.)
+ */
+struct ms_softc {
+       struct  device ms_dev;          /* required first: base device */
+       struct  zs_chanstate *ms_cs;
+
+       /* Flags to communicate with ms_softintr() */
+       volatile int ms_intr_flags;
+#define        INTR_RX_OVERRUN 1
+#define INTR_TX_EMPTY   2
+#define INTR_ST_CHECK   4
+
+       /*
+        * The receive ring buffer.
+        */
+       u_int   ms_rbget;       /* ring buffer `get' index */
+       volatile u_int  ms_rbput;       /* ring buffer `put' index */
+       u_short ms_rbuf[MS_RX_RING_SIZE]; /* rr1, data pairs */
+
+       /*
+        * State of input translator
+        */
+       short   ms_byteno;              /* input byte number, for decode */
+       char    ms_mb;                  /* mouse button state */
+       char    ms_ub;                  /* user button state */
+       int     ms_dx;                  /* delta-x */
+       int     ms_dy;                  /* delta-y */
+
+       /*
+        * State of upper interface.
+        */
+       volatile int ms_ready;          /* event queue is ready */
+       struct  evvar ms_events;        /* event queue state */
+} ms_softc;
+
+cdev_decl(ms); /* open, close, read, write, ioctl, stop, ... */
+
+struct zsops zsops_ms;
+
+/****************************************************************
+ * Definition of the driver for autoconfig.
+ ****************************************************************/
+
+static int     ms_match(struct device *, void *, void *);
+static void    ms_attach(struct device *, struct device *, void *);
+
+struct cfdriver mscd = {
+       NULL, "ms", ms_match, ms_attach,
+       DV_DULL, sizeof(struct ms_softc), NULL,
+};
+
+
+/*
+ * ms_match: how is this zs channel configured?
+ */
+int 
+ms_match(parent, match, aux)
+       struct device *parent;
+       void   *match, *aux;
+{
+       struct cfdata *cf = match;
+       struct zsc_attach_args *args = aux;
+
+       /* Exact match required for keyboard. */
+       if (cf->cf_loc[0] == args->channel)
+               return 2;
+
+       return 0;
+}
+
+void 
+ms_attach(parent, self, aux)
+       struct device *parent, *self;
+       void   *aux;
+
+{
+       struct zsc_softc *zsc = (void *) parent;
+       struct ms_softc *ms = (void *) self;
+       struct zsc_attach_args *args = aux;
+       struct zs_chanstate *cs;
+       struct cfdata *cf;
+       int channel, ms_unit;
+       int reset, s, tconst;
+
+       cf = ms->ms_dev.dv_cfdata;
+       ms_unit = cf->cf_unit;
+       channel = args->channel;
+       cs = &zsc->zsc_cs[channel];
+       cs->cs_private = ms;
+       cs->cs_ops = &zsops_ms;
+       ms->ms_cs = cs;
+
+       printf("\n");
+
+       /* Initialize the speed, etc. */
+       tconst = BPS_TO_TCONST(cs->cs_pclk_div16, MS_BPS);
+       s = splzs();
+       /* May need reset... */
+       reset = (channel == 0) ?
+               ZSWR9_A_RESET : ZSWR9_B_RESET;
+       ZS_WRITE(cs, 9, reset);
+       /* These are OK as set by zscc: WR3, WR4, WR5 */
+       cs->cs_preg[5] |= ZSWR5_DTR | ZSWR5_RTS;
+       cs->cs_preg[12] = tconst;
+       cs->cs_preg[13] = tconst >> 8;
+       zs_loadchannelregs(cs);
+       splx(s);
+
+       /* Initialize translator. */
+       ms->ms_byteno = -1;
+}
+
+/****************************************************************
+ *  Entry points for /dev/mouse
+ *  (open,close,read,write,...)
+ ****************************************************************/
+
+int
+msopen(dev, flags, mode, p)
+       dev_t dev;
+       int flags, mode;
+       struct proc *p;
+{
+       struct ms_softc *ms;
+       int error, s, unit;
+
+       unit = minor(dev);
+       if (unit >= mscd.cd_ndevs)
+               return (ENXIO);
+       ms = mscd.cd_devs[unit];
+       if (ms == NULL)
+               return (ENXIO);
+
+       /* This is an exclusive open device. */
+       if (ms->ms_events.ev_io)
+               return (EBUSY);
+       ms->ms_events.ev_io = p;
+       ev_init(&ms->ms_events);        /* may cause sleep */
+
+       ms->ms_ready = 1;               /* start accepting events */
+       return (0);
+}
+
+int
+msclose(dev, flags, mode, p)
+       dev_t dev;
+       int flags, mode;
+       struct proc *p;
+{
+       struct ms_softc *ms;
+
+       ms = mscd.cd_devs[minor(dev)];
+       ms->ms_ready = 0;               /* stop accepting events */
+       ev_fini(&ms->ms_events);
+
+       ms->ms_events.ev_io = NULL;
+       return (0);
+}
+
+int
+msread(dev, uio, flags)
+       dev_t dev;
+       struct uio *uio;
+       int flags;
+{
+       struct ms_softc *ms;
+
+       ms = mscd.cd_devs[minor(dev)];
+       return (ev_read(&ms->ms_events, uio, flags));
+}
+
+/* this routine should not exist, but is convenient to write here for now */
+int
+mswrite(dev, uio, flags)
+       dev_t dev;
+       struct uio *uio;
+       int flags;
+{
+
+       return (EOPNOTSUPP);
+}
+
+int
+msioctl(dev, cmd, data, flag, p)
+       dev_t dev;
+       u_long cmd;
+       register caddr_t data;
+       int flag;
+       struct proc *p;
+{
+       struct ms_softc *ms;
+
+       ms = mscd.cd_devs[minor(dev)];
+
+       switch (cmd) {
+
+       case FIONBIO:           /* we will remove this someday (soon???) */
+               return (0);
+
+       case FIOASYNC:
+               ms->ms_events.ev_async = *(int *)data != 0;
+               return (0);
+
+       case TIOCSPGRP:
+               if (*(int *)data != ms->ms_events.ev_io->p_pgid)
+                       return (EPERM);
+               return (0);
+
+       case VUIDGFORMAT:
+               /* we only do firm_events */
+               *(int *)data = VUID_FIRM_EVENT;
+               return (0);
+
+       case VUIDSFORMAT:
+               if (*(int *)data != VUID_FIRM_EVENT)
+                       return (EINVAL);
+               return (0);
+       }
+       return (ENOTTY);
+}
+
+int
+msselect(dev, rw, p)
+       dev_t dev;
+       int rw;
+       struct proc *p;
+{
+       struct ms_softc *ms;
+
+       ms = mscd.cd_devs[minor(dev)];
+       return (ev_select(&ms->ms_events, rw, p));
+}
+
+
+/****************************************************************
+ * Middle layer (translator)
+ ****************************************************************/
+
+/*
+ * Called by our ms_softint() routine on input.
+ */
+void
+ms_input(ms, c)
+       register struct ms_softc *ms;
+       register int c;
+{
+       register struct firm_event *fe;
+       register int mb, ub, d, get, put, any;
+       static const char to_one[] = { 1, 2, 2, 4, 4, 4, 4 };
+       static const int to_id[] = { MS_RIGHT, MS_MIDDLE, 0, MS_LEFT };
+
+       /*
+        * Discard input if not ready.  Drop sync on parity or framing
+        * error; gain sync on button byte.
+        */
+       if (ms->ms_ready == 0)
+               return;
+       if (c == -1) {
+               ms->ms_byteno = -1;
+               return;
+       }
+       if ((c & ~7) == 0x80)   /* if in 0x80..0x87 */
+               ms->ms_byteno = 0;
+
+       /*
+        * Run the decode loop, adding to the current information.
+        * We add, rather than replace, deltas, so that if the event queue
+        * fills, we accumulate data for when it opens up again.
+        */
+       switch (ms->ms_byteno) {
+
+       case -1:
+               return;
+
+       case 0:
+               /* buttons */
+               ms->ms_byteno = 1;
+               ms->ms_mb = (~c) & 0x7;
+               return;
+
+       case 1:
+               /* first delta-x */
+               ms->ms_byteno = 2;
+               ms->ms_dx += (char)c;
+               return;
+
+       case 2:
+               /* first delta-y */
+               ms->ms_byteno = 3;
+               ms->ms_dy += (char)c;
+               return;
+
+       case 3:
+               /* second delta-x */
+               ms->ms_byteno = 4;
+               ms->ms_dx += (char)c;
+               return;
+
+       case 4:
+               /* second delta-x */
+               ms->ms_byteno = -1;     /* wait for button-byte again */
+               ms->ms_dy += (char)c;
+               break;
+
+       default:
+               panic("ms_rint");
+               /* NOTREACHED */
+       }
+
+       /*
+        * We have at least one event (mouse button, delta-X, or
+        * delta-Y; possibly all three, and possibly three separate
+        * button events).  Deliver these events until we are out
+        * of changes or out of room.  As events get delivered,
+        * mark them `unchanged'.
+        */
+       any = 0;
+       get = ms->ms_events.ev_get;
+       put = ms->ms_events.ev_put;
+       fe = &ms->ms_events.ev_q[put];
+
+       /* NEXT prepares to put the next event, backing off if necessary */
+#define        NEXT \
+       if ((++put) % EV_QSIZE == get) { \
+               put--; \
+               goto out; \
+       }
+       /* ADVANCE completes the `put' of the event */
+#define        ADVANCE \
+       fe++; \
+       if (put >= EV_QSIZE) { \
+               put = 0; \
+               fe = &ms->ms_events.ev_q[0]; \
+       } \
+       any = 1
+
+       mb = ms->ms_mb;
+       ub = ms->ms_ub;
+       while ((d = mb ^ ub) != 0) {
+               /*
+                * Mouse button change.  Convert up to three changes
+                * to the `first' change, and drop it into the event queue.
+                */
+               NEXT;
+               d = to_one[d - 1];              /* from 1..7 to {1,2,4} */
+               fe->id = to_id[d - 1];          /* from {1,2,4} to ID */
+               fe->value = mb & d ? VKEY_DOWN : VKEY_UP;
+               fe->time = time;
+               ADVANCE;
+               ub ^= d;
+       }
+       if (ms->ms_dx) {
+               NEXT;
+               fe->id = LOC_X_DELTA;
+               fe->value = ms->ms_dx;
+               fe->time = time;
+               ADVANCE;
+               ms->ms_dx = 0;
+       }
+       if (ms->ms_dy) {
+               NEXT;
+               fe->id = LOC_Y_DELTA;
+               fe->value = ms->ms_dy;
+               fe->time = time;
+               ADVANCE;
+               ms->ms_dy = 0;
+       }
+out:
+       if (any) {
+               ms->ms_ub = ub;
+               ms->ms_events.ev_put = put;
+               EV_WAKEUP(&ms->ms_events);
+       }
+}
+
+/****************************************************************
+ * Interface to the lower layer (zscc)
+ ****************************************************************/
+
+static int
+ms_rxint(cs)
+       register struct zs_chanstate *cs;
+{
+       register struct ms_softc *ms;
+       register int put, put_next;
+       register u_char c, rr1;
+
+       ms = cs->cs_private;
+       put = ms->ms_rbput;
+
+       /* Read the input data ASAP. */
+       c = *(cs->cs_reg_data);
+       ZS_DELAY();
+
+       /* Save the status register too. */
+       rr1 = ZS_READ(cs, 1);
+
+       if (rr1 & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) {
+               /* Clear the receive error. */
+               *(cs->cs_reg_csr) = ZSWR0_RESET_ERRORS;
+               ZS_DELAY();
+       }
+
+       ms->ms_rbuf[put] = (c << 8) | rr1;
+       put_next = (put + 1) & MS_RX_RING_MASK;
+
+       /* Would overrun if increment makes (put==get). */
+       if (put_next == ms->ms_rbget) {
+               ms->ms_intr_flags |= INTR_RX_OVERRUN;
+       } else {
+               /* OK, really increment. */
+               put = put_next;
+       }
+
+       /* Done reading. */
+       ms->ms_rbput = put;
+
+       /* Ask for softint() call. */
+       cs->cs_softreq = 1;
+       return(1);
+}
+
+
+static int
+ms_txint(cs)
+       register struct zs_chanstate *cs;
+{
+       register struct ms_softc *ms;
+       register int count, rval;
+
+       ms = cs->cs_private;
+
+       *(cs->cs_reg_csr) = ZSWR0_RESET_TXINT;
+       ZS_DELAY();
+
+       ms->ms_intr_flags |= INTR_TX_EMPTY;
+       /* Ask for softint() call. */
+       cs->cs_softreq = 1;
+       return (1);
+}
+
+
+static int
+ms_stint(cs)
+       register struct zs_chanstate *cs;
+{
+       register struct ms_softc *ms;
+       register int rr0;
+
+       ms = cs->cs_private;
+
+       rr0 = *(cs->cs_reg_csr);
+       ZS_DELAY();
+
+       *(cs->cs_reg_csr) = ZSWR0_RESET_STATUS;
+       ZS_DELAY();
+
+       ms->ms_intr_flags |= INTR_ST_CHECK;
+       /* Ask for softint() call. */
+       cs->cs_softreq = 1;
+       return (1);
+}
+
+
+static int
+ms_softint(cs)
+       struct zs_chanstate *cs;
+{
+       register struct ms_softc *ms;
+       register int get, c, s;
+       int intr_flags;
+       register u_short ring_data;
+       register u_char rr0, rr1;
+
+       ms = cs->cs_private;
+
+       /* Atomically get and clear flags. */
+       s = splzs();
+       intr_flags = ms->ms_intr_flags;
+       ms->ms_intr_flags = 0;
+       splx(s);
+
+       /*
+        * Copy data from the receive ring to the event layer.
+        */
+       get = ms->ms_rbget;
+       while (get != ms->ms_rbput) {
+               ring_data = ms->ms_rbuf[get];
+               get = (get + 1) & MS_RX_RING_MASK;
+
+               /* low byte of ring_data is rr1 */
+               c = (ring_data >> 8) & 0xff;
+
+               if (ring_data & ZSRR1_DO)
+                       intr_flags |= INTR_RX_OVERRUN;
+               if (ring_data & (ZSRR1_FE | ZSRR1_PE)) {
+                       log(LOG_ERR, "%s: input error (0x%x)\n",
+                               ms->ms_dev.dv_xname, ring_data);
+                       c = -1; /* signal input error */
+               }
+
+               /* Pass this up to the "middle" layer. */
+               ms_input(ms, c);
+       }
+       if (intr_flags & INTR_RX_OVERRUN) {
+               log(LOG_ERR, "%s: input overrun\n",
+                   ms->ms_dev.dv_xname);
+       }
+       ms->ms_rbget = get;
+
+       if (intr_flags & INTR_TX_EMPTY) {
+               /*
+                * Transmit done.  (Not expected.)
+                */
+               log(LOG_ERR, "%s: transmit interrupt?\n",
+                   ms->ms_dev.dv_xname);
+       }
+
+       if (intr_flags & INTR_ST_CHECK) {
+               /*
+                * Status line change.  (Not expected.)
+                */
+               log(LOG_ERR, "%s: status interrupt?\n",
+                   ms->ms_dev.dv_xname);
+       }
+
+       return (1);
+}
+
+struct zsops zsops_ms = {
+       ms_rxint,       /* receive char available */
+       ms_stint,       /* external/status */
+       ms_txint,       /* xmit buffer empty */
+       ms_softint,     /* process software interrupt */
+};