diff options
author | kato <kato@FreeBSD.org> | 1999-01-18 08:38:08 +0000 |
---|---|---|
committer | kato <kato@FreeBSD.org> | 1999-01-18 08:38:08 +0000 |
commit | fddf52b2982eec73bbeea347ffa2efdd2ac37892 (patch) | |
tree | 07a5cdc3b06ee285a1f098304d1ea13d945207a5 /sys/pc98/cbus/pckbd.c | |
parent | ac7ffa4b3b3eb19d1ebe4c6d7437b8924541bede (diff) | |
download | FreeBSD-src-fddf52b2982eec73bbeea347ffa2efdd2ac37892.zip FreeBSD-src-fddf52b2982eec73bbeea347ffa2efdd2ac37892.tar.gz |
Switched to new syscons driver.
Submitted by: NOKUBI Hirotaka <hnokubi@yyy.or.jp> and
Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
Diffstat (limited to 'sys/pc98/cbus/pckbd.c')
-rw-r--r-- | sys/pc98/cbus/pckbd.c | 983 |
1 files changed, 983 insertions, 0 deletions
diff --git a/sys/pc98/cbus/pckbd.c b/sys/pc98/cbus/pckbd.c new file mode 100644 index 0000000..8b1a13c --- /dev/null +++ b/sys/pc98/cbus/pckbd.c @@ -0,0 +1,983 @@ +/*- + * $Id$ + */ + +#include "pckbd.h" +#include "opt_kbd.h" +#include "opt_devfs.h" + +#if NPCKBD > 0 + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/tty.h> +#include <sys/fcntl.h> +#include <sys/bus.h> +#include <sys/malloc.h> + +#include <machine/resource.h> + +#include <dev/kbd/kbdreg.h> + +#include <pc98/pc98/pc98.h> +#include <pc98/pc98/pc98_machdep.h> + +#ifdef __i386__ +#include <i386/isa/isa_device.h> +#endif + +#define DRIVER_NAME "pckbd" + +/* device configuration flags */ +#define KB_CONF_FAIL_IF_NO_KBD (1 << 0) /* don't install if no kbd is found */ + +/* some macros */ +#define PC98KBD_UNIT(dev) minor(dev) +#define PC98KBD_MKMINOR(unit) (unit) + +/* cdev driver declaration */ + +typedef struct pckbd_softc { + short flags; +#define PC98KBD_ATTACHED (1 << 0) + keyboard_t *kbd; +#ifdef KBD_INSTALL_CDEV + genkbd_softc_t gensc; +#endif +} pckbd_softc_t; + +#define PC98KBD_SOFTC(unit) pckbd_softc[(unit)] + +static pckbd_softc_t *pckbd_softc[NPCKBD]; + +static int pckbdprobe(struct isa_device *dev); +static int pckbdattach(struct isa_device *dev); + +static ointhand2_t pckbd_isa_intr; + +/* driver declaration for isa_devtab_tty[] */ +struct isa_driver pckbddriver = { + pckbdprobe, + pckbdattach, + DRIVER_NAME, + 0, +}; + +static int pckbd_probe_unit(int unit, pckbd_softc_t *sc, + int port, int irq, int flags); +static int pckbd_attach_unit(int unit, pckbd_softc_t *sc); +static timeout_t pckbd_timeout; + +#ifdef KBD_INSTALL_CDEV + +static d_open_t pckbdopen; +static d_close_t pckbdclose; +static d_read_t pckbdread; +static d_ioctl_t pckbdioctl; +static d_poll_t pckbdpoll; + +static struct cdevsw pckbd_cdevsw = { + pckbdopen, pckbdclose, pckbdread, nowrite, + pckbdioctl, nostop, nullreset, nodevtotty, + pckbdpoll, nommap, NULL, DRIVER_NAME, + NULL, -1, +}; + +#endif /* KBD_INSTALL_CDEV */ + +static int +pckbdprobe(struct isa_device *dev) +{ + pckbd_softc_t *sc; + int error; + + if (dev->id_unit >= sizeof(pckbd_softc)/sizeof(pckbd_softc[0])) + return 0; + sc = pckbd_softc[dev->id_unit]; + if (sc == NULL) { + sc = pckbd_softc[dev->id_unit] + = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); + if (sc == NULL) + return 0; + bzero(sc, sizeof(*sc)); + } + + /* try to find a keyboard */ + error = pckbd_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 = pckbd_isa_intr; + + return IO_KBDSIZE; +} + +static int +pckbdattach(struct isa_device *dev) +{ + pckbd_softc_t *sc; + + if (dev->id_unit >= sizeof(pckbd_softc)/sizeof(pckbd_softc[0])) + return 0; + sc = pckbd_softc[dev->id_unit]; + if (sc == NULL) + return 0; + + return ((pckbd_attach_unit(dev->id_unit, sc)) ? 0 : 1); +} + +static void +pckbd_isa_intr(int unit) +{ + keyboard_t *kbd; + + kbd = pckbd_softc[unit]->kbd; + (*kbdsw[kbd->kb_index]->intr)(kbd, NULL); +} + +static int +pckbd_probe_unit(int unit, pckbd_softc_t *sc, int port, int irq, int flags) +{ + keyboard_switch_t *sw; + int args[2]; + + if (sc->flags & PC98KBD_ATTACHED) + return 0; + + sw = kbd_get_switch(DRIVER_NAME); + if (sw == NULL) + return ENXIO; + + args[0] = port; + args[1] = irq; + return (*sw->probe)(unit, &sc->kbd, args, flags); +} + +static int +pckbd_attach_unit(int unit, pckbd_softc_t *sc) +{ + keyboard_switch_t *sw; + int error; + + if (sc->flags & PC98KBD_ATTACHED) + return 0; + + sw = kbd_get_switch(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, PC98KBD_MKMINOR(unit)), sc->kbd, + &pckbd_cdevsw); + if (error) + return error; +#endif /* KBD_INSTALL_CDEV */ + + /* + * This is a kludge to compensate for lost keyboard interrupts. + * A similar code used to be in syscons. See below. XXX + */ + pckbd_timeout(sc->kbd); + + if (bootverbose) + (*sw->diag)(sc->kbd, bootverbose); + + sc->flags |= PC98KBD_ATTACHED; + return 0; +} + +static void +pckbd_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(pckbd_timeout, arg, hz/10); +} + +/* cdev driver functions */ + +#ifdef KBD_INSTALL_CDEV + +static int +pckbdopen(dev_t dev, int flag, int mode, struct proc *p) +{ + pckbd_softc_t *sc; + int unit; + + unit = PC98KBD_UNIT(dev); + if ((unit >= NPCKBD) || ((sc = PC98KBD_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 +pckbdclose(dev_t dev, int flag, int mode, struct proc *p) +{ + pckbd_softc_t *sc; + + sc = PC98KBD_SOFTC(PC98KBD_UNIT(dev)); + return genkbdclose(&sc->gensc, sc->kbd, flag, mode, p); +} + +static int +pckbdread(dev_t dev, struct uio *uio, int flag) +{ + pckbd_softc_t *sc; + + sc = PC98KBD_SOFTC(PC98KBD_UNIT(dev)); + return genkbdread(&sc->gensc, sc->kbd, uio, flag); +} + +static int +pckbdioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p) +{ + pckbd_softc_t *sc; + + sc = PC98KBD_SOFTC(PC98KBD_UNIT(dev)); + return genkbdioctl(&sc->gensc, sc->kbd, cmd, arg, flag, p); +} + +static int +pckbdpoll(dev_t dev, int event, struct proc *p) +{ + pckbd_softc_t *sc; + + sc = PC98KBD_SOFTC(PC98KBD_UNIT(dev)); + return genkbdpoll(&sc->gensc, sc->kbd, event, p); +} + +#endif /* KBD_INSTALL_CDEV */ + +/* LOW-LEVEL */ + +#include <machine/limits.h> +#include <machine/console.h> +#include <machine/clock.h> + +#define PC98KBD_DEFAULT 0 + +typedef caddr_t KBDC; + +typedef struct pckbd_state { + KBDC kbdc; /* keyboard controller */ + 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) */ +} pckbd_state_t; + +/* keyboard driver declaration */ +static int pckbd_configure(int flags); +static kbd_probe_t pckbd_probe; +static kbd_init_t pckbd_init; +static kbd_term_t pckbd_term; +static kbd_intr_t pckbd_intr; +static kbd_test_if_t pckbd_test_if; +static kbd_enable_t pckbd_enable; +static kbd_disable_t pckbd_disable; +static kbd_read_t pckbd_read; +static kbd_check_t pckbd_check; +static kbd_read_char_t pckbd_read_char; +static kbd_check_char_t pckbd_check_char; +static kbd_ioctl_t pckbd_ioctl; +static kbd_lock_t pckbd_lock; +static kbd_clear_state_t pckbd_clear_state; +static kbd_get_state_t pckbd_get_state; +static kbd_set_state_t pckbd_set_state; + +keyboard_switch_t pckbdsw = { + pckbd_probe, + pckbd_init, + pckbd_term, + pckbd_intr, + pckbd_test_if, + pckbd_enable, + pckbd_disable, + pckbd_read, + pckbd_check, + pckbd_read_char, + pckbd_check_char, + pckbd_ioctl, + pckbd_lock, + pckbd_clear_state, + pckbd_get_state, + pckbd_set_state, + genkbd_get_fkeystr, + genkbd_diag, +}; + +KEYBOARD_DRIVER(pckbd, pckbdsw, pckbd_configure); + +struct kbdc_softc { + int port; /* base port address */ + int lock; /* FIXME: XXX not quite a semaphore... */ +}; + +/* local functions */ +static int probe_keyboard(KBDC kbdc, int flags); +static int init_keyboard(KBDC kbdc, int *type, int flags); +static KBDC kbdc_open(int port); +static int kbdc_lock(KBDC kbdc, int lock); +static int kbdc_data_ready(KBDC kbdc); +static int read_kbd_data(KBDC kbdc); +static int read_kbd_data_no_wait(KBDC kbdc); +static int wait_for_kbd_data(struct kbdc_softc *kbdc); + +/* local variables */ + +/* the initial key map, accent map and fkey strings */ +#include <i386/isa/kbdtables.h> + +/* structures for the default keyboard */ +static keyboard_t default_kbd; +static pckbd_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 +pckbd_configure(int flags) +{ + keyboard_t *kbd; + KBDC kbdc; + int arg[2]; + struct isa_device *dev; + + /* XXX: a kludge to obtain the device configuration flags */ + dev = find_isadev(isa_devtab_tty, &pckbddriver, 0); + if (dev != NULL) + flags |= dev->id_flags; + + /* probe the default keyboard */ + arg[0] = -1; + arg[1] = -1; + if (pckbd_probe(PC98KBD_DEFAULT, &kbd, arg, flags)) + return 0; + + /* initialize it */ + kbdc = ((pckbd_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 +pckbd_probe(int unit, keyboard_t **kbdp, void *arg, int flags) +{ + keyboard_t *kbd; + pckbd_state_t *state; + keymap_t *keymap; + accentmap_t *accmap; + fkeytab_t *fkeymap; + int fkeymap_size; + KBDC kbdc; + int *data = (int *)arg; + + if (unit != PC98KBD_DEFAULT) + return ENXIO; + + *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]); + + state->kbdc = kbdc = kbdc_open(data[0]); + if (kbdc == NULL) + return ENXIO; + kbd_init_struct(kbd, 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); + } + pckbd_clear_state(kbd); + state->ks_mode = K_XLATE; + + KBD_PROBE_DONE(kbd); + return 0; +} + +/* reset and initialize the device */ +static int +pckbd_init(keyboard_t *kbd) +{ + KBDC kbdc; + + if ((kbd == NULL) || !KBD_IS_PROBED(kbd)) + return ENXIO; /* shouldn't happen */ + kbdc = ((pckbd_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; + pckbd_ioctl(kbd, KDSETLED, + (caddr_t)&((pckbd_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 +pckbd_term(keyboard_t *kbd) +{ + kbd_unregister(kbd); + return 0; +} + +/* keyboard interrupt routine */ +static int +pckbd_intr(keyboard_t *kbd, void *arg) +{ + 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 = pckbd_read_char(kbd, FALSE); + } while (c != NOKEY); + } + return 0; +} + +/* test the interface to the device */ +static int +pckbd_test_if(keyboard_t *kbd) +{ + return 0; +} + +/* + * Enable the access to the device; until this function is called, + * the client cannot read from the keyboard. + */ +static int +pckbd_enable(keyboard_t *kbd) +{ + int s; + + s = spltty(); + KBD_ACTIVATE(kbd); + splx(s); + return 0; +} + +/* disallow the access to the device */ +static int +pckbd_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 +pckbd_read(keyboard_t *kbd, int wait) +{ + int c; + + if (wait) + c = read_kbd_data(((pckbd_state_t *)kbd->kb_data)->kbdc); + else + c = read_kbd_data_no_wait(((pckbd_state_t *)kbd->kb_data)->kbdc); + return (KBD_IS_ACTIVE(kbd) ? c : -1); +} + +/* check if data is waiting */ +static int +pckbd_check(keyboard_t *kbd) +{ + if (!KBD_IS_ACTIVE(kbd)) + return FALSE; + return kbdc_data_ready(((pckbd_state_t *)kbd->kb_data)->kbdc); +} + +/* read char from the keyboard */ +static u_int +pckbd_read_char(keyboard_t *kbd, int wait) +{ + pckbd_state_t *state; + u_int action; + int scancode; + int keycode; + + state = (pckbd_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(scancode) { + case 0xF3: /* GRPH (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 0x73: /* GRPH (compose key) pressed */ + if (!(state->ks_flags & COMPOSE)) { + state->ks_flags |= COMPOSE; + state->ks_composed_char = 0; + } + 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 0x42: case 0x43: case 0x44: /* keypad 7,8,9 */ + state->ks_composed_char *= 10; + state->ks_composed_char += scancode - 0x3B; + if (state->ks_composed_char > UCHAR_MAX) + return ERRKEY; + goto next_code; + case 0x46: case 0x47: case 0x48: /* keypad 4,5,6 */ + state->ks_composed_char *= 10; + state->ks_composed_char += scancode - 0x42; + if (state->ks_composed_char > UCHAR_MAX) + return ERRKEY; + goto next_code; + case 0x4A: case 0x4B: case 0x4C: /* keypad 1,2,3 */ + state->ks_composed_char *= 10; + state->ks_composed_char += scancode - 0x49; + if (state->ks_composed_char > UCHAR_MAX) + return ERRKEY; + goto next_code; + case 0x4E: /* 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 0xC2: case 0xC3: case 0xC4: /* keypad 7,8,9 */ + case 0xC6: case 0xC7: case 0xC8: /* keypad 4,5,6 */ + case 0xCA: case 0xCB: case 0xCC: /* keypad 1,2,3 */ + case 0xCE: /* keypad 0 */ + goto next_code; + + case 0x73: /* GRPH 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 +pckbd_check_char(keyboard_t *kbd) +{ + pckbd_state_t *state; + + if (!KBD_IS_ACTIVE(kbd)) + return FALSE; + state = (pckbd_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 +pckbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) +{ + pckbd_state_t *state = kbd->kb_data; + 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) { + pckbd_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; + } + 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 pckbd_ioctl(kbd, KDSETLED, arg); + + case KDSETRAD: /* set keyboard repeat rate */ + break; + + 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 +pckbd_lock(keyboard_t *kbd, int lock) +{ + return kbdc_lock(((pckbd_state_t *)kbd->kb_data)->kbdc, lock); +} + +/* clear the internal state of the keyboard */ +static void +pckbd_clear_state(keyboard_t *kbd) +{ + pckbd_state_t *state; + + state = (pckbd_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; +} + +/* save the internal state */ +static int +pckbd_get_state(keyboard_t *kbd, void *buf, size_t len) +{ + if (len == 0) + return sizeof(pckbd_state_t); + if (len < sizeof(pckbd_state_t)) + return -1; + bcopy(kbd->kb_data, buf, sizeof(pckbd_state_t)); + return 0; +} + +/* set the internal state */ +static int +pckbd_set_state(keyboard_t *kbd, void *buf, size_t len) +{ + if (len < sizeof(pckbd_state_t)) + return ENOMEM; + if (((pckbd_state_t *)kbd->kb_data)->kbdc + != ((pckbd_state_t *)buf)->kbdc) + return ENOMEM; + bcopy(buf, kbd->kb_data, sizeof(pckbd_state_t)); + return 0; +} + +/* local functions */ + +static int +probe_keyboard(KBDC kbdc, int flags) +{ + return 0; +} + +static int +init_keyboard(KBDC kbdc, int *type, int flags) +{ + *type = KB_OTHER; + return 0; +} + +/* keyboard I/O routines */ + +/* 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 37 +#define KBDD_DELAYTIME 37 + +/* I/O ports */ +#define KBD_STATUS_PORT 2 /* status port, read */ +#define KBD_DATA_PORT 0 /* data port, read */ + +/* status bits (KBD_STATUS_PORT) */ +#define KBDS_BUFFER_FULL 0x0002 + +/* macros */ + +#define kbdcp(p) ((struct kbdc_softc *)(p)) + +/* local variables */ + +static struct kbdc_softc kbdc_softc[NPCKBD] = { { 0 }, }; + +/* associate a port number with a KBDC */ + +static KBDC +kbdc_open(int port) +{ + if (port <= 0) + port = IO_KBD; + + if (NPCKBD) { + /* PC-98 has only one keyboard I/F */ + kbdc_softc[0].port = port; + kbdc_softc[0].lock = FALSE; + return (KBDC)&kbdc_softc[0]; + } + return NULL; /* You didn't include sc driver in your config file */ +} + +/* set/reset polling lock */ +static 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 */ +static int +kbdc_data_ready(KBDC p) +{ + return (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL); +} + +/* wait for data from the keyboard */ +static int +wait_for_kbd_data(struct kbdc_softc *kbdc) +{ + /* CPU will stay inside the loop for 200msec at most */ + int retry = 10000; + int port = kbdc->port; + + while (!(inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL)) { + DELAY(KBDD_DELAYTIME); + DELAY(KBDC_DELAYTIME); + if (--retry < 0) + return 0; + } + DELAY(KBDD_DELAYTIME); + return 1; +} + +/* read one byte from the keyboard */ +static int +read_kbd_data(KBDC p) +{ + if (!wait_for_kbd_data(kbdcp(p))) + return -1; /* timeout */ + DELAY(KBDC_DELAYTIME); + return inb(kbdcp(p)->port + KBD_DATA_PORT); +} + +/* read one byte from the keyboard, but return immediately if + * no data is waiting + */ +static int +read_kbd_data_no_wait(KBDC p) +{ + if (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) { + DELAY(KBDD_DELAYTIME); + return inb(kbdcp(p)->port + KBD_DATA_PORT); + } + return -1; /* no data */ +} + +#endif /* NPCKBD > 0 */ |