/*      $NetBSD: pccons.c,v 1.63 2022/05/24 20:50:18 andvar Exp $       */
/*      $OpenBSD: pccons.c,v 1.22 1999/01/30 22:39:37 imp Exp $ */
/*      NetBSD: pccons.c,v 1.89 1995/05/04 19:35:20 cgd Exp     */

/*-
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* William Jolitz and Don Ahn.
*
* 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. 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.
*
*      @(#)pccons.c    5.11 (Berkeley) 5/21/91
*/

/*-
* Copyright (c) 1993, 1994, 1995 Charles M. Hannum.  All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* William Jolitz and Don Ahn.
*
* Copyright (c) 1994 Charles M. Hannum.
* Copyright (c) 1992, 1993 Erik Forsberg.
*
* 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.
*
*      @(#)pccons.c    5.11 (Berkeley) 5/21/91
*/

/*
* code to work keyboard & display for PC-style console
*/

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pccons.c,v 1.63 2022/05/24 20:50:18 andvar Exp $");

#include "opt_ddb.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/tty.h>
#include <sys/callout.h>
#include <sys/poll.h>
#include <sys/conf.h>
#include <sys/vnode.h>
#include <sys/kernel.h>
#include <sys/kcore.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/kauth.h>

#include <sys/bus.h>

#include <dev/ic/pcdisplay.h>
#include <machine/pccons.h>
#include <machine/kbdreg.h>

#include <dev/cons.h>
#include <dev/isa/isavar.h>

#include <arc/arc/arcbios.h>
#include <arc/dev/pcconsvar.h>

#include "ioconf.h"

#define XFREE86_BUG_COMPAT

#ifndef BEEP_FREQ
#define BEEP_FREQ 1600
#endif
#ifndef BEEP_TIME
#define BEEP_TIME (hz/5)
#endif

#define PCBURST 128

static u_short *Crtat;                  /* pointer to backing store */
static u_short *crtat;                  /* pointer to current char */
static u_char async, kernel, polling;   /* Really, you don't want to know. */
static u_char lock_state = 0x00,        /* all off */
             old_lock_state = 0xff,
             typematic_rate = 0xff,    /* don't update until set by user */
             old_typematic_rate = 0xff;
static u_short cursor_shape = 0xffff,   /* don't update until set by user */
              old_cursor_shape = 0xffff;
static pccons_keymap_t scan_codes[KB_NUM_KEYS];/* keyboard translation table */
int pc_xmode = 0;

/*
*  Keyboard output queue.
*/
int     kb_oq_put = 0;
int     kb_oq_get = 0;
u_char  kb_oq[8];

#define PCUNIT(x)       (minor(x))

static struct video_state {
       int     cx, cy;         /* escape parameters */
       int     row, col;       /* current cursor position */
       int     nrow, ncol, nchr;       /* current screen geometry */
       int     offset;         /* Saved cursor pos */
       u_char  state;          /* parser state */
#define VSS_ESCAPE      1
#define VSS_EBRACE      2
#define VSS_EPARAM      3
       char    so;             /* in standout mode? */
       char    color;          /* color or mono display */
       char    at;             /* normal attributes */
       char    so_at;          /* standout attributes */
} vs;

static callout_t async_update_ch;

void pc_xmode_on(void);
void pc_xmode_off(void);
static u_char kbc_get8042cmd(void);
int kbd_cmd(u_char, u_char);
static inline int kbd_wait_output(void);
static inline int kbd_wait_input(void);
void kbd_flush_input(void);
void set_cursor_shape(void);
void get_cursor_shape(void);
void async_update(void);
void do_async_update(u_char);

void pccnputc(dev_t, int c);
int pccngetc(dev_t);
void pccnpollc(dev_t, int);

dev_type_open(pcopen);
dev_type_close(pcclose);
dev_type_read(pcread);
dev_type_write(pcwrite);
dev_type_ioctl(pcioctl);
dev_type_tty(pctty);
dev_type_poll(pcpoll);
dev_type_mmap(pcmmap);

const struct cdevsw pc_cdevsw = {
       .d_open = pcopen,
       .d_close = pcclose,
       .d_read = pcread,
       .d_write = pcwrite,
       .d_ioctl = pcioctl,
       .d_stop = nostop,
       .d_tty = pctty,
       .d_poll = pcpoll,
       .d_mmap = pcmmap,
       .d_kqfilter = ttykqfilter,
       .d_discard = nodiscard,
       .d_flag = D_TTY
};

#define CHR             2

char *sget(void);
void sput(const u_char *, int);

void    pcstart(struct tty *);
int     pcparam(struct tty *, struct termios *);
static inline void wcopy(void *, void *, u_int);
void    pc_context_init(bus_space_tag_t, bus_space_tag_t, bus_space_tag_t,
           struct pccons_config *);

extern void fillw(int, uint16_t *, int);

#define KBD_DELAY \
               DELAY(10);

#define crtc_read_1(reg) \
       bus_space_read_1(pccons_console_context.pc_crt_iot, \
           pccons_console_context.pc_6845_ioh, reg)
#define crtc_write_1(reg, data) \
       bus_space_write_1(pccons_console_context.pc_crt_iot, \
           pccons_console_context.pc_6845_ioh, reg, data)

struct pccons_context pccons_console_context;

void
kbd_context_init(bus_space_tag_t kbd_iot, struct pccons_config *config)
{
       struct pccons_kbd_context *pkc = &pccons_console_context.pc_pkc;

       if (pkc->pkc_initialized)
               return;
       pkc->pkc_initialized = 1;

       pkc->pkc_iot = kbd_iot;

       bus_space_map(kbd_iot, config->pc_kbd_cmdp, 1, 0,
           &pkc->pkc_cmd_ioh);
       bus_space_map(kbd_iot, config->pc_kbd_datap, 1, 0,
           &pkc->pkc_data_ioh);
}

void
pc_context_init(bus_space_tag_t crt_iot, bus_space_tag_t crt_memt,
   bus_space_tag_t kbd_iot, struct pccons_config *config)
{
       struct pccons_context *pc = &pccons_console_context;

       if (pc->pc_initialized)
               return;
       pc->pc_initialized = 1;

       kbd_context_init(kbd_iot, config);

       pc->pc_crt_iot = crt_iot;
       pc->pc_crt_memt = crt_memt;

       bus_space_map(crt_iot, config->pc_mono_iobase, 2, 0,
           &pc->pc_mono_ioh);
       bus_space_map(crt_memt, config->pc_mono_memaddr, 0x20000, 0,
           &pc->pc_mono_memh);
       bus_space_map(crt_iot, config->pc_cga_iobase, 2, 0,
           &pc->pc_cga_ioh);
       bus_space_map(crt_memt, config->pc_cga_memaddr, 0x20000, 0,
           &pc->pc_cga_memh);

       /*
        * pc->pc_6845_ioh and pc->pc_crt_memh will be initialized later,
        * when `Crtat' is initialized.
        */

       pc->pc_config = config;

       (*config->pc_init)();
}

/*
* bcopy variant that only moves word-aligned 16-bit entities,
* for stupid VGA cards.  cnt is required to be an even value.
*/
static inline void
wcopy(void *src, void *tgt, u_int cnt)
{
       uint16_t *from = src;
       uint16_t *to = tgt;

       cnt >>= 1;
       if (to < from || to >= from + cnt)
               while (cnt--)
                       *to++ = *from++;
       else {
               to += cnt;
               from += cnt;
               while (cnt--)
                       *--to = *--from;
       }
}

