6a90d9750d
syscons and psm, curtesy Kazutaka Yokota with minor changes by me. This contains an update of the psm driver as well. This also fixes the breakage that I introduced to the psm driver by making syscons poll for keyboard events in the atempt to fix the hanging keyboard problem. It works perfectly for me, and I'd like to hear from all that have had keyboard/ps/2 mouse problems if this is the cure... Submitted by: Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp)
471 lines
11 KiB
C
471 lines
11 KiB
C
/*-
|
|
* 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);
|
|
}
|