summaryrefslogtreecommitdiffstats
path: root/sys/dev/atkbdc
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/atkbdc')
-rw-r--r--sys/dev/atkbdc/atkbd.c1335
-rw-r--r--sys/dev/atkbdc/atkbd_atkbdc.c128
-rw-r--r--sys/dev/atkbdc/atkbd_isa.c128
-rw-r--r--sys/dev/atkbdc/atkbdc.c1021
-rw-r--r--sys/dev/atkbdc/atkbdc_isa.c269
-rw-r--r--sys/dev/atkbdc/atkbdc_subr.c269
-rw-r--r--sys/dev/atkbdc/atkbdcreg.h247
-rw-r--r--sys/dev/atkbdc/atkbdreg.h47
-rw-r--r--sys/dev/atkbdc/psm.c2454
9 files changed, 5898 insertions, 0 deletions
diff --git a/sys/dev/atkbdc/atkbd.c b/sys/dev/atkbdc/atkbd.c
new file mode 100644
index 0000000..7072734
--- /dev/null
+++ b/sys/dev/atkbdc/atkbd.c
@@ -0,0 +1,1335 @@
+/*-
+ * 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 "atkbd.h"
+#include "opt_kbd.h"
+#include "opt_atkbd.h"
+
+#if NATKBD > 0
+
+#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 <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 port, 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] = port;
+ 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 port, 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] = port;
+ 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 following comments are extracted from syscons.c (1.287) */
+ /*
+ * With release 2.1 of the Xaccel server, the keyboard is left
+ * hanging pretty often. Apparently an interrupt from the
+ * keyboard is lost, and I don't know why (yet).
+ * This ugly hack calls scintr if input is ready for the keyboard
+ * and conveniently hides the problem. XXX
+ */
+ /*
+ * Try removing anything stuck in the keyboard controller; whether
+ * it's a keyboard scan code or mouse data. `scintr()' doesn't
+ * read the mouse data directly, but `kbdio' routines will, as a
+ * side effect.
+ */
+ s = spltty();
+ kbd = (keyboard_t *)arg;
+ if ((*kbdsw[kbd->kb_index]->lock)(kbd, TRUE)) {
+ /*
+ * We have seen the lock flag is not set. Let's reset
+ * the flag early, otherwise the LED update routine fails
+ * which may want the lock during the interrupt routine.
+ */
+ (*kbdsw[kbd->kb_index]->lock)(kbd, FALSE);
+ if ((*kbdsw[kbd->kb_index]->check_char)(kbd))
+ (*kbdsw[kbd->kb_index]->intr)(kbd, NULL);
+ }
+ splx(s);
+ timeout(atkbd_timeout, arg, hz/10);
+}
+
+/* cdev driver functions */
+
+
+/* LOW-LEVEL */
+
+#include <machine/limits.h>
+#include <machine/console.h>
+#include <machine/clock.h>
+
+#define ATKBD_DEFAULT 0
+
+typedef struct atkbd_state {
+ KBDC kbdc; /* keyboard controller */
+ /* XXX: don't move this field; pcvt
+ * expects `kbdc' to be the first
+ * field in this structure. */
+ int ks_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
+ int ks_flags; /* flags */
+#define COMPOSE (1 << 0)
+ int ks_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 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 conole 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;
+
+ /* XXX */
+ if (unit == ATKBD_DEFAULT) {
+ if (KBD_IS_PROBED(&default_kbd))
+ return 0;
+ }
+
+ kbdc = kbdc_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 *data = (int *)arg;
+
+ /* 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);
+ if (kbd == NULL)
+ return ENOMEM;
+ bzero(kbd, sizeof(*kbd));
+ state = malloc(sizeof(*state), M_DEVBUF, M_NOWAIT);
+ keymap = malloc(sizeof(key_map), M_DEVBUF, M_NOWAIT);
+ accmap = malloc(sizeof(accent_map), M_DEVBUF, M_NOWAIT);
+ fkeymap = malloc(sizeof(fkey_tab), M_DEVBUF, M_NOWAIT);
+ fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]);
+ if ((state == NULL) || (keymap == NULL) || (accmap == NULL)
+ || (fkeymap == NULL)) {
+ if (state != NULL)
+ free(state, M_DEVBUF);
+ if (keymap != NULL)
+ free(keymap, M_DEVBUF);
+ if (accmap != NULL)
+ free(accmap, M_DEVBUF);
+ if (fkeymap != NULL)
+ free(fkeymap, M_DEVBUF);
+ free(kbd, M_DEVBUF);
+ return ENOMEM;
+ }
+ bzero(state, sizeof(*state));
+ } else if (KBD_IS_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 = kbdc_open(data[0]);
+ if (state->kbdc == NULL)
+ return ENXIO;
+ kbd_init_struct(kbd, ATKBD_DRIVER_NAME, KB_OTHER, unit, flags,
+ data[0], IO_KBDSIZE);
+ bcopy(&key_map, keymap, sizeof(key_map));
+ bcopy(&accent_map, accmap, sizeof(accent_map));
+ bcopy(fkey_tab, fkeymap,
+ imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab)));
+ kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size);
+ kbd->kb_data = (void *)state;
+
+ if (probe_keyboard(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);
+ KBD_INIT_DONE(kbd);
+ }
+ if (!KBD_IS_CONFIGURED(kbd)) {
+ if (kbd_register(kbd) < 0)
+ return ENXIO;
+ KBD_CONFIG_DONE(kbd);
+ }
+
+ return 0;
+}
+
+/* finish using this keyboard */
+static int
+atkbd_term(keyboard_t *kbd)
+{
+ kbd_unregister(kbd);
+ return 0;
+}
+
+/* keyboard interrupt routine */
+static int
+atkbd_intr(keyboard_t *kbd, void *arg)
+{
+ atkbd_state_t *state;
+ int c;
+
+ if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) {
+ /* let the callback function to process the input */
+ (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT,
+ kbd->kb_callback.kc_arg);
+ } else {
+ /* read and discard the input; no one is waiting for input */
+ do {
+ c = atkbd_read_char(kbd, FALSE);
+ } while (c != NOKEY);
+
+ if (!KBD_HAS_DEVICE(kbd)) {
+ /*
+ * The keyboard was not detected before;
+ * it must have been reconnected!
+ */
+ state = (atkbd_state_t *)kbd->kb_data;
+ init_keyboard(state->kbdc, &kbd->kb_type,
+ kbd->kb_config);
+ atkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state);
+ KBD_FOUND_DEVICE(kbd);
+ }
+ }
+ return 0;
+}
+
+/* test the interface to the device */
+static int
+atkbd_test_if(keyboard_t *kbd)
+{
+ int error;
+ int s;
+
+ error = 0;
+ empty_both_buffers(((atkbd_state_t *)kbd->kb_data)->kbdc, 10);
+ s = spltty();
+ if (!test_controller(((atkbd_state_t *)kbd->kb_data)->kbdc))
+ error = EIO;
+ else if (test_kbd_port(((atkbd_state_t *)kbd->kb_data)->kbdc) != 0)
+ error = EIO;
+ splx(s);
+
+ return error;
+}
+
+/*
+ * Enable the access to the device; until this function is called,
+ * the client cannot read from the keyboard.
+ */
+static int
+atkbd_enable(keyboard_t *kbd)
+{
+ int s;
+
+ s = spltty();
+ KBD_ACTIVATE(kbd);
+ splx(s);
+ return 0;
+}
+
+/* disallow the access to the device */
+static int
+atkbd_disable(keyboard_t *kbd)
+{
+ int s;
+
+ s = spltty();
+ KBD_DEACTIVATE(kbd);
+ splx(s);
+ return 0;
+}
+
+/* read one byte from the keyboard if it's allowed */
+static int
+atkbd_read(keyboard_t *kbd, int wait)
+{
+ int c;
+
+ if (wait)
+ c = read_kbd_data(((atkbd_state_t *)kbd->kb_data)->kbdc);
+ else
+ c = read_kbd_data_no_wait(((atkbd_state_t *)kbd->kb_data)->kbdc);
+ 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;
+ kbd->kb_prev_key = keycode | (scancode & 0x80);
+ 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;
+ kbd->kb_prev_key = keycode | (scancode & 0x80);
+ 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;
+ kbd->kb_prev_key = keycode | (scancode & 0x80);
+ if (state->ks_composed_char > UCHAR_MAX)
+ return ERRKEY;
+ goto next_code;
+ case 0x52: /* keypad 0 */
+ state->ks_composed_char *= 10;
+ kbd->kb_prev_key = keycode | (scancode & 0x80);
+ 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 */
+ kbd->kb_prev_key = keycode | (scancode & 0x80);
+ 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;
+ kbd->kb_prev_key = keycode | (scancode & 0x80);
+ return ERRKEY;
+ }
+ break;
+ }
+ }
+
+ /* keycode to key action */
+ action = genkbd_keyaction(kbd, keycode, scancode & 0x80,
+ &state->ks_state, &state->ks_accents);
+ kbd->kb_prev_key = keycode | (scancode & 0x80);
+ if (action == NOKEY)
+ goto next_code;
+ else
+ return action;
+}
+
+/* check if char is waiting */
+static int
+atkbd_check_char(keyboard_t *kbd)
+{
+ atkbd_state_t *state;
+
+ if (!KBD_IS_ACTIVE(kbd))
+ return FALSE;
+ state = (atkbd_state_t *)kbd->kb_data;
+ if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0))
+ return TRUE;
+ return kbdc_data_ready(state->kbdc);
+}
+
+/* some useful control functions */
+static int
+atkbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
+{
+ /* trasnlate LED_XXX bits into the device specific bits */
+ static u_char ledmap[8] = {
+ 0, 4, 2, 6, 1, 5, 3, 7,
+ };
+ atkbd_state_t *state = kbd->kb_data;
+ int error;
+ int s;
+ int i;
+
+ s = spltty();
+ switch (cmd) {
+
+ case KDGKBMODE: /* get keyboard mode */
+ *(int *)arg = state->ks_mode;
+ break;
+ case KDSKBMODE: /* set keyboard mode */
+ switch (*(int *)arg) {
+ case K_XLATE:
+ if (state->ks_mode != K_XLATE) {
+ /* make lock key state and LED state match */
+ state->ks_state &= ~LOCK_MASK;
+ state->ks_state |= KBD_LED_VAL(kbd);
+ }
+ /* FALL THROUGH */
+ case K_RAW:
+ case K_CODE:
+ if (state->ks_mode != *(int *)arg) {
+ atkbd_clear_state(kbd);
+ state->ks_mode = *(int *)arg;
+ }
+ break;
+ default:
+ splx(s);
+ return EINVAL;
+ }
+ break;
+
+ case KDGETLED: /* get keyboard LED */
+ *(int *)arg = KBD_LED_VAL(kbd);
+ break;
+ case KDSETLED: /* set keyboard LED */
+ /* NOTE: lock key state in ks_state won't be changed */
+ if (*(int *)arg & ~LOCK_MASK) {
+ splx(s);
+ return EINVAL;
+ }
+ i = *(int *)arg;
+ /* replace CAPS LED with ALTGR LED for ALTGR keyboards */
+ if (kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
+ if (i & ALKED)
+ i |= CLKED;
+ else
+ i &= ~CLKED;
+ }
+ if (KBD_HAS_DEVICE(kbd)) {
+ error = write_kbd(state->kbdc, KBDC_SET_LEDS,
+ ledmap[i & LED_MASK]);
+ if (error) {
+ splx(s);
+ return error;
+ }
+ }
+ KBD_LED_VAL(kbd) = *(int *)arg;
+ break;
+
+ case KDGKBSTATE: /* get lock key state */
+ *(int *)arg = state->ks_state & LOCK_MASK;
+ break;
+ case KDSKBSTATE: /* set lock key state */
+ if (*(int *)arg & ~LOCK_MASK) {
+ splx(s);
+ return EINVAL;
+ }
+ state->ks_state &= ~LOCK_MASK;
+ state->ks_state |= *(int *)arg;
+ splx(s);
+ /* set LEDs and quit */
+ return atkbd_ioctl(kbd, KDSETLED, arg);
+
+ case 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;
+ /* FALL THROUGH */
+ default:
+ splx(s);
+ return genkbd_commonioctl(kbd, cmd, arg);
+ }
+
+ splx(s);
+ return 0;
+}
+
+/* lock the access to the keyboard */
+static int
+atkbd_lock(keyboard_t *kbd, int lock)
+{
+ return kbdc_lock(((atkbd_state_t *)kbd->kb_data)->kbdc, lock);
+}
+
+/* clear the internal state of the keyboard */
+static void
+atkbd_clear_state(keyboard_t *kbd)
+{
+ atkbd_state_t *state;
+
+ state = (atkbd_state_t *)kbd->kb_data;
+ state->ks_flags = 0;
+ state->ks_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
+setup_kbd_port(KBDC kbdc, int port, int intr)
+{
+ if (!set_controller_command_byte(kbdc,
+ KBD_KBD_CONTROL_BITS,
+ ((port) ? KBD_ENABLE_KBD_PORT : KBD_DISABLE_KBD_PORT)
+ | ((intr) ? KBD_ENABLE_KBD_INT : KBD_DISABLE_KBD_INT)))
+ return 1;
+ return 0;
+}
+
+static int
+get_kbd_echo(KBDC kbdc)
+{
+ /* enable the keyboard port, but disable the keyboard intr. */
+ if (setup_kbd_port(kbdc, TRUE, FALSE))
+ /* CONTROLLER ERROR: there is very little we can do... */
+ return ENXIO;
+
+ /* see if something is present */
+ write_kbd_command(kbdc, KBDC_ECHO);
+ if (read_kbd_data(kbdc) != KBD_ECHO) {
+ empty_both_buffers(kbdc, 10);
+ test_controller(kbdc);
+ test_kbd_port(kbdc);
+ return ENXIO;
+ }
+
+ /* enable the keyboard port and intr. */
+ if (setup_kbd_port(kbdc, TRUE, TRUE)) {
+ /*
+ * CONTROLLER ERROR
+ * This is serious; the keyboard intr is left disabled!
+ */
+ return ENXIO;
+ }
+
+ return 0;
+}
+
+static int
+probe_keyboard(KBDC kbdc, int flags)
+{
+ /*
+ * Don't try to print anything in this function. The low-level
+ * console may not have been initialized yet...
+ */
+ int err;
+ int c;
+ int m;
+
+ if (!kbdc_lock(kbdc, TRUE)) {
+ /* driver error? */
+ return ENXIO;
+ }
+
+ /* flush any noise in the buffer */
+ empty_both_buffers(kbdc, 10);
+
+ /* save the current keyboard controller command byte */
+ m = kbdc_get_device_mask(kbdc) & ~KBD_KBD_CONTROL_BITS;
+ c = get_controller_command_byte(kbdc);
+ if (c == -1) {
+ /* CONTROLLER ERROR */
+ kbdc_set_device_mask(kbdc, m);
+ kbdc_lock(kbdc, FALSE);
+ return ENXIO;
+ }
+
+ /*
+ * The keyboard may have been screwed up by the boot block.
+ * We may just be able to recover from error by testing the controller
+ * and the keyboard port. The controller command byte needs to be
+ * saved before this recovery operation, as some controllers seem
+ * to set the command byte to particular values.
+ */
+ test_controller(kbdc);
+ test_kbd_port(kbdc);
+
+ err = get_kbd_echo(kbdc);
+ if (err == 0) {
+ kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS);
+ } else {
+ if (c != -1)
+ /* try to restore the command byte as before */
+ set_controller_command_byte(kbdc, 0xff, c);
+ kbdc_set_device_mask(kbdc, m);
+ }
+
+ kbdc_lock(kbdc, FALSE);
+ return err;
+}
+
+static int
+init_keyboard(KBDC kbdc, int *type, int flags)
+{
+ int codeset;
+ int id;
+ int c;
+
+ if (!kbdc_lock(kbdc, TRUE)) {
+ /* driver error? */
+ return EIO;
+ }
+
+ /* save the current controller command byte */
+ empty_both_buffers(kbdc, 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:
+ case 0x83ab:
+ *type = KB_101;
+ break;
+ case -1: /* AT 84 keyboard doesn't return ID */
+ *type = KB_84;
+ break;
+ default:
+ break;
+ }
+ if (bootverbose)
+ printf("atkbd: keyboard ID 0x%x (%d)\n", id, *type);
+
+ /* reset keyboard hardware */
+ if (!(flags & KB_CONF_NO_RESET) && !reset_kbd(kbdc)) {
+ /*
+ * KEYBOARD ERROR
+ * Keyboard reset may fail either because the keyboard
+ * doen't exist, or because the keyboard doesn't pass
+ * the self-test, or the keyboard controller on the
+ * motherboard and the keyboard somehow fail to shake hands.
+ * It is just possible, particularly in the last case,
+ * that the keyoard controller may be left in a hung state.
+ * test_controller() and test_kbd_port() appear to bring
+ * the keyboard controller back (I don't know why and how,
+ * though.)
+ */
+ empty_both_buffers(kbdc, 10);
+ test_controller(kbdc);
+ test_kbd_port(kbdc);
+ /*
+ * We could disable the keyboard port and interrupt... but,
+ * the keyboard may still exist (see above).
+ */
+ set_controller_command_byte(kbdc, 0xff, c);
+ kbdc_lock(kbdc, FALSE);
+ if (bootverbose)
+ printf("atkbd: failed to reset the keyboard.\n");
+ return EIO;
+ }
+
+ /*
+ * Allow us to set the XT_KEYBD flag in UserConfig so that keyboards
+ * such as those on the IBM ThinkPad laptop computers can be used
+ * with the standard console driver.
+ */
+ if (codeset == 1) {
+ if (send_kbd_command_and_data(kbdc,
+ KBDC_SET_SCANCODE_SET, codeset) == KBD_ACK) {
+ /* XT kbd doesn't need scan code translation */
+ c &= ~KBD_TRANSLATION;
+ } else {
+ /*
+ * KEYBOARD ERROR
+ * The XT kbd isn't usable unless the proper scan
+ * code set is selected.
+ */
+ set_controller_command_byte(kbdc, 0xff, c);
+ kbdc_lock(kbdc, FALSE);
+ printf("atkbd: unable to set the XT keyboard mode.\n");
+ return EIO;
+ }
+ }
+
+#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;
+}
+
+#endif /* NATKBD > 0 */
diff --git a/sys/dev/atkbdc/atkbd_atkbdc.c b/sys/dev/atkbdc/atkbd_atkbdc.c
new file mode 100644
index 0000000..f686170
--- /dev/null
+++ b/sys/dev/atkbdc/atkbd_atkbdc.c
@@ -0,0 +1,128 @@
+/*-
+ * 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 "atkbd.h"
+#include "opt_kbd.h"
+
+#if NATKBD > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+
+#include <machine/resource.h>
+
+#include <dev/kbd/kbdreg.h>
+#include <dev/kbd/atkbdreg.h>
+#include <dev/kbd/atkbdcreg.h>
+
+#include <isa/isareg.h>
+#include <isa/isavar.h>
+
+devclass_t atkbd_devclass;
+
+static int atkbdprobe(device_t dev);
+static int atkbdattach(device_t dev);
+static void atkbd_isa_intr(void *arg);
+
+static device_method_t atkbd_methods[] = {
+ DEVMETHOD(device_probe, atkbdprobe),
+ DEVMETHOD(device_attach, atkbdattach),
+ { 0, 0 }
+};
+
+static driver_t atkbd_driver = {
+ ATKBD_DRIVER_NAME,
+ atkbd_methods,
+ 1,
+};
+
+static int
+atkbdprobe(device_t dev)
+{
+ uintptr_t port;
+ uintptr_t irq;
+ uintptr_t flags;
+
+ device_set_desc(dev, "AT Keyboard");
+
+ /* obtain parameters */
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port);
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq);
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags);
+
+ /* probe the device */
+ return atkbd_probe_unit(device_get_unit(dev), port, irq, flags);
+}
+
+static int
+atkbdattach(device_t dev)
+{
+ keyboard_t *kbd;
+ uintptr_t port;
+ uintptr_t irq;
+ uintptr_t flags;
+ struct resource *res;
+ void *ih;
+ int zero = 0;
+ int error;
+
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port);
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq);
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags);
+
+ error = atkbd_attach_unit(device_get_unit(dev), &kbd, port, irq, flags);
+ if (error)
+ return error;
+
+ /* declare our interrupt handler */
+ res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ BUS_SETUP_INTR(device_get_parent(dev), dev, res, INTR_TYPE_TTY,
+ atkbd_isa_intr, kbd, &ih);
+
+ 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);
+
+#endif /* NATKBD > 0 */
diff --git a/sys/dev/atkbdc/atkbd_isa.c b/sys/dev/atkbdc/atkbd_isa.c
new file mode 100644
index 0000000..f686170
--- /dev/null
+++ b/sys/dev/atkbdc/atkbd_isa.c
@@ -0,0 +1,128 @@
+/*-
+ * 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 "atkbd.h"
+#include "opt_kbd.h"
+
+#if NATKBD > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+
+#include <machine/resource.h>
+
+#include <dev/kbd/kbdreg.h>
+#include <dev/kbd/atkbdreg.h>
+#include <dev/kbd/atkbdcreg.h>
+
+#include <isa/isareg.h>
+#include <isa/isavar.h>
+
+devclass_t atkbd_devclass;
+
+static int atkbdprobe(device_t dev);
+static int atkbdattach(device_t dev);
+static void atkbd_isa_intr(void *arg);
+
+static device_method_t atkbd_methods[] = {
+ DEVMETHOD(device_probe, atkbdprobe),
+ DEVMETHOD(device_attach, atkbdattach),
+ { 0, 0 }
+};
+
+static driver_t atkbd_driver = {
+ ATKBD_DRIVER_NAME,
+ atkbd_methods,
+ 1,
+};
+
+static int
+atkbdprobe(device_t dev)
+{
+ uintptr_t port;
+ uintptr_t irq;
+ uintptr_t flags;
+
+ device_set_desc(dev, "AT Keyboard");
+
+ /* obtain parameters */
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port);
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq);
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags);
+
+ /* probe the device */
+ return atkbd_probe_unit(device_get_unit(dev), port, irq, flags);
+}
+
+static int
+atkbdattach(device_t dev)
+{
+ keyboard_t *kbd;
+ uintptr_t port;
+ uintptr_t irq;
+ uintptr_t flags;
+ struct resource *res;
+ void *ih;
+ int zero = 0;
+ int error;
+
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port);
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq);
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags);
+
+ error = atkbd_attach_unit(device_get_unit(dev), &kbd, port, irq, flags);
+ if (error)
+ return error;
+
+ /* declare our interrupt handler */
+ res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ BUS_SETUP_INTR(device_get_parent(dev), dev, res, INTR_TYPE_TTY,
+ atkbd_isa_intr, kbd, &ih);
+
+ 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);
+
+#endif /* NATKBD > 0 */
diff --git a/sys/dev/atkbdc/atkbdc.c b/sys/dev/atkbdc/atkbdc.c
new file mode 100644
index 0000000..68fcffc
--- /dev/null
+++ b/sys/dev/atkbdc/atkbdc.c
@@ -0,0 +1,1021 @@
+/*-
+ * 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 "atkbdc.h"
+#include "opt_kbd.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/syslog.h>
+
+#include <machine/clock.h>
+
+#include <dev/kbd/atkbdcreg.h>
+
+#include <isa/isareg.h>
+
+/* constants */
+
+#define MAXKBDC MAX(NATKBDC, 1)
+
+/* macros */
+
+#ifndef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#define kbdcp(p) ((atkbdc_softc_t *)(p))
+#define nextq(i) (((i) + 1) % KBDQ_BUFSIZE)
+#define availq(q) ((q)->head != (q)->tail)
+#if KBDIO_DEBUG >= 2
+#define emptyq(q) ((q)->tail = (q)->head = (q)->qcount = 0)
+#else
+#define emptyq(q) ((q)->tail = (q)->head = 0)
+#endif
+
+/* local variables */
+
+/*
+ * We always need at least one copy of the kbdc_softc struct for the
+ * low-level console. As the low-level console accesses the keyboard
+ * controller before kbdc, and all other devices, is probed, we
+ * statically allocate one entry. XXX
+ */
+static atkbdc_softc_t default_kbdc;
+static atkbdc_softc_t *atkbdc_softc[MAXKBDC] = { &default_kbdc };
+
+static int verbose = KBDIO_DEBUG;
+
+/* function prototypes */
+
+static int atkbdc_setup(atkbdc_softc_t *sc, int port);
+static int addq(kqueue *q, int c);
+static int removeq(kqueue *q);
+static int wait_while_controller_busy(atkbdc_softc_t *kbdc);
+static int wait_for_data(atkbdc_softc_t *kbdc);
+static int wait_for_kbd_data(atkbdc_softc_t *kbdc);
+static int wait_for_kbd_ack(atkbdc_softc_t *kbdc);
+static int wait_for_aux_data(atkbdc_softc_t *kbdc);
+static int wait_for_aux_ack(atkbdc_softc_t *kbdc);
+
+#if NATKBDC > 0
+
+atkbdc_softc_t
+*atkbdc_get_softc(int unit)
+{
+ atkbdc_softc_t *sc;
+
+ if (unit >= sizeof(atkbdc_softc)/sizeof(atkbdc_softc[0]))
+ return NULL;
+ sc = atkbdc_softc[unit];
+ if (sc == NULL) {
+ sc = atkbdc_softc[unit]
+ = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT);
+ if (sc == NULL)
+ return NULL;
+ bzero(sc, sizeof(*sc));
+ sc->port = -1; /* XXX */
+ }
+ return sc;
+}
+
+int
+atkbdc_probe_unit(int unit, int port)
+{
+ if (port <= 0)
+ return ENXIO;
+ return 0;
+}
+
+int
+atkbdc_attach_unit(int unit, atkbdc_softc_t *sc, int port)
+{
+ return atkbdc_setup(sc, port);
+}
+
+#endif /* NATKBDC > 0 */
+
+/* the backdoor to the keyboard controller! XXX */
+int
+atkbdc_configure(void)
+{
+ return atkbdc_setup(atkbdc_softc[0], -1);
+}
+
+static int
+atkbdc_setup(atkbdc_softc_t *sc, int port)
+{
+ if (port <= 0)
+ port = IO_KBD;
+
+ if (sc->port <= 0) {
+ sc->command_byte = -1;
+ sc->command_mask = 0;
+ sc->lock = FALSE;
+ sc->kbd.head = sc->kbd.tail = 0;
+ sc->aux.head = sc->aux.tail = 0;
+#if KBDIO_DEBUG >= 2
+ sc->kbd.call_count = 0;
+ sc->kbd.qcount = sc->kbd.max_qcount = 0;
+ sc->aux.call_count = 0;
+ sc->aux.qcount = sc->aux.max_qcount = 0;
+#endif
+ }
+ sc->port = port; /* may override the previous value */
+ return 0;
+}
+
+/* associate a port number with a KBDC */
+
+KBDC
+kbdc_open(int port)
+{
+ int s;
+ int i;
+
+ if (port <= 0)
+ port = IO_KBD;
+
+ s = spltty();
+ for (i = 0; i < sizeof(atkbdc_softc)/sizeof(atkbdc_softc[0]); ++i) {
+ if (atkbdc_softc[i] == NULL)
+ continue;
+ if (atkbdc_softc[i]->port == port) {
+ splx(s);
+ return (KBDC)atkbdc_softc[i];
+ }
+ if (atkbdc_softc[i]->port <= 0) {
+ if (atkbdc_setup(atkbdc_softc[i], port))
+ break;
+ splx(s);
+ return (KBDC)atkbdc_softc[i];
+ }
+ }
+ splx(s);
+ return NULL;
+}
+
+/*
+ * I/O access arbitration in `kbdio'
+ *
+ * The `kbdio' module uses a simplistic convention to arbitrate
+ * I/O access to the controller/keyboard/mouse. The convention requires
+ * close cooperation of the calling device driver.
+ *
+ * The device driver which utilizes the `kbdio' module are assumed to
+ * have the following set of routines.
+ * a. An interrupt handler (the bottom half of the driver).
+ * b. Timeout routines which may briefly polls the keyboard controller.
+ * c. Routines outside interrupt context (the top half of the driver).
+ * They should follow the rules below:
+ * 1. The interrupt handler may assume that it always has full access
+ * to the controller/keyboard/mouse.
+ * 2. The other routines must issue `spltty()' if they wish to
+ * prevent the interrupt handler from accessing
+ * the controller/keyboard/mouse.
+ * 3. The timeout routines and the top half routines of the device driver
+ * arbitrate I/O access by observing the lock flag in `kbdio'.
+ * The flag is manipulated via `kbdc_lock()'; when one wants to
+ * perform I/O, call `kbdc_lock(kbdc, TRUE)' and proceed only if
+ * the call returns with TRUE. Otherwise the caller must back off.
+ * Call `kbdc_lock(kbdc, FALSE)' when necessary I/O operaion
+ * is finished. This mechanism does not prevent the interrupt
+ * handler from being invoked at any time and carrying out I/O.
+ * Therefore, `spltty()' must be strategically placed in the device
+ * driver code. Also note that the timeout routine may interrupt
+ * `kbdc_lock()' called by the top half of the driver, but this
+ * interruption is OK so long as the timeout routine observes the
+ * the rule 4 below.
+ * 4. The interrupt and timeout routines should not extend I/O operation
+ * across more than one interrupt or timeout; they must complete
+ * necessary I/O operation within one invokation of the routine.
+ * This measns that if the timeout routine acquires the lock flag,
+ * it must reset the flag to FALSE before it returns.
+ */
+
+/* set/reset polling lock */
+int
+kbdc_lock(KBDC p, int lock)
+{
+ int prevlock;
+
+ prevlock = kbdcp(p)->lock;
+ kbdcp(p)->lock = lock;
+
+ return (prevlock != lock);
+}
+
+/* check if any data is waiting to be processed */
+int
+kbdc_data_ready(KBDC p)
+{
+ return (availq(&kbdcp(p)->kbd) || availq(&kbdcp(p)->aux)
+ || (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL));
+}
+
+/* queuing functions */
+
+static int
+addq(kqueue *q, int c)
+{
+ if (nextq(q->tail) != q->head) {
+ q->q[q->tail] = c;
+ q->tail = nextq(q->tail);
+#if KBDIO_DEBUG >= 2
+ ++q->call_count;
+ ++q->qcount;
+ if (q->qcount > q->max_qcount)
+ q->max_qcount = q->qcount;
+#endif
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int
+removeq(kqueue *q)
+{
+ int c;
+
+ if (q->tail != q->head) {
+ c = q->q[q->head];
+ q->head = nextq(q->head);
+#if KBDIO_DEBUG >= 2
+ --q->qcount;
+#endif
+ return c;
+ }
+ return -1;
+}
+
+/*
+ * device I/O routines
+ */
+static int
+wait_while_controller_busy(struct atkbdc_softc *kbdc)
+{
+ /* CPU will stay inside the loop for 100msec at most */
+ int retry = 5000;
+ int port = kbdc->port;
+ int f;
+
+ while ((f = inb(port + KBD_STATUS_PORT)) & KBDS_INPUT_BUFFER_FULL) {
+ if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ addq(&kbdc->kbd, inb(port + KBD_DATA_PORT));
+ } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ addq(&kbdc->aux, inb(port + KBD_DATA_PORT));
+ }
+ DELAY(KBDC_DELAYTIME);
+ if (--retry < 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * wait for any data; whether it's from the controller,
+ * the keyboard, or the aux device.
+ */
+static int
+wait_for_data(struct atkbdc_softc *kbdc)
+{
+ /* CPU will stay inside the loop for 200msec at most */
+ int retry = 10000;
+ int port = kbdc->port;
+ int f;
+
+ while ((f = inb(port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) == 0) {
+ DELAY(KBDC_DELAYTIME);
+ if (--retry < 0)
+ return 0;
+ }
+ DELAY(KBDD_DELAYTIME);
+ return f;
+}
+
+/* wait for data from the keyboard */
+static int
+wait_for_kbd_data(struct atkbdc_softc *kbdc)
+{
+ /* CPU will stay inside the loop for 200msec at most */
+ int retry = 10000;
+ int port = kbdc->port;
+ int f;
+
+ while ((f = inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL)
+ != KBDS_KBD_BUFFER_FULL) {
+ if (f == KBDS_AUX_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ addq(&kbdc->aux, inb(port + KBD_DATA_PORT));
+ }
+ DELAY(KBDC_DELAYTIME);
+ if (--retry < 0)
+ return 0;
+ }
+ DELAY(KBDD_DELAYTIME);
+ return f;
+}
+
+/*
+ * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the keyboard.
+ * queue anything else.
+ */
+static int
+wait_for_kbd_ack(struct atkbdc_softc *kbdc)
+{
+ /* CPU will stay inside the loop for 200msec at most */
+ int retry = 10000;
+ int port = kbdc->port;
+ int f;
+ int b;
+
+ while (retry-- > 0) {
+ if ((f = inb(port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ b = inb(port + KBD_DATA_PORT);
+ if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) {
+ if ((b == KBD_ACK) || (b == KBD_RESEND)
+ || (b == KBD_RESET_FAIL))
+ return b;
+ addq(&kbdc->kbd, b);
+ } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) {
+ addq(&kbdc->aux, b);
+ }
+ }
+ DELAY(KBDC_DELAYTIME);
+ }
+ return -1;
+}
+
+/* wait for data from the aux device */
+static int
+wait_for_aux_data(struct atkbdc_softc *kbdc)
+{
+ /* CPU will stay inside the loop for 200msec at most */
+ int retry = 10000;
+ int port = kbdc->port;
+ int f;
+
+ while ((f = inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL)
+ != KBDS_AUX_BUFFER_FULL) {
+ if (f == KBDS_KBD_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ addq(&kbdc->kbd, inb(port + KBD_DATA_PORT));
+ }
+ DELAY(KBDC_DELAYTIME);
+ if (--retry < 0)
+ return 0;
+ }
+ DELAY(KBDD_DELAYTIME);
+ return f;
+}
+
+/*
+ * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the aux device.
+ * queue anything else.
+ */
+static int
+wait_for_aux_ack(struct atkbdc_softc *kbdc)
+{
+ /* CPU will stay inside the loop for 200msec at most */
+ int retry = 10000;
+ int port = kbdc->port;
+ int f;
+ int b;
+
+ while (retry-- > 0) {
+ if ((f = inb(port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ b = inb(port + KBD_DATA_PORT);
+ if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) {
+ if ((b == PSM_ACK) || (b == PSM_RESEND)
+ || (b == PSM_RESET_FAIL))
+ return b;
+ addq(&kbdc->aux, b);
+ } else if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) {
+ addq(&kbdc->kbd, b);
+ }
+ }
+ DELAY(KBDC_DELAYTIME);
+ }
+ return -1;
+}
+
+/* write a one byte command to the controller */
+int
+write_controller_command(KBDC p, int c)
+{
+ if (!wait_while_controller_busy(kbdcp(p)))
+ return FALSE;
+ outb(kbdcp(p)->port + KBD_COMMAND_PORT, c);
+ return TRUE;
+}
+
+/* write a one byte data to the controller */
+int
+write_controller_data(KBDC p, int c)
+{
+ if (!wait_while_controller_busy(kbdcp(p)))
+ return FALSE;
+ outb(kbdcp(p)->port + KBD_DATA_PORT, c);
+ return TRUE;
+}
+
+/* write a one byte keyboard command */
+int
+write_kbd_command(KBDC p, int c)
+{
+ if (!wait_while_controller_busy(kbdcp(p)))
+ return FALSE;
+ outb(kbdcp(p)->port + KBD_DATA_PORT, c);
+ return TRUE;
+}
+
+/* write a one byte auxiliary device command */
+int
+write_aux_command(KBDC p, int c)
+{
+ if (!write_controller_command(p, KBDC_WRITE_TO_AUX))
+ return FALSE;
+ return write_controller_data(p, c);
+}
+
+/* send a command to the keyboard and wait for ACK */
+int
+send_kbd_command(KBDC p, int c)
+{
+ int retry = KBD_MAXRETRY;
+ int res = -1;
+
+ while (retry-- > 0) {
+ if (!write_kbd_command(p, c))
+ continue;
+ res = wait_for_kbd_ack(kbdcp(p));
+ if (res == KBD_ACK)
+ break;
+ }
+ return res;
+}
+
+/* send a command to the auxiliary device and wait for ACK */
+int
+send_aux_command(KBDC p, int c)
+{
+ int retry = KBD_MAXRETRY;
+ int res = -1;
+
+ while (retry-- > 0) {
+ if (!write_aux_command(p, c))
+ continue;
+ /*
+ * FIXME: XXX
+ * The aux device may have already sent one or two bytes of
+ * status data, when a command is received. It will immediately
+ * stop data transmission, thus, leaving an incomplete data
+ * packet in our buffer. We have to discard any unprocessed
+ * data in order to remove such packets. Well, we may remove
+ * unprocessed, but necessary data byte as well...
+ */
+ emptyq(&kbdcp(p)->aux);
+ res = wait_for_aux_ack(kbdcp(p));
+ if (res == PSM_ACK)
+ break;
+ }
+ return res;
+}
+
+/* send a command and a data to the keyboard, wait for ACKs */
+int
+send_kbd_command_and_data(KBDC p, int c, int d)
+{
+ int retry;
+ int res = -1;
+
+ for (retry = KBD_MAXRETRY; retry > 0; --retry) {
+ if (!write_kbd_command(p, c))
+ continue;
+ res = wait_for_kbd_ack(kbdcp(p));
+ if (res == KBD_ACK)
+ break;
+ else if (res != KBD_RESEND)
+ return res;
+ }
+ if (retry <= 0)
+ return res;
+
+ for (retry = KBD_MAXRETRY, res = -1; retry > 0; --retry) {
+ if (!write_kbd_command(p, d))
+ continue;
+ res = wait_for_kbd_ack(kbdcp(p));
+ if (res != KBD_RESEND)
+ break;
+ }
+ return res;
+}
+
+/* send a command and a data to the auxiliary device, wait for ACKs */
+int
+send_aux_command_and_data(KBDC p, int c, int d)
+{
+ int retry;
+ int res = -1;
+
+ for (retry = KBD_MAXRETRY; retry > 0; --retry) {
+ if (!write_aux_command(p, c))
+ continue;
+ emptyq(&kbdcp(p)->aux);
+ res = wait_for_aux_ack(kbdcp(p));
+ if (res == PSM_ACK)
+ break;
+ else if (res != PSM_RESEND)
+ return res;
+ }
+ if (retry <= 0)
+ return res;
+
+ for (retry = KBD_MAXRETRY, res = -1; retry > 0; --retry) {
+ if (!write_aux_command(p, d))
+ continue;
+ res = wait_for_aux_ack(kbdcp(p));
+ if (res != PSM_RESEND)
+ break;
+ }
+ return res;
+}
+
+/*
+ * read one byte from any source; whether from the controller,
+ * the keyboard, or the aux device
+ */
+int
+read_controller_data(KBDC p)
+{
+ if (availq(&kbdcp(p)->kbd))
+ return removeq(&kbdcp(p)->kbd);
+ if (availq(&kbdcp(p)->aux))
+ return removeq(&kbdcp(p)->aux);
+ if (!wait_for_data(kbdcp(p)))
+ return -1; /* timeout */
+ return inb(kbdcp(p)->port + KBD_DATA_PORT);
+}
+
+#if KBDIO_DEBUG >= 2
+static int call = 0;
+#endif
+
+/* read one byte from the keyboard */
+int
+read_kbd_data(KBDC p)
+{
+#if KBDIO_DEBUG >= 2
+ if (++call > 2000) {
+ call = 0;
+ log(LOG_DEBUG, "kbdc: kbd q: %d calls, max %d chars, "
+ "aux q: %d calls, max %d chars\n",
+ kbdcp(p)->kbd.call_count, kbdcp(p)->kbd.max_qcount,
+ kbdcp(p)->aux.call_count, kbdcp(p)->aux.max_qcount);
+ }
+#endif
+
+ if (availq(&kbdcp(p)->kbd))
+ return removeq(&kbdcp(p)->kbd);
+ if (!wait_for_kbd_data(kbdcp(p)))
+ return -1; /* timeout */
+ return inb(kbdcp(p)->port + KBD_DATA_PORT);
+}
+
+/* read one byte from the keyboard, but return immediately if
+ * no data is waiting
+ */
+int
+read_kbd_data_no_wait(KBDC p)
+{
+ int f;
+
+#if KBDIO_DEBUG >= 2
+ if (++call > 2000) {
+ call = 0;
+ log(LOG_DEBUG, "kbdc: kbd q: %d calls, max %d chars, "
+ "aux q: %d calls, max %d chars\n",
+ kbdcp(p)->kbd.call_count, kbdcp(p)->kbd.max_qcount,
+ kbdcp(p)->aux.call_count, kbdcp(p)->aux.max_qcount);
+ }
+#endif
+
+ if (availq(&kbdcp(p)->kbd))
+ return removeq(&kbdcp(p)->kbd);
+ f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL;
+ if (f == KBDS_AUX_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ addq(&kbdcp(p)->aux, inb(kbdcp(p)->port + KBD_DATA_PORT));
+ f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL;
+ }
+ if (f == KBDS_KBD_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ return inb(kbdcp(p)->port + KBD_DATA_PORT);
+ }
+ return -1; /* no data */
+}
+
+/* read one byte from the aux device */
+int
+read_aux_data(KBDC p)
+{
+ if (availq(&kbdcp(p)->aux))
+ return removeq(&kbdcp(p)->aux);
+ if (!wait_for_aux_data(kbdcp(p)))
+ return -1; /* timeout */
+ return inb(kbdcp(p)->port + KBD_DATA_PORT);
+}
+
+/* read one byte from the aux device, but return immediately if
+ * no data is waiting
+ */
+int
+read_aux_data_no_wait(KBDC p)
+{
+ int f;
+
+ if (availq(&kbdcp(p)->aux))
+ return removeq(&kbdcp(p)->aux);
+ f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL;
+ if (f == KBDS_KBD_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ addq(&kbdcp(p)->kbd, inb(kbdcp(p)->port + KBD_DATA_PORT));
+ f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL;
+ }
+ if (f == KBDS_AUX_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ return inb(kbdcp(p)->port + KBD_DATA_PORT);
+ }
+ return -1; /* no data */
+}
+
+/* discard data from the keyboard */
+void
+empty_kbd_buffer(KBDC p, int wait)
+{
+ int t;
+ int b;
+ int f;
+#if KBDIO_DEBUG >= 2
+ int c1 = 0;
+ int c2 = 0;
+#endif
+ int delta = 2;
+
+ for (t = wait; t > 0; ) {
+ if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ b = inb(kbdcp(p)->port + KBD_DATA_PORT);
+ if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) {
+ addq(&kbdcp(p)->aux, b);
+#if KBDIO_DEBUG >= 2
+ ++c2;
+ } else {
+ ++c1;
+#endif
+ }
+ t = wait;
+ } else {
+ t -= delta;
+ }
+ DELAY(delta*1000);
+ }
+#if KBDIO_DEBUG >= 2
+ if ((c1 > 0) || (c2 > 0))
+ log(LOG_DEBUG, "kbdc: %d:%d char read (empty_kbd_buffer)\n", c1, c2);
+#endif
+
+ emptyq(&kbdcp(p)->kbd);
+}
+
+/* discard data from the aux device */
+void
+empty_aux_buffer(KBDC p, int wait)
+{
+ int t;
+ int b;
+ int f;
+#if KBDIO_DEBUG >= 2
+ int c1 = 0;
+ int c2 = 0;
+#endif
+ int delta = 2;
+
+ for (t = wait; t > 0; ) {
+ if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ b = inb(kbdcp(p)->port + KBD_DATA_PORT);
+ if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) {
+ addq(&kbdcp(p)->kbd, b);
+#if KBDIO_DEBUG >= 2
+ ++c1;
+ } else {
+ ++c2;
+#endif
+ }
+ t = wait;
+ } else {
+ t -= delta;
+ }
+ DELAY(delta*1000);
+ }
+#if KBDIO_DEBUG >= 2
+ if ((c1 > 0) || (c2 > 0))
+ log(LOG_DEBUG, "kbdc: %d:%d char read (empty_aux_buffer)\n", c1, c2);
+#endif
+
+ emptyq(&kbdcp(p)->aux);
+}
+
+/* discard any data from the keyboard or the aux device */
+void
+empty_both_buffers(KBDC p, int wait)
+{
+ int t;
+ int f;
+#if KBDIO_DEBUG >= 2
+ int c1 = 0;
+ int c2 = 0;
+#endif
+ int delta = 2;
+
+ for (t = wait; t > 0; ) {
+ if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) {
+ DELAY(KBDD_DELAYTIME);
+ (void)inb(kbdcp(p)->port + KBD_DATA_PORT);
+#if KBDIO_DEBUG >= 2
+ if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL)
+ ++c1;
+ else
+ ++c2;
+#endif
+ t = wait;
+ } else {
+ t -= delta;
+ }
+ DELAY(delta*1000);
+ }
+#if KBDIO_DEBUG >= 2
+ if ((c1 > 0) || (c2 > 0))
+ log(LOG_DEBUG, "kbdc: %d:%d char read (empty_both_buffers)\n", c1, c2);
+#endif
+
+ emptyq(&kbdcp(p)->kbd);
+ emptyq(&kbdcp(p)->aux);
+}
+
+/* keyboard and mouse device control */
+
+/* NOTE: enable the keyboard port but disable the keyboard
+ * interrupt before calling "reset_kbd()".
+ */
+int
+reset_kbd(KBDC p)
+{
+ int retry = KBD_MAXRETRY;
+ int again = KBD_MAXWAIT;
+ int c = KBD_RESEND; /* keep the compiler happy */
+
+ while (retry-- > 0) {
+ empty_both_buffers(p, 10);
+ if (!write_kbd_command(p, KBDC_RESET_KBD))
+ continue;
+ emptyq(&kbdcp(p)->kbd);
+ c = read_controller_data(p);
+ if (verbose || bootverbose)
+ log(LOG_DEBUG, "kbdc: RESET_KBD return code:%04x\n", c);
+ if (c == KBD_ACK) /* keyboard has agreed to reset itself... */
+ break;
+ }
+ if (retry < 0)
+ return FALSE;
+
+ while (again-- > 0) {
+ /* wait awhile, well, in fact we must wait quite loooooooooooong */
+ DELAY(KBD_RESETDELAY*1000);
+ c = read_controller_data(p); /* RESET_DONE/RESET_FAIL */
+ if (c != -1) /* wait again if the controller is not ready */
+ break;
+ }
+ if (verbose || bootverbose)
+ log(LOG_DEBUG, "kbdc: RESET_KBD status:%04x\n", c);
+ if (c != KBD_RESET_DONE)
+ return FALSE;
+ return TRUE;
+}
+
+/* NOTE: enable the aux port but disable the aux interrupt
+ * before calling `reset_aux_dev()'.
+ */
+int
+reset_aux_dev(KBDC p)
+{
+ int retry = KBD_MAXRETRY;
+ int again = KBD_MAXWAIT;
+ int c = PSM_RESEND; /* keep the compiler happy */
+
+ while (retry-- > 0) {
+ empty_both_buffers(p, 10);
+ if (!write_aux_command(p, PSMC_RESET_DEV))
+ continue;
+ emptyq(&kbdcp(p)->aux);
+ /* NOTE: Compaq Armada laptops require extra delay here. XXX */
+ for (again = KBD_MAXWAIT; again > 0; --again) {
+ DELAY(KBD_RESETDELAY*1000);
+ c = read_aux_data_no_wait(p);
+ if (c != -1)
+ break;
+ }
+ if (verbose || bootverbose)
+ log(LOG_DEBUG, "kbdc: RESET_AUX return code:%04x\n", c);
+ if (c == PSM_ACK) /* aux dev is about to reset... */
+ break;
+ }
+ if (retry < 0)
+ return FALSE;
+
+ for (again = KBD_MAXWAIT; again > 0; --again) {
+ /* wait awhile, well, quite looooooooooooong */
+ DELAY(KBD_RESETDELAY*1000);
+ c = read_aux_data_no_wait(p); /* RESET_DONE/RESET_FAIL */
+ if (c != -1) /* wait again if the controller is not ready */
+ break;
+ }
+ if (verbose || bootverbose)
+ log(LOG_DEBUG, "kbdc: RESET_AUX status:%04x\n", c);
+ if (c != PSM_RESET_DONE) /* reset status */
+ return FALSE;
+
+ c = read_aux_data(p); /* device ID */
+ if (verbose || bootverbose)
+ log(LOG_DEBUG, "kbdc: RESET_AUX ID:%04x\n", c);
+ /* NOTE: we could check the device ID now, but leave it later... */
+ return TRUE;
+}
+
+/* controller diagnostics and setup */
+
+int
+test_controller(KBDC p)
+{
+ int retry = KBD_MAXRETRY;
+ int again = KBD_MAXWAIT;
+ int c = KBD_DIAG_FAIL;
+
+ while (retry-- > 0) {
+ empty_both_buffers(p, 10);
+ if (write_controller_command(p, KBDC_DIAGNOSE))
+ break;
+ }
+ if (retry < 0)
+ return FALSE;
+
+ emptyq(&kbdcp(p)->kbd);
+ while (again-- > 0) {
+ /* wait awhile */
+ DELAY(KBD_RESETDELAY*1000);
+ c = read_controller_data(p); /* DIAG_DONE/DIAG_FAIL */
+ if (c != -1) /* wait again if the controller is not ready */
+ break;
+ }
+ if (verbose || bootverbose)
+ log(LOG_DEBUG, "kbdc: DIAGNOSE status:%04x\n", c);
+ return (c == KBD_DIAG_DONE);
+}
+
+int
+test_kbd_port(KBDC p)
+{
+ int retry = KBD_MAXRETRY;
+ int again = KBD_MAXWAIT;
+ int c = -1;
+
+ while (retry-- > 0) {
+ empty_both_buffers(p, 10);
+ if (write_controller_command(p, KBDC_TEST_KBD_PORT))
+ break;
+ }
+ if (retry < 0)
+ return FALSE;
+
+ emptyq(&kbdcp(p)->kbd);
+ while (again-- > 0) {
+ c = read_controller_data(p);
+ if (c != -1) /* try again if the controller is not ready */
+ break;
+ }
+ if (verbose || bootverbose)
+ log(LOG_DEBUG, "kbdc: TEST_KBD_PORT status:%04x\n", c);
+ return c;
+}
+
+int
+test_aux_port(KBDC p)
+{
+ int retry = KBD_MAXRETRY;
+ int again = KBD_MAXWAIT;
+ int c = -1;
+
+ while (retry-- > 0) {
+ empty_both_buffers(p, 10);
+ if (write_controller_command(p, KBDC_TEST_AUX_PORT))
+ break;
+ }
+ if (retry < 0)
+ return FALSE;
+
+ emptyq(&kbdcp(p)->kbd);
+ while (again-- > 0) {
+ c = read_controller_data(p);
+ if (c != -1) /* try again if the controller is not ready */
+ break;
+ }
+ if (verbose || bootverbose)
+ log(LOG_DEBUG, "kbdc: TEST_AUX_PORT status:%04x\n", c);
+ return c;
+}
+
+int
+kbdc_get_device_mask(KBDC p)
+{
+ return kbdcp(p)->command_mask;
+}
+
+void
+kbdc_set_device_mask(KBDC p, int mask)
+{
+ kbdcp(p)->command_mask =
+ mask & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS);
+}
+
+int
+get_controller_command_byte(KBDC p)
+{
+ if (kbdcp(p)->command_byte != -1)
+ return kbdcp(p)->command_byte;
+ if (!write_controller_command(p, KBDC_GET_COMMAND_BYTE))
+ return -1;
+ emptyq(&kbdcp(p)->kbd);
+ kbdcp(p)->command_byte = read_controller_data(p);
+ return kbdcp(p)->command_byte;
+}
+
+int
+set_controller_command_byte(KBDC p, int mask, int command)
+{
+ if (get_controller_command_byte(p) == -1)
+ return FALSE;
+
+ command = (kbdcp(p)->command_byte & ~mask) | (command & mask);
+ if (command & KBD_DISABLE_KBD_PORT) {
+ if (!write_controller_command(p, KBDC_DISABLE_KBD_PORT))
+ return FALSE;
+ }
+ if (!write_controller_command(p, KBDC_SET_COMMAND_BYTE))
+ return FALSE;
+ if (!write_controller_data(p, command))
+ return FALSE;
+ kbdcp(p)->command_byte = command;
+
+ if (verbose)
+ log(LOG_DEBUG, "kbdc: new command byte:%04x (set_controller...)\n",
+ command);
+
+ return TRUE;
+}
diff --git a/sys/dev/atkbdc/atkbdc_isa.c b/sys/dev/atkbdc/atkbdc_isa.c
new file mode 100644
index 0000000..9829f2b
--- /dev/null
+++ b/sys/dev/atkbdc/atkbdc_isa.c
@@ -0,0 +1,269 @@
+/*-
+ * 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 "atkbdc.h"
+#include "opt_kbd.h"
+
+#if NATKBDC > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/malloc.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>
+
+MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device");
+
+/* children */
+typedef struct atkbdc_device {
+ int flags; /* configuration flags */
+ int port; /* port number (same as the controller's) */
+ int irq; /* ISA IRQ mask */
+} atkbdc_device_t;
+
+/* kbdc */
+devclass_t atkbdc_devclass;
+
+static int atkbdc_probe(device_t dev);
+static int atkbdc_attach(device_t dev);
+static int atkbdc_print_child(device_t bus, device_t dev);
+static int atkbdc_read_ivar(device_t bus, device_t dev, int index,
+ u_long *val);
+static int atkbdc_write_ivar(device_t bus, device_t dev, int index,
+ u_long val);
+
+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_print_child, atkbdc_print_child),
+ DEVMETHOD(bus_read_ivar, atkbdc_read_ivar),
+ DEVMETHOD(bus_write_ivar, atkbdc_write_ivar),
+ DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_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 int
+atkbdc_probe(device_t dev)
+{
+ int error;
+ int rid;
+ struct resource *port;
+
+ /* Check isapnp ids */
+ if (isa_get_vendorid(dev))
+ return (ENXIO);
+
+ device_set_desc(dev, "keyboard controller (i8042)");
+ rid = 0;
+ port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
+ 0, ~0, IO_KBDSIZE, RF_ACTIVE);
+ if (!port)
+ return ENXIO;
+ error = atkbdc_probe_unit(device_get_unit(dev), rman_get_start(port));
+ bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
+ return error;
+}
+
+static void
+atkbdc_add_device(device_t dev, const char *name, int unit)
+{
+ atkbdc_softc_t *sc = *(atkbdc_softc_t **)device_get_softc(dev);
+ atkbdc_device_t *kdev;
+ device_t child;
+ int t;
+
+ kdev = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, M_NOWAIT);
+ if (!kdev)
+ return;
+ bzero(kdev, sizeof *kdev);
+
+ kdev->port = sc->port;
+
+ if (resource_int_value(name, unit, "irq", &t) == 0)
+ kdev->irq = t;
+ else
+ kdev->irq = -1;
+
+ if (resource_int_value(name, unit, "flags", &t) == 0)
+ kdev->flags = t;
+ else
+ kdev->flags = 0;
+
+ child = device_add_child(dev, name, unit);
+ device_set_ivars(child, kdev);
+}
+
+static int
+atkbdc_attach(device_t dev)
+{
+ atkbdc_softc_t *sc;
+ struct resource *port;
+ int unit;
+ int error;
+ int rid;
+ int i;
+
+ 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;
+ }
+
+ /* XXX should track resource in softc */
+ rid = 0;
+ port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
+ 0, ~0, IO_KBDSIZE, RF_ACTIVE);
+ if (!port)
+ return ENXIO;
+ error = atkbdc_attach_unit(unit, sc, rman_get_start(port));
+ if (error)
+ return error;
+ *(atkbdc_softc_t **)device_get_softc(dev) = sc;
+
+ /*
+ * Add all devices configured to be attached to atkbdc0.
+ */
+ for (i = resource_query_string(-1, "at", "atkbdc0");
+ i != -1;
+ i = resource_query_string(i, "at", "atkbdc0")) {
+ atkbdc_add_device(dev, resource_query_name(i),
+ resource_query_unit(i));
+ }
+
+ /*
+ * and atkbdc?
+ */
+ for (i = resource_query_string(-1, "at", "atkbdc");
+ i != -1;
+ i = resource_query_string(i, "at", "atkbdc")) {
+ atkbdc_add_device(dev, resource_query_name(i),
+ resource_query_unit(i));
+ }
+
+ bus_generic_attach(dev);
+
+ return 0;
+}
+
+static int
+atkbdc_print_child(device_t bus, device_t dev)
+{
+ atkbdc_device_t *kbdcdev;
+ int retval = 0;
+
+ kbdcdev = (atkbdc_device_t *)device_get_ivars(dev);
+
+ retval += bus_print_child_header(bus, dev);
+ if (kbdcdev->flags != 0)
+ retval += printf(" flags 0x%x", kbdcdev->flags);
+ if (kbdcdev->irq != -1)
+ retval += printf(" irq %d", kbdcdev->irq);
+ retval += bus_print_child_footer(bus, dev);
+
+ return (retval);
+}
+
+static int
+atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ switch (index) {
+ case KBDC_IVAR_PORT:
+ *val = (u_long)ivar->port;
+ break;
+ case KBDC_IVAR_IRQ:
+ *val = (u_long)ivar->irq;
+ break;
+ case KBDC_IVAR_FLAGS:
+ *val = (u_long)ivar->flags;
+ break;
+ default:
+ return ENOENT;
+ }
+ return 0;
+}
+
+static int
+atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ switch (index) {
+ case KBDC_IVAR_PORT:
+ ivar->port = (int)val;
+ break;
+ case KBDC_IVAR_IRQ:
+ ivar->irq = (int)val;
+ break;
+ case KBDC_IVAR_FLAGS:
+ ivar->flags = (int)val;
+ break;
+ default:
+ return ENOENT;
+ }
+ return 0;
+}
+
+DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0);
+
+#endif /* NATKBDC > 0 */
diff --git a/sys/dev/atkbdc/atkbdc_subr.c b/sys/dev/atkbdc/atkbdc_subr.c
new file mode 100644
index 0000000..9829f2b
--- /dev/null
+++ b/sys/dev/atkbdc/atkbdc_subr.c
@@ -0,0 +1,269 @@
+/*-
+ * 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 "atkbdc.h"
+#include "opt_kbd.h"
+
+#if NATKBDC > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/malloc.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>
+
+MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device");
+
+/* children */
+typedef struct atkbdc_device {
+ int flags; /* configuration flags */
+ int port; /* port number (same as the controller's) */
+ int irq; /* ISA IRQ mask */
+} atkbdc_device_t;
+
+/* kbdc */
+devclass_t atkbdc_devclass;
+
+static int atkbdc_probe(device_t dev);
+static int atkbdc_attach(device_t dev);
+static int atkbdc_print_child(device_t bus, device_t dev);
+static int atkbdc_read_ivar(device_t bus, device_t dev, int index,
+ u_long *val);
+static int atkbdc_write_ivar(device_t bus, device_t dev, int index,
+ u_long val);
+
+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_print_child, atkbdc_print_child),
+ DEVMETHOD(bus_read_ivar, atkbdc_read_ivar),
+ DEVMETHOD(bus_write_ivar, atkbdc_write_ivar),
+ DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_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 int
+atkbdc_probe(device_t dev)
+{
+ int error;
+ int rid;
+ struct resource *port;
+
+ /* Check isapnp ids */
+ if (isa_get_vendorid(dev))
+ return (ENXIO);
+
+ device_set_desc(dev, "keyboard controller (i8042)");
+ rid = 0;
+ port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
+ 0, ~0, IO_KBDSIZE, RF_ACTIVE);
+ if (!port)
+ return ENXIO;
+ error = atkbdc_probe_unit(device_get_unit(dev), rman_get_start(port));
+ bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
+ return error;
+}
+
+static void
+atkbdc_add_device(device_t dev, const char *name, int unit)
+{
+ atkbdc_softc_t *sc = *(atkbdc_softc_t **)device_get_softc(dev);
+ atkbdc_device_t *kdev;
+ device_t child;
+ int t;
+
+ kdev = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, M_NOWAIT);
+ if (!kdev)
+ return;
+ bzero(kdev, sizeof *kdev);
+
+ kdev->port = sc->port;
+
+ if (resource_int_value(name, unit, "irq", &t) == 0)
+ kdev->irq = t;
+ else
+ kdev->irq = -1;
+
+ if (resource_int_value(name, unit, "flags", &t) == 0)
+ kdev->flags = t;
+ else
+ kdev->flags = 0;
+
+ child = device_add_child(dev, name, unit);
+ device_set_ivars(child, kdev);
+}
+
+static int
+atkbdc_attach(device_t dev)
+{
+ atkbdc_softc_t *sc;
+ struct resource *port;
+ int unit;
+ int error;
+ int rid;
+ int i;
+
+ 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;
+ }
+
+ /* XXX should track resource in softc */
+ rid = 0;
+ port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
+ 0, ~0, IO_KBDSIZE, RF_ACTIVE);
+ if (!port)
+ return ENXIO;
+ error = atkbdc_attach_unit(unit, sc, rman_get_start(port));
+ if (error)
+ return error;
+ *(atkbdc_softc_t **)device_get_softc(dev) = sc;
+
+ /*
+ * Add all devices configured to be attached to atkbdc0.
+ */
+ for (i = resource_query_string(-1, "at", "atkbdc0");
+ i != -1;
+ i = resource_query_string(i, "at", "atkbdc0")) {
+ atkbdc_add_device(dev, resource_query_name(i),
+ resource_query_unit(i));
+ }
+
+ /*
+ * and atkbdc?
+ */
+ for (i = resource_query_string(-1, "at", "atkbdc");
+ i != -1;
+ i = resource_query_string(i, "at", "atkbdc")) {
+ atkbdc_add_device(dev, resource_query_name(i),
+ resource_query_unit(i));
+ }
+
+ bus_generic_attach(dev);
+
+ return 0;
+}
+
+static int
+atkbdc_print_child(device_t bus, device_t dev)
+{
+ atkbdc_device_t *kbdcdev;
+ int retval = 0;
+
+ kbdcdev = (atkbdc_device_t *)device_get_ivars(dev);
+
+ retval += bus_print_child_header(bus, dev);
+ if (kbdcdev->flags != 0)
+ retval += printf(" flags 0x%x", kbdcdev->flags);
+ if (kbdcdev->irq != -1)
+ retval += printf(" irq %d", kbdcdev->irq);
+ retval += bus_print_child_footer(bus, dev);
+
+ return (retval);
+}
+
+static int
+atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ switch (index) {
+ case KBDC_IVAR_PORT:
+ *val = (u_long)ivar->port;
+ break;
+ case KBDC_IVAR_IRQ:
+ *val = (u_long)ivar->irq;
+ break;
+ case KBDC_IVAR_FLAGS:
+ *val = (u_long)ivar->flags;
+ break;
+ default:
+ return ENOENT;
+ }
+ return 0;
+}
+
+static int
+atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val)
+{
+ atkbdc_device_t *ivar;
+
+ ivar = (atkbdc_device_t *)device_get_ivars(dev);
+ switch (index) {
+ case KBDC_IVAR_PORT:
+ ivar->port = (int)val;
+ break;
+ case KBDC_IVAR_IRQ:
+ ivar->irq = (int)val;
+ break;
+ case KBDC_IVAR_FLAGS:
+ ivar->flags = (int)val;
+ break;
+ default:
+ return ENOENT;
+ }
+ return 0;
+}
+
+DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0);
+
+#endif /* NATKBDC > 0 */
diff --git a/sys/dev/atkbdc/atkbdcreg.h b/sys/dev/atkbdc/atkbdcreg.h
new file mode 100644
index 0000000..2085bb6
--- /dev/null
+++ b/sys/dev/atkbdc/atkbdcreg.h
@@ -0,0 +1,247 @@
+/*-
+ * 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_
+
+/* constants */
+
+/* I/O ports */
+#define KBD_STATUS_PORT 4 /* status port, read */
+#define KBD_COMMAND_PORT 4 /* controller command port, write */
+#define KBD_DATA_PORT 0 /* data port, read/write
+ * also used as keyboard command
+ * and mouse command port
+ */
+
+/* controller commands (sent to KBD_COMMAND_PORT) */
+#define KBDC_SET_COMMAND_BYTE 0x0060
+#define KBDC_GET_COMMAND_BYTE 0x0020
+#define KBDC_WRITE_TO_AUX 0x00d4
+#define KBDC_DISABLE_AUX_PORT 0x00a7
+#define KBDC_ENABLE_AUX_PORT 0x00a8
+#define KBDC_TEST_AUX_PORT 0x00a9
+#define KBDC_DIAGNOSE 0x00aa
+#define KBDC_TEST_KBD_PORT 0x00ab
+#define KBDC_DISABLE_KBD_PORT 0x00ad
+#define KBDC_ENABLE_KBD_PORT 0x00ae
+
+/* controller command byte (set by KBDC_SET_COMMAND_BYTE) */
+#define KBD_TRANSLATION 0x0040
+#define KBD_RESERVED_BITS 0x0004
+#define KBD_OVERRIDE_KBD_LOCK 0x0008
+#define KBD_ENABLE_KBD_PORT 0x0000
+#define KBD_DISABLE_KBD_PORT 0x0010
+#define KBD_ENABLE_AUX_PORT 0x0000
+#define KBD_DISABLE_AUX_PORT 0x0020
+#define KBD_ENABLE_AUX_INT 0x0002
+#define KBD_DISABLE_AUX_INT 0x0000
+#define KBD_ENABLE_KBD_INT 0x0001
+#define KBD_DISABLE_KBD_INT 0x0000
+#define KBD_KBD_CONTROL_BITS (KBD_DISABLE_KBD_PORT | KBD_ENABLE_KBD_INT)
+#define KBD_AUX_CONTROL_BITS (KBD_DISABLE_AUX_PORT | KBD_ENABLE_AUX_INT)
+
+/* keyboard device commands (sent to KBD_DATA_PORT) */
+#define KBDC_RESET_KBD 0x00ff
+#define KBDC_ENABLE_KBD 0x00f4
+#define KBDC_DISABLE_KBD 0x00f5
+#define KBDC_SET_DEFAULTS 0x00f6
+#define KBDC_SEND_DEV_ID 0x00f2
+#define KBDC_SET_LEDS 0x00ed
+#define KBDC_ECHO 0x00ee
+#define KBDC_SET_SCANCODE_SET 0x00f0
+#define KBDC_SET_TYPEMATIC 0x00f3
+
+/* aux device commands (sent to KBD_DATA_PORT) */
+#define PSMC_RESET_DEV 0x00ff
+#define PSMC_ENABLE_DEV 0x00f4
+#define PSMC_DISABLE_DEV 0x00f5
+#define PSMC_SET_DEFAULTS 0x00f6
+#define PSMC_SEND_DEV_ID 0x00f2
+#define PSMC_SEND_DEV_STATUS 0x00e9
+#define PSMC_SEND_DEV_DATA 0x00eb
+#define PSMC_SET_SCALING11 0x00e6
+#define PSMC_SET_SCALING21 0x00e7
+#define PSMC_SET_RESOLUTION 0x00e8
+#define PSMC_SET_STREAM_MODE 0x00ea
+#define PSMC_SET_REMOTE_MODE 0x00f0
+#define PSMC_SET_SAMPLING_RATE 0x00f3
+
+/* PSMC_SET_RESOLUTION argument */
+#define PSMD_RES_LOW 0 /* typically 25ppi */
+#define PSMD_RES_MEDIUM_LOW 1 /* typically 50ppi */
+#define PSMD_RES_MEDIUM_HIGH 2 /* typically 100ppi (default) */
+#define PSMD_RES_HIGH 3 /* typically 200ppi */
+#define PSMD_MAX_RESOLUTION PSMD_RES_HIGH
+
+/* PSMC_SET_SAMPLING_RATE */
+#define PSMD_MAX_RATE 255 /* FIXME: not sure if it's possible */
+
+/* status bits (KBD_STATUS_PORT) */
+#define KBDS_BUFFER_FULL 0x0021
+#define KBDS_ANY_BUFFER_FULL 0x0001
+#define KBDS_KBD_BUFFER_FULL 0x0001
+#define KBDS_AUX_BUFFER_FULL 0x0021
+#define KBDS_INPUT_BUFFER_FULL 0x0002
+
+/* return code */
+#define KBD_ACK 0x00fa
+#define KBD_RESEND 0x00fe
+#define KBD_RESET_DONE 0x00aa
+#define KBD_RESET_FAIL 0x00fc
+#define KBD_DIAG_DONE 0x0055
+#define KBD_DIAG_FAIL 0x00fd
+#define KBD_ECHO 0x00ee
+
+#define PSM_ACK 0x00fa
+#define PSM_RESEND 0x00fe
+#define PSM_RESET_DONE 0x00aa
+#define PSM_RESET_FAIL 0x00fc
+
+/* aux device ID */
+#define PSM_MOUSE_ID 0
+#define PSM_BALLPOINT_ID 2
+#define PSM_INTELLI_ID 3
+
+#ifdef KERNEL
+
+#define ATKBDC_DRIVER_NAME "atkbdc"
+
+/*
+ * driver specific options: the following options may be set by
+ * `options' statements in the kernel configuration file.
+ */
+
+/* retry count */
+#ifndef KBD_MAXRETRY
+#define KBD_MAXRETRY 3
+#endif
+
+/* timing parameters */
+#ifndef KBD_RESETDELAY
+#define KBD_RESETDELAY 200 /* wait 200msec after kbd/mouse reset */
+#endif
+#ifndef KBD_MAXWAIT
+#define KBD_MAXWAIT 5 /* wait 5 times at most after reset */
+#endif
+
+/* I/O recovery time */
+#define KBDC_DELAYTIME 20
+#define KBDD_DELAYTIME 7
+
+/* debug option */
+#ifndef KBDIO_DEBUG
+#define KBDIO_DEBUG 0
+#endif
+
+/* end of driver specific options */
+
+/* types/structures */
+
+#define KBDQ_BUFSIZE 32
+
+typedef struct _kqueue {
+ int head;
+ int tail;
+ unsigned char q[KBDQ_BUFSIZE];
+#if KBDIO_DEBUG >= 2
+ int call_count;
+ int qcount;
+ int max_qcount;
+#endif
+} kqueue;
+
+typedef struct atkbdc_softc {
+ int port; /* base port address */
+ int command_byte; /* current command byte value */
+ int command_mask; /* command byte mask bits for kbd/aux devices */
+ int lock; /* FIXME: XXX not quite a semaphore... */
+ kqueue kbd; /* keyboard data queue */
+ kqueue aux; /* auxiliary data queue */
+} atkbdc_softc_t;
+
+enum kbdc_device_ivar {
+ KBDC_IVAR_PORT,
+ KBDC_IVAR_IRQ,
+ KBDC_IVAR_FLAGS,
+};
+
+typedef caddr_t KBDC;
+
+/* function prototypes */
+
+atkbdc_softc_t *atkbdc_get_softc(int unit);
+int atkbdc_probe_unit(int unit, int port);
+int atkbdc_attach_unit(int unit, atkbdc_softc_t *sc, int port);
+int atkbdc_configure(void);
+
+KBDC kbdc_open(int port);
+int kbdc_lock(KBDC kbdc, int lock);
+int kbdc_data_ready(KBDC kbdc);
+
+int write_controller_command(KBDC kbdc,int c);
+int write_controller_data(KBDC kbdc,int c);
+
+int write_kbd_command(KBDC kbdc,int c);
+int write_aux_command(KBDC kbdc,int c);
+int send_kbd_command(KBDC kbdc,int c);
+int send_aux_command(KBDC kbdc,int c);
+int send_kbd_command_and_data(KBDC kbdc,int c,int d);
+int send_aux_command_and_data(KBDC kbdc,int c,int d);
+
+int read_controller_data(KBDC kbdc);
+int read_kbd_data(KBDC kbdc);
+int read_kbd_data_no_wait(KBDC kbdc);
+int read_aux_data(KBDC kbdc);
+int read_aux_data_no_wait(KBDC kbdc);
+
+void empty_kbd_buffer(KBDC kbdc, int t);
+void empty_aux_buffer(KBDC kbdc, int t);
+void empty_both_buffers(KBDC kbdc, int t);
+
+int reset_kbd(KBDC kbdc);
+int reset_aux_dev(KBDC kbdc);
+
+int test_controller(KBDC kbdc);
+int test_kbd_port(KBDC kbdc);
+int test_aux_port(KBDC kbdc);
+
+int kbdc_get_device_mask(KBDC kbdc);
+void kbdc_set_device_mask(KBDC kbdc, int mask);
+
+int get_controller_command_byte(KBDC kbdc);
+int set_controller_command_byte(KBDC kbdc, int command, int flag);
+
+#endif /* KERNEL */
+
+#endif /* !_DEV_KBD_ATKBDCREG_H_ */
diff --git a/sys/dev/atkbdc/atkbdreg.h b/sys/dev/atkbdc/atkbdreg.h
new file mode 100644
index 0000000..bda6afb
--- /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 port, int irq, int flags);
+int atkbd_attach_unit(int unit, keyboard_t **kbd,
+ int port, int irq, int flags);
+
+#endif /* KERNEL */
+
+#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..928e920
--- /dev/null
+++ b/sys/dev/atkbdc/psm.c
@@ -0,0 +1,2454 @@
+/*-
+ * 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 "psm.h"
+#include "opt_psm.h"
+
+#if NPSM > 0
+
+#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 <sys/malloc.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <sys/select.h>
+#include <sys/uio.h>
+
+#include <machine/clock.h>
+#include <machine/limits.h>
+#include <machine/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
+
+/* features */
+
+/* #define PSM_HOOKRESUME hook the system resume event */
+/* #define PSM_RESETAFTERSUSPEND reset the device at the resume event */
+
+#ifdef PSM_HOOKAPM
+#undef PSM_HOOKRESUME
+#define PSM_HOOKRESUME 1
+#endif /* PSM_HOOKAPM */
+
+#ifndef PSM_HOOKRESUME
+#undef PSM_RESETAFTERSUSPEND
+#endif /* PSM_HOOKRESUME */
+
+/* end of driver specific options */
+
+/* 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 */
+ 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 */
+ int addr; /* I/O port address */
+ 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 */
+};
+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_FLAGS (PSM_CONFIG_RESOLUTION \
+ | PSM_CONFIG_ACCEL \
+ | PSM_CONFIG_NOCHECKSYNC \
+ | PSM_CONFIG_NOIDPROBE \
+ | PSM_CONFIG_NORESET \
+ | PSM_CONFIG_FORCETAP \
+ | PSM_CONFIG_IGNPORTERROR)
+
+/* 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 __P((struct psm_softc *, unsigned char *,
+ int *, int, mousestatus_t *));
+
+/* function prototypes */
+static int psmprobe __P((device_t));
+static int psmattach __P((device_t));
+static int psmresume __P((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 __P((KBDC));
+static int disable_aux_dev __P((KBDC));
+static int get_mouse_status __P((KBDC, int *, int, int));
+static int get_aux_id __P((KBDC));
+static int set_mouse_sampling_rate __P((KBDC, int));
+static int set_mouse_scaling __P((KBDC, int));
+static int set_mouse_resolution __P((KBDC, int));
+#ifdef PSM_RESETAFTERSUSPEND
+static int set_mouse_mode __P((KBDC));
+#endif /* PSM_RESETAFTERSUSPEND */
+static int get_mouse_buttons __P((KBDC));
+static int is_a_mouse __P((int));
+static void recover_from_error __P((KBDC));
+static int restore_controller __P((KBDC, int));
+#ifdef PSM_RESETAFTERSUSPEND
+static int reinitialize __P((int, mousemode_t *));
+#endif
+static int doopen __P((int, int));
+static char *model_name(int);
+static void psmintr(void*);
+
+/* vendor specific features */
+typedef int probefunc_t __P((struct psm_softc *));
+
+static int mouse_id_proc1 __P((KBDC, int, int, 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_msintelli;
+static probefunc_t enable_mmanplus;
+static probefunc_t enable_versapad;
+static int tame_mouse __P((struct psm_softc *, mousestatus_t *, unsigned char *));
+
+static struct {
+ int model;
+ unsigned char syncmask;
+ int packetsize;
+ probefunc_t *probefunc;
+} vendortype[] = {
+ { MOUSE_MODEL_NET, /* Genius NetMouse */
+ 0xc8, MOUSE_INTELLI_PACKETSIZE, enable_gmouse, },
+ { MOUSE_MODEL_NETSCROLL, /* Genius NetScroll */
+ 0xc8, 6, enable_groller, },
+ { MOUSE_MODEL_GLIDEPOINT, /* ALPS GlidePoint */
+ 0xc0, MOUSE_PS2_PACKETSIZE, enable_aglide, },
+ { MOUSE_MODEL_MOUSEMANPLUS, /* Logitech MouseMan+ */
+ 0x08, MOUSE_PS2_PACKETSIZE, enable_mmanplus, },
+ { MOUSE_MODEL_THINK, /* Kensignton ThinkingMouse */
+ 0x80, MOUSE_PS2_PACKETSIZE, enable_kmouse, },
+ { MOUSE_MODEL_INTELLI, /* Microsoft IntelliMouse */
+ 0xc8, MOUSE_INTELLI_PACKETSIZE, enable_msintelli, },
+ { MOUSE_MODEL_VERSAPAD, /* Interlink electronics VersaPad */
+ 0xe8, MOUSE_PS2VERSA_PACKETSIZE, enable_versapad, },
+ { MOUSE_MODEL_GENERIC,
+ 0xc0, MOUSE_PS2_PACKETSIZE, NULL, },
+};
+#define GENERIC_MOUSE_ENTRY 7
+
+/* device driver declarateion */
+static device_method_t psm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, psmprobe),
+ DEVMETHOD(device_attach, psmattach),
+ DEVMETHOD(device_resume, psmresume),
+
+ { 0, 0 }
+};
+
+static driver_t psm_driver = {
+ "psm",
+ 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",
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+ /* bmaj */ -1
+};
+
+/* 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);
+}
+
+#ifdef PSM_RESETAFTERSUSPEND
+/*
+ * 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);
+}
+#endif /* PSM_RESETAFTERSUSPEND */
+
+
+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 */
+ -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" },
+ { MOUSE_MODEL_NET, "NetMouse" },
+ { MOUSE_MODEL_GLIDEPOINT, "GlidePoint" },
+ { MOUSE_MODEL_THINK, "ThinkingMouse" },
+ { MOUSE_MODEL_INTELLI, "IntelliMouse" },
+ { MOUSE_MODEL_MOUSEMANPLUS, "MouseMan+" },
+ { MOUSE_MODEL_VERSAPAD, "VersaPad" },
+ { 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");
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+#ifdef PSM_RESETAFTERSUSPEND
+/*
+ * 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
+reinitialize(int unit, mousemode_t *mode)
+{
+ struct psm_softc *sc = PSM_SOFTC(unit);
+ 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",
+ unit, i);
+ /* fall though */
+ 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",
+ 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", 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", 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",
+ 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 (reinitialize).\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(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 (reinitialize).\n", unit);
+
+ return TRUE;
+}
+#endif /* PSM_RESETAFTERSUSPEND */
+
+static int
+doopen(int unit, int command_byte)
+{
+ struct psm_softc *sc = PSM_SOFTC(unit);
+ 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 (!reinitialize(unit, &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",
+ unit);
+ return (EIO);
+ }
+ }
+
+ if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3)
+ log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", 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",
+ unit);
+ return (EIO);
+ }
+
+ return (0);
+}
+
+/* psm driver entry points */
+
+#define endprobe(v) { if (bootverbose) \
+ --verbose; \
+ kbdc_set_device_mask(sc->kbdc, mask); \
+ kbdc_lock(sc->kbdc, FALSE); \
+ free(sc, M_DEVBUF); \
+ return (v); \
+ }
+
+static int
+psmprobe(device_t dev)
+{
+ int unit = device_get_unit(dev);
+ struct psm_softc *sc = device_get_softc(dev);
+ uintptr_t port;
+ uintptr_t flags;
+ int stat[3];
+ int command_byte;
+ int mask;
+ int i;
+
+#if 0
+ kbdc_debug(TRUE);
+#endif
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port);
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags);
+
+ sc->addr = port;
+ sc->kbdc = kbdc_open(sc->addr);
+ sc->config = flags & PSM_CONFIG_FLAGS;
+ 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);
+ /* fall though */
+ 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:
+ 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);
+ void *ih;
+ struct resource *res;
+ uintptr_t irq;
+ int zero = 0;
+
+ if (sc == NULL) /* shouldn't happen */
+ return (ENXIO);
+
+ /* Setup initial state */
+ sc->state = PSM_VALID;
+
+ /* Done */
+ make_dev(&psm_cdevsw, PSM_MKMINOR(unit, FALSE), 0, 0, 0666, "psm%d", unit);
+ 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;
+
+ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq);
+ res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ BUS_SETUP_INTR(device_get_parent(dev), dev, res, INTR_TYPE_TTY,
+ psmintr, sc, &ih);
+
+ return (0);
+}
+
+static int
+psmopen(dev_t dev, int flag, int fmt, struct proc *p)
+{
+ int unit = PSM_UNIT(dev);
+ struct psm_softc *sc;
+ int command_byte;
+ int err;
+ int s;
+
+ /* Validate unit number */
+ if (unit >= NPSM)
+ return (ENXIO);
+
+ /* 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->rsel.si_flags = 0;
+ sc->rsel.si_pid = 0;
+ sc->mode.level = sc->dflt_mode.level;
+ sc->mode.protocol = sc->dflt_mode.protocol;
+
+ /* 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;
+
+ /* 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(unit, 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 proc *p)
+{
+ 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",
+ PSM_UNIT(dev));
+ /* 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);
+
+ /* 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",
+ PSM_UNIT(dev));
+ }
+
+ if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3)
+ log(LOG_DEBUG, "psm%d: failed to get status (psmclose).\n",
+ PSM_UNIT(dev));
+ }
+
+ 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",
+ PSM_UNIT(dev));
+ }
+
+ /* 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 proc *p)
+{
+ 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
+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;
+ 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;
+
+ /*
+ * Check sync bits. We check for overflow bits and the bit 3
+ * for most mice. True, the code doesn't work if overflow
+ * condition occurs. But we expect it rarely happens...
+ */
+ if ((sc->inputbytes == 0)
+ && ((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]);
+ continue;
+ }
+
+ 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];
+
+ /*
+ * A kludge for Kensington device!
+ * The MSB of the horizontal count appears to be stored in
+ * a strange place. This kludge doesn't affect other mice
+ * because the bit is the overflow bit which is, in most cases,
+ * expected to be zero when we reach here. XXX
+ */
+ if (sc->hw.model != MOUSE_MODEL_VERSAPAD)
+ 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_INTELLI:
+ case MOUSE_MODEL_NET:
+ /* wheel data is in the fourth byte */
+ z = (char)sc->ipacket[3];
+ 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, and currently ignored */
+ /* FALL THROUGH */
+ case 0:
+ /* device type packet - shouldn't happen */
+ /* FALL THROUGH */
+ default:
+ x = y = 0;
+ ms.button = ms.obutton;
+ 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 button and wheel events */
+ ms.button |= (sc->ipacket[3] & MOUSE_PS2_BUTTON3DOWN)
+ ? MOUSE_BUTTON4DOWN : 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_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;
+
+ /* 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 proc *p)
+{
+ 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(p, &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;
+}
+
+#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 */
+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!! */
+ sc->hw.buttons = get_mouse_buttons(sc->kbdc);
+ sc->hw.buttons = 4;
+ return TRUE;
+}
+
+/* Genius NetMouse/NetMouse Pro */
+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+ */
+static int
+enable_mmanplus(struct psm_softc *sc)
+{
+ static char res[] = {
+ -1, PSMD_RES_LOW, PSMD_RES_HIGH, PSMD_RES_MEDIUM_HIGH,
+ PSMD_RES_MEDIUM_LOW, -1, PSMD_RES_HIGH, PSMD_RES_MEDIUM_LOW,
+ PSMD_RES_MEDIUM_HIGH, PSMD_RES_HIGH,
+ };
+ KBDC kbdc = sc->kbdc;
+ int data[3];
+ int i;
+
+ /* the special sequence to enable the fourth button and the roller. */
+ for (i = 0; i < sizeof(res)/sizeof(res[0]); ++i) {
+ if (res[i] < 0) {
+ if (!set_mouse_scaling(kbdc, 1))
+ return FALSE;
+ } else {
+ if (set_mouse_resolution(kbdc, res[i]) != res[i])
+ 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,...
+ */
+ /* 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 */
+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;
+}
+
+/* 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 */
+
+ return TRUE; /* PS/2 absolute mode */
+}
+
+static int
+psmresume(device_t dev)
+{
+#ifdef PSM_HOOKRESUME
+ struct psm_softc *sc = device_get_softc(dev);
+ int unit = device_get_unit(dev);
+ int err = 0;
+ int s;
+ int c;
+
+ if (verbose >= 2)
+ log(LOG_NOTICE, "psm%d: system resume hook called.\n", unit);
+
+ /* don't let anybody mess with the aux device */
+ if (!kbdc_lock(sc->kbdc, TRUE))
+ return (EIO);
+ s = spltty();
+
+ /* 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 (psmresume).\n",
+ 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 (psmresume).\n",
+ 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;
+
+#ifdef PSM_RESETAFTERSUSPEND
+ /* try to detect the aux device; are you still there? */
+ if (reinitialize(unit, &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! (psmresume).\n",
+ unit);
+ err = ENXIO;
+ }
+#endif /* PSM_RESETAFTERSUSPEND */
+ splx(s);
+
+ /* restore the driver state */
+ if ((sc->state & PSM_OPEN) && (err == 0)) {
+ /* enable the aux device and the port again */
+ err = doopen(unit, c);
+ if (err != 0)
+ log(LOG_ERR, "psm%d: failed to enable the device (psmresume).\n",
+ 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 (psmresume).\n",
+ unit);
+ err = EIO;
+ }
+ }
+
+ /* done */
+ kbdc_lock(sc->kbdc, FALSE);
+ 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);
+#else /* !PSM_HOOKRESUME */
+ return (0);
+#endif /* PSM_HOOKRESUME */
+}
+
+DRIVER_MODULE(psm, atkbdc, psm_driver, psm_devclass, 0, 0);
+
+#endif /* NPSM > 0 */
OpenPOWER on IntegriCloud