static inline int
kbd_wait_output(void)
{
       u_int i;

       for (i = 100000; i; i--)
               if ((kbd_cmd_read_1() & KBS_IBF) == 0) {
                       KBD_DELAY;
                       return 1;
               }
       return 0;
}

static inline int
kbd_wait_input(void)
{
       u_int i;

       for (i = 100000; i; i--)
               if ((kbd_cmd_read_1() & KBS_DIB) != 0) {
                       KBD_DELAY;
                       return 1;
               }
       return 0;
}

void
kbd_flush_input(void)
{
       uint8_t c;

       while ((c = kbd_cmd_read_1()) & 0x03)
               if ((c & KBS_DIB) == KBS_DIB) {
                       /* XXX - delay is needed to prevent some keyboards from
                          wedging when the system boots */
                       delay(6);
                       (void)kbd_data_read_1();
               }
}

#if 1
/*
* Get the current command byte.
*/
static u_char
kbc_get8042cmd(void)
{

       if (!kbd_wait_output())
               return -1;
       kbd_cmd_write_1(K_RDCMDBYTE);
       if (!kbd_wait_input())
               return -1;
       return kbd_data_read_1();
}
#endif

/*
* Pass command byte to keyboard controller (8042).
*/
int
kbc_put8042cmd(uint8_t val)
{

       if (!kbd_wait_output())
               return 0;
       kbd_cmd_write_1(K_LDCMDBYTE);
       if (!kbd_wait_output())
               return 0;
       kbd_data_write_1(val);
       return 1;
}

/*
* Pass command to keyboard itself
*/
int
kbd_cmd(uint8_t val, uint8_t polled)
{
       u_int retries = 3;
       u_int i;

       if (!polled) {
               i = spltty();
               if (kb_oq_get == kb_oq_put) {
                       kbd_data_write_1(val);
               }
               kb_oq[kb_oq_put] = val;
               kb_oq_put = (kb_oq_put + 1) & 7;
               splx(i);
               return 1;
       }

       do {
               if (!kbd_wait_output())
                       return 0;
               kbd_data_write_1(val);
               for (i = 100000; i; i--) {
                       if (kbd_cmd_read_1() & KBS_DIB) {
                               uint8_t c;

                               KBD_DELAY;
                               c = kbd_data_read_1();
                               if (c == KBR_ACK || c == KBR_ECHO) {
                                       return 1;
                               }
                               if (c == KBR_RESEND) {
                                       break;
                               }
#ifdef DIAGNOSTIC
                               printf("kbd_cmd: input char %x lost\n", c);
#endif
                       }
               }
       } while (--retries);
       return 0;
}

void
set_cursor_shape(void)
{

       crtc_write_1(0, 10);
       crtc_write_1(1, cursor_shape >> 8);
       crtc_write_1(0, 11);
       crtc_write_1(1, cursor_shape);
       old_cursor_shape = cursor_shape;
}

void
get_cursor_shape(void)
{

       crtc_write_1(0, 10);
       cursor_shape = crtc_read_1(1) << 8;
       crtc_write_1(0, 11);
       cursor_shape |= crtc_read_1(1);

       /*
        * real 6845's, as found on, MDA, Hercules or CGA cards, do
        * not support reading the cursor shape registers. the 6845
        * tri-states its data bus. This is _normally_ read by the
        * CPU as either 0x00 or 0xff.. in which case we just use
        * a line cursor.
        */
       if (cursor_shape == 0x0000 || cursor_shape == 0xffff)
               cursor_shape = 0x0b10;
       else
               cursor_shape &= 0x1f1f;
}

void
do_async_update(uint8_t poll)
{
       int pos;
       static int old_pos = -1;

       async = 0;

       if (lock_state != old_lock_state) {
               old_lock_state = lock_state;
               if (!kbd_cmd(KBC_MODEIND, poll) ||
                   !kbd_cmd(lock_state, poll)) {
                       printf("pc: timeout updating leds\n");
                       (void) kbd_cmd(KBC_ENABLE, poll);
               }
       }
       if (typematic_rate != old_typematic_rate) {
               old_typematic_rate = typematic_rate;
               if (!kbd_cmd(KBC_TYPEMATIC, poll) ||
                   !kbd_cmd(typematic_rate, poll)) {
                       printf("pc: timeout updating typematic rate\n");
                       (void) kbd_cmd(KBC_ENABLE, poll);
               }
       }

       if (pc_xmode > 0)
               return;

       pos = crtat - Crtat;
       if (pos != old_pos) {
               crtc_write_1(0, 14);
               crtc_write_1(1, pos >> 8);
               crtc_write_1(0, 15);
               crtc_write_1(1, pos);
               old_pos = pos;
       }
       if (cursor_shape != old_cursor_shape)
               set_cursor_shape();
}

void
async_update(void)
{

       if (kernel || polling) {
               if (async)
                       callout_stop(&async_update_ch);
               do_async_update(1);
       } else {
               if (async)
                       return;
               async = 1;
               callout_reset(&async_update_ch, 1,
                   (void(*)(void *))do_async_update, NULL);
       }
}

/*
* these are both bad jokes
*/
int
pccons_common_match(bus_space_tag_t crt_iot, bus_space_tag_t crt_memt,
   bus_space_tag_t kbd_iot, struct pccons_config *config)
{
       int i;

       pc_context_init(crt_iot, crt_memt, kbd_iot, config);

       /* Enable interrupts and keyboard, etc. */
       if (!kbc_put8042cmd(CMDBYTE)) {
               printf("pcprobe: command error\n");
               return 0;
       }

#if 1
       /* Flush any garbage. */
       kbd_flush_input();
       /* Reset the keyboard. */
       if (!kbd_cmd(KBC_RESET, 1)) {
               printf("pcprobe: reset error %d\n", 1);
               goto lose;
       }
       for (i = 600000; i; i--)
               if ((kbd_cmd_read_1() & KBS_DIB) != 0) {
                       KBD_DELAY;
                       break;
               }
       if (i == 0 || kbd_data_read_1() != KBR_RSTDONE) {
               printf("pcprobe: reset error %d\n", 2);
               goto lose;
       }
       /*
        * Some keyboards seem to leave a second ack byte after the reset.
        * This is kind of stupid, but we account for them anyway by just
        * flushing the buffer.
        */
       kbd_flush_input();
       /* Just to be sure. */
       if (!kbd_cmd(KBC_ENABLE, 1)) {
               printf("pcprobe: reset error %d\n", 3);
               goto lose;
       }

       /*
        * Some keyboard/8042 combinations do not seem to work if the keyboard
        * is set to table 1; in fact, it would appear that some keyboards just
        * ignore the command altogether.  So by default, we use the AT scan
        * codes and have the 8042 translate them.  Unfortunately, this is
        * known to not work on some PS/2 machines.  We try desperately to deal
        * with this by checking the (lack of a) translate bit in the 8042 and
        * attempting to set the keyboard to XT mode.  If this all fails, well,
        * tough luck.
        *
        * XXX It would perhaps be a better choice to just use AT scan codes
        * and not bother with this.
        */
       if (kbc_get8042cmd() & KC8_TRANS) {
               /* The 8042 is translating for us; use AT codes. */
               if (!kbd_cmd(KBC_SETTABLE, 1) || !kbd_cmd(2, 1)) {
                       printf("pcprobe: reset error %d\n", 4);
                       goto lose;
               }
       } else {
               /* Stupid 8042; set keyboard to XT codes. */
               if (!kbd_cmd(KBC_SETTABLE, 1) || !kbd_cmd(1, 1)) {
                       printf("pcprobe: reset error %d\n", 5);
                       goto lose;
               }
       }

lose:
       /*
        * Technically, we should probably fail the probe.  But we'll be nice
        * and allow keyboard-less machines to boot with the console.
        */
#endif

       return 1;
}

