diff options
author | yokota <yokota@FreeBSD.org> | 1999-01-09 02:44:50 +0000 |
---|---|---|
committer | yokota <yokota@FreeBSD.org> | 1999-01-09 02:44:50 +0000 |
commit | f10c5df4354e9dcf241989c9490f57fcd2ced1fc (patch) | |
tree | 595cc3d8efe3870b612f2264ba6a30474322c94c /sys/dev/atkbdc/atkbd.c | |
parent | dc29b5609b3591deb088424bce52f2d654b06b2c (diff) | |
download | FreeBSD-src-f10c5df4354e9dcf241989c9490f57fcd2ced1fc.zip FreeBSD-src-f10c5df4354e9dcf241989c9490f57fcd2ced1fc.tar.gz |
Add the new keyboard driver and video card driver. They will be
used by console drivers.
(They are not yet activated yet. Wait for announcement later.)
Diffstat (limited to 'sys/dev/atkbdc/atkbd.c')
-rw-r--r-- | sys/dev/atkbdc/atkbd.c | 1302 |
1 files changed, 1302 insertions, 0 deletions
diff --git a/sys/dev/atkbdc/atkbd.c b/sys/dev/atkbdc/atkbd.c new file mode 100644 index 0000000..21ed569 --- /dev/null +++ b/sys/dev/atkbdc/atkbd.c @@ -0,0 +1,1302 @@ +/*- + * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> + * 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 as + * the first lines of this file unmodified. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. + * + * $Id: $ + */ + +#include "atkbd.h" +#include "opt_kbd.h" +#include "opt_devfs.h" + +#if NATKBD > 0 + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/tty.h> +#include <sys/fcntl.h> +#include <sys/malloc.h> + +#include <dev/kbd/kbdreg.h> +#include <dev/kbd/atkbdreg.h> +#include <dev/kbd/atkbdcreg.h> + +#ifndef __i386__ + +#define ATKBD_SOFTC(unit) \ + ((atkbd_softc_t *)devclass_get_softc(atkbd_devclass, unit)) + +#else /* __i386__ */ + +#include <i386/isa/isa.h> +#include <i386/isa/isa_device.h> + +extern struct isa_driver atkbddriver; /* XXX: a kludge; see below */ + +static atkbd_softc_t *atkbd_softc[NATKBD]; + +#define ATKBD_SOFTC(unit) atkbd_softc[(unit)] + +#endif /* __i386__ */ + +static timeout_t atkbd_timeout; + +#ifdef KBD_INSTALL_CDEV + +static d_open_t atkbdopen; +static d_close_t atkbdclose; +static d_read_t atkbdread; +static d_ioctl_t atkbdioctl; +static d_poll_t atkbdpoll; + +static struct cdevsw atkbd_cdevsw = { + atkbdopen, atkbdclose, atkbdread, nowrite, + atkbdioctl, nostop, nullreset, nodevtotty, + atkbdpoll, nommap, NULL, ATKBD_DRIVER_NAME, + NULL, -1, +}; + +#endif /* KBD_INSTALL_CDEV */ + +#ifdef __i386__ + +atkbd_softc_t +*atkbd_get_softc(int unit) +{ + atkbd_softc_t *sc; + + if (unit >= sizeof(atkbd_softc)/sizeof(atkbd_softc[0])) + return NULL; + sc = atkbd_softc[unit]; + if (sc == NULL) { + sc = atkbd_softc[unit] + = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); + if (sc == NULL) + return NULL; + bzero(sc, sizeof(*sc)); + } + return sc; +} + +#endif /* __i386__ */ + +int +atkbd_probe_unit(int unit, atkbd_softc_t *sc, int port, int irq, int flags) +{ + keyboard_switch_t *sw; + int args[2]; + + if (sc->flags & ATKBD_ATTACHED) + return 0; + + sw = kbd_get_switch(ATKBD_DRIVER_NAME); + if (sw == NULL) + return ENXIO; + + args[0] = port; + args[1] = irq; + return (*sw->probe)(unit, &sc->kbd, args, flags); +} + +int +atkbd_attach_unit(int unit, atkbd_softc_t *sc) +{ + keyboard_switch_t *sw; + int error; + + if (sc->flags & ATKBD_ATTACHED) + return 0; + + sw = kbd_get_switch(ATKBD_DRIVER_NAME); + if (sw == NULL) + return ENXIO; + + /* reset, initialize and enable the device */ + error = (*sw->init)(sc->kbd); + if (error) + return ENXIO; + (*sw->enable)(sc->kbd); + +#ifdef KBD_INSTALL_CDEV + /* attach a virtual keyboard cdev */ + error = kbd_attach(makedev(0, ATKBD_MKMINOR(unit)), sc->kbd, + &atkbd_cdevsw); + if (error) + return error; +#endif + + /* + * This is a kludge to compensate for lost keyboard interrupts. + * A similar code used to be in syscons. See below. XXX + */ + atkbd_timeout(sc->kbd); + + if (bootverbose) + (*sw->diag)(sc->kbd, bootverbose); + + sc->flags |= ATKBD_ATTACHED; + return 0; +} + +static void +atkbd_timeout(void *arg) +{ + keyboard_t *kbd; + int s; + + /* The following comments are extracted from syscons.c (1.287) */ + /* + * With release 2.1 of the Xaccel server, the keyboard is left + * hanging pretty often. Apparently an interrupt from the + * keyboard is lost, and I don't know why (yet). + * This ugly hack calls scintr if input is ready for the keyboard + * and conveniently hides the problem. XXX + */ + /* + * Try removing anything stuck in the keyboard controller; whether + * it's a keyboard scan code or mouse data. `scintr()' doesn't + * read the mouse data directly, but `kbdio' routines will, as a + * side effect. + */ + s = spltty(); + kbd = (keyboard_t *)arg; + if ((*kbdsw[kbd->kb_index]->lock)(kbd, TRUE)) { + /* + * We have seen the lock flag is not set. Let's reset + * the flag early, otherwise the LED update routine fails + * which may want the lock during the interrupt routine. + */ + (*kbdsw[kbd->kb_index]->lock)(kbd, FALSE); + if ((*kbdsw[kbd->kb_index]->check_char)(kbd)) + (*kbdsw[kbd->kb_index]->intr)(kbd, NULL); + } + splx(s); + timeout(atkbd_timeout, arg, hz/10); +} + +/* cdev driver functions */ + +#ifdef KBD_INSTALL_CDEV + +static int +atkbdopen(dev_t dev, int flag, int mode, struct proc *p) +{ + atkbd_softc_t *sc; + int unit; + + unit = ATKBD_UNIT(dev); + if ((unit >= NATKBD) || ((sc = ATKBD_SOFTC(unit)) == NULL)) + return ENXIO; + if (mode & (FWRITE | O_CREAT | O_APPEND | O_TRUNC)) + return ENODEV; + + /* FIXME: set the initial input mode (K_XLATE?) and lock state? */ + return genkbdopen(&sc->gensc, sc->kbd, flag, mode, p); +} + +static int +atkbdclose(dev_t dev, int flag, int mode, struct proc *p) +{ + atkbd_softc_t *sc; + + sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); + return genkbdclose(&sc->gensc, sc->kbd, flag, mode, p); +} + +static int +atkbdread(dev_t dev, struct uio *uio, int flag) +{ + atkbd_softc_t *sc; + + sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); + return genkbdread(&sc->gensc, sc->kbd, uio, flag); +} + +static int +atkbdioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p) +{ + atkbd_softc_t *sc; + + sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); + return genkbdioctl(&sc->gensc, sc->kbd, cmd, arg, flag, p); +} + +static int +atkbdpoll(dev_t dev, int event, struct proc *p) +{ + atkbd_softc_t *sc; + + sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); + return genkbdpoll(&sc->gensc, sc->kbd, event, p); +} + +#endif /* KBD_INSTALL_CDEV */ + +/* LOW-LEVEL */ + +#include <machine/limits.h> +#include <machine/console.h> +#include <machine/clock.h> + +#define ATKBD_DEFAULT 0 + +typedef struct atkbd_state { + KBDC kbdc; /* keyboard controller */ + /* XXX: don't move this field; pcvt + * expects `kbdc' to be the first + * field in this structure. */ + int ks_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ + int ks_flags; /* flags */ +#define COMPOSE (1 << 0) + int ks_state; /* shift/lock key state */ + int ks_accents; /* accent key index (> 0) */ + u_int ks_composed_char; /* composed char code (> 0) */ + u_char ks_prefix; /* AT scan code prefix */ +} atkbd_state_t; + +/* keyboard driver declaration */ +static int atkbd_configure(int flags); +static kbd_probe_t atkbd_probe; +static kbd_init_t atkbd_init; +static kbd_term_t atkbd_term; +static kbd_intr_t atkbd_intr; +static kbd_test_if_t atkbd_test_if; +static kbd_enable_t atkbd_enable; +static kbd_disable_t atkbd_disable; +static kbd_read_t atkbd_read; +static kbd_check_t atkbd_check; +static kbd_read_char_t atkbd_read_char; +static kbd_check_char_t atkbd_check_char; +static kbd_ioctl_t atkbd_ioctl; +static kbd_lock_t atkbd_lock; +static kbd_clear_state_t atkbd_clear_state; +static kbd_get_state_t atkbd_get_state; +static kbd_set_state_t atkbd_set_state; + +keyboard_switch_t atkbdsw = { + atkbd_probe, + atkbd_init, + atkbd_term, + atkbd_intr, + atkbd_test_if, + atkbd_enable, + atkbd_disable, + atkbd_read, + atkbd_check, + atkbd_read_char, + atkbd_check_char, + atkbd_ioctl, + atkbd_lock, + atkbd_clear_state, + atkbd_get_state, + atkbd_set_state, + genkbd_get_fkeystr, + genkbd_diag, +}; + +KEYBOARD_DRIVER(atkbd, atkbdsw, atkbd_configure); + +/* local functions */ +static int setup_kbd_port(KBDC kbdc, int port, int intr); +static int get_kbd_echo(KBDC kbdc); +static int probe_keyboard(KBDC kbdc, int flags); +static int init_keyboard(KBDC kbdc, int *type, int flags); +static int write_kbd(KBDC kbdc, int command, int data); +static int get_kbd_id(KBDC kbdc); + +/* local variables */ + +/* the initial key map, accent map and fkey strings */ +#include <i386/isa/kbdtables.h> + +/* structures for the default keyboard */ +static keyboard_t default_kbd; +static atkbd_state_t default_kbd_state; +static keymap_t default_keymap; +static accentmap_t default_accentmap; +static fkeytab_t default_fkeytab[NUM_FKEYS]; + +/* + * The back door to the keyboard driver! + * This function is called by the console driver, via the kbdio module, + * to tickle keyboard drivers when the low-level console is being initialized. + * Almost nothing in the kernel has been initialied yet. Try to probe + * keyboards if possible. + * NOTE: because of the way the low-level conole is initialized, this routine + * may be called more than once!! + */ +static int +atkbd_configure(int flags) +{ + keyboard_t *kbd; + KBDC kbdc; + int arg[2]; +#ifdef __i386__ + struct isa_device *dev; + + /* XXX: a kludge to obtain the device configuration flags */ + dev = find_isadev(isa_devtab_tty, &atkbddriver, 0); + if (dev != NULL) + flags |= dev->id_flags; +#endif + + /* probe the keyboard controller */ + atkbdc_configure(); + + /* probe the default keyboard */ + arg[0] = -1; + arg[1] = -1; + if (atkbd_probe(ATKBD_DEFAULT, &kbd, arg, flags)) + return 0; + + /* initialize it */ + kbdc = ((atkbd_state_t *)kbd->kb_data)->kbdc; + if (!(flags & KB_CONF_PROBE_ONLY) && !KBD_IS_PROBED(kbd)) { + if (KBD_HAS_DEVICE(kbd) + && init_keyboard(kbdc, &kbd->kb_type, flags) + && (flags & KB_CONF_FAIL_IF_NO_KBD)) + return 0; + KBD_INIT_DONE(kbd); + } + + /* and register */ + if (!KBD_IS_CONFIGURED(kbd)) { + if (kbd_register(kbd) < 0) + return 0; + KBD_CONFIG_DONE(kbd); + } + + return 1; /* return the number of found keyboards */ +} + +/* low-level functions */ + +/* initialize the keyboard_t structure and try to detect a keyboard */ +static int +atkbd_probe(int unit, keyboard_t **kbdp, void *arg, int flags) +{ + keyboard_t *kbd; + atkbd_state_t *state; + keymap_t *keymap; + accentmap_t *accmap; + fkeytab_t *fkeymap; + int fkeymap_size; + KBDC kbdc; + int *data = (int *)arg; + + /* XXX */ + if (unit == ATKBD_DEFAULT) { + *kbdp = kbd = &default_kbd; + if (KBD_IS_PROBED(kbd)) + return 0; + state = &default_kbd_state; + keymap = &default_keymap; + accmap = &default_accentmap; + fkeymap = default_fkeytab; + fkeymap_size = + sizeof(default_fkeytab)/sizeof(default_fkeytab[0]); + } else if (*kbdp == NULL) { + *kbdp = kbd = malloc(sizeof(*kbd), M_DEVBUF, M_NOWAIT); + if (kbd == NULL) + return ENOMEM; + bzero(kbd, sizeof(*kbd)); + state = malloc(sizeof(*state), M_DEVBUF, M_NOWAIT); + keymap = malloc(sizeof(key_map), M_DEVBUF, M_NOWAIT); + accmap = malloc(sizeof(accent_map), M_DEVBUF, M_NOWAIT); + fkeymap = malloc(sizeof(fkey_tab), M_DEVBUF, M_NOWAIT); + fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]); + if ((state == NULL) || (keymap == NULL) || (accmap == NULL) + || (fkeymap == NULL)) { + if (state != NULL) + free(state, M_DEVBUF); + if (keymap != NULL) + free(keymap, M_DEVBUF); + if (accmap != NULL) + free(accmap, M_DEVBUF); + if (fkeymap != NULL) + free(fkeymap, M_DEVBUF); + free(kbd, M_DEVBUF); + return ENOMEM; + } + bzero(state, sizeof(*state)); + } else if (KBD_IS_PROBED(*kbdp)) { + return 0; + } else { + kbd = *kbdp; + state = (atkbd_state_t *)kbd->kb_data; + bzero(state, sizeof(*state)); + keymap = kbd->kb_keymap; + accmap = kbd->kb_accentmap; + fkeymap = kbd->kb_fkeytab; + fkeymap_size = kbd->kb_fkeytab_size; + } + + state->kbdc = kbdc = kbdc_open(data[0]); + if (kbdc == NULL) + return ENXIO; + kbd_init_struct(kbd, ATKBD_DRIVER_NAME, KB_OTHER, unit, flags, data[0], + IO_KBDSIZE); + bcopy(&key_map, keymap, sizeof(key_map)); + bcopy(&accent_map, accmap, sizeof(accent_map)); + bcopy(fkey_tab, fkeymap, + imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab))); + kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size); + kbd->kb_data = (void *)state; + + if (probe_keyboard(kbdc, flags)) { + if (flags & KB_CONF_FAIL_IF_NO_KBD) + return ENXIO; + } else { + KBD_FOUND_DEVICE(kbd); + } + atkbd_clear_state(kbd); + state->ks_mode = K_XLATE; + /* + * FIXME: set the initial value for lock keys in ks_state + * according to the BIOS data? + */ + + KBD_PROBE_DONE(kbd); + return 0; +} + +/* reset and initialize the device */ +static int +atkbd_init(keyboard_t *kbd) +{ + KBDC kbdc; + + if ((kbd == NULL) || !KBD_IS_PROBED(kbd)) + return ENXIO; /* shouldn't happen */ + kbdc = ((atkbd_state_t *)kbd->kb_data)->kbdc; + if (kbdc == NULL) + return ENXIO; /* shouldn't happen */ + + if (!KBD_IS_INITIALIZED(kbd)) { + if (KBD_HAS_DEVICE(kbd) + && init_keyboard(kbdc, &kbd->kb_type, kbd->kb_config) + && (kbd->kb_config & KB_CONF_FAIL_IF_NO_KBD)) + return ENXIO; + atkbd_ioctl(kbd, KDSETLED, + (caddr_t)&((atkbd_state_t *)(kbd->kb_data))->ks_state); + KBD_INIT_DONE(kbd); + } + if (!KBD_IS_CONFIGURED(kbd)) { + if (kbd_register(kbd) < 0) + return ENXIO; + KBD_CONFIG_DONE(kbd); + } + + return 0; +} + +/* finish using this keyboard */ +static int +atkbd_term(keyboard_t *kbd) +{ + kbd_unregister(kbd); + return 0; +} + +/* keyboard interrupt routine */ +static int +atkbd_intr(keyboard_t *kbd, void *arg) +{ + atkbd_state_t *state; + int c; + + if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) { + /* let the callback function to process the input */ + (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT, + kbd->kb_callback.kc_arg); + } else { + /* read and discard the input; no one is waiting for input */ + do { + c = atkbd_read_char(kbd, FALSE); + } while (c != NOKEY); + + if (!KBD_HAS_DEVICE(kbd)) { + /* + * The keyboard was not detected before; + * it must have been reconnected! + */ + state = (atkbd_state_t *)kbd->kb_data; + init_keyboard(state->kbdc, &kbd->kb_type, + kbd->kb_config); + atkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state); + KBD_FOUND_DEVICE(kbd); + } + } + return 0; +} + +/* test the interface to the device */ +static int +atkbd_test_if(keyboard_t *kbd) +{ + int error; + int s; + + error = 0; + empty_both_buffers(((atkbd_state_t *)kbd->kb_data)->kbdc, 10); + s = spltty(); + if (!test_controller(((atkbd_state_t *)kbd->kb_data)->kbdc)) + error = EIO; + else if (test_kbd_port(((atkbd_state_t *)kbd->kb_data)->kbdc) != 0) + error = EIO; + splx(s); + + return error; +} + +/* + * Enable the access to the device; until this function is called, + * the client cannot read from the keyboard. + */ +static int +atkbd_enable(keyboard_t *kbd) +{ + int s; + + s = spltty(); + KBD_ACTIVATE(kbd); + splx(s); + return 0; +} + +/* disallow the access to the device */ +static int +atkbd_disable(keyboard_t *kbd) +{ + int s; + + s = spltty(); + KBD_DEACTIVATE(kbd); + splx(s); + return 0; +} + +/* read one byte from the keyboard if it's allowed */ +static int +atkbd_read(keyboard_t *kbd, int wait) +{ + int c; + + if (wait) + c = read_kbd_data(((atkbd_state_t *)kbd->kb_data)->kbdc); + else + c = read_kbd_data_no_wait(((atkbd_state_t *)kbd->kb_data)->kbdc); + return (KBD_IS_ACTIVE(kbd) ? c : -1); +} + +/* check if data is waiting */ +static int +atkbd_check(keyboard_t *kbd) +{ + if (!KBD_IS_ACTIVE(kbd)) + return FALSE; + return kbdc_data_ready(((atkbd_state_t *)kbd->kb_data)->kbdc); +} + +/* read char from the keyboard */ +static u_int +atkbd_read_char(keyboard_t *kbd, int wait) +{ + atkbd_state_t *state; + u_int action; + int scancode; + int keycode; + + state = (atkbd_state_t *)kbd->kb_data; +next_code: + /* do we have a composed char to return? */ + if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) { + action = state->ks_composed_char; + state->ks_composed_char = 0; + if (action > UCHAR_MAX) + return ERRKEY; + return action; + } + + /* see if there is something in the keyboard port */ + if (wait) { + do { + scancode = read_kbd_data(state->kbdc); + } while (scancode == -1); + } else { + scancode = read_kbd_data_no_wait(state->kbdc); + if (scancode == -1) + return NOKEY; + } + + /* return the byte as is for the K_RAW mode */ + if (state->ks_mode == K_RAW) + return scancode; + + /* translate the scan code into a keycode */ + keycode = scancode & 0x7F; + switch (state->ks_prefix) { + case 0x00: /* normal scancode */ + switch(scancode) { + case 0xB8: /* left alt (compose key) released */ + if (state->ks_flags & COMPOSE) { + state->ks_flags &= ~COMPOSE; + if (state->ks_composed_char > UCHAR_MAX) + state->ks_composed_char = 0; + } + break; + case 0x38: /* left alt (compose key) pressed */ + if (!(state->ks_flags & COMPOSE)) { + state->ks_flags |= COMPOSE; + state->ks_composed_char = 0; + } + break; + case 0xE0: + case 0xE1: + state->ks_prefix = scancode; + goto next_code; + } + break; + case 0xE0: /* 0xE0 prefix */ + state->ks_prefix = 0; + switch (keycode) { + case 0x1C: /* right enter key */ + keycode = 0x59; + break; + case 0x1D: /* right ctrl key */ + keycode = 0x5A; + break; + case 0x35: /* keypad divide key */ + keycode = 0x5B; + break; + case 0x37: /* print scrn key */ + keycode = 0x5C; + break; + case 0x38: /* right alt key (alt gr) */ + keycode = 0x5D; + break; + case 0x47: /* grey home key */ + keycode = 0x5E; + break; + case 0x48: /* grey up arrow key */ + keycode = 0x5F; + break; + case 0x49: /* grey page up key */ + keycode = 0x60; + break; + case 0x4B: /* grey left arrow key */ + keycode = 0x61; + break; + case 0x4D: /* grey right arrow key */ + keycode = 0x62; + break; + case 0x4F: /* grey end key */ + keycode = 0x63; + break; + case 0x50: /* grey down arrow key */ + keycode = 0x64; + break; + case 0x51: /* grey page down key */ + keycode = 0x65; + break; + case 0x52: /* grey insert key */ + keycode = 0x66; + break; + case 0x53: /* grey delete key */ + keycode = 0x67; + break; + /* the following 3 are only used on the MS "Natural" keyboard */ + case 0x5b: /* left Window key */ + keycode = 0x69; + break; + case 0x5c: /* right Window key */ + keycode = 0x6a; + break; + case 0x5d: /* menu key */ + keycode = 0x6b; + break; + default: /* ignore everything else */ + goto next_code; + } + break; + case 0xE1: /* 0xE1 prefix */ + state->ks_prefix = 0; + if (keycode == 0x1D) + state->ks_prefix = 0x1D; + goto next_code; + /* NOT REACHED */ + case 0x1D: /* pause / break */ + state->ks_prefix = 0; + if (keycode != 0x45) + goto next_code; + keycode = 0x68; + break; + } + + /* return the key code in the K_CODE mode */ + if (state->ks_mode == K_CODE) + return (keycode | (scancode & 0x80)); + + /* compose a character code */ + if (state->ks_flags & COMPOSE) { + switch (scancode) { + /* key pressed, process it */ + case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ + state->ks_composed_char *= 10; + state->ks_composed_char += scancode - 0x40; + if (state->ks_composed_char > UCHAR_MAX) + return ERRKEY; + goto next_code; + case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ + state->ks_composed_char *= 10; + state->ks_composed_char += scancode - 0x47; + if (state->ks_composed_char > UCHAR_MAX) + return ERRKEY; + goto next_code; + case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ + state->ks_composed_char *= 10; + state->ks_composed_char += scancode - 0x4E; + if (state->ks_composed_char > UCHAR_MAX) + return ERRKEY; + goto next_code; + case 0x52: /* keypad 0 */ + state->ks_composed_char *= 10; + if (state->ks_composed_char > UCHAR_MAX) + return ERRKEY; + goto next_code; + + /* key released, no interest here */ + case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */ + case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */ + case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */ + case 0xD2: /* keypad 0 */ + goto next_code; + + case 0x38: /* left alt key */ + break; + + default: + if (state->ks_composed_char > 0) { + state->ks_flags &= ~COMPOSE; + state->ks_composed_char = 0; + return ERRKEY; + } + break; + } + } + + /* keycode to key action */ + action = genkbd_keyaction(kbd, keycode, scancode & 0x80, + &state->ks_state, &state->ks_accents); + if (action == NOKEY) + goto next_code; + else + return action; +} + +/* check if char is waiting */ +static int +atkbd_check_char(keyboard_t *kbd) +{ + atkbd_state_t *state; + + if (!KBD_IS_ACTIVE(kbd)) + return FALSE; + state = (atkbd_state_t *)kbd->kb_data; + if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) + return TRUE; + return kbdc_data_ready(state->kbdc); +} + +/* some useful control functions */ +static int +atkbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) +{ + /* trasnlate LED_XXX bits into the device specific bits */ + static u_char ledmap[8] = { + 0, 4, 2, 6, 1, 5, 3, 7, + }; + atkbd_state_t *state = kbd->kb_data; + int error; + int s; + int i; + + s = spltty(); + switch (cmd) { + + case KDGKBMODE: /* get keyboard mode */ + *(int *)arg = state->ks_mode; + break; + case KDSKBMODE: /* set keyboard mode */ + switch (*(int *)arg) { + case K_XLATE: + if (state->ks_mode != K_XLATE) { + /* make lock key state and LED state match */ + state->ks_state &= ~LOCK_MASK; + state->ks_state |= KBD_LED_VAL(kbd); + } + /* FALL THROUGH */ + case K_RAW: + case K_CODE: + if (state->ks_mode != *(int *)arg) { + atkbd_clear_state(kbd); + state->ks_mode = *(int *)arg; + } + break; + default: + splx(s); + return EINVAL; + } + break; + + case KDGETLED: /* get keyboard LED */ + *(int *)arg = KBD_LED_VAL(kbd); + break; + case KDSETLED: /* set keyboard LED */ + /* NOTE: lock key state in ks_state won't be changed */ + if (*(int *)arg & ~LOCK_MASK) { + splx(s); + return EINVAL; + } + i = *(int *)arg; + /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ + if (kbd->kb_keymap->n_keys > ALTGR_OFFSET) { + if (i & ALKED) + i |= CLKED; + else + i &= ~CLKED; + } + if (KBD_HAS_DEVICE(kbd)) { + error = write_kbd(state->kbdc, KBDC_SET_LEDS, + ledmap[i & LED_MASK]); + if (error) { + splx(s); + return error; + } + } + KBD_LED_VAL(kbd) = *(int *)arg; + break; + + case KDGKBSTATE: /* get lock key state */ + *(int *)arg = state->ks_state & LOCK_MASK; + break; + case KDSKBSTATE: /* set lock key state */ + if (*(int *)arg & ~LOCK_MASK) { + splx(s); + return EINVAL; + } + state->ks_state &= ~LOCK_MASK; + state->ks_state |= *(int *)arg; + splx(s); + /* set LEDs and quit */ + return atkbd_ioctl(kbd, KDSETLED, arg); + + case KDSETRAD: /* set keyboard repeat rate */ + splx(s); + if (!KBD_HAS_DEVICE(kbd)) + return 0; + return write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, + *(int *)arg); + + case PIO_KEYMAP: /* set keyboard translation table */ + case PIO_KEYMAPENT: /* set keyboard translation table entry */ + case PIO_DEADKEYMAP: /* set accent key translation table */ + state->ks_accents = 0; + /* FALL THROUGH */ + default: + splx(s); + return genkbd_commonioctl(kbd, cmd, arg); + } + + splx(s); + return 0; +} + +/* lock the access to the keyboard */ +static int +atkbd_lock(keyboard_t *kbd, int lock) +{ + return kbdc_lock(((atkbd_state_t *)kbd->kb_data)->kbdc, lock); +} + +/* clear the internal state of the keyboard */ +static void +atkbd_clear_state(keyboard_t *kbd) +{ + atkbd_state_t *state; + + state = (atkbd_state_t *)kbd->kb_data; + state->ks_flags = 0; + state->ks_state &= LOCK_MASK; /* preserve locking key state */ + state->ks_accents = 0; + state->ks_composed_char = 0; +#if 0 + state->ks_prefix = 0; /* XXX */ +#endif +} + +/* save the internal state */ +static int +atkbd_get_state(keyboard_t *kbd, void *buf, size_t len) +{ + if (len == 0) + return sizeof(atkbd_state_t); + if (len < sizeof(atkbd_state_t)) + return -1; + bcopy(kbd->kb_data, buf, sizeof(atkbd_state_t)); + return 0; +} + +/* set the internal state */ +static int +atkbd_set_state(keyboard_t *kbd, void *buf, size_t len) +{ + if (len < sizeof(atkbd_state_t)) + return ENOMEM; + if (((atkbd_state_t *)kbd->kb_data)->kbdc + != ((atkbd_state_t *)buf)->kbdc) + return ENOMEM; + bcopy(buf, kbd->kb_data, sizeof(atkbd_state_t)); + return 0; +} + +/* local functions */ + +static int +setup_kbd_port(KBDC kbdc, int port, int intr) +{ + if (!set_controller_command_byte(kbdc, + KBD_KBD_CONTROL_BITS, + ((port) ? KBD_ENABLE_KBD_PORT : KBD_DISABLE_KBD_PORT) + | ((intr) ? KBD_ENABLE_KBD_INT : KBD_DISABLE_KBD_INT))) + return 1; + return 0; +} + +static int +get_kbd_echo(KBDC kbdc) +{ + /* enable the keyboard port, but disable the keyboard intr. */ + if (setup_kbd_port(kbdc, TRUE, FALSE)) + /* CONTROLLER ERROR: there is very little we can do... */ + return ENXIO; + + /* see if something is present */ + write_kbd_command(kbdc, KBDC_ECHO); + if (read_kbd_data(kbdc) != KBD_ECHO) { + empty_both_buffers(kbdc, 10); + test_controller(kbdc); + test_kbd_port(kbdc); + return ENXIO; + } + + /* enable the keyboard port and intr. */ + if (setup_kbd_port(kbdc, TRUE, TRUE)) { + /* + * CONTROLLER ERROR + * This is serious; the keyboard intr is left disabled! + */ + return ENXIO; + } + + return 0; +} + +static int +probe_keyboard(KBDC kbdc, int flags) +{ + /* + * Don't try to print anything in this function. The low-level + * console may not have been initialized yet... + */ + int err; + int c; + int m; + + if (!kbdc_lock(kbdc, TRUE)) { + /* driver error? */ + return ENXIO; + } + + /* flush any noise in the buffer */ + empty_both_buffers(kbdc, 10); + + /* save the current keyboard controller command byte */ + m = kbdc_get_device_mask(kbdc) & ~KBD_KBD_CONTROL_BITS; + c = get_controller_command_byte(kbdc); + if (c == -1) { + /* CONTROLLER ERROR */ + kbdc_set_device_mask(kbdc, m); + kbdc_lock(kbdc, FALSE); + return ENXIO; + } + + /* + * The keyboard may have been screwed up by the boot block. + * We may just be able to recover from error by testing the controller + * and the keyboard port. The controller command byte needs to be + * saved before this recovery operation, as some controllers seem + * to set the command byte to particular values. + */ + test_controller(kbdc); + test_kbd_port(kbdc); + + err = get_kbd_echo(kbdc); + if (err == 0) { + kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS); + } else { + if (c != -1) + /* try to restore the command byte as before */ + set_controller_command_byte(kbdc, 0xff, c); + kbdc_set_device_mask(kbdc, m); + } + + kbdc_lock(kbdc, FALSE); + return err; +} + +static int +init_keyboard(KBDC kbdc, int *type, int flags) +{ + int codeset; + int id; + int c; + + if (!kbdc_lock(kbdc, TRUE)) { + /* driver error? */ + return EIO; + } + + /* save the current controller command byte */ + empty_both_buffers(kbdc, 10); + c = get_controller_command_byte(kbdc); + if (c == -1) { + /* CONTROLLER ERROR */ + kbdc_lock(kbdc, FALSE); + printf("atkbd: unable to get the current command byte value.\n"); + return EIO; + } + if (bootverbose) + printf("atkbd: the current kbd controller command byte %04x\n", + c); +#if 0 + /* override the keyboard lock switch */ + c |= KBD_OVERRIDE_KBD_LOCK; +#endif + + /* enable the keyboard port, but disable the keyboard intr. */ + if (setup_kbd_port(kbdc, TRUE, FALSE)) { + /* CONTROLLER ERROR: there is very little we can do... */ + printf("atkbd: unable to set the command byte.\n"); + kbdc_lock(kbdc, FALSE); + return EIO; + } + + /* + * Check if we have an XT keyboard before we attempt to reset it. + * The procedure assumes that the keyboard and the controller have + * been set up properly by BIOS and have not been messed up + * during the boot process. + */ + codeset = -1; + if (flags & KB_CONF_ALT_SCANCODESET) + /* the user says there is a XT keyboard */ + codeset = 1; +#ifdef KBD_DETECT_XT_KEYBOARD + else if ((c & KBD_TRANSLATION) == 0) { + /* SET_SCANCODE_SET is not always supported; ignore error */ + if (send_kbd_command_and_data(kbdc, KBDC_SET_SCANCODE_SET, 0) + == KBD_ACK) + codeset = read_kbd_data(kbdc); + } + if (bootverbose) + printf("atkbd: scancode set %d\n", codeset); +#endif /* KBD_DETECT_XT_KEYBOARD */ + + *type = KB_OTHER; + id = get_kbd_id(kbdc); + switch(id) { + case 0x41ab: + case 0x83ab: + *type = KB_101; + break; + case -1: /* AT 84 keyboard doesn't return ID */ + *type = KB_84; + break; + default: + break; + } + if (bootverbose) + printf("atkbd: keyboard ID 0x%x (%d)\n", id, *type); + + /* reset keyboard hardware */ + if (!(flags & KB_CONF_NO_RESET) && !reset_kbd(kbdc)) { + /* + * KEYBOARD ERROR + * Keyboard reset may fail either because the keyboard + * doen't exist, or because the keyboard doesn't pass + * the self-test, or the keyboard controller on the + * motherboard and the keyboard somehow fail to shake hands. + * It is just possible, particularly in the last case, + * that the keyoard controller may be left in a hung state. + * test_controller() and test_kbd_port() appear to bring + * the keyboard controller back (I don't know why and how, + * though.) + */ + empty_both_buffers(kbdc, 10); + test_controller(kbdc); + test_kbd_port(kbdc); + /* + * We could disable the keyboard port and interrupt... but, + * the keyboard may still exist (see above). + */ + set_controller_command_byte(kbdc, 0xff, c); + kbdc_lock(kbdc, FALSE); + if (bootverbose) + printf("atkbd: failed to reset the keyboard.\n"); + return EIO; + } + + /* + * Allow us to set the XT_KEYBD flag in UserConfig so that keyboards + * such as those on the IBM ThinkPad laptop computers can be used + * with the standard console driver. + */ + if (codeset == 1) { + if (send_kbd_command_and_data(kbdc, + KBDC_SET_SCANCODE_SET, codeset) == KBD_ACK) { + /* XT kbd doesn't need scan code translation */ + c &= ~KBD_TRANSLATION; + } else { + /* + * KEYBOARD ERROR + * The XT kbd isn't usable unless the proper scan + * code set is selected. + */ + set_controller_command_byte(kbdc, 0xff, c); + kbdc_lock(kbdc, FALSE); + printf("atkbd: unable to set the XT keyboard mode.\n"); + return EIO; + } + } + + /* enable the keyboard port and intr. */ + if (!set_controller_command_byte(kbdc, + KBD_KBD_CONTROL_BITS | KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK, + (c & (KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK)) + | KBD_ENABLE_KBD_PORT | KBD_ENABLE_KBD_INT)) { + /* + * CONTROLLER ERROR + * This is serious; we are left with the disabled + * keyboard intr. + */ + set_controller_command_byte(kbdc, 0xff, c); + kbdc_lock(kbdc, FALSE); + printf("atkbd: unable to enable the keyboard port and intr.\n"); + return EIO; + } + + kbdc_lock(kbdc, FALSE); + return 0; +} + +static int +write_kbd(KBDC kbdc, int command, int data) +{ + int s; + + /* prevent the timeout routine from polling the keyboard */ + if (!kbdc_lock(kbdc, TRUE)) + return EBUSY; + + /* disable the keyboard and mouse interrupt */ + s = spltty(); +#if 0 + c = get_controller_command_byte(kbdc); + if ((c == -1) + || !set_controller_command_byte(kbdc, + kbdc_get_device_mask(kbdc), + KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT + | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { + /* CONTROLLER ERROR */ + kbdc_lock(kbdc, FALSE); + splx(s); + return EIO; + } + /* + * Now that the keyboard controller is told not to generate + * the keyboard and mouse interrupts, call `splx()' to allow + * the other tty interrupts. The clock interrupt may also occur, + * but the timeout routine (`scrn_timer()') will be blocked + * by the lock flag set via `kbdc_lock()' + */ + splx(s); +#endif + + if (send_kbd_command_and_data(kbdc, command, data) != KBD_ACK) + send_kbd_command(kbdc, KBDC_ENABLE_KBD); + +#if 0 + /* restore the interrupts */ + if (!set_controller_command_byte(kbdc, + kbdc_get_device_mask(kbdc), + c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { + /* CONTROLLER ERROR */ + } +#else + splx(s); +#endif + kbdc_lock(kbdc, FALSE); + + return 0; +} + +static int +get_kbd_id(KBDC kbdc) +{ + int id1, id2; + + empty_both_buffers(kbdc, 10); + id1 = id2 = -1; + if (send_kbd_command(kbdc, KBDC_SEND_DEV_ID) != KBD_ACK) + return -1; + + DELAY(10000); /* 10 msec delay */ + id1 = read_kbd_data(kbdc); + if (id1 != -1) + id2 = read_kbd_data(kbdc); + + if ((id1 == -1) || (id2 == -1)) { + empty_both_buffers(kbdc, 10); + test_controller(kbdc); + test_kbd_port(kbdc); + return -1; + } + return ((id2 << 8) | id1); +} + +#endif /* NATKBD > 0 */ |