aboutsummaryrefslogtreecommitdiff
path: root/sys/isa/kbdio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/isa/kbdio.c')
-rw-r--r--sys/isa/kbdio.c470
1 files changed, 470 insertions, 0 deletions
diff --git a/sys/isa/kbdio.c b/sys/isa/kbdio.c
new file mode 100644
index 000000000000..f3262958a8f5
--- /dev/null
+++ b/sys/isa/kbdio.c
@@ -0,0 +1,470 @@
+/*-
+ * Copyright (c) 1996 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 REGENTS 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 REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <machine/clock.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/kbdio.h>
+
+/*
+ * device I/O routines
+ */
+int
+wait_while_controller_busy(int port)
+{
+ /* CPU will stay inside the loop for 100msec at most */
+ int retry = 5000;
+
+ while (inb(port + KBD_STATUS_PORT) & KBDS_CONTROLLER_BUSY) {
+ DELAY(20);
+ if (--retry < 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int
+wait_until_controller_is_really_idle(int port)
+{
+ /* CPU will stay inside the loop for 100msec at most */
+ int retry = 5000;
+
+ while (inb(port + KBD_STATUS_PORT)
+ & (KBDS_CONTROLLER_BUSY | KBDS_ANY_BUFFER_FULL)) {
+ DELAY(20);
+ if (--retry < 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * wait for any data; whether it's from the controller,
+ * the keyboard, or the aux device.
+ */
+int
+wait_for_data(int port)
+{
+ /* CPU will stay inside the loop for 200msec at most */
+ int retry = 10000;
+
+ while ((inb(port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) == 0) {
+ DELAY(20);
+ if (--retry < 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* wait for data from the keyboard */
+int
+wait_for_kbd_data(int port)
+{
+ /* CPU will stay inside the loop for 200msec at most */
+ int retry = 10000;
+
+ while ((inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL)
+ != KBDS_KBD_BUFFER_FULL) {
+ DELAY(20);
+ if (--retry < 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* wait for data from the aux device */
+int
+wait_for_aux_data(int port)
+{
+ /* CPU will stay inside the loop for 200msec at most */
+ int retry = 10000;
+
+ while ((inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL)
+ != KBDS_AUX_BUFFER_FULL) {
+ DELAY(20);
+ if (--retry < 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void
+write_controller_command(int port, int c)
+{
+ wait_until_controller_is_really_idle(port);
+ outb(port + KBD_COMMAND_PORT, c);
+}
+
+void
+write_controller_data(int port, int c)
+{
+ wait_until_controller_is_really_idle(port);
+ outb(port + KBD_DATA_PORT, c);
+}
+
+void
+write_kbd_command(int port, int c)
+{
+ wait_until_controller_is_really_idle(port);
+ outb(port + KBD_DATA_PORT, c);
+}
+
+void
+write_aux_command(int port, int c)
+{
+ write_controller_command(port,KBDC_WRITE_TO_AUX);
+ write_controller_data(port, c);
+}
+
+int
+send_kbd_command(int port, int c)
+{
+ int retry = KBD_MAXRETRY;
+ int res;
+
+ while (retry-- > 0) {
+ wait_until_controller_is_really_idle(port);
+ outb(port + KBD_DATA_PORT, c);
+ res = read_controller_data(port);
+ if (res == KBD_ACK)
+ break;
+ }
+ return res;
+}
+
+int
+send_aux_command(int port, int c)
+{
+ int retry = KBD_MAXRETRY;
+ int res;
+
+ while (retry-- > 0) {
+ wait_until_controller_is_really_idle(port);
+ outb(port + KBD_COMMAND_PORT, KBDC_WRITE_TO_AUX);
+ wait_until_controller_is_really_idle(port);
+ outb(port + KBD_DATA_PORT, c);
+ res = read_aux_data(port);
+ if (res == PSM_ACK)
+ break;
+ }
+ return res;
+}
+
+int
+send_kbd_command_and_data(int port, int c, int d)
+{
+ int retry;
+ int res;
+
+ for (retry = KBD_MAXRETRY; retry > 0; --retry) {
+ wait_until_controller_is_really_idle(port);
+ outb(port + KBD_DATA_PORT, c);
+ res = read_controller_data(port);
+ if (res == KBD_ACK)
+ break;
+ }
+
+ for (retry = KBD_MAXRETRY; retry > 0; --retry) {
+ wait_until_controller_is_really_idle(port);
+ outb(port + KBD_DATA_PORT, d);
+ res = read_controller_data(port);
+ if (res != KBD_RESEND)
+ break;
+ }
+ return res;
+}
+
+int
+send_aux_command_and_data(int port, int c, int d)
+{
+ int retry;
+ int res;
+
+ for (retry = KBD_MAXRETRY; retry > 0; --retry) {
+ wait_until_controller_is_really_idle(port);
+ outb(port + KBD_COMMAND_PORT, KBDC_WRITE_TO_AUX);
+ wait_until_controller_is_really_idle(port);
+ outb(port + KBD_DATA_PORT, c);
+ res = read_aux_data(port);
+ if (res == PSM_ACK)
+ break;
+ else if (res != PSM_RESEND)
+ return res;
+ }
+
+ for (retry = KBD_MAXRETRY; retry > 0; --retry) {
+ wait_until_controller_is_really_idle(port);
+ outb(port + KBD_COMMAND_PORT, KBDC_WRITE_TO_AUX);
+ wait_until_controller_is_really_idle(port);
+ outb(port + KBD_DATA_PORT, d);
+ res = read_aux_data(port);
+ 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(int port)
+{
+ wait_while_controller_busy(port);
+ if (!wait_for_data(port))
+ return -1; /* timeout */
+ return inb(port + KBD_DATA_PORT);
+}
+
+/* read one byte from the keyboard */
+int
+read_kbd_data(int port)
+{
+ wait_while_controller_busy(port);
+ if (!wait_for_kbd_data(port))
+ return -1; /* timeout */
+ return inb(port + KBD_DATA_PORT);
+}
+
+/* read one byte from the keyboard, but return immediately if
+ * no data is waiting
+ */
+int
+read_kbd_data_no_wait(int port)
+{
+ wait_while_controller_busy(port);
+ if ((inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL)
+ != KBDS_KBD_BUFFER_FULL)
+ return -1; /* no data */
+ return inb(port + KBD_DATA_PORT);
+}
+
+/* read one byte from the aux device */
+int
+read_aux_data(int port)
+{
+ wait_while_controller_busy(port);
+ if (!wait_for_aux_data(port))
+ return -1; /* timeout */
+ return inb(port + KBD_DATA_PORT);
+}
+
+/* discard data from the keyboard */
+void
+empty_kbd_buffer(int port)
+{
+ int b;
+ int c = 0;
+
+ while ((inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL)
+ == KBDS_KBD_BUFFER_FULL) {
+ b = inb(port + KBD_DATA_PORT);
+ ++c;
+ DELAY(20);
+ }
+#ifdef KBDIO_DEBUG
+ log(LOG_DEBUG,"kbdio: %d char read (empty_kbd_buffer)\n",c);
+#endif
+}
+
+/* discard data from the aux device */
+void
+empty_aux_buffer(int port)
+{
+ int b;
+ int c = 0;
+
+ while ((inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL)
+ == KBDS_AUX_BUFFER_FULL) {
+ b = inb(port + KBD_DATA_PORT);
+ ++c;
+ DELAY(20);
+ }
+#ifdef KBDIO_DEBUG
+ log(LOG_DEBUG,"kbdio: %d char read (empty_aux_buffer)\n",c);
+#endif
+}
+
+/* discard any data from the keyboard or the aux device */
+void
+empty_both_buffers(int port)
+{
+ int b;
+ int c = 0;
+
+ while (inb(port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) {
+ b = inb(port + KBD_DATA_PORT);
+ ++c;
+ DELAY(20);
+ }
+#ifdef KBDIO_DEBUG
+ log(LOG_DEBUG,"kbdio: %d char read (empty_both_buffers)\n",c);
+#endif
+}
+
+/* keyboard and mouse device control */
+
+/* NOTE: enable the keyboard port but disable the keyboard
+ * interrupt before calling "reset_kbd()".
+ */
+int
+reset_kbd(int port)
+{
+ int retry = KBD_MAXRETRY;
+ int again = KBD_MAXWAIT;
+ int c;
+
+ while (retry-- > 0) {
+ empty_both_buffers(port);
+ write_kbd_command(port, KBDC_RESET_KBD);
+ c = read_controller_data(port);
+#ifdef KBDIO_DEBUG
+ log(LOG_DEBUG,"kbdio: RESET_KBD return code:%04x\n",c);
+#endif
+ 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(port); /* RESET_DONE/RESET_FAIL */
+ if (c != -1) /* wait again if the controller is not ready */
+ break;
+ }
+#ifdef KBDIO_DEBUG
+ log(LOG_DEBUG,"kbdio: RESET_KBD status:%04x\n",c);
+#endif
+ 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(int port)
+{
+ int retry = KBD_MAXRETRY;
+ int again = KBD_MAXWAIT;
+ int c;
+
+ while (retry-- > 0) {
+ empty_both_buffers(port);
+ write_aux_command(port, PSMC_RESET_DEV);
+ c = read_controller_data(port); /* read_aux_data()? */
+#ifdef KBDIO_DEBUG
+ log(LOG_DEBUG,"kbdio: RESET_AUX return code:%04x\n",c);
+#endif
+ if (c == PSM_ACK) /* aux dev is about to reset... */
+ break;
+ }
+ if (retry < 0)
+ return FALSE;
+
+ while (again-- > 0) {
+ /* wait awhile, well, quite looooooooooooong */
+ DELAY(KBD_RESETDELAY*1000);
+ c = read_aux_data(port); /* RESET_DONE/RESET_FAIL */
+ if (c != -1) /* wait again if the controller is not ready */
+ break;
+ }
+#ifdef KBDIO_DEBUG
+ log(LOG_DEBUG,"kbdio: RESET_AUX status:%04x\n",c);
+#endif
+ if (c != PSM_RESET_DONE) /* reset status */
+ return FALSE;
+
+ c = read_aux_data(port); /* device ID */
+#ifdef KBDIO_DEBUG
+ log(LOG_DEBUG,"kbdio: RESET_AUX ID:%04x\n",c);
+#endif
+ /* NOTE: we could check the device ID now, but leave it later... */
+ return TRUE;
+}
+
+/* controller diagnostics and setup */
+
+int
+test_controller(int port)
+{
+ int c;
+
+ empty_both_buffers(port);
+ write_controller_command(port, KBDC_DIAGNOSE);
+ c = read_controller_data(port); /* DIAG_DONE/DIAG_FAIL */
+#ifdef KBDIO_DEBUG
+ log(LOG_DEBUG,"kbdio: DIAGNOSE status:%04x\n",c);
+#endif
+ return (c == KBD_DIAG_DONE);
+}
+
+int
+test_kbd_port(int port)
+{
+ int c;
+
+ empty_both_buffers(port);
+ write_controller_command(port, KBDC_TEST_KBD_PORT);
+ c = read_controller_data(port);
+#ifdef KBDIO_DEBUG
+ log(LOG_DEBUG,"kbdio: TEST_KBD_PORT status:%04x\n",c);
+#endif
+ return c;
+}
+
+int
+test_aux_port(int port)
+{
+ int c;
+
+ empty_both_buffers(port);
+ write_controller_command(port, KBDC_TEST_AUX_PORT);
+ c = read_controller_data(port);
+#ifdef KBDIO_DEBUG
+ log(LOG_DEBUG,"kbdio: TEST_AUX_PORT status:%04x\n",c);
+#endif
+ return c;
+}
+
+void
+set_controller_command_byte(int port, int command, int flag)
+{
+ write_controller_command(port, KBDC_SET_COMMAND_BYTE);
+ write_controller_data(port, command | flag);
+ wait_while_controller_busy(port);
+}