void pccons_common_attach(struct pc_softc *sc, bus_space_tag_t crt_iot,
   bus_space_tag_t crt_memt, bus_space_tag_t kbd_iot,
   struct pccons_config *config)
{

       printf(": %s\n", vs.color ? "color" : "mono");
       callout_init(&async_update_ch, 0);
       do_async_update(1);
}

int
pcopen(dev_t dev, int flag, int mode, struct lwp *l)
{
       struct pc_softc *sc;
       struct tty *tp;

       sc = device_lookup_private(&pc_cd, PCUNIT(dev));
       if (sc == NULL)
               return ENXIO;

       if (!sc->sc_tty) {
               tp = sc->sc_tty = tty_alloc();
       }
       else {
               tp = sc->sc_tty;
       }

       tp->t_oproc = pcstart;
       tp->t_param = pcparam;
       tp->t_dev = dev;

       if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
               return (EBUSY);

       if ((tp->t_state & TS_ISOPEN) == 0) {
               ttychars(tp);
               tp->t_iflag = TTYDEF_IFLAG;
               tp->t_oflag = TTYDEF_OFLAG;
               tp->t_cflag = TTYDEF_CFLAG;
               tp->t_lflag = TTYDEF_LFLAG;
               tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
               pcparam(tp, &tp->t_termios);
               ttsetwater(tp);
       }

       tp->t_state |= TS_CARR_ON;

       return (*tp->t_linesw->l_open)(dev, tp);
}

int
pcclose(dev_t dev, int flag, int mode, struct lwp *l)
{
       struct pc_softc *sc = device_lookup_private(&pc_cd, PCUNIT(dev));
       struct tty *tp = sc->sc_tty;

       (*tp->t_linesw->l_close)(tp, flag);
       ttyclose(tp);
#ifdef notyet /* XXX */
       tty_free(tp);
#endif
       return 0;
}

int
pcread(dev_t dev, struct uio *uio, int flag)
{
       struct pc_softc *sc = device_lookup_private(&pc_cd, PCUNIT(dev));
       struct tty *tp = sc->sc_tty;

       return (*tp->t_linesw->l_read)(tp, uio, flag);
}

int
pcwrite(dev_t dev, struct uio *uio, int flag)
{
       struct pc_softc *sc = device_lookup_private(&pc_cd, PCUNIT(dev));
       struct tty *tp = sc->sc_tty;

       return (*tp->t_linesw->l_write)(tp, uio, flag);
}

int
pcpoll(dev_t dev, int events, struct lwp *l)
{
       struct pc_softc *sc = device_lookup_private(&pc_cd, PCUNIT(dev));
       struct tty *tp = sc->sc_tty;

       return (*tp->t_linesw->l_poll)(tp, events, l);
}

struct tty *
pctty(dev_t dev)
{
       struct pc_softc *sc = device_lookup_private(&pc_cd, PCUNIT(dev));
       struct tty *tp = sc->sc_tty;

       return tp;
}

/*
* Got a console receive interrupt -
* the console processor wants to give us a character.
* Catch the character, and see who it goes to.
*/
int
pcintr(void *arg)
{
       struct pc_softc *sc = arg;
       struct tty *tp = sc->sc_tty;
       uint8_t *cp;

       if ((kbd_cmd_read_1() & KBS_DIB) == 0)
               return 0;
       if (polling)
               return 1;
       do {
               cp = sget();
               if (!tp || (tp->t_state & TS_ISOPEN) == 0)
                       return 1;
               if (cp)
                       do
                               (*tp->t_linesw->l_rint)(*cp++, tp);
                       while (*cp);
       } while (kbd_cmd_read_1() & KBS_DIB);
       return 1;
}

int
pcioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
{
       struct pc_softc *sc = device_lookup_private(&pc_cd, PCUNIT(dev));
       struct tty *tp = sc->sc_tty;
       int error;

       error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
       if (error != EPASSTHROUGH)
               return error;
       error = ttioctl(tp, cmd, data, flag, l);
       if (error != EPASSTHROUGH)
               return error;

       switch (cmd) {
       case CONSOLE_X_MODE_ON:
               pc_xmode_on();
               return 0;
       case CONSOLE_X_MODE_OFF:
               pc_xmode_off();
               return 0;
       case CONSOLE_X_BELL:
               /*
                * If set, data is a pointer to a length 2 array of
                * integers.  data[0] is the pitch in Hz and data[1]
                * is the duration in msec.
                */
               if (data)
                       sysbeep(((int*)data)[0],
                               (((int*)data)[1] * hz) / 1000);
               else
                       sysbeep(BEEP_FREQ, BEEP_TIME);
               return 0;
       case CONSOLE_SET_TYPEMATIC_RATE: {
               u_char  rate;

               if (!data)
                       return EINVAL;
               rate = *((u_char *)data);
               /*
                * Check that it isn't too big (which would cause it to be
                * confused with a command).
                */
               if (rate & 0x80)
                       return EINVAL;
               typematic_rate = rate;
               async_update();
               return 0;
       }
       case CONSOLE_SET_KEYMAP: {
               pccons_keymap_t *map = (pccons_keymap_t *) data;
               int i;

               if (!data)
                       return EINVAL;
               for (i = 0; i < KB_NUM_KEYS; i++)
                       if (map[i].unshift[KB_CODE_SIZE-1] ||
                           map[i].shift[KB_CODE_SIZE-1] ||
                           map[i].ctl[KB_CODE_SIZE-1] ||
                           map[i].altgr[KB_CODE_SIZE-1] ||
                           map[i].shift_altgr[KB_CODE_SIZE-1])
                               return EINVAL;

               memcpy(scan_codes, data, sizeof(pccons_keymap_t[KB_NUM_KEYS]));
               return 0;
       }
       case CONSOLE_GET_KEYMAP:
               if (!data)
                       return EINVAL;
               memcpy(scan_codes, data, sizeof(pccons_keymap_t[KB_NUM_KEYS]));
               return 0;

       default:
               return EPASSTHROUGH;
       }

#ifdef DIAGNOSTIC
       panic("pcioctl: impossible");
#endif
}

void
pcstart(struct tty *tp)
{
       struct clist *cl;
       int s, len;
       u_char buf[PCBURST];

       s = spltty();
       if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))
               goto out;
       tp->t_state |= TS_BUSY;
       splx(s);
       /*
        * We need to do this outside spl since it could be fairly
        * expensive and we don't want our serial ports to overflow.
        */
       cl = &tp->t_outq;
       len = q_to_b(cl, buf, PCBURST);
       sput(buf, len);
       s = spltty();
       tp->t_state &= ~TS_BUSY;
       if (ttypull(tp)) {
               tp->t_state |= TS_TIMEOUT;
               callout_schedule(&tp->t_rstrt_ch, 1);
       }
out:
       splx(s);
}

