summaryrefslogtreecommitdiffstats
path: root/sys/dev/atkbdc
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/atkbdc')
-rw-r--r--sys/dev/atkbdc/atkbd.c1409
-rw-r--r--sys/dev/atkbdc/atkbd_atkbdc.c178
-rw-r--r--sys/dev/atkbdc/atkbd_isa.c178
-rw-r--r--sys/dev/atkbdc/atkbdc.c1039
-rw-r--r--sys/dev/atkbdc/atkbdc_isa.c369
-rw-r--r--sys/dev/atkbdc/atkbdc_subr.c369
-rw-r--r--sys/dev/atkbdc/atkbdcreg.h263
-rw-r--r--sys/dev/atkbdc/atkbdreg.h47
-rw-r--r--sys/dev/atkbdc/psm.c2972
9 files changed, 6824 insertions, 0 deletions
diff --git a/sys/dev/atkbdc/atkbd.c b/sys/dev/atkbdc/atkbd.c
new file mode 100644
index 0000000..dcc6ae8
--- /dev/null
+++ b/sys/dev/atkbdc/atkbd.c
@@ -0,0 +1,1409 @@
+/*-
+ * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_kbd.h"
+#include "opt_atkbd.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#ifdef __i386__
+#include <machine/md_var.h>
+#include <machine/psl.h>
+#include <machine/vm86.h>
+#include <machine/pc/bios.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#endif /* __i386__ */
+
+#include <sys/kbio.h>
+#include <dev/kbd/kbdreg.h>
+#include <dev/kbd/atkbdreg.h>
+#include <dev/kbd/atkbdcreg.h>
+
+#include <isa/isareg.h>
+
+static timeout_t atkbd_timeout;
+
+int
+atkbd_probe_unit(int unit, int ctlr, int irq, int flags)
+{
+ keyboard_switch_t *sw;
+ int args[2];
+ int error;
+
+ sw = kbd_get_switch(ATKBD_DRIVER_NAME);
+ if (sw == NULL)
+ return ENXIO;
+
+ args[0] = ctlr;
+ args[1] = irq;
+ error = (*sw->probe)(unit, args, flags);
+ if (error)
+ return error;
+ return 0;
+}
+
+int
+atkbd_attach_unit(int unit, keyboard_t **kbd, int ctlr, int irq, int flags)
+{
+ keyboard_switch_t *sw;
+ int args[2];
+ int error;
+
+ sw = kbd_get_switch(ATKBD_DRIVER_NAME);
+ if (sw == NULL)
+ return ENXIO;
+
+ /* reset, initialize and enable the device */
+ args[0] = ctlr;
+ args[1] = irq;
+ *kbd = NULL;
+ error = (*sw->probe)(unit, args, flags);
+ if (error)
+ return error;
+ error = (*sw->init)(unit, kbd, args, flags);
+ if (error)
+ return error;
+ (*sw->enable)(*kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ /* attach a virtual keyboard cdev */
+ error = kbd_attach(*kbd);
+ 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(*kbd);
+
+ if (bootverbose)
+ (*sw->diag)(*kbd, bootverbose);
+ return 0;
+}
+
+static void
+atkbd_timeout(void *arg)
+{
+ keyboard_t *kbd;
+ int s;
+
+ /*
+ * The original text of 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 the low-level interrupt routine 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. The low-level
+ * interrupt routine doesn't read the mouse data directly,
+ * but the keyboard controller driver will, as a side effect.
+ */
+ /*
+ * And here is bde's original comment about this:
+ *
+ * This is necessary to handle edge triggered interrupts - if we
+ * returned when our IRQ is high due to unserviced input, then there
+ * would be no more keyboard IRQs until the keyboard is reset by
+ * external powers.
+ *
+ * The keyboard apparently unwedges the irq in most cases.
+ */
+ 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);
+}
+
+/* LOW-LEVEL */
+
+#include <machine/limits.h>
+
+#define ATKBD_DEFAULT 0
+
+typedef struct atkbd_state {
+ KBDC kbdc; /* keyboard controller */
+ /* XXX: don't move this field; pcvt
+ * expects `kbdc' to be the first
+ * field in this structure. */
+ int ks_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
+ int ks_flags; /* flags */
+#define COMPOSE (1 << 0)
+ int ks_polling;
+ 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;
+static kbd_poll_mode_t atkbd_poll;
+
+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,
+ atkbd_poll,
+ genkbd_diag,
+};
+
+KEYBOARD_DRIVER(atkbd, atkbdsw, atkbd_configure);
+
+/* local functions */
+static int get_typematic(keyboard_t *kbd);
+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);
+static int typematic(int delay, int rate);
+static int typematic_delay(int delay);
+static int typematic_rate(int rate);
+
+/* local variables */
+
+/* the initial key map, accent map and fkey strings */
+#ifdef ATKBD_DFLT_KEYMAP
+#define KBD_DFLT_KEYMAP
+#include "atkbdmap.h"
+#endif
+#include <dev/kbd/kbdtables.h>
+
+/* structures for the default keyboard */
+static keyboard_t default_kbd;
+static atkbd_state_t default_kbd_state;
+static keymap_t default_keymap;
+static accentmap_t default_accentmap;
+static fkeytab_t default_fkeytab[NUM_FKEYS];
+
+/*
+ * The back door to the keyboard driver!
+ * This function is called by the console driver, via the kbdio module,
+ * to tickle keyboard drivers when the low-level console is being initialized.
+ * Almost nothing in the kernel has been initialied yet. Try to probe
+ * keyboards if possible.
+ * NOTE: because of the way the low-level console is initialized, this routine
+ * may be called more than once!!
+ */
+static int
+atkbd_configure(int flags)
+{
+ keyboard_t *kbd;
+ int arg[2];
+ int i;
+
+ /* probe the keyboard controller */
+ atkbdc_configure();
+
+ /* if the driver is disabled, unregister the keyboard if any */
+ if ((resource_int_value("atkbd", ATKBD_DEFAULT, "disabled", &i) == 0)
+ && i != 0) {
+ i = kbd_find_keyboard(ATKBD_DRIVER_NAME, ATKBD_DEFAULT);
+ if (i >= 0) {
+ kbd = kbd_get_keyboard(i);
+ kbd_unregister(kbd);
+ kbd->kb_flags &= ~KB_REGISTERED;
+ }
+ return 0;
+ }
+
+ /* XXX: a kludge to obtain the device configuration flags */
+ if (resource_int_value("atkbd", ATKBD_DEFAULT, "flags", &i) == 0)
+ flags |= i;
+
+ /* probe the default keyboard */
+ arg[0] = -1;
+ arg[1] = -1;
+ kbd = NULL;
+ if (atkbd_probe(ATKBD_DEFAULT, arg, flags))
+ return 0;
+ if (atkbd_init(ATKBD_DEFAULT, &kbd, arg, flags))
+ return 0;
+
+ /* return the number of found keyboards */
+ return 1;
+}
+
+/* low-level functions */
+
+/* detect a keyboard */
+static int
+atkbd_probe(int unit, void *arg, int flags)
+{
+ KBDC kbdc;
+ int *data = (int *)arg; /* data[0]: controller, data[1]: irq */
+
+ /* XXX */
+ if (unit == ATKBD_DEFAULT) {
+ if (KBD_IS_PROBED(&default_kbd))
+ return 0;
+ }
+
+ kbdc = atkbdc_open(data[0]);
+ if (kbdc == NULL)
+ return ENXIO;
+ if (probe_keyboard(kbdc, flags)) {
+ if (flags & KB_CONF_FAIL_IF_NO_KBD)
+ return ENXIO;
+ }
+ return 0;
+}
+
+/* reset and initialize the device */
+static int
+atkbd_init(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;
+ int delay[2];
+ int *data = (int *)arg; /* data[0]: controller, data[1]: irq */
+
+ /* XXX */
+ if (unit == ATKBD_DEFAULT) {
+ *kbdp = kbd = &default_kbd;
+ if (KBD_IS_INITIALIZED(kbd) && KBD_IS_CONFIGURED(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 | M_ZERO);
+ state = malloc(sizeof(*state), M_DEVBUF, M_NOWAIT | M_ZERO);
+ 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 ((kbd == NULL) || (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);
+ if (kbd != NULL)
+ free(kbd, M_DEVBUF);
+ return ENOMEM;
+ }
+ } else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*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;
+ }
+
+ if (!KBD_IS_PROBED(kbd)) {
+ state->kbdc = atkbdc_open(data[0]);
+ if (state->kbdc == NULL)
+ return ENXIO;
+ kbd_init_struct(kbd, ATKBD_DRIVER_NAME, KB_OTHER, unit, flags,
+ 0, 0);
+ 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(state->kbdc, flags)) { /* shouldn't happen */
+ 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);
+ }
+ if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) {
+ kbd->kb_config = flags & ~KB_CONF_PROBE_ONLY;
+ if (KBD_HAS_DEVICE(kbd)
+ && init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config)
+ && (kbd->kb_config & KB_CONF_FAIL_IF_NO_KBD))
+ return ENXIO;
+ atkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state);
+ get_typematic(kbd);
+ delay[0] = kbd->kb_delay1;
+ delay[1] = kbd->kb_delay2;
+ atkbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
+ 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 delay[2];
+ 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);
+ get_typematic(kbd);
+ delay[0] = kbd->kb_delay1;
+ delay[1] = kbd->kb_delay2;
+ atkbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
+ 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);
+ if (c != -1)
+ ++kbd->kb_count;
+ 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;
+ }
+ ++kbd->kb_count;
+
+#if KBDIO_DEBUG >= 10
+ printf("atkbd_read_char(): scancode:0x%x\n", scancode);
+#endif
+
+ /* 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 0x46: /* ctrl-pause/break on AT 101 (see below) */
+ keycode = 0x68;
+ 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 */
+ /*
+ * The pause/break key on the 101 keyboard produces:
+ * E1-1D-45 E1-9D-C5
+ * Ctrl-pause/break produces:
+ * E0-46 E0-C6 (See above.)
+ */
+ 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;
+ }
+
+ if (kbd->kb_type == KB_84) {
+ switch (keycode) {
+ case 0x37: /* *(numpad)/print screen */
+ if (state->ks_flags & SHIFTS)
+ keycode = 0x5c; /* print screen */
+ break;
+ case 0x45: /* num lock/pause */
+ if (state->ks_flags & CTLS)
+ keycode = 0x68; /* pause */
+ break;
+ case 0x46: /* scroll lock/break */
+ if (state->ks_flags & CTLS)
+ keycode = 0x6c; /* break */
+ break;
+ }
+ } else if (kbd->kb_type == KB_101) {
+ switch (keycode) {
+ case 0x5c: /* print screen */
+ if (state->ks_flags & ALTS)
+ keycode = 0x54; /* sysrq */
+ break;
+ case 0x68: /* pause/break */
+ if (state->ks_flags & CTLS)
+ keycode = 0x6c; /* break */
+ 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 (keycode | (scancode & 0x80)) {
+ /* key pressed, process it */
+ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */
+ state->ks_composed_char *= 10;
+ state->ks_composed_char += keycode - 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 += keycode - 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 += keycode - 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);
+ }
+ /* FALLTHROUGH */
+ 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 (state->ks_mode == K_XLATE &&
+ 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 KDSETREPEAT: /* set keyboard repeat rate (new interface) */
+ splx(s);
+ if (!KBD_HAS_DEVICE(kbd))
+ return 0;
+ i = typematic(((int *)arg)[0], ((int *)arg)[1]);
+ error = write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, i);
+ if (error == 0) {
+ kbd->kb_delay1 = typematic_delay(i);
+ kbd->kb_delay2 = typematic_rate(i);
+ }
+ return error;
+
+ case KDSETRAD: /* set keyboard repeat rate (old interface) */
+ splx(s);
+ if (!KBD_HAS_DEVICE(kbd))
+ return 0;
+ error = write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, *(int *)arg);
+ if (error == 0) {
+ kbd->kb_delay1 = typematic_delay(*(int *)arg);
+ kbd->kb_delay2 = typematic_rate(*(int *)arg);
+ }
+ return error;
+
+ 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;
+ /* FALLTHROUGH */
+ 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_polling = 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;
+}
+
+static int
+atkbd_poll(keyboard_t *kbd, int on)
+{
+ atkbd_state_t *state;
+ int s;
+
+ state = (atkbd_state_t *)kbd->kb_data;
+ s = spltty();
+ if (on)
+ ++state->ks_polling;
+ else
+ --state->ks_polling;
+ splx(s);
+ return 0;
+}
+
+/* local functions */
+
+static int
+get_typematic(keyboard_t *kbd)
+{
+#ifdef __i386__
+ /*
+ * Only some systems allow us to retrieve the keyboard repeat
+ * rate previously set via the BIOS...
+ */
+ struct vm86frame vmf;
+ u_int32_t p;
+
+ bzero(&vmf, sizeof(vmf));
+ vmf.vmf_ax = 0xc000;
+ vm86_intcall(0x15, &vmf);
+ if ((vmf.vmf_eflags & PSL_C) || vmf.vmf_ah)
+ return ENODEV;
+ p = BIOS_PADDRTOVADDR(((u_int32_t)vmf.vmf_es << 4) + vmf.vmf_bx);
+ if ((readb(p + 6) & 0x40) == 0) /* int 16, function 0x09 supported? */
+ return ENODEV;
+ vmf.vmf_ax = 0x0900;
+ vm86_intcall(0x16, &vmf);
+ if ((vmf.vmf_al & 0x08) == 0) /* int 16, function 0x0306 supported? */
+ return ENODEV;
+ vmf.vmf_ax = 0x0306;
+ vm86_intcall(0x16, &vmf);
+ kbd->kb_delay1 = typematic_delay(vmf.vmf_bh << 5);
+ kbd->kb_delay2 = typematic_rate(vmf.vmf_bl);
+ return 0;
+#else
+ return ENODEV;
+#endif /* __i386__ */
+}
+
+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;
+ }
+
+ /* temporarily block data transmission from the keyboard */
+ write_controller_command(kbdc, KBDC_DISABLE_KBD_PORT);
+
+ /* flush any noise in the buffer */
+ empty_both_buffers(kbdc, 100);
+
+ /* 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);
+
+ /*
+ * Even if the keyboard doesn't seem to be present (err != 0),
+ * we shall enable the keyboard port and interrupt so that
+ * the driver will be operable when the keyboard is attached
+ * to the system later. It is NOT recommended to hot-plug
+ * the AT keyboard, but many people do so...
+ */
+ kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS);
+ setup_kbd_port(kbdc, TRUE, TRUE);
+#if 0
+ if (err == 0) {
+ kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS);
+ } else {
+ /* try to restore the command byte as before */
+ set_controller_command_byte(kbdc, 0xff, c);
+ kbdc_set_device_mask(kbdc, m);
+ }
+#endif
+
+ 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;
+ }
+
+ /* temporarily block data transmission from the keyboard */
+ write_controller_command(kbdc, KBDC_DISABLE_KBD_PORT);
+
+ /* save the current controller command byte */
+ empty_both_buffers(kbdc, 200);
+ 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: /* 101/102/... Enhanced */
+ case 0x83ab: /* ditto */
+ case 0x54ab: /* SpaceSaver */
+ case 0x84ab: /* ditto */
+#if 0
+ case 0x90ab: /* 'G' */
+ case 0x91ab: /* 'P' */
+ case 0x92ab: /* 'A' */
+#endif
+ *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 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;
+ }
+ }
+
+#ifdef __alpha__
+ if (send_kbd_command_and_data(
+ kbdc, KBDC_SET_SCANCODE_SET, 2) != KBD_ACK) {
+ printf("atkbd: can't set translation.\n");
+
+ }
+ c |= KBD_TRANSLATION;
+#endif
+
+ /* 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);
+}
+
+static int delays[] = { 250, 500, 750, 1000 };
+static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63,
+ 68, 76, 84, 92, 100, 110, 118, 126,
+ 136, 152, 168, 184, 200, 220, 236, 252,
+ 272, 304, 336, 368, 400, 440, 472, 504 };
+
+static int
+typematic_delay(int i)
+{
+ return delays[(i >> 5) & 3];
+}
+
+static int
+typematic_rate(int i)
+{
+ return rates[i & 0x1f];
+}
+
+static int
+typematic(int delay, int rate)
+{
+ int value;
+ int i;
+
+ for (i = sizeof(delays)/sizeof(delays[0]) - 1; i > 0; --i) {
+ if (delay >= delays[i])
+ break;
+ }
+ value = i << 5;
+ for (i = sizeof(rates)/sizeof(rates[0]) - 1; i > 0; --i) {
+ if (rate >= rates[i])
+ break;
+ }
+ value |= i;
+ return value;
+}
diff --git a/sys/dev/atkbdc/atkbd_atkbdc.c b/sys/dev/atkbdc/atkbd_atkbdc.c
new file mode 100644
index 0000000..0e505dad
--- /dev/null
+++ b/sys/dev/atkbdc/atkbd_atkbdc.c
@@ -0,0 +1,178 @@
+/*-
+ * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_kbd.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <sys/kbio.h>
+#include <dev/kbd/kbdreg.h>
+#include <dev/kbd/atkbdreg.h>
+#include <dev/kbd/atkbdcreg.h>
+
+#include <isa/isareg.h>
+#include <isa/isavar.h>
+
+typedef struct {
+ struct resource *intr;
+ void *ih;
+} atkbd_softc_t;
+
+devclass_t atkbd_devclass;
+
+static void atkbdidentify(driver_t *driver, device_t dev);
+static int atkbdprobe(device_t dev);
+static int atkbdattach(device_t dev);
+static int atkbdresume(device_t dev);
+static void atkbd_isa_intr(void *arg);
+
+static device_method_t atkbd_methods[] = {
+ DEVMETHOD(device_identify, atkbdidentify),
+ DEVMETHOD(device_probe, atkbdprobe),
+ DEVMETHOD(device_attach, atkbdattach),
+ DEVMETHOD(device_resume, atkbdresume),
+ { 0, 0 }
+};
+
+static driver_t atkbd_driver = {
+ ATKBD_DRIVER_NAME,
+ atkbd_methods,
+ sizeof(atkbd_softc_t),
+};
+
+static void
+atkbdidentify(driver_t *driver, device_t parent)
+{
+
+ /* always add at least one child */
+ BUS_ADD_CHILD(parent, KBDC_RID_KBD, driver->name, device_get_unit(parent));
+}
+
+static int
+atkbdprobe(device_t dev)
+{
+ struct resource *res;
+ u_long irq;
+ int flags;
+ int rid;
+
+ device_set_desc(dev, "AT Keyboard");
+
+ /* obtain parameters */
+ flags = device_get_flags(dev);
+
+ /* see if IRQ is available */
+ rid = KBDC_RID_KBD;
+ res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (res == NULL) {
+ if (bootverbose)
+ device_printf(dev, "unable to allocate IRQ\n");
+ return ENXIO;
+ }
+ irq = rman_get_start(res);
+ bus_release_resource(dev, SYS_RES_IRQ, rid, res);
+
+ /* probe the device */
+ return atkbd_probe_unit(device_get_unit(dev),
+ device_get_unit(device_get_parent(dev)),
+ irq, flags);
+}
+
+static int
+atkbdattach(device_t dev)
+{
+ atkbd_softc_t *sc;
+ keyboard_t *kbd;
+ u_long irq;
+ int flags;
+ int rid;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ rid = KBDC_RID_KBD;
+ irq = bus_get_resource_start(dev, SYS_RES_IRQ, rid);
+ flags = device_get_flags(dev);
+ error = atkbd_attach_unit(device_get_unit(dev), &kbd,
+ device_get_unit(device_get_parent(dev)),
+ irq, flags);
+ if (error)
+ return error;
+
+ /* declare our interrupt handler */
+ sc->intr = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->intr == NULL)
+ return ENXIO;
+ error = bus_setup_intr(dev, sc->intr, INTR_TYPE_TTY, atkbd_isa_intr,
+ kbd, &sc->ih);
+ if (error)
+ bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr);
+
+ return error;
+}
+
+static int
+atkbdresume(device_t dev)
+{
+ atkbd_softc_t *sc;
+ keyboard_t *kbd;
+ int args[2];
+
+ sc = device_get_softc(dev);
+ kbd = kbd_get_keyboard(kbd_find_keyboard(ATKBD_DRIVER_NAME,
+ device_get_unit(dev)));
+ if (kbd) {
+ kbd->kb_flags &= ~KB_INITIALIZED;
+ args[0] = device_get_unit(device_get_parent(dev));
+ args[1] = rman_get_start(sc->intr);
+ (*kbdsw[kbd->kb_index]->init)(device_get_unit(dev), &kbd,
+ args, device_get_flags(dev));
+ (*kbdsw[kbd->kb_index]->clear_state)(kbd);
+ }
+ return 0;
+}
+
+static void
+atkbd_isa_intr(void *arg)
+{
+ keyboard_t *kbd;
+
+ kbd = (keyboard_t *)arg;
+ (*kbdsw[kbd->kb_index]->intr)(kbd, NULL);
+}
+
+DRIVER_MODULE(atkbd, atkbdc, atkbd_driver, atkbd_devclass, 0, 0);
diff --git a/sys/dev/atkbdc/atkbd_isa.c b/sys/dev/atkbdc/atkbd_isa.c
new file mode 100644
index 0000000..0e505dad
--- /dev/null
+++ b/sys/dev/atkbdc/atkbd_isa.c
@@ -0,0 +1,178 @@
+/*-
+ * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_kbd.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <sys/kbio.h>
+#include <dev/kbd/kbdreg.h>
+#include <dev/kbd/atkbdreg.h>
+#include <dev/kbd/atkbdcreg.h>
+
+#include <isa/isareg.h>
+#include <isa/isavar.h>
+
+typedef struct {
+ struct resource *intr;
+ void *ih;
+} atkbd_softc_t;
+
+devclass_t atkbd_devclass;
+
+static void atkbdidentify(driver_t *driver, device_t dev);
+static int atkbdprobe(device_t dev);
+static int atkbdattach(device_t dev);
+static int atkbdresume(device_t dev);
+static void atkbd_isa_intr(void *arg);
+
+static device_method_t atkbd_methods[] = {
+ DEVMETHOD(device_identify, atkbdidentify),
+ DEVMETHOD(device_probe, atkbdprobe),
+ DEVMETHOD(device_attach, atkbdattach),
+ DEVMETHOD(device_resume, atkbdresume),
+ { 0, 0 }
+};
+
+static driver_t atkbd_driver = {
+ ATKBD_DRIVER_NAME,
+ atkbd_methods,
+ sizeof(atkbd_softc_t),
+};
+
+static void
+atkbdidentify(driver_t *driver, device_t parent)
+{
+
+ /* always add at least one child */
+ BUS_ADD_CHILD(parent, KBDC_RID_KBD, driver->name, device_get_unit(parent));
+}
+
+static int
+atkbdprobe(device_t dev)
+{
+ struct resource *res;
+ u_long irq;
+ int flags;
+ int rid;
+
+ device_set_desc(dev, "AT Keyboard");
+
+ /* obtain parameters */
+ flags = device_get_flags(dev);
+
+ /* see if IRQ is available */
+ rid = KBDC_RID_KBD;
+ res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (res == NULL) {
+ if (bootverbose)
+ device_printf(dev, "unable to allocate IRQ\n");
+ return ENXIO;
+ }
+ irq = rman_get_start(res);
+ bus_release_resource(dev, SYS_RES_IRQ, rid, res);
+
+ /* probe the device */
+ return atkbd_probe_unit(device_get_unit(dev),
+ device_get_unit(device_get_parent(dev)),
+ irq, flags);
+}
+
+static int
+atkbdattach(device_t dev)
+{
+ atkbd_softc_t *sc;
+ keyboard_t *kbd;
+ u_long irq;
+ int flags;
+ int rid;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ rid = KBDC_RID_KBD;
+ irq = bus_get_resource_start(dev, SYS_RES_IRQ, rid);
+ flags = device_get_flags(dev);
+ error = atkbd_attach_unit(device_get_unit(dev), &kbd,
+ device_get_unit(device_get_parent(dev)),
+ irq, flags);
+ if (error)
+ return error;
+
+ /* declare our interrupt handler */
+ sc->intr = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->intr == NULL)
+ return ENXIO;
+ error = bus_setup_intr(dev, sc->intr, INTR_TYPE_TTY, atkbd_isa_intr,
+ kbd, &sc->ih);
+ if (error)
+ bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr);
+
+ return error;
+}
+
+static int
+atkbdresume(device_t dev)
+{
+ atkbd_softc_t *sc;
+ keyboard_t *kbd;
+ int args[2];
+
+ sc = device_get_softc(dev);
+ kbd = kbd_get_keyboard(kbd_find_keyboard(ATKBD_DRIVER_NAME,
+ device_get_unit(dev)));
+ if (kbd) {
+ kbd->kb_flags &= ~KB_INITIALIZED;
+ args[0] = device_get_unit(device_get_parent(dev));
+ args[1] = rman_get_start(sc->intr);
+ (*kbdsw[kbd->kb_index]->init)(device_get_unit(dev), &kbd,
+ args, device_get_flags(dev));
+ (*kbdsw[kbd->kb_index]->clear_state)(kbd);
+ }
+ return 0;
+}
+
+static void
+atkbd_isa_intr(void *arg)
+{
+ keyboard_t *kbd;
+
+ kbd = (keyboard_t *)arg;
+ (*kbdsw[kbd->kb_index]->intr)(kbd, NULL);
+}
+
+DRIVER_MODULE(atkbd, atkbdc, atkbd_driver, atkbd_devclass, 0, 0);
diff --git a/sys/dev/atkbdc/atkbdc.c b/sys/dev/atkbdc/atkbdc.c
new file mode 100644
index 0000000..8593368
--- /dev/null
+++ b/sys/dev/atkbdc/atkbdc.c
@@ -0,0 +1,1039 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ * from kbdio.c,v 1.13 1998/09/25 11:55:46 yokota Exp
+ */
+
+#include "opt_kbd.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/syslog.h>
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+
+#include <dev/kbd/atkbdcreg.h>
+
+#include <isa/isareg.h>
+
+/* constants */
+
+#define MAXKBDC 1 /* XXX */
+
+/* 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
+
+#define read_data(k) (bus_space_read_1((k)->iot, (k)->ioh0, 0))
+#define read_status(k) (bus_space_read_1((k)->iot, (k)->ioh1, 0))
+#define write_data(k, d) \
+ (bus_space_write_1((k)->iot, (k)->ioh0, 0, (d)))
+#define write_command(k, d) \
+ (bus_space_write_1((k)->iot, (k)->ioh1, 0, (d)))
+
+/* 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, bus_space_tag_t tag,
+ bus_space_handle_t h0, bus_space_handle_t h1);
+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);
+
+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 | M_ZERO);
+ if (sc == NULL)
+ return NULL;
+ }
+ return sc;
+}
+
+int
+atkbdc_probe_unit(int unit, struct resource *port0, struct resource *port1)
+{
+ if (rman_get_start(port0) <= 0)
+ return ENXIO;
+ if (rman_get_start(port1) <= 0)
+ return ENXIO;
+ return 0;
+}
+
+int
+atkbdc_attach_unit(int unit, atkbdc_softc_t *sc, struct resource *port0,
+ struct resource *port1)
+{
+ return atkbdc_setup(sc, rman_get_bustag(port0),
+ rman_get_bushandle(port0),
+ rman_get_bushandle(port1));
+}
+
+/* the backdoor to the keyboard controller! XXX */
+int
+atkbdc_configure(void)
+{
+ bus_space_tag_t tag;
+ bus_space_handle_t h0;
+ bus_space_handle_t h1;
+ int port0;
+ int port1;
+
+ port0 = IO_KBD;
+ resource_int_value("atkbdc", 0, "port", &port0);
+ port1 = IO_KBD + KBD_STATUS_PORT;
+#if 0
+ resource_int_value("atkbdc", 0, "port", &port0);
+#endif
+
+ /* XXX: tag should be passed from the caller */
+#if defined(__i386__)
+ tag = I386_BUS_SPACE_IO;
+#elif defined(__alpha__)
+ tag = busspace_isa_io;
+#elif defined(__ia64__)
+ tag = IA64_BUS_SPACE_IO;
+#else
+#error "define tag!"
+#endif
+
+#if notyet
+ bus_space_map(tag, port0, IO_KBDSIZE, 0, &h0);
+ bus_space_map(tag, port1, IO_KBDSIZE, 0, &h1);
+#else
+ h0 = (bus_space_handle_t)port0;
+ h1 = (bus_space_handle_t)port1;
+#endif
+ return atkbdc_setup(atkbdc_softc[0], tag, h0, h1);
+}
+
+static int
+atkbdc_setup(atkbdc_softc_t *sc, bus_space_tag_t tag, bus_space_handle_t h0,
+ bus_space_handle_t h1)
+{
+ if (sc->ioh0 == 0) { /* XXX */
+ 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->iot = tag;
+ sc->ioh0 = h0;
+ sc->ioh1 = h1;
+ return 0;
+}
+
+/* open a keyboard controller */
+KBDC
+atkbdc_open(int unit)
+{
+ if (unit <= 0)
+ unit = 0;
+ if (unit >= MAXKBDC)
+ return NULL;
+ if ((atkbdc_softc[unit]->port0 != NULL)
+ || (atkbdc_softc[unit]->ioh0 != 0)) /* XXX */
+ return (KBDC)atkbdc_softc[unit];
+ 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 drivers which utilize 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 poll 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
+ * 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 any
+ * necessary I/O operation within one invocation of the routine.
+ * This means 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)
+ || (read_status(kbdcp(p)) & 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 f;
+
+ while ((f = read_status(kbdc)) & KBDS_INPUT_BUFFER_FULL) {
+ if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ addq(&kbdc->kbd, read_data(kbdc));
+ } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ addq(&kbdc->aux, read_data(kbdc));
+ }
+ 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 f;
+
+ while ((f = read_status(kbdc) & 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 f;
+
+ while ((f = read_status(kbdc) & KBDS_BUFFER_FULL)
+ != KBDS_KBD_BUFFER_FULL) {
+ if (f == KBDS_AUX_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ addq(&kbdc->aux, read_data(kbdc));
+ }
+ 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 f;
+ int b;
+
+ while (retry-- > 0) {
+ if ((f = read_status(kbdc)) & KBDS_ANY_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ b = read_data(kbdc);
+ 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 f;
+
+ while ((f = read_status(kbdc) & KBDS_BUFFER_FULL)
+ != KBDS_AUX_BUFFER_FULL) {
+ if (f == KBDS_KBD_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ addq(&kbdc->kbd, read_data(kbdc));
+ }
+ 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 f;
+ int b;
+
+ while (retry-- > 0) {
+ if ((f = read_status(kbdc)) & KBDS_ANY_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ b = read_data(kbdc);
+ 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;
+ write_command(kbdcp(p), 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;
+ write_data(kbdcp(p), 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;
+ write_data(kbdcp(p), 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 read_data(kbdcp(p));
+}
+
+#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 read_data(kbdcp(p));
+}
+
+/* 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 = read_status(kbdcp(p)) & KBDS_BUFFER_FULL;
+ if (f == KBDS_AUX_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ addq(&kbdcp(p)->aux, read_data(kbdcp(p)));
+ f = read_status(kbdcp(p)) & KBDS_BUFFER_FULL;
+ }
+ if (f == KBDS_KBD_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ return read_data(kbdcp(p));
+ }
+ 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 read_data(kbdcp(p));
+}
+
+/* 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 = read_status(kbdcp(p)) & KBDS_BUFFER_FULL;
+ if (f == KBDS_KBD_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ addq(&kbdcp(p)->kbd, read_data(kbdcp(p)));
+ f = read_status(kbdcp(p)) & KBDS_BUFFER_FULL;
+ }
+ if (f == KBDS_AUX_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ return read_data(kbdcp(p));
+ }
+ 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 = read_status(kbdcp(p))) & KBDS_ANY_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ b = read_data(kbdcp(p));
+ 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 = read_status(kbdcp(p))) & KBDS_ANY_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ b = read_data(kbdcp(p));
+ 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 = read_status(kbdcp(p))) & KBDS_ANY_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ (void)read_data(kbdcp(p));
+#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/atkbdc_isa.c b/sys/dev/atkbdc/atkbdc_isa.c
new file mode 100644
index 0000000..5619fff
--- /dev/null
+++ b/sys/dev/atkbdc/atkbdc_isa.c
@@ -0,0 +1,369 @@
+/*-
+ * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_kbd.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/kbd/atkbdcreg.h>
+
+#include <isa/isareg.h>
+#include <isa/isavar.h>
+
+static MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device");
+
+/* children */
+typedef struct atkbdc_device {
+ struct resource_list resources;
+ int rid;
+ u_int32_t vendorid;
+ u_int32_t serial;
+ u_int32_t logicalid;
+ u_int32_t compatid;
+} atkbdc_device_t;
+
+/* kbdc */
+devclass_t atkbdc_devclass;
+
+static int atkbdc_probe(device_t dev);
+static int atkbdc_attach(device_t dev);
+static device_t atkbdc_add_child(device_t bus, int order, char *name,
+ int unit);
+static int atkbdc_print_child(device_t bus, device_t dev);
+static int atkbdc_read_ivar(device_t bus, device_t dev, int index,
+ uintptr_t *val);
+static int atkbdc_write_ivar(device_t bus, device_t dev, int index,
+ uintptr_t val);
+static struct resource_list
+ *atkbdc_get_resource_list (device_t bus, device_t dev);
+static struct resource
+ *atkbdc_alloc_resource(device_t bus, device_t dev, int type,
+ int *rid, u_long start, u_long end,
+ u_long count, u_int flags);
+static int atkbdc_release_resource(device_t bus, device_t dev, int type,
+ int rid, struct resource *res);
+
+static device_method_t atkbdc_methods[] = {
+ DEVMETHOD(device_probe, atkbdc_probe),
+ DEVMETHOD(device_attach, atkbdc_attach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ DEVMETHOD(bus_add_child, atkbdc_add_child),
+ DEVMETHOD(bus_print_child, atkbdc_print_child),
+ DEVMETHOD(bus_read_ivar, atkbdc_read_ivar),
+ DEVMETHOD(bus_write_ivar, atkbdc_write_ivar),
+ DEVMETHOD(bus_get_resource_list,atkbdc_get_resource_list),
+ DEVMETHOD(bus_alloc_resource, atkbdc_alloc_resource),
+ DEVMETHOD(bus_release_resource, atkbdc_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
+ DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
+ DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ { 0, 0 }
+};
+
+static driver_t atkbdc_driver = {
+ ATKBDC_DRIVER_NAME,
+ atkbdc_methods,
+ sizeof(atkbdc_softc_t *),
+};
+
+static struct isa_pnp_id atkbdc_ids[] = {
+ { 0x0303d041, "Keyboard controller (i8042)" }, /* PNP0303 */
+ { 0 }
+};
+
+static int
+atkbdc_probe(device_t dev)
+{
+ struct resource *port0;
+ struct resource *port1;
+ u_long start;
+ u_long count;
+ int error;
+ int rid;
+
+ /* check PnP IDs */
+ if (ISA_PNP_PROBE(device_get_parent(dev), dev, atkbdc_ids) == ENXIO)
+ return ENXIO;
+
+ device_set_desc(dev, "Keyboard controller (i8042)");
+
+ /*
+ * Adjust I/O port resources.
+ * The AT keyboard controller uses two ports (a command/data port
+ * 0x60 and a status port 0x64), which may be given to us in
+ * one resource (0x60 through 0x64) or as two separate resources
+ * (0x60 and 0x64). Furthermore, /boot/device.hints may contain
+ * just one port, 0x60. We shall adjust resource settings
+ * so that these two ports are available as two separate resources.
+ */
+ device_quiet(dev);
+ rid = 0;
+ if (bus_get_resource(dev, SYS_RES_IOPORT, rid, &start, &count) != 0)
+ return ENXIO;
+ if (count > 1) /* adjust the count */
+ bus_set_resource(dev, SYS_RES_IOPORT, rid, start, 1);
+ port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
+ RF_ACTIVE);
+ if (port0 == NULL)
+ return ENXIO;
+ rid = 1;
+ if (bus_get_resource(dev, SYS_RES_IOPORT, rid, NULL, NULL) != 0)
+ bus_set_resource(dev, SYS_RES_IOPORT, 1,
+ start + KBD_STATUS_PORT, 1);
+ port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
+ RF_ACTIVE);
+ if (port1 == NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, port0);
+ return ENXIO;
+ }
+ device_verbose(dev);
+
+ error = atkbdc_probe_unit(device_get_unit(dev), port0, port1);
+ if (error == 0)
+ bus_generic_probe(dev);
+
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, port0);
+ bus_release_resource(dev, SYS_RES_IOPORT, 1, port1);
+
+ return error;
+}
+
+static int
+atkbdc_attach(device_t dev)
+{
+ atkbdc_softc_t *sc;
+ int unit;
+ int error;
+ int rid;
+
+ unit = device_get_unit(dev);
+ sc = *(atkbdc_softc_t **)device_get_softc(dev);
+ if (sc == NULL) {
+ /*
+ * We have to maintain two copies of the kbdc_softc struct,
+ * as the low-level console needs to have access to the
+ * keyboard controller before kbdc is probed and attached.
+ * kbdc_soft[] contains the default entry for that purpose.
+ * See atkbdc.c. XXX
+ */
+ sc = atkbdc_get_softc(unit);
+ if (sc == NULL)
+ return ENOMEM;
+ }
+
+ rid = 0;
+ sc->port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
+ RF_ACTIVE);
+ if (sc->port0 == NULL)
+ return ENXIO;
+ rid = 1;
+ sc->port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
+ RF_ACTIVE);
+ if (sc->port1 == NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0);
+ return ENXIO;
+ }
+
+ error = atkbdc_attach_unit(unit, sc, sc->port0, sc->port1);
+ if (error) {
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0);
+ bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->port1);
+ return error;
+ }
+ *(atkbdc_softc_t **)device_get_softc(dev) = sc;
+
+ bus_generic_attach(dev);
+
+ return 0;
+}
+
+static device_t
+atkbdc_add_child(device_t bus, int order, char *name, int unit)
+{
+ atkbdc_device_t *ivar;
+ device_t child;
+ int t;
+
+ ivar = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV,
+ M_NOWAIT | M_ZERO);
+ if (!ivar)
+ return NULL;
+
+ child = device_add_child_ordered(bus, order, name, unit);
+ if (child == NULL) {
+ free(ivar, M_ATKBDDEV);
+ return child;
+ }
+
+ resource_list_init(&ivar->resources);
+ ivar->rid = order;
+
+ /*
+ * If the device is not created by the PnP BIOS or ACPI,
+ * refer to device hints for IRQ.
+ */
+ if (ISA_PNP_PROBE(device_get_parent(bus), bus, atkbdc_ids) != 0) {
+ if (resource_int_value(name, unit, "irq", &t) != 0)
+ t = -1;
+ } else {
+ t = bus_get_resource_start(bus, SYS_RES_IRQ, ivar->rid);
+ }
+ if (t > 0)
+ resource_list_add(&ivar->resources, SYS_RES_IRQ, ivar->rid,
+ t, t, 1);
+
+ if (resource_int_value(name, unit, "flags", &t) == 0)
+ device_set_flags(child, t);
+ if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0)
+ device_disable(child);
+
+ device_set_ivars(child, ivar);
+
+ return child;
+}
+
+static int
+atkbdc_print_child(device_t bus, device_t dev)
+{
+ atkbdc_device_t *kbdcdev;
+ u_long irq;
+ int flags;
+ int retval = 0;
+
+ kbdcdev = (atkbdc_device_t *)device_get_ivars(dev);
+
+ retval += bus_print_child_header(bus, dev);
+ flags = device_get_flags(dev);
+ if (flags != 0)
+ retval += printf(" flags 0x%x", flags);
+ irq = bus_get_resource_start(dev, SYS_RES_IRQ, kbdcdev->rid);
+ if (irq != 0)
+ retval += printf(" irq %ld", irq);
+ retval += bus_print_child_footer(bus, dev);
+
+ return (retval);
+}
+
+static int
+atkbdc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ switch (index) {
+ case KBDC_IVAR_VENDORID:
+ *val = (u_long)ivar->vendorid;
+ break;
+ case KBDC_IVAR_SERIAL:
+ *val = (u_long)ivar->serial;
+ break;
+ case KBDC_IVAR_LOGICALID:
+ *val = (u_long)ivar->logicalid;
+ break;
+ case KBDC_IVAR_COMPATID:
+ *val = (u_long)ivar->compatid;
+ break;
+ default:
+ return ENOENT;
+ }
+ return 0;
+}
+
+static int
+atkbdc_write_ivar(device_t bus, device_t dev, int index, uintptr_t val)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ switch (index) {
+ case KBDC_IVAR_VENDORID:
+ ivar->vendorid = (u_int32_t)val;
+ break;
+ case KBDC_IVAR_SERIAL:
+ ivar->serial = (u_int32_t)val;
+ break;
+ case KBDC_IVAR_LOGICALID:
+ ivar->logicalid = (u_int32_t)val;
+ break;
+ case KBDC_IVAR_COMPATID:
+ ivar->compatid = (u_int32_t)val;
+ break;
+ default:
+ return ENOENT;
+ }
+ return 0;
+}
+
+static struct resource_list
+*atkbdc_get_resource_list (device_t bus, device_t dev)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ return &ivar->resources;
+}
+
+static struct resource
+*atkbdc_alloc_resource(device_t bus, device_t dev, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ return resource_list_alloc(&ivar->resources, bus, dev, type, rid,
+ start, end, count, flags);
+}
+
+static int
+atkbdc_release_resource(device_t bus, device_t dev, int type, int rid,
+ struct resource *res)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ return resource_list_release(&ivar->resources, bus, dev, type, rid,
+ res);
+}
+
+DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0);
+DRIVER_MODULE(atkbdc, acpi, atkbdc_driver, atkbdc_devclass, 0, 0);
diff --git a/sys/dev/atkbdc/atkbdc_subr.c b/sys/dev/atkbdc/atkbdc_subr.c
new file mode 100644
index 0000000..5619fff
--- /dev/null
+++ b/sys/dev/atkbdc/atkbdc_subr.c
@@ -0,0 +1,369 @@
+/*-
+ * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_kbd.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/kbd/atkbdcreg.h>
+
+#include <isa/isareg.h>
+#include <isa/isavar.h>
+
+static MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device");
+
+/* children */
+typedef struct atkbdc_device {
+ struct resource_list resources;
+ int rid;
+ u_int32_t vendorid;
+ u_int32_t serial;
+ u_int32_t logicalid;
+ u_int32_t compatid;
+} atkbdc_device_t;
+
+/* kbdc */
+devclass_t atkbdc_devclass;
+
+static int atkbdc_probe(device_t dev);
+static int atkbdc_attach(device_t dev);
+static device_t atkbdc_add_child(device_t bus, int order, char *name,
+ int unit);
+static int atkbdc_print_child(device_t bus, device_t dev);
+static int atkbdc_read_ivar(device_t bus, device_t dev, int index,
+ uintptr_t *val);
+static int atkbdc_write_ivar(device_t bus, device_t dev, int index,
+ uintptr_t val);
+static struct resource_list
+ *atkbdc_get_resource_list (device_t bus, device_t dev);
+static struct resource
+ *atkbdc_alloc_resource(device_t bus, device_t dev, int type,
+ int *rid, u_long start, u_long end,
+ u_long count, u_int flags);
+static int atkbdc_release_resource(device_t bus, device_t dev, int type,
+ int rid, struct resource *res);
+
+static device_method_t atkbdc_methods[] = {
+ DEVMETHOD(device_probe, atkbdc_probe),
+ DEVMETHOD(device_attach, atkbdc_attach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ DEVMETHOD(bus_add_child, atkbdc_add_child),
+ DEVMETHOD(bus_print_child, atkbdc_print_child),
+ DEVMETHOD(bus_read_ivar, atkbdc_read_ivar),
+ DEVMETHOD(bus_write_ivar, atkbdc_write_ivar),
+ DEVMETHOD(bus_get_resource_list,atkbdc_get_resource_list),
+ DEVMETHOD(bus_alloc_resource, atkbdc_alloc_resource),
+ DEVMETHOD(bus_release_resource, atkbdc_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
+ DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
+ DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ { 0, 0 }
+};
+
+static driver_t atkbdc_driver = {
+ ATKBDC_DRIVER_NAME,
+ atkbdc_methods,
+ sizeof(atkbdc_softc_t *),
+};
+
+static struct isa_pnp_id atkbdc_ids[] = {
+ { 0x0303d041, "Keyboard controller (i8042)" }, /* PNP0303 */
+ { 0 }
+};
+
+static int
+atkbdc_probe(device_t dev)
+{
+ struct resource *port0;
+ struct resource *port1;
+ u_long start;
+ u_long count;
+ int error;
+ int rid;
+
+ /* check PnP IDs */
+ if (ISA_PNP_PROBE(device_get_parent(dev), dev, atkbdc_ids) == ENXIO)
+ return ENXIO;
+
+ device_set_desc(dev, "Keyboard controller (i8042)");
+
+ /*
+ * Adjust I/O port resources.
+ * The AT keyboard controller uses two ports (a command/data port
+ * 0x60 and a status port 0x64), which may be given to us in
+ * one resource (0x60 through 0x64) or as two separate resources
+ * (0x60 and 0x64). Furthermore, /boot/device.hints may contain
+ * just one port, 0x60. We shall adjust resource settings
+ * so that these two ports are available as two separate resources.
+ */
+ device_quiet(dev);
+ rid = 0;
+ if (bus_get_resource(dev, SYS_RES_IOPORT, rid, &start, &count) != 0)
+ return ENXIO;
+ if (count > 1) /* adjust the count */
+ bus_set_resource(dev, SYS_RES_IOPORT, rid, start, 1);
+ port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
+ RF_ACTIVE);
+ if (port0 == NULL)
+ return ENXIO;
+ rid = 1;
+ if (bus_get_resource(dev, SYS_RES_IOPORT, rid, NULL, NULL) != 0)
+ bus_set_resource(dev, SYS_RES_IOPORT, 1,
+ start + KBD_STATUS_PORT, 1);
+ port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
+ RF_ACTIVE);
+ if (port1 == NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, port0);
+ return ENXIO;
+ }
+ device_verbose(dev);
+
+ error = atkbdc_probe_unit(device_get_unit(dev), port0, port1);
+ if (error == 0)
+ bus_generic_probe(dev);
+
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, port0);
+ bus_release_resource(dev, SYS_RES_IOPORT, 1, port1);
+
+ return error;
+}
+
+static int
+atkbdc_attach(device_t dev)
+{
+ atkbdc_softc_t *sc;
+ int unit;
+ int error;
+ int rid;
+
+ unit = device_get_unit(dev);
+ sc = *(atkbdc_softc_t **)device_get_softc(dev);
+ if (sc == NULL) {
+ /*
+ * We have to maintain two copies of the kbdc_softc struct,
+ * as the low-level console needs to have access to the
+ * keyboard controller before kbdc is probed and attached.
+ * kbdc_soft[] contains the default entry for that purpose.
+ * See atkbdc.c. XXX
+ */
+ sc = atkbdc_get_softc(unit);
+ if (sc == NULL)
+ return ENOMEM;
+ }
+
+ rid = 0;
+ sc->port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
+ RF_ACTIVE);
+ if (sc->port0 == NULL)
+ return ENXIO;
+ rid = 1;
+ sc->port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
+ RF_ACTIVE);
+ if (sc->port1 == NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0);
+ return ENXIO;
+ }
+
+ error = atkbdc_attach_unit(unit, sc, sc->port0, sc->port1);
+ if (error) {
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0);
+ bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->port1);
+ return error;
+ }
+ *(atkbdc_softc_t **)device_get_softc(dev) = sc;
+
+ bus_generic_attach(dev);
+
+ return 0;
+}
+
+static device_t
+atkbdc_add_child(device_t bus, int order, char *name, int unit)
+{
+ atkbdc_device_t *ivar;
+ device_t child;
+ int t;
+
+ ivar = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV,
+ M_NOWAIT | M_ZERO);
+ if (!ivar)
+ return NULL;
+
+ child = device_add_child_ordered(bus, order, name, unit);
+ if (child == NULL) {
+ free(ivar, M_ATKBDDEV);
+ return child;
+ }
+
+ resource_list_init(&ivar->resources);
+ ivar->rid = order;
+
+ /*
+ * If the device is not created by the PnP BIOS or ACPI,
+ * refer to device hints for IRQ.
+ */
+ if (ISA_PNP_PROBE(device_get_parent(bus), bus, atkbdc_ids) != 0) {
+ if (resource_int_value(name, unit, "irq", &t) != 0)
+ t = -1;
+ } else {
+ t = bus_get_resource_start(bus, SYS_RES_IRQ, ivar->rid);
+ }
+ if (t > 0)
+ resource_list_add(&ivar->resources, SYS_RES_IRQ, ivar->rid,
+ t, t, 1);
+
+ if (resource_int_value(name, unit, "flags", &t) == 0)
+ device_set_flags(child, t);
+ if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0)
+ device_disable(child);
+
+ device_set_ivars(child, ivar);
+
+ return child;
+}
+
+static int
+atkbdc_print_child(device_t bus, device_t dev)
+{
+ atkbdc_device_t *kbdcdev;
+ u_long irq;
+ int flags;
+ int retval = 0;
+
+ kbdcdev = (atkbdc_device_t *)device_get_ivars(dev);
+
+ retval += bus_print_child_header(bus, dev);
+ flags = device_get_flags(dev);
+ if (flags != 0)
+ retval += printf(" flags 0x%x", flags);
+ irq = bus_get_resource_start(dev, SYS_RES_IRQ, kbdcdev->rid);
+ if (irq != 0)
+ retval += printf(" irq %ld", irq);
+ retval += bus_print_child_footer(bus, dev);
+
+ return (retval);
+}
+
+static int
+atkbdc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ switch (index) {
+ case KBDC_IVAR_VENDORID:
+ *val = (u_long)ivar->vendorid;
+ break;
+ case KBDC_IVAR_SERIAL:
+ *val = (u_long)ivar->serial;
+ break;
+ case KBDC_IVAR_LOGICALID:
+ *val = (u_long)ivar->logicalid;
+ break;
+ case KBDC_IVAR_COMPATID:
+ *val = (u_long)ivar->compatid;
+ break;
+ default:
+ return ENOENT;
+ }
+ return 0;
+}
+
+static int
+atkbdc_write_ivar(device_t bus, device_t dev, int index, uintptr_t val)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ switch (index) {
+ case KBDC_IVAR_VENDORID:
+ ivar->vendorid = (u_int32_t)val;
+ break;
+ case KBDC_IVAR_SERIAL:
+ ivar->serial = (u_int32_t)val;
+ break;
+ case KBDC_IVAR_LOGICALID:
+ ivar->logicalid = (u_int32_t)val;
+ break;
+ case KBDC_IVAR_COMPATID:
+ ivar->compatid = (u_int32_t)val;
+ break;
+ default:
+ return ENOENT;
+ }
+ return 0;
+}
+
+static struct resource_list
+*atkbdc_get_resource_list (device_t bus, device_t dev)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ return &ivar->resources;
+}
+
+static struct resource
+*atkbdc_alloc_resource(device_t bus, device_t dev, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ return resource_list_alloc(&ivar->resources, bus, dev, type, rid,
+ start, end, count, flags);
+}
+
+static int
+atkbdc_release_resource(device_t bus, device_t dev, int type, int rid,
+ struct resource *res)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ return resource_list_release(&ivar->resources, bus, dev, type, rid,
+ res);
+}
+
+DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0);
+DRIVER_MODULE(atkbdc, acpi, atkbdc_driver, atkbdc_devclass, 0, 0);
diff --git a/sys/dev/atkbdc/atkbdcreg.h b/sys/dev/atkbdc/atkbdcreg.h
new file mode 100644
index 0000000..f2ec844
--- /dev/null
+++ b/sys/dev/atkbdc/atkbdcreg.h
@@ -0,0 +1,263 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ * from kbdio.h,v 1.8 1998/09/25 11:55:46 yokota Exp
+ */
+
+#ifndef _DEV_KBD_ATKBDCREG_H_
+#define _DEV_KBD_ATKBDCREG_H_
+
+#include "opt_kbd.h" /* Structures depend on the value if KBDIO_DEBUG */
+
+/* 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
+#define PSM_EXPLORER_ID 4
+#define PSM_4DMOUSE_ID 6
+#define PSM_4DPLUS_ID 8
+
+#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;
+
+struct resource;
+
+typedef struct atkbdc_softc {
+ struct resource *port0; /* data port */
+ struct resource *port1; /* status port */
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh0;
+ bus_space_handle_t ioh1;
+ 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_VENDORID,
+ KBDC_IVAR_SERIAL,
+ KBDC_IVAR_LOGICALID,
+ KBDC_IVAR_COMPATID,
+};
+
+typedef caddr_t KBDC;
+
+#define KBDC_RID_KBD 0
+#define KBDC_RID_AUX 1
+
+/* function prototypes */
+
+atkbdc_softc_t *atkbdc_get_softc(int unit);
+int atkbdc_probe_unit(int unit, struct resource *port0, struct resource *port1);
+int atkbdc_attach_unit(int unit, atkbdc_softc_t *sc, struct resource *port0,
+ struct resource *port1);
+int atkbdc_configure(void);
+
+KBDC atkbdc_open(int unit);
+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 0000000..4ae93e3
--- /dev/null
+++ b/sys/dev/atkbdc/atkbdreg.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_KBD_ATKBDREG_H_
+#define _DEV_KBD_ATKBDREG_H_
+
+#define ATKBD_DRIVER_NAME "atkbd"
+
+/* 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
+
+int atkbd_probe_unit(int unit, int ctlr, int irq, int flags);
+int atkbd_attach_unit(int unit, keyboard_t **kbd,
+ int ctlr, int irq, int flags);
+
+#endif
+
+#endif /* !_DEV_KBD_ATKBDREG_H_ */
diff --git a/sys/dev/atkbdc/psm.c b/sys/dev/atkbdc/psm.c
new file mode 100644
index 0000000..5e95cb5
--- /dev/null
+++ b/sys/dev/atkbdc/psm.c
@@ -0,0 +1,2972 @@
+/*-
+ * Copyright (c) 1992, 1993 Erik Forsberg.
+ * Copyright (c) 1996, 1997 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ``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 I 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Ported to 386bsd Oct 17, 1992
+ * Sandi Donno, Computer Science, University of Cape Town, South Africa
+ * Please send bug reports to sandi@cs.uct.ac.za
+ *
+ * Thanks are also due to Rick Macklem, rick@snowhite.cis.uoguelph.ca -
+ * although I was only partially successful in getting the alpha release
+ * of his "driver for the Logitech and ATI Inport Bus mice for use with
+ * 386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless
+ * found his code to be an invaluable reference when porting this driver
+ * to 386bsd.
+ *
+ * Further modifications for latest 386BSD+patchkit and port to NetBSD,
+ * Andrew Herbert <andrew@werple.apana.org.au> - 8 June 1993
+ *
+ * Cloned from the Microsoft Bus Mouse driver, also by Erik Forsberg, by
+ * Andrew Herbert - 12 June 1993
+ *
+ * Modified for PS/2 mouse by Charles Hannum <mycroft@ai.mit.edu>
+ * - 13 June 1993
+ *
+ * Modified for PS/2 AUX mouse by Shoji Yuen <yuen@nuie.nagoya-u.ac.jp>
+ * - 24 October 1993
+ *
+ * Hardware access routines and probe logic rewritten by
+ * Kazutaka Yokota <yokota@zodiac.mech.utsunomiya-u.ac.jp>
+ * - 3, 14, 22 October 1996.
+ * - 12 November 1996. IOCTLs and rearranging `psmread', `psmioctl'...
+ * - 14, 30 November 1996. Uses `kbdio.c'.
+ * - 13 December 1996. Uses queuing version of `kbdio.c'.
+ * - January/February 1997. Tweaked probe logic for
+ * HiNote UltraII/Latitude/Armada laptops.
+ * - 30 July 1997. Added APM support.
+ * - 5 March 1997. Defined driver configuration flags (PSM_CONFIG_XXX).
+ * Improved sync check logic.
+ * Vendor specific support routines.
+ */
+
+#include "opt_psm.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/poll.h>
+#include <sys/syslog.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <sys/selinfo.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+
+#include <machine/limits.h>
+#include <sys/mouse.h>
+#include <machine/resource.h>
+
+#include <isa/isavar.h>
+#include <dev/kbd/atkbdcreg.h>
+
+/*
+ * Driver specific options: the following options may be set by
+ * `options' statements in the kernel configuration file.
+ */
+
+/* debugging */
+#ifndef PSM_DEBUG
+#define PSM_DEBUG 0 /* logging: 0: none, 1: brief, 2: verbose */
+#endif
+
+#ifndef PSM_SYNCERR_THRESHOLD1
+#define PSM_SYNCERR_THRESHOLD1 20
+#endif
+
+#ifndef PSM_INPUT_TIMEOUT
+#define PSM_INPUT_TIMEOUT 2000000 /* 2 sec */
+#endif
+
+/* end of driver specific options */
+
+#define PSM_DRIVER_NAME "psm"
+#define PSMCPNP_DRIVER_NAME "psmcpnp"
+
+/* input queue */
+#define PSM_BUFSIZE 960
+#define PSM_SMALLBUFSIZE 240
+
+/* operation levels */
+#define PSM_LEVEL_BASE 0
+#define PSM_LEVEL_STANDARD 1
+#define PSM_LEVEL_NATIVE 2
+#define PSM_LEVEL_MIN PSM_LEVEL_BASE
+#define PSM_LEVEL_MAX PSM_LEVEL_NATIVE
+
+/* Logitech PS2++ protocol */
+#define MOUSE_PS2PLUS_CHECKBITS(b) \
+ ((((b[2] & 0x03) << 2) | 0x02) == (b[1] & 0x0f))
+#define MOUSE_PS2PLUS_PACKET_TYPE(b) \
+ (((b[0] & 0x30) >> 2) | ((b[1] & 0x30) >> 4))
+
+/* some macros */
+#define PSM_UNIT(dev) (minor(dev) >> 1)
+#define PSM_NBLOCKIO(dev) (minor(dev) & 1)
+#define PSM_MKMINOR(unit,block) (((unit) << 1) | ((block) ? 0:1))
+
+#ifndef max
+#define max(x,y) ((x) > (y) ? (x) : (y))
+#endif
+#ifndef min
+#define min(x,y) ((x) < (y) ? (x) : (y))
+#endif
+
+#define abs(x) (((x) < 0) ? -(x) : (x))
+
+/* ring buffer */
+typedef struct ringbuf {
+ int count; /* # of valid elements in the buffer */
+ int head; /* head pointer */
+ int tail; /* tail poiner */
+ unsigned char buf[PSM_BUFSIZE];
+} ringbuf_t;
+
+/* driver control block */
+struct psm_softc { /* Driver status information */
+ int unit;
+ struct selinfo rsel; /* Process selecting for Input */
+ unsigned char state; /* Mouse driver state */
+ int config; /* driver configuration flags */
+ int flags; /* other flags */
+ KBDC kbdc; /* handle to access the keyboard controller */
+ struct resource *intr; /* IRQ resource */
+ void *ih; /* interrupt handle */
+ mousehw_t hw; /* hardware information */
+ mousemode_t mode; /* operation mode */
+ mousemode_t dflt_mode; /* default operation mode */
+ mousestatus_t status; /* accumulated mouse movement */
+ ringbuf_t queue; /* mouse status queue */
+ unsigned char ipacket[16]; /* interim input buffer */
+ int inputbytes; /* # of bytes in the input buffer */
+ int button; /* the latest button state */
+ int xold; /* previous absolute X position */
+ int yold; /* previous absolute Y position */
+ int syncerrors;
+ struct timeval inputtimeout;
+ int watchdog; /* watchdog timer flag */
+ struct callout_handle callout; /* watchdog timer call out */
+ dev_t dev;
+ dev_t bdev;
+};
+devclass_t psm_devclass;
+#define PSM_SOFTC(unit) ((struct psm_softc*)devclass_get_softc(psm_devclass, unit))
+
+/* driver state flags (state) */
+#define PSM_VALID 0x80
+#define PSM_OPEN 1 /* Device is open */
+#define PSM_ASLP 2 /* Waiting for mouse data */
+
+/* driver configuration flags (config) */
+#define PSM_CONFIG_RESOLUTION 0x000f /* resolution */
+#define PSM_CONFIG_ACCEL 0x00f0 /* acceleration factor */
+#define PSM_CONFIG_NOCHECKSYNC 0x0100 /* disable sync. test */
+#define PSM_CONFIG_NOIDPROBE 0x0200 /* disable mouse model probe */
+#define PSM_CONFIG_NORESET 0x0400 /* don't reset the mouse */
+#define PSM_CONFIG_FORCETAP 0x0800 /* assume `tap' action exists */
+#define PSM_CONFIG_IGNPORTERROR 0x1000 /* ignore error in aux port test */
+#define PSM_CONFIG_HOOKRESUME 0x2000 /* hook the system resume event */
+#define PSM_CONFIG_INITAFTERSUSPEND 0x4000 /* init the device at the resume event */
+#define PSM_CONFIG_SYNCHACK 0x8000 /* enable `out-of-sync' hack */
+
+#define PSM_CONFIG_FLAGS (PSM_CONFIG_RESOLUTION \
+ | PSM_CONFIG_ACCEL \
+ | PSM_CONFIG_NOCHECKSYNC \
+ | PSM_CONFIG_SYNCHACK \
+ | PSM_CONFIG_NOIDPROBE \
+ | PSM_CONFIG_NORESET \
+ | PSM_CONFIG_FORCETAP \
+ | PSM_CONFIG_IGNPORTERROR \
+ | PSM_CONFIG_HOOKRESUME \
+ | PSM_CONFIG_INITAFTERSUSPEND)
+
+/* other flags (flags) */
+#define PSM_FLAGS_FINGERDOWN 0x0001 /* VersaPad finger down */
+
+/* for backward compatibility */
+#define OLD_MOUSE_GETHWINFO _IOR('M', 1, old_mousehw_t)
+#define OLD_MOUSE_GETMODE _IOR('M', 2, old_mousemode_t)
+#define OLD_MOUSE_SETMODE _IOW('M', 3, old_mousemode_t)
+
+typedef struct old_mousehw {
+ int buttons;
+ int iftype;
+ int type;
+ int hwid;
+} old_mousehw_t;
+
+typedef struct old_mousemode {
+ int protocol;
+ int rate;
+ int resolution;
+ int accelfactor;
+} old_mousemode_t;
+
+/* packet formatting function */
+typedef int packetfunc_t(struct psm_softc *, unsigned char *,
+ int *, int, mousestatus_t *);
+
+/* function prototypes */
+static void psmidentify(driver_t *, device_t);
+static int psmprobe(device_t);
+static int psmattach(device_t);
+static int psmdetach(device_t);
+static int psmresume(device_t);
+
+static d_open_t psmopen;
+static d_close_t psmclose;
+static d_read_t psmread;
+static d_ioctl_t psmioctl;
+static d_poll_t psmpoll;
+
+static int enable_aux_dev(KBDC);
+static int disable_aux_dev(KBDC);
+static int get_mouse_status(KBDC, int *, int, int);
+static int get_aux_id(KBDC);
+static int set_mouse_sampling_rate(KBDC, int);
+static int set_mouse_scaling(KBDC, int);
+static int set_mouse_resolution(KBDC, int);
+static int set_mouse_mode(KBDC);
+static int get_mouse_buttons(KBDC);
+static int is_a_mouse(int);
+static void recover_from_error(KBDC);
+static int restore_controller(KBDC, int);
+static int doinitialize(struct psm_softc *, mousemode_t *);
+static int doopen(struct psm_softc *, int);
+static int reinitialize(struct psm_softc *, int);
+static char *model_name(int);
+static void psmintr(void *);
+static void psmtimeout(void *);
+
+/* vendor specific features */
+typedef int probefunc_t(struct psm_softc *);
+
+static int mouse_id_proc1(KBDC, int, int, int *);
+static int mouse_ext_command(KBDC, int);
+static probefunc_t enable_groller;
+static probefunc_t enable_gmouse;
+static probefunc_t enable_aglide;
+static probefunc_t enable_kmouse;
+static probefunc_t enable_msexplorer;
+static probefunc_t enable_msintelli;
+static probefunc_t enable_4dmouse;
+static probefunc_t enable_4dplus;
+static probefunc_t enable_mmanplus;
+static probefunc_t enable_versapad;
+static int tame_mouse(struct psm_softc *, mousestatus_t *, unsigned char *);
+
+static struct {
+ int model;
+ unsigned char syncmask;
+ int packetsize;
+ probefunc_t *probefunc;
+} vendortype[] = {
+ /*
+ * WARNING: the order of probe is very important. Don't mess it
+ * unless you know what you are doing.
+ */
+ { MOUSE_MODEL_NET, /* Genius NetMouse */
+ 0x08, MOUSE_PS2INTELLI_PACKETSIZE, enable_gmouse, },
+ { MOUSE_MODEL_NETSCROLL, /* Genius NetScroll */
+ 0xc8, 6, enable_groller, },
+ { MOUSE_MODEL_MOUSEMANPLUS, /* Logitech MouseMan+ */
+ 0x08, MOUSE_PS2_PACKETSIZE, enable_mmanplus, },
+ { MOUSE_MODEL_EXPLORER, /* Microsoft IntelliMouse Explorer */
+ 0x08, MOUSE_PS2INTELLI_PACKETSIZE, enable_msexplorer, },
+ { MOUSE_MODEL_4D, /* A4 Tech 4D Mouse */
+ 0x08, MOUSE_4D_PACKETSIZE, enable_4dmouse, },
+ { MOUSE_MODEL_4DPLUS, /* A4 Tech 4D+ Mouse */
+ 0xc8, MOUSE_4DPLUS_PACKETSIZE, enable_4dplus, },
+ { MOUSE_MODEL_INTELLI, /* Microsoft IntelliMouse */
+ 0x08, MOUSE_PS2INTELLI_PACKETSIZE, enable_msintelli, },
+ { MOUSE_MODEL_GLIDEPOINT, /* ALPS GlidePoint */
+ 0xc0, MOUSE_PS2_PACKETSIZE, enable_aglide, },
+ { MOUSE_MODEL_THINK, /* Kensignton ThinkingMouse */
+ 0x80, MOUSE_PS2_PACKETSIZE, enable_kmouse, },
+ { MOUSE_MODEL_VERSAPAD, /* Interlink electronics VersaPad */
+ 0xe8, MOUSE_PS2VERSA_PACKETSIZE, enable_versapad, },
+ { MOUSE_MODEL_GENERIC,
+ 0xc0, MOUSE_PS2_PACKETSIZE, NULL, },
+};
+#define GENERIC_MOUSE_ENTRY ((sizeof(vendortype) / sizeof(*vendortype)) - 1)
+
+/* device driver declarateion */
+static device_method_t psm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, psmidentify),
+ DEVMETHOD(device_probe, psmprobe),
+ DEVMETHOD(device_attach, psmattach),
+ DEVMETHOD(device_detach, psmdetach),
+ DEVMETHOD(device_resume, psmresume),
+
+ { 0, 0 }
+};
+
+static driver_t psm_driver = {
+ PSM_DRIVER_NAME,
+ psm_methods,
+ sizeof(struct psm_softc),
+};
+
+#define CDEV_MAJOR 21
+
+static struct cdevsw psm_cdevsw = {
+ /* open */ psmopen,
+ /* close */ psmclose,
+ /* read */ psmread,
+ /* write */ nowrite,
+ /* ioctl */ psmioctl,
+ /* poll */ psmpoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ PSM_DRIVER_NAME,
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+};
+
+/* debug message level */
+static int verbose = PSM_DEBUG;
+
+/* device I/O routines */
+static int
+enable_aux_dev(KBDC kbdc)
+{
+ int res;
+
+ res = send_aux_command(kbdc, PSMC_ENABLE_DEV);
+ if (verbose >= 2)
+ log(LOG_DEBUG, "psm: ENABLE_DEV return code:%04x\n", res);
+
+ return (res == PSM_ACK);
+}
+
+static int
+disable_aux_dev(KBDC kbdc)
+{
+ int res;
+
+ res = send_aux_command(kbdc, PSMC_DISABLE_DEV);
+ if (verbose >= 2)
+ log(LOG_DEBUG, "psm: DISABLE_DEV return code:%04x\n", res);
+
+ return (res == PSM_ACK);
+}
+
+static int
+get_mouse_status(KBDC kbdc, int *status, int flag, int len)
+{
+ int cmd;
+ int res;
+ int i;
+
+ switch (flag) {
+ case 0:
+ default:
+ cmd = PSMC_SEND_DEV_STATUS;
+ break;
+ case 1:
+ cmd = PSMC_SEND_DEV_DATA;
+ break;
+ }
+ empty_aux_buffer(kbdc, 5);
+ res = send_aux_command(kbdc, cmd);
+ if (verbose >= 2)
+ log(LOG_DEBUG, "psm: SEND_AUX_DEV_%s return code:%04x\n",
+ (flag == 1) ? "DATA" : "STATUS", res);
+ if (res != PSM_ACK)
+ return 0;
+
+ for (i = 0; i < len; ++i) {
+ status[i] = read_aux_data(kbdc);
+ if (status[i] < 0)
+ break;
+ }
+
+ if (verbose) {
+ log(LOG_DEBUG, "psm: %s %02x %02x %02x\n",
+ (flag == 1) ? "data" : "status", status[0], status[1], status[2]);
+ }
+
+ return i;
+}
+
+static int
+get_aux_id(KBDC kbdc)
+{
+ int res;
+ int id;
+
+ empty_aux_buffer(kbdc, 5);
+ res = send_aux_command(kbdc, PSMC_SEND_DEV_ID);
+ if (verbose >= 2)
+ log(LOG_DEBUG, "psm: SEND_DEV_ID return code:%04x\n", res);
+ if (res != PSM_ACK)
+ return (-1);
+
+ /* 10ms delay */
+ DELAY(10000);
+
+ id = read_aux_data(kbdc);
+ if (verbose >= 2)
+ log(LOG_DEBUG, "psm: device ID: %04x\n", id);
+
+ return id;
+}
+
+static int
+set_mouse_sampling_rate(KBDC kbdc, int rate)
+{
+ int res;
+
+ res = send_aux_command_and_data(kbdc, PSMC_SET_SAMPLING_RATE, rate);
+ if (verbose >= 2)
+ log(LOG_DEBUG, "psm: SET_SAMPLING_RATE (%d) %04x\n", rate, res);
+
+ return ((res == PSM_ACK) ? rate : -1);
+}
+
+static int
+set_mouse_scaling(KBDC kbdc, int scale)
+{
+ int res;
+
+ switch (scale) {
+ case 1:
+ default:
+ scale = PSMC_SET_SCALING11;
+ break;
+ case 2:
+ scale = PSMC_SET_SCALING21;
+ break;
+ }
+ res = send_aux_command(kbdc, scale);
+ if (verbose >= 2)
+ log(LOG_DEBUG, "psm: SET_SCALING%s return code:%04x\n",
+ (scale == PSMC_SET_SCALING21) ? "21" : "11", res);
+
+ return (res == PSM_ACK);
+}
+
+/* `val' must be 0 through PSMD_MAX_RESOLUTION */
+static int
+set_mouse_resolution(KBDC kbdc, int val)
+{
+ int res;
+
+ res = send_aux_command_and_data(kbdc, PSMC_SET_RESOLUTION, val);
+ if (verbose >= 2)
+ log(LOG_DEBUG, "psm: SET_RESOLUTION (%d) %04x\n", val, res);
+
+ return ((res == PSM_ACK) ? val : -1);
+}
+
+/*
+ * NOTE: once `set_mouse_mode()' is called, the mouse device must be
+ * re-enabled by calling `enable_aux_dev()'
+ */
+static int
+set_mouse_mode(KBDC kbdc)
+{
+ int res;
+
+ res = send_aux_command(kbdc, PSMC_SET_STREAM_MODE);
+ if (verbose >= 2)
+ log(LOG_DEBUG, "psm: SET_STREAM_MODE return code:%04x\n", res);
+
+ return (res == PSM_ACK);
+}
+
+static int
+get_mouse_buttons(KBDC kbdc)
+{
+ int c = 2; /* assume two buttons by default */
+ int status[3];
+
+ /*
+ * NOTE: a special sequence to obtain Logitech Mouse specific
+ * information: set resolution to 25 ppi, set scaling to 1:1, set
+ * scaling to 1:1, set scaling to 1:1. Then the second byte of the
+ * mouse status bytes is the number of available buttons.
+ * Some manufactures also support this sequence.
+ */
+ if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW)
+ return c;
+ if (set_mouse_scaling(kbdc, 1) && set_mouse_scaling(kbdc, 1)
+ && set_mouse_scaling(kbdc, 1)
+ && (get_mouse_status(kbdc, status, 0, 3) >= 3)) {
+ if (status[1] != 0)
+ return status[1];
+ }
+ return c;
+}
+
+/* misc subroutines */
+/*
+ * Someday, I will get the complete list of valid pointing devices and
+ * their IDs... XXX
+ */
+static int
+is_a_mouse(int id)
+{
+#if 0
+ static int valid_ids[] = {
+ PSM_MOUSE_ID, /* mouse */
+ PSM_BALLPOINT_ID, /* ballpoint device */
+ PSM_INTELLI_ID, /* Intellimouse */
+ PSM_EXPLORER_ID, /* Intellimouse Explorer */
+ -1 /* end of table */
+ };
+ int i;
+
+ for (i = 0; valid_ids[i] >= 0; ++i)
+ if (valid_ids[i] == id)
+ return TRUE;
+ return FALSE;
+#else
+ return TRUE;
+#endif
+}
+
+static char *
+model_name(int model)
+{
+ static struct {
+ int model_code;
+ char *model_name;
+ } models[] = {
+ { MOUSE_MODEL_NETSCROLL, "NetScroll" },
+ { MOUSE_MODEL_NET, "NetMouse/NetScroll Optical" },
+ { MOUSE_MODEL_GLIDEPOINT, "GlidePoint" },
+ { MOUSE_MODEL_THINK, "ThinkingMouse" },
+ { MOUSE_MODEL_INTELLI, "IntelliMouse" },
+ { MOUSE_MODEL_MOUSEMANPLUS, "MouseMan+" },
+ { MOUSE_MODEL_VERSAPAD, "VersaPad" },
+ { MOUSE_MODEL_EXPLORER, "IntelliMouse Explorer" },
+ { MOUSE_MODEL_4D, "4D Mouse" },
+ { MOUSE_MODEL_4DPLUS, "4D+ Mouse" },
+ { MOUSE_MODEL_GENERIC, "Generic PS/2 mouse" },
+ { MOUSE_MODEL_UNKNOWN, NULL },
+ };
+ int i;
+
+ for (i = 0; models[i].model_code != MOUSE_MODEL_UNKNOWN; ++i) {
+ if (models[i].model_code == model)
+ return models[i].model_name;
+ }
+ return "Unknown";
+}
+
+static void
+recover_from_error(KBDC kbdc)
+{
+ /* discard anything left in the output buffer */
+ empty_both_buffers(kbdc, 10);
+
+#if 0
+ /*
+ * NOTE: KBDC_RESET_KBD may not restore the communication between the
+ * keyboard and the controller.
+ */
+ reset_kbd(kbdc);
+#else
+ /*
+ * NOTE: somehow diagnostic and keyboard port test commands bring the
+ * keyboard back.
+ */
+ if (!test_controller(kbdc))
+ log(LOG_ERR, "psm: keyboard controller failed.\n");
+ /* if there isn't a keyboard in the system, the following error is OK */
+ if (test_kbd_port(kbdc) != 0) {
+ if (verbose)
+ log(LOG_ERR, "psm: keyboard port failed.\n");
+ }
+#endif
+}
+
+static int
+restore_controller(KBDC kbdc, int command_byte)
+{
+ empty_both_buffers(kbdc, 10);
+
+ if (!set_controller_command_byte(kbdc, 0xff, command_byte)) {
+ log(LOG_ERR, "psm: failed to restore the keyboard controller "
+ "command byte.\n");
+ empty_both_buffers(kbdc, 10);
+ return FALSE;
+ } else {
+ empty_both_buffers(kbdc, 10);
+ return TRUE;
+ }
+}
+
+/*
+ * Re-initialize the aux port and device. The aux port must be enabled
+ * and its interrupt must be disabled before calling this routine.
+ * The aux device will be disabled before returning.
+ * The keyboard controller must be locked via `kbdc_lock()' before
+ * calling this routine.
+ */
+static int
+doinitialize(struct psm_softc *sc, mousemode_t *mode)
+{
+ KBDC kbdc = sc->kbdc;
+ int stat[3];
+ int i;
+
+ switch((i = test_aux_port(kbdc))) {
+ case 1: /* ignore this error */
+ case PSM_ACK:
+ if (verbose)
+ log(LOG_DEBUG, "psm%d: strange result for test aux port (%d).\n",
+ sc->unit, i);
+ /* FALLTHROUGH */
+ case 0: /* no error */
+ break;
+ case -1: /* time out */
+ default: /* error */
+ recover_from_error(kbdc);
+ if (sc->config & PSM_CONFIG_IGNPORTERROR)
+ break;
+ log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n",
+ sc->unit, i);
+ return FALSE;
+ }
+
+ if (sc->config & PSM_CONFIG_NORESET) {
+ /*
+ * Don't try to reset the pointing device. It may possibly be
+ * left in the unknown state, though...
+ */
+ } else {
+ /*
+ * NOTE: some controllers appears to hang the `keyboard' when
+ * the aux port doesn't exist and `PSMC_RESET_DEV' is issued.
+ */
+ if (!reset_aux_dev(kbdc)) {
+ recover_from_error(kbdc);
+ log(LOG_ERR, "psm%d: failed to reset the aux device.\n", sc->unit);
+ return FALSE;
+ }
+ }
+
+ /*
+ * both the aux port and the aux device is functioning, see
+ * if the device can be enabled.
+ */
+ if (!enable_aux_dev(kbdc) || !disable_aux_dev(kbdc)) {
+ log(LOG_ERR, "psm%d: failed to enable the aux device.\n", sc->unit);
+ return FALSE;
+ }
+ empty_both_buffers(kbdc, 10); /* remove stray data if any */
+
+ if (sc->config & PSM_CONFIG_NOIDPROBE) {
+ i = GENERIC_MOUSE_ENTRY;
+ } else {
+ /* FIXME: hardware ID, mouse buttons? */
+
+ /* other parameters */
+ for (i = 0; vendortype[i].probefunc != NULL; ++i) {
+ if ((*vendortype[i].probefunc)(sc)) {
+ if (verbose >= 2)
+ log(LOG_ERR, "psm%d: found %s\n",
+ sc->unit, model_name(vendortype[i].model));
+ break;
+ }
+ }
+ }
+
+ sc->hw.model = vendortype[i].model;
+ sc->mode.packetsize = vendortype[i].packetsize;
+
+ /* set mouse parameters */
+ if (mode != (mousemode_t *)NULL) {
+ if (mode->rate > 0)
+ mode->rate = set_mouse_sampling_rate(kbdc, mode->rate);
+ if (mode->resolution >= 0)
+ mode->resolution = set_mouse_resolution(kbdc, mode->resolution);
+ set_mouse_scaling(kbdc, 1);
+ set_mouse_mode(kbdc);
+ }
+
+ /* request a data packet and extract sync. bits */
+ if (get_mouse_status(kbdc, stat, 1, 3) < 3) {
+ log(LOG_DEBUG, "psm%d: failed to get data (doinitialize).\n",
+ sc->unit);
+ sc->mode.syncmask[0] = 0;
+ } else {
+ sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0]; /* syncbits */
+ /* the NetScroll Mouse will send three more bytes... Ignore them */
+ empty_aux_buffer(kbdc, 5);
+ }
+
+ /* just check the status of the mouse */
+ if (get_mouse_status(kbdc, stat, 0, 3) < 3)
+ log(LOG_DEBUG, "psm%d: failed to get status (doinitialize).\n",
+ sc->unit);
+
+ return TRUE;
+}
+
+static int
+doopen(struct psm_softc *sc, int command_byte)
+{
+ int stat[3];
+
+ /* enable the mouse device */
+ if (!enable_aux_dev(sc->kbdc)) {
+ /* MOUSE ERROR: failed to enable the mouse because:
+ * 1) the mouse is faulty,
+ * 2) the mouse has been removed(!?)
+ * In the latter case, the keyboard may have hung, and need
+ * recovery procedure...
+ */
+ recover_from_error(sc->kbdc);
+#if 0
+ /* FIXME: we could reset the mouse here and try to enable
+ * it again. But it will take long time and it's not a good
+ * idea to disable the keyboard that long...
+ */
+ if (!doinitialize(sc, &sc->mode) || !enable_aux_dev(sc->kbdc)) {
+ recover_from_error(sc->kbdc);
+#else
+ {
+#endif
+ restore_controller(sc->kbdc, command_byte);
+ /* mark this device is no longer available */
+ sc->state &= ~PSM_VALID;
+ log(LOG_ERR, "psm%d: failed to enable the device (doopen).\n",
+ sc->unit);
+ return (EIO);
+ }
+ }
+
+ if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3)
+ log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", sc->unit);
+
+ /* enable the aux port and interrupt */
+ if (!set_controller_command_byte(sc->kbdc,
+ kbdc_get_device_mask(sc->kbdc),
+ (command_byte & KBD_KBD_CONTROL_BITS)
+ | KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT)) {
+ /* CONTROLLER ERROR */
+ disable_aux_dev(sc->kbdc);
+ restore_controller(sc->kbdc, command_byte);
+ log(LOG_ERR, "psm%d: failed to enable the aux interrupt (doopen).\n",
+ sc->unit);
+ return (EIO);
+ }
+
+ /* start the watchdog timer */
+ sc->watchdog = FALSE;
+ sc->callout = timeout(psmtimeout, (void *)(uintptr_t)sc, hz*2);
+
+ return (0);
+}
+
+static int
+reinitialize(struct psm_softc *sc, int doinit)
+{
+ int err;
+ int c;
+ int s;
+
+ /* don't let anybody mess with the aux device */
+ if (!kbdc_lock(sc->kbdc, TRUE))
+ return (EIO);
+ s = spltty();
+
+ /* block our watchdog timer */
+ sc->watchdog = FALSE;
+ untimeout(psmtimeout, (void *)(uintptr_t)sc, sc->callout);
+ callout_handle_init(&sc->callout);
+
+ /* save the current controller command byte */
+ empty_both_buffers(sc->kbdc, 10);
+ c = get_controller_command_byte(sc->kbdc);
+ if (verbose >= 2)
+ log(LOG_DEBUG, "psm%d: current command byte: %04x (reinitialize).\n",
+ sc->unit, c);
+
+ /* enable the aux port but disable the aux interrupt and the keyboard */
+ if ((c == -1) || !set_controller_command_byte(sc->kbdc,
+ kbdc_get_device_mask(sc->kbdc),
+ KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
+ | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+ /* CONTROLLER ERROR */
+ splx(s);
+ kbdc_lock(sc->kbdc, FALSE);
+ log(LOG_ERR, "psm%d: unable to set the command byte (reinitialize).\n",
+ sc->unit);
+ return (EIO);
+ }
+
+ /* flush any data */
+ if (sc->state & PSM_VALID) {
+ disable_aux_dev(sc->kbdc); /* this may fail; but never mind... */
+ empty_aux_buffer(sc->kbdc, 10);
+ }
+ sc->inputbytes = 0;
+ sc->syncerrors = 0;
+
+ /* try to detect the aux device; are you still there? */
+ err = 0;
+ if (doinit) {
+ if (doinitialize(sc, &sc->mode)) {
+ /* yes */
+ sc->state |= PSM_VALID;
+ } else {
+ /* the device has gone! */
+ restore_controller(sc->kbdc, c);
+ sc->state &= ~PSM_VALID;
+ log(LOG_ERR, "psm%d: the aux device has gone! (reinitialize).\n",
+ sc->unit);
+ err = ENXIO;
+ }
+ }
+ splx(s);
+
+ /* restore the driver state */
+ if ((sc->state & PSM_OPEN) && (err == 0)) {
+ /* enable the aux device and the port again */
+ err = doopen(sc, c);
+ if (err != 0)
+ log(LOG_ERR, "psm%d: failed to enable the device (reinitialize).\n",
+ sc->unit);
+ } else {
+ /* restore the keyboard port and disable the aux port */
+ if (!set_controller_command_byte(sc->kbdc,
+ kbdc_get_device_mask(sc->kbdc),
+ (c & KBD_KBD_CONTROL_BITS)
+ | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+ /* CONTROLLER ERROR */
+ log(LOG_ERR, "psm%d: failed to disable the aux port (reinitialize).\n",
+ sc->unit);
+ err = EIO;
+ }
+ }
+
+ kbdc_lock(sc->kbdc, FALSE);
+ return (err);
+}
+
+/* psm driver entry points */
+
+static void
+psmidentify(driver_t *driver, device_t parent)
+{
+ device_t psmc;
+ device_t psm;
+ u_long irq;
+ int unit;
+
+ unit = device_get_unit(parent);
+
+ /* always add at least one child */
+ psm = BUS_ADD_CHILD(parent, KBDC_RID_AUX, driver->name, unit);
+ if (psm == NULL)
+ return;
+
+ irq = bus_get_resource_start(psm, SYS_RES_IRQ, KBDC_RID_AUX);
+ if (irq > 0)
+ return;
+
+ /*
+ * If the PS/2 mouse device has already been reported by ACPI or
+ * PnP BIOS, obtain the IRQ resource from it.
+ * (See psmcpnp_attach() below.)
+ */
+ psmc = device_find_child(device_get_parent(parent),
+ PSMCPNP_DRIVER_NAME, unit);
+ if (psmc == NULL)
+ return;
+ irq = bus_get_resource_start(psmc, SYS_RES_IRQ, 0);
+ if (irq <= 0)
+ return;
+ bus_set_resource(psm, SYS_RES_IRQ, KBDC_RID_AUX, irq, 1);
+}
+
+#define endprobe(v) { if (bootverbose) \
+ --verbose; \
+ kbdc_set_device_mask(sc->kbdc, mask); \
+ kbdc_lock(sc->kbdc, FALSE); \
+ return (v); \
+ }
+
+static int
+psmprobe(device_t dev)
+{
+ int unit = device_get_unit(dev);
+ struct psm_softc *sc = device_get_softc(dev);
+ int stat[3];
+ int command_byte;
+ int mask;
+ int rid;
+ int i;
+
+#if 0
+ kbdc_debug(TRUE);
+#endif
+
+ /* see if IRQ is available */
+ rid = KBDC_RID_AUX;
+ sc->intr = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->intr == NULL) {
+ if (bootverbose)
+ device_printf(dev, "unable to allocate IRQ\n");
+ return (ENXIO);
+ }
+ bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr);
+
+ sc->unit = unit;
+ sc->kbdc = atkbdc_open(device_get_unit(device_get_parent(dev)));
+ sc->config = device_get_flags(dev) & PSM_CONFIG_FLAGS;
+ /* XXX: for backward compatibility */
+#if defined(PSM_HOOKRESUME) || defined(PSM_HOOKAPM)
+ sc->config |=
+#ifdef PSM_RESETAFTERSUSPEND
+ PSM_CONFIG_HOOKRESUME | PSM_CONFIG_INITAFTERSUSPEND;
+#else
+ PSM_CONFIG_HOOKRESUME;
+#endif
+#endif /* PSM_HOOKRESUME | PSM_HOOKAPM */
+ sc->flags = 0;
+ if (bootverbose)
+ ++verbose;
+
+ device_set_desc(dev, "PS/2 Mouse");
+
+ if (!kbdc_lock(sc->kbdc, TRUE)) {
+ printf("psm%d: unable to lock the controller.\n", unit);
+ if (bootverbose)
+ --verbose;
+ return (ENXIO);
+ }
+
+ /*
+ * NOTE: two bits in the command byte controls the operation of the
+ * aux port (mouse port): the aux port disable bit (bit 5) and the aux
+ * port interrupt (IRQ 12) enable bit (bit 2).
+ */
+
+ /* discard anything left after the keyboard initialization */
+ empty_both_buffers(sc->kbdc, 10);
+
+ /* save the current command byte; it will be used later */
+ mask = kbdc_get_device_mask(sc->kbdc) & ~KBD_AUX_CONTROL_BITS;
+ command_byte = get_controller_command_byte(sc->kbdc);
+ if (verbose)
+ printf("psm%d: current command byte:%04x\n", unit, command_byte);
+ if (command_byte == -1) {
+ /* CONTROLLER ERROR */
+ printf("psm%d: unable to get the current command byte value.\n",
+ unit);
+ endprobe(ENXIO);
+ }
+
+ /*
+ * disable the keyboard port while probing the aux port, which must be
+ * enabled during this routine
+ */
+ if (!set_controller_command_byte(sc->kbdc,
+ KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS,
+ KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
+ | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+ /*
+ * this is CONTROLLER ERROR; I don't know how to recover
+ * from this error...
+ */
+ restore_controller(sc->kbdc, command_byte);
+ printf("psm%d: unable to set the command byte.\n", unit);
+ endprobe(ENXIO);
+ }
+ write_controller_command(sc->kbdc, KBDC_ENABLE_AUX_PORT);
+
+ /*
+ * NOTE: `test_aux_port()' is designed to return with zero if the aux
+ * port exists and is functioning. However, some controllers appears
+ * to respond with zero even when the aux port doesn't exist. (It may
+ * be that this is only the case when the controller DOES have the aux
+ * port but the port is not wired on the motherboard.) The keyboard
+ * controllers without the port, such as the original AT, are
+ * supporsed to return with an error code or simply time out. In any
+ * case, we have to continue probing the port even when the controller
+ * passes this test.
+ *
+ * XXX: some controllers erroneously return the error code 1 when
+ * it has the perfectly functional aux port. We have to ignore this
+ * error code. Even if the controller HAS error with the aux port,
+ * it will be detected later...
+ * XXX: another incompatible controller returns PSM_ACK (0xfa)...
+ */
+ switch ((i = test_aux_port(sc->kbdc))) {
+ case 1: /* ignore this error */
+ case PSM_ACK:
+ if (verbose)
+ printf("psm%d: strange result for test aux port (%d).\n",
+ unit, i);
+ /* FALLTHROUGH */
+ case 0: /* no error */
+ break;
+ case -1: /* time out */
+ default: /* error */
+ recover_from_error(sc->kbdc);
+ if (sc->config & PSM_CONFIG_IGNPORTERROR)
+ break;
+ restore_controller(sc->kbdc, command_byte);
+ if (verbose)
+ printf("psm%d: the aux port is not functioning (%d).\n",
+ unit, i);
+ endprobe(ENXIO);
+ }
+
+ if (sc->config & PSM_CONFIG_NORESET) {
+ /*
+ * Don't try to reset the pointing device. It may possibly be
+ * left in the unknown state, though...
+ */
+ } else {
+ /*
+ * NOTE: some controllers appears to hang the `keyboard' when the aux
+ * port doesn't exist and `PSMC_RESET_DEV' is issued.
+ */
+ if (!reset_aux_dev(sc->kbdc)) {
+ recover_from_error(sc->kbdc);
+ restore_controller(sc->kbdc, command_byte);
+ if (verbose)
+ printf("psm%d: failed to reset the aux device.\n", unit);
+ endprobe(ENXIO);
+ }
+ }
+
+ /*
+ * both the aux port and the aux device is functioning, see if the
+ * device can be enabled. NOTE: when enabled, the device will start
+ * sending data; we shall immediately disable the device once we know
+ * the device can be enabled.
+ */
+ if (!enable_aux_dev(sc->kbdc) || !disable_aux_dev(sc->kbdc)) {
+ /* MOUSE ERROR */
+ recover_from_error(sc->kbdc);
+ restore_controller(sc->kbdc, command_byte);
+ if (verbose)
+ printf("psm%d: failed to enable the aux device.\n", unit);
+ endprobe(ENXIO);
+ }
+
+ /* save the default values after reset */
+ if (get_mouse_status(sc->kbdc, stat, 0, 3) >= 3) {
+ sc->dflt_mode.rate = sc->mode.rate = stat[2];
+ sc->dflt_mode.resolution = sc->mode.resolution = stat[1];
+ } else {
+ sc->dflt_mode.rate = sc->mode.rate = -1;
+ sc->dflt_mode.resolution = sc->mode.resolution = -1;
+ }
+
+ /* hardware information */
+ sc->hw.iftype = MOUSE_IF_PS2;
+
+ /* verify the device is a mouse */
+ sc->hw.hwid = get_aux_id(sc->kbdc);
+ if (!is_a_mouse(sc->hw.hwid)) {
+ restore_controller(sc->kbdc, command_byte);
+ if (verbose)
+ printf("psm%d: unknown device type (%d).\n", unit, sc->hw.hwid);
+ endprobe(ENXIO);
+ }
+ switch (sc->hw.hwid) {
+ case PSM_BALLPOINT_ID:
+ sc->hw.type = MOUSE_TRACKBALL;
+ break;
+ case PSM_MOUSE_ID:
+ case PSM_INTELLI_ID:
+ case PSM_EXPLORER_ID:
+ case PSM_4DMOUSE_ID:
+ case PSM_4DPLUS_ID:
+ sc->hw.type = MOUSE_MOUSE;
+ break;
+ default:
+ sc->hw.type = MOUSE_UNKNOWN;
+ break;
+ }
+
+ if (sc->config & PSM_CONFIG_NOIDPROBE) {
+ sc->hw.buttons = 2;
+ i = GENERIC_MOUSE_ENTRY;
+ } else {
+ /* # of buttons */
+ sc->hw.buttons = get_mouse_buttons(sc->kbdc);
+
+ /* other parameters */
+ for (i = 0; vendortype[i].probefunc != NULL; ++i) {
+ if ((*vendortype[i].probefunc)(sc)) {
+ if (verbose >= 2)
+ printf("psm%d: found %s\n",
+ unit, model_name(vendortype[i].model));
+ break;
+ }
+ }
+ }
+
+ sc->hw.model = vendortype[i].model;
+
+ sc->dflt_mode.level = PSM_LEVEL_BASE;
+ sc->dflt_mode.packetsize = MOUSE_PS2_PACKETSIZE;
+ sc->dflt_mode.accelfactor = (sc->config & PSM_CONFIG_ACCEL) >> 4;
+ if (sc->config & PSM_CONFIG_NOCHECKSYNC)
+ sc->dflt_mode.syncmask[0] = 0;
+ else
+ sc->dflt_mode.syncmask[0] = vendortype[i].syncmask;
+ if (sc->config & PSM_CONFIG_FORCETAP)
+ sc->mode.syncmask[0] &= ~MOUSE_PS2_TAP;
+ sc->dflt_mode.syncmask[1] = 0; /* syncbits */
+ sc->mode = sc->dflt_mode;
+ sc->mode.packetsize = vendortype[i].packetsize;
+
+ /* set mouse parameters */
+#if 0
+ /*
+ * A version of Logitech FirstMouse+ won't report wheel movement,
+ * if SET_DEFAULTS is sent... Don't use this command.
+ * This fix was found by Takashi Nishida.
+ */
+ i = send_aux_command(sc->kbdc, PSMC_SET_DEFAULTS);
+ if (verbose >= 2)
+ printf("psm%d: SET_DEFAULTS return code:%04x\n", unit, i);
+#endif
+ if (sc->config & PSM_CONFIG_RESOLUTION) {
+ sc->mode.resolution
+ = set_mouse_resolution(sc->kbdc,
+ (sc->config & PSM_CONFIG_RESOLUTION) - 1);
+ } else if (sc->mode.resolution >= 0) {
+ sc->mode.resolution
+ = set_mouse_resolution(sc->kbdc, sc->dflt_mode.resolution);
+ }
+ if (sc->mode.rate > 0) {
+ sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, sc->dflt_mode.rate);
+ }
+ set_mouse_scaling(sc->kbdc, 1);
+
+ /* request a data packet and extract sync. bits */
+ if (get_mouse_status(sc->kbdc, stat, 1, 3) < 3) {
+ printf("psm%d: failed to get data.\n", unit);
+ sc->mode.syncmask[0] = 0;
+ } else {
+ sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0]; /* syncbits */
+ /* the NetScroll Mouse will send three more bytes... Ignore them */
+ empty_aux_buffer(sc->kbdc, 5);
+ }
+
+ /* just check the status of the mouse */
+ /*
+ * NOTE: XXX there are some arcane controller/mouse combinations out
+ * there, which hung the controller unless there is data transmission
+ * after ACK from the mouse.
+ */
+ if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) {
+ printf("psm%d: failed to get status.\n", unit);
+ } else {
+ /*
+ * When in its native mode, some mice operate with different
+ * default parameters than in the PS/2 compatible mode.
+ */
+ sc->dflt_mode.rate = sc->mode.rate = stat[2];
+ sc->dflt_mode.resolution = sc->mode.resolution = stat[1];
+ }
+
+ /* disable the aux port for now... */
+ if (!set_controller_command_byte(sc->kbdc,
+ KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS,
+ (command_byte & KBD_KBD_CONTROL_BITS)
+ | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+ /*
+ * this is CONTROLLER ERROR; I don't know the proper way to
+ * recover from this error...
+ */
+ restore_controller(sc->kbdc, command_byte);
+ printf("psm%d: unable to set the command byte.\n", unit);
+ endprobe(ENXIO);
+ }
+
+ /* done */
+ kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS);
+ kbdc_lock(sc->kbdc, FALSE);
+ return (0);
+}
+
+static int
+psmattach(device_t dev)
+{
+ int unit = device_get_unit(dev);
+ struct psm_softc *sc = device_get_softc(dev);
+ int error;
+ int rid;
+
+ if (sc == NULL) /* shouldn't happen */
+ return (ENXIO);
+
+ /* Setup initial state */
+ sc->state = PSM_VALID;
+ callout_handle_init(&sc->callout);
+
+ /* Setup our interrupt handler */
+ rid = KBDC_RID_AUX;
+ sc->intr = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->intr == NULL)
+ return (ENXIO);
+ error = bus_setup_intr(dev, sc->intr, INTR_TYPE_TTY, psmintr, sc, &sc->ih);
+ if (error) {
+ bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr);
+ return (error);
+ }
+
+ /* Done */
+ sc->dev = make_dev(&psm_cdevsw, PSM_MKMINOR(unit, FALSE), 0, 0, 0666,
+ "psm%d", unit);
+ sc->bdev = make_dev(&psm_cdevsw, PSM_MKMINOR(unit, TRUE), 0, 0, 0666,
+ "bpsm%d", unit);
+
+ if (!verbose) {
+ printf("psm%d: model %s, device ID %d\n",
+ unit, model_name(sc->hw.model), sc->hw.hwid & 0x00ff);
+ } else {
+ printf("psm%d: model %s, device ID %d-%02x, %d buttons\n",
+ unit, model_name(sc->hw.model),
+ sc->hw.hwid & 0x00ff, sc->hw.hwid >> 8, sc->hw.buttons);
+ printf("psm%d: config:%08x, flags:%08x, packet size:%d\n",
+ unit, sc->config, sc->flags, sc->mode.packetsize);
+ printf("psm%d: syncmask:%02x, syncbits:%02x\n",
+ unit, sc->mode.syncmask[0], sc->mode.syncmask[1]);
+ }
+
+ if (bootverbose)
+ --verbose;
+
+ return (0);
+}
+
+static int
+psmdetach(device_t dev)
+{
+ struct psm_softc *sc;
+ int rid;
+
+ sc = device_get_softc(dev);
+ if (sc->state & PSM_OPEN)
+ return EBUSY;
+
+ rid = KBDC_RID_AUX;
+ bus_teardown_intr(dev, sc->intr, sc->ih);
+ bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr);
+
+ destroy_dev(sc->dev);
+ destroy_dev(sc->bdev);
+
+ return 0;
+}
+
+static int
+psmopen(dev_t dev, int flag, int fmt, struct thread *td)
+{
+ int unit = PSM_UNIT(dev);
+ struct psm_softc *sc;
+ int command_byte;
+ int err;
+ int s;
+
+ /* Get device data */
+ sc = PSM_SOFTC(unit);
+ if ((sc == NULL) || (sc->state & PSM_VALID) == 0)
+ /* the device is no longer valid/functioning */
+ return (ENXIO);
+
+ /* Disallow multiple opens */
+ if (sc->state & PSM_OPEN)
+ return (EBUSY);
+
+ device_busy(devclass_get_device(psm_devclass, unit));
+
+ /* Initialize state */
+ sc->mode.level = sc->dflt_mode.level;
+ sc->mode.protocol = sc->dflt_mode.protocol;
+ sc->watchdog = FALSE;
+
+ /* flush the event queue */
+ sc->queue.count = 0;
+ sc->queue.head = 0;
+ sc->queue.tail = 0;
+ sc->status.flags = 0;
+ sc->status.button = 0;
+ sc->status.obutton = 0;
+ sc->status.dx = 0;
+ sc->status.dy = 0;
+ sc->status.dz = 0;
+ sc->button = 0;
+
+ /* empty input buffer */
+ bzero(sc->ipacket, sizeof(sc->ipacket));
+ sc->inputbytes = 0;
+ sc->syncerrors = 0;
+
+ /* don't let timeout routines in the keyboard driver to poll the kbdc */
+ if (!kbdc_lock(sc->kbdc, TRUE))
+ return (EIO);
+
+ /* save the current controller command byte */
+ s = spltty();
+ command_byte = get_controller_command_byte(sc->kbdc);
+
+ /* enable the aux port and temporalily disable the keyboard */
+ if ((command_byte == -1)
+ || !set_controller_command_byte(sc->kbdc,
+ kbdc_get_device_mask(sc->kbdc),
+ KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
+ | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+ /* CONTROLLER ERROR; do you know how to get out of this? */
+ kbdc_lock(sc->kbdc, FALSE);
+ splx(s);
+ log(LOG_ERR, "psm%d: unable to set the command byte (psmopen).\n",
+ unit);
+ 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 timeout routines will be blocked by the poll flag set
+ * via `kbdc_lock()'
+ */
+ splx(s);
+
+ /* enable the mouse device */
+ err = doopen(sc, command_byte);
+
+ /* done */
+ if (err == 0)
+ sc->state |= PSM_OPEN;
+ kbdc_lock(sc->kbdc, FALSE);
+ return (err);
+}
+
+static int
+psmclose(dev_t dev, int flag, int fmt, struct thread *td)
+{
+ int unit = PSM_UNIT(dev);
+ struct psm_softc *sc = PSM_SOFTC(unit);
+ int stat[3];
+ int command_byte;
+ int s;
+
+ /* don't let timeout routines in the keyboard driver to poll the kbdc */
+ if (!kbdc_lock(sc->kbdc, TRUE))
+ return (EIO);
+
+ /* save the current controller command byte */
+ s = spltty();
+ command_byte = get_controller_command_byte(sc->kbdc);
+ if (command_byte == -1) {
+ kbdc_lock(sc->kbdc, FALSE);
+ splx(s);
+ return (EIO);
+ }
+
+ /* disable the aux interrupt and temporalily disable the keyboard */
+ if (!set_controller_command_byte(sc->kbdc,
+ kbdc_get_device_mask(sc->kbdc),
+ KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
+ | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+ log(LOG_ERR, "psm%d: failed to disable the aux int (psmclose).\n",
+ unit);
+ /* CONTROLLER ERROR;
+ * NOTE: we shall force our way through. Because the only
+ * ill effect we shall see is that we may not be able
+ * to read ACK from the mouse, and it doesn't matter much
+ * so long as the mouse will accept the DISABLE command.
+ */
+ }
+ splx(s);
+
+ /* stop the watchdog timer */
+ untimeout(psmtimeout, (void *)(uintptr_t)sc, sc->callout);
+ callout_handle_init(&sc->callout);
+
+ /* remove anything left in the output buffer */
+ empty_aux_buffer(sc->kbdc, 10);
+
+ /* disable the aux device, port and interrupt */
+ if (sc->state & PSM_VALID) {
+ if (!disable_aux_dev(sc->kbdc)) {
+ /* MOUSE ERROR;
+ * NOTE: we don't return error and continue, pretending
+ * we have successfully disabled the device. It's OK because
+ * the interrupt routine will discard any data from the mouse
+ * hereafter.
+ */
+ log(LOG_ERR, "psm%d: failed to disable the device (psmclose).\n",
+ unit);
+ }
+
+ if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3)
+ log(LOG_DEBUG, "psm%d: failed to get status (psmclose).\n",
+ unit);
+ }
+
+ if (!set_controller_command_byte(sc->kbdc,
+ kbdc_get_device_mask(sc->kbdc),
+ (command_byte & KBD_KBD_CONTROL_BITS)
+ | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+ /* CONTROLLER ERROR;
+ * we shall ignore this error; see the above comment.
+ */
+ log(LOG_ERR, "psm%d: failed to disable the aux port (psmclose).\n",
+ unit);
+ }
+
+ /* remove anything left in the output buffer */
+ empty_aux_buffer(sc->kbdc, 10);
+
+ /* close is almost always successful */
+ sc->state &= ~PSM_OPEN;
+ kbdc_lock(sc->kbdc, FALSE);
+ device_unbusy(devclass_get_device(psm_devclass, unit));
+ return (0);
+}
+
+static int
+tame_mouse(struct psm_softc *sc, mousestatus_t *status, unsigned char *buf)
+{
+ static unsigned char butmapps2[8] = {
+ 0,
+ MOUSE_PS2_BUTTON1DOWN,
+ MOUSE_PS2_BUTTON2DOWN,
+ MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN,
+ MOUSE_PS2_BUTTON3DOWN,
+ MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON3DOWN,
+ MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN,
+ MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN,
+ };
+ static unsigned char butmapmsc[8] = {
+ MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
+ MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
+ MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP,
+ MOUSE_MSC_BUTTON3UP,
+ MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP,
+ MOUSE_MSC_BUTTON2UP,
+ MOUSE_MSC_BUTTON1UP,
+ 0,
+ };
+ int mapped;
+ int i;
+
+ if (sc->mode.level == PSM_LEVEL_BASE) {
+ mapped = status->button & ~MOUSE_BUTTON4DOWN;
+ if (status->button & MOUSE_BUTTON4DOWN)
+ mapped |= MOUSE_BUTTON1DOWN;
+ status->button = mapped;
+ buf[0] = MOUSE_PS2_SYNC | butmapps2[mapped & MOUSE_STDBUTTONS];
+ i = max(min(status->dx, 255), -256);
+ if (i < 0)
+ buf[0] |= MOUSE_PS2_XNEG;
+ buf[1] = i;
+ i = max(min(status->dy, 255), -256);
+ if (i < 0)
+ buf[0] |= MOUSE_PS2_YNEG;
+ buf[2] = i;
+ return MOUSE_PS2_PACKETSIZE;
+ } else if (sc->mode.level == PSM_LEVEL_STANDARD) {
+ buf[0] = MOUSE_MSC_SYNC | butmapmsc[status->button & MOUSE_STDBUTTONS];
+ i = max(min(status->dx, 255), -256);
+ buf[1] = i >> 1;
+ buf[3] = i - buf[1];
+ i = max(min(status->dy, 255), -256);
+ buf[2] = i >> 1;
+ buf[4] = i - buf[2];
+ i = max(min(status->dz, 127), -128);
+ buf[5] = (i >> 1) & 0x7f;
+ buf[6] = (i - (i >> 1)) & 0x7f;
+ buf[7] = (~status->button >> 3) & 0x7f;
+ return MOUSE_SYS_PACKETSIZE;
+ }
+ return sc->inputbytes;;
+}
+
+static int
+psmread(dev_t dev, struct uio *uio, int flag)
+{
+ register struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
+ unsigned char buf[PSM_SMALLBUFSIZE];
+ int error = 0;
+ int s;
+ int l;
+
+ if ((sc->state & PSM_VALID) == 0)
+ return EIO;
+
+ /* block until mouse activity occured */
+ s = spltty();
+ while (sc->queue.count <= 0) {
+ if (PSM_NBLOCKIO(dev)) {
+ splx(s);
+ return EWOULDBLOCK;
+ }
+ sc->state |= PSM_ASLP;
+ error = tsleep((caddr_t) sc, PZERO | PCATCH, "psmrea", 0);
+ sc->state &= ~PSM_ASLP;
+ if (error) {
+ splx(s);
+ return error;
+ } else if ((sc->state & PSM_VALID) == 0) {
+ /* the device disappeared! */
+ splx(s);
+ return EIO;
+ }
+ }
+ splx(s);
+
+ /* copy data to the user land */
+ while ((sc->queue.count > 0) && (uio->uio_resid > 0)) {
+ s = spltty();
+ l = min(sc->queue.count, uio->uio_resid);
+ if (l > sizeof(buf))
+ l = sizeof(buf);
+ if (l > sizeof(sc->queue.buf) - sc->queue.head) {
+ bcopy(&sc->queue.buf[sc->queue.head], &buf[0],
+ sizeof(sc->queue.buf) - sc->queue.head);
+ bcopy(&sc->queue.buf[0],
+ &buf[sizeof(sc->queue.buf) - sc->queue.head],
+ l - (sizeof(sc->queue.buf) - sc->queue.head));
+ } else {
+ bcopy(&sc->queue.buf[sc->queue.head], &buf[0], l);
+ }
+ sc->queue.count -= l;
+ sc->queue.head = (sc->queue.head + l) % sizeof(sc->queue.buf);
+ splx(s);
+ error = uiomove(buf, l, uio);
+ if (error)
+ break;
+ }
+
+ return error;
+}
+
+static int
+block_mouse_data(struct psm_softc *sc, int *c)
+{
+ int s;
+
+ if (!kbdc_lock(sc->kbdc, TRUE))
+ return EIO;
+
+ s = spltty();
+ *c = get_controller_command_byte(sc->kbdc);
+ if ((*c == -1)
+ || !set_controller_command_byte(sc->kbdc,
+ kbdc_get_device_mask(sc->kbdc),
+ KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
+ | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+ /* this is CONTROLLER ERROR */
+ splx(s);
+ kbdc_lock(sc->kbdc, FALSE);
+ return EIO;
+ }
+
+ /*
+ * The device may be in the middle of status data transmission.
+ * The transmission will be interrupted, thus, incomplete status
+ * data must be discarded. Although the aux interrupt is disabled
+ * at the keyboard controller level, at most one aux interrupt
+ * may have already been pending and a data byte is in the
+ * output buffer; throw it away. Note that the second argument
+ * to `empty_aux_buffer()' is zero, so that the call will just
+ * flush the internal queue.
+ * `psmintr()' will be invoked after `splx()' if an interrupt is
+ * pending; it will see no data and returns immediately.
+ */
+ empty_aux_buffer(sc->kbdc, 0); /* flush the queue */
+ read_aux_data_no_wait(sc->kbdc); /* throw away data if any */
+ sc->inputbytes = 0;
+ splx(s);
+
+ return 0;
+}
+
+static int
+unblock_mouse_data(struct psm_softc *sc, int c)
+{
+ int error = 0;
+
+ /*
+ * We may have seen a part of status data during `set_mouse_XXX()'.
+ * they have been queued; flush it.
+ */
+ empty_aux_buffer(sc->kbdc, 0);
+
+ /* restore ports and interrupt */
+ if (!set_controller_command_byte(sc->kbdc,
+ kbdc_get_device_mask(sc->kbdc),
+ c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) {
+ /* CONTROLLER ERROR; this is serious, we may have
+ * been left with the inaccessible keyboard and
+ * the disabled mouse interrupt.
+ */
+ error = EIO;
+ }
+
+ kbdc_lock(sc->kbdc, FALSE);
+ return error;
+}
+
+static int
+psmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
+{
+ struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
+ mousemode_t mode;
+ mousestatus_t status;
+#if (defined(MOUSE_GETVARS))
+ mousevar_t *var;
+#endif
+ mousedata_t *data;
+ int stat[3];
+ int command_byte;
+ int error = 0;
+ int s;
+
+ /* Perform IOCTL command */
+ switch (cmd) {
+
+ case OLD_MOUSE_GETHWINFO:
+ s = spltty();
+ ((old_mousehw_t *)addr)->buttons = sc->hw.buttons;
+ ((old_mousehw_t *)addr)->iftype = sc->hw.iftype;
+ ((old_mousehw_t *)addr)->type = sc->hw.type;
+ ((old_mousehw_t *)addr)->hwid = sc->hw.hwid & 0x00ff;
+ splx(s);
+ break;
+
+ case MOUSE_GETHWINFO:
+ s = spltty();
+ *(mousehw_t *)addr = sc->hw;
+ if (sc->mode.level == PSM_LEVEL_BASE)
+ ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC;
+ splx(s);
+ break;
+
+ case OLD_MOUSE_GETMODE:
+ s = spltty();
+ switch (sc->mode.level) {
+ case PSM_LEVEL_BASE:
+ ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
+ break;
+ case PSM_LEVEL_STANDARD:
+ ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
+ break;
+ case PSM_LEVEL_NATIVE:
+ ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
+ break;
+ }
+ ((old_mousemode_t *)addr)->rate = sc->mode.rate;
+ ((old_mousemode_t *)addr)->resolution = sc->mode.resolution;
+ ((old_mousemode_t *)addr)->accelfactor = sc->mode.accelfactor;
+ splx(s);
+ break;
+
+ case MOUSE_GETMODE:
+ s = spltty();
+ *(mousemode_t *)addr = sc->mode;
+ ((mousemode_t *)addr)->resolution =
+ MOUSE_RES_LOW - sc->mode.resolution;
+ switch (sc->mode.level) {
+ case PSM_LEVEL_BASE:
+ ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
+ ((mousemode_t *)addr)->packetsize = MOUSE_PS2_PACKETSIZE;
+ break;
+ case PSM_LEVEL_STANDARD:
+ ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
+ ((mousemode_t *)addr)->packetsize = MOUSE_SYS_PACKETSIZE;
+ ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK;
+ ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC;
+ break;
+ case PSM_LEVEL_NATIVE:
+ /* FIXME: this isn't quite correct... XXX */
+ ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
+ break;
+ }
+ splx(s);
+ break;
+
+ case OLD_MOUSE_SETMODE:
+ case MOUSE_SETMODE:
+ if (cmd == OLD_MOUSE_SETMODE) {
+ mode.rate = ((old_mousemode_t *)addr)->rate;
+ /*
+ * resolution old I/F new I/F
+ * default 0 0
+ * low 1 -2
+ * medium low 2 -3
+ * medium high 3 -4
+ * high 4 -5
+ */
+ if (((old_mousemode_t *)addr)->resolution > 0)
+ mode.resolution = -((old_mousemode_t *)addr)->resolution - 1;
+ mode.accelfactor = ((old_mousemode_t *)addr)->accelfactor;
+ mode.level = -1;
+ } else {
+ mode = *(mousemode_t *)addr;
+ }
+
+ /* adjust and validate parameters. */
+ if (mode.rate > UCHAR_MAX)
+ return EINVAL;
+ if (mode.rate == 0)
+ mode.rate = sc->dflt_mode.rate;
+ else if (mode.rate == -1)
+ /* don't change the current setting */
+ ;
+ else if (mode.rate < 0)
+ return EINVAL;
+ if (mode.resolution >= UCHAR_MAX)
+ return EINVAL;
+ if (mode.resolution >= 200)
+ mode.resolution = MOUSE_RES_HIGH;
+ else if (mode.resolution >= 100)
+ mode.resolution = MOUSE_RES_MEDIUMHIGH;
+ else if (mode.resolution >= 50)
+ mode.resolution = MOUSE_RES_MEDIUMLOW;
+ else if (mode.resolution > 0)
+ mode.resolution = MOUSE_RES_LOW;
+ if (mode.resolution == MOUSE_RES_DEFAULT)
+ mode.resolution = sc->dflt_mode.resolution;
+ else if (mode.resolution == -1)
+ /* don't change the current setting */
+ ;
+ else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */
+ mode.resolution = MOUSE_RES_LOW - mode.resolution;
+ if (mode.level == -1)
+ /* don't change the current setting */
+ mode.level = sc->mode.level;
+ else if ((mode.level < PSM_LEVEL_MIN) || (mode.level > PSM_LEVEL_MAX))
+ return EINVAL;
+ if (mode.accelfactor == -1)
+ /* don't change the current setting */
+ mode.accelfactor = sc->mode.accelfactor;
+ else if (mode.accelfactor < 0)
+ return EINVAL;
+
+ /* don't allow anybody to poll the keyboard controller */
+ error = block_mouse_data(sc, &command_byte);
+ if (error)
+ return error;
+
+ /* set mouse parameters */
+ if (mode.rate > 0)
+ mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate);
+ if (mode.resolution >= 0)
+ mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution);
+ set_mouse_scaling(sc->kbdc, 1);
+ get_mouse_status(sc->kbdc, stat, 0, 3);
+
+ s = spltty();
+ sc->mode.rate = mode.rate;
+ sc->mode.resolution = mode.resolution;
+ sc->mode.accelfactor = mode.accelfactor;
+ sc->mode.level = mode.level;
+ splx(s);
+
+ unblock_mouse_data(sc, command_byte);
+ break;
+
+ case MOUSE_GETLEVEL:
+ *(int *)addr = sc->mode.level;
+ break;
+
+ case MOUSE_SETLEVEL:
+ if ((*(int *)addr < PSM_LEVEL_MIN) || (*(int *)addr > PSM_LEVEL_MAX))
+ return EINVAL;
+ sc->mode.level = *(int *)addr;
+ break;
+
+ case MOUSE_GETSTATUS:
+ s = spltty();
+ status = sc->status;
+ sc->status.flags = 0;
+ sc->status.obutton = sc->status.button;
+ sc->status.button = 0;
+ sc->status.dx = 0;
+ sc->status.dy = 0;
+ sc->status.dz = 0;
+ splx(s);
+ *(mousestatus_t *)addr = status;
+ break;
+
+#if (defined(MOUSE_GETVARS))
+ case MOUSE_GETVARS:
+ var = (mousevar_t *)addr;
+ bzero(var, sizeof(*var));
+ s = spltty();
+ var->var[0] = MOUSE_VARS_PS2_SIG;
+ var->var[1] = sc->config;
+ var->var[2] = sc->flags;
+ splx(s);
+ break;
+
+ case MOUSE_SETVARS:
+ return ENODEV;
+#endif /* MOUSE_GETVARS */
+
+ case MOUSE_READSTATE:
+ case MOUSE_READDATA:
+ data = (mousedata_t *)addr;
+ if (data->len > sizeof(data->buf)/sizeof(data->buf[0]))
+ return EINVAL;
+
+ error = block_mouse_data(sc, &command_byte);
+ if (error)
+ return error;
+ if ((data->len = get_mouse_status(sc->kbdc, data->buf,
+ (cmd == MOUSE_READDATA) ? 1 : 0, data->len)) <= 0)
+ error = EIO;
+ unblock_mouse_data(sc, command_byte);
+ break;
+
+#if (defined(MOUSE_SETRESOLUTION))
+ case MOUSE_SETRESOLUTION:
+ mode.resolution = *(int *)addr;
+ if (mode.resolution >= UCHAR_MAX)
+ return EINVAL;
+ else if (mode.resolution >= 200)
+ mode.resolution = MOUSE_RES_HIGH;
+ else if (mode.resolution >= 100)
+ mode.resolution = MOUSE_RES_MEDIUMHIGH;
+ else if (mode.resolution >= 50)
+ mode.resolution = MOUSE_RES_MEDIUMLOW;
+ else if (mode.resolution > 0)
+ mode.resolution = MOUSE_RES_LOW;
+ if (mode.resolution == MOUSE_RES_DEFAULT)
+ mode.resolution = sc->dflt_mode.resolution;
+ else if (mode.resolution == -1)
+ mode.resolution = sc->mode.resolution;
+ else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */
+ mode.resolution = MOUSE_RES_LOW - mode.resolution;
+
+ error = block_mouse_data(sc, &command_byte);
+ if (error)
+ return error;
+ sc->mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution);
+ if (sc->mode.resolution != mode.resolution)
+ error = EIO;
+ unblock_mouse_data(sc, command_byte);
+ break;
+#endif /* MOUSE_SETRESOLUTION */
+
+#if (defined(MOUSE_SETRATE))
+ case MOUSE_SETRATE:
+ mode.rate = *(int *)addr;
+ if (mode.rate > UCHAR_MAX)
+ return EINVAL;
+ if (mode.rate == 0)
+ mode.rate = sc->dflt_mode.rate;
+ else if (mode.rate < 0)
+ mode.rate = sc->mode.rate;
+
+ error = block_mouse_data(sc, &command_byte);
+ if (error)
+ return error;
+ sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate);
+ if (sc->mode.rate != mode.rate)
+ error = EIO;
+ unblock_mouse_data(sc, command_byte);
+ break;
+#endif /* MOUSE_SETRATE */
+
+#if (defined(MOUSE_SETSCALING))
+ case MOUSE_SETSCALING:
+ if ((*(int *)addr <= 0) || (*(int *)addr > 2))
+ return EINVAL;
+
+ error = block_mouse_data(sc, &command_byte);
+ if (error)
+ return error;
+ if (!set_mouse_scaling(sc->kbdc, *(int *)addr))
+ error = EIO;
+ unblock_mouse_data(sc, command_byte);
+ break;
+#endif /* MOUSE_SETSCALING */
+
+#if (defined(MOUSE_GETHWID))
+ case MOUSE_GETHWID:
+ error = block_mouse_data(sc, &command_byte);
+ if (error)
+ return error;
+ sc->hw.hwid &= ~0x00ff;
+ sc->hw.hwid |= get_aux_id(sc->kbdc);
+ *(int *)addr = sc->hw.hwid & 0x00ff;
+ unblock_mouse_data(sc, command_byte);
+ break;
+#endif /* MOUSE_GETHWID */
+
+ default:
+ return ENOTTY;
+ }
+
+ return error;
+}
+
+static void
+psmtimeout(void *arg)
+{
+ struct psm_softc *sc;
+ int s;
+
+ sc = (struct psm_softc *)arg;
+ s = spltty();
+ if (sc->watchdog && kbdc_lock(sc->kbdc, TRUE)) {
+ if (verbose >= 4)
+ log(LOG_DEBUG, "psm%d: lost interrupt?\n", sc->unit);
+ psmintr(sc);
+ kbdc_lock(sc->kbdc, FALSE);
+ }
+ sc->watchdog = TRUE;
+ splx(s);
+ sc->callout = timeout(psmtimeout, (void *)(uintptr_t)sc, hz);
+}
+
+static void
+psmintr(void *arg)
+{
+ /*
+ * the table to turn PS/2 mouse button bits (MOUSE_PS2_BUTTON?DOWN)
+ * into `mousestatus' button bits (MOUSE_BUTTON?DOWN).
+ */
+ static int butmap[8] = {
+ 0,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
+ };
+ static int butmap_versapad[8] = {
+ 0,
+ MOUSE_BUTTON3DOWN,
+ 0,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN
+ };
+ register struct psm_softc *sc = arg;
+ mousestatus_t ms;
+ struct timeval tv;
+ int x, y, z;
+ int c;
+ int l;
+ int x0, y0;
+
+ /* read until there is nothing to read */
+ while((c = read_aux_data_no_wait(sc->kbdc)) != -1) {
+
+ /* discard the byte if the device is not open */
+ if ((sc->state & PSM_OPEN) == 0)
+ continue;
+
+ getmicrouptime(&tv);
+ if ((sc->inputbytes > 0) && timevalcmp(&tv, &sc->inputtimeout, >)) {
+ log(LOG_DEBUG, "psmintr: delay too long; resetting byte count\n");
+ sc->inputbytes = 0;
+ sc->syncerrors = 0;
+ }
+ sc->inputtimeout.tv_sec = PSM_INPUT_TIMEOUT/1000000;
+ sc->inputtimeout.tv_usec = PSM_INPUT_TIMEOUT%1000000;
+ timevaladd(&sc->inputtimeout, &tv);
+
+ sc->ipacket[sc->inputbytes++] = c;
+ if (sc->inputbytes < sc->mode.packetsize)
+ continue;
+
+#if 0
+ log(LOG_DEBUG, "psmintr: %02x %02x %02x %02x %02x %02x\n",
+ sc->ipacket[0], sc->ipacket[1], sc->ipacket[2],
+ sc->ipacket[3], sc->ipacket[4], sc->ipacket[5]);
+#endif
+
+ c = sc->ipacket[0];
+
+ if ((c & sc->mode.syncmask[0]) != sc->mode.syncmask[1]) {
+ log(LOG_DEBUG, "psmintr: out of sync (%04x != %04x).\n",
+ c & sc->mode.syncmask[0], sc->mode.syncmask[1]);
+ ++sc->syncerrors;
+ if (sc->syncerrors < sc->mode.packetsize) {
+ log(LOG_DEBUG, "psmintr: discard a byte (%d).\n", sc->syncerrors);
+ --sc->inputbytes;
+ bcopy(&sc->ipacket[1], &sc->ipacket[0], sc->inputbytes);
+ } else if (sc->syncerrors == sc->mode.packetsize) {
+ log(LOG_DEBUG, "psmintr: re-enable the mouse.\n");
+ sc->inputbytes = 0;
+ disable_aux_dev(sc->kbdc);
+ enable_aux_dev(sc->kbdc);
+ } else if (sc->syncerrors < PSM_SYNCERR_THRESHOLD1) {
+ log(LOG_DEBUG, "psmintr: discard a byte (%d).\n", sc->syncerrors);
+ --sc->inputbytes;
+ bcopy(&sc->ipacket[1], &sc->ipacket[0], sc->inputbytes);
+ } else if (sc->syncerrors >= PSM_SYNCERR_THRESHOLD1) {
+ log(LOG_DEBUG, "psmintr: reset the mouse.\n");
+ reinitialize(sc, TRUE);
+ }
+ continue;
+ }
+
+ /*
+ * A kludge for Kensington device!
+ * The MSB of the horizontal count appears to be stored in
+ * a strange place.
+ */
+ if (sc->hw.model == MOUSE_MODEL_THINK)
+ sc->ipacket[1] |= (c & MOUSE_PS2_XOVERFLOW) ? 0x80 : 0;
+
+ /* ignore the overflow bits... */
+ x = (c & MOUSE_PS2_XNEG) ? sc->ipacket[1] - 256 : sc->ipacket[1];
+ y = (c & MOUSE_PS2_YNEG) ? sc->ipacket[2] - 256 : sc->ipacket[2];
+ z = 0;
+ ms.obutton = sc->button; /* previous button state */
+ ms.button = butmap[c & MOUSE_PS2_BUTTONS];
+ /* `tapping' action */
+ if (sc->config & PSM_CONFIG_FORCETAP)
+ ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
+
+ switch (sc->hw.model) {
+
+ case MOUSE_MODEL_EXPLORER:
+ /*
+ * b7 b6 b5 b4 b3 b2 b1 b0
+ * byte 1: oy ox sy sx 1 M R L
+ * byte 2: x x x x x x x x
+ * byte 3: y y y y y y y y
+ * byte 4: * * S2 S1 s d2 d1 d0
+ *
+ * L, M, R, S1, S2: left, middle, right and side buttons
+ * s: wheel data sign bit
+ * d2-d0: wheel data
+ */
+ z = (sc->ipacket[3] & MOUSE_EXPLORER_ZNEG)
+ ? (sc->ipacket[3] & 0x0f) - 16 : (sc->ipacket[3] & 0x0f);
+ ms.button |= (sc->ipacket[3] & MOUSE_EXPLORER_BUTTON4DOWN)
+ ? MOUSE_BUTTON4DOWN : 0;
+ ms.button |= (sc->ipacket[3] & MOUSE_EXPLORER_BUTTON5DOWN)
+ ? MOUSE_BUTTON5DOWN : 0;
+ break;
+
+ case MOUSE_MODEL_INTELLI:
+ case MOUSE_MODEL_NET:
+ /* wheel data is in the fourth byte */
+ z = (char)sc->ipacket[3];
+ /* some mice may send 7 when there is no Z movement?! XXX */
+ if ((z >= 7) || (z <= -7))
+ z = 0;
+ /* some compatible mice have additional buttons */
+ ms.button |= (c & MOUSE_PS2INTELLI_BUTTON4DOWN)
+ ? MOUSE_BUTTON4DOWN : 0;
+ ms.button |= (c & MOUSE_PS2INTELLI_BUTTON5DOWN)
+ ? MOUSE_BUTTON5DOWN : 0;
+ break;
+
+ case MOUSE_MODEL_MOUSEMANPLUS:
+ /*
+ * PS2++ protocl packet
+ *
+ * b7 b6 b5 b4 b3 b2 b1 b0
+ * byte 1: * 1 p3 p2 1 * * *
+ * byte 2: c1 c2 p1 p0 d1 d0 1 0
+ *
+ * p3-p0: packet type
+ * c1, c2: c1 & c2 == 1, if p2 == 0
+ * c1 & c2 == 0, if p2 == 1
+ *
+ * packet type: 0 (device type)
+ * See comments in enable_mmanplus() below.
+ *
+ * packet type: 1 (wheel data)
+ *
+ * b7 b6 b5 b4 b3 b2 b1 b0
+ * byte 3: h * B5 B4 s d2 d1 d0
+ *
+ * h: 1, if horizontal roller data
+ * 0, if vertical roller data
+ * B4, B5: button 4 and 5
+ * s: sign bit
+ * d2-d0: roller data
+ *
+ * packet type: 2 (reserved)
+ */
+ if (((c & MOUSE_PS2PLUS_SYNCMASK) == MOUSE_PS2PLUS_SYNC)
+ && (abs(x) > 191)
+ && MOUSE_PS2PLUS_CHECKBITS(sc->ipacket)) {
+ /* the extended data packet encodes button and wheel events */
+ switch (MOUSE_PS2PLUS_PACKET_TYPE(sc->ipacket)) {
+ case 1:
+ /* wheel data packet */
+ x = y = 0;
+ if (sc->ipacket[2] & 0x80) {
+ /* horizontal roller count - ignore it XXX*/
+ } else {
+ /* vertical roller count */
+ z = (sc->ipacket[2] & MOUSE_PS2PLUS_ZNEG)
+ ? (sc->ipacket[2] & 0x0f) - 16
+ : (sc->ipacket[2] & 0x0f);
+ }
+ ms.button |= (sc->ipacket[2] & MOUSE_PS2PLUS_BUTTON4DOWN)
+ ? MOUSE_BUTTON4DOWN : 0;
+ ms.button |= (sc->ipacket[2] & MOUSE_PS2PLUS_BUTTON5DOWN)
+ ? MOUSE_BUTTON5DOWN : 0;
+ break;
+ case 2:
+ /* this packet type is reserved by Logitech... */
+ /*
+ * IBM ScrollPoint Mouse uses this packet type to
+ * encode both vertical and horizontal scroll movement.
+ */
+ x = y = 0;
+ /* horizontal count */
+ if (sc->ipacket[2] & 0x0f)
+ z = (sc->ipacket[2] & MOUSE_SPOINT_WNEG) ? -2 : 2;
+ /* vertical count */
+ if (sc->ipacket[2] & 0xf0)
+ z = (sc->ipacket[2] & MOUSE_SPOINT_ZNEG) ? -1 : 1;
+#if 0
+ /* vertical count */
+ z = (sc->ipacket[2] & MOUSE_SPOINT_ZNEG)
+ ? ((sc->ipacket[2] >> 4) & 0x0f) - 16
+ : ((sc->ipacket[2] >> 4) & 0x0f);
+ /* horizontal count */
+ w = (sc->ipacket[2] & MOUSE_SPOINT_WNEG)
+ ? (sc->ipacket[2] & 0x0f) - 16
+ : (sc->ipacket[2] & 0x0f);
+#endif
+ break;
+ case 0:
+ /* device type packet - shouldn't happen */
+ /* FALLTHROUGH */
+ default:
+ x = y = 0;
+ ms.button = ms.obutton;
+ if (bootverbose)
+ log(LOG_DEBUG, "psmintr: unknown PS2++ packet type %d: "
+ "0x%02x 0x%02x 0x%02x\n",
+ MOUSE_PS2PLUS_PACKET_TYPE(sc->ipacket),
+ sc->ipacket[0], sc->ipacket[1], sc->ipacket[2]);
+ break;
+ }
+ } else {
+ /* preserve button states */
+ ms.button |= ms.obutton & MOUSE_EXTBUTTONS;
+ }
+ break;
+
+ case MOUSE_MODEL_GLIDEPOINT:
+ /* `tapping' action */
+ ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
+ break;
+
+ case MOUSE_MODEL_NETSCROLL:
+ /* three addtional bytes encode buttons and wheel events */
+ ms.button |= (sc->ipacket[3] & MOUSE_PS2_BUTTON3DOWN)
+ ? MOUSE_BUTTON4DOWN : 0;
+ ms.button |= (sc->ipacket[3] & MOUSE_PS2_BUTTON1DOWN)
+ ? MOUSE_BUTTON5DOWN : 0;
+ z = (sc->ipacket[3] & MOUSE_PS2_XNEG)
+ ? sc->ipacket[4] - 256 : sc->ipacket[4];
+ break;
+
+ case MOUSE_MODEL_THINK:
+ /* the fourth button state in the first byte */
+ ms.button |= (c & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0;
+ break;
+
+ case MOUSE_MODEL_VERSAPAD:
+ /* VersaPad PS/2 absolute mode message format
+ *
+ * [packet1] 7 6 5 4 3 2 1 0(LSB)
+ * ipacket[0]: 1 1 0 A 1 L T R
+ * ipacket[1]: H7 H6 H5 H4 H3 H2 H1 H0
+ * ipacket[2]: V7 V6 V5 V4 V3 V2 V1 V0
+ * ipacket[3]: 1 1 1 A 1 L T R
+ * ipacket[4]:V11 V10 V9 V8 H11 H10 H9 H8
+ * ipacket[5]: 0 P6 P5 P4 P3 P2 P1 P0
+ *
+ * [note]
+ * R: right physical mouse button (1=on)
+ * T: touch pad virtual button (1=tapping)
+ * L: left physical mouse button (1=on)
+ * A: position data is valid (1=valid)
+ * H: horizontal data (12bit signed integer. H11 is sign bit.)
+ * V: vertical data (12bit signed integer. V11 is sign bit.)
+ * P: pressure data
+ *
+ * Tapping is mapped to MOUSE_BUTTON4.
+ */
+ ms.button = butmap_versapad[c & MOUSE_PS2VERSA_BUTTONS];
+ ms.button |= (c & MOUSE_PS2VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0;
+ x = y = 0;
+ if (c & MOUSE_PS2VERSA_IN_USE) {
+ x0 = sc->ipacket[1] | (((sc->ipacket[4]) & 0x0f) << 8);
+ y0 = sc->ipacket[2] | (((sc->ipacket[4]) & 0xf0) << 4);
+ if (x0 & 0x800)
+ x0 -= 0x1000;
+ if (y0 & 0x800)
+ y0 -= 0x1000;
+ if (sc->flags & PSM_FLAGS_FINGERDOWN) {
+ x = sc->xold - x0;
+ y = y0 - sc->yold;
+ if (x < 0) /* XXX */
+ x++;
+ else if (x)
+ x--;
+ if (y < 0)
+ y++;
+ else if (y)
+ y--;
+ } else {
+ sc->flags |= PSM_FLAGS_FINGERDOWN;
+ }
+ sc->xold = x0;
+ sc->yold = y0;
+ } else {
+ sc->flags &= ~PSM_FLAGS_FINGERDOWN;
+ }
+ c = ((x < 0) ? MOUSE_PS2_XNEG : 0)
+ | ((y < 0) ? MOUSE_PS2_YNEG : 0);
+ break;
+
+ case MOUSE_MODEL_4D:
+ /*
+ * b7 b6 b5 b4 b3 b2 b1 b0
+ * byte 1: s2 d2 s1 d1 1 M R L
+ * byte 2: sx x x x x x x x
+ * byte 3: sy y y y y y y y
+ *
+ * s1: wheel 1 direction
+ * d1: wheel 1 data
+ * s2: wheel 2 direction
+ * d2: wheel 2 data
+ */
+ x = (sc->ipacket[1] & 0x80) ? sc->ipacket[1] - 256 : sc->ipacket[1];
+ y = (sc->ipacket[2] & 0x80) ? sc->ipacket[2] - 256 : sc->ipacket[2];
+ switch (c & MOUSE_4D_WHEELBITS) {
+ case 0x10:
+ z = 1;
+ break;
+ case 0x30:
+ z = -1;
+ break;
+ case 0x40: /* 2nd wheel turning right XXX */
+ z = 2;
+ break;
+ case 0xc0: /* 2nd wheel turning left XXX */
+ z = -2;
+ break;
+ }
+ break;
+
+ case MOUSE_MODEL_4DPLUS:
+ if ((x < 16 - 256) && (y < 16 - 256)) {
+ /*
+ * b7 b6 b5 b4 b3 b2 b1 b0
+ * byte 1: 0 0 1 1 1 M R L
+ * byte 2: 0 0 0 0 1 0 0 0
+ * byte 3: 0 0 0 0 S s d1 d0
+ *
+ * L, M, R, S: left, middle, right and side buttons
+ * s: wheel data sign bit
+ * d1-d0: wheel data
+ */
+ x = y = 0;
+ if (sc->ipacket[2] & MOUSE_4DPLUS_BUTTON4DOWN)
+ ms.button |= MOUSE_BUTTON4DOWN;
+ z = (sc->ipacket[2] & MOUSE_4DPLUS_ZNEG)
+ ? ((sc->ipacket[2] & 0x07) - 8)
+ : (sc->ipacket[2] & 0x07) ;
+ } else {
+ /* preserve previous button states */
+ ms.button |= ms.obutton & MOUSE_EXTBUTTONS;
+ }
+ break;
+
+ case MOUSE_MODEL_GENERIC:
+ default:
+ break;
+ }
+
+ /* scale values */
+ if (sc->mode.accelfactor >= 1) {
+ if (x != 0) {
+ x = x * x / sc->mode.accelfactor;
+ if (x == 0)
+ x = 1;
+ if (c & MOUSE_PS2_XNEG)
+ x = -x;
+ }
+ if (y != 0) {
+ y = y * y / sc->mode.accelfactor;
+ if (y == 0)
+ y = 1;
+ if (c & MOUSE_PS2_YNEG)
+ y = -y;
+ }
+ }
+
+ ms.dx = x;
+ ms.dy = y;
+ ms.dz = z;
+ ms.flags = ((x || y || z) ? MOUSE_POSCHANGED : 0)
+ | (ms.obutton ^ ms.button);
+
+ if (sc->mode.level < PSM_LEVEL_NATIVE)
+ sc->inputbytes = tame_mouse(sc, &ms, sc->ipacket);
+
+ sc->status.flags |= ms.flags;
+ sc->status.dx += ms.dx;
+ sc->status.dy += ms.dy;
+ sc->status.dz += ms.dz;
+ sc->status.button = ms.button;
+ sc->button = ms.button;
+
+ sc->watchdog = FALSE;
+
+ /* queue data */
+ if (sc->queue.count + sc->inputbytes < sizeof(sc->queue.buf)) {
+ l = min(sc->inputbytes, sizeof(sc->queue.buf) - sc->queue.tail);
+ bcopy(&sc->ipacket[0], &sc->queue.buf[sc->queue.tail], l);
+ if (sc->inputbytes > l)
+ bcopy(&sc->ipacket[l], &sc->queue.buf[0], sc->inputbytes - l);
+ sc->queue.tail =
+ (sc->queue.tail + sc->inputbytes) % sizeof(sc->queue.buf);
+ sc->queue.count += sc->inputbytes;
+ }
+ sc->inputbytes = 0;
+
+ if (sc->state & PSM_ASLP) {
+ sc->state &= ~PSM_ASLP;
+ wakeup((caddr_t) sc);
+ }
+ selwakeup(&sc->rsel);
+ }
+}
+
+static int
+psmpoll(dev_t dev, int events, struct thread *td)
+{
+ struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
+ int s;
+ int revents = 0;
+
+ /* Return true if a mouse event available */
+ s = spltty();
+ if (events & (POLLIN | POLLRDNORM)) {
+ if (sc->queue.count > 0)
+ revents |= events & (POLLIN | POLLRDNORM);
+ else
+ selrecord(td, &sc->rsel);
+ }
+ splx(s);
+
+ return (revents);
+}
+
+/* vendor/model specific routines */
+
+static int mouse_id_proc1(KBDC kbdc, int res, int scale, int *status)
+{
+ if (set_mouse_resolution(kbdc, res) != res)
+ return FALSE;
+ if (set_mouse_scaling(kbdc, scale)
+ && set_mouse_scaling(kbdc, scale)
+ && set_mouse_scaling(kbdc, scale)
+ && (get_mouse_status(kbdc, status, 0, 3) >= 3))
+ return TRUE;
+ return FALSE;
+}
+
+static int
+mouse_ext_command(KBDC kbdc, int command)
+{
+ int c;
+
+ c = (command >> 6) & 0x03;
+ if (set_mouse_resolution(kbdc, c) != c)
+ return FALSE;
+ c = (command >> 4) & 0x03;
+ if (set_mouse_resolution(kbdc, c) != c)
+ return FALSE;
+ c = (command >> 2) & 0x03;
+ if (set_mouse_resolution(kbdc, c) != c)
+ return FALSE;
+ c = (command >> 0) & 0x03;
+ if (set_mouse_resolution(kbdc, c) != c)
+ return FALSE;
+ return TRUE;
+}
+
+#if notyet
+/* Logitech MouseMan Cordless II */
+static int
+enable_lcordless(struct psm_softc *sc)
+{
+ int status[3];
+ int ch;
+
+ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 2, status))
+ return FALSE;
+ if (status[1] == PSMD_RES_HIGH)
+ return FALSE;
+ ch = (status[0] & 0x07) - 1; /* channel # */
+ if ((ch <= 0) || (ch > 4))
+ return FALSE;
+ /*
+ * status[1]: always one?
+ * status[2]: battery status? (0-100)
+ */
+ return TRUE;
+}
+#endif /* notyet */
+
+/* Genius NetScroll Mouse, MouseSystems SmartScroll Mouse */
+static int
+enable_groller(struct psm_softc *sc)
+{
+ int status[3];
+
+ /*
+ * The special sequence to enable the fourth button and the
+ * roller. Immediately after this sequence check status bytes.
+ * if the mouse is NetScroll, the second and the third bytes are
+ * '3' and 'D'.
+ */
+
+ /*
+ * If the mouse is an ordinary PS/2 mouse, the status bytes should
+ * look like the following.
+ *
+ * byte 1 bit 7 always 0
+ * bit 6 stream mode (0)
+ * bit 5 disabled (0)
+ * bit 4 1:1 scaling (0)
+ * bit 3 always 0
+ * bit 0-2 button status
+ * byte 2 resolution (PSMD_RES_HIGH)
+ * byte 3 report rate (?)
+ */
+
+ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status))
+ return FALSE;
+ if ((status[1] != '3') || (status[2] != 'D'))
+ return FALSE;
+ /* FIXME: SmartScroll Mouse has 5 buttons! XXX */
+ sc->hw.buttons = 4;
+ return TRUE;
+}
+
+/* Genius NetMouse/NetMouse Pro, ASCII Mie Mouse, NetScroll Optical */
+static int
+enable_gmouse(struct psm_softc *sc)
+{
+ int status[3];
+
+ /*
+ * The special sequence to enable the middle, "rubber" button.
+ * Immediately after this sequence check status bytes.
+ * if the mouse is NetMouse, NetMouse Pro, or ASCII MIE Mouse,
+ * the second and the third bytes are '3' and 'U'.
+ * NOTE: NetMouse reports that it has three buttons although it has
+ * two buttons and a rubber button. NetMouse Pro and MIE Mouse
+ * say they have three buttons too and they do have a button on the
+ * side...
+ */
+ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status))
+ return FALSE;
+ if ((status[1] != '3') || (status[2] != 'U'))
+ return FALSE;
+ return TRUE;
+}
+
+/* ALPS GlidePoint */
+static int
+enable_aglide(struct psm_softc *sc)
+{
+ int status[3];
+
+ /*
+ * The special sequence to obtain ALPS GlidePoint specific
+ * information. Immediately after this sequence, status bytes will
+ * contain something interesting.
+ * NOTE: ALPS produces several models of GlidePoint. Some of those
+ * do not respond to this sequence, thus, cannot be detected this way.
+ */
+ if (set_mouse_sampling_rate(sc->kbdc, 100) != 100)
+ return FALSE;
+ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_LOW, 2, status))
+ return FALSE;
+ if ((status[1] == PSMD_RES_LOW) || (status[2] == 100))
+ return FALSE;
+ return TRUE;
+}
+
+/* Kensington ThinkingMouse/Trackball */
+static int
+enable_kmouse(struct psm_softc *sc)
+{
+ static unsigned char rate[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
+ KBDC kbdc = sc->kbdc;
+ int status[3];
+ int id1;
+ int id2;
+ int i;
+
+ id1 = get_aux_id(kbdc);
+ if (set_mouse_sampling_rate(kbdc, 10) != 10)
+ return FALSE;
+ /*
+ * The device is now in the native mode? It returns a different
+ * ID value...
+ */
+ id2 = get_aux_id(kbdc);
+ if ((id1 == id2) || (id2 != 2))
+ return FALSE;
+
+ if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW)
+ return FALSE;
+#if PSM_DEBUG >= 2
+ /* at this point, resolution is LOW, sampling rate is 10/sec */
+ if (get_mouse_status(kbdc, status, 0, 3) < 3)
+ return FALSE;
+#endif
+
+ /*
+ * The special sequence to enable the third and fourth buttons.
+ * Otherwise they behave like the first and second buttons.
+ */
+ for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) {
+ if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
+ return FALSE;
+ }
+
+ /*
+ * At this point, the device is using default resolution and
+ * sampling rate for the native mode.
+ */
+ if (get_mouse_status(kbdc, status, 0, 3) < 3)
+ return FALSE;
+ if ((status[1] == PSMD_RES_LOW) || (status[2] == rate[i - 1]))
+ return FALSE;
+
+ /* the device appears be enabled by this sequence, diable it for now */
+ disable_aux_dev(kbdc);
+ empty_aux_buffer(kbdc, 5);
+
+ return TRUE;
+}
+
+/* Logitech MouseMan+/FirstMouse+, IBM ScrollPoint Mouse */
+static int
+enable_mmanplus(struct psm_softc *sc)
+{
+ KBDC kbdc = sc->kbdc;
+ int data[3];
+
+ /* the special sequence to enable the fourth button and the roller. */
+ /*
+ * NOTE: for ScrollPoint to respond correctly, the SET_RESOLUTION
+ * must be called exactly three times since the last RESET command
+ * before this sequence. XXX
+ */
+ if (!set_mouse_scaling(kbdc, 1))
+ return FALSE;
+ if (!mouse_ext_command(kbdc, 0x39) || !mouse_ext_command(kbdc, 0xdb))
+ return FALSE;
+ if (get_mouse_status(kbdc, data, 1, 3) < 3)
+ return FALSE;
+
+ /*
+ * PS2++ protocl, packet type 0
+ *
+ * b7 b6 b5 b4 b3 b2 b1 b0
+ * byte 1: * 1 p3 p2 1 * * *
+ * byte 2: 1 1 p1 p0 m1 m0 1 0
+ * byte 3: m7 m6 m5 m4 m3 m2 m1 m0
+ *
+ * p3-p0: packet type: 0
+ * m7-m0: model ID: MouseMan+:0x50, FirstMouse+:0x51, ScrollPoint:0x58...
+ */
+ /* check constant bits */
+ if ((data[0] & MOUSE_PS2PLUS_SYNCMASK) != MOUSE_PS2PLUS_SYNC)
+ return FALSE;
+ if ((data[1] & 0xc3) != 0xc2)
+ return FALSE;
+ /* check d3-d0 in byte 2 */
+ if (!MOUSE_PS2PLUS_CHECKBITS(data))
+ return FALSE;
+ /* check p3-p0 */
+ if (MOUSE_PS2PLUS_PACKET_TYPE(data) != 0)
+ return FALSE;
+
+ sc->hw.hwid &= 0x00ff;
+ sc->hw.hwid |= data[2] << 8; /* save model ID */
+
+ /*
+ * MouseMan+ (or FirstMouse+) is now in its native mode, in which
+ * the wheel and the fourth button events are encoded in the
+ * special data packet. The mouse may be put in the IntelliMouse mode
+ * if it is initialized by the IntelliMouse's method.
+ */
+ return TRUE;
+}
+
+/* MS IntelliMouse Explorer */
+static int
+enable_msexplorer(struct psm_softc *sc)
+{
+ static unsigned char rate0[] = { 200, 100, 80, };
+ static unsigned char rate1[] = { 200, 200, 80, };
+ KBDC kbdc = sc->kbdc;
+ int id;
+ int i;
+
+ /* the special sequence to enable the extra buttons and the roller. */
+ for (i = 0; i < sizeof(rate1)/sizeof(rate1[0]); ++i) {
+ if (set_mouse_sampling_rate(kbdc, rate1[i]) != rate1[i])
+ return FALSE;
+ }
+ /* the device will give the genuine ID only after the above sequence */
+ id = get_aux_id(kbdc);
+ if (id != PSM_EXPLORER_ID)
+ return FALSE;
+
+ sc->hw.hwid = id;
+ sc->hw.buttons = 5; /* IntelliMouse Explorer XXX */
+
+ /*
+ * XXX: this is a kludge to fool some KVM switch products
+ * which think they are clever enough to know the 4-byte IntelliMouse
+ * protocol, and assume any other protocols use 3-byte packets.
+ * They don't convey 4-byte data packets from the IntelliMouse Explorer
+ * correctly to the host computer because of this!
+ * The following sequence is actually IntelliMouse's "wake up"
+ * sequence; it will make the KVM think the mouse is IntelliMouse
+ * when it is in fact IntelliMouse Explorer.
+ */
+ for (i = 0; i < sizeof(rate0)/sizeof(rate0[0]); ++i) {
+ if (set_mouse_sampling_rate(kbdc, rate0[i]) != rate0[i])
+ break;
+ }
+ id = get_aux_id(kbdc);
+
+ return TRUE;
+}
+
+/* MS IntelliMouse */
+static int
+enable_msintelli(struct psm_softc *sc)
+{
+ /*
+ * Logitech MouseMan+ and FirstMouse+ will also respond to this
+ * probe routine and act like IntelliMouse.
+ */
+
+ static unsigned char rate[] = { 200, 100, 80, };
+ KBDC kbdc = sc->kbdc;
+ int id;
+ int i;
+
+ /* the special sequence to enable the third button and the roller. */
+ for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) {
+ if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
+ return FALSE;
+ }
+ /* the device will give the genuine ID only after the above sequence */
+ id = get_aux_id(kbdc);
+ if (id != PSM_INTELLI_ID)
+ return FALSE;
+
+ sc->hw.hwid = id;
+ sc->hw.buttons = 3;
+
+ return TRUE;
+}
+
+/* A4 Tech 4D Mouse */
+static int
+enable_4dmouse(struct psm_softc *sc)
+{
+ /*
+ * Newer wheel mice from A4 Tech may use the 4D+ protocol.
+ */
+
+ static unsigned char rate[] = { 200, 100, 80, 60, 40, 20 };
+ KBDC kbdc = sc->kbdc;
+ int id;
+ int i;
+
+ for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) {
+ if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
+ return FALSE;
+ }
+ id = get_aux_id(kbdc);
+ /*
+ * WinEasy 4D, 4 Way Scroll 4D: 6
+ * Cable-Free 4D: 8 (4DPLUS)
+ * WinBest 4D+, 4 Way Scroll 4D+: 8 (4DPLUS)
+ */
+ if (id != PSM_4DMOUSE_ID)
+ return FALSE;
+
+ sc->hw.hwid = id;
+ sc->hw.buttons = 3; /* XXX some 4D mice have 4? */
+
+ return TRUE;
+}
+
+/* A4 Tech 4D+ Mouse */
+static int
+enable_4dplus(struct psm_softc *sc)
+{
+ /*
+ * Newer wheel mice from A4 Tech seem to use this protocol.
+ * Older models are recognized as either 4D Mouse or IntelliMouse.
+ */
+ KBDC kbdc = sc->kbdc;
+ int id;
+
+ /*
+ * enable_4dmouse() already issued the following ID sequence...
+ static unsigned char rate[] = { 200, 100, 80, 60, 40, 20 };
+ int i;
+
+ for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) {
+ if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
+ return FALSE;
+ }
+ */
+
+ id = get_aux_id(kbdc);
+ if (id != PSM_4DPLUS_ID)
+ return FALSE;
+
+ sc->hw.hwid = id;
+ sc->hw.buttons = 4; /* XXX */
+
+ return TRUE;
+}
+
+/* Interlink electronics VersaPad */
+static int
+enable_versapad(struct psm_softc *sc)
+{
+ KBDC kbdc = sc->kbdc;
+ int data[3];
+
+ set_mouse_resolution(kbdc, PSMD_RES_MEDIUM_HIGH); /* set res. 2 */
+ set_mouse_sampling_rate(kbdc, 100); /* set rate 100 */
+ set_mouse_scaling(kbdc, 1); /* set scale 1:1 */
+ set_mouse_scaling(kbdc, 1); /* set scale 1:1 */
+ set_mouse_scaling(kbdc, 1); /* set scale 1:1 */
+ set_mouse_scaling(kbdc, 1); /* set scale 1:1 */
+ if (get_mouse_status(kbdc, data, 0, 3) < 3) /* get status */
+ return FALSE;
+ if (data[2] != 0xa || data[1] != 0 ) /* rate == 0xa && res. == 0 */
+ return FALSE;
+ set_mouse_scaling(kbdc, 1); /* set scale 1:1 */
+
+ sc->config |= PSM_CONFIG_HOOKRESUME | PSM_CONFIG_INITAFTERSUSPEND;
+
+ return TRUE; /* PS/2 absolute mode */
+}
+
+static int
+psmresume(device_t dev)
+{
+ struct psm_softc *sc = device_get_softc(dev);
+ int unit = device_get_unit(dev);
+ int err;
+
+ if (verbose >= 2)
+ log(LOG_NOTICE, "psm%d: system resume hook called.\n", unit);
+
+ if (!(sc->config & PSM_CONFIG_HOOKRESUME))
+ return (0);
+
+ err = reinitialize(sc, sc->config & PSM_CONFIG_INITAFTERSUSPEND);
+
+ if ((sc->state & PSM_ASLP) && !(sc->state & PSM_VALID)) {
+ /*
+ * Release the blocked process; it must be notified that the device
+ * cannot be accessed anymore.
+ */
+ sc->state &= ~PSM_ASLP;
+ wakeup((caddr_t)sc);
+ }
+
+ if (verbose >= 2)
+ log(LOG_DEBUG, "psm%d: system resume hook exiting.\n", unit);
+
+ return (err);
+}
+
+DRIVER_MODULE(psm, atkbdc, psm_driver, psm_devclass, 0, 0);
+
+/*
+ * This sucks up assignments from PNPBIOS and ACPI.
+ */
+
+/*
+ * When the PS/2 mouse device is reported by ACPI or PnP BIOS, it may
+ * appear BEFORE the AT keyboard controller. As the PS/2 mouse device
+ * can be probed and attached only after the AT keyboard controller is
+ * attached, we shall quietly reserve the IRQ resource for later use.
+ * If the PS/2 mouse device is reported to us AFTER the keyboard controller,
+ * copy the IRQ resource to the PS/2 mouse device instance hanging
+ * under the keyboard controller, then probe and attach it.
+ */
+
+static devclass_t psmcpnp_devclass;
+
+static device_probe_t psmcpnp_probe;
+static device_attach_t psmcpnp_attach;
+
+static device_method_t psmcpnp_methods[] = {
+ DEVMETHOD(device_probe, psmcpnp_probe),
+ DEVMETHOD(device_attach, psmcpnp_attach),
+
+ { 0, 0 }
+};
+
+static driver_t psmcpnp_driver = {
+ PSMCPNP_DRIVER_NAME,
+ psmcpnp_methods,
+ 1, /* no softc */
+};
+
+static struct isa_pnp_id psmcpnp_ids[] = {
+ { 0x030fd041, "PS/2 mouse port" }, /* PNP0F03 */
+ { 0x130fd041, "PS/2 mouse port" }, /* PNP0F13 */
+ { 0x1303d041, "PS/2 port" }, /* PNP0313, XXX */
+ { 0x80374d24, "IBM PS/2 mouse port" }, /* IBM3780, ThinkPad */
+ { 0x81374d24, "IBM PS/2 mouse port" }, /* IBM3781, ThinkPad */
+ { 0x0490d94d, "SONY VAIO PS/2 mouse port"}, /* SNY9004, Vaio*/
+ { 0 }
+};
+
+static int
+create_a_copy(device_t atkbdc, device_t me)
+{
+ device_t psm;
+ u_long irq;
+
+ /* find the PS/2 mouse device instance under the keyboard controller */
+ psm = device_find_child(atkbdc, PSM_DRIVER_NAME,
+ device_get_unit(atkbdc));
+ if (psm == NULL)
+ return ENXIO;
+ if (device_get_state(psm) != DS_NOTPRESENT)
+ return 0;
+
+ /* move our resource to the found device */
+ irq = bus_get_resource_start(me, SYS_RES_IRQ, 0);
+ bus_set_resource(psm, SYS_RES_IRQ, KBDC_RID_AUX, irq, 1);
+
+ /* ...then probe and attach it */
+ return device_probe_and_attach(psm);
+}
+
+static int
+psmcpnp_probe(device_t dev)
+{
+ struct resource *res;
+ u_long irq;
+ int rid;
+
+ if (ISA_PNP_PROBE(device_get_parent(dev), dev, psmcpnp_ids))
+ return ENXIO;
+
+ /*
+ * The PnP BIOS and ACPI are supposed to assign an IRQ (12)
+ * to the PS/2 mouse device node. But, some buggy PnP BIOS
+ * declares the PS/2 mouse device node without an IRQ resource!
+ * If this happens, we shall refer to device hints.
+ * If we still don't find it there, use a hardcoded value... XXX
+ */
+ rid = 0;
+ irq = bus_get_resource_start(dev, SYS_RES_IRQ, rid);
+ if (irq <= 0) {
+ if (resource_long_value(PSM_DRIVER_NAME,
+ device_get_unit(dev), "irq", &irq) != 0)
+ irq = 12; /* XXX */
+ device_printf(dev, "irq resource info is missing; "
+ "assuming irq %ld\n", irq);
+ bus_set_resource(dev, SYS_RES_IRQ, rid, irq, 1);
+ }
+ res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_SHAREABLE);
+ bus_release_resource(dev, SYS_RES_IRQ, rid, res);
+
+ /* keep quiet */
+ if (!bootverbose)
+ device_quiet(dev);
+
+ return ((res == NULL) ? ENXIO : 0);
+}
+
+static int
+psmcpnp_attach(device_t dev)
+{
+ device_t atkbdc;
+ int rid;
+
+ /* find the keyboard controller, which may be on acpi* or isa* bus */
+ atkbdc = devclass_get_device(devclass_find(ATKBDC_DRIVER_NAME),
+ device_get_unit(dev));
+ if ((atkbdc != NULL) && (device_get_state(atkbdc) == DS_ATTACHED)) {
+ create_a_copy(atkbdc, dev);
+ } else {
+ /*
+ * If we don't have the AT keyboard controller yet,
+ * just reserve the IRQ for later use...
+ * (See psmidentify() above.)
+ */
+ rid = 0;
+ bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_SHAREABLE);
+ }
+
+ return 0;
+}
+
+DRIVER_MODULE(psmcpnp, isa, psmcpnp_driver, psmcpnp_devclass, 0, 0);
+DRIVER_MODULE(psmcpnp, acpi, psmcpnp_driver, psmcpnp_devclass, 0, 0);
OpenPOWER on IntegriCloud