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/kbd | |
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/kbd')
-rw-r--r-- | sys/dev/kbd/atkbd.c | 1302 | ||||
-rw-r--r-- | sys/dev/kbd/atkbdc.c | 1017 | ||||
-rw-r--r-- | sys/dev/kbd/atkbdcreg.h | 246 | ||||
-rw-r--r-- | sys/dev/kbd/atkbdreg.h | 61 | ||||
-rw-r--r-- | sys/dev/kbd/kbd.c | 1193 | ||||
-rw-r--r-- | sys/dev/kbd/kbdreg.h | 270 |
6 files changed, 4089 insertions, 0 deletions
diff --git a/sys/dev/kbd/atkbd.c b/sys/dev/kbd/atkbd.c new file mode 100644 index 0000000..21ed569 --- /dev/null +++ b/sys/dev/kbd/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 */ diff --git a/sys/dev/kbd/atkbdc.c b/sys/dev/kbd/atkbdc.c new file mode 100644 index 0000000..0f61835 --- /dev/null +++ b/sys/dev/kbd/atkbdc.c @@ -0,0 +1,1017 @@ +/*- + * Copyright (c) 1996-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. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $Id: $ + * from kbdio.c,v 1.13 1998/09/25 11:55:46 yokota Exp + */ + +#include "atkbdc.h" +#include "opt_kbd.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/syslog.h> + +#include <machine/clock.h> + +#include <dev/kbd/atkbdcreg.h> + +#ifndef __i386__ +#include <isa/isareg.h> +#else +#include <i386/isa/isa.h> +#endif + +/* constants */ + +#define MAXKBDC MAX(NATKBDC, 1) + +/* macros */ + +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + +#define kbdcp(p) ((atkbdc_softc_t *)(p)) +#define nextq(i) (((i) + 1) % KBDQ_BUFSIZE) +#define availq(q) ((q)->head != (q)->tail) +#if KBDIO_DEBUG >= 2 +#define emptyq(q) ((q)->tail = (q)->head = (q)->qcount = 0) +#else +#define emptyq(q) ((q)->tail = (q)->head = 0) +#endif + +/* local variables */ + +/* + * We always need at least one copy of the kbdc_softc struct for the + * low-level console. As the low-level console accesses the keyboard + * controller before kbdc, and all other devices, is probed, we + * statically allocate one entry. XXX + */ +static atkbdc_softc_t default_kbdc; +static atkbdc_softc_t *atkbdc_softc[MAXKBDC] = { &default_kbdc }; + +static int verbose = KBDIO_DEBUG; + +/* function prototypes */ + +static int atkbdc_setup(atkbdc_softc_t *sc, int port); +static int addq(kqueue *q, int c); +static int removeq(kqueue *q); +static int wait_while_controller_busy(atkbdc_softc_t *kbdc); +static int wait_for_data(atkbdc_softc_t *kbdc); +static int wait_for_kbd_data(atkbdc_softc_t *kbdc); +static int wait_for_kbd_ack(atkbdc_softc_t *kbdc); +static int wait_for_aux_data(atkbdc_softc_t *kbdc); +static int wait_for_aux_ack(atkbdc_softc_t *kbdc); + +#if NATKBDC > 0 + +atkbdc_softc_t +*atkbdc_get_softc(int unit) +{ + atkbdc_softc_t *sc; + + if (unit >= sizeof(atkbdc_softc)/sizeof(atkbdc_softc[0])) + return NULL; + sc = atkbdc_softc[unit]; + if (sc == NULL) { + sc = atkbdc_softc[unit] + = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); + if (sc == NULL) + return NULL; + bzero(sc, sizeof(*sc)); + sc->port = -1; /* XXX */ + } + return sc; +} + +int +atkbdc_probe_unit(atkbdc_softc_t *sc, int unit, int port) +{ + return atkbdc_setup(sc, port); +} + +#endif /* NATKBDC > 0 */ + +/* the backdoor to the keyboard controller! XXX */ +int +atkbdc_configure(void) +{ + return atkbdc_setup(atkbdc_softc[0], -1); +} + +static int +atkbdc_setup(atkbdc_softc_t *sc, int port) +{ + if (port <= 0) + port = IO_KBD; + + if (sc->port <= 0) { + sc->command_byte = -1; + sc->command_mask = 0; + sc->lock = FALSE; + sc->kbd.head = sc->kbd.tail = 0; + sc->aux.head = sc->aux.tail = 0; +#if KBDIO_DEBUG >= 2 + sc->kbd.call_count = 0; + sc->kbd.qcount = sc->kbd.max_qcount = 0; + sc->aux.call_count = 0; + sc->aux.qcount = sc->aux.max_qcount = 0; +#endif + } + sc->port = port; /* may override the previous value */ + return 0; +} + +/* associate a port number with a KBDC */ + +KBDC +kbdc_open(int port) +{ + int s; + int i; + + if (port <= 0) + port = IO_KBD; + + s = spltty(); + for (i = 0; i < sizeof(atkbdc_softc)/sizeof(atkbdc_softc[0]); ++i) { + if (atkbdc_softc[i] == NULL) + continue; + if (atkbdc_softc[i]->port == port) { + splx(s); + return (KBDC)atkbdc_softc[i]; + } + if (atkbdc_softc[i]->port <= 0) { + if (atkbdc_setup(atkbdc_softc[i], port)) + break; + splx(s); + return (KBDC)atkbdc_softc[i]; + } + } + splx(s); + return NULL; +} + +/* + * I/O access arbitration in `kbdio' + * + * The `kbdio' module uses a simplistic convention to arbitrate + * I/O access to the controller/keyboard/mouse. The convention requires + * close cooperation of the calling device driver. + * + * The device driver which utilizes the `kbdio' module are assumed to + * have the following set of routines. + * a. An interrupt handler (the bottom half of the driver). + * b. Timeout routines which may briefly polls the keyboard controller. + * c. Routines outside interrupt context (the top half of the driver). + * They should follow the rules below: + * 1. The interrupt handler may assume that it always has full access + * to the controller/keyboard/mouse. + * 2. The other routines must issue `spltty()' if they wish to + * prevent the interrupt handler from accessing + * the controller/keyboard/mouse. + * 3. The timeout routines and the top half routines of the device driver + * arbitrate I/O access by observing the lock flag in `kbdio'. + * The flag is manipulated via `kbdc_lock()'; when one wants to + * perform I/O, call `kbdc_lock(kbdc, TRUE)' and proceed only if + * the call returns with TRUE. Otherwise the caller must back off. + * Call `kbdc_lock(kbdc, FALSE)' when necessary I/O operaion + * is finished. This mechanism does not prevent the interrupt + * handler from being invoked at any time and carrying out I/O. + * Therefore, `spltty()' must be strategically placed in the device + * driver code. Also note that the timeout routine may interrupt + * `kbdc_lock()' called by the top half of the driver, but this + * interruption is OK so long as the timeout routine observes the + * the rule 4 below. + * 4. The interrupt and timeout routines should not extend I/O operation + * across more than one interrupt or timeout; they must complete + * necessary I/O operation within one invokation of the routine. + * This measns that if the timeout routine acquires the lock flag, + * it must reset the flag to FALSE before it returns. + */ + +/* set/reset polling lock */ +int +kbdc_lock(KBDC p, int lock) +{ + int prevlock; + + prevlock = kbdcp(p)->lock; + kbdcp(p)->lock = lock; + + return (prevlock != lock); +} + +/* check if any data is waiting to be processed */ +int +kbdc_data_ready(KBDC p) +{ + return (availq(&kbdcp(p)->kbd) || availq(&kbdcp(p)->aux) + || (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)); +} + +/* queuing functions */ + +static int +addq(kqueue *q, int c) +{ + if (nextq(q->tail) != q->head) { + q->q[q->tail] = c; + q->tail = nextq(q->tail); +#if KBDIO_DEBUG >= 2 + ++q->call_count; + ++q->qcount; + if (q->qcount > q->max_qcount) + q->max_qcount = q->qcount; +#endif + return TRUE; + } + return FALSE; +} + +static int +removeq(kqueue *q) +{ + int c; + + if (q->tail != q->head) { + c = q->q[q->head]; + q->head = nextq(q->head); +#if KBDIO_DEBUG >= 2 + --q->qcount; +#endif + return c; + } + return -1; +} + +/* + * device I/O routines + */ +static int +wait_while_controller_busy(struct atkbdc_softc *kbdc) +{ + /* CPU will stay inside the loop for 100msec at most */ + int retry = 5000; + int port = kbdc->port; + int f; + + while ((f = inb(port + KBD_STATUS_PORT)) & KBDS_INPUT_BUFFER_FULL) { + if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + addq(&kbdc->kbd, inb(port + KBD_DATA_PORT)); + } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + addq(&kbdc->aux, inb(port + KBD_DATA_PORT)); + } + DELAY(KBDC_DELAYTIME); + if (--retry < 0) + return FALSE; + } + return TRUE; +} + +/* + * wait for any data; whether it's from the controller, + * the keyboard, or the aux device. + */ +static int +wait_for_data(struct atkbdc_softc *kbdc) +{ + /* CPU will stay inside the loop for 200msec at most */ + int retry = 10000; + int port = kbdc->port; + int f; + + while ((f = inb(port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) == 0) { + DELAY(KBDC_DELAYTIME); + if (--retry < 0) + return 0; + } + DELAY(KBDD_DELAYTIME); + return f; +} + +/* wait for data from the keyboard */ +static int +wait_for_kbd_data(struct atkbdc_softc *kbdc) +{ + /* CPU will stay inside the loop for 200msec at most */ + int retry = 10000; + int port = kbdc->port; + int f; + + while ((f = inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) + != KBDS_KBD_BUFFER_FULL) { + if (f == KBDS_AUX_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + addq(&kbdc->aux, inb(port + KBD_DATA_PORT)); + } + DELAY(KBDC_DELAYTIME); + if (--retry < 0) + return 0; + } + DELAY(KBDD_DELAYTIME); + return f; +} + +/* + * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the keyboard. + * queue anything else. + */ +static int +wait_for_kbd_ack(struct atkbdc_softc *kbdc) +{ + /* CPU will stay inside the loop for 200msec at most */ + int retry = 10000; + int port = kbdc->port; + int f; + int b; + + while (retry-- > 0) { + if ((f = inb(port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + b = inb(port + KBD_DATA_PORT); + if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { + if ((b == KBD_ACK) || (b == KBD_RESEND) + || (b == KBD_RESET_FAIL)) + return b; + addq(&kbdc->kbd, b); + } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { + addq(&kbdc->aux, b); + } + } + DELAY(KBDC_DELAYTIME); + } + return -1; +} + +/* wait for data from the aux device */ +static int +wait_for_aux_data(struct atkbdc_softc *kbdc) +{ + /* CPU will stay inside the loop for 200msec at most */ + int retry = 10000; + int port = kbdc->port; + int f; + + while ((f = inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) + != KBDS_AUX_BUFFER_FULL) { + if (f == KBDS_KBD_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + addq(&kbdc->kbd, inb(port + KBD_DATA_PORT)); + } + DELAY(KBDC_DELAYTIME); + if (--retry < 0) + return 0; + } + DELAY(KBDD_DELAYTIME); + return f; +} + +/* + * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the aux device. + * queue anything else. + */ +static int +wait_for_aux_ack(struct atkbdc_softc *kbdc) +{ + /* CPU will stay inside the loop for 200msec at most */ + int retry = 10000; + int port = kbdc->port; + int f; + int b; + + while (retry-- > 0) { + if ((f = inb(port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + b = inb(port + KBD_DATA_PORT); + if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { + if ((b == PSM_ACK) || (b == PSM_RESEND) + || (b == PSM_RESET_FAIL)) + return b; + addq(&kbdc->aux, b); + } else if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { + addq(&kbdc->kbd, b); + } + } + DELAY(KBDC_DELAYTIME); + } + return -1; +} + +/* write a one byte command to the controller */ +int +write_controller_command(KBDC p, int c) +{ + if (!wait_while_controller_busy(kbdcp(p))) + return FALSE; + outb(kbdcp(p)->port + KBD_COMMAND_PORT, c); + return TRUE; +} + +/* write a one byte data to the controller */ +int +write_controller_data(KBDC p, int c) +{ + if (!wait_while_controller_busy(kbdcp(p))) + return FALSE; + outb(kbdcp(p)->port + KBD_DATA_PORT, c); + return TRUE; +} + +/* write a one byte keyboard command */ +int +write_kbd_command(KBDC p, int c) +{ + if (!wait_while_controller_busy(kbdcp(p))) + return FALSE; + outb(kbdcp(p)->port + KBD_DATA_PORT, c); + return TRUE; +} + +/* write a one byte auxiliary device command */ +int +write_aux_command(KBDC p, int c) +{ + if (!write_controller_command(p, KBDC_WRITE_TO_AUX)) + return FALSE; + return write_controller_data(p, c); +} + +/* send a command to the keyboard and wait for ACK */ +int +send_kbd_command(KBDC p, int c) +{ + int retry = KBD_MAXRETRY; + int res = -1; + + while (retry-- > 0) { + if (!write_kbd_command(p, c)) + continue; + res = wait_for_kbd_ack(kbdcp(p)); + if (res == KBD_ACK) + break; + } + return res; +} + +/* send a command to the auxiliary device and wait for ACK */ +int +send_aux_command(KBDC p, int c) +{ + int retry = KBD_MAXRETRY; + int res = -1; + + while (retry-- > 0) { + if (!write_aux_command(p, c)) + continue; + /* + * FIXME: XXX + * The aux device may have already sent one or two bytes of + * status data, when a command is received. It will immediately + * stop data transmission, thus, leaving an incomplete data + * packet in our buffer. We have to discard any unprocessed + * data in order to remove such packets. Well, we may remove + * unprocessed, but necessary data byte as well... + */ + emptyq(&kbdcp(p)->aux); + res = wait_for_aux_ack(kbdcp(p)); + if (res == PSM_ACK) + break; + } + return res; +} + +/* send a command and a data to the keyboard, wait for ACKs */ +int +send_kbd_command_and_data(KBDC p, int c, int d) +{ + int retry; + int res = -1; + + for (retry = KBD_MAXRETRY; retry > 0; --retry) { + if (!write_kbd_command(p, c)) + continue; + res = wait_for_kbd_ack(kbdcp(p)); + if (res == KBD_ACK) + break; + else if (res != KBD_RESEND) + return res; + } + if (retry <= 0) + return res; + + for (retry = KBD_MAXRETRY, res = -1; retry > 0; --retry) { + if (!write_kbd_command(p, d)) + continue; + res = wait_for_kbd_ack(kbdcp(p)); + if (res != KBD_RESEND) + break; + } + return res; +} + +/* send a command and a data to the auxiliary device, wait for ACKs */ +int +send_aux_command_and_data(KBDC p, int c, int d) +{ + int retry; + int res = -1; + + for (retry = KBD_MAXRETRY; retry > 0; --retry) { + if (!write_aux_command(p, c)) + continue; + emptyq(&kbdcp(p)->aux); + res = wait_for_aux_ack(kbdcp(p)); + if (res == PSM_ACK) + break; + else if (res != PSM_RESEND) + return res; + } + if (retry <= 0) + return res; + + for (retry = KBD_MAXRETRY, res = -1; retry > 0; --retry) { + if (!write_aux_command(p, d)) + continue; + res = wait_for_aux_ack(kbdcp(p)); + if (res != PSM_RESEND) + break; + } + return res; +} + +/* + * read one byte from any source; whether from the controller, + * the keyboard, or the aux device + */ +int +read_controller_data(KBDC p) +{ + if (availq(&kbdcp(p)->kbd)) + return removeq(&kbdcp(p)->kbd); + if (availq(&kbdcp(p)->aux)) + return removeq(&kbdcp(p)->aux); + if (!wait_for_data(kbdcp(p))) + return -1; /* timeout */ + return inb(kbdcp(p)->port + KBD_DATA_PORT); +} + +#if KBDIO_DEBUG >= 2 +static int call = 0; +#endif + +/* read one byte from the keyboard */ +int +read_kbd_data(KBDC p) +{ +#if KBDIO_DEBUG >= 2 + if (++call > 2000) { + call = 0; + log(LOG_DEBUG, "kbdc: kbd q: %d calls, max %d chars, " + "aux q: %d calls, max %d chars\n", + kbdcp(p)->kbd.call_count, kbdcp(p)->kbd.max_qcount, + kbdcp(p)->aux.call_count, kbdcp(p)->aux.max_qcount); + } +#endif + + if (availq(&kbdcp(p)->kbd)) + return removeq(&kbdcp(p)->kbd); + if (!wait_for_kbd_data(kbdcp(p))) + return -1; /* timeout */ + return inb(kbdcp(p)->port + KBD_DATA_PORT); +} + +/* read one byte from the keyboard, but return immediately if + * no data is waiting + */ +int +read_kbd_data_no_wait(KBDC p) +{ + int f; + +#if KBDIO_DEBUG >= 2 + if (++call > 2000) { + call = 0; + log(LOG_DEBUG, "kbdc: kbd q: %d calls, max %d chars, " + "aux q: %d calls, max %d chars\n", + kbdcp(p)->kbd.call_count, kbdcp(p)->kbd.max_qcount, + kbdcp(p)->aux.call_count, kbdcp(p)->aux.max_qcount); + } +#endif + + if (availq(&kbdcp(p)->kbd)) + return removeq(&kbdcp(p)->kbd); + f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; + if (f == KBDS_AUX_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + addq(&kbdcp(p)->aux, inb(kbdcp(p)->port + KBD_DATA_PORT)); + f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; + } + if (f == KBDS_KBD_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + return inb(kbdcp(p)->port + KBD_DATA_PORT); + } + return -1; /* no data */ +} + +/* read one byte from the aux device */ +int +read_aux_data(KBDC p) +{ + if (availq(&kbdcp(p)->aux)) + return removeq(&kbdcp(p)->aux); + if (!wait_for_aux_data(kbdcp(p))) + return -1; /* timeout */ + return inb(kbdcp(p)->port + KBD_DATA_PORT); +} + +/* read one byte from the aux device, but return immediately if + * no data is waiting + */ +int +read_aux_data_no_wait(KBDC p) +{ + int f; + + if (availq(&kbdcp(p)->aux)) + return removeq(&kbdcp(p)->aux); + f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; + if (f == KBDS_KBD_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + addq(&kbdcp(p)->kbd, inb(kbdcp(p)->port + KBD_DATA_PORT)); + f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; + } + if (f == KBDS_AUX_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + return inb(kbdcp(p)->port + KBD_DATA_PORT); + } + return -1; /* no data */ +} + +/* discard data from the keyboard */ +void +empty_kbd_buffer(KBDC p, int wait) +{ + int t; + int b; + int f; +#if KBDIO_DEBUG >= 2 + int c1 = 0; + int c2 = 0; +#endif + int delta = 2; + + for (t = wait; t > 0; ) { + if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + b = inb(kbdcp(p)->port + KBD_DATA_PORT); + if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { + addq(&kbdcp(p)->aux, b); +#if KBDIO_DEBUG >= 2 + ++c2; + } else { + ++c1; +#endif + } + t = wait; + } else { + t -= delta; + } + DELAY(delta*1000); + } +#if KBDIO_DEBUG >= 2 + if ((c1 > 0) || (c2 > 0)) + log(LOG_DEBUG, "kbdc: %d:%d char read (empty_kbd_buffer)\n", c1, c2); +#endif + + emptyq(&kbdcp(p)->kbd); +} + +/* discard data from the aux device */ +void +empty_aux_buffer(KBDC p, int wait) +{ + int t; + int b; + int f; +#if KBDIO_DEBUG >= 2 + int c1 = 0; + int c2 = 0; +#endif + int delta = 2; + + for (t = wait; t > 0; ) { + if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + b = inb(kbdcp(p)->port + KBD_DATA_PORT); + if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { + addq(&kbdcp(p)->kbd, b); +#if KBDIO_DEBUG >= 2 + ++c1; + } else { + ++c2; +#endif + } + t = wait; + } else { + t -= delta; + } + DELAY(delta*1000); + } +#if KBDIO_DEBUG >= 2 + if ((c1 > 0) || (c2 > 0)) + log(LOG_DEBUG, "kbdc: %d:%d char read (empty_aux_buffer)\n", c1, c2); +#endif + + emptyq(&kbdcp(p)->aux); +} + +/* discard any data from the keyboard or the aux device */ +void +empty_both_buffers(KBDC p, int wait) +{ + int t; + int f; +#if KBDIO_DEBUG >= 2 + int c1 = 0; + int c2 = 0; +#endif + int delta = 2; + + for (t = wait; t > 0; ) { + if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + (void)inb(kbdcp(p)->port + KBD_DATA_PORT); +#if KBDIO_DEBUG >= 2 + if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) + ++c1; + else + ++c2; +#endif + t = wait; + } else { + t -= delta; + } + DELAY(delta*1000); + } +#if KBDIO_DEBUG >= 2 + if ((c1 > 0) || (c2 > 0)) + log(LOG_DEBUG, "kbdc: %d:%d char read (empty_both_buffers)\n", c1, c2); +#endif + + emptyq(&kbdcp(p)->kbd); + emptyq(&kbdcp(p)->aux); +} + +/* keyboard and mouse device control */ + +/* NOTE: enable the keyboard port but disable the keyboard + * interrupt before calling "reset_kbd()". + */ +int +reset_kbd(KBDC p) +{ + int retry = KBD_MAXRETRY; + int again = KBD_MAXWAIT; + int c = KBD_RESEND; /* keep the compiler happy */ + + while (retry-- > 0) { + empty_both_buffers(p, 10); + if (!write_kbd_command(p, KBDC_RESET_KBD)) + continue; + emptyq(&kbdcp(p)->kbd); + c = read_controller_data(p); + if (verbose || bootverbose) + log(LOG_DEBUG, "kbdc: RESET_KBD return code:%04x\n", c); + if (c == KBD_ACK) /* keyboard has agreed to reset itself... */ + break; + } + if (retry < 0) + return FALSE; + + while (again-- > 0) { + /* wait awhile, well, in fact we must wait quite loooooooooooong */ + DELAY(KBD_RESETDELAY*1000); + c = read_controller_data(p); /* RESET_DONE/RESET_FAIL */ + if (c != -1) /* wait again if the controller is not ready */ + break; + } + if (verbose || bootverbose) + log(LOG_DEBUG, "kbdc: RESET_KBD status:%04x\n", c); + if (c != KBD_RESET_DONE) + return FALSE; + return TRUE; +} + +/* NOTE: enable the aux port but disable the aux interrupt + * before calling `reset_aux_dev()'. + */ +int +reset_aux_dev(KBDC p) +{ + int retry = KBD_MAXRETRY; + int again = KBD_MAXWAIT; + int c = PSM_RESEND; /* keep the compiler happy */ + + while (retry-- > 0) { + empty_both_buffers(p, 10); + if (!write_aux_command(p, PSMC_RESET_DEV)) + continue; + emptyq(&kbdcp(p)->aux); + /* NOTE: Compaq Armada laptops require extra delay here. XXX */ + for (again = KBD_MAXWAIT; again > 0; --again) { + DELAY(KBD_RESETDELAY*1000); + c = read_aux_data_no_wait(p); + if (c != -1) + break; + } + if (verbose || bootverbose) + log(LOG_DEBUG, "kbdc: RESET_AUX return code:%04x\n", c); + if (c == PSM_ACK) /* aux dev is about to reset... */ + break; + } + if (retry < 0) + return FALSE; + + for (again = KBD_MAXWAIT; again > 0; --again) { + /* wait awhile, well, quite looooooooooooong */ + DELAY(KBD_RESETDELAY*1000); + c = read_aux_data_no_wait(p); /* RESET_DONE/RESET_FAIL */ + if (c != -1) /* wait again if the controller is not ready */ + break; + } + if (verbose || bootverbose) + log(LOG_DEBUG, "kbdc: RESET_AUX status:%04x\n", c); + if (c != PSM_RESET_DONE) /* reset status */ + return FALSE; + + c = read_aux_data(p); /* device ID */ + if (verbose || bootverbose) + log(LOG_DEBUG, "kbdc: RESET_AUX ID:%04x\n", c); + /* NOTE: we could check the device ID now, but leave it later... */ + return TRUE; +} + +/* controller diagnostics and setup */ + +int +test_controller(KBDC p) +{ + int retry = KBD_MAXRETRY; + int again = KBD_MAXWAIT; + int c = KBD_DIAG_FAIL; + + while (retry-- > 0) { + empty_both_buffers(p, 10); + if (write_controller_command(p, KBDC_DIAGNOSE)) + break; + } + if (retry < 0) + return FALSE; + + emptyq(&kbdcp(p)->kbd); + while (again-- > 0) { + /* wait awhile */ + DELAY(KBD_RESETDELAY*1000); + c = read_controller_data(p); /* DIAG_DONE/DIAG_FAIL */ + if (c != -1) /* wait again if the controller is not ready */ + break; + } + if (verbose || bootverbose) + log(LOG_DEBUG, "kbdc: DIAGNOSE status:%04x\n", c); + return (c == KBD_DIAG_DONE); +} + +int +test_kbd_port(KBDC p) +{ + int retry = KBD_MAXRETRY; + int again = KBD_MAXWAIT; + int c = -1; + + while (retry-- > 0) { + empty_both_buffers(p, 10); + if (write_controller_command(p, KBDC_TEST_KBD_PORT)) + break; + } + if (retry < 0) + return FALSE; + + emptyq(&kbdcp(p)->kbd); + while (again-- > 0) { + c = read_controller_data(p); + if (c != -1) /* try again if the controller is not ready */ + break; + } + if (verbose || bootverbose) + log(LOG_DEBUG, "kbdc: TEST_KBD_PORT status:%04x\n", c); + return c; +} + +int +test_aux_port(KBDC p) +{ + int retry = KBD_MAXRETRY; + int again = KBD_MAXWAIT; + int c = -1; + + while (retry-- > 0) { + empty_both_buffers(p, 10); + if (write_controller_command(p, KBDC_TEST_AUX_PORT)) + break; + } + if (retry < 0) + return FALSE; + + emptyq(&kbdcp(p)->kbd); + while (again-- > 0) { + c = read_controller_data(p); + if (c != -1) /* try again if the controller is not ready */ + break; + } + if (verbose || bootverbose) + log(LOG_DEBUG, "kbdc: TEST_AUX_PORT status:%04x\n", c); + return c; +} + +int +kbdc_get_device_mask(KBDC p) +{ + return kbdcp(p)->command_mask; +} + +void +kbdc_set_device_mask(KBDC p, int mask) +{ + kbdcp(p)->command_mask = + mask & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS); +} + +int +get_controller_command_byte(KBDC p) +{ + if (kbdcp(p)->command_byte != -1) + return kbdcp(p)->command_byte; + if (!write_controller_command(p, KBDC_GET_COMMAND_BYTE)) + return -1; + emptyq(&kbdcp(p)->kbd); + kbdcp(p)->command_byte = read_controller_data(p); + return kbdcp(p)->command_byte; +} + +int +set_controller_command_byte(KBDC p, int mask, int command) +{ + if (get_controller_command_byte(p) == -1) + return FALSE; + + command = (kbdcp(p)->command_byte & ~mask) | (command & mask); + if (command & KBD_DISABLE_KBD_PORT) { + if (!write_controller_command(p, KBDC_DISABLE_KBD_PORT)) + return FALSE; + } + if (!write_controller_command(p, KBDC_SET_COMMAND_BYTE)) + return FALSE; + if (!write_controller_data(p, command)) + return FALSE; + kbdcp(p)->command_byte = command; + + if (verbose) + log(LOG_DEBUG, "kbdc: new command byte:%04x (set_controller...)\n", + command); + + return TRUE; +} diff --git a/sys/dev/kbd/atkbdcreg.h b/sys/dev/kbd/atkbdcreg.h new file mode 100644 index 0000000..81b0ad3 --- /dev/null +++ b/sys/dev/kbd/atkbdcreg.h @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 1996-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. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $Id: $ + * from kbdio.h,v 1.8 1998/09/25 11:55:46 yokota Exp + */ + +#ifndef _DEV_KBD_ATKBDCREG_H_ +#define _DEV_KBD_ATKBDCREG_H_ + +/* constants */ + +/* I/O ports */ +#define KBD_STATUS_PORT 4 /* status port, read */ +#define KBD_COMMAND_PORT 4 /* controller command port, write */ +#define KBD_DATA_PORT 0 /* data port, read/write + * also used as keyboard command + * and mouse command port + */ + +/* controller commands (sent to KBD_COMMAND_PORT) */ +#define KBDC_SET_COMMAND_BYTE 0x0060 +#define KBDC_GET_COMMAND_BYTE 0x0020 +#define KBDC_WRITE_TO_AUX 0x00d4 +#define KBDC_DISABLE_AUX_PORT 0x00a7 +#define KBDC_ENABLE_AUX_PORT 0x00a8 +#define KBDC_TEST_AUX_PORT 0x00a9 +#define KBDC_DIAGNOSE 0x00aa +#define KBDC_TEST_KBD_PORT 0x00ab +#define KBDC_DISABLE_KBD_PORT 0x00ad +#define KBDC_ENABLE_KBD_PORT 0x00ae + +/* controller command byte (set by KBDC_SET_COMMAND_BYTE) */ +#define KBD_TRANSLATION 0x0040 +#define KBD_RESERVED_BITS 0x0004 +#define KBD_OVERRIDE_KBD_LOCK 0x0008 +#define KBD_ENABLE_KBD_PORT 0x0000 +#define KBD_DISABLE_KBD_PORT 0x0010 +#define KBD_ENABLE_AUX_PORT 0x0000 +#define KBD_DISABLE_AUX_PORT 0x0020 +#define KBD_ENABLE_AUX_INT 0x0002 +#define KBD_DISABLE_AUX_INT 0x0000 +#define KBD_ENABLE_KBD_INT 0x0001 +#define KBD_DISABLE_KBD_INT 0x0000 +#define KBD_KBD_CONTROL_BITS (KBD_DISABLE_KBD_PORT | KBD_ENABLE_KBD_INT) +#define KBD_AUX_CONTROL_BITS (KBD_DISABLE_AUX_PORT | KBD_ENABLE_AUX_INT) + +/* keyboard device commands (sent to KBD_DATA_PORT) */ +#define KBDC_RESET_KBD 0x00ff +#define KBDC_ENABLE_KBD 0x00f4 +#define KBDC_DISABLE_KBD 0x00f5 +#define KBDC_SET_DEFAULTS 0x00f6 +#define KBDC_SEND_DEV_ID 0x00f2 +#define KBDC_SET_LEDS 0x00ed +#define KBDC_ECHO 0x00ee +#define KBDC_SET_SCANCODE_SET 0x00f0 +#define KBDC_SET_TYPEMATIC 0x00f3 + +/* aux device commands (sent to KBD_DATA_PORT) */ +#define PSMC_RESET_DEV 0x00ff +#define PSMC_ENABLE_DEV 0x00f4 +#define PSMC_DISABLE_DEV 0x00f5 +#define PSMC_SET_DEFAULTS 0x00f6 +#define PSMC_SEND_DEV_ID 0x00f2 +#define PSMC_SEND_DEV_STATUS 0x00e9 +#define PSMC_SEND_DEV_DATA 0x00eb +#define PSMC_SET_SCALING11 0x00e6 +#define PSMC_SET_SCALING21 0x00e7 +#define PSMC_SET_RESOLUTION 0x00e8 +#define PSMC_SET_STREAM_MODE 0x00ea +#define PSMC_SET_REMOTE_MODE 0x00f0 +#define PSMC_SET_SAMPLING_RATE 0x00f3 + +/* PSMC_SET_RESOLUTION argument */ +#define PSMD_RES_LOW 0 /* typically 25ppi */ +#define PSMD_RES_MEDIUM_LOW 1 /* typically 50ppi */ +#define PSMD_RES_MEDIUM_HIGH 2 /* typically 100ppi (default) */ +#define PSMD_RES_HIGH 3 /* typically 200ppi */ +#define PSMD_MAX_RESOLUTION PSMD_RES_HIGH + +/* PSMC_SET_SAMPLING_RATE */ +#define PSMD_MAX_RATE 255 /* FIXME: not sure if it's possible */ + +/* status bits (KBD_STATUS_PORT) */ +#define KBDS_BUFFER_FULL 0x0021 +#define KBDS_ANY_BUFFER_FULL 0x0001 +#define KBDS_KBD_BUFFER_FULL 0x0001 +#define KBDS_AUX_BUFFER_FULL 0x0021 +#define KBDS_INPUT_BUFFER_FULL 0x0002 + +/* return code */ +#define KBD_ACK 0x00fa +#define KBD_RESEND 0x00fe +#define KBD_RESET_DONE 0x00aa +#define KBD_RESET_FAIL 0x00fc +#define KBD_DIAG_DONE 0x0055 +#define KBD_DIAG_FAIL 0x00fd +#define KBD_ECHO 0x00ee + +#define PSM_ACK 0x00fa +#define PSM_RESEND 0x00fe +#define PSM_RESET_DONE 0x00aa +#define PSM_RESET_FAIL 0x00fc + +/* aux device ID */ +#define PSM_MOUSE_ID 0 +#define PSM_BALLPOINT_ID 2 +#define PSM_INTELLI_ID 3 + +#ifdef KERNEL + +#define ATKBDC_DRIVER_NAME "atkbdc" + +/* + * driver specific options: the following options may be set by + * `options' statements in the kernel configuration file. + */ + +/* retry count */ +#ifndef KBD_MAXRETRY +#define KBD_MAXRETRY 3 +#endif + +/* timing parameters */ +#ifndef KBD_RESETDELAY +#define KBD_RESETDELAY 200 /* wait 200msec after kbd/mouse reset */ +#endif +#ifndef KBD_MAXWAIT +#define KBD_MAXWAIT 5 /* wait 5 times at most after reset */ +#endif + +/* I/O recovery time */ +#define KBDC_DELAYTIME 20 +#define KBDD_DELAYTIME 7 + +/* debug option */ +#ifndef KBDIO_DEBUG +#define KBDIO_DEBUG 0 +#endif + +/* end of driver specific options */ + +/* types/structures */ + +#define KBDQ_BUFSIZE 32 + +typedef struct _kqueue { + int head; + int tail; + unsigned char q[KBDQ_BUFSIZE]; +#if KBDIO_DEBUG >= 2 + int call_count; + int qcount; + int max_qcount; +#endif +} kqueue; + +typedef struct atkbdc_softc { + int port; /* base port address */ + int command_byte; /* current command byte value */ + int command_mask; /* command byte mask bits for kbd/aux devices */ + int lock; /* FIXME: XXX not quite a semaphore... */ + kqueue kbd; /* keyboard data queue */ + kqueue aux; /* auxiliary data queue */ +} atkbdc_softc_t; + +enum kbdc_device_ivar { + KBDC_IVAR_PORT, + KBDC_IVAR_IRQ, + KBDC_IVAR_FLAGS, +}; + +typedef caddr_t KBDC; + +/* function prototypes */ + +atkbdc_softc_t *atkbdc_get_softc(int unit); +int atkbdc_probe_unit(atkbdc_softc_t *sc, int unit, int port); +int atkbdc_configure(void); + +KBDC kbdc_open(int port); +int kbdc_lock(KBDC kbdc, int lock); +int kbdc_data_ready(KBDC kbdc); + +int write_controller_command(KBDC kbdc,int c); +int write_controller_data(KBDC kbdc,int c); + +int write_kbd_command(KBDC kbdc,int c); +int write_aux_command(KBDC kbdc,int c); +int send_kbd_command(KBDC kbdc,int c); +int send_aux_command(KBDC kbdc,int c); +int send_kbd_command_and_data(KBDC kbdc,int c,int d); +int send_aux_command_and_data(KBDC kbdc,int c,int d); + +int read_controller_data(KBDC kbdc); +int read_kbd_data(KBDC kbdc); +int read_kbd_data_no_wait(KBDC kbdc); +int read_aux_data(KBDC kbdc); +int read_aux_data_no_wait(KBDC kbdc); + +void empty_kbd_buffer(KBDC kbdc, int t); +void empty_aux_buffer(KBDC kbdc, int t); +void empty_both_buffers(KBDC kbdc, int t); + +int reset_kbd(KBDC kbdc); +int reset_aux_dev(KBDC kbdc); + +int test_controller(KBDC kbdc); +int test_kbd_port(KBDC kbdc); +int test_aux_port(KBDC kbdc); + +int kbdc_get_device_mask(KBDC kbdc); +void kbdc_set_device_mask(KBDC kbdc, int mask); + +int get_controller_command_byte(KBDC kbdc); +int set_controller_command_byte(KBDC kbdc, int command, int flag); + +#endif /* KERNEL */ + +#endif /* !_DEV_KBD_ATKBDCREG_H_ */ diff --git a/sys/dev/kbd/atkbdreg.h b/sys/dev/kbd/atkbdreg.h new file mode 100644 index 0000000..9f20eee --- /dev/null +++ b/sys/dev/kbd/atkbdreg.h @@ -0,0 +1,61 @@ +/*- + * 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: $ + */ + +#ifndef _DEV_KBD_ATKBDREG_H_ +#define _DEV_KBD_ATKBDREG_H_ + +#define ATKBD_DRIVER_NAME "atkbd" +#define ATKBD_UNIT(dev) minor(dev) +#define ATKBD_MKMINOR(unit) (unit) + +/* device configuration flags (atkbdprobe, atkbdattach) */ +#define KB_CONF_FAIL_IF_NO_KBD (1 << 0) /* don't install if no kbd is found */ +#define KB_CONF_NO_RESET (1 << 1) /* don't reset the keyboard */ +#define KB_CONF_ALT_SCANCODESET (1 << 2) /* assume the XT type keyboard */ + +#ifdef KERNEL + +typedef struct atkbd_softc { + short flags; +#define ATKBD_ATTACHED (1 << 0) + keyboard_t *kbd; +#ifdef KBD_INSTALL_CDEV + genkbd_softc_t gensc; +#endif +} atkbd_softc_t; + +#ifdef __i386__ +atkbd_softc_t *atkbd_get_softc(int unit); +#endif +int atkbd_probe_unit(int unit, atkbd_softc_t *sc, + int port, int irq, int flags); +int atkbd_attach_unit(int unit, atkbd_softc_t *sc); + +#endif /* KERNEL */ + +#endif /* !_DEV_KBD_ATKBDREG_H_ */ diff --git a/sys/dev/kbd/kbd.c b/sys/dev/kbd/kbd.c new file mode 100644 index 0000000..7fac709 --- /dev/null +++ b/sys/dev/kbd/kbd.c @@ -0,0 +1,1193 @@ +/*- + * 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 "kbd.h" +#include "opt_kbd.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/tty.h> +#include <sys/poll.h> +#include <sys/vnode.h> +#include <sys/uio.h> + +#include <machine/console.h> + +#include <dev/kbd/kbdreg.h> + +/* local arrays */ + +/* + * We need at least one entry each in order to initialize a keyboard + * for the kernel console. The arrays will be increased dynamically + * when necessary. + */ +static keyboard_t *kbd_ini; +static keyboard_switch_t *kbdsw_ini; +static struct cdevsw *kbdcdevsw_ini; + +static keyboard_t **keyboard = &kbd_ini; +static int keyboards = 1; + keyboard_switch_t **kbdsw = &kbdsw_ini; +static struct cdevsw **kbdcdevsw = &kbdcdevsw_ini; + +#define ARRAY_DELTA 4 + +static void +kbd_realloc_array(void) +{ + keyboard_t **new_kbd; + keyboard_switch_t **new_kbdsw; + struct cdevsw **new_cdevsw; + int newsize; + int s; + + s = spltty(); + newsize = ((keyboards + ARRAY_DELTA)/ARRAY_DELTA)*ARRAY_DELTA; + new_kbd = malloc(sizeof(*new_kbd)*newsize, M_DEVBUF, M_NOWAIT); + new_kbdsw = malloc(sizeof(*new_kbdsw)*newsize, M_DEVBUF, M_NOWAIT); + new_cdevsw = malloc(sizeof(*new_cdevsw)*newsize, M_DEVBUF, M_NOWAIT); + bzero(new_kbd, sizeof(*new_kbd)*newsize); + bzero(new_kbdsw, sizeof(*new_kbdsw)*newsize); + bzero(new_cdevsw, sizeof(*new_cdevsw)*newsize); + bcopy(keyboard, new_kbd, sizeof(*keyboard)*keyboards); + bcopy(kbdsw, new_kbdsw, sizeof(*kbdsw)*keyboards); + bcopy(kbdcdevsw, new_cdevsw, sizeof(*kbdcdevsw)*keyboards); + if (keyboards > 1) { + free(keyboard, M_DEVBUF); + free(kbdsw, M_DEVBUF); + free(kbdcdevsw, M_DEVBUF); + } + keyboard = new_kbd; + kbdsw = new_kbdsw; + kbdcdevsw = new_cdevsw; + keyboards = newsize; + splx(s); + + if (bootverbose) + printf("kbd: new array size %d\n", keyboards); +} + +/* + * Low-level keyboard driver functions + * Keyboard subdrivers, such as the AT keyboard driver and the USB keyboard + * driver, call these functions to initialize the keyboard_t structure + * and register it to the virtual keyboard driver `kbd'. + */ + +/* initialize the keyboard_t structure */ +void +kbd_init_struct(keyboard_t *kbd, char *name, int type, int unit, int config, + int port, int port_size) +{ + kbd->kb_flags = KB_NO_DEVICE; /* device has not been found */ + kbd->kb_name = name; + kbd->kb_type = type; + kbd->kb_unit = unit; + kbd->kb_config = config; + kbd->kb_led = 0; /* unknown */ + kbd->kb_io_base = port; + kbd->kb_io_size = port_size; + kbd->kb_data = NULL; + kbd->kb_keymap = NULL; + kbd->kb_accentmap = NULL; + kbd->kb_fkeytab = NULL; + kbd->kb_fkeytab_size = 0; +} + +void +kbd_set_maps(keyboard_t *kbd, keymap_t *keymap, accentmap_t *accmap, + fkeytab_t *fkeymap, int fkeymap_size) +{ + kbd->kb_keymap = keymap; + kbd->kb_accentmap = accmap; + kbd->kb_fkeytab = fkeymap; + kbd->kb_fkeytab_size = fkeymap_size; +} + +/* register a keyboard and associate it with a function table */ +int +kbd_register(keyboard_t *kbd) +{ + keyboard_driver_t **list; + keyboard_driver_t *p; + int index; + + for (index = 0; index < keyboards; ++index) { + if (keyboard[index] == NULL) + break; + } + if (index >= keyboards) + return -1; + + kbd->kb_index = index; + KBD_UNBUSY(kbd); + KBD_VALID(kbd); + kbd->kb_active = 0; /* disabled until someone calls kbd_enable() */ + kbd->kb_token = NULL; + kbd->kb_callback.kc_func = NULL; + kbd->kb_callback.kc_arg = NULL; + + list = (keyboard_driver_t **)kbddriver_set.ls_items; + while ((p = *list++) != NULL) { + if (strcmp(p->name, kbd->kb_name) == 0) { + keyboard[index] = kbd; + kbdsw[index] = p->kbdsw; + return index; + } + } + + return -1; +} + +int +kbd_unregister(keyboard_t *kbd) +{ + int error; + int s; + + if ((kbd->kb_index < 0) || (kbd->kb_index >= keyboards)) + return ENOENT; + if (keyboard[kbd->kb_index] != kbd) + return ENOENT; + + s = spltty(); + if (KBD_IS_BUSY(kbd)) { + error = (*kbd->kb_callback.kc_func)(kbd, KBDIO_UNLOADING, + kbd->kb_callback.kc_arg); + if (error) { + splx(s); + return error; + } + if (KBD_IS_BUSY(kbd)) { + splx(s); + return EBUSY; + } + } + KBD_INVALID(kbd); + keyboard[kbd->kb_index] = NULL; + kbdsw[kbd->kb_index] = NULL; + + splx(s); + return 0; +} + +/* find a funciton table by the driver name */ +keyboard_switch_t +*kbd_get_switch(char *driver) +{ + keyboard_driver_t **list; + keyboard_driver_t *p; + + list = (keyboard_driver_t **)kbddriver_set.ls_items; + while ((p = *list++) != NULL) { + if (strcmp(p->name, driver) == 0) + return p->kbdsw; + } + + return NULL; +} + +/* + * Keyboard client functions + * Keyboard clients, such as the console driver `syscons' and the keyboard + * cdev driver, use these functions to claim and release a keyboard for + * exclusive use. + */ + +/* find the keyboard specified by a driver name and a unit number */ +int +kbd_find_keyboard(char *driver, int unit) +{ + int i; + + for (i = 0; i < keyboards; ++i) { + if (keyboard[i] == NULL) + continue; + if (!KBD_IS_VALID(keyboard[i])) + continue; + if (strcmp("*", driver) && strcmp(keyboard[i]->kb_name, driver)) + continue; + if ((unit != -1) && (keyboard[i]->kb_unit != unit)) + continue; + return i; + } + return -1; +} + +/* allocate a keyboard */ +int +kbd_allocate(char *driver, int unit, void *id, kbd_callback_func_t *func, + void *arg) +{ + int index; + int s; + + if (func == NULL) + return -1; + + s = spltty(); + index = kbd_find_keyboard(driver, unit); + if (index >= 0) { + if (KBD_IS_BUSY(keyboard[index])) { + splx(s); + return -1; + } + keyboard[index]->kb_token = id; + KBD_BUSY(keyboard[index]); + keyboard[index]->kb_callback.kc_func = func; + keyboard[index]->kb_callback.kc_arg = arg; + (*kbdsw[index]->clear_state)(keyboard[index]); + } + splx(s); + return index; +} + +int +kbd_release(keyboard_t *kbd, void *id) +{ + int error; + int s; + + s = spltty(); + if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) { + error = EINVAL; + } else if (kbd->kb_token != id) { + error = EPERM; + } else { + kbd->kb_token = NULL; + KBD_UNBUSY(kbd); + kbd->kb_callback.kc_func = NULL; + kbd->kb_callback.kc_arg = NULL; + (*kbdsw[kbd->kb_index]->clear_state)(kbd); + error = 0; + } + splx(s); + return error; +} + +int +kbd_change_callback(keyboard_t *kbd, void *id, kbd_callback_func_t *func, + void *arg) +{ + int error; + int s; + + s = spltty(); + if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) { + error = EINVAL; + } else if (kbd->kb_token != id) { + error = EPERM; + } else if (func == NULL) { + error = EINVAL; + } else { + kbd->kb_callback.kc_func = func; + kbd->kb_callback.kc_arg = arg; + error = 0; + } + splx(s); + return error; +} + +/* get a keyboard structure */ +keyboard_t +*kbd_get_keyboard(int index) +{ + if ((index < 0) || (index >= keyboards)) + return NULL; + if (!KBD_IS_VALID(keyboard[index])) + return NULL; + return keyboard[index]; +} + +/* + * The back door for the console driver; configure keyboards + * This function is for the kernel console to initialize keyboards + * at very early stage. + */ + +int +kbd_configure(int flags) +{ + keyboard_driver_t **list; + keyboard_driver_t *p; + + list = (keyboard_driver_t **)kbddriver_set.ls_items; + while ((p = *list++) != NULL) { + if (p->configure != NULL) + (*p->configure)(flags); + } + + return 0; +} + +#ifdef KBD_INSTALL_CDEV + +/* + * Virtual keyboard cdev driver functions + * The virtual keyboard driver dispatches driver functions to + * appropriate subdrivers. + */ + +#define KBD_UNIT(dev) minor(dev) + +static d_open_t kbdopen; +static d_close_t kbdclose; +static d_read_t kbdread; +static d_write_t kbdwrite; +static d_ioctl_t kbdioctl; +static d_reset_t kbdreset; +static d_devtotty_t kbddevtotty; +static d_poll_t kbdpoll; +static d_mmap_t kbdmmap; + +#define CDEV_MAJOR 112 + +static struct cdevsw kbd_cdevsw = { + kbdopen, kbdclose, kbdread, kbdwrite, /* ??? */ + kbdioctl, nullstop, kbdreset, kbddevtotty, + kbdpoll, kbdmmap, nostrategy, "kbd", + NULL, -1, nodump, nopsize, +}; + +static void +vkbdattach(void *arg) +{ + static int kbd_devsw_installed = FALSE; + dev_t dev; + + if (!kbd_devsw_installed) { + dev = makedev(CDEV_MAJOR, 0); + cdevsw_add(&dev, &kbd_cdevsw, NULL); + kbd_devsw_installed = TRUE; + } +} + +PSEUDO_SET(vkbdattach, kbd); + +int +kbd_attach(dev_t dev, keyboard_t *kbd, struct cdevsw *cdevsw) +{ + int s; + + if (kbd->kb_index >= keyboards) + return EINVAL; + if (keyboard[kbd->kb_index] != kbd) + return EINVAL; + + s = spltty(); + kbd->kb_minor = minor(dev); + kbdcdevsw[kbd->kb_index] = cdevsw; + splx(s); + + /* XXX: DEVFS? */ + + if (kbd->kb_index + 1 >= keyboards) + kbd_realloc_array(); + + printf("kbd%d at %s%d\n", kbd->kb_index, kbd->kb_name, kbd->kb_unit); + return 0; +} + +int +kbd_detach(dev_t dev, keyboard_t *kbd, struct cdevsw *cdevsw) +{ + int s; + + if (kbd->kb_index >= keyboards) + return EINVAL; + if (keyboard[kbd->kb_index] != kbd) + return EINVAL; + if (kbdcdevsw[kbd->kb_index] != cdevsw) + return EINVAL; + + s = spltty(); + (*kbdsw[kbd->kb_index]->term)(kbd); + kbdcdevsw[kbd->kb_index] = NULL; + splx(s); + return 0; +} + +static int +kbdopen(dev_t dev, int flag, int mode, struct proc *p) +{ + int unit; + + unit = KBD_UNIT(dev); + if (unit >= keyboards) + return ENXIO; + if (kbdcdevsw[unit] == NULL) + return ENXIO; + if (KBD_IS_BUSY(keyboard[unit])) + return EBUSY; + return (*kbdcdevsw[unit]->d_open)(makedev(0, keyboard[unit]->kb_minor), + flag, mode, p); +} + +static int +kbdclose(dev_t dev, int flag, int mode, struct proc *p) +{ + int unit; + + unit = KBD_UNIT(dev); + if (kbdcdevsw[unit] == NULL) + return ENXIO; + return (*kbdcdevsw[unit]->d_close)(makedev(0, keyboard[unit]->kb_minor), + flag, mode, p); +} + +static int +kbdread(dev_t dev, struct uio *uio, int flag) +{ + int unit; + + unit = KBD_UNIT(dev); + if (kbdcdevsw[unit] == NULL) + return ENXIO; + return (*kbdcdevsw[unit]->d_read)(makedev(0, keyboard[unit]->kb_minor), + uio, flag); +} + +static int +kbdwrite(dev_t dev, struct uio *uio, int flag) +{ + int unit; + + unit = KBD_UNIT(dev); + if (kbdcdevsw[unit] == NULL) + return ENXIO; + return (*kbdcdevsw[unit]->d_write)(makedev(0, keyboard[unit]->kb_minor), + uio, flag); +} + +static int +kbdioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p) +{ + int unit; + + unit = KBD_UNIT(dev); + if (kbdcdevsw[unit] == NULL) + return ENXIO; + return (*kbdcdevsw[unit]->d_ioctl)(makedev(0, keyboard[unit]->kb_minor), + cmd, arg, flag, p); +} + +static int +kbdreset(dev_t dev) +{ + int unit; + + unit = KBD_UNIT(dev); + if (kbdcdevsw[unit] == NULL) + return ENXIO; + return (*kbdcdevsw[unit]->d_reset)(makedev(0, keyboard[unit]->kb_minor)); +} + +static struct tty +*kbddevtotty(dev_t dev) +{ + int unit; + + unit = KBD_UNIT(dev); + if (kbdcdevsw[unit] == NULL) + return NULL; + return (*kbdcdevsw[unit]->d_devtotty)(makedev(0, keyboard[unit]->kb_minor)); +} + +static int +kbdpoll(dev_t dev, int event, struct proc *p) +{ + int unit; + + unit = KBD_UNIT(dev); + if (kbdcdevsw[unit] == NULL) + return ENXIO; + return (*kbdcdevsw[unit]->d_poll)(makedev(0, keyboard[unit]->kb_minor), + event, p); +} + +static int +kbdmmap(dev_t dev, vm_offset_t offset, int nprot) +{ + int unit; + + unit = KBD_UNIT(dev); + if (kbdcdevsw[unit] == NULL) + return ENXIO; + return (*kbdcdevsw[unit]->d_mmap)(makedev(0, keyboard[unit]->kb_minor), + offset, nprot); +} + +/* + * Generic keyboard cdev driver functions + * Keyboard subdrivers may call these functions to implement common + * driver functions. + */ + +#define KB_QSIZE 512 +#define KB_BUFSIZE 64 + +static kbd_callback_func_t genkbd_event; + +int +genkbdopen(genkbd_softc_t *sc, keyboard_t *kbd, int mode, int flag, + struct proc *p) +{ + int s; + int i; + + s = spltty(); + if (!KBD_IS_VALID(kbd)) { + splx(s); + return ENXIO; + } + i = kbd_allocate(kbd->kb_name, kbd->kb_unit, sc, + genkbd_event, (void *)sc); + if (i < 0) { + splx(s); + return EBUSY; + } + /* assert(i == kbd->kb_index) */ + /* assert(kbd == kbd_get_keyboard(i)) */ + + /* + * NOTE: even when we have successfully claimed a keyboard, + * the device may still be missing (!KBD_HAS_DEVICE(kbd)). + */ + +#if 0 + bzero(&sc->gkb_q, sizeof(sc->gkb_q)); +#endif + clist_alloc_cblocks(&sc->gkb_q, KB_QSIZE, KB_QSIZE/2); /* XXX */ + sc->gkb_rsel.si_flags = 0; + sc->gkb_rsel.si_pid = 0; + splx(s); + + return 0; +} + +int +genkbdclose(genkbd_softc_t *sc, keyboard_t *kbd, int mode, int flag, + struct proc *p) +{ + int s; + + /* + * NOTE: the device may have already become invalid. + * !KBD_IS_VALID(kbd) + */ + s = spltty(); + kbd_release(kbd, (void *)sc); +#if 0 + clist_free_cblocks(&sc->gkb_q); +#endif + splx(s); + + return 0; +} + +int +genkbdread(genkbd_softc_t *sc, keyboard_t *kbd, struct uio *uio, int flag) +{ + u_char buffer[KB_BUFSIZE]; + int len; + int error; + int s; + + /* wait for input */ + s = spltty(); + while (sc->gkb_q.c_cc == 0) { + if (!KBD_IS_VALID(kbd)) { + splx(s); + return EIO; + } + if (flag & IO_NDELAY) { + splx(s); + return EWOULDBLOCK; + } + sc->gkb_flags |= KB_ASLEEP; + error = tsleep((caddr_t)sc, PZERO | PCATCH, "kbdrea", 0); + if (error) { + sc->gkb_flags &= ~KB_ASLEEP; + splx(s); + return error; + } + } + splx(s); + + /* copy as much input as possible */ + error = 0; + while (uio->uio_resid > 0) { + len = imin(uio->uio_resid, sizeof(buffer)); + len = q_to_b(&sc->gkb_q, buffer, len); + if (len <= 0) + break; + error = uiomove(buffer, len, uio); + if (error) + break; + } + + return error; +} + +int +genkbdwrite(genkbd_softc_t *sc, keyboard_t *kbd, struct uio *uio, int flag) +{ + if (!KBD_IS_VALID(kbd)) + return ENXIO; + return ENODEV; +} + +int +genkbdioctl(genkbd_softc_t *sc, keyboard_t *kbd, u_long cmd, caddr_t arg, + int flag, struct proc *p) +{ + int error; + + if (kbd == NULL) /* XXX */ + return ENXIO; + if (!KBD_IS_VALID(kbd)) + return ENXIO; + error = (*kbdsw[kbd->kb_index]->ioctl)(kbd, cmd, arg); + if (error == ENOIOCTL) + error = ENODEV; + return error; +} + +int +genkbdpoll(genkbd_softc_t *sc, keyboard_t *kbd, int events, struct proc *p) +{ + int revents; + int s; + + revents = 0; + s = spltty(); + if (events & (POLLIN | POLLRDNORM)) { + if ((sc->gkb_q.c_cc > 0) || !KBD_IS_VALID(kbd)) + revents |= (POLLIN | POLLRDNORM); + else + selrecord(p, &sc->gkb_rsel); + } + splx(s); + return revents; +} + +static int +genkbd_event(keyboard_t *kbd, int event, void *arg) +{ + genkbd_softc_t *sc; + size_t len; + u_char *cp; + int mode; + int c; + + /* assert(KBD_IS_VALID(kbd)) */ + sc = (genkbd_softc_t *)arg; + + switch (event) { + case KBDIO_KEYINPUT: + break; + case KBDIO_UNLOADING: + /* the keyboard is going... */ + kbd_release(kbd, (void *)sc); + return 0; + default: + return EINVAL; + } + + /* obtain the current key input mode */ + if ((*kbdsw[kbd->kb_index]->ioctl)(kbd, KDGKBMODE, (caddr_t)&mode)) + mode = K_XLATE; + + /* read all pending input */ + while ((*kbdsw[kbd->kb_index]->check_char)(kbd)) { + c = (*kbdsw[kbd->kb_index]->read_char)(kbd, FALSE); + if (c == NOKEY) + continue; + if (c == ERRKEY) /* XXX: ring bell? */ + continue; + if (!KBD_IS_BUSY(kbd)) + /* the device is not open, discard the input */ + continue; + + /* store the byte as is for K_RAW and K_CODE modes */ + if (mode != K_XLATE) { + putc(KEYCHAR(c), &sc->gkb_q); + continue; + } + + /* K_XLATE */ + if (c & RELKEY) /* key release is ignored */ + continue; + + /* process special keys; most of them are just ignored... */ + if (c & SPCLKEY) { + switch (KEYCHAR(c)) { + /* locking keys */ + case NLK: case CLK: case SLK: case ALK: + /* shift keys */ + case LSH: case RSH: case LCTR: case RCTR: + case LALT: case RALT: case ASH: case META: + /* other special keys */ + case NOP: case SPSC: case RBT: case SUSP: + case STBY: case DBG: case NEXT: + /* ignore them... */ + continue; + case BTAB: /* a backtab: ESC [ Z */ + putc(0x1b, &sc->gkb_q); + putc('[', &sc->gkb_q); + putc('Z', &sc->gkb_q); + continue; + } + } + + /* normal chars, normal chars with the META, function keys */ + switch (KEYFLAGS(c)) { + case 0: /* a normal char */ + putc(KEYCHAR(c), &sc->gkb_q); + break; + case MKEY: /* the META flag: prepend ESC */ + putc(0x1b, &sc->gkb_q); + putc(KEYCHAR(c), &sc->gkb_q); + break; + case FKEY | SPCLKEY: /* a function key, return string */ + cp = (*kbdsw[kbd->kb_index]->get_fkeystr)(kbd, + KEYCHAR(c), &len); + if (cp != NULL) { + while (len-- > 0) + putc(*cp++, &sc->gkb_q); + } + break; + } + } + + /* wake up sleeping/polling processes */ + if (sc->gkb_q.c_cc > 0) { + if (sc->gkb_flags & KB_ASLEEP) { + sc->gkb_flags &= ~KB_ASLEEP; + wakeup((caddr_t)sc); + } + selwakeup(&sc->gkb_rsel); + } + + return 0; +} + +#endif /* KBD_INSTALL_CDEV */ + +/* + * Generic low-level keyboard functions + * The low-level functions in the keyboard subdriver may use these + * functions. + */ + +int +genkbd_commonioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) +{ + keyarg_t *keyp; + fkeyarg_t *fkeyp; + int s; + int i; + + s = spltty(); + switch (cmd) { + + case KDGKBINFO: /* get keyboard information */ + ((keyboard_info_t *)arg)->kb_index = kbd->kb_index; + i = imin(strlen(kbd->kb_name) + 1, + sizeof(((keyboard_info_t *)arg)->kb_name)); + bcopy(kbd->kb_name, ((keyboard_info_t *)arg)->kb_name, i); + ((keyboard_info_t *)arg)->kb_unit = kbd->kb_unit; + ((keyboard_info_t *)arg)->kb_type = kbd->kb_type; + ((keyboard_info_t *)arg)->kb_config = kbd->kb_config; + ((keyboard_info_t *)arg)->kb_flags = kbd->kb_flags; + break; + + case KDGKBTYPE: /* get keyboard type */ + *(int *)arg = kbd->kb_type; + break; + + case GIO_KEYMAP: /* get keyboard translation table */ + bcopy(kbd->kb_keymap, arg, sizeof(*kbd->kb_keymap)); + break; + case PIO_KEYMAP: /* set keyboard translation table */ + bzero(kbd->kb_accentmap, sizeof(*kbd->kb_accentmap)); + bcopy(arg, kbd->kb_keymap, sizeof(*kbd->kb_keymap)); + break; + + case GIO_KEYMAPENT: /* get keyboard translation table entry */ + keyp = (keyarg_t *)arg; + if (keyp->keynum >= sizeof(kbd->kb_keymap->key) + /sizeof(kbd->kb_keymap->key[0])) { + splx(s); + return EINVAL; + } + bcopy(&kbd->kb_keymap[keyp->keynum], &keyp->key, + sizeof(keyp->key)); + break; + case PIO_KEYMAPENT: /* set keyboard translation table entry */ + keyp = (keyarg_t *)arg; + if (keyp->keynum >= sizeof(kbd->kb_keymap->key) + /sizeof(kbd->kb_keymap->key[0])) { + splx(s); + return EINVAL; + } + bcopy(&keyp->key, &kbd->kb_keymap[keyp->keynum], + sizeof(keyp->key)); + break; + + case GIO_DEADKEYMAP: /* get accent key translation table */ + bcopy(kbd->kb_accentmap, arg, sizeof(*kbd->kb_accentmap)); + break; + case PIO_DEADKEYMAP: /* set accent key translation table */ + bcopy(arg, kbd->kb_accentmap, sizeof(*kbd->kb_accentmap)); + break; + + case GETFKEY: /* get functionkey string */ + fkeyp = (fkeyarg_t *)arg; + if (fkeyp->keynum >= kbd->kb_fkeytab_size) { + splx(s); + return EINVAL; + } + bcopy(kbd->kb_fkeytab[fkeyp->keynum].str, fkeyp->keydef, + kbd->kb_fkeytab[fkeyp->keynum].len); + fkeyp->flen = kbd->kb_fkeytab[fkeyp->keynum].len; + break; + case SETFKEY: /* set functionkey string */ + fkeyp = (fkeyarg_t *)arg; + if (fkeyp->keynum >= kbd->kb_fkeytab_size) { + splx(s); + return EINVAL; + } + kbd->kb_fkeytab[fkeyp->keynum].len = imin(fkeyp->flen, MAXFK); + bcopy(fkeyp->keydef, kbd->kb_fkeytab[fkeyp->keynum].str, + kbd->kb_fkeytab[fkeyp->keynum].len); + break; + + default: + splx(s); + return ENOIOCTL; + } + + splx(s); + return 0; +} + +/* get a pointer to the string associated with the given function key */ +u_char +*genkbd_get_fkeystr(keyboard_t *kbd, int fkey, size_t *len) +{ + if (kbd == NULL) + return NULL; + fkey -= F_FN; + if (fkey > kbd->kb_fkeytab_size) + return NULL; + *len = kbd->kb_fkeytab[fkey].len; + return kbd->kb_fkeytab[fkey].str; +} + +/* diagnostic dump */ +static char +*get_kbd_type_name(int type) +{ + static struct { + int type; + char *name; + } name_table[] = { + { KB_84, "AT 84" }, + { KB_101, "AT 101/102" }, + { KB_OTHER, "generic" }, + }; + int i; + + for (i = 0; i < sizeof(name_table)/sizeof(name_table[0]); ++i) { + if (type == name_table[i].type) + return name_table[i].name; + } + return "unknown"; +} + +void +genkbd_diag(keyboard_t *kbd, int level) +{ + if (level > 0) { + printf("kbd%d: %s%d, %s (%d), config:0x%x, flags:0x%x", + kbd->kb_index, kbd->kb_name, kbd->kb_unit, + get_kbd_type_name(kbd->kb_type), kbd->kb_type, + kbd->kb_config, kbd->kb_flags); + if (kbd->kb_io_base > 0) + printf(", port:0x%x-0x%x", kbd->kb_io_base, + kbd->kb_io_base + kbd->kb_io_size - 1); + printf("\n"); + } +} + +#define set_lockkey_state(k, s, l) \ + if (!((s) & l ## DOWN)) { \ + int i; \ + (s) |= l ## DOWN; \ + (s) ^= l ## ED; \ + i = (s) & LOCK_MASK; \ + (*kbdsw[(k)->kb_index]->ioctl)((k), KDSETLED, (caddr_t)&i); \ + } + +static u_int +save_accent_key(keyboard_t *kbd, u_int key, int *accents) +{ + int i; + + /* make an index into the accent map */ + i = key - F_ACC + 1; + if ((i > kbd->kb_accentmap->n_accs) + || (kbd->kb_accentmap->acc[i - 1].accchar == 0)) { + /* the index is out of range or pointing to an empty entry */ + *accents = 0; + return ERRKEY; + } + + /* + * If the same accent key has been hit twice, produce the accent char + * itself. + */ + if (i == *accents) { + key = kbd->kb_accentmap->acc[i - 1].accchar; + *accents = 0; + return key; + } + + /* remember the index and wait for the next key */ + *accents = i; + return NOKEY; +} + +static u_int +make_accent_char(keyboard_t *kbd, u_int ch, int *accents) +{ + struct acc_t *acc; + int i; + + acc = &kbd->kb_accentmap->acc[*accents - 1]; + *accents = 0; + + /* + * If the accent key is followed by the space key, + * produce the accent char itself. + */ + if (ch == ' ') + return acc->accchar; + + /* scan the accent map */ + for (i = 0; i < NUM_ACCENTCHARS; ++i) { + if (acc->map[i][0] == 0) /* end of table */ + break; + if (acc->map[i][0] == ch) + return acc->map[i][1]; + } + /* this char cannot be accented... */ + return ERRKEY; +} + +int +genkbd_keyaction(keyboard_t *kbd, int keycode, int up, int *shiftstate, + int *accents) +{ + struct keyent_t *key; + int state = *shiftstate; + int action; + int f; + int i; + + f = state & (AGRS | ALKED); + if ((f == AGRS1) || (f == AGRS2) || (f == ALKED)) + keycode += ALTGR_OFFSET; + key = &kbd->kb_keymap->key[keycode]; + i = ((state & SHIFTS) ? 1 : 0) + | ((state & CTLS) ? 2 : 0) + | ((state & ALTS) ? 4 : 0); + if (((key->flgs & FLAG_LOCK_C) && (state & CLKED)) + || ((key->flgs & FLAG_LOCK_N) && (state & NLKED)) ) + i ^= 1; + + action = key->map[i]; + if (up) { /* break: key released */ + if (key->spcl & (0x80 >> i)) { + /* special keys */ + switch (action) { + case LSH: + state &= ~SHIFTS1; + break; + case RSH: + state &= ~SHIFTS2; + break; + case LCTR: + state &= ~CTLS1; + break; + case RCTR: + state &= ~CTLS2; + break; + case LALT: + state &= ~ALTS1; + break; + case RALT: + state &= ~ALTS2; + break; + case ASH: + state &= ~AGRS1; + break; + case META: + state &= ~METAS1; + break; + case NLK: + state &= ~NLKDOWN; + break; + case CLK: +#ifndef PC98 + state &= ~CLKDOWN; +#else + state &= ~CLKED; + i = state & LOCK_MASK; + (*kbdsw[kbd->kb_index]->ioctl)(kbd, KDSETLED, + (caddr_t)&i); +#endif + break; + case SLK: + state &= ~SLKDOWN; + break; + case ALK: + state &= ~ALKDOWN; + break; + } + *shiftstate = state; + return (SPCLKEY | RELKEY | action); + } + /* release events of regular keys are not reported */ + return NOKEY; + } else { /* make: key pressed */ + if (key->spcl & (0x80 >> i)) { + /* special keys */ + switch (action) { + /* LOCKING KEYS */ + case NLK: + set_lockkey_state(kbd, state, NLK); + break; + case CLK: +#ifndef PC98 + set_lockkey_state(kbd, state, CLK); +#else + state |= CLKED; + i = state & LOCK_MASK; + (*kbdsw[kbd->kb_index]->ioctl)(kbd, KDSETLED, + (caddr_t)&i); +#endif + break; + case SLK: + set_lockkey_state(kbd, state, SLK); + break; + case ALK: + set_lockkey_state(kbd, state, ALK); + break; + /* NON-LOCKING KEYS */ + case SPSC: case RBT: case SUSP: case STBY: + case DBG: case NEXT: + *accents = 0; + break; + case BTAB: + *accents = 0; + action |= BKEY; + break; + case LSH: + state |= SHIFTS1; + break; + case RSH: + state |= SHIFTS2; + break; + case LCTR: + state |= CTLS1; + break; + case RCTR: + state |= CTLS2; + break; + case LALT: + state |= ALTS1; + break; + case RALT: + state |= ALTS2; + break; + case ASH: + state |= AGRS1; + break; + case META: + state |= METAS1; + break; + default: + /* is this an accent (dead) key? */ + if (action >= F_ACC && action <= L_ACC) { + action = save_accent_key(kbd, action, + accents); + switch (action) { + case NOKEY: + case ERRKEY: + return action; + default: + if (state & METAS) + return (action | MKEY); + else + return action; + } + /* NOT REACHED */ + } + /* other special keys */ + if (*accents > 0) { + *accents = 0; + return ERRKEY; + } + if (action >= F_FN && action <= L_FN) + action |= FKEY; + /* XXX: return fkey string for the FKEY? */ + } + *shiftstate = state; + return (SPCLKEY | action); + } else { + /* regular keys */ + if (*accents > 0) { + /* make an accented char */ + action = make_accent_char(kbd, action, accents); + if (action == ERRKEY) + return action; + } + if (state & METAS) + action |= MKEY; + return action; + } + } + /* NOT REACHED */ +} diff --git a/sys/dev/kbd/kbdreg.h b/sys/dev/kbd/kbdreg.h new file mode 100644 index 0000000..f1a6c8b --- /dev/null +++ b/sys/dev/kbd/kbdreg.h @@ -0,0 +1,270 @@ +/*- + * 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: $ + */ + +#ifndef _DEV_KBD_KBDREG_H_ +#define _DEV_KBD_KBDREG_H_ + +/* forward declarations */ +typedef struct keyboard keyboard_t; +struct keymap; +struct accentmap; +struct fkeytab; + +/* call back funcion */ +typedef int kbd_callback_func_t(keyboard_t *kbd, int event, + void *arg); +/* event types */ +#define KBDIO_KEYINPUT 0 +#define KBDIO_UNLOADING 1 + +typedef struct keyboard_callback { + kbd_callback_func_t *kc_func; + void *kc_arg; +} keyboard_callback_t; + +/* keyboard */ +struct keyboard { + /* the following fields are managed by kbdio */ + int kb_index; /* kbdio index# */ + int kb_minor; /* minor number of the sub-device */ + int kb_flags; /* internal flags */ +#define KB_VALID (1 << 16) /* this entry is valid */ +#define KB_NO_DEVICE (1 << 17) /* device not present */ +#define KB_PROBED (1 << 18) /* device probed */ +#define KB_INITIALIZED (1 << 19) /* device initialized */ +#define KB_REGISTERED (1 << 20) /* device registered to kbdio */ +#define KB_BUSY (1 << 21) /* device used by a client */ + int kb_active; /* 0: inactive */ + void *kb_token; /* id of the current client */ + keyboard_callback_t kb_callback;/* callback function */ + + /* + * Device configuration flags: + * The upper 16 bits are common between various keyboard devices. + * The lower 16 bits are device-specific. + */ + int kb_config; +#define KB_CONF_PROBE_ONLY (1 << 16) /* probe only, don't initialize */ + + /* the following fields are set up by the driver */ + char *kb_name; /* driver name */ + int kb_unit; /* unit # */ + int kb_type; /* KB_84, KB_101, KB_OTHER,... */ + int kb_io_base; /* port# if any */ + int kb_io_size; /* # of occupied port */ + int kb_led; /* LED status */ + struct keymap *kb_keymap; /* key map */ + struct accentmap *kb_accentmap; /* accent map */ + struct fkeytab *kb_fkeytab; /* function key strings */ + int kb_fkeytab_size;/* # of function key strings */ + void *kb_data; /* the driver's private data */ +}; + +#define KBD_IS_VALID(k) ((k)->kb_flags & KB_VALID) +#define KBD_VALID(k) ((k)->kb_flags |= KB_VALID) +#define KBD_INVALID(k) ((k)->kb_flags &= ~KB_VALID) +#define KBD_HAS_DEVICE(k) (!((k)->kb_flags & KB_NO_DEVICE)) +#define KBD_FOUND_DEVICE(k) ((k)->kb_flags &= ~KB_NO_DEVICE) +#define KBD_IS_PROBED(k) ((k)->kb_flags & KB_PROBED) +#define KBD_PROBE_DONE(k) ((k)->kb_flags |= KB_PROBED) +#define KBD_IS_INITIALIZED(k) ((k)->kb_flags & KB_INITIALIZED) +#define KBD_INIT_DONE(k) ((k)->kb_flags |= KB_INITIALIZED) +#define KBD_IS_CONFIGURED(k) ((k)->kb_flags & KB_REGISTERED) +#define KBD_CONFIG_DONE(k) ((k)->kb_flags |= KB_REGISTERED) +#define KBD_IS_BUSY(k) ((k)->kb_flags & KB_BUSY) +#define KBD_BUSY(k) ((k)->kb_flags |= KB_BUSY) +#define KBD_UNBUSY(k) ((k)->kb_flags &= ~KB_BUSY) +#define KBD_IS_ACTIVE(k) ((k)->kb_active) +#define KBD_ACTIVATE(k) (++(k)->kb_active) +#define KBD_DEACTIVATE(k) (--(k)->kb_active) +#define KBD_LED_VAL(k) ((k)->kb_led) + +/* keyboard function table */ +typedef int kbd_probe_t(int unit, keyboard_t **kbdp, void *arg, + int flags); +typedef int kbd_init_t(keyboard_t *kbd); +typedef int kbd_term_t(keyboard_t *kbd); +typedef int kbd_intr_t(keyboard_t *kbd, void *arg); +typedef int kbd_test_if_t(keyboard_t *kbd); +typedef int kbd_enable_t(keyboard_t *kbd); +typedef int kbd_disable_t(keyboard_t *kbd); +typedef int kbd_read_t(keyboard_t *kbd, int wait); +typedef int kbd_check_t(keyboard_t *kbd); +typedef u_int kbd_read_char_t(keyboard_t *kbd, int wait); +typedef int kbd_check_char_t(keyboard_t *kbd); +typedef int kbd_ioctl_t(keyboard_t *kbd, u_long cmd, caddr_t data); +typedef int kbd_lock_t(keyboard_t *kbd, int lock); +typedef void kbd_clear_state_t(keyboard_t *kbd); +typedef int kbd_get_state_t(keyboard_t *kbd, void *buf, size_t len); +typedef int kbd_set_state_t(keyboard_t *kbd, void *buf, size_t len); +typedef u_char *kbd_get_fkeystr_t(keyboard_t *kbd, int fkey, + size_t *len); +typedef void kbd_diag_t(keyboard_t *kbd, int level); + +typedef struct keyboard_switch { + kbd_probe_t *probe; + kbd_init_t *init; + kbd_term_t *term; + kbd_intr_t *intr; + kbd_test_if_t *test_if; + kbd_enable_t *enable; + kbd_disable_t *disable; + kbd_read_t *read; + kbd_check_t *check; + kbd_read_char_t *read_char; + kbd_check_char_t *check_char; + kbd_ioctl_t *ioctl; + kbd_lock_t *lock; + kbd_clear_state_t *clear_state; + kbd_get_state_t *get_state; + kbd_set_state_t *set_state; + kbd_get_fkeystr_t *get_fkeystr; + kbd_diag_t *diag; +} keyboard_switch_t; + +/* keyboard driver */ +typedef struct keyboard_driver { + char *name; + keyboard_switch_t *kbdsw; + int (*configure)(int); /* backdoor for the console driver */ +} keyboard_driver_t; + +#ifdef KERNEL + +#define KEYBOARD_DRIVER(name, sw, config) \ + static struct keyboard_driver name##_driver = { \ + #name, &sw, config \ + }; \ + DATA_SET(kbddriver_set, name##_driver); + +/* global variables */ +extern keyboard_switch_t **kbdsw; +extern struct linker_set kbddriver_set; + +/* functions for the keyboard driver */ +int kbd_register(keyboard_t *kbd); +int kbd_unregister(keyboard_t *kbd); +keyboard_switch_t *kbd_get_switch(char *driver); +void kbd_init_struct(keyboard_t *kbd, char *name, int type, + int unit, int config, int port, + int port_size); +void kbd_set_maps(keyboard_t *kbd, struct keymap *keymap, + struct accentmap *accmap, + struct fkeytab *fkeymap, int fkeymap_size); + +/* functions for the keyboard client */ +int kbd_allocate(char *driver, int unit, void *id, + kbd_callback_func_t *func, void *arg); +int kbd_release(keyboard_t *kbd, void *id); +int kbd_change_callback(keyboard_t *kbd, void *id, + kbd_callback_func_t *func, void *arg); +int kbd_find_keyboard(char *driver, int unit); +keyboard_t *kbd_get_keyboard(int index); + +/* a back door for the console driver to tickle the keyboard driver XXX */ +int kbd_configure(int flags); + /* see `kb_config' above for flag bit definitions */ + +#ifdef KBD_INSTALL_CDEV + +/* virtual keyboard cdev driver functions */ + +int kbd_attach(dev_t dev, keyboard_t *kbd, + struct cdevsw *sw); +int kbd_detach(dev_t dev, keyboard_t *kbd, + struct cdevsw *sw); + +/* generic keyboard cdev driver functions */ + +typedef struct genkbd_softc { + int gkb_flags; /* flag/status bits */ +#define KB_ASLEEP (1 << 0) + struct clist gkb_q; /* input queue */ + struct selinfo gkb_rsel; +} genkbd_softc_t; + +int genkbdopen(genkbd_softc_t *sc, keyboard_t *kbd, int flag, int mode, + struct proc *p); +int genkbdclose(genkbd_softc_t *sc, keyboard_t *kbd, int flag, int mode, + struct proc *p); +int genkbdread(genkbd_softc_t *sc, keyboard_t *kbd, struct uio *uio, + int flag); +int genkbdwrite(genkbd_softc_t *sc, keyboard_t *kbd, struct uio *uio, + int flag); +int genkbdioctl(genkbd_softc_t *sc, keyboard_t *kbd, u_long cmd, + caddr_t arg, int flag, struct proc *p); +int genkbdpoll(genkbd_softc_t *sc, keyboard_t *kbd, int event, + struct proc *p); + +#endif /* KBD_INSTALL_CDEV */ + +/* generic low-level keyboard functions */ + +/* shift key state */ +#define SHIFTS1 (1 << 16) +#define SHIFTS2 (1 << 17) +#define SHIFTS (SHIFTS1 | SHIFTS2) +#define CTLS1 (1 << 18) +#define CTLS2 (1 << 19) +#define CTLS (CTLS1 | CTLS2) +#define ALTS1 (1 << 20) +#define ALTS2 (1 << 21) +#define ALTS (ALTS1 | ALTS2) +#define AGRS1 (1 << 22) +#define AGRS2 (1 << 23) +#define AGRS (AGRS1 | AGRS2) +#define METAS1 (1 << 24) +#define METAS2 (1 << 25) +#define METAS (METAS1 | METAS2) +#define NLKDOWN (1 << 26) +#define SLKDOWN (1 << 27) +#define CLKDOWN (1 << 28) +#define ALKDOWN (1 << 29) +/* lock key state (defined in machine/console.h) */ +/* +#define CLKED LED_CAP +#define NLKED LED_NUM +#define SLKED LED_SCR +#define ALKED (1 << 3) +#define LOCK_MASK (CLKED | NLKED | SLKED | ALKED) +#define LED_CAP (1 << 0) +#define LED_NUM (1 << 1) +#define LED_SCR (1 << 2) +#define LED_MASK (LED_CAP | LED_NUM | LED_SCR) +*/ + +kbd_get_fkeystr_t genkbd_get_fkeystr; +kbd_diag_t genkbd_diag; + +int genkbd_commonioctl(keyboard_t *kbd, u_long cmd, caddr_t arg); +int genkbd_keyaction(keyboard_t *kbd, int keycode, int down, + int *shiftstate, int *accents); + +#endif /* KERNEL */ + +#endif /* !_DEV_KBD_KBDREG_H_ */ |