/* ARGSUSED */
void pccons_common_cnattach(bus_space_tag_t crt_iot, bus_space_tag_t crt_memt,
   bus_space_tag_t kbd_iot, struct pccons_config *config)
{
       int maj;
       static struct consdev pccons = {
               NULL, NULL, pccngetc, pccnputc, pccnpollc, NULL, NULL,
                   NULL, NODEV, CN_NORMAL
       };

       /*
        * For now, don't screw with it.
        */
       /* crtat = 0; */

       pc_context_init(crt_iot, crt_memt, kbd_iot, config);

       /* locate the major number */
       maj = cdevsw_lookup_major(&pc_cdevsw);
       pccons.cn_dev = makedev(maj, 0);

       cn_tab = &pccons;
}

/* ARGSUSED */
void
pccnputc(dev_t dev, int c)
{
       u_char cc, oldkernel = kernel;

       kernel = 1;
       if (c == '\n') {
               sput("\r\n", 2);
       } else {
               cc = c;
               sput(&cc, 1);
       }
       kernel = oldkernel;
}

/* ARGSUSED */
int
pccngetc(dev_t dev)
{
       char *cp;

       if (pc_xmode > 0)
               return 0;

       do {
               /* wait for byte */
               while ((kbd_cmd_read_1() & KBS_DIB) == 0);
               /* see if it's worthwhile */
               cp = sget();
       } while (!cp);
       if (*cp == '\r')
               return '\n';
       return *cp;
}

void
pccnpollc(dev_t dev, int on)
{

       polling = on;
       if (!on) {
               int unit;
               struct pc_softc *sc;
               int s;

               /*
                * If disabling polling on a device that's been configured,
                * make sure there are no bytes left in the FIFO, holding up
                * the interrupt line.  Otherwise we won't get any further
                * interrupts.
                */
               unit = PCUNIT(dev);
               if (pc_cd.cd_ndevs > unit) {
                       sc = device_lookup_private(&pc_cd, unit);
                       if (sc != NULL) {
                               s = spltty();
                               pcintr(sc);
                               splx(s);
                       }
               }
       }
}

/*
* Set line parameters.
*/
int
pcparam(struct tty *tp, struct termios *t)
{

       tp->t_ispeed = t->c_ispeed;
       tp->t_ospeed = t->c_ospeed;
       tp->t_cflag = t->c_cflag;
       return 0;
}

#define wrtchar(c, at) do {\
       char *cp0 = (char *)crtat; *cp0++ = (c); *cp0 = (at); crtat++; vs.col++; \
} while (0)

/* translate ANSI color codes to standard pc ones */
static char fgansitopc[] = {
       FG_BLACK, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE,
       FG_MAGENTA, FG_CYAN, FG_LIGHTGREY
};

static char bgansitopc[] = {
       BG_BLACK, BG_RED, BG_GREEN, BG_BROWN, BG_BLUE,
       BG_MAGENTA, BG_CYAN, BG_LIGHTGREY
};

static u_char iso2ibm437[] =
{
           0,     0,     0,     0,     0,     0,     0,     0,
           0,     0,     0,     0,     0,     0,     0,     0,
           0,     0,     0,     0,     0,     0,     0,     0,
           0,     0,     0,     0,     0,     0,     0,     0,
        0xff,  0xad,  0x9b,  0x9c,     0,  0x9d,     0,  0x40,
        0x6f,  0x63,  0x61,  0xae,     0,     0,     0,     0,
        0xf8,  0xf1,  0xfd,  0x33,     0,  0xe6,     0,  0xfa,
           0,  0x31,  0x6f,  0xaf,  0xac,  0xab,     0,  0xa8,
        0x41,  0x41,  0x41,  0x41,  0x8e,  0x8f,  0x92,  0x80,
        0x45,  0x90,  0x45,  0x45,  0x49,  0x49,  0x49,  0x49,
        0x81,  0xa5,  0x4f,  0x4f,  0x4f,  0x4f,  0x99,  0x4f,
        0x4f,  0x55,  0x55,  0x55,  0x9a,  0x59,     0,  0xe1,
        0x85,  0xa0,  0x83,  0x61,  0x84,  0x86,  0x91,  0x87,
        0x8a,  0x82,  0x88,  0x89,  0x8d,  0xa1,  0x8c,  0x8b,
           0,  0xa4,  0x95,  0xa2,  0x93,  0x6f,  0x94,  0x6f,
        0x6f,  0x97,  0xa3,  0x96,  0x81,  0x98,     0,     0
};

