diff --git a/sys/dev/atkbdc/atkbd.c b/sys/dev/atkbdc/atkbd.c new file mode 100644 index 000000000000..21ed5692cef1 --- /dev/null +++ b/sys/dev/atkbdc/atkbd.c @@ -0,0 +1,1302 @@ +/*- + * Copyright (c) 1999 Kazutaka YOKOTA + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifndef __i386__ + +#define ATKBD_SOFTC(unit) \ + ((atkbd_softc_t *)devclass_get_softc(atkbd_devclass, unit)) + +#else /* __i386__ */ + +#include +#include + +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 +#include +#include + +#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 + +/* 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/atkbdc/atkbdc.c b/sys/dev/atkbdc/atkbdc.c new file mode 100644 index 000000000000..0f61835f0763 --- /dev/null +++ b/sys/dev/atkbdc/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 +#include +#include +#include +#include + +#include + +#include + +#ifndef __i386__ +#include +#else +#include +#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/atkbdc/atkbdcreg.h b/sys/dev/atkbdc/atkbdcreg.h new file mode 100644 index 000000000000..81b0ad35dce9 --- /dev/null +++ b/sys/dev/atkbdc/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/atkbdc/atkbdreg.h b/sys/dev/atkbdc/atkbdreg.h new file mode 100644 index 000000000000..9f20eeea5f3d --- /dev/null +++ b/sys/dev/atkbdc/atkbdreg.h @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 1999 Kazutaka YOKOTA + * 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/fb/fb.c b/sys/dev/fb/fb.c new file mode 100644 index 000000000000..e765bc2ab93a --- /dev/null +++ b/sys/dev/fb/fb.c @@ -0,0 +1,415 @@ +/*- + * Copyright (c) 1999 Kazutaka YOKOTA + * 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. + * 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 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 "fb.h" +#include "opt_fb.h" + +#include +#include +#include +#include +#include + +#include + +#include + +/* local arrays */ + +/* + * We need at least one entry each in order to initialize a video card + * for the kernel console. The arrays will be increased dynamically + * when necessary. + */ +static video_adapter_t *adp_ini; +static video_switch_t *vidsw_ini; +static struct cdevsw *vidcdevsw_ini; + +static video_adapter_t **adapter = &adp_ini; +static int adapters = 1; + video_switch_t **vidsw = &vidsw_ini; +static struct cdevsw **vidcdevsw = &vidcdevsw_ini; + +#define ARRAY_DELTA 4 + +static void +vid_realloc_array(void) +{ + video_adapter_t **new_adp; + video_switch_t **new_vidsw; + struct cdevsw **new_cdevsw; + int newsize; + int s; + + s = spltty(); + newsize = ((adapters + ARRAY_DELTA)/ARRAY_DELTA)*ARRAY_DELTA; + new_adp = malloc(sizeof(*new_adp)*newsize, M_DEVBUF, M_WAITOK); + new_vidsw = malloc(sizeof(*new_vidsw)*newsize, M_DEVBUF, M_WAITOK); + new_cdevsw = malloc(sizeof(*new_cdevsw)*newsize, M_DEVBUF, M_WAITOK); + bzero(new_adp, sizeof(*new_adp)*newsize); + bzero(new_vidsw, sizeof(*new_vidsw)*newsize); + bzero(new_cdevsw, sizeof(*new_cdevsw)*newsize); + bcopy(adapter, new_adp, sizeof(*adapter)*adapters); + bcopy(vidsw, new_vidsw, sizeof(*vidsw)*adapters); + bcopy(vidcdevsw, new_cdevsw, sizeof(*vidcdevsw)*adapters); + if (adapters > 1) { + free(adapter, M_DEVBUF); + free(vidsw, M_DEVBUF); + free(vidcdevsw, M_DEVBUF); + } + adapter = new_adp; + vidsw = new_vidsw; + vidcdevsw = new_cdevsw; + adapters = newsize; + splx(s); + + if (bootverbose) + printf("fb: new array size %d\n", adapters); +} + +/* + * Low-level frame buffer driver functions + * frame buffer subdrivers, such as the VGA driver, call these functions + * to initialize the video_adapter structure and register it to the virtual + * frame buffer driver `fb'. + */ + +/* initialize the video_adapter_t structure */ +void +vid_init_struct(video_adapter_t *adp, char *name, int type, int unit) +{ + adp->va_flags = 0; + adp->va_name = name; + adp->va_type = type; + adp->va_unit = unit; +} + +/* Register a video adapter */ +int +vid_register(video_adapter_t *adp) +{ + video_driver_t **list; + video_driver_t *p; + int index; + + for (index = 0; index < adapters; ++index) { + if (adapter[index] == NULL) + break; + } + if (index >= adapters) + return -1; + + adp->va_index = index; + adp->va_token = NULL; + list = (video_driver_t **)videodriver_set.ls_items; + while ((p = *list++) != NULL) { + if (strcmp(p->name, adp->va_name) == 0) { + adapter[index] = adp; + vidsw[index] = p->vidsw; + return index; + } + } + + return -1; +} + +int +vid_unregister(video_adapter_t *adp) +{ + if ((adp->va_index < 0) || (adp->va_index >= adapters)) + return ENOENT; + if (adapter[adp->va_index] != adp) + return ENOENT; + + adapter[adp->va_index] = NULL; + vidsw[adp->va_index] = NULL; + return 0; +} + +/* Get video I/O function table */ +video_switch_t +*vid_get_switch(char *name) +{ + video_driver_t **list; + video_driver_t *p; + + list = (video_driver_t **)videodriver_set.ls_items; + while ((p = *list++) != NULL) { + if (strcmp(p->name, name) == 0) + return p->vidsw; + } + + return NULL; +} + +/* + * Video card client functions + * Video card clients, such as the console driver `syscons' and the frame + * buffer cdev driver, use these functions to claim and release a card for + * exclusive use. + */ + +/* find the video card specified by a driver name and a unit number */ +int +vid_find_adapter(char *driver, int unit) +{ + int i; + + for (i = 0; i < adapters; ++i) { + if (adapter[i] == NULL) + continue; + if (strcmp("*", driver) && strcmp(adapter[i]->va_name, driver)) + continue; + if ((unit != -1) && (adapter[i]->va_unit != unit)) + continue; + return i; + } + return -1; +} + +/* allocate a video card */ +int +vid_allocate(char *driver, int unit, void *id) +{ + int index; + int s; + + s = spltty(); + index = vid_find_adapter(driver, unit); + if (index >= 0) { + if (adapter[index]->va_token) { + splx(s); + return -1; + } + adapter[index]->va_token = id; + } + splx(s); + return index; +} + +int +vid_release(video_adapter_t *adp, void *id) +{ + int error; + int s; + + s = spltty(); + if (adp->va_token == NULL) { + error = EINVAL; + } else if (adp->va_token != id) { + error = EPERM; + } else { + adp->va_token = NULL; + error = 0; + } + splx(s); + return error; +} + +/* Get a video adapter structure */ +video_adapter_t +*vid_get_adapter(int index) +{ + if ((index < 0) || (index >= adapters)) + return NULL; + return adapter[index]; +} + +/* Configure drivers: this is a backdoor for the console driver XXX */ +int +vid_configure(int flags) +{ + video_driver_t **list; + video_driver_t *p; + + list = (video_driver_t **)videodriver_set.ls_items; + while ((p = *list++) != NULL) { + if (p->configure != NULL) + (*p->configure)(flags); + } + + return 0; +} + +/* + * Virtual frame buffer cdev driver functions + * The virtual frame buffer driver dispatches driver functions to + * appropriate subdrivers. + */ + +#define DRIVER_NAME "fb" + +#ifdef FB_INSTALL_CDEV + +#define FB_UNIT(dev) minor(dev) +#define FB_MKMINOR(unit) (u) + +#if notyet + +static d_open_t fbopen; +static d_close_t fbclose; +static d_ioctl_t fbioctl; +static d_mmap_t fbmmap; + +#define CDEV_MAJOR 141 /* XXX */ + +static struct cdevsw fb_cdevsw = { + fbopen, fbclose, noread, nowrite, /* ??? */ + fbioctl, nostop, nullreset, nodevtotty, + seltrue, fbmmap, NULL, DRIVER_NAME, + NULL, -1, nodump, nopsize, +}; + +static void +vfbattach(void *arg) +{ + static int fb_devsw_installed = FALSE; + dev_t dev; + + if (!fb_devsw_installed) { + dev = makedev(CDEV_MAJOR, 0); + cdevsw_add(&dev, &fb_cdevsw, NULL); + fb_devsw_installed = TRUE; + } +} + +PSEUDO_SET(vfbattach, fb); + +#endif /* notyet */ + +int +fb_attach(dev_t dev, video_adapter *adp, struct cdevsw *cdevsw) +{ + int s; + + if (adp->va_index >= adapters) + return EINVAL; + if (adapter[adp->va_index] != adp) + return EINVAL; + + s = spltty(); + adp->va_minor = minor(dev); + vidcdevsw[adp->va_index] = cdevsw; + splx(s); + + /* XXX: DEVFS? */ + + if (adp->va_index + 1 >= adapters) + vid_realloc_array(); + + printf("fb%d at %s%d\n", adp->va_index, adp->va_name, adp->va_unit); + return 0; +} + +int +fb_detach(dev_t dev, video_adapter *adp, struct cdevsw *cdevsw) +{ + int s; + + if (adp->va_index >= adapters) + return EINVAL; + if (adapter[adp->va_index] != adp) + return EINVAL; + if (vidcdevsw[adp->va_index] != cdevsw) + return EINVAL; + + s = spltty(); + vidcdevsw[adp->va_index] = NULL; + splx(s); + return 0; +} + +#endif /* FB_INSTALL_CDEV */ + +static char +*adapter_name(int type) +{ + static struct { + int type; + char *name; + } names[] = { + { KD_MONO, "MDA" }, + { KD_HERCULES, "Hercules" }, + { KD_CGA, "CGA" }, + { KD_EGA, "EGA" }, + { KD_VGA, "VGA" }, + { KD_PC98, "PC-98x1" }, + { -1, "Unknown" }, + }; + int i; + + for (i = 0; names[i].type != -1; ++i) + if (names[i].type == type) + break; + return names[i].name; +} + +void +fb_dump_adp_info(char *driver, video_adapter_t *adp, int level) +{ + if (level <= 0) + return; + + printf("%s%d: %s%d, %s, type:%s (%d), flags:0x%x\n", + DRIVER_NAME, adp->va_index, driver, adp->va_unit, adp->va_name, + adapter_name(adp->va_type), adp->va_type, adp->va_flags); + printf("%s%d: port:0x%x-0x%x, crtc:0x%x, mem:0x%x 0x%x\n", + DRIVER_NAME, adp->va_index, + adp->va_io_base, adp->va_io_base + adp->va_io_size - 1, + adp->va_crtc_addr, adp->va_mem_base, adp->va_mem_size); + printf("%s%d: init mode:%d, bios mode:%d, current mode:%d\n", + DRIVER_NAME, adp->va_index, + adp->va_initial_mode, adp->va_initial_bios_mode, adp->va_mode); + printf("%s%d: window:0x%x size:%dk gran:%dk, buf:0x%x size:%dk\n", + DRIVER_NAME, adp->va_index, + adp->va_window, adp->va_window_size/1024, adp->va_window_gran/1024, + adp->va_buffer, adp->va_buffer_size/1024); +} + +void +fb_dump_mode_info(char *driver, video_adapter_t *adp, video_info_t *info, + int level) +{ + if (level <= 0) + return; + + printf("%s%d: %s, mode:%d, flags:0x%x ", + driver, adp->va_unit, adp->va_name, info->vi_mode, info->vi_flags); + if (info->vi_flags & V_INFO_GRAPHICS) + printf("G %dx%dx%d, %d plane(s), font:%dx%d, ", + info->vi_width, info->vi_height, + info->vi_depth, info->vi_planes, + info->vi_cwidth, info->vi_cheight); + else + printf("T %dx%d, font:%dx%d, ", + info->vi_width, info->vi_height, + info->vi_cwidth, info->vi_cheight); + printf("win:0x%x\n", info->vi_window); +} diff --git a/sys/dev/fb/fbreg.h b/sys/dev/fb/fbreg.h new file mode 100644 index 000000000000..ee5b1f2f2fee --- /dev/null +++ b/sys/dev/fb/fbreg.h @@ -0,0 +1,154 @@ +/*- + * Copyright (c) 1999 Kazutaka YOKOTA + * 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: $ + */ + +#ifndef _DEV_FB_FBREG_H_ +#define _DEV_FB_FBREG_H_ + +#ifdef KERNEL + +#define V_MAX_ADAPTERS 8 /* XXX */ + +/* some macros */ +#ifdef __i386__ +#define bcopy_toio(s, d, c) generic_bcopy(s, d, c) +#define bcopy_fromio(s, d, c) generic_bcopy(s, d, c) +#define bzero_io(d, c) generic_bzero(d, c) +void generic_bcopy(const void *s, void *d, size_t c); +void generic_bzero(void *d, size_t c); +#else /* !__i386__ */ +#define bcopy_toio(s, d, c) memcpy_toio(d, s, c) +#define bcopy_fromio(s, d, c) memcpy_fromio(d, s, c) +#define bzero_io(d, c) memset_io(d, 0, c) +#endif /* !__i386__ */ + +/* video function table */ +typedef int vi_probe_t(int unit, video_adapter_t **adpp, void *arg, int flags); +typedef int vi_init_t(int unit, video_adapter_t *adp, int flags); +typedef int vi_get_info_t(video_adapter_t *adp, int mode, video_info_t *info); +typedef int vi_query_mode_t(video_adapter_t *adp, video_info_t *info); +typedef int vi_set_mode_t(video_adapter_t *adp, int mode); +typedef int vi_save_font_t(video_adapter_t *adp, int page, int size, + u_char *data, int c, int count); +typedef int vi_load_font_t(video_adapter_t *adp, int page, int size, + u_char *data, int c, int count); +typedef int vi_show_font_t(video_adapter_t *adp, int page); +typedef int vi_save_palette_t(video_adapter_t *adp, u_char *palette); +typedef int vi_load_palette_t(video_adapter_t *adp, u_char *palette); +typedef int vi_set_border_t(video_adapter_t *adp, int border); +typedef int vi_save_state_t(video_adapter_t *adp, void *p, size_t size); +typedef int vi_load_state_t(video_adapter_t *adp, void *p); +typedef int vi_set_win_org_t(video_adapter_t *adp, off_t offset); +typedef int vi_read_hw_cursor_t(video_adapter_t *adp, int *col, int *row); +typedef int vi_set_hw_cursor_t(video_adapter_t *adp, int col, int row); +typedef int vi_set_hw_cursor_shape_t(video_adapter_t *adp, int base, + int height, int celsize, int blink); +typedef int vi_blank_display_t(video_adapter_t *adp, int mode); +#define V_DISPLAY_POWER_ON 0 +#define V_DISPLAY_SUSPEND 1 +#define V_DISPLAY_SUSPEND1 1 +#define V_DISPLAY_SUSPEND2 2 +#define V_DISPLAY_POWER_OFF 3 +typedef int vi_mmap_t(video_adapter_t *adp, vm_offset_t offset); +typedef int vi_diag_t(video_adapter_t *adp, int level); + +typedef struct video_switch { + vi_probe_t *probe; + vi_init_t *init; + vi_get_info_t *get_info; + vi_query_mode_t *query_mode; + vi_set_mode_t *set_mode; + vi_save_font_t *save_font; + vi_load_font_t *load_font; + vi_show_font_t *show_font; + vi_save_palette_t *save_palette; + vi_load_palette_t *load_palette; + vi_set_border_t *set_border; + vi_save_state_t *save_state; + vi_load_state_t *load_state; + vi_set_win_org_t *set_win_org; + vi_read_hw_cursor_t *read_hw_cursor; + vi_set_hw_cursor_t *set_hw_cursor; + vi_set_hw_cursor_shape_t *set_hw_cursor_shape; + vi_blank_display_t *blank_display; + vi_mmap_t *mmap; + vi_diag_t *diag; +} video_switch_t; + +/* video driver */ +typedef struct video_driver { + char *name; + video_switch_t *vidsw; + int (*configure)(int); /* backdoor for the console driver */ +} video_driver_t; + +#define VIDEO_DRIVER(name, sw, config) \ + static struct video_driver name##_driver = { \ + #name, &sw, config \ + }; \ + DATA_SET(videodriver_set, name##_driver); + +/* global variables */ +extern struct video_switch **vidsw; +extern struct linker_set videodriver_set; + +/* functions for the video card driver */ +int vid_register(video_adapter_t *adp); +int vid_unregister(video_adapter_t *adp); +video_switch_t *vid_get_switch(char *name); +void vid_init_struct(video_adapter_t *adp, char *name, int type, + int unit); + +/* functions for the video card client */ +int vid_allocate(char *driver, int unit, void *id); +int vid_release(video_adapter_t *adp, void *id); +int vid_find_adapter(char *driver, int unit); +video_adapter_t *vid_get_adapter(int index); + +/* a backdoor for the console driver to tickle the video driver XXX */ +int vid_configure(int flags); +#define VIO_PROBE_ONLY (1 << 0) /* probe only, don't initialize */ + +#ifdef FB_INSTALL_CDEV + +/* virtual frame buffer driver functions */ +int fb_attach(dev_t dev, video_adapter_t *adp, + struct cdevsw *cdevsw); +int fb_detach(dev_t dev, video_adapter_t *adp, + struct cdevsw *cdevsw); + +#endif /* FB_INSTALL_CDEV */ + +/* generic low-level driver functions */ + +void fb_dump_adp_info(char *driver, video_adapter_t *adp, int level); +void fb_dump_mode_info(char *driver, video_adapter_t *adp, + video_info_t *info, int level); + +#endif /* KERNEL */ + +#endif /* !_DEV_FB_FBREG_H_ */ diff --git a/sys/dev/fb/splash.c b/sys/dev/fb/splash.c new file mode 100644 index 000000000000..7d0b90ea46b8 --- /dev/null +++ b/sys/dev/fb/splash.c @@ -0,0 +1,181 @@ +/*- + * $Id:$ + */ + +#include "splash.h" + +#if NSPLASH > 0 + +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* video adapter and image decoder */ +static video_adapter_t *splash_adp; +static splash_decoder_t *splash_decoder; + +/* decoder candidates */ +static int decoders; +static splash_decoder_t **decoder_set; +#define DECODER_ARRAY_DELTA 4 + +/* splash image data file */ +static void *splash_image_data; +static size_t splash_image_size; + +/* console driver callback */ +static int (*splash_callback)(int); + +static int +splash_find_image(void) +{ + caddr_t image_module; + caddr_t p; + + if (splash_image_data == NULL) { + image_module = preload_search_by_type(SPLASH_IMAGE); + if (image_module == NULL) + return ENOENT; + p = preload_search_info(image_module, MODINFO_ADDR); + if (p == NULL) + return ENOENT; + splash_image_data = *(void **)p; + p = preload_search_info(image_module, MODINFO_SIZE); + if (p == NULL) + return ENOENT; + splash_image_size = *(size_t *)p; + } + return 0; +} + +static int +splash_test(splash_decoder_t *decoder) +{ + if ((*decoder->init)(splash_adp, splash_image_data, splash_image_size)) + return ENODEV; /* XXX */ + if (bootverbose) + printf("splash: image decoder found: %s\n", decoder->name); + splash_decoder = decoder; + if (splash_callback != NULL) + (*splash_callback)(SPLASH_INIT); + return 0; +} + +int +splash_register(splash_decoder_t *decoder) +{ + splash_decoder_t **p; + int i; + + /* only one decoder can be active */ + if (splash_decoder != NULL) + return ENODEV; /* XXX */ + + /* if the splash image is not in memory, abort */ + splash_find_image(); + if (bootverbose) + printf("splash: image@%p, size:%u\n", + splash_image_data, splash_image_size); + if (splash_image_data == NULL) + return ENOENT; + + /* + * If the video card has aleady been initialized, test this + * decoder immediately. + */ + if (splash_adp != NULL) + return splash_test(decoder); + + /* register the decoder for later use */ + for (i = 0; i < decoders; ++i) { + if (decoder_set[i] == NULL) + break; + } + if ((i >= decoders) && (decoders % DECODER_ARRAY_DELTA) == 0) { + p = malloc(sizeof(*p)*(decoders + DECODER_ARRAY_DELTA), + M_DEVBUF, M_NOWAIT); + if (p == NULL) + return ENOMEM; + if (decoder_set != NULL) + bcopy(decoder_set, p, sizeof(*p)*decoders); + free(decoder_set, M_DEVBUF); + decoder_set = p; + i = decoders++; + } + decoder_set[i] = decoder; + return 0; +} + +int +splash_unregister(splash_decoder_t *decoder) +{ + int error; + int i; + + if (splash_decoder == decoder) { + if ((error = splash_term(splash_adp)) != 0) + return error; + } + for (i = 0; i < decoders; ++i) { + if (decoder_set[i] == decoder) { + decoder_set[i] = NULL; + break; + } + } + return 0; +} + +int +splash_init(video_adapter_t *adp, int (*callback)(int)) +{ + int i; + + splash_adp = adp; + splash_callback = callback; + + /* try registered decoders with this adapter and loaded image */ + splash_decoder = NULL; + splash_find_image(); + if (splash_image_data == NULL) + return 0; + for (i = 0; i < decoders; ++i) { + if (decoder_set[i] == NULL) + continue; + if (splash_test(decoder_set[i]) == 0) + break; + } + return 0; +} + +int +splash_term(video_adapter_t *adp) +{ + int error = 0; + + if (splash_decoder != NULL) { + if (splash_callback != NULL) + error = (*splash_callback)(SPLASH_TERM); + if (error == 0) + error = (*splash_decoder->term)(adp); + if (error == 0) + splash_decoder = NULL; + } + return error; +} + +int +splash(video_adapter_t *adp, int on) +{ + if (splash_decoder != NULL) + return (*splash_decoder->splash)(adp, on); + return ENODEV; +} + +#endif /* NSPLASH > 0 */ diff --git a/sys/dev/fb/splashreg.h b/sys/dev/fb/splashreg.h new file mode 100644 index 000000000000..83496fa1f597 --- /dev/null +++ b/sys/dev/fb/splashreg.h @@ -0,0 +1,51 @@ +/*- + * $Id: $ + */ + +#ifndef _DEV_FB_SPLASHREG_H_ +#define _DEV_FB_SPLASHREG_H_ + +#define SPLASH_IMAGE "splash_image_data" + +struct video_adapter; + +typedef struct splash_decoder { + char *name; + int (*init)(struct video_adapter *adp, void *data, + size_t size); + int (*term)(struct video_adapter *adp); + int (*splash)(struct video_adapter *adp, int on); +} splash_decoder_t; + +#define SPLASH_DECODER(name, sw) \ + static int name##_modevent(module_t mod, int type, void *data) \ + { \ + switch ((modeventtype_t)type) { \ + case MOD_LOAD: \ + return splash_register(&sw); \ + case MOD_UNLOAD: \ + return splash_unregister(&sw); \ + } \ + return 0; \ + } \ + static moduledata_t name##_mod = { \ + #name, \ + name##_modevent, \ + NULL \ + }; \ + DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_ANY) + +/* entry point for the splash image decoder */ +int splash_register(splash_decoder_t *decoder); +int splash_unregister(splash_decoder_t *decoder); + +/* entry points for the console driver */ +int splash_init(video_adapter_t *adp, int (*callback)(int)); +int splash_term(video_adapter_t *adp); +int splash(video_adapter_t *adp, int on); + +/* event types for the callback function */ +#define SPLASH_INIT 0 +#define SPLASH_TERM 1 + +#endif /* _DEV_FB_SPLASHREG_H_ */ diff --git a/sys/dev/fb/vgareg.h b/sys/dev/fb/vgareg.h new file mode 100644 index 000000000000..841408e89879 --- /dev/null +++ b/sys/dev/fb/vgareg.h @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 1999 Kazutaka YOKOTA + * 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id:$ + */ + +#ifndef _DEV_FB_VGAREG_H_ +#define _DEV_FB_VGAREG_H_ + +/* physical addresses */ +#define MDA_BUF_BASE 0xb0000 +#define MDA_BUF_SIZE 0x08000 +#define MDA_BUF BIOS_PADDRTOVADDR(MDA_BUF_BASE) +#define CGA_BUF_BASE 0xb8000 +#define CGA_BUF_SIZE 0x08000 +#define CGA_BUF BIOS_PADDRTOVADDR(CGA_BUF_BASE) +#define EGA_BUF_BASE 0xa0000 +#define EGA_BUF_SIZE 0x20000 +#define EGA_BUF BIOS_PADDRTOVADDR(EGA_BUF_BASE) +#define GRAPHICS_BUF_BASE 0xa0000 +#define GRAPHICS_BUF_SIZE 0x10000 +#define GRAPHICS_BUF BIOS_PADDRTOVADDR(GRAPHICS_BUF_BASE) +#define FONT_BUF BIOS_PADDRTOVADDR(GRAPHICS_BUF_BASE) +#define VIDEO_BUF_BASE 0xa0000 +#define VIDEO_BUF_SIZE 0x20000 + +/* I/O port addresses */ +#define MONO_CRTC (IO_MDA + 0x04) /* crt controller base mono */ +#define COLOR_CRTC (IO_CGA + 0x04) /* crt controller base color */ +#define MISC (IO_VGA + 0x02) /* misc output register */ +#define ATC (IO_VGA + 0x00) /* attribute controller */ +#define TSIDX (IO_VGA + 0x04) /* timing sequencer idx */ +#define TSREG (IO_VGA + 0x05) /* timing sequencer data */ +#define PIXMASK (IO_VGA + 0x06) /* pixel write mask */ +#define PALRADR (IO_VGA + 0x07) /* palette read address */ +#define PALWADR (IO_VGA + 0x08) /* palette write address */ +#define PALDATA (IO_VGA + 0x09) /* palette data register */ +#define GDCIDX (IO_VGA + 0x0E) /* graph data controller idx */ +#define GDCREG (IO_VGA + 0x0F) /* graph data controller data */ + +#ifdef KERNEL +extern int (*vga_sub_configure)(int flags); +#endif + +#endif /* _DEV_FB_VGAREG_H_ */ diff --git a/sys/dev/kbd/atkbd.c b/sys/dev/kbd/atkbd.c new file mode 100644 index 000000000000..21ed5692cef1 --- /dev/null +++ b/sys/dev/kbd/atkbd.c @@ -0,0 +1,1302 @@ +/*- + * Copyright (c) 1999 Kazutaka YOKOTA + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifndef __i386__ + +#define ATKBD_SOFTC(unit) \ + ((atkbd_softc_t *)devclass_get_softc(atkbd_devclass, unit)) + +#else /* __i386__ */ + +#include +#include + +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 +#include +#include + +#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 + +/* 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 000000000000..0f61835f0763 --- /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 +#include +#include +#include +#include + +#include + +#include + +#ifndef __i386__ +#include +#else +#include +#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 000000000000..81b0ad35dce9 --- /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 000000000000..9f20eeea5f3d --- /dev/null +++ b/sys/dev/kbd/atkbdreg.h @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 1999 Kazutaka YOKOTA + * 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 000000000000..7fac7091921b --- /dev/null +++ b/sys/dev/kbd/kbd.c @@ -0,0 +1,1193 @@ +/*- + * Copyright (c) 1999 Kazutaka YOKOTA + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/* 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 000000000000..f1a6c8bfd9a5 --- /dev/null +++ b/sys/dev/kbd/kbdreg.h @@ -0,0 +1,270 @@ +/*- + * Copyright (c) 1999 Kazutaka YOKOTA + * 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_ */ diff --git a/sys/i386/isa/atkbd_isa.c b/sys/i386/isa/atkbd_isa.c new file mode 100644 index 000000000000..d87dcdc941b8 --- /dev/null +++ b/sys/i386/isa/atkbd_isa.c @@ -0,0 +1,100 @@ +/*- + * Copyright (c) 1999 Kazutaka YOKOTA + * 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" + +#if NATKBD > 0 + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +static int atkbdprobe(struct isa_device *dev); +static int atkbdattach(struct isa_device *dev); +static ointhand2_t atkbd_isa_intr; + +struct isa_driver atkbddriver = { + atkbdprobe, + atkbdattach, + ATKBD_DRIVER_NAME, + 0, +}; + +static int +atkbdprobe(struct isa_device *dev) +{ + atkbd_softc_t *sc; + int error; + + sc = atkbd_get_softc(dev->id_unit); + if (sc == NULL) + return 0; + + /* try to find a keyboard */ + error = atkbd_probe_unit(dev->id_unit, sc, dev->id_iobase, + dev->id_irq, dev->id_flags); + if (error) + return 0; + + /* declare our interrupt handler */ + dev->id_ointr = atkbd_isa_intr; + + return -1; +} + +static int +atkbdattach(struct isa_device *dev) +{ + atkbd_softc_t *sc; + + sc = atkbd_get_softc(dev->id_unit); + if (sc == NULL) + return 0; + + return ((atkbd_attach_unit(dev->id_unit, sc)) ? 0 : 1); +} + +static void +atkbd_isa_intr(int unit) +{ + keyboard_t *kbd; + + kbd = atkbd_get_softc(unit)->kbd; + (*kbdsw[kbd->kb_index]->intr)(kbd, NULL); +} + +#endif /* NATKBD > 0 */ diff --git a/sys/i386/isa/atkbdc_isa.c b/sys/i386/isa/atkbdc_isa.c new file mode 100644 index 000000000000..c547a405600b --- /dev/null +++ b/sys/i386/isa/atkbdc_isa.c @@ -0,0 +1,83 @@ +/*- + * 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. + * 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: $ + */ + +#include "atkbdc.h" +#include "opt_kbd.h" + +#if NATKBDC > 0 + +#include +#include +#include + +#include + +#include +#include + +static int atkbdc_probe(struct isa_device *dev); +static int atkbdc_attach(struct isa_device *dev); + +struct isa_driver atkbdcdriver = { + atkbdc_probe, + atkbdc_attach, + ATKBDC_DRIVER_NAME, + 0, +}; + +static int +atkbdc_probe(struct isa_device *dev) +{ + atkbdc_softc_t *sc; + int error; + + sc = atkbdc_get_softc(dev->id_unit); + if (sc == NULL) + return 0; + + error = atkbdc_probe_unit(sc, dev->id_unit, dev->id_iobase); + if (error) + return 0; + if (dev->id_iobase <= 0) + dev->id_iobase = sc->port; + return IO_KBDSIZE; +} + +static int +atkbdc_attach(struct isa_device *dev) +{ + atkbdc_softc_t *sc; + + sc = atkbdc_get_softc(dev->id_unit); + return ((sc == NULL) ? 0 : 1); +} + +#endif /* NATKBDC > 0 */ diff --git a/sys/i386/isa/vga_isa.c b/sys/i386/isa/vga_isa.c new file mode 100644 index 000000000000..0aaab5c0d8a4 --- /dev/null +++ b/sys/i386/isa/vga_isa.c @@ -0,0 +1,2238 @@ +/*- + * Copyright (c) 1999 Kazutaka YOKOTA + * Copyright (c) 1992-1998 Søren Schmidt + * 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. + * 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 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 "vga.h" +#include "opt_vga.h" +#include "opt_fb.h" +#include "opt_syscons.h" /* should be removed in the future, XXX */ + +#if NVGA > 0 + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#ifndef __i386__ +#include +#include +#else +#include +#include +#endif + +#define DRIVER_NAME "vga" + +/* cdev driver declaration */ + +#define ISAVGA_UNIT(dev) minor(dev) +#define ISAVGA_MKMINOR(unit) (unit) + +typedef struct isavga_softc { + video_adapter_t *adp; +} isavga_softc_t; + +#ifndef __i386__ + +#define ISAVGA_SOFTC(unit) \ + ((isavga_softc_t *)devclass_get_softc(isavga_devclass, unit)) + +devclass_t isavga_devclass; + +static int isavga_probe(device_t dev); +static int isavga_attach(device_t dev); + +static device_method_t isavga_methods[] = { + DEVMETHOD(device_probe, isavga_probe), + DEVMETHOD(device_attach, isavga_attach), + { 0, 0 } +}; + +static driver_t isavga_driver = { + DRIVER_NAME, + isavga_methods, + DRIVER_TYPE_TTY, + sizeof(isavga_softc_t), +}; + +#else /* __i386__ */ + +#define ISAVGA_SOFTC(unit) (isavga_softc[unit]) + +static isavga_softc_t *isavga_softc[NVGA]; + +static int isavga_probe(struct isa_device *dev); +static int isavga_attach(struct isa_device *dev); + +struct isa_driver vgadriver = { + isavga_probe, + isavga_attach, + DRIVER_NAME, + 0, +}; + +#endif /* __i386__ */ + +static int isavga_probe_unit(int unit, isavga_softc_t *sc, + int flags); +static int isavga_attach_unit(int unit, isavga_softc_t *sc, + int flags); + +#ifdef FB_INSTALL_CDEV + +static d_open_t isavgaopen; +static d_close_t isavgaclose; +static d_read_t isavgaread; +static d_ioctl_t isavgaioctl; + +static struct cdevsw vga_cdevsw = { + isavgaopen, isavgaclose, noread, nowrite, /* ?? */ + isavgaioctl, nostop, nullreset, nodevtotty, + seltrue, nommap, NULL, DRIVER_NAME, + NULL, -1, nodump, nopsize, +}; + +#endif /* FB_INSTALL_CDEV */ + +#ifndef __i386__ + +static int +isavga_probe(device_t dev) +{ + isavga_softc_t *sc; + + sc = device_get_softc(dev); + return isavga_probe_unit(device_get_unit(dev), sc, isa_get_flags(dev)); +} + +static int +isavga_attach(device_t dev) +{ + isavga_softc_t *sc; + + sc = device_get_softc(dev); + return isavga_attach_unit(device_get_unit(dev), sc, isa_get_flags(dev)); +} + +#else /* __i386__ */ + +static int +isavga_probe(struct isa_device *dev) +{ + isavga_softc_t *sc; + int error; + + if (dev->id_unit >= sizeof(isavga_softc)/sizeof(isavga_softc[0])) + return 0; + sc = isavga_softc[dev->id_unit] + = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); + if (sc == NULL) + return 0; + + error = isavga_probe_unit(dev->id_unit, sc, dev->id_flags); + if (error) { + isavga_softc[dev->id_unit] = NULL; + free(sc, M_DEVBUF); + return 0; + } + + dev->id_iobase = sc->adp->va_io_base; + dev->id_maddr = (caddr_t)BIOS_PADDRTOVADDR(sc->adp->va_mem_base); + dev->id_msize = sc->adp->va_mem_size; + + return sc->adp->va_io_size; +} + +static int +isavga_attach(struct isa_device *dev) +{ + isavga_softc_t *sc; + + if (dev->id_unit >= sizeof(isavga_softc)/sizeof(isavga_softc[0])) + return 0; + sc = isavga_softc[dev->id_unit]; + if (sc == NULL) + return 0; + + return ((isavga_attach_unit(dev->id_unit, sc, dev->id_flags)) ? 0 : 1); +} + +#endif /* __i386__ */ + +static int +isavga_probe_unit(int unit, isavga_softc_t *sc, int flags) +{ + video_switch_t *sw; + + bzero(sc, sizeof(*sc)); + sw = vid_get_switch(DRIVER_NAME); + if (sw == NULL) + return 0; + return (*sw->probe)(unit, &sc->adp, NULL, flags); +} + +static int +isavga_attach_unit(int unit, isavga_softc_t *sc, int flags) +{ + video_switch_t *sw; + int error; + + sw = vid_get_switch(DRIVER_NAME); + if (sw == NULL) + return ENXIO; + + error = (*sw->init)(unit, sc->adp, flags); + if (error) + return ENXIO; + +#ifdef FB_INSTALL_CDEV + /* attach a virtual frame buffer device */ + error = fb_attach(makedev(0, ISAVGA_MKMINOR(unit)), scp->adp, + &vga_cdevsw); + if (error) + return error; +#endif /* FB_INSTALL_CDEV */ + + if (bootverbose) + (*sw->diag)(sc->adp, bootverbose); + + return 0; +} + +/* LOW-LEVEL */ + +#include +#include + +#define probe_done(adp) ((adp)->va_flags & V_ADP_PROBED) +#define init_done(adp) ((adp)->va_flags & V_ADP_INITIALIZED) +#define config_done(adp) ((adp)->va_flags & V_ADP_REGISTERED) + +/* for compatibility with old kernel options */ +#ifdef SC_ALT_SEQACCESS +#undef SC_ALT_SEQACCESS +#undef VGA_ALT_SEQACCESS +#define VGA_ALT_SEQACCESS 1 +#endif + +#ifdef SLOW_VGA +#undef SLOW_VGA +#undef VGA_SLOW_IOACCESS +#define VGA_SLOW_IOACCESS 1 +#endif + +/* architecture dependent option */ +#ifdef __alpha__ +#define VGA_NO_BIOS 1 +#endif + +/* this should really be in `rtc.h' */ +#define RTC_EQUIPMENT 0x14 + +/* various sizes */ +#define V_MODE_MAP_SIZE (M_VGA_CG320 + 1) +#define V_MODE_PARAM_SIZE 64 + +/* video adapter state buffer */ +struct adp_state { + int sig; +#define V_STATE_SIG 0x736f6962 + u_char regs[V_MODE_PARAM_SIZE]; +}; +typedef struct adp_state adp_state_t; + +/* video adapter information */ +#define DCC_MONO 0 +#define DCC_CGA40 1 +#define DCC_CGA80 2 +#define DCC_EGAMONO 3 +#define DCC_EGA40 4 +#define DCC_EGA80 5 + +/* + * NOTE: `va_window' should have a virtual address, but is initialized + * with a physical address in the following table, as verify_adapter() + * will perform address conversion at run-time. + */ +static video_adapter_t adapter_init_value[] = { + /* DCC_MONO */ + { 0, KD_MONO, "mda", 0, 0, 0, IO_MDA, IO_MDASIZE, MONO_CRTC, + MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, + 0, 0, 0, 7, 0, 0, NULL }, + /* DCC_CGA40 */ + { 0, KD_CGA, "cga", 0, 0, V_ADP_COLOR, IO_CGA, IO_CGASIZE, COLOR_CRTC, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, + 0, 0, 0, 3, 0, 0, NULL }, + /* DCC_CGA80 */ + { 0, KD_CGA, "cga", 0, 0, V_ADP_COLOR, IO_CGA, IO_CGASIZE, COLOR_CRTC, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, + 0, 0, 0, 3, 0, 0, NULL }, + /* DCC_EGAMONO */ + { 0, KD_EGA, "ega", 0, 0, 0, IO_MDA, 48, MONO_CRTC, + EGA_BUF_BASE, EGA_BUF_SIZE, MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, + 0, 0, 0, 7, 0, 0, NULL }, + /* DCC_EGA40 */ + { 0, KD_EGA, "ega", 0, 0, V_ADP_COLOR, IO_MDA, 48, COLOR_CRTC, + EGA_BUF_BASE, EGA_BUF_SIZE, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, + 0, 0, 0, 3, 0, 0, NULL }, + /* DCC_EGA80 */ + { 0, KD_EGA, "ega", 0, 0, V_ADP_COLOR, IO_MDA, 48, COLOR_CRTC, + EGA_BUF_BASE, EGA_BUF_SIZE, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, + 0, 0, 0, 3, 0, 0, NULL }, +}; + +static video_adapter_t biosadapter[2]; +static int biosadapters = 0; + +/* video driver declarations */ +static int vga_configure(int flags); + int (*vga_sub_configure)(int flags); +static int vga_nop(void); +static vi_probe_t vga_probe; +static vi_init_t vga_init; +static vi_get_info_t vga_get_info; +static vi_query_mode_t vga_query_mode; +static vi_set_mode_t vga_set_mode; +static vi_save_font_t vga_save_font; +static vi_load_font_t vga_load_font; +static vi_show_font_t vga_show_font; +static vi_save_palette_t vga_save_palette; +static vi_load_palette_t vga_load_palette; +static vi_set_border_t vga_set_border; +static vi_save_state_t vga_save_state; +static vi_load_state_t vga_load_state; +static vi_set_win_org_t vga_set_origin; +static vi_read_hw_cursor_t vga_read_hw_cursor; +static vi_set_hw_cursor_t vga_set_hw_cursor; +static vi_set_hw_cursor_shape_t vga_set_hw_cursor_shape; +static vi_mmap_t vga_mmap; +static vi_diag_t vga_diag; + +static video_switch_t vgavidsw = { + vga_probe, + vga_init, + vga_get_info, + vga_query_mode, + vga_set_mode, + vga_save_font, + vga_load_font, + vga_show_font, + vga_save_palette, + vga_load_palette, + vga_set_border, + vga_save_state, + vga_load_state, + vga_set_origin, + vga_read_hw_cursor, + vga_set_hw_cursor, + vga_set_hw_cursor_shape, + (vi_blank_display_t *)vga_nop, + vga_mmap, + vga_diag, +}; + +VIDEO_DRIVER(mda, vgavidsw, NULL); +VIDEO_DRIVER(cga, vgavidsw, NULL); +VIDEO_DRIVER(ega, vgavidsw, NULL); +VIDEO_DRIVER(vga, vgavidsw, vga_configure); + +/* VGA BIOS standard video modes */ +#define EOT (-1) +#define NA (-2) + +static video_info_t bios_vmode[] = { + /* CGA */ + { M_B40x25, V_INFO_COLOR, 40, 25, 8, 8, 2, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + { M_C40x25, V_INFO_COLOR, 40, 25, 8, 8, 4, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + { M_B80x25, V_INFO_COLOR, 80, 25, 8, 8, 2, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + { M_C80x25, V_INFO_COLOR, 80, 25, 8, 8, 4, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + /* EGA */ + { M_ENH_B40x25, V_INFO_COLOR, 40, 25, 8, 14, 2, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + { M_ENH_C40x25, V_INFO_COLOR, 40, 25, 8, 14, 4, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + { M_ENH_B80x25, V_INFO_COLOR, 80, 25, 8, 14, 2, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + { M_ENH_C80x25, V_INFO_COLOR, 80, 25, 8, 14, 4, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + /* VGA */ + { M_VGA_C40x25, V_INFO_COLOR, 40, 25, 8, 16, 4, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + { M_VGA_M80x25, 0, 80, 25, 8, 16, 2, 1, + MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0 }, + { M_VGA_C80x25, V_INFO_COLOR, 80, 25, 8, 16, 4, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + /* MDA */ + { M_EGAMONO80x25, 0, 80, 25, 8, 14, 2, 1, + MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0 }, + /* EGA */ + { M_ENH_B80x43, V_INFO_COLOR, 80, 43, 8, 8, 2, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + { M_ENH_C80x43, V_INFO_COLOR, 80, 43, 8, 8, 4, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + /* VGA */ + { M_VGA_M80x30, 0, 80, 30, 8, 16, 2, 1, + MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0 }, + { M_VGA_C80x30, V_INFO_COLOR, 80, 30, 8, 16, 4, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + { M_VGA_M80x50, 0, 80, 50, 8, 8, 2, 1, + MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0 }, + { M_VGA_C80x50, V_INFO_COLOR, 80, 50, 8, 8, 4, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + { M_VGA_M80x60, 0, 80, 60, 8, 8, 2, 1, + MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0 }, + { M_VGA_C80x60, V_INFO_COLOR, 80, 60, 8, 8, 4, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, +#ifndef VGA_NO_MODE_CHANGE + /* CGA */ + { M_BG320, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 2, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + { M_CG320, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 2, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + { M_BG640, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 200, 8, 8, 1, 1, + CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, + /* EGA */ + { M_CG320_D, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 4, 4, + GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, + { M_CG640_E, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 200, 8, 8, 4, 4, + GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, + { M_EGAMONOAPA, V_INFO_GRAPHICS, 640, 350, 8, 14, 4, 4, + GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, 64*1024, 0, 0 }, + { M_ENHMONOAPA2,V_INFO_GRAPHICS, 640, 350, 8, 14, 4, 4, + GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, + { M_CG640x350, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 350, 8, 14, 2, 2, + GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, + { M_ENH_CG640, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 350, 8, 14, 4, 4, + GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, + /* VGA */ + { M_BG640x480, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 480, 8, 16, 4, 4, + GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, + { M_CG640x480, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 480, 8, 16, 4, 4, + GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, + { M_VGA_CG320, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 8, 1, + GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, + { M_VGA_MODEX, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 240, 8, 8, 8, 1, + GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, +#endif /* VGA_NO_MODE_CHANGE */ + + { EOT }, +}; + +static int init_done = FALSE; +static u_char *video_mode_ptr = NULL; /* EGA/VGA */ +static u_char *video_mode_ptr2 = NULL; /* CGA/MDA */ +static u_char *mode_map[V_MODE_MAP_SIZE]; +static adp_state_t adpstate; +static adp_state_t adpstate2; +static int rows_offset = 1; + +/* local macros and functions */ +#define BIOS_SADDRTOLADDR(p) ((((p) & 0xffff0000) >> 12) + ((p) & 0x0000ffff)) + +#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) +static void map_mode_table(u_char *map[], u_char *table, int max); +#endif +static void clear_mode_map(video_adapter_t *adp, u_char *map[], int max, + int color); +#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) +static int map_mode_num(int mode); +#endif +static int map_gen_mode_num(int type, int color, int mode); +static int map_bios_mode_num(int type, int color, int bios_mode); +static u_char *get_mode_param(int mode); +#ifndef VGA_NO_BIOS +static void fill_adapter_param(int code, video_adapter_t *adp); +#endif +static int verify_adapter(video_adapter_t *adp); +#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) +#define COMP_IDENTICAL 0 +#define COMP_SIMILAR 1 +#define COMP_DIFFERENT 2 +static int comp_adpregs(u_char *buf1, u_char *buf2); +#endif +static int probe_adapters(void); + +#define PARAM_BUFSIZE 6 +static void set_font_mode(video_adapter_t *adp, u_char *buf); +static void set_normal_mode(video_adapter_t *adp, u_char *buf); + +static void dump_buffer(u_char *buf, size_t len); + +#define ISMAPPED(pa, width) \ + (((pa) <= (u_long)0x1000 - (width)) \ + || ((pa) >= ISA_HOLE_START && (pa) <= 0x100000 - (width))) + +#define prologue(adp, flag, err) \ + if (!init_done || !((adp)->va_flags & (flag))) \ + return (err) + +/* a backdoor for the console driver */ +static int +vga_configure(int flags) +{ + int i; + + probe_adapters(); + for (i = 0; i < biosadapters; ++i) { + if (!probe_done(&biosadapter[i])) + continue; + biosadapter[i].va_flags |= V_ADP_INITIALIZED; + if (!config_done(&biosadapter[i])) { + if (vid_register(&biosadapter[i]) < 0) + continue; + biosadapter[i].va_flags |= V_ADP_REGISTERED; + } + } + if (vga_sub_configure != NULL) + (*vga_sub_configure)(flags); + + return biosadapters; +} + +/* local subroutines */ + +#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) +/* construct the mode parameter map */ +static void +map_mode_table(u_char *map[], u_char *table, int max) +{ + int i; + + for(i = 0; i < max; ++i) + map[i] = table + i*V_MODE_PARAM_SIZE; + for(; i < V_MODE_MAP_SIZE; ++i) + map[i] = NULL; +} +#endif /* !VGA_NO_BIOS && !VGA_NO_MODE_CHANGE */ + +static void +clear_mode_map(video_adapter_t *adp, u_char *map[], int max, int color) +{ + video_info_t info; + int i; + + /* + * NOTE: we don't touch `bios_vmode[]' because it is shared + * by all adapters. + */ + for(i = 0; i < max; ++i) { + if (vga_get_info(adp, i, &info)) + continue; + if ((info.vi_flags & V_INFO_COLOR) != color) + map[i] = NULL; + } +} + +#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) +/* map the non-standard video mode to a known mode number */ +static int +map_mode_num(int mode) +{ + static struct { + int from; + int to; + } mode_map[] = { + { M_ENH_B80x43, M_ENH_B80x25 }, + { M_ENH_C80x43, M_ENH_C80x25 }, + { M_VGA_M80x30, M_VGA_M80x25 }, + { M_VGA_C80x30, M_VGA_C80x25 }, + { M_VGA_M80x50, M_VGA_M80x25 }, + { M_VGA_C80x50, M_VGA_C80x25 }, + { M_VGA_M80x60, M_VGA_M80x25 }, + { M_VGA_C80x60, M_VGA_C80x25 }, + { M_VGA_MODEX, M_VGA_CG320 }, + }; + int i; + + for (i = 0; i < sizeof(mode_map)/sizeof(mode_map[0]); ++i) { + if (mode_map[i].from == mode) + return mode_map[i].to; + } + return mode; +} +#endif /* !VGA_NO_BIOS && !VGA_NO_MODE_CHANGE */ + +/* map a generic video mode to a known mode number */ +static int +map_gen_mode_num(int type, int color, int mode) +{ + static struct { + int from; + int to_color; + int to_mono; + } mode_map[] = { + { M_TEXT_80x30, M_VGA_C80x30, M_VGA_M80x30, }, + { M_TEXT_80x43, M_ENH_C80x43, M_ENH_B80x43, }, + { M_TEXT_80x50, M_VGA_C80x50, M_VGA_M80x50, }, + { M_TEXT_80x60, M_VGA_C80x60, M_VGA_M80x60, }, + }; + int i; + + if (mode == M_TEXT_80x25) { + switch (type) { + + case KD_VGA: + if (color) + return M_VGA_C80x25; + else + return M_VGA_M80x25; + break; + + case KD_EGA: + if (color) + return M_ENH_C80x25; + else + return M_EGAMONO80x25; + break; + + case KD_CGA: + return M_C80x25; + + case KD_MONO: + case KD_HERCULES: + return M_EGAMONO80x25; /* XXX: this name is confusing */ + + default: + return -1; + } + } + + for (i = 0; i < sizeof(mode_map)/sizeof(mode_map[0]); ++i) { + if (mode_map[i].from == mode) + return ((color) ? mode_map[i].to_color : mode_map[i].to_mono); + } + return mode; +} + +/* turn the BIOS video number into our video mode number */ +static int +map_bios_mode_num(int type, int color, int bios_mode) +{ + static int cga_modes[7] = { + M_B40x25, M_C40x25, /* 0, 1 */ + M_B80x25, M_C80x25, /* 2, 3 */ + M_BG320, M_CG320, + M_BG640, + }; + static int ega_modes[17] = { + M_ENH_B40x25, M_ENH_C40x25, /* 0, 1 */ + M_ENH_B80x25, M_ENH_C80x25, /* 2, 3 */ + M_BG320, M_CG320, + M_BG640, + M_EGAMONO80x25, /* 7 */ + 8, 9, 10, 11, 12, + M_CG320_D, + M_CG640_E, + M_ENHMONOAPA2, /* XXX: video momery > 64K */ + M_ENH_CG640, /* XXX: video momery > 64K */ + }; + static int vga_modes[20] = { + M_VGA_C40x25, M_VGA_C40x25, /* 0, 1 */ + M_VGA_C80x25, M_VGA_C80x25, /* 2, 3 */ + M_BG320, M_CG320, + M_BG640, + M_VGA_M80x25, /* 7 */ + 8, 9, 10, 11, 12, + M_CG320_D, + M_CG640_E, + M_ENHMONOAPA2, + M_ENH_CG640, + M_BG640x480, M_CG640x480, + M_VGA_CG320, + }; + + switch (type) { + + case KD_VGA: + if (bios_mode < sizeof(vga_modes)/sizeof(vga_modes[0])) + return vga_modes[bios_mode]; + else if (color) + return M_VGA_C80x25; + else + return M_VGA_M80x25; + break; + + case KD_EGA: + if (bios_mode < sizeof(ega_modes)/sizeof(ega_modes[0])) + return ega_modes[bios_mode]; + else if (color) + return M_ENH_C80x25; + else + return M_EGAMONO80x25; + break; + + case KD_CGA: + if (bios_mode < sizeof(cga_modes)/sizeof(cga_modes[0])) + return cga_modes[bios_mode]; + else + return M_C80x25; + break; + + case KD_MONO: + case KD_HERCULES: + return M_EGAMONO80x25; /* XXX: this name is confusing */ + + default: + break; + } + return -1; +} + +/* look up a parameter table entry */ +static u_char +*get_mode_param(int mode) +{ +#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) + if (mode >= V_MODE_MAP_SIZE) + mode = map_mode_num(mode); +#endif + if ((mode >= 0) && (mode < V_MODE_MAP_SIZE)) + return mode_map[mode]; + else + return NULL; +} + +#ifndef VGA_NO_BIOS +static void +fill_adapter_param(int code, video_adapter_t *adp) +{ + static struct { + int primary; + int secondary; + } dcc[] = { + { DCC_MONO, DCC_EGA40 /* CGA monitor */ }, + { DCC_MONO, DCC_EGA80 /* CGA monitor */ }, + { DCC_MONO, DCC_EGA80 /* CGA emulation */ }, + { DCC_MONO, DCC_EGA80 }, + { DCC_CGA40, DCC_EGAMONO }, + { DCC_CGA80, DCC_EGAMONO }, + { DCC_EGA40 /* CGA monitor */, DCC_MONO}, + { DCC_EGA80 /* CGA monitor */, DCC_MONO}, + { DCC_EGA80 /* CGA emulation */,DCC_MONO }, + { DCC_EGA80, DCC_MONO }, + { DCC_EGAMONO, DCC_CGA40 }, + { DCC_EGAMONO, DCC_CGA40 }, + }; + + if ((code < 0) || (code >= sizeof(dcc)/sizeof(dcc[0]))) { + adp[V_ADP_PRIMARY] = adapter_init_value[DCC_MONO]; + adp[V_ADP_SECONDARY] = adapter_init_value[DCC_CGA80]; + } else { + adp[V_ADP_PRIMARY] = adapter_init_value[dcc[code].primary]; + adp[V_ADP_SECONDARY] = adapter_init_value[dcc[code].secondary]; + } +} +#endif /* VGA_NO_BIOS */ + +static int +verify_adapter(video_adapter_t *adp) +{ + volatile u_int16_t *buf; + u_int16_t v; + u_int32_t p; + + buf = (u_int16_t *)BIOS_PADDRTOVADDR(adp->va_window); + v = readw(buf); + writew(buf, 0xA55A); + if (readw(buf) != 0xA55A) + return 1; + writew(buf, v); + + switch (adp->va_type) { + + case KD_EGA: + outb(adp->va_crtc_addr, 7); + if (inb(adp->va_crtc_addr) == 7) { + adp->va_type = KD_VGA; + adp->va_name = "vga"; + adp->va_flags |= V_ADP_STATESAVE | V_ADP_PALETTE; + } + adp->va_flags |= V_ADP_STATELOAD | V_ADP_BORDER; + /* the color adapter may be in the 40x25 mode... XXX */ + +#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) + /* get the BIOS video mode pointer */ + p = *(u_int32_t *)BIOS_PADDRTOVADDR(0x4a8); + p = BIOS_SADDRTOLADDR(p); + if (ISMAPPED(p, sizeof(u_int32_t))) { + p = *(u_int32_t *)BIOS_PADDRTOVADDR(p); + p = BIOS_SADDRTOLADDR(p); + if (ISMAPPED(p, V_MODE_PARAM_SIZE)) + video_mode_ptr = (u_char *)BIOS_PADDRTOVADDR(p); + } +#endif + break; + + case KD_CGA: + adp->va_flags |= V_ADP_COLOR | V_ADP_BORDER; + /* may be in the 40x25 mode... XXX */ +#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) + /* get the BIOS video mode pointer */ + p = *(u_int32_t *)BIOS_PADDRTOVADDR(0x1d*4); + p = BIOS_SADDRTOLADDR(p); + video_mode_ptr2 = (u_char *)BIOS_PADDRTOVADDR(p); +#endif + break; + + case KD_MONO: +#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) + /* get the BIOS video mode pointer */ + p = *(u_int32_t *)BIOS_PADDRTOVADDR(0x1d*4); + p = BIOS_SADDRTOLADDR(p); + video_mode_ptr2 = (u_char *)BIOS_PADDRTOVADDR(p); +#endif + break; + } + + return 0; +} + +#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) +/* compare two parameter table entries */ +static int +comp_adpregs(u_char *buf1, u_char *buf2) +{ + static struct { + u_char mask; + } params[V_MODE_PARAM_SIZE] = { + 0xff, 0x00, 0xff, /* COLS, ROWS, POINTS */ + 0x00, 0x00, /* page length */ + 0xfe, 0xff, 0xff, 0xff, /* sequencer registers */ + 0xf3, /* misc register */ + 0xff, 0xff, 0xff, 0x7f, 0xff, /* CRTC */ + 0xff, 0xff, 0xff, 0x7f, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x7f, 0xff, 0xff, + 0x7f, 0xff, 0xff, 0xef, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, /* attribute controller registers */ + 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xff, /* GDC register */ + 0xff, 0xff, 0xff, 0xff, + }; + int identical = TRUE; + int i; + + if ((buf1 == NULL) || (buf2 == NULL)) + return COMP_DIFFERENT; + + for (i = 0; i < sizeof(params)/sizeof(params[0]); ++i) { + if (params[i].mask == 0) /* don't care */ + continue; + if ((buf1[i] & params[i].mask) != (buf2[i] & params[i].mask)) + return COMP_DIFFERENT; + if (buf1[i] != buf2[i]) + identical = FALSE; + } + return (identical) ? COMP_IDENTICAL : COMP_SIMILAR; +} +#endif /* !VGA_NO_BIOS && !VGA_NO_MODE_CHANGE */ + +/* probe video adapters and return the number of detected adapters */ +static int +probe_adapters(void) +{ + video_adapter_t *adp; + video_info_t info; + u_char *mp; + int i; + + /* do this test only once */ + if (init_done) + return biosadapters; + init_done = TRUE; + + /* + * Locate display adapters. + * The AT architecture supports upto two adapters. `syscons' allows + * the following combinations of adapters: + * 1) MDA + CGA + * 2) MDA + EGA/VGA color + * 3) CGA + EGA/VGA mono + * Note that `syscons' doesn't bother with MCGA as it is only + * avaiable for low end PS/2 models which has 80286 or earlier CPUs, + * thus, they are not running FreeBSD! + * When there are two adapaters in the system, one becomes `primary' + * and the other `secondary'. The EGA adapter has a set of DIP + * switches on board for this information and the EGA BIOS copies + * it in the BIOS data area BIOSDATA_VIDEOSWITCH (40:88). + * The VGA BIOS has more sophisticated mechanism and has this + * information in BIOSDATA_DCCINDEX (40:8a), but it also maintains + * compatibility with the EGA BIOS by updating BIOSDATA_VIDEOSWITCH. + */ + + /* + * Check rtc and BIOS data area. + * XXX: we don't use BIOSDATA_EQUIPMENT, since it is not a dead + * copy of RTC_EQUIPMENT. Bits 4 and 5 of ETC_EQUIPMENT are + * zeros for EGA and VGA. However, the EGA/VGA BIOS sets + * these bits in BIOSDATA_EQUIPMENT according to the monitor + * type detected. + */ +#ifndef VGA_NO_BIOS + switch ((rtcin(RTC_EQUIPMENT) >> 4) & 3) { /* bit 4 and 5 */ + case 0: + /* EGA/VGA */ + fill_adapter_param(readb(BIOS_PADDRTOVADDR(0x488)) & 0x0f, + biosadapter); + break; + case 1: + /* CGA 40x25 */ + /* FIXME: switch to the 80x25 mode? XXX */ + biosadapter[V_ADP_PRIMARY] = adapter_init_value[DCC_CGA40]; + biosadapter[V_ADP_SECONDARY] = adapter_init_value[DCC_MONO]; + break; + case 2: + /* CGA 80x25 */ + biosadapter[V_ADP_PRIMARY] = adapter_init_value[DCC_CGA80]; + biosadapter[V_ADP_SECONDARY] = adapter_init_value[DCC_MONO]; + break; + case 3: + /* MDA */ + biosadapter[V_ADP_PRIMARY] = adapter_init_value[DCC_MONO]; + biosadapter[V_ADP_SECONDARY] = adapter_init_value[DCC_CGA80]; + break; + } +#else + /* assume EGA/VGA? XXX */ + biosadapter[V_ADP_PRIMARY] = adapter_init_value[DCC_EGA80]; + biosadapter[V_ADP_SECONDARY] = adapter_init_value[DCC_MONO]; +#endif /* VGA_NO_BIOS */ + + biosadapters = 0; + if (verify_adapter(&biosadapter[V_ADP_SECONDARY]) == 0) { + ++biosadapters; + biosadapter[V_ADP_SECONDARY].va_flags |= V_ADP_PROBED; + biosadapter[V_ADP_SECONDARY].va_mode = + biosadapter[V_ADP_SECONDARY].va_initial_mode = + map_bios_mode_num(biosadapter[V_ADP_SECONDARY].va_type, + biosadapter[V_ADP_SECONDARY].va_flags + & V_ADP_COLOR, + biosadapter[V_ADP_SECONDARY].va_initial_bios_mode); + } else { + biosadapter[V_ADP_SECONDARY].va_type = -1; + } + if (verify_adapter(&biosadapter[V_ADP_PRIMARY]) == 0) { + ++biosadapters; + biosadapter[V_ADP_PRIMARY].va_flags |= V_ADP_PROBED; +#ifndef VGA_NO_BIOS + biosadapter[V_ADP_PRIMARY].va_initial_bios_mode = + readb(BIOS_PADDRTOVADDR(0x449)); +#else + biosadapter[V_ADP_PRIMARY].va_initial_bios_mode = 3; /* XXX */ +#endif + biosadapter[V_ADP_PRIMARY].va_mode = + biosadapter[V_ADP_PRIMARY].va_initial_mode = + map_bios_mode_num(biosadapter[V_ADP_PRIMARY].va_type, + biosadapter[V_ADP_PRIMARY].va_flags & V_ADP_COLOR, + biosadapter[V_ADP_PRIMARY].va_initial_bios_mode); + } else { + biosadapter[V_ADP_PRIMARY] = biosadapter[V_ADP_SECONDARY]; + biosadapter[V_ADP_SECONDARY].va_type = -1; + } + if (biosadapters == 0) + return biosadapters; +#if 0 + biosadapter[V_ADP_PRIMARY].va_index = V_ADP_PRIMARY; + biosadapter[V_ADP_SECONDARY].va_index = V_ADP_SECONDARY; +#endif + biosadapter[V_ADP_PRIMARY].va_unit = V_ADP_PRIMARY; + biosadapter[V_ADP_SECONDARY].va_unit = V_ADP_SECONDARY; + +#if 0 /* we don't need these... */ + fb_init_struct(&biosadapter[V_ADP_PRIMARY], ...); + fb_init_struct(&biosadapter[V_ADP_SECONDARY], ...); +#endif + +#if 0 + /* + * We cannot have two video adapter of the same type; there must be + * only one of color or mono adapter, or one each of them. + */ + if (biosadapters > 1) { + if (!((biosadapter[0].va_flags ^ biosadapter[1].va_flags) + & V_ADP_COLOR)) + /* we have two mono or color adapters!! */ + return (biosadapters = 0); + } +#endif + + /* + * Ensure a zero start address. This is mainly to recover after + * switching from pcvt using userconfig(). The registers are w/o + * for old hardware so it's too hard to relocate the active screen + * memory. + * This must be done before vga_save_state() for VGA. + */ + outb(biosadapter[V_ADP_PRIMARY].va_crtc_addr, 12); + outb(biosadapter[V_ADP_PRIMARY].va_crtc_addr + 1, 0); + outb(biosadapter[V_ADP_PRIMARY].va_crtc_addr, 13); + outb(biosadapter[V_ADP_PRIMARY].va_crtc_addr + 1, 0); + + /* the video mode parameter table in EGA/VGA BIOS */ + /* NOTE: there can be only one EGA/VGA, wheather color or mono, + * recognized by the video BIOS. + */ + if ((biosadapter[V_ADP_PRIMARY].va_type == KD_EGA) || + (biosadapter[V_ADP_PRIMARY].va_type == KD_VGA)) { + adp = &biosadapter[V_ADP_PRIMARY]; + } else if ((biosadapter[V_ADP_SECONDARY].va_type == KD_EGA) || + (biosadapter[V_ADP_SECONDARY].va_type == KD_VGA)) { + adp = &biosadapter[V_ADP_SECONDARY]; + } else { + adp = NULL; + } + bzero(mode_map, sizeof(mode_map)); + if (adp != NULL) { + if (adp->va_type == KD_VGA) { + vga_save_state(adp, &adpstate, sizeof(adpstate)); +#if defined(VGA_NO_BIOS) || defined(VGA_NO_MODE_CHANGE) + mode_map[adp->va_initial_mode] = adpstate.regs; + rows_offset = 1; +#else /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */ + if (video_mode_ptr == NULL) { + mode_map[adp->va_initial_mode] = adpstate.regs; + rows_offset = 1; + } else { + /* discard the table if we are not familiar with it... */ + map_mode_table(mode_map, video_mode_ptr, M_VGA_CG320 + 1); + mp = get_mode_param(adp->va_initial_mode); + if (mp != NULL) + bcopy(mp, adpstate2.regs, sizeof(adpstate2.regs)); + switch (comp_adpregs(adpstate.regs, mp)) { + case COMP_IDENTICAL: + /* + * OK, this parameter table looks reasonably familiar + * to us... + */ + /* + * This is a kludge for Toshiba DynaBook SS433 + * whose BIOS video mode table entry has the actual # + * of rows at the offset 1; BIOSes from other + * manufacturers store the # of rows - 1 there. XXX + */ + rows_offset = adpstate.regs[1] + 1 - mp[1]; + break; + + case COMP_SIMILAR: + /* + * Not exactly the same, but similar enough to be + * trusted. However, use the saved register values + * for the initial mode and other modes which are + * based on the initial mode. + */ + mode_map[adp->va_initial_mode] = adpstate.regs; + rows_offset = adpstate.regs[1] + 1 - mp[1]; + adpstate.regs[1] -= rows_offset - 1; + break; + + case COMP_DIFFERENT: + default: + /* + * Don't use the paramter table in BIOS. It doesn't + * look familiar to us. Video mode switching is allowed + * only if the new mode is the same as or based on + * the initial mode. + */ + video_mode_ptr = NULL; + bzero(mode_map, sizeof(mode_map)); + mode_map[adp->va_initial_mode] = adpstate.regs; + rows_offset = 1; + break; + } + } +#endif /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */ + +#ifndef VGA_NO_MODE_CHANGE + adp->va_flags |= V_ADP_MODECHANGE; +#endif +#ifndef VGA_NO_FONT_LOADING + adp->va_flags |= V_ADP_FONT; +#endif + } else if (adp->va_type == KD_EGA) { +#if defined(VGA_NO_BIOS) || defined(VGA_NO_MODE_CHANGE) + rows_offset = 1; +#else /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */ + if (video_mode_ptr == NULL) { + rows_offset = 1; + } else { + map_mode_table(mode_map, video_mode_ptr, M_ENH_C80x25 + 1); + /* XXX how can one validate the EGA table... */ + mp = get_mode_param(adp->va_initial_mode); + if (mp != NULL) { + adp->va_flags |= V_ADP_MODECHANGE; +#ifndef VGA_NO_FONT_LOADING + adp->va_flags |= V_ADP_FONT; +#endif + rows_offset = 1; + } else { + /* + * This is serious. We will not be able to switch video + * modes at all... + */ + video_mode_ptr = NULL; + bzero(mode_map, sizeof(mode_map)); + rows_offset = 1; + } + } +#endif /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */ + } + } + + /* remove conflicting modes if we have more than one adapter */ + if (biosadapters > 1) { + for (i = 0; i < biosadapters; ++i) { + if (!(biosadapter[i].va_flags & V_ADP_MODECHANGE)) + continue; + clear_mode_map(&biosadapter[i], mode_map, M_VGA_CG320 + 1, + (biosadapter[i].va_flags & V_ADP_COLOR) ? + V_INFO_COLOR : 0); + if ((biosadapter[i].va_type == KD_VGA) + || (biosadapter[i].va_type == KD_EGA)) { + biosadapter[i].va_io_base = + (biosadapter[i].va_flags & V_ADP_COLOR) ? + IO_VGA : IO_MDA; + biosadapter[i].va_io_size = 32; + } + } + } + + /* buffer address */ + vga_get_info(&biosadapter[V_ADP_PRIMARY], + biosadapter[V_ADP_PRIMARY].va_initial_mode, &info); + biosadapter[V_ADP_PRIMARY].va_mode_flags = info.vi_flags; + biosadapter[V_ADP_PRIMARY].va_window = BIOS_PADDRTOVADDR(info.vi_window); + biosadapter[V_ADP_PRIMARY].va_window_size = info.vi_window_size; + biosadapter[V_ADP_PRIMARY].va_window_gran = info.vi_window_gran; + if (info.vi_buffer_size == 0) { + biosadapter[V_ADP_PRIMARY].va_buffer = 0; + biosadapter[V_ADP_PRIMARY].va_buffer_size = 0; + } else { + biosadapter[V_ADP_PRIMARY].va_buffer + = BIOS_PADDRTOVADDR(info.vi_buffer); + biosadapter[V_ADP_PRIMARY].va_buffer_size = info.vi_buffer_size; + } + + if (biosadapters > 1) { + vga_get_info(&biosadapter[V_ADP_SECONDARY], + biosadapter[V_ADP_SECONDARY].va_initial_mode, &info); + biosadapter[V_ADP_SECONDARY].va_mode_flags = info.vi_flags; + biosadapter[V_ADP_SECONDARY].va_window = + BIOS_PADDRTOVADDR(info.vi_window); + biosadapter[V_ADP_SECONDARY].va_window_size = info.vi_window_size; + biosadapter[V_ADP_SECONDARY].va_window_gran = info.vi_window_gran; + if (info.vi_buffer_size == 0) { + biosadapter[V_ADP_SECONDARY].va_buffer = 0; + biosadapter[V_ADP_SECONDARY].va_buffer_size = 0; + } else { + biosadapter[V_ADP_SECONDARY].va_buffer = + BIOS_PADDRTOVADDR(info.vi_buffer); + biosadapter[V_ADP_SECONDARY].va_buffer_size = info.vi_buffer_size; + } + } + + /* + * XXX: we should verify the following values for the primary adapter... + * crtc I/O port address: *(u_int16_t *)BIOS_PADDRTOVADDR(0x463); + * color/mono display: (*(u_int8_t *)BIOS_PADDRTOVADDR(0x487) & 0x02) + * ? 0 : V_ADP_COLOR; + * columns: *(u_int8_t *)BIOS_PADDRTOVADDR(0x44a); + * rows: *(u_int8_t *)BIOS_PADDRTOVADDR(0x484); + * font size: *(u_int8_t *)BIOS_PADDRTOVADDR(0x485); + * buffer size: *(u_int16_t *)BIOS_PADDRTOVADDR(0x44c); + */ + + return biosadapters; +} + +/* entry points */ + +static int +vga_nop(void) +{ + return 0; +} + +static int +vga_probe(int unit, video_adapter_t **adpp, void *arg, int flags) +{ + probe_adapters(); + if (unit >= biosadapters) + return ENXIO; + + *adpp = &biosadapter[unit]; + + return 0; +} + +static int +vga_init(int unit, video_adapter_t *adp, int flags) +{ + if ((unit >= biosadapters) || (adp == NULL) || !probe_done(adp)) + return ENXIO; + + if (!init_done(adp)) { + /* nothing to do really... */ + adp->va_flags |= V_ADP_INITIALIZED; + } + + if (!config_done(adp)) { + if (vid_register(adp) < 0) + return ENXIO; + adp->va_flags |= V_ADP_REGISTERED; + } + if (vga_sub_configure != NULL) + (*vga_sub_configure)(0); + + return 0; +} + +/* + * get_info(): + * Return the video_info structure of the requested video mode. + * + * all adapters + */ +static int +vga_get_info(video_adapter_t *adp, int mode, video_info_t *info) +{ + int i; + + if (!init_done) + return 1; + + mode = map_gen_mode_num(adp->va_type, adp->va_flags & V_ADP_COLOR, mode); +#ifndef VGA_NO_MODE_CHANGE + if (adp->va_flags & V_ADP_MODECHANGE) { + /* + * If the parameter table entry for this mode is not found, + * the mode is not supported... + */ + if (get_mode_param(mode) == NULL) + return 1; + } else +#endif /* VGA_NO_MODE_CHANGE */ + { + /* + * Even if we don't support video mode switching on this adapter, + * the information on the initial (thus current) video mode + * should be made available. + */ + if (mode != adp->va_initial_mode) + return 1; + } + + for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { + if (bios_vmode[i].vi_mode == NA) + continue; + if (mode == bios_vmode[i].vi_mode) { + *info = bios_vmode[i]; + return 0; + } + } + return 1; +} + +/* + * query_mode(): + * Find a video mode matching the requested parameters. + * Fields filled with 0 are considered "don't care" fields and + * match any modes. + * + * all adapters + */ +static int +vga_query_mode(video_adapter_t *adp, video_info_t *info) +{ + video_info_t buf; + int i; + + if (!init_done) + return -1; + + for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { + if (bios_vmode[i].vi_mode == NA) + continue; + + if ((info->vi_width != 0) + && (info->vi_width != bios_vmode[i].vi_width)) + continue; + if ((info->vi_height != 0) + && (info->vi_height != bios_vmode[i].vi_height)) + continue; + if ((info->vi_cwidth != 0) + && (info->vi_cwidth != bios_vmode[i].vi_cwidth)) + continue; + if ((info->vi_cheight != 0) + && (info->vi_cheight != bios_vmode[i].vi_cheight)) + continue; + if ((info->vi_depth != 0) + && (info->vi_depth != bios_vmode[i].vi_depth)) + continue; + if ((info->vi_planes != 0) + && (info->vi_planes != bios_vmode[i].vi_planes)) + continue; + /* XXX: should check pixel format, memory model */ + if ((info->vi_flags != 0) + && (info->vi_flags != bios_vmode[i].vi_flags)) + continue; + + /* verify if this mode is supported on this adapter */ + if (vga_get_info(adp, bios_vmode[i].vi_mode, &buf)) + continue; + return bios_vmode[i].vi_mode; + } + return -1; +} + +/* + * set_mode(): + * Change the video mode. + * + * EGA/VGA + */ +static int +vga_set_mode(video_adapter_t *adp, int mode) +{ +#ifndef VGA_NO_MODE_CHANGE + video_info_t info; + adp_state_t params; + + prologue(adp, V_ADP_MODECHANGE, 1); + + mode = map_gen_mode_num(adp->va_type, + adp->va_flags & V_ADP_COLOR, mode); + if (vga_get_info(adp, mode, &info)) + return 1; + params.sig = V_STATE_SIG; + bcopy(get_mode_param(mode), params.regs, sizeof(params.regs)); + + switch (mode) { + case M_VGA_C80x60: case M_VGA_M80x60: + params.regs[2] = 0x08; + params.regs[19] = 0x47; + goto special_480l; + + case M_VGA_C80x30: case M_VGA_M80x30: + params.regs[19] = 0x4f; +special_480l: + params.regs[9] |= 0xc0; + params.regs[16] = 0x08; + params.regs[17] = 0x3e; + params.regs[26] = 0xea; + params.regs[28] = 0xdf; + params.regs[31] = 0xe7; + params.regs[32] = 0x04; + goto setup_mode; + + case M_ENH_C80x43: case M_ENH_B80x43: + params.regs[28] = 87; + goto special_80x50; + + case M_VGA_C80x50: case M_VGA_M80x50: +special_80x50: + params.regs[2] = 8; + params.regs[19] = 7; + goto setup_mode; + + case M_VGA_C40x25: case M_VGA_C80x25: + case M_VGA_M80x25: + case M_B40x25: case M_C40x25: + case M_B80x25: case M_C80x25: + case M_ENH_B40x25: case M_ENH_C40x25: + case M_ENH_B80x25: case M_ENH_C80x25: + case M_EGAMONO80x25: + +setup_mode: + vga_load_state(adp, ¶ms); + break; + + case M_VGA_MODEX: + /* "unchain" the VGA mode */ + params.regs[5-1+0x04] &= 0xf7; + params.regs[5-1+0x04] |= 0x04; + /* turn off doubleword mode */ + params.regs[10+0x14] &= 0xbf; + /* turn off word adressing */ + params.regs[10+0x17] |= 0x40; + /* set logical screen width */ + params.regs[10+0x13] = 80; + /* set 240 lines */ + params.regs[10+0x11] = 0x2c; + params.regs[10+0x06] = 0x0d; + params.regs[10+0x07] = 0x3e; + params.regs[10+0x10] = 0xea; + params.regs[10+0x11] = 0xac; + params.regs[10+0x12] = 0xdf; + params.regs[10+0x15] = 0xe7; + params.regs[10+0x16] = 0x06; + /* set vertical sync polarity to reflect aspect ratio */ + params.regs[9] = 0xe3; + goto setup_grmode; + + case M_BG320: case M_CG320: case M_BG640: + case M_CG320_D: case M_CG640_E: + case M_CG640x350: case M_ENH_CG640: + case M_BG640x480: case M_CG640x480: case M_VGA_CG320: + +setup_grmode: + vga_load_state(adp, ¶ms); + break; + + default: + return 1; + } + + adp->va_mode = mode; + adp->va_mode_flags = info.vi_flags; + adp->va_flags &= ~V_ADP_COLOR; + adp->va_flags |= + (info.vi_flags & V_INFO_COLOR) ? V_ADP_COLOR : 0; + adp->va_crtc_addr = + (adp->va_flags & V_ADP_COLOR) ? COLOR_CRTC : MONO_CRTC; + adp->va_window = BIOS_PADDRTOVADDR(info.vi_window); + adp->va_window_size = info.vi_window_size; + adp->va_window_gran = info.vi_window_gran; + if (info.vi_buffer_size == 0) { + adp->va_buffer = 0; + adp->va_buffer_size = 0; + } else { + adp->va_buffer = BIOS_PADDRTOVADDR(info.vi_buffer); + adp->va_buffer_size = info.vi_buffer_size; + } + + /* move hardware cursor out of the way */ + (*vidsw[adp->va_index]->set_hw_cursor)(adp, -1, -1); + + return 0; +#else /* VGA_NO_MODE_CHANGE */ + return 1; +#endif /* VGA_NO_MODE_CHANGE */ +} + +#ifndef VGA_NO_FONT_LOADING + +static void +set_font_mode(video_adapter_t *adp, u_char *buf) +{ + u_char *mp; + int s; + + s = splhigh(); + + /* save register values */ + if (adp->va_type == KD_VGA) { + outb(TSIDX, 0x02); buf[0] = inb(TSREG); + outb(TSIDX, 0x04); buf[1] = inb(TSREG); + outb(GDCIDX, 0x04); buf[2] = inb(GDCREG); + outb(GDCIDX, 0x05); buf[3] = inb(GDCREG); + outb(GDCIDX, 0x06); buf[4] = inb(GDCREG); + inb(adp->va_crtc_addr + 6); + outb(ATC, 0x10); buf[5] = inb(ATC + 1); + } else /* if (adp->va_type == KD_EGA) */ { + /* + * EGA cannot be read; copy parameters from the mode parameter + * table. + */ + mp = get_mode_param(adp->va_mode); + buf[0] = mp[5 + 0x02 - 1]; + buf[1] = mp[5 + 0x04 - 1]; + buf[2] = mp[55 + 0x04]; + buf[3] = mp[55 + 0x05]; + buf[4] = mp[55 + 0x06]; + buf[5] = mp[35 + 0x10]; + } + + /* setup vga for loading fonts */ + inb(adp->va_crtc_addr + 6); /* reset flip-flop */ + outb(ATC, 0x10); outb(ATC, buf[5] & ~0x01); + inb(adp->va_crtc_addr + 6); /* reset flip-flop */ + outb(ATC, 0x20); /* enable palette */ + +#if VGA_SLOW_IOACCESS +#ifdef VGA_ALT_SEQACCESS + outb(TSIDX, 0x00); outb(TSREG, 0x01); +#endif + outb(TSIDX, 0x02); outb(TSREG, 0x04); + outb(TSIDX, 0x04); outb(TSREG, 0x07); +#ifdef VGA_ALT_SEQACCESS + outb(TSIDX, 0x00); outb(TSREG, 0x03); +#endif + outb(GDCIDX, 0x04); outb(GDCREG, 0x02); + outb(GDCIDX, 0x05); outb(GDCREG, 0x00); + outb(GDCIDX, 0x06); outb(GDCREG, 0x04); +#else /* VGA_SLOW_IOACCESS */ +#ifdef VGA_ALT_SEQACCESS + outw(TSIDX, 0x0100); +#endif + outw(TSIDX, 0x0402); + outw(TSIDX, 0x0704); +#ifdef VGA_ALT_SEQACCESS + outw(TSIDX, 0x0300); +#endif + outw(GDCIDX, 0x0204); + outw(GDCIDX, 0x0005); + outw(GDCIDX, 0x0406); /* addr = a0000, 64kb */ +#endif /* VGA_SLOW_IOACCESS */ + + splx(s); +} + +static void +set_normal_mode(video_adapter_t *adp, u_char *buf) +{ + int s; + + s = splhigh(); + + /* setup vga for normal operation mode again */ + inb(adp->va_crtc_addr + 6); /* reset flip-flop */ + outb(ATC, 0x10); outb(ATC, buf[5]); + inb(adp->va_crtc_addr + 6); /* reset flip-flop */ + outb(ATC, 0x20); /* enable palette */ + +#if VGA_SLOW_IOACCESS +#ifdef VGA_ALT_SEQACCESS + outb(TSIDX, 0x00); outb(TSREG, 0x01); +#endif + outb(TSIDX, 0x02); outb(TSREG, buf[0]); + outb(TSIDX, 0x04); outb(TSREG, buf[1]); +#ifdef VGA_ALT_SEQACCESS + outb(TSIDX, 0x00); outb(TSREG, 0x03); +#endif + outb(GDCIDX, 0x04); outb(GDCREG, buf[2]); + outb(GDCIDX, 0x05); outb(GDCREG, buf[3]); + if (adp->va_crtc_addr == MONO_CRTC) { + outb(GDCIDX, 0x06); outb(GDCREG,(buf[4] & 0x03) | 0x08); + } else { + outb(GDCIDX, 0x06); outb(GDCREG,(buf[4] & 0x03) | 0x0c); + } +#else /* VGA_SLOW_IOACCESS */ +#ifdef VGA_ALT_SEQACCESS + outw(TSIDX, 0x0100); +#endif + outw(TSIDX, 0x0002 | (buf[0] << 8)); + outw(TSIDX, 0x0004 | (buf[1] << 8)); +#ifdef VGA_ALT_SEQACCESS + outw(TSIDX, 0x0300); +#endif + outw(GDCIDX, 0x0004 | (buf[2] << 8)); + outw(GDCIDX, 0x0005 | (buf[3] << 8)); + if (adp->va_crtc_addr == MONO_CRTC) + outw(GDCIDX, 0x0006 | (((buf[4] & 0x03) | 0x08)<<8)); + else + outw(GDCIDX, 0x0006 | (((buf[4] & 0x03) | 0x0c)<<8)); +#endif /* VGA_SLOW_IOACCESS */ + + splx(s); +} + +#endif /* VGA_NO_FONT_LOADING */ + +/* + * save_font(): + * Read the font data in the requested font page from the video adapter. + * + * EGA/VGA + */ +static int +vga_save_font(video_adapter_t *adp, int page, int fontsize, u_char *data, + int ch, int count) +{ +#ifndef VGA_NO_FONT_LOADING + u_char buf[PARAM_BUFSIZE]; + u_int32_t segment; + int c; +#ifdef VGA_ALT_SEQACCESS + int s; + u_char val = 0; +#endif + + prologue(adp, V_ADP_FONT, 1); + + if (fontsize < 14) { + /* FONT_8 */ + fontsize = 8; + } else if (fontsize >= 32) { + fontsize = 32; + } else if (fontsize >= 16) { + /* FONT_16 */ + fontsize = 16; + } else { + /* FONT_14 */ + fontsize = 14; + } + + if (page < 0 || page >= 8) + return 1; + segment = FONT_BUF + 0x4000*page; + if (page > 3) + segment -= 0xe000; + +#ifdef VGA_ALT_SEQACCESS + if (adp->va_type == KD_VGA) { /* what about EGA? XXX */ + s = splhigh(); + outb(TSIDX, 0x00); outb(TSREG, 0x01); + outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */ + outb(TSIDX, 0x01); outb(TSREG, val | 0x20); + outb(TSIDX, 0x00); outb(TSREG, 0x03); + splx(s); + } +#endif + + set_font_mode(adp, buf); + if (fontsize == 32) { + bcopy_fromio((void *)(segment + ch*32), data, fontsize*count); + } else { + for (c = ch; count > 0; ++c, --count) { + bcopy_fromio((void *)(segment + c*32), data, fontsize); + data += fontsize; + } + } + set_normal_mode(adp, buf); + +#ifdef VGA_ALT_SEQACCESS + if (adp->va_type == KD_VGA) { + s = splhigh(); + outb(TSIDX, 0x00); outb(TSREG, 0x01); + outb(TSIDX, 0x01); outb(TSREG, val & 0xdf); /* enable screen */ + outb(TSIDX, 0x00); outb(TSREG, 0x03); + splx(s); + } +#endif + + return 0; +#else /* VGA_NO_FONT_LOADING */ + return 1; +#endif /* VGA_NO_FONT_LOADING */ +} + +/* + * load_font(): + * Set the font data in the requested font page. + * NOTE: it appears that some recent video adapters do not support + * the font page other than 0... XXX + * + * EGA/VGA + */ +static int +vga_load_font(video_adapter_t *adp, int page, int fontsize, u_char *data, + int ch, int count) +{ +#ifndef VGA_NO_FONT_LOADING + u_char buf[PARAM_BUFSIZE]; + u_int32_t segment; + int c; +#ifdef VGA_ALT_SEQACCESS + int s; + u_char val = 0; +#endif + + prologue(adp, V_ADP_FONT, 1); + + if (fontsize < 14) { + /* FONT_8 */ + fontsize = 8; + } else if (fontsize >= 32) { + fontsize = 32; + } else if (fontsize >= 16) { + /* FONT_16 */ + fontsize = 16; + } else { + /* FONT_14 */ + fontsize = 14; + } + + if (page < 0 || page >= 8) + return 1; + segment = FONT_BUF + 0x4000*page; + if (page > 3) + segment -= 0xe000; + +#ifdef VGA_ALT_SEQACCESS + if (adp->va_type == KD_VGA) { /* what about EGA? XXX */ + s = splhigh(); + outb(TSIDX, 0x00); outb(TSREG, 0x01); + outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */ + outb(TSIDX, 0x01); outb(TSREG, val | 0x20); + outb(TSIDX, 0x00); outb(TSREG, 0x03); + splx(s); + } +#endif + + set_font_mode(adp, buf); + if (fontsize == 32) { + bcopy_toio(data, (void *)(segment + ch*32), fontsize*count); + } else { + for (c = ch; count > 0; ++c, --count) { + bcopy_toio(data, (void *)(segment + c*32), fontsize); + data += fontsize; + } + } + set_normal_mode(adp, buf); + +#ifdef VGA_ALT_SEQACCESS + if (adp->va_type == KD_VGA) { + s = splhigh(); + outb(TSIDX, 0x00); outb(TSREG, 0x01); + outb(TSIDX, 0x01); outb(TSREG, val & 0xdf); /* enable screen */ + outb(TSIDX, 0x00); outb(TSREG, 0x03); + splx(s); + } +#endif + + return 0; +#else /* VGA_NO_FONT_LOADING */ + return 1; +#endif /* VGA_NO_FONT_LOADING */ +} + +/* + * show_font(): + * Activate the requested font page. + * NOTE: it appears that some recent video adapters do not support + * the font page other than 0... XXX + * + * EGA/VGA + */ +static int +vga_show_font(video_adapter_t *adp, int page) +{ +#ifndef VGA_NO_FONT_LOADING + static u_char cg[] = { 0x00, 0x05, 0x0a, 0x0f, 0x30, 0x35, 0x3a, 0x3f }; + int s; + + prologue(adp, V_ADP_FONT, 1); + if (page < 0 || page >= 8) + return 1; + + s = splhigh(); + outb(TSIDX, 0x03); outb(TSREG, cg[page]); + splx(s); + + return 0; +#else /* VGA_NO_FONT_LOADING */ + return 1; +#endif /* VGA_NO_FONT_LOADING */ +} + +/* + * save_palette(): + * Read DAC values. The values have expressed in 8 bits. + * + * VGA + */ +static int +vga_save_palette(video_adapter_t *adp, u_char *palette) +{ + int i; + + prologue(adp, V_ADP_PALETTE, 1); + + /* + * We store 8 bit values in the palette buffer, while the standard + * VGA has 6 bit DAC . + */ + outb(PALRADR, 0x00); + for (i = 0; i < 256*3; ++i) + palette[i] = inb(PALDATA) << 2; + inb(adp->va_crtc_addr + 6); /* reset flip/flop */ + return 0; +} + +/* + * load_palette(): + * Set DAC values. + * + * VGA + */ +static int +vga_load_palette(video_adapter_t *adp, u_char *palette) +{ + int i; + + prologue(adp, V_ADP_PALETTE, 1); + + outb(PIXMASK, 0xff); /* no pixelmask */ + outb(PALWADR, 0x00); + for (i = 0; i < 256*3; ++i) + outb(PALDATA, palette[i] >> 2); + inb(adp->va_crtc_addr + 6); /* reset flip/flop */ + outb(ATC, 0x20); /* enable palette */ + return 0; +} + +/* + * set_border(): + * Change the border color. + * + * CGA/EGA/VGA + */ +static int +vga_set_border(video_adapter_t *adp, int color) +{ + prologue(adp, V_ADP_BORDER, 1); + + switch (adp->va_type) { + case KD_EGA: + case KD_VGA: + inb(adp->va_crtc_addr + 6); /* reset flip-flop */ + outb(ATC, 0x31); outb(ATC, color & 0xff); + break; + case KD_CGA: + outb(adp->va_crtc_addr + 5, color & 0x0f); /* color select register */ + break; + case KD_MONO: + case KD_HERCULES: + default: + break; + } + return 0; +} + +/* + * save_state(): + * Read video register values. + * NOTE: this function only reads the standard EGA/VGA registers. + * any extra/extended registers of SVGA adapters are not saved. + * + * VGA + */ +static int +vga_save_state(video_adapter_t *adp, void *p, size_t size) +{ + video_info_t info; + u_char *buf; + int crtc_addr; + int i, j; + int s; + + if (size == 0) { + /* return the required buffer size */ + prologue(adp, V_ADP_STATESAVE, 0); + return sizeof(adp_state_t); + } else { + prologue(adp, V_ADP_STATESAVE, 1); + if (size < sizeof(adp_state_t)) + return 1; + } + + ((adp_state_t *)p)->sig = V_STATE_SIG; + buf = ((adp_state_t *)p)->regs; + bzero(buf, V_MODE_PARAM_SIZE); + crtc_addr = adp->va_crtc_addr; + + s = splhigh(); + + outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */ + for (i = 0, j = 5; i < 4; i++) { + outb(TSIDX, i + 1); + buf[j++] = inb(TSREG); + } + buf[9] = inb(MISC + 10); /* dot-clock */ + outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */ + + for (i = 0, j = 10; i < 25; i++) { /* crtc */ + outb(crtc_addr, i); + buf[j++] = inb(crtc_addr + 1); + } + for (i = 0, j = 35; i < 20; i++) { /* attribute ctrl */ + inb(crtc_addr + 6); /* reset flip-flop */ + outb(ATC, i); + buf[j++] = inb(ATC + 1); + } + for (i = 0, j = 55; i < 9; i++) { /* graph data ctrl */ + outb(GDCIDX, i); + buf[j++] = inb(GDCREG); + } + inb(crtc_addr + 6); /* reset flip-flop */ + outb(ATC, 0x20); /* enable palette */ + + splx(s); + +#if 1 + if (vga_get_info(adp, adp->va_mode, &info) == 0) { + if (info.vi_flags & V_INFO_GRAPHICS) { + buf[0] = info.vi_width/info.vi_cwidth; /* COLS */ + buf[1] = info.vi_height/info.vi_cheight - 1; /* ROWS */ + } else { + buf[0] = info.vi_width; /* COLS */ + buf[1] = info.vi_height - 1; /* ROWS */ + } + buf[2] = info.vi_cheight; /* POINTS */ + } else { + /* XXX: shouldn't be happening... */ + printf("vga%d: %s: failed to obtain mode info. (vga_save_state())\n", + adp->va_unit, adp->va_name); + } +#else + buf[0] = readb(BIOS_PADDRTOVADDR(0x44a)); /* COLS */ + buf[1] = readb(BIOS_PADDRTOVADDR(0x484)); /* ROWS */ + buf[2] = readb(BIOS_PADDRTOVADDR(0x485)); /* POINTS */ + buf[3] = readb(BIOS_PADDRTOVADDR(0x44c)); + buf[4] = readb(BIOS_PADDRTOVADDR(0x44d)); +#endif + + return 0; +} + +/* + * load_state(): + * Set video registers at once. + * NOTE: this function only updates the standard EGA/VGA registers. + * any extra/extended registers of SVGA adapters are not changed. + * + * EGA/VGA + */ +static int +vga_load_state(video_adapter_t *adp, void *p) +{ + u_char *buf; + int crtc_addr; + int s; + int i; + + prologue(adp, V_ADP_STATELOAD, 1); + if (((adp_state_t *)p)->sig != V_STATE_SIG) + return 1; + + buf = ((adp_state_t *)p)->regs; + crtc_addr = adp->va_crtc_addr; + + s = splhigh(); + + outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */ + for (i = 0; i < 4; ++i) { /* program sequencer */ + outb(TSIDX, i + 1); + outb(TSREG, buf[i + 5]); + } + outb(MISC, buf[9]); /* set dot-clock */ + outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */ + outb(crtc_addr, 0x11); + outb(crtc_addr + 1, inb(crtc_addr + 1) & 0x7F); + for (i = 0; i < 25; ++i) { /* program crtc */ + outb(crtc_addr, i); + outb(crtc_addr + 1, buf[i + 10]); + } + inb(crtc_addr+6); /* reset flip-flop */ + for (i = 0; i < 20; ++i) { /* program attribute ctrl */ + outb(ATC, i); + outb(ATC, buf[i + 35]); + } + for (i = 0; i < 9; ++i) { /* program graph data ctrl */ + outb(GDCIDX, i); + outb(GDCREG, buf[i + 55]); + } + inb(crtc_addr + 6); /* reset flip-flop */ + outb(ATC, 0x20); /* enable palette */ + +#ifndef VGA_NO_BIOS + if (adp->va_unit == V_ADP_PRIMARY) { + writeb(BIOS_PADDRTOVADDR(0x44a), buf[0]); /* COLS */ + writeb(BIOS_PADDRTOVADDR(0x484), buf[1] + rows_offset - 1); /* ROWS */ + writeb(BIOS_PADDRTOVADDR(0x485), buf[2]); /* POINTS */ +#if 0 + writeb(BIOS_PADDRTOVADDR(0x44c), buf[3]); + writeb(BIOS_PADDRTOVADDR(0x44d), buf[4]); +#endif + } +#endif /* VGA_NO_BIOS */ + + splx(s); + return 0; +} + +/* + * set_origin(): + * Change the origin (window mapping) of the banked frame buffer. + */ +static int +vga_set_origin(video_adapter_t *adp, off_t offset) +{ + /* + * The standard video modes do not require window mapping; + * always return error. + */ + return 1; +} + +/* + * read_hw_cursor(): + * Read the position of the hardware text cursor. + * + * all adapters + */ +static int +vga_read_hw_cursor(video_adapter_t *adp, int *col, int *row) +{ + video_info_t info; + u_int16_t off; + int s; + + if (!init_done) + return 1; + + (*vidsw[adp->va_index]->get_info)(adp, adp->va_mode, &info); + if (info.vi_flags & V_INFO_GRAPHICS) + return 1; + + s = spltty(); + outb(adp->va_crtc_addr, 14); + off = inb(adp->va_crtc_addr + 1); + outb(adp->va_crtc_addr, 15); + off = (off << 8) | inb(adp->va_crtc_addr + 1); + splx(s); + + *row = off / info.vi_width; + *col = off % info.vi_width; + + return 0; +} + +/* + * set_hw_cursor(): + * Move the hardware text cursor. If col and row are both -1, + * the cursor won't be shown. + * + * all adapters + */ +static int +vga_set_hw_cursor(video_adapter_t *adp, int col, int row) +{ + video_info_t info; + u_int16_t off; + int s; + + if (!init_done) + return 1; + + if ((col == -1) && (row == -1)) { + off = -1; + } else { + (*vidsw[adp->va_index]->get_info)(adp, adp->va_mode, &info); + if (info.vi_flags & V_INFO_GRAPHICS) + return 1; + off = row*info.vi_width + col; + } + + s = spltty(); + outb(adp->va_crtc_addr, 14); + outb(adp->va_crtc_addr + 1, off >> 8); + outb(adp->va_crtc_addr, 15); + outb(adp->va_crtc_addr + 1, off & 0x00ff); + splx(s); + + return 0; +} + +/* + * set_hw_cursor_shape(): + * Change the shape of the hardware text cursor. If the height is + * zero or negative, the cursor won't be shown. + * + * all adapters + */ +static int +vga_set_hw_cursor_shape(video_adapter_t *adp, int base, int height, + int celsize, int blink) +{ + int s; + + if (!init_done) + return 1; + + s = spltty(); + switch (adp->va_type) { + case KD_VGA: + case KD_CGA: + case KD_MONO: + case KD_HERCULES: + default: + if (height <= 0) { + /* make the cursor invisible */ + outb(adp->va_crtc_addr, 10); + outb(adp->va_crtc_addr + 1, 32); + outb(adp->va_crtc_addr, 11); + outb(adp->va_crtc_addr + 1, 0); + } else { + outb(adp->va_crtc_addr, 10); + outb(adp->va_crtc_addr + 1, celsize - base - height); + outb(adp->va_crtc_addr, 11); + outb(adp->va_crtc_addr + 1, celsize - base - 1); + } + break; + case KD_EGA: + if (height <= 0) { + /* make the cursor invisible */ + outb(adp->va_crtc_addr, 10); + outb(adp->va_crtc_addr + 1, celsize); + outb(adp->va_crtc_addr, 11); + outb(adp->va_crtc_addr + 1, 0); + } else { + outb(adp->va_crtc_addr, 10); + outb(adp->va_crtc_addr + 1, celsize - base - height); + outb(adp->va_crtc_addr, 11); + outb(adp->va_crtc_addr + 1, celsize - base); + } + break; + } + splx(s); + + return 0; +} + +/* + * mmap(): + * Mmap frame buffer. + * + * all adapters + */ +static int +vga_mmap(video_adapter_t *adp, vm_offset_t offset) +{ + if (offset > 0x20000 - PAGE_SIZE) + return -1; +#ifdef __i386__ + return i386_btop((VIDEO_BUF_BASE + offset)); +#endif +#ifdef __alpha__ + return alpha_btop((VIDEO_BUF_BASE + offset)); +#endif +} + +static void +dump_buffer(u_char *buf, size_t len) +{ + int i; + + for(i = 0; i < len;) { + printf("%02x ", buf[i]); + if ((++i % 16) == 0) + printf("\n"); + } +} + +/* + * diag(): + * Print some information about the video adapter and video modes, + * with requested level of details. + * + * all adapters + */ +static int +vga_diag(video_adapter_t *adp, int level) +{ +#if FB_DEBUG > 1 + video_info_t info; +#endif + u_char *mp; + + if (!init_done) + return 1; + +#if FB_DEBUG > 1 +#ifndef VGA_NO_BIOS + printf("vga: RTC equip. code:0x%02x, DCC code:0x%02x\n", + rtcin(RTC_EQUIPMENT), readb(BIOS_PADDRTOVADDR(0x488))); + printf("vga: CRTC:0x%x, video option:0x%02x, ", + readw(BIOS_PADDRTOVADDR(0x463)), + readb(BIOS_PADDRTOVADDR(0x487))); + printf("rows:%d, cols:%d, font height:%d\n", + readb(BIOS_PADDRTOVADDR(0x44a)), + readb(BIOS_PADDRTOVADDR(0x484)) + 1, + readb(BIOS_PADDRTOVADDR(0x485))); +#endif /* VGA_NO_BIOS */ + printf("vga: param table EGA/VGA:%p", video_mode_ptr); + printf(", CGA/MDA:%p\n", video_mode_ptr2); + printf("vga: rows_offset:%d\n", rows_offset); +#endif /* FB_DEBUG > 1 */ + + fb_dump_adp_info(DRIVER_NAME, adp, level); + +#if FB_DEBUG > 1 + if (adp->va_flags & V_ADP_MODECHANGE) { + for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { + if (bios_vmode[i].vi_mode == NA) + continue; + if (get_mode_param(bios_vmode[i].vi_mode) == NULL) + continue; + fb_dump_mode_info(DRIVER_NAME, adp, &bios_vmode[i], level); + } + } else { + vga_get_info(adp, adp->va_initial_mode, &info); /* shouldn't fail */ + fb_dump_mode_info(DRIVER_NAME, adp, &info, level); + } +#endif /* FB_DEBUG > 1 */ + + if ((adp->va_type != KD_EGA) && (adp->va_type != KD_VGA)) + return 0; +#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) + if (video_mode_ptr == NULL) + printf("vga%d: %s: WARNING: video mode switching is not " + "fully supported on this adapter\n", + adp->va_unit, adp->va_name); +#endif + if (level <= 0) + return 0; + + if (adp->va_type == KD_VGA) { + printf("VGA parameters upon power-up\n"); + dump_buffer(adpstate.regs, sizeof(adpstate.regs)); + printf("VGA parameters in BIOS for mode %d\n", adp->va_initial_mode); + dump_buffer(adpstate2.regs, sizeof(adpstate2.regs)); + } + + mp = get_mode_param(adp->va_initial_mode); + if (mp == NULL) /* this shouldn't be happening */ + return 0; + printf("EGA/VGA parameters to be used for mode %d\n", adp->va_initial_mode); + dump_buffer(mp, V_MODE_PARAM_SIZE); + + return 0; +} + +#endif /* NVGA > 0 */