/*
* `pc3' termcap emulation.
*/
void
sput(const u_char *cp, int n)
{
       struct pccons_context *pc = &pccons_console_context;
       u_char c, scroll = 0;

       if (pc_xmode > 0)
               return;

       if (crtat == 0) {
               volatile u_short *dp;
               u_short was;
               unsigned cursorat;

               dp = bus_space_vaddr(pc->pc_crt_memt, pc->pc_cga_memh);
               was = *dp;
               *dp = 0xA55A;
               if (*dp != 0xA55A) {
                       dp = bus_space_vaddr(pc->pc_crt_memt,
                           pc->pc_mono_memh);
                       pc->pc_6845_ioh = pc->pc_mono_ioh;
                       pc->pc_crt_memh = pc->pc_mono_memh;
                       vs.color = 0;
               } else {
                       *dp = was;
                       pc->pc_6845_ioh = pc->pc_cga_ioh;
                       pc->pc_crt_memh = pc->pc_cga_memh;
                       vs.color = 1;
               }

#ifdef FAT_CURSOR
               cursor_shape = 0x0012;
#else
               get_cursor_shape();
#endif

               bios_display_info(&vs.col, &vs.row, &vs.ncol, &vs.nrow);
               vs.nchr = vs.ncol * vs.nrow;
               vs.col--;
               vs.row--;
               cursorat = vs.ncol * vs.row + vs.col;
               vs.at = FG_LIGHTGREY | BG_BLACK;

               Crtat = (u_short *)__UNVOLATILE(dp);
               crtat = Crtat + cursorat;

               if (vs.color == 0)
                       vs.so_at = FG_BLACK | BG_LIGHTGREY;
               else
                       vs.so_at = FG_YELLOW | BG_BLACK;

               fillw((vs.at << 8) | ' ', crtat, vs.nchr - cursorat);
       }

       while (n--) {
               if (!(c = *cp++))
                       continue;

               switch (c) {
               case 0x1B:
                       if (vs.state >= VSS_ESCAPE) {
                               wrtchar(c, vs.so_at);
                               vs.state = 0;
                               goto maybe_scroll;
                       } else
                               vs.state = VSS_ESCAPE;
                       break;

               case 0x9B:      /* CSI */
                       vs.cx = vs.cy = 0;
                       vs.state = VSS_EBRACE;
                       break;

               case '\t': {
                       int inccol = 8 - (vs.col & 7);
                       crtat += inccol;
                       vs.col += inccol;
               }
               maybe_scroll:
                       if (vs.col >= vs.ncol) {
                               vs.col -= vs.ncol;
                               scroll = 1;
                       }
                       break;

               case '\b':
                       if (crtat <= Crtat)
                               break;
                       --crtat;
                       if (--vs.col < 0)
                               vs.col += vs.ncol;      /* non-destructive backspace */
                       break;

               case '\r':
                       crtat -= vs.col;
                       vs.col = 0;
                       break;

               case '\n':
                       crtat += vs.ncol;
                       scroll = 1;
                       break;

               default:
                       switch (vs.state) {
                       case 0:
                               if (c == '\a')
                                       sysbeep(BEEP_FREQ, BEEP_TIME);
                               else {
                                       /*
                                        * If we're outputting multiple printed
                                        * characters, just blast them to the
                                        * screen until we reach the end of the
                                        * buffer or a control character.  This
                                        * saves time by short-circuiting the
                                        * switch.
                                        * If we reach the end of the line, we
                                        * break to do a scroll check.
                                        */
                                       for (;;) {
                                               if (c & 0x80)
                                                       c = iso2ibm437[c&0x7f];

                                               if (vs.so)
                                                       wrtchar(c, vs.so_at);
                                               else
                                                       wrtchar(c, vs.at);
                                               if (vs.col >= vs.ncol) {
                                                       vs.col = 0;
                                                       scroll = 1;
                                                       break;
                                               }
                                               if (!n || (c = *cp) < ' ')
                                                       break;
                                               n--, cp++;
                                       }
                               }
                               break;
                       case VSS_ESCAPE:
                               switch (c) {
                                       case '[': /* Start ESC [ sequence */
                                               vs.cx = vs.cy = 0;
                                               vs.state = VSS_EBRACE;
                                               break;
                                       case 'c': /* Create screen & home */
                                               fillw((vs.at << 8) | ' ',
                                                   Crtat, vs.nchr);
                                               crtat = Crtat;
                                               vs.col = 0;
                                               vs.state = 0;
                                               break;
                                       case '7': /* save cursor pos */
                                               vs.offset = crtat - Crtat;
                                               vs.state = 0;
                                               break;
                                       case '8': /* restore cursor pos */
                                               crtat = Crtat + vs.offset;
                                               vs.row = vs.offset / vs.ncol;
                                               vs.col = vs.offset % vs.ncol;
                                               vs.state = 0;
                                               break;
                                       default: /* Invalid, clear state */
                                               wrtchar(c, vs.so_at);
                                               vs.state = 0;
                                               goto maybe_scroll;
                               }
                               break;

                       default: /* VSS_EBRACE or VSS_EPARAM */
                               switch (c) {
                                       int pos;
                               case 'm':
                                       if (!vs.cx)
                                               vs.so = 0;
                                       else
                                               vs.so = 1;
                                       vs.state = 0;
                                       break;
                               case 'A': { /* back cx rows */
                                       int cx = vs.cx;
                                       if (cx <= 0)
                                               cx = 1;
                                       else
                                               cx %= vs.nrow;
                                       pos = crtat - Crtat;
                                       pos -= vs.ncol * cx;
                                       if (pos < 0)
                                               pos += vs.nchr;
                                       crtat = Crtat + pos;
                                       vs.state = 0;
                                       break;
                               }
                               case 'B': { /* down cx rows */
                                       int cx = vs.cx;
                                       if (cx <= 0)
                                               cx = 1;
                                       else
                                               cx %= vs.nrow;
                                       pos = crtat - Crtat;
                                       pos += vs.ncol * cx;
                                       if (pos >= vs.nchr)
                                               pos -= vs.nchr;
                                       crtat = Crtat + pos;
                                       vs.state = 0;
                                       break;
                               }
                               case 'C': { /* right cursor */
                                       int cx = vs.cx,
                                           col = vs.col;
                                       if (cx <= 0)
                                               cx = 1;
                                       else
                                               cx %= vs.ncol;
                                       pos = crtat - Crtat;
                                       pos += cx;
                                       col += cx;
                                       if (col >= vs.ncol) {
                                               pos -= vs.ncol;
                                               col -= vs.ncol;
                                       }
                                       vs.col = col;
                                       crtat = Crtat + pos;
                                       vs.state = 0;
                                       break;
                               }
                               case 'D': { /* left cursor */
                                       int cx = vs.cx,
                                           col = vs.col;
                                       if (cx <= 0)
                                               cx = 1;
                                       else
                                               cx %= vs.ncol;
                                       pos = crtat - Crtat;
                                       pos -= cx;
                                       col -= cx;
                                       if (col < 0) {
                                               pos += vs.ncol;
                                               col += vs.ncol;
                                       }
                                       vs.col = col;
                                       crtat = Crtat + pos;
                                       vs.state = 0;
                                       break;
                               }
                               case 'J': /* Clear ... */
                                       switch (vs.cx) {
                                       case 0:
                                               /* ... to end of display */
                                               fillw((vs.at << 8) | ' ',
                                                   crtat,
                                                   Crtat + vs.nchr - crtat);
                                               break;
                                       case 1:
                                               /* ... to next location */
                                               fillw((vs.at << 8) | ' ',
                                                   Crtat,
                                                   crtat - Crtat + 1);
                                               break;
                                       case 2:
                                               /* ... whole display */
                                               fillw((vs.at << 8) | ' ',
                                                   Crtat,
                                                   vs.nchr);
                                               break;
                                       }
                                       vs.state = 0;
                                       break;
                               case 'K': /* Clear line ... */
                                       switch (vs.cx) {
                                       case 0:
                                               /* ... current to EOL */
                                               fillw((vs.at << 8) | ' ',
                                                   crtat,
                                                   vs.ncol - vs.col);
                                               break;
                                       case 1:
                                               /* ... beginning to next */
                                               fillw((vs.at << 8) | ' ',
                                                   crtat - vs.col,
                                                   vs.col + 1);
                                               break;
                                       case 2:
                                               /* ... entire line */
                                               fillw((vs.at << 8) | ' ',
                                                   crtat - vs.col, vs.ncol);
                                               break;
                                       }
                                       vs.state = 0;
                                       break;
                               case 'f': /* in system V consoles */
                               case 'H': { /* Cursor move */
                                       int cx = vs.cx,
                                           cy = vs.cy;
                                       if (!cx || !cy) {
                                               crtat = Crtat;
                                               vs.col = 0;
                                       } else {
                                               if (cx > vs.nrow)
                                                       cx = vs.nrow;
                                               if (cy > vs.ncol)
                                                       cy = vs.ncol;
                                               crtat = Crtat +
                                                   (cx - 1) * vs.ncol + cy - 1;
                                               vs.col = cy - 1;
                                       }
                                       vs.state = 0;
                                       break;
                               }
                               case 'M': { /* delete cx rows */
                                       u_short *crtAt = crtat - vs.col;
                                       int cx = vs.cx,
                                           row = (crtAt - Crtat) / vs.ncol,
                                           nrow = vs.nrow - row;
                                       if (cx <= 0)
                                               cx = 1;
                                       else if (cx > nrow)
                                               cx = nrow;
                                       if (cx < nrow)
#ifdef PCCONS_FORCE_WORD
                                               wcopy(crtAt + vs.ncol * cx,
                                                   crtAt, vs.ncol * (nrow -
                                                   cx) * CHR);
#else
                                               memmove(crtAt,
                                                   crtAt + vs.ncol * cx,
                                                   vs.ncol * (nrow - cx) *
                                                   CHR);
#endif
                                       fillw((vs.at << 8) | ' ',
                                           crtAt + vs.ncol * (nrow - cx),
                                           vs.ncol * cx);
                                       vs.state = 0;
                                       break;
                               }
                               case 'S': { /* scroll up cx lines */
                                       int cx = vs.cx;
                                       if (cx <= 0)
                                               cx = 1;
                                       else if (cx > vs.nrow)
                                               cx = vs.nrow;
                                       if (cx < vs.nrow)
#ifdef PCCONS_FORCE_WORD
                                               wcopy(Crtat + vs.ncol * cx,
                                                   Crtat, vs.ncol * (vs.nrow -
                                                   cx) * CHR);
#else
                                               memmove(Crtat,
                                                   Crtat + vs.ncol * cx,
                                                   vs.ncol * (vs.nrow - cx) *
                                                   CHR);
#endif
                                       fillw((vs.at << 8) | ' ',
                                           Crtat + vs.ncol * (vs.nrow - cx),
                                           vs.ncol * cx);
                                       /* crtat -= vs.ncol * cx; XXX */
                                       vs.state = 0;
                                       break;
                               }
                               case 'L': { /* insert cx rows */
                                       u_short *crtAt = crtat - vs.col;
                                       int cx = vs.cx,
                                           row = (crtAt - Crtat) / vs.ncol,
                                           nrow = vs.nrow - row;
                                       if (cx <= 0)
                                               cx = 1;
                                       else if (cx > nrow)
                                               cx = nrow;
                                       if (cx < nrow)
#ifdef PCCONS_FORCE_WORD
                                               wcopy(crtAt,
                                                   crtAt + vs.ncol * cx,
                                                   vs.ncol * (nrow - cx) *
                                                   CHR);
#else
                                               memmove(crtAt + vs.ncol * cx,
                                                   crtAt,
                                                   vs.ncol * (nrow - cx) *
                                                   CHR);
#endif
                                       fillw((vs.at << 8) | ' ', crtAt,
                                           vs.ncol * cx);
                                       vs.state = 0;
                                       break;
                               }
                               case 'T': { /* scroll down cx lines */
                                       int cx = vs.cx;
                                       if (cx <= 0)
                                               cx = 1;
                                       else if (cx > vs.nrow)
                                               cx = vs.nrow;
                                       if (cx < vs.nrow)
#ifdef PCCONS_FORCE_WORD
                                               wcopy(Crtat,
                                                   Crtat + vs.ncol * cx,
                                                   vs.ncol * (vs.nrow - cx) *
                                                   CHR);
#else
                                               memmove(Crtat + vs.ncol * cx,
                                                   Crtat,
                                                   vs.ncol * (vs.nrow - cx) *
                                                   CHR);
#endif
                                       fillw((vs.at << 8) | ' ', Crtat,
                                           vs.ncol * cx);
                                       /* crtat += vs.ncol * cx; XXX */
                                       vs.state = 0;
                                       break;
                               }
                               case ';': /* Switch params in cursor def */
                                       vs.state = VSS_EPARAM;
                                       break;
                               case 'r':
                                       vs.so_at = (vs.cx & FG_MASK) |
                                           ((vs.cy << 4) & BG_MASK);
                                       vs.state = 0;
                                       break;
                               case 's': /* save cursor pos */
                                       vs.offset = crtat - Crtat;
                                       vs.state = 0;
                                       break;
                               case 'u': /* restore cursor pos */
                                       crtat = Crtat + vs.offset;
                                       vs.row = vs.offset / vs.ncol;
                                       vs.col = vs.offset % vs.ncol;
                                       vs.state = 0;
                                       break;
                               case 'x': /* set attributes */
                                       switch (vs.cx) {
                                       case 0:
                                               vs.at = FG_LIGHTGREY | BG_BLACK;
                                               break;
                                       case 1:
                                               /* ansi background */
                                               if (!vs.color)
                                                       break;
                                               vs.at &= FG_MASK;
                                               vs.at |= bgansitopc[vs.cy & 7];
                                               break;
                                       case 2:
                                               /* ansi foreground */
                                               if (!vs.color)
                                                       break;
                                               vs.at &= BG_MASK;
                                               vs.at |= fgansitopc[vs.cy & 7];
                                               break;
                                       case 3:
                                               /* pc text attribute */
                                               if (vs.state >= VSS_EPARAM)
                                                       vs.at = vs.cy;
                                               break;
                                       }
                                       vs.state = 0;
                                       break;

                               default: /* Only numbers valid here */
                                       if ((c >= '0') && (c <= '9')) {
                                               if (vs.state >= VSS_EPARAM) {
                                                       vs.cy *= 10;
                                                       vs.cy += c - '0';
                                               } else {
                                                       vs.cx *= 10;
                                                       vs.cx += c - '0';
                                               }
                                       } else
                                               vs.state = 0;
                                       break;
                               }
                               break;
                       }
               }
               if (scroll) {
                       scroll = 0;
                       /* scroll check */
                       if (crtat >= Crtat + vs.nchr) {
                               if (!kernel) {
                                       int s = spltty();
                                       if (lock_state & KB_SCROLL)
                                               tsleep(&lock_state,
                                                   PUSER, "pcputc", 0);
                                       splx(s);
                               }
#if PCCONS_FORCE_WORD
                               wcopy(Crtat + vs.ncol, Crtat,
                                   (vs.nchr - vs.ncol) * CHR);
#else
                               memmove(Crtat, Crtat + vs.ncol,
                                   (vs.nchr - vs.ncol) * CHR);
#endif
                               fillw((vs.at << 8) | ' ',
                                   Crtat + vs.nchr - vs.ncol,
                                   vs.ncol);
                               crtat -= vs.ncol;
                       }
               }
       }
       async_update();
}

/* the unshifted code for KB_SHIFT keys is used by X to distinguish between
  left and right shift when reading the keyboard map */
static pccons_keymap_t  scan_codes[KB_NUM_KEYS] = {
/*  type       unshift   shift     control   altgr     shift_altgr scancode */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 0 unused */
 { KB_ASCII,  "\033",   "\033",   "\033",   "",       ""}, /* 1 ESCape */
 { KB_ASCII,  "1",      "!",      "!",      "",       ""}, /* 2 1 */
 { KB_ASCII,  "2",      "@",      "\000",   "",       ""}, /* 3 2 */
 { KB_ASCII,  "3",      "#",      "#",      "",       ""}, /* 4 3 */
 { KB_ASCII,  "4",      "$",      "$",      "",       ""}, /* 5 4 */
 { KB_ASCII,  "5",      "%",      "%",      "",       ""}, /* 6 5 */
 { KB_ASCII,  "6",      "^",      "\036",   "",       ""}, /* 7 6 */
 { KB_ASCII,  "7",      "&",      "&",      "",       ""}, /* 8 7 */
 { KB_ASCII,  "8",      "*",      "\010",   "",       ""}, /* 9 8 */
 { KB_ASCII,  "9",      "(",      "(",      "",       ""}, /* 10 9 */
 { KB_ASCII,  "0",      ")",      ")",      "",       ""}, /* 11 0 */
 { KB_ASCII,  "-",      "_",      "\037",   "",       ""}, /* 12 - */
 { KB_ASCII,  "=",      "+",      "+",      "",       ""}, /* 13 = */
 { KB_ASCII,  "\177",   "\177",   "\010",   "",       ""}, /* 14 backspace */
 { KB_ASCII,  "\t",     "\t",     "\t",     "",       ""}, /* 15 tab */
 { KB_ASCII,  "q",      "Q",      "\021",   "",       ""}, /* 16 q */
 { KB_ASCII,  "w",      "W",      "\027",   "",       ""}, /* 17 w */
 { KB_ASCII,  "e",      "E",      "\005",   "",       ""}, /* 18 e */
 { KB_ASCII,  "r",      "R",      "\022",   "",       ""}, /* 19 r */
 { KB_ASCII,  "t",      "T",      "\024",   "",       ""}, /* 20 t */
 { KB_ASCII,  "y",      "Y",      "\031",   "",       ""}, /* 21 y */
 { KB_ASCII,  "u",      "U",      "\025",   "",       ""}, /* 22 u */
 { KB_ASCII,  "i",      "I",      "\011",   "",       ""}, /* 23 i */
 { KB_ASCII,  "o",      "O",      "\017",   "",       ""}, /* 24 o */
 { KB_ASCII,  "p",      "P",      "\020",   "",       ""}, /* 25 p */
 { KB_ASCII,  "[",      "{",      "\033",   "",       ""}, /* 26 [ */
 { KB_ASCII,  "]",      "}",      "\035",   "",       ""}, /* 27 ] */
 { KB_ASCII,  "\r",     "\r",     "\n",     "",       ""}, /* 28 return */
 { KB_CTL,    "",       "",       "",       "",       ""}, /* 29 control */
 { KB_ASCII,  "a",      "A",      "\001",   "",       ""}, /* 30 a */
 { KB_ASCII,  "s",      "S",      "\023",   "",       ""}, /* 31 s */
 { KB_ASCII,  "d",      "D",      "\004",   "",       ""}, /* 32 d */
 { KB_ASCII,  "f",      "F",      "\006",   "",       ""}, /* 33 f */
 { KB_ASCII,  "g",      "G",      "\007",   "",       ""}, /* 34 g */
 { KB_ASCII,  "h",      "H",      "\010",   "",       ""}, /* 35 h */
 { KB_ASCII,  "j",      "J",      "\n",     "",       ""}, /* 36 j */
 { KB_ASCII,  "k",      "K",      "\013",   "",       ""}, /* 37 k */
 { KB_ASCII,  "l",      "L",      "\014",   "",       ""}, /* 38 l */
 { KB_ASCII,  ";",      ":",      ";",      "",       ""}, /* 39 ; */
 { KB_ASCII,  "'",      "\"",     "'",      "",       ""}, /* 40 ' */
 { KB_ASCII,  "`",      "~",      "`",      "",       ""}, /* 41 ` */
 { KB_SHIFT,  "\001",   "",       "",       "",       ""}, /* 42 shift */
 { KB_ASCII,  "\\",     "|",      "\034",   "",       ""}, /* 43 \ */
 { KB_ASCII,  "z",      "Z",      "\032",   "",       ""}, /* 44 z */
 { KB_ASCII,  "x",      "X",      "\030",   "",       ""}, /* 45 x */
 { KB_ASCII,  "c",      "C",      "\003",   "",       ""}, /* 46 c */
 { KB_ASCII,  "v",      "V",      "\026",   "",       ""}, /* 47 v */
 { KB_ASCII,  "b",      "B",      "\002",   "",       ""}, /* 48 b */
 { KB_ASCII,  "n",      "N",      "\016",   "",       ""}, /* 49 n */
 { KB_ASCII,  "m",      "M",      "\r",     "",       ""}, /* 50 m */
 { KB_ASCII,  ",",      "<",      "<",      "",       ""}, /* 51 , */
 { KB_ASCII,  ".",      ">",      ">",      "",       ""}, /* 52 . */
 { KB_ASCII,  "/",      "?",      "\037",   "",       ""}, /* 53 / */
 { KB_SHIFT,  "\002",   "",       "",       "",       ""}, /* 54 shift */
 { KB_KP,     "*",      "*",      "*",      "",       ""}, /* 55 kp * */
 { KB_ALT,    "",       "",       "",       "",       ""}, /* 56 alt */
 { KB_ASCII,  " ",      " ",      "\000",   "",       ""}, /* 57 space */
 { KB_CAPS,   "",       "",       "",       "",       ""}, /* 58 caps */
 { KB_FUNC,   "\033[M", "\033[Y", "\033[k", "",       ""}, /* 59 f1 */
 { KB_FUNC,   "\033[N", "\033[Z", "\033[l", "",       ""}, /* 60 f2 */
 { KB_FUNC,   "\033[O", "\033[a", "\033[m", "",       ""}, /* 61 f3 */
 { KB_FUNC,   "\033[P", "\033[b", "\033[n", "",       ""}, /* 62 f4 */
 { KB_FUNC,   "\033[Q", "\033[c", "\033[o", "",       ""}, /* 63 f5 */
 { KB_FUNC,   "\033[R", "\033[d", "\033[p", "",       ""}, /* 64 f6 */
 { KB_FUNC,   "\033[S", "\033[e", "\033[q", "",       ""}, /* 65 f7 */
 { KB_FUNC,   "\033[T", "\033[f", "\033[r", "",       ""}, /* 66 f8 */
 { KB_FUNC,   "\033[U", "\033[g", "\033[s", "",       ""}, /* 67 f9 */
 { KB_FUNC,   "\033[V", "\033[h", "\033[t", "",       ""}, /* 68 f10 */
 { KB_NUM,    "",       "",       "",       "",       ""}, /* 69 num lock */
 { KB_SCROLL, "",       "",       "",       "",       ""}, /* 70 scroll lock */
 { KB_KP,     "7",      "\033[H", "7",      "",       ""}, /* 71 kp 7 */
 { KB_KP,     "8",      "\033[A", "8",      "",       ""}, /* 72 kp 8 */
 { KB_KP,     "9",      "\033[I", "9",      "",       ""}, /* 73 kp 9 */
 { KB_KP,     "-",      "-",      "-",      "",       ""}, /* 74 kp - */
 { KB_KP,     "4",      "\033[D", "4",      "",       ""}, /* 75 kp 4 */
 { KB_KP,     "5",      "\033[E", "5",      "",       ""}, /* 76 kp 5 */
 { KB_KP,     "6",      "\033[C", "6",      "",       ""}, /* 77 kp 6 */
 { KB_KP,     "+",      "+",      "+",      "",       ""}, /* 78 kp + */
 { KB_KP,     "1",      "\033[F", "1",      "",       ""}, /* 79 kp 1 */
 { KB_KP,     "2",      "\033[B", "2",      "",       ""}, /* 80 kp 2 */
 { KB_KP,     "3",      "\033[G", "3",      "",       ""}, /* 81 kp 3 */
 { KB_KP,     "0",      "\033[L", "0",      "",       ""}, /* 82 kp 0 */
 { KB_KP,     ",",      "\177",   ",",      "",       ""}, /* 83 kp , */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 84 0 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 85 0 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 86 0 */
 { KB_FUNC,   "\033[W", "\033[i", "\033[u", "",       ""}, /* 87 f11 */
 { KB_FUNC,   "\033[X", "\033[j", "\033[v", "",       ""}, /* 88 f12 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 89 0 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 90 0 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 91 0 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 92 0 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 93 0 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 94 0 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 95 0 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 96 0 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 97 0 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 98 0 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 99 0 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 100 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 101 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 102 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 103 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 104 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 105 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 106 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 107 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 108 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 109 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 110 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 111 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 112 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 113 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 114 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 115 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 116 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 117 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 118 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 119 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 120 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 121 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 122 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 123 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 124 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 125 */
 { KB_NONE,   "",       "",       "",       "",       ""}, /* 126 */
 { KB_NONE,   "",       "",       "",       "",       ""}  /* 127 */
};

/*
* Get characters from the keyboard.  If none are present, return NULL.
*/
char *
sget(void)
{
       u_char dt;
       static u_char extended = 0, shift_state = 0;
       static u_char capchar[2];

top:
       KBD_DELAY;
       dt = kbd_data_read_1();

       switch (dt) {
       case KBR_ACK: case KBR_ECHO:
               kb_oq_get = (kb_oq_get + 1) & 7;
               if(kb_oq_get != kb_oq_put) {
                       kbd_data_write_1(kb_oq[kb_oq_get]);
               }
               goto loop;
       case KBR_RESEND:
               kbd_data_write_1(kb_oq[kb_oq_get]);
               goto loop;
       }

       if (pc_xmode > 0) {
#if defined(DDB) && defined(XSERVER_DDB)
               /* F12 enters the debugger while in X mode */
               if (dt == 88)
                       Debugger();
#endif
               capchar[0] = dt;
               capchar[1] = 0;
               /*
                * Check for locking keys.
                *
                * XXX Setting the LEDs this way is a bit bogus.  What if the
                * keyboard has been remapped in X?
                */
               switch (scan_codes[dt & 0x7f].type) {
               case KB_NUM:
                       if (dt & 0x80) {
                               shift_state &= ~KB_NUM;
                               break;
                       }
                       if (shift_state & KB_NUM)
                               break;
                       shift_state |= KB_NUM;
                       lock_state ^= KB_NUM;
                       async_update();
                       break;
               case KB_CAPS:
                       if (dt & 0x80) {
                               shift_state &= ~KB_CAPS;
                               break;
                       }
                       if (shift_state & KB_CAPS)
                               break;
                       shift_state |= KB_CAPS;
                       lock_state ^= KB_CAPS;
                       async_update();
                       break;
               case KB_SCROLL:
                       if (dt & 0x80) {
                               shift_state &= ~KB_SCROLL;
                               break;
                       }
                       if (shift_state & KB_SCROLL)
                               break;
                       shift_state |= KB_SCROLL;
                       lock_state ^= KB_SCROLL;
                       if ((lock_state & KB_SCROLL) == 0)
                               wakeup((void *)&lock_state);
                       async_update();
                       break;
               }
               return capchar;
       }

       switch (dt) {
       case KBR_EXTENDED:
               extended = 1;
               goto loop;
       }

#ifdef DDB
       /*
        * Check for cntl-alt-esc.
        */
       if ((dt == 1) && (shift_state & (KB_CTL | KB_ALT)) == (KB_CTL | KB_ALT)) {
               /* XXX - check pccons_is_console */
               Debugger();
               dt |= 0x80;     /* discard esc (ddb discarded ctl-alt) */
       }
#endif

       /*
        * Check for make/break.
        */
       if (dt & 0x80) {
               /*
                * break
                */
               dt &= 0x7f;
               switch (scan_codes[dt].type) {
               case KB_NUM:
                       shift_state &= ~KB_NUM;
                       break;
               case KB_CAPS:
                       shift_state &= ~KB_CAPS;
                       break;
               case KB_SCROLL:
                       shift_state &= ~KB_SCROLL;
                       break;
               case KB_SHIFT:
                       shift_state &= ~KB_SHIFT;
                       break;
               case KB_ALT:
                       if (extended)
                               shift_state &= ~KB_ALTGR;
                       else
                               shift_state &= ~KB_ALT;
                       break;
               case KB_CTL:
                       shift_state &= ~KB_CTL;
                       break;
               }
       } else {
               /*
                * make
                */
               switch (scan_codes[dt].type) {
               /*
                * locking keys
                */
               case KB_NUM:
                       if (shift_state & KB_NUM)
                               break;
                       shift_state |= KB_NUM;
                       lock_state ^= KB_NUM;
                       async_update();
                       break;
               case KB_CAPS:
                       if (shift_state & KB_CAPS)
                               break;
                       shift_state |= KB_CAPS;
                       lock_state ^= KB_CAPS;
                       async_update();
                       break;
               case KB_SCROLL:
                       if (shift_state & KB_SCROLL)
                               break;
                       shift_state |= KB_SCROLL;
                       lock_state ^= KB_SCROLL;
                       if ((lock_state & KB_SCROLL) == 0)
                               wakeup((void *)&lock_state);
                       async_update();
                       break;
               /*
                * non-locking keys
                */
               case KB_SHIFT:
                       shift_state |= KB_SHIFT;
                       break;
               case KB_ALT:
                       if (extended)
                               shift_state |= KB_ALTGR;
                       else
                               shift_state |= KB_ALT;
                       break;
               case KB_CTL:
                       shift_state |= KB_CTL;
                       break;
               case KB_ASCII:
                       /* control has highest priority */
                       if (shift_state & KB_CTL)
                               capchar[0] = scan_codes[dt].ctl[0];
                       else if (shift_state & KB_ALTGR) {
                               if (shift_state & KB_SHIFT)
                                       capchar[0] = scan_codes[dt].shift_altgr[0];
                               else
                                       capchar[0] = scan_codes[dt].altgr[0];
                       }
                       else {
                               if (shift_state & KB_SHIFT)
                                       capchar[0] = scan_codes[dt].shift[0];
                               else
                                       capchar[0] = scan_codes[dt].unshift[0];
                       }
                       if ((lock_state & KB_CAPS) && capchar[0] >= 'a' &&
                           capchar[0] <= 'z') {
                               capchar[0] -= ('a' - 'A');
                       }
                       capchar[0] |= (shift_state & KB_ALT);
                       extended = 0;
                       return capchar;
               case KB_NONE:
printf("keycode %d\n",dt);
                       break;
               case KB_FUNC: {
                       char *more_chars;
                       if (shift_state & KB_SHIFT)
                               more_chars = scan_codes[dt].shift;
                       else if (shift_state & KB_CTL)
                               more_chars = scan_codes[dt].ctl;
                       else
                               more_chars = scan_codes[dt].unshift;
                       extended = 0;
                       return more_chars;
               }
               case KB_KP: {
                       char *more_chars;
                       if (shift_state & (KB_SHIFT | KB_CTL) ||
                           (lock_state & KB_NUM) == 0 || extended)
                               more_chars = scan_codes[dt].shift;
                       else
                               more_chars = scan_codes[dt].unshift;
                       extended = 0;
                       return more_chars;
               }
               }
       }

       extended = 0;
loop:
       if ((kbd_cmd_read_1() & KBS_DIB) == 0)
               return 0;
       goto top;
}

paddr_t
pcmmap(dev_t dev, off_t offset, int nprot)
{
       struct pccons_context *pc = &pccons_console_context;
       paddr_t pa;

       if (offset >= 0xa0000 && offset < 0xc0000) {
               if (bus_space_paddr(pc->pc_crt_memt, pc->pc_mono_memh, &pa))
                       return -1;
               pa += offset - pc->pc_config->pc_mono_memaddr;
               return mips_btop(pa);
       }
       if (offset >= 0x0000 && offset < 0x10000) {
               if (bus_space_paddr(pc->pc_crt_iot, pc->pc_mono_ioh, &pa))
                       return -1;
               pa += offset - pc->pc_config->pc_mono_iobase;
               return mips_btop(pa);
       }
       if (offset >= 0x40000000 && offset < 0x40800000) {
               if (bus_space_paddr(pc->pc_crt_memt, pc->pc_mono_memh, &pa))
                       return (-1);
               pa += offset - 0x40000000 - pc->pc_config->pc_mono_memaddr;
               return mips_btop(pa);
       }
       return -1;
}

void
pc_xmode_on(void)
{
       if (pc_xmode)
               return;
       pc_xmode = 1;

#ifdef XFREE86_BUG_COMPAT
       /* If still unchanged, get current shape. */
       if (cursor_shape == 0xffff)
               get_cursor_shape();
#endif
}

void
pc_xmode_off(void)
{
       if (pc_xmode == 0)
               return;
       pc_xmode = 0;

#ifdef XFREE86_BUG_COMPAT
       /* XXX It would be hard to justify why the X server doesn't do this. */
       set_cursor_shape();
#endif
       async_update();
}